@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.
- package/LICENSE +21 -0
- package/bin/morya-ui-mcp.js +0 -0
- package/data/catalog.json +31 -31
- package/data/example-coverage.json +2 -2
- package/data/golden-pages/list-page-dense.vue +3 -1
- package/data/golden-pages/list-page-rail.vue +3 -1
- package/data/golden-pages/list-page.vue +3 -1
- package/dist/__tests__/page-snippets.test.js +29 -0
- package/dist/__tests__/tools.test.js +40 -22
- package/dist/decisions.d.ts +2 -0
- package/dist/decisions.js +34 -6
- package/dist/golden-pages.d.ts +6 -1
- package/dist/golden-pages.js +34 -33
- package/dist/index.js +8 -8
- package/dist/page-snippets.js +712 -38
- package/dist/patterns.d.ts +4 -2
- package/dist/patterns.js +35 -6
- package/dist/style-presets.d.ts +13 -32
- package/dist/style-presets.js +100 -291
- package/dist/tools.d.ts +3 -11
- package/dist/tools.js +59 -77
- package/package.json +11 -13
|
@@ -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?.
|
|
213
|
+
expect(result.styleDirection?.resolution).toBeTruthy();
|
|
209
214
|
});
|
|
210
|
-
it('resolves an explicit style
|
|
211
|
-
const result = read(handlers.recommendPage({ intent: '用户列表', pageType: 'list', style: '
|
|
212
|
-
expect(result.styleDirection.resolution).toBe('
|
|
213
|
-
expect(result.styleDirection.
|
|
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
|
|
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('
|
|
250
|
-
const result = read(handlers.
|
|
251
|
-
expect(result.
|
|
252
|
-
expect(result.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
expect(
|
|
257
|
-
expect(
|
|
258
|
-
});
|
|
259
|
-
it('exposes style
|
|
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.
|
|
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(
|
|
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('
|
|
397
|
-
expect(result.tools).toContain('
|
|
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
|
});
|
package/dist/decisions.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
618
|
-
['
|
|
619
|
-
['
|
|
620
|
-
|
|
621
|
-
|
|
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
|
],
|
package/dist/golden-pages.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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;
|
package/dist/golden-pages.js
CHANGED
|
@@ -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: '
|
|
9
|
-
titleEn: 'List page
|
|
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: '
|
|
17
|
-
titleEn: 'List page
|
|
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: '
|
|
26
|
-
titleEn: 'List page
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
98
|
+
title: '分步向导结构样例',
|
|
99
|
+
titleEn: 'Wizard form structure sample',
|
|
99
100
|
patternId: 'wizard-form',
|
|
100
101
|
},
|
|
101
102
|
];
|
|
102
|
-
/**
|
|
103
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
98
|
-
register('
|
|
99
|
-
|
|
100
|
-
style: z.string().
|
|
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.
|
|
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/
|
|
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));
|