feat(web): D4 会话未读徽标 — 打开即已读 + 消费 session_read
- SessionMeta.unreadCount(可选,兼容旧后端)+ api/sessions.markSessionRead - lib/sessionRead.ts:会话历史落地后再推游标(best-effort,失败静默,下次 GET /sessions 收敛)+ 帧归属判定(ws.ts 因此留在 500 行内) - store.clearUnread 无变化时返回同一 state,避免多余重渲染;Nav 徽标沿用 Miuix token(mu-radius-pill/mu-err/mu-rise-in) - 逻辑测试 35 → 54 项,含变异校验(改成 clearUnread(activeSessionId) 即 FAIL)
This commit is contained in:
1 parent
e8eccd2bb2
commit
a828edacc5
9 files changed
+235
-11
No files matched your search
@@ -14,6 +14,7 @@ import {
|
||||
toRecord,
|
||||
} from "./wsParse";
|
||||
import { MODES, type AwakenState, type ChatMessage, type Mode, type WsUpEvent } from "./types";
|
||||
import { sessionReadTarget } from "./sessionRead";
|
||||
import { streamFrameApplies } from "./streamScope";
|
||||
|
||||
/**
|
||||
@@ -321,6 +322,15 @@ export function dispatchFrame(ev: { type: string; sessionId?: string; data: unkn
|
||||
s.setSessionBusy(busy, pendingCount);
|
||||
break;
|
||||
}
|
||||
case "session_read": {
|
||||
// 协同 B3/D4 — one end marked the conversation read, so the unread badge goes
|
||||
// out on every end. Only the addressed session is touched (a frame naming
|
||||
// another conversation must not clear the dot of the one on screen), and an
|
||||
// unattributable frame is dropped rather than guessed.
|
||||
const id = sessionReadTarget(ev.sessionId, r);
|
||||
if (id) s.clearUnread(id);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Unknown future event: keep it in the context log so no frame is lost.
|
||||
const text = typeof ev.data === "string" ? ev.data : JSON.stringify(ev.data);
|
||||
|
||||
Reference in new issue
Block a user