feat(dsh): adapt to DSH 0.1.5-alpha.2
Baseline moves to DSH 0.1.5-alpha.2 only (peer floor ^0.1.5-alpha.2, CI pin, manifest engines); alpha.1 users stay on v0.19.0-alpha.0. Host-contract changes handled: - the native guide entry lost its `description` field (entries are icon+title capsules now): `TabDescriptor.description` and the six `guideDesc*` keys plus the `nativeGuideDesc` fallback are removed. - the file address grammar changed: `fileAddressFor` always returns a session-scoped address (absolute paths keep their leading `/`), `parseFileAddress` is prefix-based and strips `?`/`#`. The plugin's purity-gated mirror follows. - global main panels replaced the `conversation` root slot with the keyed `main` slot (`main.conversation`): the bottom workbench's center-column locator now accepts both keys and skips `display: contents` slot hosts. Fixes a pre-existing fill bug the alpha.2 probe made obvious: the native tab body host is a block scroller with a definite height, so tab roots that only declared `flex: 1` collapsed to content height and the side chat composer drifted off the pane bottom. The native adapter now wraps every tab body in a `height: 100%` column flex host. Also promotes the dependencies `dsh-client-ui-primitives@0.1.5-alpha.2` dropped from its manifest (anser / shiki / mdast-util-* / micromark-* / katex / @shikijs/langs) and the newly unmet `dsh-session-persistence` peer into devDependencies.
This commit is contained in:
@@ -43,7 +43,7 @@ interface NativeTabRegistry {
|
||||
priority?: 'extension' | 'builtin' | 'fallback'
|
||||
canOpen?: (address: string) => boolean
|
||||
title: (address: string) => string
|
||||
guide?: readonly { order: number; title: () => string; description: () => string; icon?: unknown }[]
|
||||
guide?: readonly { order: number; title: () => string; icon?: unknown }[]
|
||||
}): () => void
|
||||
}
|
||||
|
||||
@@ -63,17 +63,6 @@ function titleOf(descriptor: TabDescriptor): string {
|
||||
return typeof descriptor.title === 'function' ? descriptor.title() : descriptor.title
|
||||
}
|
||||
|
||||
/**
|
||||
* The descriptor's guide line, evaluated fresh for the current locale. The
|
||||
* host's new-tab list renders one line per entry; without a declared
|
||||
* description every plugin page would read the same generic sentence.
|
||||
*/
|
||||
function descriptionOf(descriptor: TabDescriptor | undefined): string {
|
||||
const description = descriptor?.description
|
||||
if (description === undefined) return t('nativeGuideDesc')
|
||||
return typeof description === 'function' ? description() : description
|
||||
}
|
||||
|
||||
/**
|
||||
* The guide row's glyph for a descriptor icon (nothing when it has none).
|
||||
* The native guide renders `entry.icon`, so a takeover registered without one
|
||||
@@ -192,7 +181,6 @@ export function registerNativeSurface(deps: NativeSurfaceDeps): () => void {
|
||||
guide: [{
|
||||
order: descriptor.order ?? 100,
|
||||
title: () => titleOf(descriptor),
|
||||
description: () => descriptionOf(descriptor),
|
||||
...guideIconOf(icon),
|
||||
}],
|
||||
}),
|
||||
@@ -219,7 +207,6 @@ export function registerNativeSurface(deps: NativeSurfaceDeps): () => void {
|
||||
guide: [{
|
||||
order: 10,
|
||||
title: () => t('files'),
|
||||
description: () => descriptionOf(editor),
|
||||
// The takeover IS the editor descriptor's page, so it carries the
|
||||
// editor's glyph: without it the "Files" row is the only guide
|
||||
// entry with an empty icon slot.
|
||||
|
||||
@@ -281,33 +281,49 @@ export function NativeTabBody(props: NativeBodyInjected & NativeBodyFrameworkPro
|
||||
})
|
||||
useEffect(() => () => { records.drop(nativeTab.id) }, [records, nativeTab.id])
|
||||
if (descriptor === undefined) {
|
||||
return createElement(OrphanedTab, { ctx, store, scope, tab: view.tab, visible: nativeTab.visible })
|
||||
// The orphaned fallback sits in the SAME native host as a live body, so
|
||||
// it gets the same full-height box (its own root also relies on the
|
||||
// `flex: 1` contract the column host restores). The host div carries
|
||||
// `data-dsh-native-tab-host` (empty value) so the e2e lane can assert
|
||||
// the fill, mirroring `data-dsh-better-sidebar`.
|
||||
return createElement(
|
||||
'div',
|
||||
{ className: css.nativeTabHost, 'data-dsh-native-tab-host': '' },
|
||||
createElement(OrphanedTab, { ctx, store, scope, tab: view.tab, visible: nativeTab.visible }),
|
||||
)
|
||||
}
|
||||
return createElement(
|
||||
RenderBoundary,
|
||||
{ className: css.tabBoundaryError },
|
||||
createElement(descriptor.component, {
|
||||
ctx,
|
||||
store,
|
||||
scope,
|
||||
tab: view.tab,
|
||||
visible: nativeTab.visible,
|
||||
expanded: view.expanded,
|
||||
revealed: view.revealed,
|
||||
onToggleDir: (path: string) => { records.toggleExpanded(nativeTab.id, path) },
|
||||
onReferenceFile: (path: string, isDir: boolean) => { referenceInChat(ctx, sessionId, cwd, path, isDir) },
|
||||
onOpenDiff: (tab: SidebarTab) => {
|
||||
service.openTab({
|
||||
type: 'diff',
|
||||
title: tab.title,
|
||||
id: tab.id,
|
||||
...(tab.diff === undefined ? {} : { diff: tab.diff }),
|
||||
}, scope)
|
||||
},
|
||||
onSubagentJump: (childSessionId: string) => {
|
||||
service.openTab({ type: 'subagent', meta: { childSessionId } }, scope)
|
||||
},
|
||||
}),
|
||||
// The full-height host wrapper (see the `.nativeTabHost` rule in
|
||||
// sidebar.module.css for the native `.paneBody` contract); its
|
||||
// `data-dsh-native-tab-host` attribute lets the e2e lane assert the fill.
|
||||
createElement(
|
||||
'div',
|
||||
{ className: css.nativeTabHost, 'data-dsh-native-tab-host': '' },
|
||||
createElement(descriptor.component, {
|
||||
ctx,
|
||||
store,
|
||||
scope,
|
||||
tab: view.tab,
|
||||
visible: nativeTab.visible,
|
||||
expanded: view.expanded,
|
||||
revealed: view.revealed,
|
||||
onToggleDir: (path: string) => { records.toggleExpanded(nativeTab.id, path) },
|
||||
onReferenceFile: (path: string, isDir: boolean) => { referenceInChat(ctx, sessionId, cwd, path, isDir) },
|
||||
onOpenDiff: (tab: SidebarTab) => {
|
||||
service.openTab({
|
||||
type: 'diff',
|
||||
title: tab.title,
|
||||
id: tab.id,
|
||||
...(tab.diff === undefined ? {} : { diff: tab.diff }),
|
||||
}, scope)
|
||||
},
|
||||
onSubagentJump: (childSessionId: string) => {
|
||||
service.openTab({ type: 'subagent', meta: { childSessionId } }, scope)
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user