@morya-ui/mcp 0.2.4 → 0.2.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/README.md +1 -1
- package/data/catalog.json +1831 -1017
- package/data/example-coverage.json +98 -5
- package/data/golden-pages/dashboard-page.vue +1 -1
- package/data/golden-pages/empty-state.vue +66 -0
- package/data/golden-pages/form-page.vue +7 -3
- package/data/golden-pages/landing-page.vue +328 -0
- package/data/golden-pages/list-page.vue +16 -6
- package/data/golden-pages/login-page.vue +191 -0
- package/dist/__tests__/resources.test.js +1 -1
- package/dist/__tests__/tools.test.js +7 -0
- package/dist/golden-pages.js +22 -1
- package/dist/index.js +1 -1
- package/dist/page-snippets.js +66 -2
- package/dist/patterns.d.ts +17 -5
- package/dist/patterns.js +191 -29
- package/dist/resources.d.ts +1 -1
- package/dist/tools.js +59 -17
- package/package.json +1 -1
package/dist/patterns.js
CHANGED
|
@@ -40,7 +40,8 @@ export const pagePatterns = [
|
|
|
40
40
|
{ component: 'Select', role: '枚举或状态筛选' },
|
|
41
41
|
{ component: 'Button', role: '查询、新增和行操作' },
|
|
42
42
|
{ component: 'Table', role: '数据展示', reason: 'paginator 启用内置分页' },
|
|
43
|
-
{ component: 'Tag', role: '
|
|
43
|
+
{ component: 'Tag', role: '业务状态展示(分类/可关闭)', required: false },
|
|
44
|
+
{ component: 'Status', role: '行内状态圆点+文案', required: false },
|
|
44
45
|
{ component: 'Dialog', role: '编辑或删除确认', required: false },
|
|
45
46
|
],
|
|
46
47
|
structure: [
|
|
@@ -65,7 +66,7 @@ export const pagePatterns = [
|
|
|
65
66
|
'新增、保存等主操作使用 primary MButton',
|
|
66
67
|
'查询等次要操作使用 secondary 或 outlined MButton',
|
|
67
68
|
'删除使用 danger MButton,并配合 MDialog 或 MConfirmDialog',
|
|
68
|
-
'
|
|
69
|
+
'业务状态优先使用 MStatus(圆点+文案)或 MTag(分类标签),不使用 Button severity 表达状态',
|
|
69
70
|
'间距、颜色、圆角优先使用 --m-* 设计令牌,不直接硬编码色板',
|
|
70
71
|
],
|
|
71
72
|
interactionRules: [
|
|
@@ -163,7 +164,10 @@ export const pagePatterns = [
|
|
|
163
164
|
{ component: 'GridItem', role: '栅格子项' },
|
|
164
165
|
{ component: 'Card', role: '图表与列表明细分区' },
|
|
165
166
|
{ component: 'Icon', role: 'KPI 图标', required: false },
|
|
166
|
-
{ component: 'Tag', role: '
|
|
167
|
+
{ component: 'Tag', role: '分类标签和告警级别', required: false },
|
|
168
|
+
{ component: 'Status', role: '行内状态圆点+文案', required: false },
|
|
169
|
+
{ component: 'Empty', role: '明细表空态', required: false },
|
|
170
|
+
{ component: 'Result', role: '接口失败结果区', required: false },
|
|
167
171
|
{ component: 'Table', role: '明细数据', required: false },
|
|
168
172
|
{ component: 'Button', role: '刷新、查看详情和快捷操作', required: false },
|
|
169
173
|
{ component: 'Skeleton', role: '初始加载占位', required: false },
|
|
@@ -186,8 +190,8 @@ export const pagePatterns = [
|
|
|
186
190
|
content: '图表和明细按业务优先级排列,重要告警靠前',
|
|
187
191
|
responsive: '多列网格在窄屏降为单列或两列',
|
|
188
192
|
},
|
|
189
|
-
styleRules: ['指标使用 text hierarchy,不用大面积高饱和背景', '
|
|
190
|
-
interactionRules: ['首次加载使用 Skeleton', '
|
|
193
|
+
styleRules: ['指标使用 text hierarchy,不用大面积高饱和背景', '状态用 MStatus / 告警级别用 MTag', '图表容器使用 Card 和 --m-* Token 保持表面一致', '实时刷新操作使用 secondary 或 text MButton'],
|
|
194
|
+
interactionRules: ['首次加载使用 Skeleton', '无数据用 MEmpty;接口错误用 MResult 或 Message', '刷新中禁用重复请求并保留上次数据', '告警需要可追踪到详情或处理入口'],
|
|
191
195
|
avoid: ['不要把所有信息都做成 KPI 卡片', '不要使用颜色作为唯一告警表达', '不要用固定像素绝对定位实现响应式布局'],
|
|
192
196
|
},
|
|
193
197
|
{
|
|
@@ -219,17 +223,29 @@ export const pagePatterns = [
|
|
|
219
223
|
titleEn: 'Empty state / no results page',
|
|
220
224
|
description: '用于首次使用、搜索无结果、资源已清空或暂时没有内容的场景。',
|
|
221
225
|
descriptionEn: 'For first use, no search results, empty resources, or temporarily unavailable content.',
|
|
226
|
+
goldenPage: 'docs/golden-pages/empty-state.vue',
|
|
222
227
|
keywords: ['空状态', '无数据', '无结果', '首次使用', 'empty', 'no results', 'no data', 'zero state'],
|
|
223
228
|
components: [
|
|
224
|
-
{ component: '
|
|
229
|
+
{ component: 'PageContent', role: '承载空态的内容区', required: false },
|
|
230
|
+
{ component: 'PageToolbar', role: '保留列表上下文标题与主操作', required: false },
|
|
231
|
+
{ component: 'Empty', role: '空态主体:图标、标题、说明与操作', required: true },
|
|
232
|
+
{ component: 'Icon', role: '空态视觉锚点(也可由 Empty icon 提供)', required: false },
|
|
225
233
|
{ component: 'Button', role: '创建、重置筛选或返回', required: false },
|
|
234
|
+
{ component: 'Table', role: '表格 #empty 插槽承载', required: false },
|
|
235
|
+
{ component: 'DataView', role: '列表或网格空态承载', required: false },
|
|
226
236
|
{ component: 'Card', role: '在列表或详情容器中承载空态', required: false },
|
|
227
237
|
],
|
|
228
|
-
structure: [
|
|
238
|
+
structure: [
|
|
239
|
+
'MPageContent (or MTable #empty)',
|
|
240
|
+
'└── MEmpty',
|
|
241
|
+
' ├── icon / image',
|
|
242
|
+
' ├── title + description',
|
|
243
|
+
' └── #extra primary + optional secondary MButton',
|
|
244
|
+
],
|
|
229
245
|
layout: { page: '空态在当前内容区域内居中,不一定占满整个视口', content: '说明原因、下一步动作和可选的辅助信息', responsive: '保持按钮可触达,窄屏垂直排列' },
|
|
230
|
-
styleRules: ['空态文案使用中性、可行动的语气', '主恢复动作使用 primary MButton,次动作使用 text', '不要用错误色表达正常的无数据状态'],
|
|
246
|
+
styleRules: ['空态文案使用中性、可行动的语气', '主恢复动作使用 primary MButton,次动作使用 text', '不要用错误色表达正常的无数据状态', '颜色与间距使用 --m-* Token'],
|
|
231
247
|
interactionRules: ['搜索无结果提供清除筛选或修改条件入口', '首次使用提供创建或导入入口', '保留页面标题和上下文导航'],
|
|
232
|
-
avoid: ['不要只显示“暂无数据”而没有下一步', '不要把无数据和接口错误混为一谈'],
|
|
248
|
+
avoid: ['不要只显示“暂无数据”而没有下一步', '不要把无数据和接口错误混为一谈', '不要堆 emoji 或促销贴纸'],
|
|
233
249
|
},
|
|
234
250
|
{
|
|
235
251
|
id: 'auth-page',
|
|
@@ -237,22 +253,79 @@ export const pagePatterns = [
|
|
|
237
253
|
titleEn: 'Login / authentication page',
|
|
238
254
|
description: '用于登录、注册、找回密码和二次认证流程。',
|
|
239
255
|
descriptionEn: 'For login, registration, password recovery, and second-factor authentication flows.',
|
|
256
|
+
goldenPage: 'docs/golden-pages/login-page.vue',
|
|
240
257
|
keywords: ['登录', '注册', '认证', '密码', '验证码', 'login', 'register', 'authentication', 'password', 'otp'],
|
|
241
258
|
components: [
|
|
242
|
-
{ component: '
|
|
259
|
+
{ component: 'ConfigProvider', role: '应用根包裹' },
|
|
243
260
|
{ component: 'Form', role: '认证字段校验' },
|
|
244
|
-
{ component: 'FormItem', role: '字段标签和错误信息', required: false
|
|
245
|
-
{ component: 'Input', role: '
|
|
261
|
+
{ component: 'FormItem', role: '字段标签和错误信息', required: false },
|
|
262
|
+
{ component: 'Input', role: '账号字段' },
|
|
263
|
+
{ component: 'InputPassword', role: '密码字段' },
|
|
246
264
|
{ component: 'InputOtp', role: '验证码字段', required: false },
|
|
247
265
|
{ component: 'Button', role: '提交认证' },
|
|
248
|
-
{ component: '
|
|
266
|
+
{ component: 'Space', role: '主次操作排列', required: false },
|
|
249
267
|
],
|
|
250
|
-
structure: [
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
268
|
+
structure: [
|
|
269
|
+
'MConfigProvider',
|
|
270
|
+
'└── Split or centered shell',
|
|
271
|
+
' ├── Brand panel (optional Express craft)',
|
|
272
|
+
' └── Form panel',
|
|
273
|
+
' ├── Persistent form-level alert (token-styled) when needed',
|
|
274
|
+
' ├── MForm + MInput / MInputPassword',
|
|
275
|
+
' └── Submit + recovery links',
|
|
276
|
+
],
|
|
277
|
+
layout: { page: '认证表单使用窄 max-width 并保持视觉聚焦', form: '字段垂直排列', feedback: '字段错误用 errorMessage;表单级常驻错误用 token 告警条,勿默认 Toast' },
|
|
278
|
+
styleRules: ['密码字段使用 MInputPassword', '提交按钮使用 primary', '品牌视觉可定制,但颜色仍映射 --m-*', '不要用高对比装饰削弱错误信息和焦点状态'],
|
|
279
|
+
interactionRules: ['提交中显示 loading 并防止重复提交', '错误不清空用户已填写的非敏感字段', '支持键盘提交和清晰焦点顺序'],
|
|
254
280
|
avoid: ['不要用 placeholder 代替字段 label', '不要把所有认证错误只放在 Toast 中', '不要在错误时回显密码'],
|
|
255
281
|
},
|
|
282
|
+
{
|
|
283
|
+
id: 'marketing-landing',
|
|
284
|
+
title: '营销落地页',
|
|
285
|
+
titleEn: 'Marketing landing page',
|
|
286
|
+
description: '用于产品官网、发布页、定价前导等公开营销表面;首屏单一任务,控件仍用 morya-ui。',
|
|
287
|
+
descriptionEn: 'Public marketing surfaces (product home, launch, pre-pricing). One job in the first viewport; controls still use morya-ui.',
|
|
288
|
+
goldenPage: 'docs/golden-pages/landing-page.vue',
|
|
289
|
+
keywords: [
|
|
290
|
+
'落地页',
|
|
291
|
+
'营销',
|
|
292
|
+
'官网',
|
|
293
|
+
'首页',
|
|
294
|
+
'landing',
|
|
295
|
+
'marketing',
|
|
296
|
+
'pricing',
|
|
297
|
+
'hero',
|
|
298
|
+
'产品介绍',
|
|
299
|
+
],
|
|
300
|
+
components: [
|
|
301
|
+
{ component: 'ConfigProvider', role: '应用根包裹' },
|
|
302
|
+
{ component: 'Button', role: 'CTA' },
|
|
303
|
+
{ component: 'Tag', role: '能力标签', required: false },
|
|
304
|
+
{ component: 'Accordion', role: 'FAQ', required: false },
|
|
305
|
+
{ component: 'Card', role: '套餐或能力卡', required: false },
|
|
306
|
+
],
|
|
307
|
+
structure: [
|
|
308
|
+
'MConfigProvider',
|
|
309
|
+
'├── Nav (brand + links + CTA)',
|
|
310
|
+
'├── Hero (brand, one headline, one lead, CTA group, one visual)',
|
|
311
|
+
'├── Sections (one job each)',
|
|
312
|
+
'└── Footer',
|
|
313
|
+
],
|
|
314
|
+
layout: {
|
|
315
|
+
page: '首屏单一构图;勿做成后台仪表盘',
|
|
316
|
+
hero: '品牌 + 标题 + 一句支持文案 + CTA + 一个主视觉',
|
|
317
|
+
sections: '每节一个目的与一个短说明',
|
|
318
|
+
responsive: '导航链接可折叠;能力卡降为单列',
|
|
319
|
+
},
|
|
320
|
+
styleRules: [
|
|
321
|
+
'交互控件使用 MButton / MTag / MAccordion 等',
|
|
322
|
+
'颜色与间距使用 --m-*,可用 color-mix 做氛围',
|
|
323
|
+
'避开紫渐变、奶油衬线陶土、报纸风等 AI 默认脸(除非 brief 指定)',
|
|
324
|
+
'首屏不要堆统计条、促销胶囊、日程碎片',
|
|
325
|
+
],
|
|
326
|
+
interactionRules: ['CTA 指向明确下一步', 'FAQ 用 Accordion 降低长页噪声', '尊重 prefers-reduced-motion 若加动画'],
|
|
327
|
+
avoid: ['不要用第二套 UI 库', '不要把 Ops 的 MPageFilters 壳套在营销页上', '不要用 inset 小卡片代替首屏主视觉'],
|
|
328
|
+
},
|
|
256
329
|
{
|
|
257
330
|
id: 'wizard-form',
|
|
258
331
|
title: '分步表单 / 向导',
|
|
@@ -285,16 +358,18 @@ export const pagePatterns = [
|
|
|
285
358
|
components: [
|
|
286
359
|
{ component: 'Breadcrumb', role: '页面层级导航' },
|
|
287
360
|
{ component: 'Card', role: '内容分组表面' },
|
|
288
|
-
{ component: '
|
|
361
|
+
{ component: 'Status', role: '标题旁轻量状态(圆点+文案)', required: false },
|
|
362
|
+
{ component: 'Tag', role: '分类/强调态标签', required: false },
|
|
289
363
|
{ component: 'Divider', role: '内容分组分隔', required: false },
|
|
290
364
|
{ component: 'Button', role: '编辑、返回和危险操作' },
|
|
291
365
|
{ component: 'Dialog', role: '危险操作确认', required: false },
|
|
366
|
+
{ component: 'Result', role: '资源不存在或无权访问时的结果页', required: false },
|
|
292
367
|
],
|
|
293
368
|
structure: [
|
|
294
369
|
'Page',
|
|
295
370
|
'├── Breadcrumb',
|
|
296
371
|
'├── PageHeader',
|
|
297
|
-
'│ ├── Title +
|
|
372
|
+
'│ ├── Title + MStatus (or MTag)',
|
|
298
373
|
'│ └── Actions',
|
|
299
374
|
'├── Summary Card',
|
|
300
375
|
'├── Properties Card',
|
|
@@ -307,14 +382,14 @@ export const pagePatterns = [
|
|
|
307
382
|
actions: '编辑为主操作,返回为次操作,删除放在低强调区域',
|
|
308
383
|
},
|
|
309
384
|
styleRules: [
|
|
310
|
-
'
|
|
385
|
+
'轻量状态优先 MStatus;需要芯片感或可关闭时用 MTag',
|
|
311
386
|
'内容分组使用 MCard,必要时用 MDivider 分隔',
|
|
312
387
|
'编辑使用 primary 或 outlined MButton,删除使用 danger',
|
|
313
388
|
'不使用大面积自定义背景色覆盖组件库 surface Token',
|
|
314
389
|
],
|
|
315
390
|
interactionRules: [
|
|
316
391
|
'详情加载中使用 Skeleton 或 ProgressSpinner',
|
|
317
|
-
'
|
|
392
|
+
'资源不存在用 MResult status="404";列表空数据用 MEmpty,不要混用',
|
|
318
393
|
'危险操作必须确认并在成功后刷新或离开当前详情页',
|
|
319
394
|
],
|
|
320
395
|
avoid: [
|
|
@@ -323,6 +398,61 @@ export const pagePatterns = [
|
|
|
323
398
|
'不要让详情页的操作按钮分散在多个无关区域',
|
|
324
399
|
],
|
|
325
400
|
},
|
|
401
|
+
{
|
|
402
|
+
id: 'result-page',
|
|
403
|
+
title: '结果页 / 阻断页',
|
|
404
|
+
titleEn: 'Result / terminal page',
|
|
405
|
+
description: '用于提交成功、失败回执、403、404、500 等流程终点或阻断场景。',
|
|
406
|
+
descriptionEn: 'Terminal outcomes: success, failure, 403, 404, 500, and other blocking pages.',
|
|
407
|
+
keywords: [
|
|
408
|
+
'结果',
|
|
409
|
+
'成功',
|
|
410
|
+
'失败',
|
|
411
|
+
'403',
|
|
412
|
+
'404',
|
|
413
|
+
'500',
|
|
414
|
+
'无权',
|
|
415
|
+
'不存在',
|
|
416
|
+
'result',
|
|
417
|
+
'success',
|
|
418
|
+
'error',
|
|
419
|
+
'forbidden',
|
|
420
|
+
'not found',
|
|
421
|
+
],
|
|
422
|
+
components: [
|
|
423
|
+
{ component: 'Result', role: '结果主体:图标、标题、说明与操作', required: true },
|
|
424
|
+
{ component: 'Button', role: '下一步:返回、重试、查看详情', required: false },
|
|
425
|
+
{ component: 'PageContent', role: '承载结果区', required: false },
|
|
426
|
+
],
|
|
427
|
+
structure: [
|
|
428
|
+
'MPageContent (optional shell)',
|
|
429
|
+
'└── MResult',
|
|
430
|
+
' ├── status icon',
|
|
431
|
+
' ├── title + description',
|
|
432
|
+
' └── #extra primary + optional secondary MButton',
|
|
433
|
+
],
|
|
434
|
+
layout: {
|
|
435
|
+
page: '结果在内容区居中,不一定占满整屏',
|
|
436
|
+
content: '说明发生了什么,以及用户下一步能做什么',
|
|
437
|
+
responsive: '操作按钮可触达,窄屏垂直排列',
|
|
438
|
+
},
|
|
439
|
+
styleRules: [
|
|
440
|
+
'使用 MResult 的 status 语义,不要手写大图标+文案拼盘',
|
|
441
|
+
'成功/失败用对应 severity 色;403/404 保持中性',
|
|
442
|
+
'主恢复动作使用 primary MButton,次动作使用 secondary 或 text',
|
|
443
|
+
'颜色与间距使用 --m-* Token',
|
|
444
|
+
],
|
|
445
|
+
interactionRules: [
|
|
446
|
+
'成功后提供查看详情或返回列表',
|
|
447
|
+
'失败提供重试或联系支持入口',
|
|
448
|
+
'不要把无数据空态做成 Result;空态用 MEmpty',
|
|
449
|
+
],
|
|
450
|
+
avoid: [
|
|
451
|
+
'不要用 Empty 表达 403/404/提交失败',
|
|
452
|
+
'不要只用颜色表达结果而无标题文案',
|
|
453
|
+
'不要在结果页堆砌无关营销内容',
|
|
454
|
+
],
|
|
455
|
+
},
|
|
326
456
|
];
|
|
327
457
|
/** Advisory page-writing standards for MCP and human authors — not enforced blockers. */
|
|
328
458
|
export const pageStandards = [
|
|
@@ -340,8 +470,16 @@ export const pageStandards = [
|
|
|
340
470
|
'Place MPageContent inside MLayoutContent for consistent padding and section gap',
|
|
341
471
|
'Call get_golden_page before writing a full page and copy the matching structure',
|
|
342
472
|
],
|
|
343
|
-
discouraged: [
|
|
344
|
-
|
|
473
|
+
discouraged: [
|
|
474
|
+
'手写 min-height:100vh',
|
|
475
|
+
'在 MLayoutContent 上写 padding/gap',
|
|
476
|
+
'嵌套多层 MPageContent',
|
|
477
|
+
],
|
|
478
|
+
discouragedEn: [
|
|
479
|
+
'Hand-written min-height:100vh',
|
|
480
|
+
'Padding/gap on MLayoutContent instead of MPageContent',
|
|
481
|
+
'Nesting multiple MPageContent shells',
|
|
482
|
+
],
|
|
345
483
|
mcp: { snippet: 'layout-app-shell' },
|
|
346
484
|
},
|
|
347
485
|
{
|
|
@@ -351,17 +489,24 @@ export const pageStandards = [
|
|
|
351
489
|
recommend: [
|
|
352
490
|
'筛选区用 MPageFilters,标题+操作用 MPageToolbar,表单区用 MPageSection',
|
|
353
491
|
'列表页 MTable 直接放在 MPageContent 内',
|
|
492
|
+
'同行控件用 MSpace / MFlex;区块之间依赖 MPageContent 的 gap',
|
|
354
493
|
'局部区块优先 get_page_snippet(filters / toolbar / form-actions 等)',
|
|
355
494
|
],
|
|
356
495
|
recommendEn: [
|
|
357
496
|
'Use MPageFilters, MPageToolbar, and MPageSection for filters, headers/actions, and forms',
|
|
358
497
|
'Place MTable directly in MPageContent on list pages',
|
|
498
|
+
'Use MSpace / MFlex for same-row controls; let MPageContent gap separate sections',
|
|
359
499
|
'Prefer get_page_snippet for local blocks such as filters, toolbar, or form-actions',
|
|
360
500
|
],
|
|
361
|
-
discouraged: [
|
|
501
|
+
discouraged: [
|
|
502
|
+
'手写 .page-filters / .page-toolbar 类',
|
|
503
|
+
'用 MCard 或带 padding 的容器再包 MPageFilters / MPageSection / MPageToolbar',
|
|
504
|
+
'在已有 MPage* 区块之间再写 margin 叠间距',
|
|
505
|
+
],
|
|
362
506
|
discouragedEn: [
|
|
363
507
|
'Hand-written .page-filters / .page-toolbar classes',
|
|
364
|
-
'Wrapping MPageFilters
|
|
508
|
+
'Wrapping MPageFilters / MPageSection / MPageToolbar with MCard or extra padded containers',
|
|
509
|
+
'Adding margin between MPage* siblings that PageContent gap already spaces',
|
|
365
510
|
],
|
|
366
511
|
mcp: { decision: 'surface-nesting-choice' },
|
|
367
512
|
},
|
|
@@ -493,7 +638,7 @@ export const designRules = {
|
|
|
493
638
|
'Double borders on filter + table wrapper',
|
|
494
639
|
],
|
|
495
640
|
},
|
|
496
|
-
goldenPages: ['list-page', 'form-page', 'dashboard-page'],
|
|
641
|
+
goldenPages: ['list-page', 'form-page', 'dashboard-page', 'login-page', 'landing-page', 'empty-state'],
|
|
497
642
|
},
|
|
498
643
|
actions: {
|
|
499
644
|
primary: { component: 'MButton', props: ['severity omitted or primary'] },
|
|
@@ -502,8 +647,10 @@ export const designRules = {
|
|
|
502
647
|
cancel: { component: 'MButton', props: ['severity="secondary"', 'text'] },
|
|
503
648
|
},
|
|
504
649
|
status: {
|
|
505
|
-
|
|
650
|
+
preferred: 'MStatus',
|
|
651
|
+
chip: 'MTag',
|
|
506
652
|
mapping: { active: 'success', pending: 'warn', disabled: 'secondary', error: 'danger' },
|
|
653
|
+
note: '行内轻量状态用 MStatus;芯片/可关闭标签用 MTag',
|
|
507
654
|
},
|
|
508
655
|
feedback: {
|
|
509
656
|
default: 'message',
|
|
@@ -517,15 +664,30 @@ export const designRules = {
|
|
|
517
664
|
when: ['同时需要标题与补充说明', '后台任务/批量结果含统计', '异步通知感、角落堆叠'],
|
|
518
665
|
avoid: ['仅有单行文案时不要使用 Toast'],
|
|
519
666
|
},
|
|
667
|
+
empty: {
|
|
668
|
+
component: 'MEmpty',
|
|
669
|
+
when: ['列表无数据', '筛选无结果', '首次使用'],
|
|
670
|
+
avoid: ['不要用错误色表达正常空态', '不要用 MResult 表达无数据'],
|
|
671
|
+
},
|
|
672
|
+
result: {
|
|
673
|
+
component: 'MResult',
|
|
674
|
+
when: ['提交成功/失败页', '403 / 404 / 500', '流程终点回执'],
|
|
675
|
+
avoid: ['不要用手写图标+文案替代 MResult', '不要用 MEmpty 表达阻断错误'],
|
|
676
|
+
},
|
|
520
677
|
inlineMessage: {
|
|
521
|
-
|
|
522
|
-
when: [
|
|
678
|
+
api: 'field errorMessage | token-styled role=alert',
|
|
679
|
+
when: [
|
|
680
|
+
'登录/表单区常驻错误:优先字段 errorMessage',
|
|
681
|
+
'表单级总结:使用 --m-* 样式的 role=alert 条(见 login-page 黄金样例)',
|
|
682
|
+
'MMessage 组件当前主要为 message 服务宿主,勿臆造 severity 子节点 API',
|
|
683
|
+
],
|
|
523
684
|
},
|
|
524
685
|
doc: 'docs/feedback-message-vs-toast.md',
|
|
525
686
|
},
|
|
526
687
|
global: [
|
|
527
688
|
'优先使用组件库组件和 --m-* Token',
|
|
528
689
|
'操作反馈默认 message;仅一行文案时优先于 toast',
|
|
690
|
+
'空态用 MEmpty;结果/阻断页用 MResult;行内状态优先 MStatus',
|
|
529
691
|
'图标按钮建议提供 aria-label;表单字段建议有可见 label',
|
|
530
692
|
'浮层默认 Teleport 到 body;有明确布局约束时再改 appendTo',
|
|
531
693
|
'优先使用组件 documented variant,少写深层 CSS 覆盖',
|
package/dist/resources.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { Catalog } from './catalog.js';
|
|
3
3
|
export declare function registerCatalogResources(server: McpServer, catalog: Catalog): void;
|
|
4
4
|
export declare function countCatalogResources(catalog: Catalog): number;
|
package/dist/tools.js
CHANGED
|
@@ -3,7 +3,7 @@ import { componentDecisions, findDecision, scoreDecision } from './decisions.js'
|
|
|
3
3
|
import { listGoldenPages, readGoldenPageSource } from './golden-pages.js';
|
|
4
4
|
import { filterPageSnippets, findPageSnippet, pageSnippets, scorePageSnippet } from './page-snippets.js';
|
|
5
5
|
import { designRules, findPattern, pagePatterns, scorePattern } from './patterns.js';
|
|
6
|
-
import {
|
|
6
|
+
import { countCatalogResources, countCatalogResourceTemplates } from './resources.js';
|
|
7
7
|
function inspectButtonIconOnlyUsage(code, issues) {
|
|
8
8
|
const pairedTagRe = /<MButton\b([^>]*)>([\s\S]*?)<\/MButton>/gi;
|
|
9
9
|
let match = pairedTagRe.exec(code);
|
|
@@ -65,16 +65,17 @@ function generatedPageCode(patternId, intent, locale) {
|
|
|
65
65
|
const pageImports = useLayoutShell
|
|
66
66
|
? ', MPageContent, MPageFilters, MPageHeader, MPageSection, MPageToolbar'
|
|
67
67
|
: '';
|
|
68
|
-
const listImports = isList ? ', MSelect, MSpace, MTable' : '';
|
|
68
|
+
const listImports = isList ? ', MEmpty, MSelect, MSpace, MTable' : '';
|
|
69
69
|
const formImports = isForm || isAuth || isWizard ? ', MForm, MFormItem, MSelect' : '';
|
|
70
70
|
const dashboardImports = isDashboard ? ', MCard, MGrid, MGridItem, MPagePlaceholder, MPageStat, MSkeleton, MTable' : '';
|
|
71
71
|
const detailImports = isDetail ? ', MDivider' : '';
|
|
72
|
-
const emptyImports = isEmpty ? ',
|
|
72
|
+
const emptyImports = isEmpty ? ', MEmpty, MPageToolbar' : '';
|
|
73
73
|
const wizardImports = isWizard ? ', MStepper' : '';
|
|
74
74
|
const settingsImports = isSettings ? ', MTabs' : '';
|
|
75
|
+
const statusImports = isList || isDetail || isDashboard ? ', MStatus' : '';
|
|
75
76
|
const script = `<script setup lang="ts">
|
|
76
77
|
import { ref } from 'vue'
|
|
77
|
-
import { MButton, MCard, MConfigProvider, MInput, MTag, zhCN${layoutImports}${pageImports}${listImports}${formImports}${dashboardImports}${detailImports}${emptyImports}${wizardImports}${settingsImports} } from 'morya-ui'
|
|
78
|
+
import { MButton, MCard, MConfigProvider, MInput, MTag, zhCN${layoutImports}${pageImports}${listImports}${formImports}${dashboardImports}${detailImports}${emptyImports}${wizardImports}${settingsImports}${statusImports} } from 'morya-ui'
|
|
78
79
|
|
|
79
80
|
const loading = ref(false)
|
|
80
81
|
const error = ref('')
|
|
@@ -112,8 +113,18 @@ async function submit() {
|
|
|
112
113
|
</template>
|
|
113
114
|
</MPageToolbar>
|
|
114
115
|
<MTable :columns="columns" :rows="rows" :loading="loading" paginator :rows-per-page="10" striped bordered row-key="id">
|
|
116
|
+
<template #cell-status="{ value }">
|
|
117
|
+
<MStatus :label="String(value ?? '')" :severity="value === 'active' ? 'success' : 'secondary'" />
|
|
118
|
+
</template>
|
|
115
119
|
<template #empty>
|
|
116
|
-
<
|
|
120
|
+
<MEmpty
|
|
121
|
+
:title="${zh ? '暂无数据' : 'No data yet'}"
|
|
122
|
+
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
123
|
+
>
|
|
124
|
+
<template #extra>
|
|
125
|
+
<MButton severity="primary">${zh ? '新建' : 'Create'}</MButton>
|
|
126
|
+
</template>
|
|
127
|
+
</MEmpty>
|
|
117
128
|
</template>
|
|
118
129
|
</MTable>`;
|
|
119
130
|
const formContent = ` <MPageHeader :title="title" :description="${zh ? '填写表单并保存。' : 'Fill in the form and save.'}" />
|
|
@@ -210,17 +221,20 @@ ${content}
|
|
|
210
221
|
else if (isEmpty) {
|
|
211
222
|
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
212
223
|
<main class="m-generated-page">
|
|
213
|
-
<
|
|
214
|
-
<
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
<MPageToolbar :title="title">
|
|
225
|
+
<template #actions>
|
|
226
|
+
<MButton severity="primary" @click="submit">${zh ? '新建' : 'Create'}</MButton>
|
|
227
|
+
</template>
|
|
228
|
+
</MPageToolbar>
|
|
229
|
+
<MEmpty
|
|
230
|
+
:title="${zh ? '暂无内容' : 'Nothing here yet'}"
|
|
231
|
+
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
232
|
+
icon="database"
|
|
233
|
+
>
|
|
234
|
+
<template #extra>
|
|
235
|
+
<MButton severity="primary" @click="submit">${zh ? '创建' : 'Create'}</MButton>
|
|
236
|
+
</template>
|
|
237
|
+
</MEmpty>
|
|
224
238
|
</main>
|
|
225
239
|
</MConfigProvider>`;
|
|
226
240
|
}
|
|
@@ -857,6 +871,34 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
857
871
|
: '标准建议:MPageFilters 已自带表面样式,通常不必再用 MCard 包裹。',
|
|
858
872
|
});
|
|
859
873
|
}
|
|
874
|
+
if (/<MPageSection\b[^>]*variant=["']form["']/i.test(code) &&
|
|
875
|
+
/<MCard\b[^>]*>[\s\S]*?<MPageSection\b/i.test(code)) {
|
|
876
|
+
suggestions.push({
|
|
877
|
+
standardId: 'page-sections',
|
|
878
|
+
type: 'redundant-wrapper',
|
|
879
|
+
message: locale === 'en-US'
|
|
880
|
+
? 'Standard: MPageSection variant="form" already provides a surface; wrapping it in MCard is usually redundant.'
|
|
881
|
+
: '标准建议:MPageSection variant="form" 已自带表面样式,通常不必再用 MCard 包裹。',
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
if (/<MPageContent\b[^>]*>[\s\S]*?<MPageContent\b/i.test(code)) {
|
|
885
|
+
suggestions.push({
|
|
886
|
+
standardId: 'layout-shell',
|
|
887
|
+
type: 'redundant-wrapper',
|
|
888
|
+
message: locale === 'en-US'
|
|
889
|
+
? 'Standard: prefer a single MPageContent shell for page padding and section gap.'
|
|
890
|
+
: '标准建议:页面 padding 与区块间距优先只用一层 MPageContent。',
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
if (/<(?:div|section|main)\b[^>]*style=["'][^"']*(?:padding|gap)\s*:[^"']*["'][^>]*>\s*<(?:MPage(?:Content|Filters|Toolbar|Header|Section)|MTable)\b/i.test(code)) {
|
|
894
|
+
suggestions.push({
|
|
895
|
+
standardId: 'page-sections',
|
|
896
|
+
type: 'redundant-wrapper',
|
|
897
|
+
message: locale === 'en-US'
|
|
898
|
+
? 'Standard: avoid extra padded wrappers around MPage* blocks; prefer the components’ built-in spacing.'
|
|
899
|
+
: '标准建议:少在 MPage* 外包带 padding 的容器;优先使用组件自带间距。',
|
|
900
|
+
});
|
|
901
|
+
}
|
|
860
902
|
if (/style="[^"]*overflow(?:-y|-x)?\s*:\s*(auto|scroll)/i.test(code) ||
|
|
861
903
|
/<style\b[^>]*>[\s\S]*?overflow(?:-y|-x)?\s*:\s*(auto|scroll)/i.test(code)) {
|
|
862
904
|
suggestions.push({
|
|
@@ -943,7 +985,7 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
943
985
|
default: 'message',
|
|
944
986
|
message: { when: ['single-line action result', 'save/delete/create confirmations'] },
|
|
945
987
|
toast: { when: ['summary + detail', 'async or background notifications'] },
|
|
946
|
-
inlineMessage: {
|
|
988
|
+
inlineMessage: { api: 'field errorMessage | token-styled role=alert', when: ['persistent form/auth errors'] },
|
|
947
989
|
doc: 'docs/feedback-message-vs-toast.md',
|
|
948
990
|
},
|
|
949
991
|
global: [
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morya-ui/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"description": "MCP server for morya-ui — component docs, examples, and usage guidance for AI coding agents.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/morya-space/morya-ui/tree/main/packages/ui-mcp#readme",
|