feat(web): 前端 P0 收尾(多路忙态收口 / 握手 history 落 store / 清理幽灵键)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
AskaEth committed 2026-10-05 11:29:17 +08:00
1 parent e852e60af9
commit 012e9f3ad4
10 files changed
+365 -31

No files matched your search

+19 -2
View File
@@ -1,6 +1,11 @@
import { MODES, type ChatMessage, type Mode } from "../../types";
import { parseAdminChannelMessage, parsePersistedMessage, toRecord } from "../../wsParse";
import type { FrameEvent, FramePayload, StoreState } from "./shared";
import {
parseAdminChannelMessage,
parseHistoryMessages,
parsePersistedMessage,
toRecord,
} from "../../wsParse";
import { frameSessionId, type FrameEvent, type FramePayload, type StoreState } from "./shared";
/** The fields of a `message_edited` payload the chat needs: the edited message's
* id and its new content. `sessionId` is carried inside the broadcast DTO (and on
@@ -40,6 +45,18 @@ function parseMessageEdited(data: unknown): MessageEdited | null {
* 未命中返回 false 交给下一组。 */
export function handleMessageEvent(ev: FrameEvent, r: FramePayload, s: StoreState): boolean {
switch (ev.type) {
case "history": {
// 握手回放(契约 §3.2,仅 owner/admin,只回放主会话最近 50 条):此前这帧落到
// 未知帧兜底被丢掉 ⇒ WS 断线窗口内落库的消息永久缺失(要等切会话/刷新才补)。
// 并入时按 id 去重、只按精确键(clientMsgId / turnId)认领本端气泡(见
// sessionCache.mergeHistory)—— 整批回放绝不用内容/时间启发式,以免把正在
// 流式的气泡错配到某条旧行上。帧壳 sessionId("web")就是回放所属会话。
const msgs = parseHistoryMessages(r?.messages);
if (msgs.length) {
s.mergeHistoryPage(frameSessionId(ev, r) ?? msgs[0].sessionId ?? "", msgs);
}
return true;
}
case "message_persisted": {
// design/16 §4.2 — a user or assistant message was persisted and broadcast
// with its backend id. 裁决 16.4.5: the frame is written into the bucket its