Skip to content

Commit d38fd4f

Browse files
committed
feat: route session plugin preview to wails design system
1 parent 5c0ce9a commit d38fd4f

8 files changed

Lines changed: 153 additions & 81 deletions

File tree

docs-linhay/dev/20260528-session-management-plugin-system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
## 设计系统收编
4141
- 会话插件控制台已进入设计系统,组件为 `frontend/src/features/session-management/components/SessionPluginConsolePanel.tsx`
42-
- Storybook 入口为 `frontend/src/features/session-management/components/SessionPluginConsolePanel.stories.tsx`,标题为 `Design System/业务组件/会话插件控制台`
42+
- 业务设计系统入口为 Wails 开发服务 `http://localhost:5173/#frame=design-system`,由 `frontend/src/features/design-system/businessComponentPreviews.tsx` 直接渲染,不进入 6006 Storybook
4343
- 组件覆盖 `ready / running / done` 三种执行态,并把插件注册表、作用域选择、执行状态、会话选择、执行队列和插件输出作为同一业务组件约束。
4444
- space HTML 设计稿保留为源草图,后续实现应优先对齐设计系统组件,而不是继续扩展独立 HTML。
4545

docs-linhay/memory/2026-05-28.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
- `session-management` 的一期设计稿已补齐,文件为 `docs-linhay/spaces/20260527-session-jieba-analysis/session-plugin-console-design-v01.html`
2828
- 设计方向收敛为“密集型插件控制台”,首屏包含插件注册表、分析作用域、执行状态、会话列表、执行队列和插件输出六个区块。
2929
- 已完成桌面和 375px 窄屏检查,横向未溢出;截图已归档到 `docs-linhay/spaces/20260527-session-jieba-analysis/screenshots/20260528/session-management/`
30-
- 设计稿已进入设计系统:新增 `SessionPluginConsolePanel` 纯展示组件和 `Design System/业务组件/会话插件控制台` Storybook story,并同步到 `storyCatalog``componentManifest`
30+
- 设计稿已进入 Wails 5173 应用内设计系统:新增 `SessionPluginConsolePanel` 纯展示组件,并通过 `businessComponentPreviews.tsx``http://localhost:5173/#frame=design-system` 直接渲染;不进入 6006 Storybook。
31+
- 5173 验收截图为 `docs-linhay/spaces/20260527-session-jieba-analysis/screenshots/20260528/session-management/20260528-session-plugin-console-5173-design-system-after-v01.png`;DOM 验收确认业务组件预览存在,`ready/running/done` 三态各渲染一个 `SessionPluginConsolePanel`
3132
- 账号详情里的 `AccountCredentialsSection``AccountVerifySection` 已合并为单个 `AccountCredentialVerifySection`,模块计划也同步去掉了独立 `verify` 段。
3233

3334
## cc-switch deep link 设计

docs-linhay/spaces/20260527-session-jieba-analysis/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232

3333
## 设计稿入口
3434

35-
- 设计系统入口:`frontend/src/features/session-management/components/SessionPluginConsolePanel.stories.tsx`
35+
- 设计系统入口:`http://localhost:5173/#frame=design-system` 的“业务组件预览”
3636
- 设计系统组件:`frontend/src/features/session-management/components/SessionPluginConsolePanel.tsx`
37+
- 5173 业务预览注册:`frontend/src/features/design-system/businessComponentPreviews.tsx`
3738
- 源草图:[session-plugin-console-design-v01.html](session-plugin-console-design-v01.html)
3839
- 约束:单期只保留一个 HTML 文件;若存在多稿对比,也必须收敛在同一个 HTML 文件内。
3940

@@ -49,6 +50,7 @@
4950
- 技术说明:[session-management 插件系统设计](../../dev/20260528-session-management-plugin-system.md)
5051
- 迭代计划:[session-management 插件系统路线图](plans/20260528-session-plugin-system-roadmap-v01.md)
5152
- 验收截图:[20260528-session-plugin-host-web-after-v01.png](screenshots/20260528/session-management/20260528-session-plugin-host-web-after-v01.png)
53+
- 5173 设计系统截图:[20260528-session-plugin-console-5173-design-system-after-v01.png](screenshots/20260528/session-management/20260528-session-plugin-console-5173-design-system-after-v01.png)
5254

5355
## 当前状态
5456
- 状态:phase-1-mvp-done / design-system-admitted

frontend/src/features/design-system/DesignSystemEntryFeature.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { BrowserOpenURL } from '../../../wailsjs/runtime/runtime';
33
import WorkspacePageHeader from '../../components/ui/WorkspacePageHeader';
44
import { useI18n } from '../../context/I18nContext';
55
import { hasWailsRuntime } from '../../utils/previewMode';
6+
import {
7+
businessDesignSystemPreviews,
8+
} from './businessComponentPreviews';
9+
import { getBusinessDesignSystemPreviewStats } from './businessComponentPreviewCatalog';
610
import {
711
DESIGN_SYSTEM_SCREENSHOT_PATH,
812
DESIGN_SYSTEM_STORYBOOK_COMMAND,
@@ -17,6 +21,7 @@ import {
1721
export default function DesignSystemEntryFeature() {
1822
const { t } = useI18n();
1923
const stats = getDesignSystemStoryStats();
24+
const businessStats = getBusinessDesignSystemPreviewStats();
2025
const storybookOpenURL = resolveDesignSystemStorybookOpenURL({
2126
origin: typeof window === 'undefined' ? undefined : window.location.origin,
2227
});
@@ -105,10 +110,56 @@ export default function DesignSystemEntryFeature() {
105110
<div className="grid grid-cols-2 gap-3">
106111
<Metric label={t('design_system.groups')} value={stats.groupCount} />
107112
<Metric label={t('design_system.stories')} value={stats.storyCount} />
113+
<Metric label="业务预览" value={businessStats.previewCount} />
114+
<Metric label="业务状态" value={businessStats.stateCount} />
108115
</div>
109116
</aside>
110117
</section>
111118

119+
<section className="grid gap-4">
120+
<div className="card-swiss !p-0">
121+
<div className="border-b-2 border-[var(--border-color)] px-5 py-4">
122+
<div className="flex flex-wrap items-end justify-between gap-3">
123+
<div>
124+
<h3 className="text-xl font-black uppercase italic tracking-normal">业务组件预览</h3>
125+
<p className="mt-1 text-[length:var(--font-size-ui-sm)] font-bold uppercase tracking-normal text-[var(--text-muted)]">
126+
业务组件在 5173 应用内设计系统直接渲染,不进入 6006 Storybook。
127+
</p>
128+
</div>
129+
<span className="border-2 border-[var(--border-color)] px-2 py-1 font-mono text-[length:var(--font-size-ui-xs)] font-black">
130+
{businessDesignSystemPreviews.length} {t('design_system.items')}
131+
</span>
132+
</div>
133+
</div>
134+
<div className="grid gap-6 bg-[var(--bg-surface)] p-5">
135+
{businessDesignSystemPreviews.map((preview) => (
136+
<section key={preview.id} className="grid gap-4">
137+
<div className="grid gap-2 border-2 border-[var(--border-color)] bg-[var(--bg-main)] p-4">
138+
<div className="text-lg font-black uppercase italic tracking-normal">{preview.title}</div>
139+
<p className="text-[length:var(--font-size-ui-sm)] font-bold text-[var(--text-muted)]">
140+
{preview.description}
141+
</p>
142+
<div className="flex flex-wrap gap-2">
143+
{preview.states.map((state) => (
144+
<span
145+
key={`${preview.id}-${state}`}
146+
className="border border-[var(--border-color)] px-2 py-1 text-[length:var(--font-size-ui-2xs)] font-black uppercase tracking-[0.14em]"
147+
>
148+
{state}
149+
</span>
150+
))}
151+
</div>
152+
<code className="break-all font-mono text-[length:var(--font-size-ui-xs)] font-bold text-[var(--text-muted)]">
153+
{preview.sourcePath}
154+
</code>
155+
</div>
156+
<div className="min-w-0">{preview.render()}</div>
157+
</section>
158+
))}
159+
</div>
160+
</div>
161+
</section>
162+
112163
<section className="grid gap-4">
113164
{designSystemStoryGroups.map((group) => (
114165
<div key={group.id} className="card-swiss !p-0">
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export interface BusinessDesignSystemPreviewCatalogEntry {
2+
id: string;
3+
title: string;
4+
description: string;
5+
sourcePath: string;
6+
states: readonly string[];
7+
}
8+
9+
export const businessDesignSystemPreviewCatalog = [
10+
{
11+
id: 'session-plugin-console',
12+
title: '会话插件控制台',
13+
description: '会话插件宿主的业务控制台样板,覆盖插件注册表、执行状态、作用域、队列和输出。',
14+
sourcePath: 'frontend/src/features/session-management/components/SessionPluginConsolePanel.tsx',
15+
states: ['ready', 'running', 'done'],
16+
},
17+
] as const satisfies readonly BusinessDesignSystemPreviewCatalogEntry[];
18+
19+
export function getBusinessDesignSystemPreviewStats(
20+
previews: readonly BusinessDesignSystemPreviewCatalogEntry[] = businessDesignSystemPreviewCatalog,
21+
) {
22+
return {
23+
previewCount: previews.length,
24+
stateCount: previews.reduce((sum, preview) => sum + preview.states.length, 0),
25+
};
26+
}

frontend/src/features/session-management/components/SessionPluginConsolePanel.stories.tsx renamed to frontend/src/features/design-system/businessComponentPreviews.tsx

Lines changed: 34 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1-
import type { Meta, StoryObj } from '@storybook/react-vite';
2-
import DesignSystemStoryFrame from '../../design-system/DesignSystemStoryFrame';
3-
import SessionPluginConsolePanel from './SessionPluginConsolePanel';
1+
import type { ReactNode } from 'react';
2+
import SessionPluginConsolePanel from '../session-management/components/SessionPluginConsolePanel';
43
import type {
54
SessionPluginConsoleMode,
65
SessionPluginConsolePanelProps,
7-
} from './SessionPluginConsolePanel';
6+
} from '../session-management/components/SessionPluginConsolePanel';
7+
import {
8+
businessDesignSystemPreviewCatalog,
9+
type BusinessDesignSystemPreviewCatalogEntry,
10+
} from './businessComponentPreviewCatalog';
811

9-
const meta = {
10-
title: 'Design System/业务组件/会话插件控制台',
11-
parameters: {
12-
layout: 'fullscreen',
13-
},
14-
} satisfies Meta;
15-
16-
export default meta;
17-
type Story = StoryObj;
12+
export interface BusinessDesignSystemPreview extends BusinessDesignSystemPreviewCatalogEntry {
13+
render: () => ReactNode;
14+
}
1815

19-
const commonConsoleProps: Omit<SessionPluginConsolePanelProps, 'mode' | 'execution'> = {
16+
const sessionPluginConsoleCommonProps: Omit<SessionPluginConsolePanelProps, 'mode' | 'execution'> = {
2017
pluginHostTitle: '会话插件',
2118
pluginHostSubtitle: 'CODEX / SESSION MANAGEMENT / 426 sessions / 19 projects',
2219
pluginHint: '会话深度分析作为第一个内置插件,后续复盘、对比和导出复用同一宿主协议。',
@@ -66,22 +63,9 @@ const commonConsoleProps: Omit<SessionPluginConsolePanelProps, 'mode' | 'executi
6663
},
6764
],
6865
scopes: [
69-
{
70-
id: 'project',
71-
title: '当前项目',
72-
subtitle: 'GetTokens / 86 sessions',
73-
active: true,
74-
},
75-
{
76-
id: 'recent',
77-
title: '最近 20 条',
78-
subtitle: 'visible sessions slice',
79-
},
80-
{
81-
id: 'all',
82-
title: '全部会话',
83-
subtitle: '426 sessions, async batch',
84-
},
66+
{ id: 'project', title: '当前项目', subtitle: 'GetTokens / 86 sessions', active: true },
67+
{ id: 'recent', title: '最近 20 条', subtitle: 'visible sessions slice' },
68+
{ id: 'all', title: '全部会话', subtitle: '426 sessions, async batch' },
8569
],
8670
sessions: [
8771
{
@@ -163,7 +147,7 @@ const commonConsoleProps: Omit<SessionPluginConsolePanelProps, 'mode' | 'executi
163147
],
164148
};
165149

166-
const executions: Record<SessionPluginConsoleMode, SessionPluginConsolePanelProps['execution']> = {
150+
const sessionPluginConsoleExecutions: Record<SessionPluginConsoleMode, SessionPluginConsolePanelProps['execution']> = {
167151
ready: {
168152
dialLabel: '0%',
169153
progress: 0,
@@ -188,53 +172,33 @@ const executions: Record<SessionPluginConsoleMode, SessionPluginConsolePanelProp
188172
},
189173
};
190174

191-
function buildConsoleProps(mode: SessionPluginConsoleMode): SessionPluginConsolePanelProps {
175+
function buildSessionPluginConsoleProps(mode: SessionPluginConsoleMode): SessionPluginConsolePanelProps {
192176
return {
193-
...commonConsoleProps,
177+
...sessionPluginConsoleCommonProps,
194178
mode,
195-
execution: executions[mode],
179+
execution: sessionPluginConsoleExecutions[mode],
196180
actionStatusLabel: mode,
197181
};
198182
}
199183

200-
function ConsoleSample({ label, mode }: { label: string; mode: SessionPluginConsoleMode }) {
201-
return (
202-
<DesignSystemStoryFrame label={label}>
203-
<div className="min-w-0 bg-[var(--bg-surface)] p-5">
204-
<SessionPluginConsolePanel {...buildConsoleProps(mode)} />
205-
</div>
206-
</DesignSystemStoryFrame>
207-
);
208-
}
209-
210-
function SessionPluginConsoleOverview() {
184+
function SessionPluginConsolePreview() {
211185
return (
212-
<div className="grid gap-6 bg-[var(--bg-surface)] p-6">
213-
<div>
214-
<h2 className="text-2xl font-black uppercase italic tracking-normal">会话插件控制台</h2>
215-
<p className="mt-2 max-w-4xl text-[length:var(--font-size-ui-md)] font-bold leading-relaxed text-[var(--text-muted)]">
216-
会话插件控制台把插件注册表、分析作用域、执行状态、会话选择、队列和结果输出收敛到一个业务组件,用于后续替换 session-management 的临时分析面板。
217-
</p>
218-
</div>
219-
<ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-READY" mode="ready" />
220-
<ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-RUNNING" mode="running" />
221-
<ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-DONE" mode="done" />
186+
<div className="grid gap-5">
187+
{(['ready', 'running', 'done'] as const).map((mode) => (
188+
<section key={mode} className="grid gap-3">
189+
<div className="text-[length:var(--font-size-ui-xs)] font-black uppercase tracking-[0.18em] text-[var(--text-muted)]">
190+
DS-SESSION-PLUGIN-CONSOLE-{mode.toUpperCase()}
191+
</div>
192+
<SessionPluginConsolePanel {...buildSessionPluginConsoleProps(mode)} />
193+
</section>
194+
))}
222195
</div>
223196
);
224197
}
225198

226-
export const Overview: Story = {
227-
render: () => <SessionPluginConsoleOverview />,
228-
};
229-
230-
export const Ready: Story = {
231-
render: () => <ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-READY" mode="ready" />,
232-
};
233-
234-
export const Running: Story = {
235-
render: () => <ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-RUNNING" mode="running" />,
236-
};
237-
238-
export const Done: Story = {
239-
render: () => <ConsoleSample label="DS-SESSION-PLUGIN-CONSOLE-DONE" mode="done" />,
240-
};
199+
export const businessDesignSystemPreviews = [
200+
{
201+
...businessDesignSystemPreviewCatalog[0],
202+
render: () => <SessionPluginConsolePreview />,
203+
},
204+
] as const satisfies readonly BusinessDesignSystemPreview[];

frontend/src/features/design-system/componentManifest.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export interface DesignSystemComponentManifestEntry {
1313
suggestedDesignComponent?: string;
1414
storyPath?: string;
1515
storybookTitle?: string;
16+
previewPath?: string;
17+
previewTitle?: string;
1618
catalogGroupId?: string;
1719
requiredStates?: readonly string[];
1820
mockDataSources?: readonly string[];
@@ -79,8 +81,6 @@ const codexAccountOrderStoryPath = 'frontend/src/features/codex/components/Codex
7981
const codexAccountOrderStorybookTitle = 'Design System/业务组件/Codex 账号顺序';
8082
const codexLiveSessionsStoryPath = 'frontend/src/features/codex-live-sessions/components/CodexLiveSessionsComponents.stories.tsx';
8183
const codexLiveSessionsStorybookTitle = 'Design System/业务组件/Codex 运行会话';
82-
const sessionPluginConsoleStoryPath = 'frontend/src/features/session-management/components/SessionPluginConsolePanel.stories.tsx';
83-
const sessionPluginConsoleStorybookTitle = 'Design System/业务组件/会话插件控制台';
8484

8585
export const designSystemComponentManifest = [
8686
{
@@ -871,11 +871,11 @@ export const designSystemComponentManifest = [
871871
tier: 'feature-component',
872872
decisionReason: '会话插件控制台已从 space HTML 设计稿收编为纯展示组件,覆盖插件注册表、作用域选择、执行状态、会话选择、队列和插件输出,作为 session-management 插件宿主的设计系统基准。',
873873
matchedPatterns: ['DesignSystemStoryFrame', 'WorkspacePageHeader', 'DebugPanel grouped list'],
874-
storyPath: sessionPluginConsoleStoryPath,
875-
storybookTitle: sessionPluginConsoleStorybookTitle,
874+
previewPath: 'frontend/src/features/design-system/businessComponentPreviews.tsx',
875+
previewTitle: '5173/业务组件/会话插件控制台',
876876
catalogGroupId: 'feature-components',
877877
requiredStates: ['ready', 'running', 'done', 'scope-project', 'queue', 'analysis-result'],
878-
mockDataSources: ['storybook session-management plugin/analysis/session mock'],
878+
mockDataSources: ['5173 businessComponentPreviews session-management plugin/analysis/session mock'],
879879
},
880880
{
881881
id: 'codex-model-combobox',

frontend/src/features/design-system/storyCatalog.test.mjs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import {
88
designSystemComponentManifest,
99
getAdmittedDesignSystemComponentManifest,
1010
} from './componentManifest.ts';
11+
import {
12+
businessDesignSystemPreviewCatalog,
13+
getBusinessDesignSystemPreviewStats,
14+
} from './businessComponentPreviewCatalog.ts';
1115
import {
1216
DESIGN_SYSTEM_INSPECT_QUERY_VALUE,
1317
DESIGN_SYSTEM_STORYBOOK_DEV_OPEN_PATH,
@@ -105,10 +109,13 @@ test('design system story groups are stable and populated', () => {
105109
test('design system story stats match flattened catalog', () => {
106110
const stories = flattenDesignSystemStories(designSystemStoryGroups);
107111
const stats = getDesignSystemStoryStats(designSystemStoryGroups);
112+
const businessStats = getBusinessDesignSystemPreviewStats();
108113

109114
assert.equal(stories.length, stats.storyCount);
110115
assert.equal(designSystemStoryGroups.length, stats.groupCount);
111116
assert.ok(stats.storyCount >= 10);
117+
assert.ok(businessStats.previewCount >= 1);
118+
assert.ok(businessStats.stateCount >= businessStats.previewCount);
112119
});
113120

114121
test('storybook public catalog excludes full business components', async () => {
@@ -351,19 +358,40 @@ test('feature component manifest covers extracted feature component files', asyn
351358
}
352359
});
353360

354-
test('admitted feature component manifest entries stay internal to feature-owned stories', async () => {
361+
test('admitted feature component manifest entries stay internal to feature-owned stories or 5173 previews', async () => {
355362
const featureComponentGroup = getCatalogGroup('feature-components');
356363
assert.ok(featureComponentGroup);
357364

358365
const catalogStoriesByPath = new Map(featureComponentGroup.stories.map((story) => [story.path, story]));
366+
const businessPreviewsByPath = new Map(businessDesignSystemPreviewCatalog.map((preview) => [preview.sourcePath, preview]));
359367
const admittedStories = new Set();
360368

361369
for (const entry of getAdmittedDesignSystemComponentManifest()) {
362370
assert.equal(entry.catalogGroupId, 'feature-components', `${entry.id} must target feature-components`);
363-
assert.ok(entry.storyPath, `${entry.id} must provide a story path`);
364-
assert.ok(entry.storybookTitle, `${entry.id} must provide a Storybook title`);
371+
assert.ok(entry.storyPath || entry.previewPath, `${entry.id} must provide a story path or 5173 preview path`);
365372
assert.ok(entry.requiredStates?.length > 0, `${entry.id} must document admitted states`);
366373
assert.ok(entry.mockDataSources?.length > 0, `${entry.id} must document mock data`);
374+
375+
if (entry.previewPath) {
376+
assert.ok(entry.previewTitle, `${entry.id} must provide a 5173 preview title`);
377+
assert.equal(entry.previewPath, 'frontend/src/features/design-system/businessComponentPreviews.tsx');
378+
assert.match(entry.previewTitle, /^5173\/\//);
379+
assert.ok(businessPreviewsByPath.has(entry.sourcePath), `${entry.sourcePath} must render in 5173 business previews`);
380+
const preview = businessPreviewsByPath.get(entry.sourcePath);
381+
for (const state of entry.requiredStates) {
382+
if (state === 'scope-project' || state === 'queue' || state === 'analysis-result') {
383+
continue;
384+
}
385+
assert.ok(preview?.states.includes(state), `${entry.id} preview must include state: ${state}`);
386+
}
387+
const previewFile = entry.previewPath.replace('frontend/src/', '../../');
388+
const previewSource = await readFile(new URL(previewFile, import.meta.url), 'utf8');
389+
assert.doesNotMatch(previewSource, /wailsjs|window\.go|sidecar|fetch\(/, `${entry.previewPath} must use mock data only`);
390+
continue;
391+
}
392+
393+
assert.ok(entry.storyPath, `${entry.id} must provide a story path`);
394+
assert.ok(entry.storybookTitle, `${entry.id} must provide a Storybook title`);
367395
assert.match(entry.storyPath, /^frontend\/src\/features\//, `${entry.storyPath} must remain feature-owned`);
368396
assert.match(entry.storybookTitle, /^Design System\/\//, `${entry.storybookTitle} remains an internal business story title`);
369397

0 commit comments

Comments
 (0)