feat(guide): let a tab type declare its own line in the new-tab list

The native new-tab list rendered one generic line ("opens in the DSH
sidebar") under every plugin entry, so six plugin rows read identically.
`TabDescriptor.description` now carries the real purpose (string or
locale-following thunk; absent falls back to the generic line), and the
six visible builtins declare theirs across all 20 dictionaries.

- service.ts: `TabDescriptor.description` + docs (guide §4.1, §0 row).
- native/: the guide entry's description follows the descriptor; the
  `files` takeover reuses the editor's line.
- builtins: files / changes / tasks / side chat / terminal / browser.
- tests: builtins assert every visible type declares a distinct line;
  the lifecycle test asserts descriptor lines (and the takeover reuse).
This commit is contained in:
Menghuan1918
2026-09-09 22:25:59 +08:00
parent 9ebaa09f80
commit d4765819c0
26 changed files with 186 additions and 5 deletions
+13 -2
View File
@@ -63,6 +63,17 @@ 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
@@ -181,7 +192,7 @@ export function registerNativeSurface(deps: NativeSurfaceDeps): () => void {
guide: [{
order: descriptor.order ?? 100,
title: () => titleOf(descriptor),
description: () => t('nativeGuideDesc'),
description: () => descriptionOf(descriptor),
...guideIconOf(icon),
}],
}),
@@ -208,7 +219,7 @@ export function registerNativeSurface(deps: NativeSurfaceDeps): () => void {
guide: [{
order: 10,
title: () => t('files'),
description: () => t('nativeGuideDesc'),
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.