feat: lock mounted results as the window's permanent content (lockPane replaces tabs and persists across sessions)

This commit is contained in:
Aisland
2026-08-20 22:03:10 +08:00
parent 54bfcccd67
commit 6720b4dfab
5 changed files with 153 additions and 6 deletions
+11 -6
View File
@@ -455,7 +455,7 @@ function buildWindowTaskText(projectId: string, projectName: string, windowLabel
' - 视频 / 动画 → 生成 .mp4 / .gif(或 Lottie JSON)文件放进项目文件夹;',
' - 工作台已有内置窗能力(资源管理器 / 终端 / 浏览器 / 动画站)→ 不要重复造轮子,直接建议用户改用内置窗。',
'5. 完成内容后,用一两句话说明用户把它放进窗口的步骤。该窗口位于「' + projectName + '」项目内,如需要也可协助该项目后续的其他自定义工作。',
'6. 完成后写入「产物清单」文件:在项目文件夹里创建 widget-result.json,内容示例 {\"window\":\"' + win + '\",\"path\":\"产物相对路径\",\"kind\":\"html\"};kind 可选 html(本地页面,path 相对项目文件夹)/ url(外部链接,path 为完整 URL)/ file(其他文件,path 相对项目文件夹)。写完这个文件,工作台会自动把产物挂载进「' + win + '」窗口,用户无需手动操作。',
'6. 完成后写入「产物清单」文件:在项目文件夹里创建 widget-result.json,内容示例 {\"window\":\"' + win + '\",\"path\":\"产物相对路径\",\"kind\":\"html\"};kind 可选 html(本地页面,path 相对项目文件夹)/ url(外部链接,path 为完整 URL)/ file(其他文件,path 相对项目文件夹)。写完这个文件,工作台会自动把产物挂载进「' + win + '」窗口,用户无需手动操作;挂载后该产物会作为「' + win + '」窗口的固定内容锁定保存,用户下次打开工作台时窗口直接显示它,不会丢失或重置。',
KNOWLEDGE_PACK,
]
return lines.join('\n')
@@ -1021,7 +1021,10 @@ function buildCustomLayoutPrompt(req: string): string {
// 补挂:此前项目未打开时暂存的产物,现在自动挂进主行第一格
const pending = pendingMountRef.current[spec.id]
if (pending) {
try { splitStore.openTab('main', 0, pending) } catch {}
try {
if (pending.content && typeof pending.row === 'string') splitStore.lockPane(pending.row, pending.index ?? 0, pending.content)
else splitStore.openTab('main', 0, pending)
} catch {}
delete pendingMountRef.current[spec.id]
try { localStorage.setItem('dsh.worktable.pendingMount.v1', JSON.stringify(pendingMountRef.current)) } catch {}
}
@@ -1114,12 +1117,14 @@ function buildCustomLayoutPrompt(req: string): string {
const content = buildMountContent(folder, d)
if (!content) return
if (splitStore.active && splitStore.spec?.id === projectId) {
// 项目开着:直接挂进「窗口N」对应窗格(找不到就落主行第一格)
// 项目开着:锁死挂进「窗口N」对应窗格(清空原内容、唯一标签;找不到就落主行第一格)
const pane = windowLabelToPane(splitStore.spec, d.window)
splitStore.openTab(pane?.row ?? 'main', pane?.index ?? 0, content)
splitStore.lockPane(pane?.row ?? 'main', pane?.index ?? 0, content)
} else {
// 项目没开:暂存,打开项目时补挂
pendingMountRef.current[projectId] = content
// 项目没开:按项目已保存的 spec 解析目标窗格,暂存,打开项目时锁死补挂
const saved = projectsRef.current.projects.views[projectId] ?? projectsRef.current.projects.layouts.find((l) => l.id === projectId)
const pane = windowLabelToPane(saved, d.window)
pendingMountRef.current[projectId] = { content, row: pane?.row ?? 'main', index: pane?.index ?? 0 }
try { localStorage.setItem('dsh.worktable.pendingMount.v1', JSON.stringify(pendingMountRef.current)) } catch {}
}
} catch { /* 无清单文件 = 不挂载 */ }
+26
View File
@@ -105,6 +105,8 @@ type SplitState = {
setTopW(i: number, w: number): void
setPaneContent(row: PaneRow, i: number, content: SplitContent | null): void
openTab(row: PaneRow, i: number, content: SplitContent): void
/** 锁定窗格:清空原有标签,把内容作为该窗唯一的固定标签(挂载产物的「锁死」语义) */
lockPane(row: PaneRow, i: number, content: SplitContent): void
closeTab(row: PaneRow, i: number, tabId: string): void
setActiveTab(row: PaneRow, i: number, tabId: string): void
moveTab(fromRow: PaneRow, fromI: number, tabId: string, toRow: PaneRow, toI: number): void
@@ -586,6 +588,30 @@ export const splitStore: SplitState = {
if (content) this.openTab(row, i, content)
},
lockPane(row, i, content) {
const spec = this.spec
if (!spec) return
const tab: PaneTab = { id: 't' + Date.now().toString(36), title: tabTitleOf(content), content, active: 0 }
const mutate = (pane: SplitPane): SplitPane => ({ ...pane, content: null, tabs: [tab], active: 0 })
if (row === 'left') {
if (!spec.left || i !== 0) return
this.spec = { ...spec, left: mutate(spec.left) }
} else if (row === 'top') {
const top = [...(spec.top ?? [])]
if (!top[i]) return
top[i] = mutate(top[i])
this.spec = { ...spec, top }
} else {
const main = [...spec.main]
if (!main[i]) return
main[i] = mutate(main[i])
this.spec = { ...spec, main }
}
this.onSpecMutated?.(this.spec)
this.persist()
this.notify()
},
openTab(row, i, content) {
const spec = this.spec
if (!spec) return