@@ -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