fix: self-heal broken console view (tab-closed legacy state)

This commit is contained in:
Aisland
2026-08-21 01:24:16 +08:00
parent 14c21a10bd
commit 5196d0f768
3 changed files with 37 additions and 2 deletions
+20 -1
View File
@@ -311,6 +311,11 @@ let sessionBridge: { sessions: any; conversation: any; list: any; workspaces: an
const CONSOLE_ID = 'wt-console' const CONSOLE_ID = 'wt-console'
const CONSOLE_ICON = '🖥️' const CONSOLE_ICON = '🖥️'
/** 控制室布局是否健全:主窗格首格存在 console 类型标签(旧版关标签的坏存档没有它) */
function specHasConsoleTab(spec: LayoutSpec | undefined): boolean {
return !!spec?.main?.[0]?.tabs?.some((t) => t.content?.kind === 'builtin' && t.content.type === 'console')
}
/** 控制室默认布局:单一锁死大窗格(项目卡片网格)+ 右侧对话 */ /** 控制室默认布局:单一锁死大窗格(项目卡片网格)+ 右侧对话 */
function buildConsoleSpec(t: (key: string) => string): LayoutSpec { function buildConsoleSpec(t: (key: string) => string): LayoutSpec {
return { return {
@@ -1083,6 +1088,16 @@ function WorktableSection(props: any) {
}) })
} }
// 自愈:启动时若控制室存档是坏布局(旧版关掉控制室标签造成窗格退化成选择器),立即重建默认面板
useEffect(() => {
const saved = projects.views[CONSOLE_ID]
if (!specHasConsoleTab(saved)) {
const spec = buildConsoleSpec((k) => t(k as WorktableKey))
persistProjects((prev) => ({ ...prev, views: { ...prev.views, [CONSOLE_ID]: spec } }))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projects.views[CONSOLE_ID]])
// 子座位注册变化 → 刷新 id 序列 // 子座位注册变化 → 刷新 id 序列
useEffect(() => { useEffect(() => {
const sync = () => setRegisteredIds([...registryStore.ids]) const sync = () => setRegisteredIds([...registryStore.ids])
@@ -1302,7 +1317,11 @@ function buildCustomLayoutPrompt(req: string): string {
/** 打开「工作台」控制室:已绑定 → 打开并切换绑定对话;explicitBound 供强制绑定流程传入刚绑定的会话 */ /** 打开「工作台」控制室:已绑定 → 打开并切换绑定对话;explicitBound 供强制绑定流程传入刚绑定的会话 */
const openConsole = useCallback((explicitBound?: string | null) => { const openConsole = useCallback((explicitBound?: string | null) => {
const spec = projects.views[CONSOLE_ID] ?? buildConsoleSpec((k) => t(k as WorktableKey)) const saved = projects.views[CONSOLE_ID]
// 自愈:坏存档(旧版关掉控制室标签导致窗格退化成选择器)→ 重建默认面板并写回
const broken = !specHasConsoleTab(saved)
const spec = broken ? buildConsoleSpec((k) => t(k as WorktableKey)) : (saved as LayoutSpec)
if (broken) persistProjects((prev) => ({ ...prev, views: { ...prev.views, [CONSOLE_ID]: spec } }))
engineIdsRef.current.add(CONSOLE_ID) engineIdsRef.current.add(CONSOLE_ID)
splitStore.open(spec) splitStore.open(spec)
if (splitStore.active && splitStore.spec?.id === CONSOLE_ID) { if (splitStore.active && splitStore.spec?.id === CONSOLE_ID) {
@@ -1220,3 +1220,14 @@
rgba(63,185,80,0.9) + 双层 box-shadow ✓、busy 旋转描边 #5aa0ff ✓;三阶段流程 + 主题 rgba(63,185,80,0.9) + 双层 box-shadow ✓、busy 旋转描边 #5aa0ff ✓;三阶段流程 + 主题
全 ✓;functional-diag 全 20 STEP ERROR_COUNT: 0。 全 ✓;functional-diag 全 20 STEP ERROR_COUNT: 0。
- 备注:纯客户端改动,F5 即生效。 - 备注:纯客户端改动,F5 即生效。
## 补记(控制室坏存档自愈:旧版「关标签」坑出不可逆状态)
- 事故:用户在被修复前点掉了控制室标签的 ✕,窗格退化成选择器并持久化进
views['wt-console'](tabs 空)——重开控制室也只显示选择器,回不去。
- 实现:specHasConsoleTab 健全性检查 + 双通道自愈:① 挂载时(useEffect 监听
views[CONSOLE_ID])发现坏存档立即重建 buildConsoleSpec 并写回;② openConsole 打开时
再查一遍,坏则重建 + persistProjects 写回。探测种子改为植入坏存档验证。
- 验证:probe-batch16 healTabs=1、healType='console' ✓,全流程 ✓;functional-diag
全 20 STEP ERROR_COUNT: 0。
- 备注:纯客户端改动,F5 即生效;用户 F5 后坏存档自动修复,点「控制室」卡片即恢复面板。
+6 -1
View File
@@ -26,7 +26,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await send('Runtime.enable'); await send('Runtime.enable');
await send('Page.enable'); await send('Page.enable');
await send('Page.addScriptToEvaluateOnNewDocument', { await send('Page.addScriptToEvaluateOnNewDocument', {
source: "try{ if(location.origin==='http://127.0.0.1:3080'){ localStorage.setItem('dsh.worktable.view.v1', JSON.stringify({query:'',searchOpen:false,orderBy:'manual',dock:'footer',floatTop:null,sortMigratedV2:true})); localStorage.setItem('dsh.worktable.projects.v1', JSON.stringify({order:['t-bind'],lastUsed:{},hidden:[],nameOverrides:{},iconOverrides:{},shortcuts:[],layouts:[{id:'t-bind',title:'\u7ed1\u5b9a\u6d4b\u8bd5',icon:'\ud83e\uddea',top:null,left:null,main:[{id:'p1',title:'\u5185\u5bb91',min:200,content:null,tabs:[],active:0}],leftWidth:{default:260,min:160,max:480},chatWidth:{default:360,min:240,max:600},topHeight:{default:200,min:120,max:480},chatSide:'right',chatFullHeight:false}],bindings:{},folders:{},views:{},removed:[]})); } }catch(e){}", source: "try{ if(location.origin==='http://127.0.0.1:3080'){ localStorage.setItem('dsh.worktable.view.v1', JSON.stringify({query:'',searchOpen:false,orderBy:'manual',dock:'footer',floatTop:null,sortMigratedV2:true})); localStorage.setItem('dsh.worktable.projects.v1', JSON.stringify({order:['t-bind'],lastUsed:{},hidden:[],nameOverrides:{},iconOverrides:{},shortcuts:[],layouts:[{id:'t-bind',title:'\u7ed1\u5b9a\u6d4b\u8bd5',icon:'\ud83e\uddea',top:null,left:null,main:[{id:'p1',title:'\u5185\u5bb91',min:200,content:null,tabs:[],active:0}],leftWidth:{default:260,min:160,max:480},chatWidth:{default:360,min:240,max:600},topHeight:{default:200,min:120,max:480},chatSide:'right',chatFullHeight:false}],bindings:{},folders:{},views:{\"wt-console\":{\"id\":\"wt-console\",\"title\":\"kz\",\"top\":null,\"main\":[{\"id\":\"console\",\"title\":\"kz\",\"min\":240,\"tabs\":[],\"active\":0}],\"chatWidth\":{\"default\":340,\"min\":280,\"max\":600},\"topHeight\":{\"default\":200,\"min\":120,\"max\":480},\"chatSide\":\"right\"}},removed:[]})); } }catch(e){}",
}); });
await send('Page.navigate', { url: 'http://127.0.0.1:3080/' }); await send('Page.navigate', { url: 'http://127.0.0.1:3080/' });
const evaluate = async (expr) => { const evaluate = async (expr) => {
@@ -45,6 +45,11 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
var vis = function(sel){ return [].slice.call(document.querySelectorAll(sel)).find(function(el){ return (getComputedStyle(el).visibility!=='hidden'&&el.getBoundingClientRect().height>0); }); }; var vis = function(sel){ return [].slice.call(document.querySelectorAll(sel)).find(function(el){ return (getComputedStyle(el).visibility!=='hidden'&&el.getBoundingClientRect().height>0); }); };
var st = window.__dshWorktable.splitStore; var st = window.__dshWorktable.splitStore;
try { try {
// 自愈验证:种子里的坏存档(控制室标签被关掉)应被挂载时修复
var seeded = JSON.parse(localStorage.getItem('dsh.worktable.projects.v1'));
var cv = seeded && seeded.views && seeded.views['wt-console'];
out.healTabs = cv && cv.main && cv.main[0] && cv.main[0].tabs ? cv.main[0].tabs.length : null;
out.healType = cv && cv.main && cv.main[0] && cv.main[0].tabs && cv.main[0].tabs[0] ? cv.main[0].tabs[0].content.type : null;
var cs = getComputedStyle(document.body); var cs = getComputedStyle(document.body);
out.themeFill = cs.getPropertyValue('--dsw-alias-fill-l1').trim() || null; out.themeFill = cs.getPropertyValue('--dsw-alias-fill-l1').trim() || null;
out.htmlDataTheme = document.documentElement.getAttribute('data-theme'); out.htmlDataTheme = document.documentElement.getAttribute('data-theme');