From b56ceec41dcf899cc495bcee9c6d967969b879d3 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Wed, 9 Sep 2026 15:06:31 +0800 Subject: [PATCH] fix(native): offer one "Files" row in the new-tab list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The editor type's page identity IS the `files` kind takeover (same explorer, same title), so the guide/new-tab list showed two identical "Files" entries — observed on a real profile after the takeover landed. The editor type now contributes no guide entry of its own; it stays registered as the file RESOURCE viewer. Covered by the registration lifecycle test (editor: no guide, files takeover: guide). --- src/client/native/index.ts | 6 +++++- tests/native-surface.spec.ts | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/client/native/index.ts b/src/client/native/index.ts index 77014a0..224c3b3 100644 --- a/src/client/native/index.ts +++ b/src/client/native/index.ts @@ -160,7 +160,11 @@ export function registerNativeSurface(deps: NativeSurfaceDeps): () => void { // A resource tab is titled by the file it shows; a page tab keeps the // descriptor's own title. title: (address: string) => (isEditor ? fileTitleOf(address) ?? titleOf(descriptor) : titleOf(descriptor)), - ...(descriptor.hidden === true + // No guide entry for the editor: its page identity is the `files` + // kind takeover below (same view, same title), so listing both would + // offer the reader two identical "Files" rows. The type itself stays + // registered as the file RESOURCE viewer. + ...(descriptor.hidden === true || isEditor ? {} : { guide: [{ diff --git a/tests/native-surface.spec.ts b/tests/native-surface.spec.ts index 05978b6..58258f2 100644 --- a/tests/native-surface.spec.ts +++ b/tests/native-surface.spec.ts @@ -166,12 +166,12 @@ describe('registerNativeSurface lifecycle (service-driven registration)', () => service.registerTab({ id: 'editor', title: 'Files', component: () => null }) const records = createNativeTabRecords() - const registered: Array<{ id: string; kind: string; title: (address: string) => string }> = [] + const registered: Array<{ id: string; kind: string; title: (address: string) => string; guide: unknown }> = [] const slotKeys: string[] = [] // The registry is ABSENT while the slot callback fires and appears later // (that ordering is the regression): a holder keeps the timing honest // without a reassigned binding. - const registry: { current: { register: (definition: { id: string; kind: string; title: (address: string) => string }) => () => void } | undefined } = { current: undefined } + const registry: { current: { register: (definition: { id: string; kind: string; title: (address: string) => string; guide?: unknown }) => () => void } | undefined } = { current: undefined } let runInjected: (() => void) | undefined const ctx = { @@ -200,7 +200,7 @@ describe('registerNativeSurface lifecycle (service-driven registration)', () => // …and everything registers once it appears. registry.current = { register: (definition) => { - registered.push({ id: definition.id, kind: definition.kind, title: definition.title }) + registered.push({ id: definition.id, kind: definition.kind, title: definition.title, guide: definition.guide }) return () => {} }, } @@ -217,6 +217,12 @@ describe('registerNativeSurface lifecycle (service-driven registration)', () => expect(editorType?.title('dsh-resource://file/session/s1/src/main.ts')).toBe('main.ts') expect(editorType?.title('dsh-resource://file/absolute/work/pkg/a/b.txt')).toBe('b.txt') expect(editorType?.title('sidebar://editor')).toBe('Files') + // The new-tab/guide list must offer ONE "Files" row: the `files` kind + // takeover draws the same explorer the editor page would, so the editor + // type contributes no guide entry of its own. + expect(editorType?.guide).toBeUndefined() + const filesType = registered.find(entry => entry.kind === 'files') + expect(filesType?.guide).toBeDefined() dispose() })