Skip to content

Commit aa8e9fa

Browse files
committed
fix(live): 审查修复——槽按会话复位/降档去重/首次定档提示/防御链补全
1 parent 532f675 commit aa8e9fa

3 files changed

Lines changed: 62 additions & 29 deletions

File tree

‎app/src-tauri/src/live_session.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,12 @@ impl LiveSessionManager {
242242
// 2026-08 A1:会话暂停共享状态——按会话复位(P2 补漏:标志/补偿时长
243243
// 跨会话残留会让新会话起始即暂停、时间戳偏移)
244244
self.pause.reset();
245+
// 2026-08-21 审查修复(H1):画面档共享槽按会话复位——槽跨会话残留会
246+
// 让新会话起始即显示上一会话档位(音频档案无 screen worker 写入,
247+
// 残留永久生效);tier_override 残留会让新会话首次降档裁决被静默
248+
// 自动消费(未经本会话用户确认即降档)。
249+
*self.tier_override.lock().expect("tier override lock poisoned") = None;
250+
*self.applied_tier.lock().expect("applied tier lock poisoned") = None;
245251
let pause = self.pause.clone();
246252
// v0.7.2(REQ-151):从窗口标题初始化会话信息(平台/系列/集号——
247253
// 标题信号零成本;时长/分P 由屏幕 worker 播放器 OCR 增量补充)

‎app/src-tauri/src/streaming_asr.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,11 @@ impl StreamingAsrEngine {
265265
// TD-032 延伸修复(2026-08-21):热词含 tokens.txt 外字符时
266266
// sherpa-onnx 编码失败仍创建 ContextGraph → greedy_search 解码
267267
// 断言 abort(exit 0xffffffff);过滤后重建,空则回退普通流。
268+
// 审查修复(M3):tokens 读取失败(None)保守回退普通流——
269+
// 不过滤会放行表外字符热词(原始崩溃输入路径),防御链缺口。
268270
let filtered = match &self.token_chars {
269271
Some(chars) => filter_hotwords_by_tokens(h, chars),
270-
None => h.to_string(),
272+
None => String::new(),
271273
};
272274
if filtered.trim().is_empty() {
273275
self.recognizer.create_stream()

‎app/src/components/LiveProfileStrip.tsx‎

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,58 @@ export default function LiveProfileStrip({ windowTitle }: { windowTitle: string
5050
const [error, setError] = useState("");
5151
// 提示计时器句柄(后到提示清除前一个定时器——防旧 timer 提前清掉新提示)
5252
const noteTimer = useRef<number | null>(null);
53+
// 档位镜像(事件闭包读最新档位——首次定档判定用,审查 M2)
54+
const tierRef = useRef<VisualTier | null>(null);
55+
// 已忽略的降档建议档(保持现状——后端每采样 tick 重发,按建议档去重防重复弹条,审查 M1)
56+
const dismissedRef = useRef<VisualTier | null>(null);
57+
58+
// 档位事件:升档静默可见化;降档请求 → 确认条(幂等去重)。
59+
// 审查 L1:监听先于挂载拉取注册——事件落在(快照、监听)间隙会丢失
60+
useEffect(() => {
61+
const unlisteners: Promise<() => void>[] = [
62+
listen<TierChangedPayload>("live:tier-changed", (e) => {
63+
const t = e.payload.tier as VisualTier;
64+
// 首次定档(此前未定档)不是升档——不展示升档文案(审查 M2)
65+
const isInitial = tierRef.current === null;
66+
tierRef.current = t;
67+
setTier(t);
68+
setDowngrade(null); // 档位已变化(含降档确认后),确认条使命完成
69+
dismissedRef.current = null; // 档位已变,降档可重新询问
70+
if (e.payload.reason === "upgrade-silent" && !isInitial) {
71+
setNote(`画面档已自动升为「${TIER_LABELS[t] ?? t}档」`);
72+
if (noteTimer.current) window.clearTimeout(noteTimer.current);
73+
noteTimer.current = window.setTimeout(() => setNote(null), NOTE_TTL_MS);
74+
}
75+
}),
76+
listen<TierDowngradeRequest>("live:tier-downgrade-request", (e) => {
77+
// 幂等:同一降档请求重复询问不重复弹(保持现状后按建议档去重;审查 M1)
78+
const to = e.payload.to as VisualTier;
79+
if (dismissedRef.current !== to) setDowngrade(to);
80+
}),
81+
// 停止过渡期清空确认条(confirm 命令要求活动会话,停止后必失败——审查 L2)
82+
listen<string>("live:status", (e) => {
83+
if (e.payload === "stopped") {
84+
setDowngrade(null);
85+
dismissedRef.current = null;
86+
}
87+
}),
88+
];
89+
return () => {
90+
unlisteners.forEach((p) => void p.then((fn) => fn()));
91+
if (noteTimer.current) window.clearTimeout(noteTimer.current);
92+
};
93+
}, []);
5394

5495
// 挂载:档位拉取兜底(事件可能早于本组件挂载——同 session-info 模式);
5596
// 形态/领域重跑检测(与档案卡同口径,窗口标题确定性输入)
5697
useEffect(() => {
5798
void invoke<{ tier: string | null }>("live_session_status")
5899
.then((s) => {
59-
if (s.tier) setTier(s.tier as VisualTier);
100+
if (s.tier) {
101+
const t = s.tier as VisualTier;
102+
tierRef.current = t;
103+
setTier(t);
104+
}
60105
})
61106
.catch(() => undefined);
62107
if (!windowTitle) return;
@@ -74,30 +119,6 @@ export default function LiveProfileStrip({ windowTitle }: { windowTitle: string
74119
};
75120
}, [windowTitle]);
76121

77-
// 档位事件:升档静默可见化;降档请求 → 确认条(幂等覆盖,不重复闪烁)
78-
useEffect(() => {
79-
const unlisteners: Promise<() => void>[] = [
80-
listen<TierChangedPayload>("live:tier-changed", (e) => {
81-
const t = e.payload.tier as VisualTier;
82-
setTier(t);
83-
setDowngrade(null); // 档位已变化(含降档确认后),确认条使命完成
84-
if (e.payload.reason === "upgrade-silent") {
85-
setNote(`画面档已自动升为「${TIER_LABELS[t]}档」`);
86-
if (noteTimer.current) window.clearTimeout(noteTimer.current);
87-
noteTimer.current = window.setTimeout(() => setNote(null), NOTE_TTL_MS);
88-
}
89-
}),
90-
listen<TierDowngradeRequest>("live:tier-downgrade-request", (e) => {
91-
// 幂等:同一降档请求重复询问直接覆盖更新(后端下轮重评再询直到确认/档位变化)
92-
setDowngrade(e.payload.to as VisualTier);
93-
}),
94-
];
95-
return () => {
96-
unlisteners.forEach((p) => void p.then((fn) => fn()));
97-
if (noteTimer.current) window.clearTimeout(noteTimer.current);
98-
};
99-
}, []);
100-
101122
/** 确认降档:写入共享 override → worker 下轮检测消费并 retune;档位由
102123
* 随后的 tier-changed 事件同步(乐观关闭确认条) */
103124
const confirmDowngrade = useCallback(async () => {
@@ -130,7 +151,7 @@ export default function LiveProfileStrip({ windowTitle }: { windowTitle: string
130151
📋 {form ? FORM_LABELS[form] : "形态识别中"}
131152
</span>
132153
<span title="画面价值档(会话中自动重评:升档静默/降档确认)">
133-
🎨 画面 {tier ? `${TIER_LABELS[tier]}档` : "未定档"}
154+
🎨 画面 {tier ? `${TIER_LABELS[tier] ?? tier}档` : "未定档"}
134155
</span>
135156
<span title="内容领域(标题/平台信号;增强项)">
136157
🏷 {domain?.kind ? domainLabel(domain.kind) : "领域未定"}
@@ -157,7 +178,7 @@ export default function LiveProfileStrip({ windowTitle }: { windowTitle: string
157178
padding: "4px 8px",
158179
}}
159180
>
160-
<span style={{ color: "#b45309" }}>画面价值降低,是否降为「{TIER_LABELS[downgrade]}档」?</span>
181+
<span style={{ color: "#b45309" }}>画面价值降低,是否降为「{TIER_LABELS[downgrade] ?? downgrade}档」?</span>
161182
<button
162183
onClick={() => void confirmDowngrade()}
163184
disabled={busy}
@@ -174,7 +195,11 @@ export default function LiveProfileStrip({ windowTitle }: { windowTitle: string
174195
{busy ? "确认中…" : "确认降档"}
175196
</button>
176197
<button
177-
onClick={() => setDowngrade(null)}
198+
onClick={() => {
199+
// 保持现状:记录已忽略的建议档(后端每采样 tick 重发——同档不再弹条,审查 M1)
200+
dismissedRef.current = downgrade;
201+
setDowngrade(null);
202+
}}
178203
disabled={busy}
179204
style={{
180205
padding: "2px 8px",

0 commit comments

Comments
 (0)