feat(web): 静默/活动态切换 + 预约生效(裁决 16.4.1)
- 唯一逻辑入口 components/modeSwitch.ts(纯函数判定 + 预约态 + 落地/撤销)+ ModeSelector 组件 - 活动态:她自己切(下行 mode_switch)直接生效,用户此时切换只登记**预约**、不偷偷改、不上行, 且不清除预约(两层不打架) - 静默态:用户可即时切 + 上行 mode_switch - **用户提前切 → 下次静默态生效**:`active` 由 true→false 时落地;徽章写「已预约 · X(她静默后生效)」, 不会让人误以为立刻生效 - 「活动/静默」判据=她是否在忙(sessionBusy || 屏上 streaming || sending);若口径要改成 capacity 三态, 只需改 modeSwitch.ts 的一个谓词(已登记) - 复核:tsc 0 错 + vite build 成功 + node 逻辑测试 28 项;2 条变异反证(去掉活动态分叉 / 去掉 active 守卫)
This commit is contained in:
1 parent
093f38d986
commit
f454273c5a
5 files changed
+483
-7
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useAppStore } from "../store/useAppStore";
|
||||
import { useCurrentPersona } from "../store/personaSelectors";
|
||||
import { useModeStore } from "../store/useModeStore";
|
||||
import { useSendChat } from "../lib/useChat";
|
||||
import { sendUp } from "../lib/ws";
|
||||
import { HistoryList } from "../components/messages/HistoryList";
|
||||
@@ -9,19 +8,24 @@ import { ChatInput } from "../components/ChatInput";
|
||||
import { EmptyState } from "../components/primitives";
|
||||
import { Icon } from "../components/Icon";
|
||||
import { SessionStatusHint, ApprovalNoticeHint } from "../components/status";
|
||||
import { requestModeSwitch, useHerActive } from "../components/modeSwitch";
|
||||
import { personaChatBackground, type Orientation } from "../lib/persona";
|
||||
|
||||
/**
|
||||
* The single shared page (design/07 §1). All behavior modes live in ONE message
|
||||
* stream; each message carries its own `mode` (assigned by the agent at runtime,
|
||||
* design/01 §3 hot-switch), so its bubble renders per that mode. There is no
|
||||
* manual mode selector — the agent / backend drives the mode.
|
||||
* 唯一共享页面(design/07 §1):所有行为模式共用**一条消息流**,每条消息带自己的
|
||||
* `mode`(由 agent 在运行时热切换,design/01 §3),气泡按该消息的 mode 渲染。
|
||||
*
|
||||
* 输入模式(下一条消息的形态)的切换入口在输入框上方的 `ModeSelector`
|
||||
* (design/07 §1,裁决 16.4.1):**她活动时她自己切**;**她静默时用户可以切**;
|
||||
* 用户**提前切**只记为「已预约」,在她下次静默时生效——规则收口在
|
||||
* `components/modeSwitch.ts`,本页只负责把用户发起的切换(语音按钮)也送进同一入口。
|
||||
*/
|
||||
export function ChatShell(): React.ReactNode {
|
||||
const messages = useAppStore((s) => s.messages);
|
||||
// The chat wallpaper follows the *active session's* role (design/22 §4.6).
|
||||
const currentPersona = useCurrentPersona();
|
||||
const { send, sending } = useSendChat();
|
||||
const herActive = useHerActive(sending);
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Chat wallpaper follows the current orientation (landscape when wide, else
|
||||
@@ -96,8 +100,9 @@ export function ChatShell(): React.ReactNode {
|
||||
onVoice={() => {
|
||||
// Voice is not wired to a live pipeline yet (real call deferred). Activate
|
||||
// the voice_input uplink and switch the shell to voice mode so the agent
|
||||
// (or the demo) reflects a voice session.
|
||||
useModeStore.getState().setMode("voice");
|
||||
// (or the demo) reflects a voice session. 模式切换走统一入口:她活动时
|
||||
// 只记为预约(裁决 16.4.1),不绕过输入区的切换规则。
|
||||
requestModeSwitch("voice", herActive);
|
||||
sendUp({ type: "voice_input", data: { mode: "voice" } });
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user