@morya-ui/mcp 0.3.4 → 0.3.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/LICENSE +21 -0
- package/README.md +154 -151
- package/bin/morya-ui-mcp.js +2 -2
- package/data/catalog.json +40 -40
- package/data/example-coverage.json +2 -2
- package/data/golden-pages/dashboard-page.vue +138 -138
- package/data/golden-pages/detail-page.vue +178 -178
- package/data/golden-pages/empty-state.vue +56 -56
- package/data/golden-pages/form-in-dialog.vue +166 -166
- package/data/golden-pages/form-page.vue +107 -107
- package/data/golden-pages/landing-page.vue +358 -358
- package/data/golden-pages/list-page-dense.vue +168 -166
- package/data/golden-pages/list-page-rail.vue +272 -270
- package/data/golden-pages/list-page.vue +222 -220
- package/data/golden-pages/login-page.vue +231 -231
- package/data/golden-pages/result-page.vue +84 -84
- package/data/golden-pages/settings-page.vue +163 -163
- package/data/golden-pages/wizard-form.vue +182 -182
- package/dist/__tests__/fidelity.test.d.ts +1 -0
- package/dist/__tests__/fidelity.test.js +88 -0
- package/dist/__tests__/page-snippets.test.js +42 -5
- package/dist/__tests__/tools.test.js +49 -29
- 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 +52 -9
- package/dist/page-snippets.js +1041 -280
- package/dist/patterns.d.ts +4 -2
- package/dist/patterns.js +35 -6
- package/dist/reference-brief.d.ts +55 -0
- package/dist/reference-brief.js +468 -0
- package/dist/style-presets.d.ts +13 -32
- package/dist/style-presets.js +100 -291
- package/dist/style-shells.d.ts +20 -0
- package/dist/style-shells.js +249 -0
- package/dist/tools.d.ts +21 -3
- package/dist/tools.js +496 -424
- package/package.json +11 -13
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured reference brief → snippet / shell / token mapping for page fidelity.
|
|
3
|
+
* Agents describe what the reference shows; this module returns how to rebuild it with M*.
|
|
4
|
+
*/
|
|
5
|
+
const SURFACE_ALIASES = {
|
|
6
|
+
list: 'ops-list',
|
|
7
|
+
'admin-list': 'ops-list',
|
|
8
|
+
table: 'ops-list',
|
|
9
|
+
form: 'ops-form',
|
|
10
|
+
'form-page': 'ops-form',
|
|
11
|
+
dashboard: 'ops-dashboard',
|
|
12
|
+
detail: 'ops-detail',
|
|
13
|
+
settings: 'ops-settings',
|
|
14
|
+
login: 'account',
|
|
15
|
+
auth: 'account',
|
|
16
|
+
account: 'account',
|
|
17
|
+
empty: 'flow-empty',
|
|
18
|
+
'empty-state': 'flow-empty',
|
|
19
|
+
result: 'flow-result',
|
|
20
|
+
wizard: 'flow-wizard',
|
|
21
|
+
landing: 'express',
|
|
22
|
+
marketing: 'express',
|
|
23
|
+
express: 'express',
|
|
24
|
+
dialog: 'overlay-dialog',
|
|
25
|
+
'form-in-dialog': 'overlay-dialog',
|
|
26
|
+
drawer: 'overlay-drawer',
|
|
27
|
+
'form-in-drawer': 'overlay-drawer',
|
|
28
|
+
};
|
|
29
|
+
const BLOCK_TO_SNIPPET = {
|
|
30
|
+
filters: {
|
|
31
|
+
snippetId: 'list-filters-stack',
|
|
32
|
+
notesZh: '筛选区用 MPageFilters;搜索约 14rem,枚举约 10–12rem',
|
|
33
|
+
notesEn: 'Filters via MPageFilters; search ~14rem, selects ~10–12rem',
|
|
34
|
+
},
|
|
35
|
+
'filters-dense': {
|
|
36
|
+
snippetId: 'list-filters-dense',
|
|
37
|
+
notesZh: '高密列表用 list-filters-dense(plain + small)',
|
|
38
|
+
notesEn: 'Dense lists use list-filters-dense (plain + small)',
|
|
39
|
+
},
|
|
40
|
+
chips: {
|
|
41
|
+
snippetId: 'list-filter-chips',
|
|
42
|
+
notesZh: '已选条件用 MPageFilterChips + closable MTag',
|
|
43
|
+
notesEn: 'Active filters via MPageFilterChips + closable MTag',
|
|
44
|
+
},
|
|
45
|
+
header: {
|
|
46
|
+
snippetId: 'page-header-actions',
|
|
47
|
+
notesZh: '页头身份 + 视口内唯一 filled primary',
|
|
48
|
+
notesEn: 'Page identity + one filled primary in the viewport',
|
|
49
|
+
},
|
|
50
|
+
toolbar: {
|
|
51
|
+
snippetId: 'list-batch-toolbar',
|
|
52
|
+
notesZh: '批量操作放 MPageToolbar,不要塞进页头',
|
|
53
|
+
notesEn: 'Batch actions in MPageToolbar, not the page header',
|
|
54
|
+
},
|
|
55
|
+
table: {
|
|
56
|
+
snippetId: 'list-table',
|
|
57
|
+
notesZh: 'MTable + rows;状态列 MStatus;数字列 align end;空态 MEmpty',
|
|
58
|
+
notesEn: 'MTable + rows; status → MStatus; numbers align end; empty → MEmpty',
|
|
59
|
+
},
|
|
60
|
+
'row-actions': {
|
|
61
|
+
snippetId: 'list-row-actions',
|
|
62
|
+
notesZh: '行操作用 small + text',
|
|
63
|
+
notesEn: 'Row actions: size="small" + text',
|
|
64
|
+
},
|
|
65
|
+
status: {
|
|
66
|
+
snippetId: 'list-status-dot',
|
|
67
|
+
notesZh: '业务状态用 MStatus,不要默认 MTag',
|
|
68
|
+
notesEn: 'Business status uses MStatus, not default MTag',
|
|
69
|
+
},
|
|
70
|
+
empty: {
|
|
71
|
+
snippetId: 'empty-block',
|
|
72
|
+
notesZh: '空态带下一步 CTA',
|
|
73
|
+
notesEn: 'Empty state with a next-step CTA',
|
|
74
|
+
},
|
|
75
|
+
'form-header': {
|
|
76
|
+
snippetId: 'form-header',
|
|
77
|
+
notesZh: '窄宽表单页标题',
|
|
78
|
+
notesEn: 'Narrow form page header',
|
|
79
|
+
},
|
|
80
|
+
'form-body': {
|
|
81
|
+
snippetId: 'form-body',
|
|
82
|
+
notesZh: 'MPageSection form + MForm;左标签时设 label-width',
|
|
83
|
+
notesEn: 'MPageSection form + MForm; set label-width for left labels',
|
|
84
|
+
},
|
|
85
|
+
'form-actions': {
|
|
86
|
+
snippetId: 'form-actions',
|
|
87
|
+
notesZh: '保存 primary + loading;取消 secondary',
|
|
88
|
+
notesEn: 'Save primary + loading; cancel secondary',
|
|
89
|
+
},
|
|
90
|
+
kpi: {
|
|
91
|
+
snippetId: 'dashboard-kpi-grid',
|
|
92
|
+
notesZh: 'MPageStat 带 trend / trendDirection / trendSeverity',
|
|
93
|
+
notesEn: 'MPageStat with trend / trendDirection / trendSeverity',
|
|
94
|
+
},
|
|
95
|
+
chart: {
|
|
96
|
+
snippetId: 'dashboard-chart-card',
|
|
97
|
+
notesZh: '图表区 MCard + MSkeleton / MEmpty 占位',
|
|
98
|
+
notesEn: 'Chart area: MCard + MSkeleton / MEmpty placeholder',
|
|
99
|
+
},
|
|
100
|
+
recent: {
|
|
101
|
+
snippetId: 'dashboard-recent-table',
|
|
102
|
+
notesZh: '卡片内小表 size="small"',
|
|
103
|
+
notesEn: 'In-card table with size="small"',
|
|
104
|
+
},
|
|
105
|
+
detail: {
|
|
106
|
+
snippetId: 'detail-toolbar',
|
|
107
|
+
notesZh: '详情顶栏:状态 + 唯一编辑 primary',
|
|
108
|
+
notesEn: 'Detail toolbar: status + one edit primary',
|
|
109
|
+
},
|
|
110
|
+
result: {
|
|
111
|
+
snippetId: 'result-block',
|
|
112
|
+
notesZh: '流程终点用 MResult',
|
|
113
|
+
notesEn: 'Terminal outcomes use MResult',
|
|
114
|
+
},
|
|
115
|
+
wizard: {
|
|
116
|
+
snippetId: 'wizard-steps',
|
|
117
|
+
notesZh: '分步用 MStepper + steps',
|
|
118
|
+
notesEn: 'Wizard uses MStepper + steps',
|
|
119
|
+
},
|
|
120
|
+
auth: {
|
|
121
|
+
snippetId: 'auth-split-shell',
|
|
122
|
+
notesZh: '登录分栏壳;品牌面只用 --m-*',
|
|
123
|
+
notesEn: 'Auth split shell; brand panel uses --m-* only',
|
|
124
|
+
},
|
|
125
|
+
shell: {
|
|
126
|
+
snippetId: 'layout-app-shell',
|
|
127
|
+
notesZh: '后台骨架 MLayout + 菜单 icon',
|
|
128
|
+
notesEn: 'Admin shell: MLayout + menu icons',
|
|
129
|
+
},
|
|
130
|
+
dialog: {
|
|
131
|
+
snippetId: 'form-in-dialog',
|
|
132
|
+
notesZh: '短 CRUD 弹窗表单',
|
|
133
|
+
notesEn: 'Short CRUD dialog form',
|
|
134
|
+
},
|
|
135
|
+
drawer: {
|
|
136
|
+
snippetId: 'form-in-drawer',
|
|
137
|
+
notesZh: '侧栏编辑用 Drawer',
|
|
138
|
+
notesEn: 'Side editing uses Drawer',
|
|
139
|
+
},
|
|
140
|
+
confirm: {
|
|
141
|
+
snippetId: 'confirm-delete',
|
|
142
|
+
notesZh: '删除确认',
|
|
143
|
+
notesEn: 'Delete confirmation',
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
const SURFACE_DEFAULT_BLOCKS = {
|
|
147
|
+
'ops-list': ['shell', 'header', 'filters', 'table', 'status', 'row-actions'],
|
|
148
|
+
'ops-form': ['form-header', 'form-body', 'form-actions'],
|
|
149
|
+
'ops-dashboard': ['header', 'kpi', 'chart', 'recent'],
|
|
150
|
+
'ops-detail': ['detail', 'dialog'],
|
|
151
|
+
'ops-settings': ['form-header', 'form-body', 'form-actions'],
|
|
152
|
+
account: ['auth'],
|
|
153
|
+
'flow-empty': ['empty'],
|
|
154
|
+
'flow-result': ['result'],
|
|
155
|
+
'flow-wizard': ['wizard', 'form-body', 'form-actions'],
|
|
156
|
+
express: ['header'],
|
|
157
|
+
'overlay-dialog': ['dialog'],
|
|
158
|
+
'overlay-drawer': ['drawer'],
|
|
159
|
+
};
|
|
160
|
+
const SURFACE_SHELL = {
|
|
161
|
+
'ops-list': 'ops-quiet',
|
|
162
|
+
'ops-form': 'ops-quiet',
|
|
163
|
+
'ops-dashboard': 'ops-quiet',
|
|
164
|
+
'ops-detail': 'ops-quiet',
|
|
165
|
+
'ops-settings': 'ops-quiet',
|
|
166
|
+
account: 'account-split',
|
|
167
|
+
'flow-empty': 'flow-empty-frame',
|
|
168
|
+
'flow-result': 'flow-result-hero',
|
|
169
|
+
'flow-wizard': 'ops-quiet',
|
|
170
|
+
express: 'express-hero',
|
|
171
|
+
'overlay-dialog': null,
|
|
172
|
+
'overlay-drawer': null,
|
|
173
|
+
};
|
|
174
|
+
const TOKEN_ROLES = [
|
|
175
|
+
{
|
|
176
|
+
role: 'page-bg',
|
|
177
|
+
token: '--m-color-surface',
|
|
178
|
+
notesZh: '页面底',
|
|
179
|
+
notesEn: 'Page background',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
role: 'text',
|
|
183
|
+
token: '--m-color-text',
|
|
184
|
+
notesZh: '正文',
|
|
185
|
+
notesEn: 'Body text',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
role: 'muted',
|
|
189
|
+
token: '--m-color-text-muted',
|
|
190
|
+
notesZh: '次要说明',
|
|
191
|
+
notesEn: 'Secondary copy',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
role: 'border',
|
|
195
|
+
token: '--m-color-border',
|
|
196
|
+
notesZh: '分隔 / 边框',
|
|
197
|
+
notesEn: 'Dividers / borders',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
role: 'brand',
|
|
201
|
+
token: '--m-color-primary',
|
|
202
|
+
notesZh: '主操作与品牌强调;品牌差异优先改主题,不在页面写死色',
|
|
203
|
+
notesEn: 'Primary actions and brand accent; prefer theme overrides over page hex',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
role: 'danger',
|
|
207
|
+
token: '--m-color-danger',
|
|
208
|
+
notesZh: '破坏性操作',
|
|
209
|
+
notesEn: 'Destructive actions',
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
role: 'space',
|
|
213
|
+
token: '--m-space-4 / --m-space-6',
|
|
214
|
+
notesZh: '区块间距',
|
|
215
|
+
notesEn: 'Section spacing',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
role: 'radius',
|
|
219
|
+
token: '--m-radius-md',
|
|
220
|
+
notesZh: '签名面圆角',
|
|
221
|
+
notesEn: 'Signature surface radius',
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
function normalizeSurface(raw, description, intent) {
|
|
225
|
+
const key = (raw || '').trim().toLowerCase();
|
|
226
|
+
if (key && SURFACE_ALIASES[key])
|
|
227
|
+
return SURFACE_ALIASES[key];
|
|
228
|
+
if (key && Object.keys(SURFACE_DEFAULT_BLOCKS).includes(key)) {
|
|
229
|
+
return key;
|
|
230
|
+
}
|
|
231
|
+
const blob = `${description} ${intent}`.toLowerCase();
|
|
232
|
+
if (/登录|注册|auth|login|sign\s*in/.test(blob))
|
|
233
|
+
return 'account';
|
|
234
|
+
if (/落地|营销|landing|pricing|官网|hero/.test(blob))
|
|
235
|
+
return 'express';
|
|
236
|
+
if (/仪表盘|dashboard|kpi|监控|工作台/.test(blob))
|
|
237
|
+
return 'ops-dashboard';
|
|
238
|
+
if (/向导|分步|wizard|stepper/.test(blob))
|
|
239
|
+
return 'flow-wizard';
|
|
240
|
+
if (/空状态|无数据|empty/.test(blob))
|
|
241
|
+
return 'flow-empty';
|
|
242
|
+
if (/结果|403|404|500|result/.test(blob))
|
|
243
|
+
return 'flow-result';
|
|
244
|
+
if (/抽屉|drawer/.test(blob))
|
|
245
|
+
return 'overlay-drawer';
|
|
246
|
+
if (/弹窗|dialog|modal/.test(blob))
|
|
247
|
+
return 'overlay-dialog';
|
|
248
|
+
if (/详情|detail|概览/.test(blob))
|
|
249
|
+
return 'ops-detail';
|
|
250
|
+
if (/设置|settings|偏好/.test(blob))
|
|
251
|
+
return 'ops-settings';
|
|
252
|
+
if (/表单|新建|编辑|form|create|edit/.test(blob) && !/列表|list|table/.test(blob))
|
|
253
|
+
return 'ops-form';
|
|
254
|
+
return 'ops-list';
|
|
255
|
+
}
|
|
256
|
+
function normalizeDensity(raw, blob) {
|
|
257
|
+
const d = (raw || '').trim().toLowerCase();
|
|
258
|
+
if (d === 'compact' || d === 'dense' || d === '高密' || d === '紧凑')
|
|
259
|
+
return 'compact';
|
|
260
|
+
if (d === 'spacious' || d === 'loose' || d === '宽松' || d === '留白')
|
|
261
|
+
return 'spacious';
|
|
262
|
+
if (/dense|compact|高密|紧凑|工具台/.test(blob))
|
|
263
|
+
return 'compact';
|
|
264
|
+
if (/spacious|宽松|留白|宽松/.test(blob))
|
|
265
|
+
return 'spacious';
|
|
266
|
+
return 'default';
|
|
267
|
+
}
|
|
268
|
+
function normalizeBlockId(block) {
|
|
269
|
+
const b = block.trim().toLowerCase();
|
|
270
|
+
const aliases = {
|
|
271
|
+
filter: 'filters',
|
|
272
|
+
筛选: 'filters',
|
|
273
|
+
高级筛选: 'filters',
|
|
274
|
+
search: 'filters',
|
|
275
|
+
已选: 'chips',
|
|
276
|
+
'filter-chips': 'chips',
|
|
277
|
+
页头: 'header',
|
|
278
|
+
title: 'header',
|
|
279
|
+
批量: 'toolbar',
|
|
280
|
+
表格: 'table',
|
|
281
|
+
列表: 'table',
|
|
282
|
+
状态: 'status',
|
|
283
|
+
行操作: 'row-actions',
|
|
284
|
+
actions: 'row-actions',
|
|
285
|
+
空态: 'empty',
|
|
286
|
+
表单: 'form-body',
|
|
287
|
+
保存: 'form-actions',
|
|
288
|
+
指标: 'kpi',
|
|
289
|
+
stats: 'kpi',
|
|
290
|
+
图表: 'chart',
|
|
291
|
+
最近: 'recent',
|
|
292
|
+
登录: 'auth',
|
|
293
|
+
brand: 'auth',
|
|
294
|
+
品牌: 'auth',
|
|
295
|
+
骨架: 'shell',
|
|
296
|
+
layout: 'shell',
|
|
297
|
+
确认: 'confirm',
|
|
298
|
+
向导: 'wizard',
|
|
299
|
+
结果: 'result',
|
|
300
|
+
};
|
|
301
|
+
return aliases[b] || b;
|
|
302
|
+
}
|
|
303
|
+
function componentHintForBlock(block) {
|
|
304
|
+
switch (block) {
|
|
305
|
+
case 'filters':
|
|
306
|
+
case 'filters-dense':
|
|
307
|
+
return ['PageFilters', 'Input', 'Select'];
|
|
308
|
+
case 'table':
|
|
309
|
+
return ['Table', 'Empty'];
|
|
310
|
+
case 'status':
|
|
311
|
+
return ['Status'];
|
|
312
|
+
case 'kpi':
|
|
313
|
+
return ['PageStat', 'Grid'];
|
|
314
|
+
case 'chart':
|
|
315
|
+
return ['Card', 'Skeleton', 'Empty'];
|
|
316
|
+
case 'auth':
|
|
317
|
+
return ['Form', 'InputPassword', 'Button'];
|
|
318
|
+
case 'dialog':
|
|
319
|
+
return ['Dialog', 'Form'];
|
|
320
|
+
case 'drawer':
|
|
321
|
+
return ['Drawer', 'Form'];
|
|
322
|
+
case 'result':
|
|
323
|
+
return ['Result'];
|
|
324
|
+
case 'empty':
|
|
325
|
+
return ['Empty'];
|
|
326
|
+
case 'wizard':
|
|
327
|
+
return ['Stepper', 'Form'];
|
|
328
|
+
default:
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Map a reference brief to snippets, shell, and validation hints.
|
|
334
|
+
*/
|
|
335
|
+
export function mapReferenceBrief(input) {
|
|
336
|
+
const description = (input.description || '').trim();
|
|
337
|
+
const intent = (input.intent || '').trim();
|
|
338
|
+
const blob = `${description} ${intent} ${(input.style || '').trim()}`;
|
|
339
|
+
const surface = normalizeSurface(input.surface, description, intent);
|
|
340
|
+
const density = normalizeDensity(input.density, blob);
|
|
341
|
+
const requestedBlocks = (input.requiredBlocks || []).map(normalizeBlockId).filter(Boolean);
|
|
342
|
+
let blocks = requestedBlocks.length > 0 ? [...requestedBlocks] : [...SURFACE_DEFAULT_BLOCKS[surface]];
|
|
343
|
+
if (density === 'compact' && blocks.includes('filters')) {
|
|
344
|
+
blocks = blocks.map((b) => (b === 'filters' ? 'filters-dense' : b));
|
|
345
|
+
}
|
|
346
|
+
const mapping = [];
|
|
347
|
+
const suggestedSnippets = [];
|
|
348
|
+
for (const block of blocks) {
|
|
349
|
+
const entry = BLOCK_TO_SNIPPET[block];
|
|
350
|
+
if (!entry)
|
|
351
|
+
continue;
|
|
352
|
+
mapping.push({
|
|
353
|
+
block,
|
|
354
|
+
snippetId: entry.snippetId,
|
|
355
|
+
notesZh: entry.notesZh,
|
|
356
|
+
notesEn: entry.notesEn,
|
|
357
|
+
});
|
|
358
|
+
if (!suggestedSnippets.includes(entry.snippetId)) {
|
|
359
|
+
suggestedSnippets.push(entry.snippetId);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const requiredComponents = new Set((input.requiredComponents || []).map((c) => c.replace(/^M/, '')));
|
|
363
|
+
for (const block of blocks) {
|
|
364
|
+
for (const c of componentHintForBlock(block))
|
|
365
|
+
requiredComponents.add(c);
|
|
366
|
+
}
|
|
367
|
+
const craftCuesZh = [
|
|
368
|
+
'按 mapping 逐块 get_page_snippet,不要整页克隆黄金样例',
|
|
369
|
+
'品牌色差异优先覆盖主题 --m-color-primary / 字体,页面 CSS 只用 token',
|
|
370
|
+
density === 'compact'
|
|
371
|
+
? '密度 compact:筛选 plain+small,列表可对照 list-page-dense'
|
|
372
|
+
: density === 'spacious'
|
|
373
|
+
? '密度 spacious:加大区块间距,避免塞满'
|
|
374
|
+
: '密度 default:对齐 Ops polish(一主按钮、菜单 icon、MStatus)',
|
|
375
|
+
];
|
|
376
|
+
if (input.primaryAction) {
|
|
377
|
+
craftCuesZh.push(`主操作文案对齐参考:「${input.primaryAction}」`);
|
|
378
|
+
}
|
|
379
|
+
const craftCuesEn = [
|
|
380
|
+
'Fill blocks via get_page_snippet from mapping — do not clone a whole golden page',
|
|
381
|
+
'Brand differences belong in theme tokens (--m-color-primary / fonts), not page hex',
|
|
382
|
+
density === 'compact'
|
|
383
|
+
? 'Density compact: plain+small filters; optional list-page-dense structure check'
|
|
384
|
+
: density === 'spacious'
|
|
385
|
+
? 'Density spacious: more section gap; avoid packing'
|
|
386
|
+
: 'Density default: Ops polish (one primary, menu icons, MStatus)',
|
|
387
|
+
];
|
|
388
|
+
if (input.primaryAction) {
|
|
389
|
+
craftCuesEn.push(`Primary CTA label from reference: “${input.primaryAction}”`);
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
surface,
|
|
393
|
+
density,
|
|
394
|
+
styleSummary: (input.style || description || null) && (input.style || description || '').trim()
|
|
395
|
+
? (input.style || description).trim()
|
|
396
|
+
: null,
|
|
397
|
+
suggestedSnippets,
|
|
398
|
+
suggestedShellId: SURFACE_SHELL[surface],
|
|
399
|
+
mapping,
|
|
400
|
+
tokenRoles: TOKEN_ROLES,
|
|
401
|
+
craftCuesZh,
|
|
402
|
+
craftCuesEn,
|
|
403
|
+
validationHints: {
|
|
404
|
+
requiredBlocks: blocks,
|
|
405
|
+
requiredComponents: [...requiredComponents],
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/** Detect whether generated code covers a required block (heuristic). */
|
|
410
|
+
export function codeCoversBlock(code, block) {
|
|
411
|
+
const c = code;
|
|
412
|
+
switch (normalizeBlockId(block)) {
|
|
413
|
+
case 'filters':
|
|
414
|
+
case 'filters-dense':
|
|
415
|
+
return /<MPageFilters\b/i.test(c);
|
|
416
|
+
case 'chips':
|
|
417
|
+
return /<MPageFilterChips\b/i.test(c);
|
|
418
|
+
case 'header':
|
|
419
|
+
return /<MPageHeader\b/i.test(c);
|
|
420
|
+
case 'toolbar':
|
|
421
|
+
return /<MPageToolbar\b/i.test(c);
|
|
422
|
+
case 'table':
|
|
423
|
+
return /<MTable\b/i.test(c);
|
|
424
|
+
case 'status':
|
|
425
|
+
return /<MStatus\b/i.test(c);
|
|
426
|
+
case 'row-actions':
|
|
427
|
+
return /#cell-actions|<MDropdown\b/i.test(c);
|
|
428
|
+
case 'empty':
|
|
429
|
+
return /<MEmpty\b/i.test(c);
|
|
430
|
+
case 'form-header':
|
|
431
|
+
return /<MPageHeader\b/i.test(c);
|
|
432
|
+
case 'form-body':
|
|
433
|
+
return /<MForm\b/i.test(c);
|
|
434
|
+
case 'form-actions':
|
|
435
|
+
return /native-type=["']submit["']|type=["']submit["']|<MPageSection\b[^>]*variant=["']actions["']/i.test(c);
|
|
436
|
+
case 'kpi':
|
|
437
|
+
return /<MPageStat\b/i.test(c);
|
|
438
|
+
case 'chart':
|
|
439
|
+
return /<MCard\b/i.test(c) && (/<MSkeleton\b|<MEmpty\b/i.test(c) || /chart|图表/i.test(c));
|
|
440
|
+
case 'recent':
|
|
441
|
+
return /<MTable\b/i.test(c);
|
|
442
|
+
case 'detail':
|
|
443
|
+
return /<MPageHeader\b/i.test(c);
|
|
444
|
+
case 'result':
|
|
445
|
+
return /<MResult\b/i.test(c);
|
|
446
|
+
case 'wizard':
|
|
447
|
+
return /<MStepper\b/i.test(c);
|
|
448
|
+
case 'auth':
|
|
449
|
+
return /login-|auth-|<MInputPassword\b/i.test(c);
|
|
450
|
+
case 'shell':
|
|
451
|
+
return /<MLayout\b/i.test(c);
|
|
452
|
+
case 'dialog':
|
|
453
|
+
return /<MDialog\b/i.test(c);
|
|
454
|
+
case 'drawer':
|
|
455
|
+
return /<MDrawer\b/i.test(c);
|
|
456
|
+
case 'confirm':
|
|
457
|
+
return /<MConfirm(?:Dialog|Popup)\b/i.test(c);
|
|
458
|
+
default:
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
export function codeCoversComponent(code, component) {
|
|
463
|
+
const name = component.replace(/^M/, '').trim();
|
|
464
|
+
if (!name)
|
|
465
|
+
return true;
|
|
466
|
+
const re = new RegExp(`<M${name}\\b`, 'i');
|
|
467
|
+
return re.test(code);
|
|
468
|
+
}
|
package/dist/style-presets.d.ts
CHANGED
|
@@ -1,24 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Structure still comes from golden pages;
|
|
2
|
+
* Visual style direction for generated pages — no named preset catalog.
|
|
3
|
+
* Structure still comes from golden pages; look follows the user’s reference / words.
|
|
4
4
|
*/
|
|
5
|
-
export interface StylePreset {
|
|
6
|
-
id: string;
|
|
7
|
-
title: string;
|
|
8
|
-
titleEn: string;
|
|
9
|
-
summary: string;
|
|
10
|
-
summaryEn: string;
|
|
11
|
-
lanes: Array<'ops' | 'account' | 'flow' | 'express' | 'system'>;
|
|
12
|
-
keywords: string[];
|
|
13
|
-
cues: string[];
|
|
14
|
-
cuesEn: string[];
|
|
15
|
-
apply: string[];
|
|
16
|
-
applyEn: string[];
|
|
17
|
-
avoid: string[];
|
|
18
|
-
avoidEn: string[];
|
|
19
|
-
/** Preferred list golden craft variant when pattern is admin-list. */
|
|
20
|
-
listGoldenPage?: string;
|
|
21
|
-
}
|
|
22
5
|
/** How agents pick a visual direction before coding. */
|
|
23
6
|
export declare const styleResolution: {
|
|
24
7
|
priorityZh: string[];
|
|
@@ -26,25 +9,23 @@ export declare const styleResolution: {
|
|
|
26
9
|
hardRulesZh: string[];
|
|
27
10
|
hardRulesEn: string[];
|
|
28
11
|
};
|
|
29
|
-
export
|
|
30
|
-
export declare function findStylePreset(idOrAlias: string): StylePreset | undefined;
|
|
31
|
-
export declare function scoreStylePreset(preset: StylePreset, query: string): number;
|
|
12
|
+
export type StyleResolutionKind = 'explicit' | 'inferred' | 'ask';
|
|
32
13
|
export interface StyleDirectionResult {
|
|
33
|
-
resolution:
|
|
34
|
-
|
|
14
|
+
resolution: StyleResolutionKind;
|
|
15
|
+
/** Free-text summary of the direction (user words or inferred reading). */
|
|
16
|
+
summary?: string;
|
|
35
17
|
confidence: number;
|
|
36
|
-
candidates: Array<{
|
|
37
|
-
id: string;
|
|
38
|
-
title: string;
|
|
39
|
-
titleEn: string;
|
|
40
|
-
score: number;
|
|
41
|
-
}>;
|
|
42
18
|
guidanceZh: string;
|
|
43
19
|
guidanceEn: string;
|
|
44
20
|
}
|
|
45
21
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
22
|
+
* Pick list golden craft variant from free-text style/intent.
|
|
23
|
+
* Returns null to keep the canonical list-page.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveListCraftVariant(text: string | null | undefined): 'list-page-dense' | 'list-page-rail' | null;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve visual direction from an optional free-text style string and/or intent.
|
|
28
|
+
* No preset catalog — explicit user words win; otherwise ask or carefully infer.
|
|
48
29
|
*/
|
|
49
30
|
export declare function resolveStyleDirection(args: {
|
|
50
31
|
intent?: string;
|