@changfenhuang/dsh-genui 0.9.2 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +11 -0
- package/README.zh-CN.md +17 -0
- package/lib/client.js +3 -3
- package/lib/index.js +11 -4
- package/lib/types/client/TemplateDrawer.d.ts +7 -0
- package/lib/types/client/achievement-store.d.ts +35 -0
- package/lib/types/client/achievement-toast.d.ts +2 -0
- package/lib/types/client/achievements.d.ts +48 -0
- package/lib/types/client/panel.d.ts +4 -1
- package/lib/types/client/templates.d.ts +22 -0
- package/package.json +1 -1
- package/src/client/GenuiBlock.module.css +151 -14
- package/src/client/GenuiBlock.tsx +16 -1
- package/src/client/PlotBlock.tsx +23 -10
- package/src/client/TemplateDrawer.module.css +152 -0
- package/src/client/TemplateDrawer.tsx +137 -0
- package/src/client/achievement-store.ts +151 -0
- package/src/client/achievement-toast.tsx +68 -0
- package/src/client/achievements.ts +111 -0
- package/src/client/blocks/advanced.tsx +36 -4
- package/src/client/guard.ts +20 -4
- package/src/client/index.tsx +13 -0
- package/src/client/panel.tsx +109 -10
- package/src/client/templates.ts +280 -0
package/src/client/panel.tsx
CHANGED
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
|
27
27
|
import type {} from '@deepseek-ai/dsh-client-runtime/client'
|
|
28
|
+
import { IconChevronDownOutline14, IconChevronUpOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
28
29
|
import { GenuiActionContext, type GenuiActionHandler } from './action-context.ts'
|
|
29
30
|
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
30
31
|
import { GenuiBlock } from './GenuiBlock.tsx'
|
|
31
32
|
import { ErrorBoundary } from './ErrorBoundary.tsx'
|
|
32
33
|
import { panelStateKey } from './interaction-store.ts'
|
|
34
|
+
import { TemplateDrawer } from './TemplateDrawer.tsx'
|
|
33
35
|
import { clearSessionPanel, getPanelExpandToken, getPanelSpec, setLocalPanel, subscribePanel, subscribePanelExpand } from './panel-store.ts'
|
|
34
36
|
import css from './GenuiBlock.module.css'
|
|
35
37
|
|
|
@@ -37,10 +39,20 @@ import css from './GenuiBlock.module.css'
|
|
|
37
39
|
const PANEL_HEIGHT_MIN = 120
|
|
38
40
|
const PANEL_HEIGHT_MAX = 600
|
|
39
41
|
|
|
42
|
+
/** First-panel hint dismissal flag (module-level localStorage key):
|
|
43
|
+
* shown once beside the first rendered spec, never again. */
|
|
44
|
+
const ONBOARD_KEY = 'dsh.genui.panel-hint'
|
|
45
|
+
|
|
46
|
+
// Achievement telemetry re-exports (already injected at apply).
|
|
47
|
+
import { recordPanel, recordTemplateUse } from './achievement-store.ts'
|
|
48
|
+
|
|
40
49
|
/** Injection face built per session in apply (scoped conversation send). */
|
|
41
50
|
export interface GenuiPanelInjected {
|
|
42
51
|
sessionId: string
|
|
43
52
|
sendGenuiAction: GenuiActionHandler
|
|
53
|
+
/** Template center "try it": insert the template instruction into the
|
|
54
|
+
* current composer draft (standard conversation.input.for channel). */
|
|
55
|
+
insertTemplate: (text: string) => void
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
export type GenuiPanelProps = PropsRuntime<'conversation.input.dock'> & GenuiPanelInjected
|
|
@@ -51,7 +63,7 @@ export type GenuiPanelProps = PropsRuntime<'conversation.input.dock'> & GenuiPan
|
|
|
51
63
|
* default so the dock never steals the message flow's scroll room; the
|
|
52
64
|
* header always shows the current panel title.
|
|
53
65
|
*/
|
|
54
|
-
export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
66
|
+
export function GenuiPanel({ sessionId, sendGenuiAction, insertTemplate }: GenuiPanelProps) {
|
|
55
67
|
const spec = useSyncExternalStore(subscribePanel, () => getPanelSpec(sessionId))
|
|
56
68
|
const expandToken = useSyncExternalStore(subscribePanelExpand, () => getPanelExpandToken(sessionId))
|
|
57
69
|
const [collapsed, setCollapsed] = useState(true)
|
|
@@ -60,6 +72,39 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
60
72
|
const [resizing, setResizing] = useState(false)
|
|
61
73
|
const dragStart = useRef<{ y: number; height: number } | null>(null)
|
|
62
74
|
const bodyRef = useRef<HTMLDivElement | null>(null)
|
|
75
|
+
// Template center (0.9.4) + achievements (0.9.5): the drawer host —
|
|
76
|
+
// null = closed, 'templates' | 'achievements' = open on that tab.
|
|
77
|
+
const [drawer, setDrawer] = useState<null | 'templates' | 'achievements'>(null)
|
|
78
|
+
const [flash, setFlash] = useState<string | null>(null)
|
|
79
|
+
const flashTimer = useRef(0)
|
|
80
|
+
const hasSpec = spec !== null && spec.items.length > 0
|
|
81
|
+
// First-panel hint: shown once beside the first rendered spec (never on the
|
|
82
|
+
// hero — the host composer hero flex-compresses dock items with no content,
|
|
83
|
+
// and the template button is a permanent header entry anyway).
|
|
84
|
+
const [hint, setHint] = useState<boolean>(() => {
|
|
85
|
+
try {
|
|
86
|
+
return hasSpec && localStorage.getItem(ONBOARD_KEY) !== '1'
|
|
87
|
+
} catch {
|
|
88
|
+
return hasSpec
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!hint) return
|
|
93
|
+
const t = window.setTimeout(() => {
|
|
94
|
+
try {
|
|
95
|
+
localStorage.setItem(ONBOARD_KEY, '1')
|
|
96
|
+
} catch {
|
|
97
|
+
// Private-mode storage failures are non-fatal: the hint may reappear.
|
|
98
|
+
}
|
|
99
|
+
setHint(false)
|
|
100
|
+
}, 6000)
|
|
101
|
+
return () => window.clearTimeout(t)
|
|
102
|
+
}, [hint])
|
|
103
|
+
// Achievement telemetry (0.9.5): one panel appearance per dock mount.
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!hasSpec) return
|
|
106
|
+
recordPanel()
|
|
107
|
+
}, [hasSpec])
|
|
63
108
|
|
|
64
109
|
// Explicit expand requests (the /panel command) open the dock even when the
|
|
65
110
|
// user collapsed it manually: the monotone token guarantees a fresh request
|
|
@@ -89,6 +134,9 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
89
134
|
}
|
|
90
135
|
}, [resizing])
|
|
91
136
|
|
|
137
|
+
// Template-center flash timer must not outlive the dock.
|
|
138
|
+
useEffect(() => () => window.clearTimeout(flashTimer.current), [])
|
|
139
|
+
|
|
92
140
|
/** Start a vertical resize drag on the panel's top edge. Dragging UP grows
|
|
93
141
|
* the panel (the edge moves toward the message flow); dragging down shrinks
|
|
94
142
|
* it. Pointer capture binds move/up/cancel to the HANDLE, so dragging past
|
|
@@ -126,7 +174,14 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
126
174
|
handle.addEventListener('pointercancel', onUp)
|
|
127
175
|
}, [bodyHeight])
|
|
128
176
|
|
|
129
|
-
if (
|
|
177
|
+
if (!hasSpec) return null
|
|
178
|
+
|
|
179
|
+
const showFlash = (text: string): void => {
|
|
180
|
+
setFlash(text)
|
|
181
|
+
window.clearTimeout(flashTimer.current)
|
|
182
|
+
flashTimer.current = window.setTimeout(() => setFlash(null), 2500)
|
|
183
|
+
}
|
|
184
|
+
|
|
130
185
|
return (
|
|
131
186
|
<div className={css.panel} data-genui-panel>
|
|
132
187
|
{/* Resize grip on the panel's TOP EDGE (above the header): the drag
|
|
@@ -149,8 +204,33 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
149
204
|
onClick={() => setCollapsed(c => !c)}
|
|
150
205
|
>
|
|
151
206
|
<span className={css.panelBadge}>面板</span>
|
|
152
|
-
<span className={css.panelTitle}>{spec
|
|
153
|
-
<span className={css.panelChevron} aria-hidden>
|
|
207
|
+
<span className={css.panelTitle}>{spec?.title ?? (drawer !== null ? 'GenUI 探索' : 'GenUI 面板')}</span>
|
|
208
|
+
<span className={css.panelChevron} aria-hidden>
|
|
209
|
+
{/* Host-style glyphs (same icon set as the TodoDock header) instead of typed arrows. */}
|
|
210
|
+
{collapsed ? <IconChevronUpOutline14 /> : <IconChevronDownOutline14 />}
|
|
211
|
+
</span>
|
|
212
|
+
</button>
|
|
213
|
+
{/* Template center (0.9.4): one button — the discovery surface for
|
|
214
|
+
new users and a quick-start entry for everyone. */}
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
className={`${css.panelTpl}${drawer === 'templates' ? ` ${css.panelTplActive}` : ''}`}
|
|
218
|
+
aria-label="模板中心"
|
|
219
|
+
title="模板中心:预览并试用 GenUI 示例"
|
|
220
|
+
onClick={() => { setCollapsed(false); setDrawer(d => (d === 'templates' ? null : 'templates')) }}
|
|
221
|
+
>
|
|
222
|
+
模板
|
|
223
|
+
</button>
|
|
224
|
+
{/* Achievements (0.9.5): the exploration trophies, rendered by GenUI
|
|
225
|
+
itself (dogfooding). */}
|
|
226
|
+
<button
|
|
227
|
+
type="button"
|
|
228
|
+
className={`${css.panelTpl}${drawer === 'achievements' ? ` ${css.panelTplActive}` : ''}`}
|
|
229
|
+
aria-label="探索成就"
|
|
230
|
+
title="探索成就:解锁徽章与进度"
|
|
231
|
+
onClick={() => { setCollapsed(false); setDrawer(d => (d === 'achievements' ? null : 'achievements')) }}
|
|
232
|
+
>
|
|
233
|
+
成就
|
|
154
234
|
</button>
|
|
155
235
|
{/* In-place dismiss (issue #23): the same local override `/panel
|
|
156
236
|
clear` applies — persists to localStorage, notifies subscribers,
|
|
@@ -165,6 +245,11 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
165
245
|
<span aria-hidden>✕</span>
|
|
166
246
|
</button>
|
|
167
247
|
</div>
|
|
248
|
+
{hint && drawer === null && (
|
|
249
|
+
<div className={css.panelHint} role="status">
|
|
250
|
+
💡 这是 GenUI 会话面板:点右上角「模板」可浏览并试用示例
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
168
253
|
{!collapsed && (
|
|
169
254
|
<div
|
|
170
255
|
ref={bodyRef}
|
|
@@ -172,14 +257,28 @@ export function GenuiPanel({ sessionId, sendGenuiAction }: GenuiPanelProps) {
|
|
|
172
257
|
data-genui-panel-body
|
|
173
258
|
style={bodyHeight === null ? undefined : { height: bodyHeight }}
|
|
174
259
|
>
|
|
175
|
-
|
|
176
|
-
<
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
260
|
+
{drawer !== null ? (
|
|
261
|
+
<TemplateDrawer
|
|
262
|
+
tab={drawer}
|
|
263
|
+
onUse={(text) => {
|
|
264
|
+
recordTemplateUse()
|
|
265
|
+
insertTemplate(text)
|
|
266
|
+
showFlash('指令已插入输入框,发送即可让模型生成')
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
) : spec !== null ? (
|
|
270
|
+
<GenuiActionContext.Provider value={sendGenuiAction}>
|
|
271
|
+
<ErrorBoundary label="面板">
|
|
272
|
+
{/* content-fingerprinted: same panel spec re-published restores its state */}
|
|
273
|
+
<GenuiBlock spec={spec} stateKey={panelStateKey(sessionId, JSON.stringify(spec))} />
|
|
274
|
+
</ErrorBoundary>
|
|
275
|
+
</GenuiActionContext.Provider>
|
|
276
|
+
) : null}
|
|
181
277
|
</div>
|
|
182
278
|
)}
|
|
279
|
+
{flash !== null && (
|
|
280
|
+
<div className={css.panelFlash} role="status">{flash}</div>
|
|
281
|
+
)}
|
|
183
282
|
</div>
|
|
184
283
|
)
|
|
185
284
|
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* templates.ts — GenUI 模板中心数据(0.9.4)。
|
|
3
|
+
*
|
|
4
|
+
* 每个模板 = 一段自然语言 `instruction`(试用时插入输入框,模型按 genui
|
|
5
|
+
* skill 生成对应界面)+ 一份**合法**的 `demo` spec(预览时由 GenuiBlock
|
|
6
|
+
* 直接渲染,用户先看到效果再决定试用)。字段对齐 spec.ts / guard.ts 的
|
|
7
|
+
* 实际 schema(tests/templates.spec.ts 用 validateGenuiSpec 逐条校验)。
|
|
8
|
+
*
|
|
9
|
+
* 模板即说明书:覆盖布局/数据/图表/交互/测验/高级各能力面。
|
|
10
|
+
*/
|
|
11
|
+
import type { GenuiSpec } from './spec.ts'
|
|
12
|
+
|
|
13
|
+
export interface GenuiTemplate {
|
|
14
|
+
id: string
|
|
15
|
+
category: '仪表盘' | '数据' | '流程' | '图表' | '交互' | '测验' | '高级'
|
|
16
|
+
name: string
|
|
17
|
+
description: string
|
|
18
|
+
/** 试用时插入输入框的自然语言指令。 */
|
|
19
|
+
instruction: string
|
|
20
|
+
/** 预览用的合法 spec(≤200 节点、嵌套 ≤8 层)。 */
|
|
21
|
+
demo: GenuiSpec
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const GENUI_TEMPLATES: readonly GenuiTemplate[] = [
|
|
25
|
+
{
|
|
26
|
+
id: 'tpl-dashboard',
|
|
27
|
+
category: '仪表盘',
|
|
28
|
+
name: '项目仪表盘',
|
|
29
|
+
description: '一张卡片汇总核心指标:数字 + 环比 + 进度条,决策一眼可见。',
|
|
30
|
+
instruction: '请用 dsh-ui 给当前项目做一个仪表盘:4 个关键指标的 stat 卡片(带环比 delta)、整体进度 progress 条、今天的 3 件待办 list。标题用「项目仪表盘」。',
|
|
31
|
+
demo: {
|
|
32
|
+
title: '项目仪表盘(示例)',
|
|
33
|
+
gap: 12,
|
|
34
|
+
items: [
|
|
35
|
+
{ type: 'card', title: '本周关键指标', items: [
|
|
36
|
+
{ type: 'grid', cols: 2, items: [
|
|
37
|
+
{ type: 'stat', label: '本周完成', value: '12 / 18', delta: '+4' },
|
|
38
|
+
{ type: 'stat', label: '阻塞事项', value: '2', delta: '-1' },
|
|
39
|
+
{ type: 'stat', label: '平均耗时', value: '3.2h', delta: '-8%' },
|
|
40
|
+
{ type: 'stat', label: '覆盖率', value: '86%' },
|
|
41
|
+
] },
|
|
42
|
+
] },
|
|
43
|
+
{ type: 'card', title: '整体进度', items: [
|
|
44
|
+
{ type: 'progress', label: 'v1.0 里程碑', value: 68, valueLabel: '68 / 100' },
|
|
45
|
+
] },
|
|
46
|
+
{ type: 'card', title: '今日待办', items: [
|
|
47
|
+
{ type: 'list', items: ['评审 M1 模板中心 PR', '同步 vendor 到 SSiD', '周三周会材料'] },
|
|
48
|
+
] },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'tpl-compare',
|
|
54
|
+
category: '数据',
|
|
55
|
+
name: '方案对比表',
|
|
56
|
+
description: '多方案逐维度对照,表格直接可读,争议点一目了然。',
|
|
57
|
+
instruction: '请用 dsh-ui 把以下方案的对比做成一张 table:方案 A/B/C × 维度(成本/复杂度/风险/收益),列首高亮推荐项;顶部加一行 callout 说明结论。',
|
|
58
|
+
demo: {
|
|
59
|
+
title: '方案对比(示例)',
|
|
60
|
+
items: [
|
|
61
|
+
{ type: 'callout', tone: 'info', title: '结论', content: '方案 B 综合最优:成本中、复杂度低、风险低。' },
|
|
62
|
+
{ type: 'table', columns: ['维度', '方案 A', '方案 B ★', '方案 C'], rows: [
|
|
63
|
+
['成本', '高', '中', '低'],
|
|
64
|
+
['复杂度', '高', '中', '低'],
|
|
65
|
+
['风险', '低', '低', '高'],
|
|
66
|
+
['收益', '大', '中', '小'],
|
|
67
|
+
] },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'tpl-steps',
|
|
73
|
+
category: '流程',
|
|
74
|
+
name: '五步上手流程',
|
|
75
|
+
description: '关键步骤排成时间线,每步一句要点,适合教程/交接/发布流程。',
|
|
76
|
+
instruction: '请用 dsh-ui 把操作步骤做成 steps 教程:5 步(按实际流程),每步标题 + 一两句要点,顶部用 badge 标出预计耗时。',
|
|
77
|
+
demo: {
|
|
78
|
+
title: '上手流程(示例)',
|
|
79
|
+
items: [
|
|
80
|
+
{ type: 'badge', label: '约 5 分钟', tone: 'accent' },
|
|
81
|
+
{ type: 'steps', current: 2, steps: [
|
|
82
|
+
{ title: '安装插件', desc: '从插件中心安装,或 npm 安装到 profile' },
|
|
83
|
+
{ title: '发送第一条指令', desc: '让模型用 dsh-ui 输出一个界面' },
|
|
84
|
+
{ title: '体验交互', desc: '点按钮/判卷/切 tab,动作会回传模型' },
|
|
85
|
+
{ title: '常驻面板', desc: '模型用 panel:true 把结果钉在会话面板' },
|
|
86
|
+
{ title: '进阶组件', desc: '图表/流程/3D 按需懒加载' },
|
|
87
|
+
] },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'tpl-quiz',
|
|
93
|
+
category: '测验',
|
|
94
|
+
name: '随堂测验',
|
|
95
|
+
description: '一题一答即时判卷:选中即知对错,附讲解,适合培训问答。',
|
|
96
|
+
instruction: '请用 dsh-ui 出 3 道随堂测验(quiz 组件:question + options,其中一项 correct,附 explanation),标题「随堂测验」,每题即选即评。',
|
|
97
|
+
demo: {
|
|
98
|
+
title: '随堂测验(示例)',
|
|
99
|
+
gap: 10,
|
|
100
|
+
items: [
|
|
101
|
+
{ type: 'text', content: '选中即判卷并显示讲解。', size: 'muted' },
|
|
102
|
+
{ type: 'quiz', id: 'q-dsh', question: 'DSH 插件的最小单位是?', options: [
|
|
103
|
+
{ label: '服务' },
|
|
104
|
+
{ label: '插件(cordis 插件)', correct: true, feedback: '✓ 一切皆插件:服务、工具、UI 都通过插件注册。' },
|
|
105
|
+
{ label: '工具' },
|
|
106
|
+
], explanation: 'DSH 基于 cordis,任何能力(含服务与工具)都以插件形式注册。' },
|
|
107
|
+
{ type: 'quiz', id: 'q-fence', question: 'dsh-ui 围栏由谁输出?', options: [
|
|
108
|
+
{ label: '用户' },
|
|
109
|
+
{ label: '模型', correct: true, feedback: '✓ 模型在回复中输出 dsh-ui fence。' },
|
|
110
|
+
{ label: '宿主' },
|
|
111
|
+
], explanation: '模型输出 fence,渲染器将其画成真实组件。' },
|
|
112
|
+
{ type: 'quiz', id: 'q-panel', question: 'panel:true 的围栏渲染到哪里?', options: [
|
|
113
|
+
{ label: '消息流' },
|
|
114
|
+
{ label: '会话面板 dock', correct: true, feedback: '✓ 面板更新不刷屏。' },
|
|
115
|
+
{ label: '设置页' },
|
|
116
|
+
] },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'tpl-stats',
|
|
122
|
+
category: '数据',
|
|
123
|
+
name: '关键值 + 进度',
|
|
124
|
+
description: '一次交付:数值、进度、里程碑三件套,周报/复盘通用。',
|
|
125
|
+
instruction: '请用 dsh-ui 总结本周关键数据:keyvalue 列 3 组关键值、两根 progress 进度(计划 vs 实际)、timeline 放 3 个里程碑,标题「周报速览」。',
|
|
126
|
+
demo: {
|
|
127
|
+
title: '周报速览(示例)',
|
|
128
|
+
items: [
|
|
129
|
+
{ type: 'keyvalue', pairs: [
|
|
130
|
+
{ key: '完成项', value: '12' },
|
|
131
|
+
{ key: '延期', value: '1' },
|
|
132
|
+
{ key: '风险', value: '2(均已有缓解)' },
|
|
133
|
+
] },
|
|
134
|
+
{ type: 'progress', label: '计划进度', value: 75, valueLabel: '75%' },
|
|
135
|
+
{ type: 'progress', label: '实际进度', value: 82, valueLabel: '82%' },
|
|
136
|
+
{ type: 'timeline', items: [
|
|
137
|
+
{ title: '周一', desc: '完成 M1 开发', time: '08-25' },
|
|
138
|
+
{ title: '周三', desc: '全链路验证通过', time: '08-27' },
|
|
139
|
+
{ title: '周五', desc: '发布 0.9.4', time: '08-29' },
|
|
140
|
+
] },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'tpl-chart',
|
|
146
|
+
category: '图表',
|
|
147
|
+
name: '趋势柱状图',
|
|
148
|
+
description: '数据直接画成柱状图,多序列对比,走势一眼看清。',
|
|
149
|
+
instruction: '请用 dsh-ui 把下列数据画成 chart(bars,多序列):近 6 周完成量 vs 计划量;图表上方给一句趋势结论。',
|
|
150
|
+
demo: {
|
|
151
|
+
title: '趋势对比(示例)',
|
|
152
|
+
items: [
|
|
153
|
+
{ type: 'text', content: '结论:近 3 周完成量连续上升,本周首次反超计划。' },
|
|
154
|
+
{ type: 'chart', kind: 'line', data: [
|
|
155
|
+
{ label: 'W21', value: 8 },
|
|
156
|
+
{ label: 'W22', value: 10 },
|
|
157
|
+
{ label: 'W23', value: 9 },
|
|
158
|
+
{ label: 'W24', value: 12 },
|
|
159
|
+
{ label: 'W25', value: 15 },
|
|
160
|
+
{ label: 'W26', value: 18 },
|
|
161
|
+
] },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'tpl-tabs',
|
|
167
|
+
category: '交互',
|
|
168
|
+
name: '分标签页',
|
|
169
|
+
description: '一大块内容拆成标签页,锚点清晰;面板不挤、消息不刷屏。',
|
|
170
|
+
instruction: '请用 dsh-ui 把内容分成 tabs 三个标签:概览/明细/FAQ,每个标签 3 条以内要点;标题「功能速览」。',
|
|
171
|
+
demo: {
|
|
172
|
+
title: '功能速览(示例)',
|
|
173
|
+
items: [
|
|
174
|
+
{ type: 'tabs', tabs: [
|
|
175
|
+
{ label: '概览', items: [{ type: 'text', content: '三项能力:实时渲染、交互回传、常驻面板。' }] },
|
|
176
|
+
{ label: '明细', items: [{ type: 'list', items: ['渲染:fence 增量解析,边流边出', '交互:动作回传模型增量更新', '面板:会话级 dock,折叠/调高'] }] },
|
|
177
|
+
{ label: 'FAQ', items: [{ type: 'text', content: '问:不动模型会怎样?答:组件仍可本地判卷/展开。' }] },
|
|
178
|
+
] },
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'tpl-checklist',
|
|
184
|
+
category: '流程',
|
|
185
|
+
name: '交付检查清单',
|
|
186
|
+
description: '提交前逐项打勾:清单即流程,漏项看得见。',
|
|
187
|
+
instruction: '请用 dsh-ui 做一张交付检查清单:6 项(测试/文档/发布/回滚/监控/公告),checkbox 可勾选,顶部 badge 提示「全部勾选再合并」。',
|
|
188
|
+
demo: {
|
|
189
|
+
title: '交付清单(示例)',
|
|
190
|
+
items: [
|
|
191
|
+
{ type: 'badge', label: '全部勾选再合并', tone: 'warn' },
|
|
192
|
+
{ type: 'list', items: [
|
|
193
|
+
{ type: 'checkbox', label: '单元测试通过' },
|
|
194
|
+
{ type: 'checkbox', label: '文档同步' },
|
|
195
|
+
{ type: 'checkbox', label: '发布套餐就绪' },
|
|
196
|
+
{ type: 'checkbox', label: '回滚预案' },
|
|
197
|
+
] },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'tpl-accordion',
|
|
203
|
+
category: '高级',
|
|
204
|
+
name: 'FAQ 手风琴',
|
|
205
|
+
description: '一问一答收在折叠面板里,长文档变短,回答不吓人。',
|
|
206
|
+
instruction: '请用 dsh-ui 做 FAQ 手风琴(accordion):5 个高频问题,标题栏就是问题、展开一条给答案;第一问默认展开。',
|
|
207
|
+
demo: {
|
|
208
|
+
title: 'FAQ(示例)',
|
|
209
|
+
items: [
|
|
210
|
+
{ type: 'accordion', items: [
|
|
211
|
+
{ title: '支持哪些图表?', items: [{ type: 'text', content: 'chart(bars/line/donut)、plot 函数图、echart 全功能图表。' }] },
|
|
212
|
+
{ title: '面板能持久吗?', items: [{ type: 'text', content: '同一内容指纹刷新/重放恢复状态;新内容重置。' }] },
|
|
213
|
+
{ title: '交互会打扰模型吗?', items: [{ type: 'text', content: '带 action 的组件才回传;纯展示组件零往返。' }] },
|
|
214
|
+
] },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'tpl-diagram',
|
|
220
|
+
category: '高级',
|
|
221
|
+
name: '系统架构图',
|
|
222
|
+
description: '框架图画成 SVG:节点分层、连接线、分区标注,比截图清楚。',
|
|
223
|
+
instruction: '请用 dsh-ui 画一幅架构图(diagram):描述当前系统的 5 个节点与连接(kind: architecture 或 flowchart),包含一个安全区 zone。',
|
|
224
|
+
demo: {
|
|
225
|
+
title: '架构图(示例)',
|
|
226
|
+
items: [
|
|
227
|
+
{ type: 'diagram', kind: 'flowchart', title: 'DSH 插件链路', nodes: [
|
|
228
|
+
{ id: 'a', label: '模型', x: 20, y: 20, w: 120, h: 48 },
|
|
229
|
+
{ id: 'b', label: '插件', x: 20, y: 120, w: 120, h: 48 },
|
|
230
|
+
{ id: 'c', label: '宿主', x: 200, y: 70, w: 120, h: 48 },
|
|
231
|
+
], edges: [
|
|
232
|
+
{ from: 'a', to: 'b' },
|
|
233
|
+
{ from: 'b', to: 'c' },
|
|
234
|
+
] },
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'tpl-3d',
|
|
240
|
+
category: '高级',
|
|
241
|
+
name: '3D 场景',
|
|
242
|
+
description: '一个可旋转的 3D 场景:产品演示/空间示意用得上。',
|
|
243
|
+
instruction: '请用 dsh-ui 渲染一个 3D 场景(scene3d):一个立方体 + 一个球体,标题「示例场景」,附一句使用场景说明。',
|
|
244
|
+
demo: {
|
|
245
|
+
title: '3D 场景(示例)',
|
|
246
|
+
items: [
|
|
247
|
+
{ type: 'text', content: '首次加载需下载 three.js(约 700KB,懒加载),之后走缓存。', size: 'muted' },
|
|
248
|
+
{ type: 'scene3d', title: '示例场景', meshes: [
|
|
249
|
+
{ shape: 'box', size: 1, color: '#4FC3F7', position: [-1.2, 0, 0] },
|
|
250
|
+
{ shape: 'sphere', size: 0.6, color: '#81C784', position: [1.2, 0, 0] },
|
|
251
|
+
] },
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: 'tpl-breadcrumb',
|
|
257
|
+
category: '数据',
|
|
258
|
+
name: '路径面包屑',
|
|
259
|
+
description: '长路径拆成面包屑:文件位置、组织层级一读就懂。',
|
|
260
|
+
instruction: '请用 dsh-ui 展示当前工作区结构:breadcrumb 显示文件路径(4 段)、file-tree 描述项目目录(3 个目录、每个 2 个子项)。',
|
|
261
|
+
demo: {
|
|
262
|
+
title: '目录结构(示例)',
|
|
263
|
+
items: [
|
|
264
|
+
{ type: 'breadcrumb', items: ['工作区', 'dsh-genui', 'src', 'client'] },
|
|
265
|
+
{ type: 'file-tree', items: [
|
|
266
|
+
{ name: 'src', type: 'dir', children: [
|
|
267
|
+
{ name: 'client', type: 'dir', children: [
|
|
268
|
+
{ name: 'panel.tsx' },
|
|
269
|
+
{ name: 'templates.ts' },
|
|
270
|
+
] },
|
|
271
|
+
{ name: 'index.tsx' },
|
|
272
|
+
] },
|
|
273
|
+
{ name: 'tests', type: 'dir', children: [
|
|
274
|
+
{ name: 'templates.spec.ts' },
|
|
275
|
+
] },
|
|
276
|
+
] },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
]
|