setCustomOpen(false)} />}
{customOpen && (
✨ {t('customLayout.title')}
)}
{requestDelete &&
setRequestDelete(null)} />}
{requestDelete && (
⚠️ {t('confirm.title')}
{requestDelete.kind === 'layout'
? t('confirm.layoutBody', { name: requestDelete.name })
: requestDelete.kind === 'shortcut'
? t('confirm.shortcutBody', { name: requestDelete.name })
: t('confirm.projectBody', { name: requestDelete.name })}
)}
{/* 「工作台」控制室项目:固定首位、不可删除;未绑定点开 = 强制绑定弹窗 */}
{renderProjectSlot
? renderProjectSlot('sidebar.worktable.project', ownerProps)
:
{t('empty')}
}
{visibleLayouts.map((l) => (
))}
{visibleShortcuts.length > 0 && (
)}
)
}
export const inject = ['slots', 'locale', 'sessions', 'conversation', 'workspaces']
export function apply(ctx: any) {
// 自定义窗口 → 宿主会话桥:保存 sessions/conversation/list 服务引用(模块级)
sessionBridge = { sessions: ctx.sessions ?? null, conversation: ctx.conversation ?? null, list: ctx.sessions?.list ?? null, workspaces: ctx.workspaces ?? null }
try { hostApi = ctx.get?.('connection')?.api ?? null } catch { hostApi = null }
try { (window as any).__dshHostApi = hostApi } catch {}
try { (window as any).__dshOpenSession = (id: string) => ctx.sessions?.open?.(id); (window as any).__dshSessions = ctx.sessions; (window as any).__dshPromptIntoSession = (id: string, text: string) => promptIntoSession(id, text); (window as any).__dshWorkspaces = ctx.workspaces; (window as any).__dshBuildWindowTaskText = buildWindowTaskText; (window as any).__dshSyncSessionScope = () => syncSessionScope(sessionBridge?.list) } catch {}
ctx.effect(() => {
const style = document.createElement('style')
style.setAttribute('data-dsh-plugin', 'dsh-worktable')
style.textContent = css
document.head.appendChild(style)
return () => { style.remove() }
}, 'dsh-worktable: styles')
// 绑定按钮 hover 气泡:事件委托 + body 级气泡(跨层显示,不遮挡右侧对话也不盖项目名)
ctx.effect(() => {
const over = (e: MouseEvent) => {
const t = e.target as HTMLElement | null
const btn = t && t.closest ? (t.closest('.dsh-wt_bindBtn') as HTMLElement | null) : null
if (btn) showBindTip(btn)
}
const out = (e: MouseEvent) => {
const t = e.target as HTMLElement | null
if (t && t.closest && t.closest('.dsh-wt_bindBtn')) hideBindTip()
}
const hide = () => hideBindTip()
document.addEventListener('mouseover', over, true)
document.addEventListener('mouseout', out, true)
document.addEventListener('scroll', hide, true)
document.addEventListener('click', hide, true)
return () => {
document.removeEventListener('mouseover', over, true)
document.removeEventListener('mouseout', out, true)
document.removeEventListener('scroll', hide, true)
document.removeEventListener('click', hide, true)
if (bindTipEl) { bindTipEl.remove(); bindTipEl = null }
}
}, 'dsh-worktable: bind tip')
// locale 词典(宿主 locale 服务缺席时由 t 的回退分支兜底)
ctx.effect(() => {
if (ctx.locale?.register) return ctx.locale.register(NS, { zh, en })
}, 'dsh-worktable: dictionaries')
// 子座位注册 id 序列跟踪(供排序/编辑模式使用)
const syncIds = () => {
const entries = ctx.slots.entries('sidebar.worktable.project')
const ids: string[] = []
for (const entry of entries) {
const id = entry?.options?.id
if (typeof id === 'string' && id && !ids.includes(id)) ids.push(id)
}
registryStore.ids = ids
for (const fn of registryStore.listeners) fn()
}
const disposeSubscribe = ctx.slots.subscribe('sidebar.worktable.project', syncIds)
syncIds()
ctx.effect(() => disposeSubscribe, 'dsh-worktable: project registry watch')
// 会话作用域(当前会话 cwd 与后台任务)→ 功能窗数据源
const sessionsList = ctx.sessions?.list
if (sessionsList && typeof sessionsList.getSnapshot === 'function') {
syncSessionScope(sessionsList)
const disposeScope = sessionsList.subscribe(() => syncSessionScope(sessionsList))
ctx.effect(() => disposeScope, 'dsh-worktable: session scope watch')
}
// 分栏工作区浮层(M1 通用引擎,shell.overlay 座位)
ctx.slots.inject('shell.overlay', () => ctx.slots.register({
name: 'shell.overlay',
id: 'dsh-worktable-split',
order: 100,
}, SplitWorkspace), 'dsh-worktable: split workspace overlay')
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({
name: 'sidebar.footer.action',
id: 'dsh-worktable',
order: 20,
children: {
'sidebar.worktable.project': {
kind: 'list',
scope: 'root',
},
},
}, WorktableSection), 'dsh-worktable: worktable section')
}
export { WorktableSection }