@morya-ui/mcp 0.3.4 → 0.3.5

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.
@@ -202,19 +202,23 @@ describe('@morya-ui/mcp handlers', () => {
202
202
  const result = read(handlers.recommendPage({ intent: '用户列表页', pageType: 'list' }));
203
203
  expect(result.matchedPattern).toBe('admin-list');
204
204
  expect(result.nextStep).toContain('validate_usage');
205
+ expect(result.nextStep).toMatch(/get_page_snippet|suggestedSnippets|relatedSnippets/);
205
206
  expect(result.nextStep).toContain('get_golden_page');
207
+ expect(result.nextStep).toMatch(/可选|Optional/);
208
+ expect(result.suggestedSnippets?.length).toBeGreaterThan(0);
209
+ expect(result.suggestedSnippets).toContain('list-filters-stack');
210
+ expect(result.suggestedSnippets).toContain('list-table');
206
211
  expect(result.pageStandards).toBeUndefined();
207
212
  expect(result.scrollStandards).toBeUndefined();
208
- expect(result.styleDirection?.candidates?.length).toBeGreaterThan(0);
213
+ expect(result.styleDirection?.resolution).toBeTruthy();
209
214
  });
210
- it('resolves an explicit style preset on recommend_page', () => {
211
- const result = read(handlers.recommendPage({ intent: '用户列表', pageType: 'list', style: 'soft' }));
212
- expect(result.styleDirection.resolution).toBe('preset');
213
- expect(result.styleDirection.preset?.id).toBe('soft');
214
- expect(result.styleDirection.guidance).toContain('soft');
215
+ it('resolves an explicit free-text style on recommend_page', () => {
216
+ const result = read(handlers.recommendPage({ intent: '用户列表', pageType: 'list', style: '简洁留白,少装饰' }));
217
+ expect(result.styleDirection.resolution).toBe('explicit');
218
+ expect(result.styleDirection.summary).toContain('简洁留白');
215
219
  expect(result.goldenPage).toContain('list-page');
216
220
  });
217
- it('routes dense/rail styles to list craft golden variants', () => {
221
+ it('routes dense/rail craft cues to list golden variants', () => {
218
222
  const dense = read(handlers.recommendPage({
219
223
  intent: '监控告警列表',
220
224
  pageType: 'list',
@@ -225,6 +229,8 @@ describe('@morya-ui/mcp handlers', () => {
225
229
  expect(dense.scaffold.goldenPage).toBe('list-page-dense');
226
230
  expect(dense.scaffold.files.component).toContain('density="compact"');
227
231
  expect(dense.nextStep).toContain('list-page-dense');
232
+ expect(dense.suggestedSnippets).toContain('list-filters-dense');
233
+ expect(dense.suggestedSnippets).not.toContain('list-filters-stack');
228
234
  const rail = read(handlers.recommendPage({
229
235
  intent: '品牌色后台列表',
230
236
  pageType: 'list',
@@ -234,6 +240,7 @@ describe('@morya-ui/mcp handlers', () => {
234
240
  expect(rail.goldenPage).toContain('list-page-rail');
235
241
  expect(rail.scaffold.goldenPage).toBe('list-page-rail');
236
242
  expect(rail.scaffold.files.component).toContain('gp-rail-sider');
243
+ expect(rail.suggestedSnippets).toContain('list-filters-stack');
237
244
  });
238
245
  it('returns dense and rail golden page sources', () => {
239
246
  const dense = read(handlers.getGoldenPage({ page: 'list-page-dense' }));
@@ -246,19 +253,21 @@ describe('@morya-ui/mcp handlers', () => {
246
253
  expect(rail.style).toBe('rail');
247
254
  expect(rail.source).toContain('color-mix');
248
255
  });
249
- it('lists style presets with resolution priority', () => {
250
- const result = read(handlers.listStylePresets({ mode: 'zh-CN' }));
251
- expect(result.presets.map((item) => item.id)).toEqual(expect.arrayContaining(['quiet', 'soft', 'dense', 'rail', 'studio', 'ink']));
252
- expect(result.resolution[0]).toContain('参考');
253
- });
254
- it('reads a style preset by alias', () => {
255
- const result = read(handlers.getStylePreset({ style: '柔和留白', mode: 'zh-CN' }));
256
- expect(result.id).toBe('soft');
257
- expect(result.apply.length).toBeGreaterThan(0);
258
- });
259
- it('exposes style presets from get_design_rules', () => {
256
+ it('exposes style direction rules without a preset catalog', () => {
257
+ const result = read(handlers.getStyleDirection({ mode: 'zh-CN' }));
258
+ expect(result.resolution).toBe('ask');
259
+ expect(result.priority[0]).toContain('参考');
260
+ expect(result.hardRules.some((rule) => rule.includes('没有命名风格预设'))).toBe(true);
261
+ expect(result.note).toContain('没有命名风格预设');
262
+ const explicit = read(handlers.getStyleDirection({ style: '毛玻璃一点', mode: 'zh-CN' }));
263
+ expect(explicit.resolution).toBe('explicit');
264
+ expect(explicit.summary).toContain('毛玻璃');
265
+ });
266
+ it('exposes style direction from get_design_rules', () => {
260
267
  const result = read(handlers.getDesignRules({ mode: 'zh-CN' }));
261
- expect(result.styles.presets.some((item) => item.id === 'dense')).toBe(true);
268
+ expect(result.styles.resolution[0]).toContain('参考');
269
+ expect(result.styles.presets).toBeUndefined();
270
+ expect(result.styles.note).toContain('没有命名风格预设');
262
271
  });
263
272
  it('keeps get_golden_page focused on source code', () => {
264
273
  const result = read(handlers.getGoldenPage({ page: 'list-page' }));
@@ -355,6 +364,14 @@ describe('@morya-ui/mcp handlers', () => {
355
364
  expect(result.id).toBe('overlay-choice');
356
365
  expect(result.options.some((option) => option.component === 'Drawer')).toBe(true);
357
366
  });
367
+ it('exposes relatedSnippets on decision options', () => {
368
+ const result = read(handlers.recommendComponent({ decision: 'confirm-choice' }));
369
+ const confirm = result.options.find((option) => option.component === 'ConfirmDialog');
370
+ expect(confirm?.relatedSnippets).toContain('confirm-delete');
371
+ const formSurface = read(handlers.recommendComponent({ decision: 'form-surface-choice' }));
372
+ const dialog = formSurface.options.find((option) => option.component === 'Dialog');
373
+ expect(dialog?.relatedSnippets).toContain('form-in-dialog');
374
+ });
358
375
  it('returns L2 recipes and antiPatterns on decision reads', () => {
359
376
  const result = read(handlers.recommendComponent({ decision: 'data-display-choice' }));
360
377
  const table = result.options.find((option) => option.component === 'Table');
@@ -390,11 +407,12 @@ describe('@morya-ui/mcp handlers', () => {
390
407
  expect(result.counts.decisions).toBeGreaterThan(0);
391
408
  expect(result.counts.resources).toBeGreaterThan(100);
392
409
  expect(result.counts.resourceTemplates).toBe(3);
393
- expect(result.tools).toHaveLength(20);
410
+ expect(result.tools).toHaveLength(19);
394
411
  expect(result.tools).toContain('get_golden_page');
395
412
  expect(result.tools).toContain('get_page_snippet');
396
- expect(result.tools).toContain('list_style_presets');
397
- expect(result.tools).toContain('get_style_preset');
413
+ expect(result.tools).toContain('get_style_direction');
414
+ expect(result.tools).not.toContain('list_style_presets');
415
+ expect(result.tools).not.toContain('get_style_preset');
398
416
  expect(result.tools).toContain('validate_page');
399
417
  expect(result.tools).not.toContain('create_page');
400
418
  });
@@ -16,6 +16,8 @@ export interface ComponentDecisionOption {
16
16
  recipe: ComponentDecisionRecipe;
17
17
  antiPatterns: string[];
18
18
  antiPatternsEn: string[];
19
+ /** Page-snippet ids to compose after choosing this option (composition-first). */
20
+ relatedSnippets?: string[];
19
21
  }
20
22
  export interface ComponentDecision {
21
23
  id: string;
package/dist/decisions.js CHANGED
@@ -45,7 +45,7 @@ export const componentDecisions = [
45
45
  recipe: recipe([
46
46
  ['v-model / modelValue 控制开关(不要用 visible)', 'v-model / modelValue for open state (not visible)'],
47
47
  ['header 或 title 设弹窗标题', 'header or title for the dialog title'],
48
- ['width 约 32rem 适配短表单(见 form-in-dialog 黄金样例)', 'width ~32rem for short forms (see form-in-dialog golden)'],
48
+ ['width 约 32rem 适配短表单(见 form-in-dialog snippet)', 'width ~32rem for short forms (see form-in-dialog snippet)'],
49
49
  ['表单字段用 fluid;主按钮 severity="primary"', 'Form fields use fluid; primary button severity="primary"'],
50
50
  ], [
51
51
  ['#footer 放取消/保存', '#footer for cancel / save'],
@@ -54,6 +54,7 @@ export const componentDecisions = [
54
54
  ['@close 重置表单模型', '@close to reset the form model'],
55
55
  ['保存可用按钮 @click,或 MForm @submit + type="submit"', 'Save via button @click, or MForm @submit + type="submit"'],
56
56
  ]),
57
+ relatedSnippets: ['form-in-dialog', 'page-header-actions'],
57
58
  ...anti([
58
59
  ['用普通 Dialog 手写删除确认 → MConfirmDialog', 'Hand-rolled delete confirm in Dialog → MConfirmDialog'],
59
60
  ['长多分组配置塞进 Dialog → Form 页或 Drawer', 'Long multi-section config in Dialog → Form page or Drawer'],
@@ -74,6 +75,7 @@ export const componentDecisions = [
74
75
  ['#footer 取消/保存', '#footer cancel / save'],
75
76
  ['默认插槽 MForm', 'Default slot: MForm'],
76
77
  ], [['@close 清理草稿', '@close to clear draft state']]),
78
+ relatedSnippets: ['form-in-drawer'],
77
79
  ...anti([
78
80
  ['极短 2–3 字段也用 Drawer → Dialog', 'Tiny 2–3 field form in Drawer → Dialog'],
79
81
  ['危险删除只用 Drawer 无确认 → MConfirmDialog', 'Destructive delete in Drawer without confirm → MConfirmDialog'],
@@ -97,6 +99,7 @@ export const componentDecisions = [
97
99
  ['MForm @submit(或 @submit.prevent)', 'MForm @submit (or @submit.prevent)'],
98
100
  ['提交按钮 type="submit" + severity="primary"', 'Submit button type="submit" + severity="primary"'],
99
101
  ]),
102
+ relatedSnippets: ['page-content-form', 'form-header', 'form-body', 'form-actions'],
100
103
  ...anti([
101
104
  ['列表短 CRUD 开独立路由 → form-in-dialog', 'Short list CRUD as a route → form-in-dialog'],
102
105
  ['表单外再套装饰性 MCard → 用 PageSection form', 'Extra decorative MCard around form → PageSection form'],
@@ -151,6 +154,7 @@ export const componentDecisions = [
151
154
  ['modal 默认阻塞;任务型内容放默认插槽', 'modal blocks by default; task content in default slot'],
152
155
  ['危险确认不要用 Dialog 冒充 → ConfirmDialog', 'Do not fake destructive confirm with Dialog → ConfirmDialog'],
153
156
  ], [['#footer 主/次操作', '#footer primary / secondary actions']], [['@close / @update:modelValue', '@close / @update:modelValue']]),
157
+ relatedSnippets: ['form-in-dialog'],
154
158
  ...anti([
155
159
  ['Dialog + 手写「确定删除」→ MConfirmDialog', 'Dialog + hand-rolled Delete? → MConfirmDialog'],
156
160
  ['v-model:visible → v-model / modelValue', 'v-model:visible → v-model / modelValue'],
@@ -171,6 +175,7 @@ export const componentDecisions = [
171
175
  ['position="right"(常用)', 'position="right" (typical)'],
172
176
  ['header 说明侧栏任务', 'header describes the side-panel job'],
173
177
  ], [['#footer 操作区', '#footer actions']], [['@close', '@close']]),
178
+ relatedSnippets: ['form-in-drawer'],
174
179
  ...anti([['简单 Yes/No 确认用 Drawer → ConfirmDialog / ConfirmPopup', 'Simple Yes/No in Drawer → ConfirmDialog / ConfirmPopup']]),
175
180
  },
176
181
  {
@@ -614,11 +619,27 @@ export const componentDecisions = [
614
619
  avoidWhen: ['普通表单字段分组', 'KPI 指标展示'],
615
620
  avoidWhenEn: ['Regular form field grouping', 'KPI metric display'],
616
621
  recipe: recipe([
617
- ['内放 MInput / MSelect 等筛选控件', 'Place MInput / MSelect filters inside'],
618
- ['可 collapsible + #advanced', 'Optional collapsible + #advanced'],
619
- ['不要外包 MCard', 'Do not wrap in MCard'],
620
- ], [['#advanced 高级筛选', '#advanced for advanced filters']]),
621
- ...anti([['表单字段组用 PageFilters → PageSection form / Fieldset', 'Form field groups via PageFilters → PageSection form / Fieldset']]),
622
+ ['内放 MInput / MSelect;同行用 MSpace wrap', 'Place MInput / MSelect inside; peers in MSpace wrap'],
623
+ ['查询/重置放 #actions(与折叠切换同列)', 'Query/reset in #actions (trailing with toggle)'],
624
+ ['默认 variant="filled";dense craft 用 plain + size="small"', 'Default variant="filled"; dense craft: plain + size="small"'],
625
+ ['有次要条件:collapsible + v-model:expanded + #advanced', 'Secondary fields: collapsible + v-model:expanded + #advanced'],
626
+ ['toggle 默认「高级筛选/收起」+ chevron;已选用 FilterChips', 'Toggle defaults to Advanced/Collapse + chevron; active via FilterChips'],
627
+ ], [
628
+ ['#actions 查询/重置', '#actions for query / reset'],
629
+ ['#advanced 高级筛选', '#advanced for advanced filters'],
630
+ ['#active 可选;更常见是下方 FilterChips', '#active optional; sibling FilterChips is more common'],
631
+ ]),
632
+ relatedSnippets: [
633
+ 'list-filters-stack',
634
+ 'list-filters',
635
+ 'list-filters-collapsible',
636
+ 'list-filter-chips',
637
+ 'list-filters-dense',
638
+ ],
639
+ ...anti([
640
+ ['表单字段组用 PageFilters → PageSection form / Fieldset', 'Form field groups via PageFilters → PageSection form / Fieldset'],
641
+ ['手写已选条 → list-filter-chips / list-filters-stack', 'Hand-rolled active bar → list-filter-chips / list-filters-stack'],
642
+ ]),
622
643
  },
623
644
  {
624
645
  component: 'PageToolbar',
@@ -1096,6 +1117,7 @@ export const componentDecisions = [
1096
1117
  ['icon 或 illustration 按需(默认即可)', 'icon or illustration as needed (default OK)'],
1097
1118
  ['表格内放在 #empty', 'Inside tables: #empty slot'],
1098
1119
  ], [['#extra 放下一步按钮(创建…)', '#extra for next-step button (Create…)']]),
1120
+ relatedSnippets: ['empty-block'],
1099
1121
  ...anti([
1100
1122
  ['403/404 用 Empty → Result', '403/404 via Empty → Result'],
1101
1123
  ['#action → #extra', '#action → #extra'],
@@ -1120,6 +1142,7 @@ export const componentDecisions = [
1120
1142
  ['title + description', 'title + description'],
1121
1143
  ['size 按页面比重', 'size for page weight'],
1122
1144
  ], [['#footer 逃逸/下一步按钮', '#footer escape / next actions']]),
1145
+ relatedSnippets: ['result-block'],
1123
1146
  ...anti([
1124
1147
  ['表格无数据用 Result → Empty', 'Table no-rows via Result → Empty'],
1125
1148
  ['操作插槽写成 #extra → #footer', 'Actions slot #extra → #footer'],
@@ -1156,6 +1179,7 @@ export const componentDecisions = [
1156
1179
  ['触发器放默认插槽(按钮/图标)', 'Trigger in default slot (button/icon)'],
1157
1180
  ['危险项用文档 severity / 确认流', 'Danger items: docs severity / confirm flow'],
1158
1181
  ]),
1182
+ relatedSnippets: ['row-actions-menu', 'list-row-actions'],
1159
1183
  ...anti([['表单枚举用 Dropdown → Select', 'Form enum via Dropdown → Select']]),
1160
1184
  },
1161
1185
  {
@@ -1245,6 +1269,7 @@ export const componentDecisions = [
1245
1269
  ["message.success('已保存') / info / warn / error", "message.success('Saved') / info / warn / error"],
1246
1270
  ['大多数操作反馈的默认选择', 'Default for most operation feedback'],
1247
1271
  ]),
1272
+ relatedSnippets: ['confirm-delete', 'form-in-dialog'],
1248
1273
  ...anti([
1249
1274
  ["toast.add({ summary: '已保存' }) → message.success('已保存')", "toast.add({ summary: 'Saved' }) → message.success('Saved')"],
1250
1275
  ['<MMessage severity> 当内嵌 Alert → errorMessage / role="alert"', '<MMessage severity> as inline Alert → errorMessage / role="alert"'],
@@ -1277,6 +1302,7 @@ export const componentDecisions = [
1277
1302
  ['表单级:token 样式的 role="alert" 条(见 login-page)', 'Form-level: token-styled role="alert" bar (see login-page)'],
1278
1303
  ['<MMessage> 只是 message 宿主,不是内嵌 Alert', '<MMessage> is the message host, not an inline Alert'],
1279
1304
  ]),
1305
+ relatedSnippets: ['auth-split-shell'],
1280
1306
  ...anti([
1281
1307
  ['登录失败只闪 Toast → 表单区 alert / errorMessage', 'Login failure only via Toast → form alert / errorMessage'],
1282
1308
  ['编造 MMessage severity 插槽 Alert API', 'Invented MMessage severity slot Alert API'],
@@ -1321,6 +1347,7 @@ export const componentDecisions = [
1321
1347
  ['@accept 执行删除', '@accept to perform delete'],
1322
1348
  ['@reject 关闭', '@reject to dismiss'],
1323
1349
  ]),
1350
+ relatedSnippets: ['confirm-delete'],
1324
1351
  ...anti([
1325
1352
  ['普通 Dialog 手写「确定/取消」删除 → ConfirmDialog', 'Hand-rolled Dialog Yes/No delete → ConfirmDialog'],
1326
1353
  ['编辑表单误用 ConfirmDialog → Dialog + Form', 'Edit form via ConfirmDialog → Dialog + Form'],
@@ -1344,6 +1371,7 @@ export const componentDecisions = [
1344
1371
  ['target 锚定触发元素(或 position)', 'target anchors the trigger (or position)'],
1345
1372
  ['placement top|bottom|left|right', 'placement top|bottom|left|right'],
1346
1373
  ], undefined, [['@accept / @reject', '@accept / @reject']]),
1374
+ relatedSnippets: ['confirm-delete'],
1347
1375
  ...anti([['无 target 的全局危险确认 → ConfirmDialog', 'Global dangerous confirm without target → ConfirmDialog']]),
1348
1376
  },
1349
1377
  ],
@@ -1,10 +1,15 @@
1
+ /**
2
+ * Golden pages = optional whole-page assembly demos / block-order baselines.
3
+ * Default agent path is composition-first (page snippets + decision recipes).
4
+ * FROZEN: do not add new craft variants (e.g. more list-page-*). Express density/sider via style direction + Ops polish.
5
+ */
1
6
  export interface GoldenPageRecord {
2
7
  id: string;
3
8
  file: string;
4
9
  title: string;
5
10
  titleEn: string;
6
11
  patternId: string;
7
- /** Visual style preset this sample demonstrates (structure may be shared). */
12
+ /** Optional craft cue this sample demonstrates (structure may be shared). Not a style-preset catalog. */
8
13
  style?: string;
9
14
  /** Canonical structure id when this is a craft variant. */
10
15
  variantOf?: string;
@@ -1,20 +1,21 @@
1
1
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ import { resolveListCraftVariant } from './style-presets.js';
4
5
  export const goldenPageCatalog = [
5
6
  {
6
7
  id: 'list-page',
7
8
  file: 'list-page.vue',
8
- title: '列表页黄金样例(soft)',
9
- titleEn: 'List page golden sample (soft)',
9
+ title: '列表页结构样例(soft)',
10
+ titleEn: 'List page structure sample (soft)',
10
11
  patternId: 'admin-list',
11
12
  style: 'soft',
12
13
  },
13
14
  {
14
15
  id: 'list-page-dense',
15
16
  file: 'list-page-dense.vue',
16
- title: '列表页黄金样例(dense)',
17
- titleEn: 'List page golden sample (dense)',
17
+ title: '列表页结构样例(dense)',
18
+ titleEn: 'List page structure sample (dense)',
18
19
  patternId: 'admin-list',
19
20
  style: 'dense',
20
21
  variantOf: 'list-page',
@@ -22,8 +23,8 @@ export const goldenPageCatalog = [
22
23
  {
23
24
  id: 'list-page-rail',
24
25
  file: 'list-page-rail.vue',
25
- title: '列表页黄金样例(rail)',
26
- titleEn: 'List page golden sample (rail)',
26
+ title: '列表页结构样例(rail)',
27
+ titleEn: 'List page structure sample (rail)',
27
28
  patternId: 'admin-list',
28
29
  style: 'rail',
29
30
  variantOf: 'list-page',
@@ -31,81 +32,78 @@ export const goldenPageCatalog = [
31
32
  {
32
33
  id: 'form-page',
33
34
  file: 'form-page.vue',
34
- title: '表单页黄金样例',
35
- titleEn: 'Form page golden sample',
35
+ title: '表单页结构样例',
36
+ titleEn: 'Form page structure sample',
36
37
  patternId: 'form-page',
37
38
  },
38
39
  {
39
40
  id: 'dashboard-page',
40
41
  file: 'dashboard-page.vue',
41
- title: '仪表盘黄金样例',
42
- titleEn: 'Dashboard golden sample',
42
+ title: '仪表盘结构样例',
43
+ titleEn: 'Dashboard structure sample',
43
44
  patternId: 'dashboard',
44
45
  },
45
46
  {
46
47
  id: 'login-page',
47
48
  file: 'login-page.vue',
48
- title: '登录页黄金样例',
49
- titleEn: 'Login page golden sample',
49
+ title: '登录页结构样例',
50
+ titleEn: 'Login page structure sample',
50
51
  patternId: 'auth-page',
51
52
  },
52
53
  {
53
54
  id: 'landing-page',
54
55
  file: 'landing-page.vue',
55
- title: '营销落地页黄金样例',
56
- titleEn: 'Marketing landing golden sample',
56
+ title: '营销落地页结构样例',
57
+ titleEn: 'Marketing landing structure sample',
57
58
  patternId: 'marketing-landing',
58
59
  },
59
60
  {
60
61
  id: 'empty-state',
61
62
  file: 'empty-state.vue',
62
- title: '空状态黄金样例',
63
- titleEn: 'Empty state golden sample',
63
+ title: '空状态结构样例',
64
+ titleEn: 'Empty state structure sample',
64
65
  patternId: 'empty-state',
65
66
  },
66
67
  {
67
68
  id: 'detail-page',
68
69
  file: 'detail-page.vue',
69
- title: '详情页黄金样例',
70
- titleEn: 'Detail page golden sample',
70
+ title: '详情页结构样例',
71
+ titleEn: 'Detail page structure sample',
71
72
  patternId: 'detail-page',
72
73
  },
73
74
  {
74
75
  id: 'form-in-dialog',
75
76
  file: 'form-in-dialog.vue',
76
- title: '列表内弹窗表单黄金样例',
77
- titleEn: 'List create/edit dialog golden sample',
77
+ title: '列表内弹窗表单结构样例',
78
+ titleEn: 'List create/edit dialog structure sample',
78
79
  patternId: 'form-in-dialog',
79
80
  },
80
81
  {
81
82
  id: 'result-page',
82
83
  file: 'result-page.vue',
83
- title: '结果 / 阻断页黄金样例',
84
- titleEn: 'Result / terminal page golden sample',
84
+ title: '结果 / 阻断页结构样例',
85
+ titleEn: 'Result / terminal page structure sample',
85
86
  patternId: 'result-page',
86
87
  },
87
88
  {
88
89
  id: 'settings-page',
89
90
  file: 'settings-page.vue',
90
- title: '设置页黄金样例',
91
- titleEn: 'Settings page golden sample',
91
+ title: '设置页结构样例',
92
+ titleEn: 'Settings page structure sample',
92
93
  patternId: 'settings-page',
93
94
  },
94
95
  {
95
96
  id: 'wizard-form',
96
97
  file: 'wizard-form.vue',
97
- title: '分步向导黄金样例',
98
- titleEn: 'Wizard form golden sample',
98
+ title: '分步向导结构样例',
99
+ titleEn: 'Wizard form structure sample',
99
100
  patternId: 'wizard-form',
100
101
  },
101
102
  ];
102
- /** Map style preset → list golden craft variant (same structure). */
103
- const listStyleGolden = {
104
- soft: 'list-page',
105
- quiet: 'list-page',
106
- studio: 'list-page',
107
- ink: 'list-page',
103
+ /** Optional legacy exact keys for list craft (density / sider) — not a style catalog. */
104
+ const listCraftGolden = {
108
105
  dense: 'list-page-dense',
106
+ compact: 'list-page-dense',
109
107
  rail: 'list-page-rail',
110
108
  };
111
109
  const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
@@ -137,7 +135,10 @@ export function resolveGoldenPageId(args) {
137
135
  : null) ||
138
136
  null;
139
137
  if (args.patternId === 'admin-list' || canonical === 'list-page' || canonical?.startsWith('list-page')) {
140
- const styled = style ? listStyleGolden[style] : null;
138
+ const key = style.replace(/[-_\s]/g, '');
139
+ const exact = style ? listCraftGolden[style] || listCraftGolden[key] : null;
140
+ const fromText = resolveListCraftVariant(args.styleId);
141
+ const styled = exact || fromText;
141
142
  if (styled && findGoldenPage(styled))
142
143
  return styled;
143
144
  return 'list-page';
package/dist/index.js CHANGED
@@ -86,20 +86,20 @@ register('get_pattern', 'Read the structure, component composition, layout, and
86
86
  pattern: z.string().min(1),
87
87
  mode: z.string().optional(),
88
88
  }, async (args) => handlers.getPattern(args));
89
- register('recommend_page', 'Recommend a page pattern and component composition from product intent. Pass style for a visual preset (quiet/soft/dense/rail/studio/ink). Pass includeScaffold: true for starter Vue code.', {
89
+ register('recommend_page', 'Recommend a page pattern and component composition from product intent. Pass style as free-text user description when they specified a look (no preset catalog). Pass includeScaffold: true for starter Vue code.', {
90
90
  intent: z.string().min(1),
91
91
  pageType: z.string().optional(),
92
92
  features: z.array(z.string()).max(20).optional(),
93
- style: z.string().optional().describe('Optional visual style preset id or alias, e.g. soft / dense / 柔和留白.'),
93
+ style: z.string().optional().describe('Optional free-text style description from the user (not a preset id).'),
94
94
  mode: z.string().optional(),
95
95
  includeScaffold: z.boolean().optional(),
96
96
  }, async (args) => handlers.recommendPage(args));
97
- register('get_design_rules', 'Return design-token, style presets, semantic-action, accessibility, and composition rules for generated pages.', { mode: z.string().optional() }, async (args) => handlers.getDesignRules(args));
98
- register('list_style_presets', 'List selectable page visual styles (quiet/soft/dense/rail/studio/ink) and resolution priority (reference → preset → prompt → ask).', { mode: z.string().optional() }, async (args) => handlers.listStylePresets(args));
99
- register('get_style_preset', 'Read apply/avoid cues for one visual style preset (quiet, soft, dense, rail, studio, ink, or Chinese aliases).', {
100
- style: z.string().min(1).describe('Style preset id or alias.'),
97
+ register('get_design_rules', 'Return design-token, style-direction rules, semantic-action, accessibility, and composition rules for generated pages.', { mode: z.string().optional() }, async (args) => handlers.getDesignRules(args));
98
+ register('get_style_direction', 'Resolve visual style direction: follow user reference/description, infer from clear cues, or ask when uncertain. No named preset catalog.', {
99
+ intent: z.string().optional(),
100
+ style: z.string().optional().describe('Free-text style description from the user.'),
101
101
  mode: z.string().optional(),
102
- }, async (args) => handlers.getStylePreset(args));
102
+ }, async (args) => handlers.getStyleDirection(args));
103
103
  register('recommend_component', 'List, read, or recommend component selection guides. Omit query/decision to list; pass decision only to read; pass query to recommend.', {
104
104
  query: z.string().optional(),
105
105
  decision: z.string().optional(),
@@ -108,7 +108,7 @@ register('recommend_component', 'List, read, or recommend component selection gu
108
108
  offset: z.number().int().min(0).optional(),
109
109
  }, async (args) => handlers.recommendComponent(args));
110
110
  register('list_golden_pages', 'List golden page samples for list, form, and dashboard layouts.', { mode: z.string().optional() }, async (args) => handlers.listGoldenPageCatalog(args));
111
- register('get_golden_page', 'Read a golden page Vue source sample (list-page, list-page-dense, list-page-rail, form-page, dashboard-page, login-page, landing-page, empty-state, detail-page, form-in-dialog, result-page, settings-page, wizard-form). List craft variants: dense/rail when style is dense/rail; soft structure is list-page.', {
111
+ register('get_golden_page', 'Read a golden page Vue source sample (list-page, list-page-dense, list-page-rail, form-page, dashboard-page, login-page, landing-page, empty-state, detail-page, form-in-dialog, result-page, settings-page, wizard-form). List craft variants: dense/compact cues → dense; rail/brand-sider cues → rail; otherwise list-page.', {
112
112
  page: z.string().min(1),
113
113
  mode: z.string().optional(),
114
114
  }, async (args) => handlers.getGoldenPage(args));