diff --git a/src/stores/chat/chat-machine.helpers.ts b/src/stores/chat/chat-machine.helpers.ts index 0241888a..b1f2e639 100644 --- a/src/stores/chat/chat-machine.helpers.ts +++ b/src/stores/chat/chat-machine.helpers.ts @@ -32,7 +32,7 @@ import { Result } from "@/utils/result"; // Constants // ============================================================ /** 翻历史每页大小(仅 `loadMoreHistoryActor` 用) */ -export const PAGE_SIZE = 20; +export const PAGE_SIZE = 50; // ============================================================ // Repository injection @@ -137,6 +137,25 @@ export async function readAndSyncHistory(): Promise<{ /** 真实从 network 读到的消息数(调试用) */ networkCount: number; }> { + /** + * 空历史首屏欢迎语(AI persona 首条消息)。 + * 触发条件:最终返回的 messages 为空(network 空 + local 空 / 或 network 失败且 local 空)。 + * + * 不主动持久化到 local —— 这是纯 UI 层"首屏不空"的兜底: + * - 用户一旦发了消息,network 上有真实消息 → 后续 readAndSyncHistory + * 返回的 messages 非空,不会再触发本条 + * - 用户从未发过消息就关掉 app 再开,仍然没 network 消息 → 会再次显示本条 + * ("每次冷启动给个温暖的起点" —— 若要"只显示一次",需额外存 local) + */ + const greetingMessage: UiMessage = { + content: + "You're here! Facebook is so restrictive, " + + "there were things I couldn't say there. " + + "Finally I can relax. How was your day out?", + isFromAI: true, + date: formatDate(), + }; + // 1. 读 local const localResult = await chatRepo.getLocalMessages(); const localMessages = @@ -155,10 +174,19 @@ export async function readAndSyncHistory(): Promise<{ "[chat-machine] loadHistory NETWORK FAILED", networkResult.success ? null : (networkResult as { error: unknown }).error, ); + + // 空历史 → fallback 也走 welcome(不让屏幕空空如也) + const fallbackMessages = + localMessages.length === 0 ? [greetingMessage] : localMessages; + if (fallbackMessages[0] === greetingMessage) { + console.log( + "[chat-machine] loadHistory EMPTY → prepend greeting (network-failed path)", + ); + } return { - messages: localMessages, // 退而求其次:返 local(不然屏幕空) + messages: fallbackMessages, hasMore: false, - newOffset: 0, + newOffset: fallbackMessages.length, localOverwritten: false, localCount: localMessages.length, networkCount: 0, @@ -169,17 +197,24 @@ export async function readAndSyncHistory(): Promise<{ count: networkUi.length, }); - // 3. 用 network 覆盖 local("再用网络数据覆盖本地数据"这句的字面实现) + // 3. 用 network 覆盖 local const saveResult = await chatRepo.saveMessagesToLocal(networkResult.data.messages); const localOverwritten = Result.isOk(saveResult); console.log("[chat-machine] loadHistory SAVE TO LOCAL DONE", { localOverwritten, }); + // 空历史 → prepend welcome(network 权威为空 = 用户从未发过消息) + const finalMessages = + networkUi.length === 0 ? [greetingMessage] : networkUi; + if (finalMessages[0] === greetingMessage) { + console.log("[chat-machine] loadHistory EMPTY → prepend greeting"); + } + return { - messages: networkUi, // authorititative - hasMore: networkUi.length >= PAGE_SIZE, - newOffset: networkUi.length, + messages: finalMessages, // authoritative(empty 时塞 greeting) + hasMore: finalMessages.length >= PAGE_SIZE, + newOffset: finalMessages.length, localOverwritten, localCount: localMessages.length, networkCount: networkUi.length,