From 23780bd694aa9875d59dedc9299adc575babab00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E5=85=AC?= Date: Fri, 11 Sep 2026 16:38:02 +0800 Subject: [PATCH] feat(web-shell): hide sources panel add-source button behind URL flag The "+" button in the sources panel header serves no practical purpose in the current product flow, so it is now hidden by default. The code path is kept intact and can be re-enabled by appending ?addSource=1 to the Web Shell URL, following the existing ?composer= escape-hatch pattern. Existing EnvironmentPanel tests opt into the flag in a beforeEach; new collocated tests cover both the hidden default and the enabled state. --- .../panels/EnvironmentPanel.test.tsx | 8 ++- .../components/panels/SourcesSection.test.tsx | 62 +++++++++++++++++++ .../components/panels/SourcesSection.tsx | 9 ++- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 packages/web-shell/client/components/panels/SourcesSection.test.tsx diff --git a/packages/web-shell/client/components/panels/EnvironmentPanel.test.tsx b/packages/web-shell/client/components/panels/EnvironmentPanel.test.tsx index 2d74dd95ddd..f4368a75d1c 100644 --- a/packages/web-shell/client/components/panels/EnvironmentPanel.test.tsx +++ b/packages/web-shell/client/components/panels/EnvironmentPanel.test.tsx @@ -3,7 +3,7 @@ import type { DaemonSessionTaskStatus } from '@qwen-code/sdk/daemon'; import { act, type ReactNode } from 'react'; import { createRoot, type Root } from 'react-dom/client'; -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { I18nProvider } from '../../i18n'; import { EnvironmentPanel } from './EnvironmentPanel'; import type { SourcesState } from './SourcesSection'; @@ -44,11 +44,17 @@ Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }); let container: HTMLDivElement | null = null; let root: Root | null = null; +beforeEach(() => { + // The add-source button is hidden by default; enable it for these tests. + window.history.replaceState({}, '', '/?addSource=1'); +}); + afterEach(() => { act(() => root?.unmount()); container?.remove(); container = null; root = null; + window.history.replaceState({}, '', '/'); }); function mount( diff --git a/packages/web-shell/client/components/panels/SourcesSection.test.tsx b/packages/web-shell/client/components/panels/SourcesSection.test.tsx new file mode 100644 index 00000000000..c7555b47129 --- /dev/null +++ b/packages/web-shell/client/components/panels/SourcesSection.test.tsx @@ -0,0 +1,62 @@ +// @vitest-environment jsdom + +import { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { I18nProvider } from '../../i18n'; +import { SourcesSection, type SourcesState } from './SourcesSection'; + +Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }); + +let container: HTMLDivElement | null = null; +let root: Root | null = null; + +afterEach(() => { + act(() => root?.unmount()); + container?.remove(); + container = null; + root = null; + window.history.replaceState({}, '', '/'); +}); + +function supportedState(): SourcesState { + return { + supported: true, + sources: [], + owner: { isCurrent: () => true }, + revision: 1, + hydrated: true, + loading: false, + error: null, + refresh: vi.fn(), + upsert: vi.fn(), + remove: vi.fn(), + }; +} + +function mount(): HTMLDivElement { + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + act(() => { + root!.render( + + + , + ); + }); + return container; +} + +describe('SourcesSection add-source button', () => { + it('is hidden by default', () => { + const el = mount(); + expect(el.querySelector('button[aria-label="Add source"]')).toBeNull(); + }); + + it('appears when the addSource URL parameter is set', () => { + window.history.replaceState({}, '', '/?addSource=1'); + const el = mount(); + expect(el.querySelector('button[aria-label="Add source"]')).not.toBeNull(); + }); +}); diff --git a/packages/web-shell/client/components/panels/SourcesSection.tsx b/packages/web-shell/client/components/panels/SourcesSection.tsx index 173d25cedea..81393fac7c9 100644 --- a/packages/web-shell/client/components/panels/SourcesSection.tsx +++ b/packages/web-shell/client/components/panels/SourcesSection.tsx @@ -18,6 +18,13 @@ import styles from './EnvironmentPanel.module.css'; export type SourcesState = ReturnType; +// Escape hatch: the add-source affordance is hidden by default; append +// ?addSource=1 to the URL to bring it back. +function isAddSourceEnabled(): boolean { + if (typeof window === 'undefined') return false; + return new URLSearchParams(window.location.search).get('addSource') === '1'; +} + interface SourcesSectionProps { hidden?: boolean; state?: SourcesState; @@ -97,7 +104,7 @@ export function SourcesSection({ {t('sources.title')}{' '} {entries.length} - {state?.supported && ( + {state?.supported && isAddSourceEnabled() && (