@morya-ui/mcp 0.2.2 → 0.2.4

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/dist/patterns.js CHANGED
@@ -32,7 +32,10 @@ export const pagePatterns = [
32
32
  { component: 'LayoutHeader', role: '顶栏面包屑' },
33
33
  { component: 'LayoutContent', role: '页面主体内容区' },
34
34
  { component: 'Breadcrumb', role: '页面层级导航' },
35
- { component: 'Space', role: '筛选区与行操作间距布局' },
35
+ { component: 'PageContent', role: '内容区垂直堆叠与间距', reason: '替代手写 gap/padding CSS' },
36
+ { component: 'PageFilters', role: '筛选区表面与边框' },
37
+ { component: 'PageToolbar', role: '标题与主操作两端对齐' },
38
+ { component: 'Space', role: '筛选控件与行操作间距' },
36
39
  { component: 'Input', role: '关键词筛选' },
37
40
  { component: 'Select', role: '枚举或状态筛选' },
38
41
  { component: 'Button', role: '查询、新增和行操作' },
@@ -47,15 +50,16 @@ export const pagePatterns = [
47
50
  ' └── MLayout',
48
51
  ' ├── MLayoutHeader → MBreadcrumb',
49
52
  ' └── MLayoutContent',
50
- ' ├── Filter section (MSpace + MInput + MSelect + MButton)',
51
- ' ├── PageHeader (title + primary action)',
52
- ' └── MTable (paginator, #cell-* slots, #empty)',
53
+ ' └── MPageContent',
54
+ ' ├── MPageFilters (MSpace + MInput + MSelect + MButton)',
55
+ ' ├── MPageToolbar (title + primary action)',
56
+ ' └── MTable (paginator, #cell-* slots, #empty)',
53
57
  ],
54
58
  layout: {
55
- page: 'MLayout has-sider 作为后台骨架;内容区 padding 使用 --m-space-6',
56
- header: 'MLayoutHeader 放面包屑;标题与主操作在内容区 toolbar 两端对齐',
57
- filters: '筛选区使用浅色背景 + border-radius;桌面端 MSpace wrap,窄屏堆叠',
58
- data: 'MTable 启用 paginator;行操作用 MButton size="small";空态用 #empty 插槽',
59
+ page: 'MLayout fillViewport + has-sider;MLayoutContent 内用 MPageContent 堆叠区块',
60
+ header: 'MLayoutHeader 放面包屑;标题与主操作用 MPageToolbar',
61
+ filters: 'MPageFilters 包裹筛选控件;内部 MSpace wrap 或 MFlex',
62
+ data: 'MTable 直接放在 MPageContent 内,不要额外 MCard;paginator + #empty',
59
63
  },
60
64
  styleRules: [
61
65
  '新增、保存等主操作使用 primary MButton',
@@ -90,7 +94,10 @@ export const pagePatterns = [
90
94
  { component: 'ConfigProvider', role: '应用根包裹' },
91
95
  { component: 'Layout', role: '页面骨架' },
92
96
  { component: 'LayoutHeader', role: '顶栏面包屑' },
93
- { component: 'LayoutContent', role: '表单主体区', reason: 'max-width 约 42rem' },
97
+ { component: 'LayoutContent', role: '页面主体滚动区' },
98
+ { component: 'PageContent', role: '表单页窄宽与垂直间距', reason: 'width="narrow"' },
99
+ { component: 'PageHeader', role: '页面标题与说明' },
100
+ { component: 'PageSection', role: '表单表面与底栏', reason: 'variant form / actions' },
94
101
  { component: 'Breadcrumb', role: '页面层级导航' },
95
102
  { component: 'Form', role: '声明式校验和布局' },
96
103
  { component: 'FormItem', role: '字段标签、帮助文本和校验状态', required: false, reason: '由 MForm 同目录导出,作为表单子结构使用' },
@@ -104,16 +111,16 @@ export const pagePatterns = [
104
111
  '└── MLayout',
105
112
  ' ├── MLayoutHeader → MBreadcrumb',
106
113
  ' └── MLayoutContent',
107
- ' ├── Page intro (title + description)',
108
- ' └── MForm',
109
- ' ├── MFormItem fields',
110
- ' └── Actions (submit + cancel)',
114
+ ' └── MPageContent (width="narrow")',
115
+ ' ├── MPageHeader',
116
+ ' └── MPageSection (variant="form")',
117
+ ' └── MForm + MPageSection (variant="actions")',
111
118
  ],
112
119
  layout: {
113
- shell: 'MLayout + MLayoutHeader + MLayoutContent;表单 max-width 约 42rem',
114
- page: '表单主体使用 max-width,避免字段横向过宽',
115
- form: '默认 label-position=top;字段按业务逻辑分组',
116
- actions: '提交按钮在前,取消使用 text secondary,操作区与表单分隔',
120
+ shell: 'MLayout fillViewport + MLayoutHeader + MLayoutContent',
121
+ page: 'MPageContent width="narrow" 限制表单宽度',
122
+ form: 'MPageSection variant="form" 包裹 MForm;label-position=top',
123
+ actions: 'MPageSection variant="actions" 放提交/取消;不要手写 border-top CSS',
117
124
  responsive: '多列字段在窄屏降为单列',
118
125
  },
119
126
  styleRules: [
@@ -148,9 +155,13 @@ export const pagePatterns = [
148
155
  { component: 'LayoutHeader', role: '顶栏与面包屑' },
149
156
  { component: 'LayoutContent', role: '仪表盘内容' },
150
157
  { component: 'Breadcrumb', role: '页面层级导航' },
158
+ { component: 'PageContent', role: '仪表盘垂直间距', reason: 'density="spacious"' },
159
+ { component: 'PageHeader', role: '页面标题' },
160
+ { component: 'PageStat', role: 'KPI 指标卡' },
161
+ { component: 'PagePlaceholder', role: '图表占位' },
151
162
  { component: 'Grid', role: 'KPI 与内容区栅格' },
152
163
  { component: 'GridItem', role: '栅格子项' },
153
- { component: 'Card', role: '指标和内容分区' },
164
+ { component: 'Card', role: '图表与列表明细分区' },
154
165
  { component: 'Icon', role: 'KPI 图标', required: false },
155
166
  { component: 'Tag', role: '状态和告警级别' },
156
167
  { component: 'Table', role: '明细数据', required: false },
@@ -313,7 +324,127 @@ export const pagePatterns = [
313
324
  ],
314
325
  },
315
326
  ];
327
+ /** Advisory page-writing standards for MCP and human authors — not enforced blockers. */
328
+ export const pageStandards = [
329
+ {
330
+ id: 'layout-shell',
331
+ title: '页面骨架',
332
+ titleEn: 'Page shell',
333
+ recommend: [
334
+ '后台页根布局使用 MLayout fillViewport',
335
+ 'MLayoutContent 内使用 MPageContent 统一 padding 与区块间距',
336
+ '写整页前先 get_golden_page 复制对应类型结构',
337
+ ],
338
+ recommendEn: [
339
+ 'Use MLayout fillViewport as the admin page root',
340
+ 'Place MPageContent inside MLayoutContent for consistent padding and section gap',
341
+ 'Call get_golden_page before writing a full page and copy the matching structure',
342
+ ],
343
+ discouraged: ['手写 min-height:100vh', '在 MLayoutContent 上写 padding/gap'],
344
+ discouragedEn: ['Hand-written min-height:100vh', 'Padding/gap on MLayoutContent instead of MPageContent'],
345
+ mcp: { snippet: 'layout-app-shell' },
346
+ },
347
+ {
348
+ id: 'page-sections',
349
+ title: '页面区块组件',
350
+ titleEn: 'Page section components',
351
+ recommend: [
352
+ '筛选区用 MPageFilters,标题+操作用 MPageToolbar,表单区用 MPageSection',
353
+ '列表页 MTable 直接放在 MPageContent 内',
354
+ '局部区块优先 get_page_snippet(filters / toolbar / form-actions 等)',
355
+ ],
356
+ recommendEn: [
357
+ 'Use MPageFilters, MPageToolbar, and MPageSection for filters, headers/actions, and forms',
358
+ 'Place MTable directly in MPageContent on list pages',
359
+ 'Prefer get_page_snippet for local blocks such as filters, toolbar, or form-actions',
360
+ ],
361
+ discouraged: ['手写 .page-filters / .page-toolbar 类', '用 MCard 包裹 MPageFilters 或 bordered MTable'],
362
+ discouragedEn: [
363
+ 'Hand-written .page-filters / .page-toolbar classes',
364
+ 'Wrapping MPageFilters or bordered MTable with MCard',
365
+ ],
366
+ mcp: { decision: 'surface-nesting-choice' },
367
+ },
368
+ {
369
+ id: 'scroll',
370
+ title: '滚动',
371
+ titleEn: 'Scroll',
372
+ recommend: [
373
+ '组件内置滚动区(Layout 壳、菜单/下拉面板、Table 视口等)使用内置 MScrollbar',
374
+ 'Dialog / Drawer / Popover / Splitter 等内容区不强制主题滚动;需要时由业务自行包 MScrollbar',
375
+ '已内置滚动的组件通常无需再外包一层',
376
+ ],
377
+ recommendEn: [
378
+ 'Component-owned scroll chrome (Layout shell, menu/select panels, Table viewport) uses built-in MScrollbar',
379
+ 'Do not force themed scroll into Dialog / Drawer / Popover / Splitter content; apps can wrap MScrollbar when needed',
380
+ 'Components that already scroll internally usually need no extra wrapper',
381
+ ],
382
+ discouraged: [
383
+ '在 Dialog / Drawer / Popover / Splitter / Textarea 等用户内容区强行内置 MScrollbar',
384
+ '业务代码定制 ::-webkit-scrollbar 替代组件内置滚动',
385
+ '重复包裹已内置滚动的组件',
386
+ ],
387
+ discouragedEn: [
388
+ 'Forcing MScrollbar into user content areas such as Dialog, Drawer, Popover, Splitter, or Textarea',
389
+ '::-webkit-scrollbar styling instead of component-owned scroll chrome',
390
+ 'Extra scroll wrappers around components that already scroll',
391
+ ],
392
+ mcp: { snippet: 'scrollable-panel', decision: 'page-scroll-choice' },
393
+ },
394
+ {
395
+ id: 'tokens',
396
+ title: '设计令牌',
397
+ titleEn: 'Design tokens',
398
+ recommend: ['颜色、间距、圆角、字号优先 --m-* Token', '控件宽度等局部尺寸可用 inline style'],
399
+ recommendEn: [
400
+ 'Prefer --m-* tokens for color, spacing, radius, and typography',
401
+ 'Inline styles are fine for local control widths',
402
+ ],
403
+ discouraged: ['页面级 hex / 裸 rgb() 色值', '维护第二套色板变量'],
404
+ discouragedEn: ['Page-level hex or raw rgb() colors', 'Maintaining a second color palette'],
405
+ },
406
+ {
407
+ id: 'feedback',
408
+ title: '操作反馈',
409
+ titleEn: 'Action feedback',
410
+ recommend: [
411
+ '单行结果(已保存/已删除)默认 message.success / error',
412
+ '需要标题+补充说明时用 toast',
413
+ '危险操作配合 MConfirmDialog',
414
+ ],
415
+ recommendEn: [
416
+ 'Default single-line results to message.success / error',
417
+ 'Use toast when a summary plus detail is needed',
418
+ 'Pair destructive actions with MConfirmDialog',
419
+ ],
420
+ discouraged: ['只有一行文案却用 toast', '删除等危险操作无确认'],
421
+ discouragedEn: ['Toast for single-line-only feedback', 'Destructive actions without confirmation'],
422
+ mcp: { decision: 'overlay-choice' },
423
+ },
424
+ {
425
+ id: 'accessibility',
426
+ title: '无障碍',
427
+ titleEn: 'Accessibility',
428
+ recommend: [
429
+ '图标按钮提供 aria-label 或 ariaLabel',
430
+ '表单控件有可见 label 或等价可访问名称',
431
+ '筛选区、表格等语义区域可加 aria-label',
432
+ ],
433
+ recommendEn: [
434
+ 'Provide aria-label or ariaLabel on icon-only buttons',
435
+ 'Give form controls a visible label or equivalent accessible name',
436
+ 'Add aria-label to semantic regions such as filter bars or tables when helpful',
437
+ ],
438
+ },
439
+ ];
316
440
  export const designRules = {
441
+ meta: {
442
+ nature: 'recommended',
443
+ noteZh: 'standards 为页面书写标准(推荐实践);特殊场景可偏离。validate_page 仅给出参考建议。',
444
+ noteEn: 'standards are recommended page-writing practices; deviations are fine when justified. validate_page is advisory only.',
445
+ },
446
+ /** Page writing standards — single source for layout, scroll, tokens, feedback, a11y, etc. */
447
+ standards: pageStandards,
317
448
  tokens: {
318
449
  colors: ['--m-color-primary', '--m-color-surface', '--m-color-text', '--m-color-border'],
319
450
  spacing: '--m-space-*',
@@ -321,6 +452,49 @@ export const designRules = {
321
452
  typography: '--m-font-size-xs/sm/md/lg',
322
453
  motion: '--m-motion-fast/normal',
323
454
  },
455
+ composition: {
456
+ workflow: [
457
+ 'For full pages: recommend_page → get_golden_page → get_design_rules.',
458
+ 'For local edits: get_page_snippet(section) for filters/toolbar/form-actions/KPI/scrollable-panel blocks.',
459
+ 'Use MLayout fillViewport as the app shell; put MPageContent inside MLayoutContent.',
460
+ 'Prefer MPage* components over scoped CSS for filters, toolbars, headers, form surfaces, and KPI cards.',
461
+ 'Use MSpace or MFlex for control groups inside MPageFilters; use MPageToolbar for title + primary action.',
462
+ ],
463
+ snippets: [
464
+ 'list-filters',
465
+ 'list-toolbar',
466
+ 'list-table',
467
+ 'list-row-actions',
468
+ 'form-header',
469
+ 'form-body',
470
+ 'form-actions',
471
+ 'dashboard-kpi-grid',
472
+ 'dashboard-chart-card',
473
+ 'layout-app-shell',
474
+ 'scrollable-panel',
475
+ ],
476
+ pageStack: {
477
+ list: ['MLayout', 'MLayoutHeader', 'MLayoutContent', 'MPageContent', 'MPageFilters', 'MPageToolbar', 'MTable'],
478
+ form: ['MLayout', 'MLayoutHeader', 'MLayoutContent', 'MPageContent', 'MPageHeader', 'MPageSection', 'MForm'],
479
+ dashboard: ['MLayout', 'MLayoutHeader', 'MLayoutContent', 'MPageContent', 'MPageHeader', 'MGrid', 'MPageStat', 'MCard'],
480
+ },
481
+ spacing: {
482
+ pageContent: 'MPageContent handles padding (--m-space-6) and gap (--m-space-4); spacious uses --m-space-6 gap',
483
+ filters: 'MPageFilters handles padded muted surface; do not wrap filters in MCard',
484
+ toolbar: 'MPageToolbar handles title/actions alignment with --m-space-4 gap',
485
+ form: 'MPageSection variant="form" handles form padding/border/shadow',
486
+ },
487
+ borders: {
488
+ do: ['MLayoutHeader bordered (default)', 'MPageFilters for filter regions', 'MPageSection variant="form" for forms', 'MTable bordered when table needs grid lines'],
489
+ avoid: [
490
+ 'MCard wrapping MTable that is already bordered',
491
+ 'Nested MCard with bordered=true on both levels',
492
+ 'Hand-written section borders when MPageFilters or MPageSection applies',
493
+ 'Double borders on filter + table wrapper',
494
+ ],
495
+ },
496
+ goldenPages: ['list-page', 'form-page', 'dashboard-page'],
497
+ },
324
498
  actions: {
325
499
  primary: { component: 'MButton', props: ['severity omitted or primary'] },
326
500
  secondary: { component: 'MButton', props: ['severity="secondary"', 'outlined or text'] },
@@ -350,12 +524,11 @@ export const designRules = {
350
524
  doc: 'docs/feedback-message-vs-toast.md',
351
525
  },
352
526
  global: [
353
- '优先使用组件库组件和 --m-* Token,不重复维护第二套色板',
354
- '操作反馈默认 message;无 detail 禁止用 toast.add({ summary only })',
355
- '图标按钮必须提供 aria-label 或 ariaLabel',
356
- '表单字段必须有可见 label 或等价的可访问名称',
357
- '浮层默认 Teleport body;只有有明确布局约束时才改 appendTo',
358
- '优先使用组件的 documented variant,不通过深层 CSS 覆盖内部样式',
527
+ '优先使用组件库组件和 --m-* Token',
528
+ '操作反馈默认 message;仅一行文案时优先于 toast',
529
+ '图标按钮建议提供 aria-label;表单字段建议有可见 label',
530
+ '浮层默认 Teleport 到 body;有明确布局约束时再改 appendTo',
531
+ '优先使用组件 documented variant,少写深层 CSS 覆盖',
359
532
  ],
360
533
  };
361
534
  export function findPattern(input) {
package/dist/tools.d.ts CHANGED
@@ -14,7 +14,7 @@ export declare function createToolHandlers(catalog?: import("./catalog.js").Cata
14
14
  };
15
15
  search: (args: {
16
16
  query: string;
17
- scope?: "all" | "components" | "guides" | "api" | "examples" | "patterns" | "decisions";
17
+ scope?: "all" | "components" | "guides" | "api" | "examples" | "patterns" | "decisions" | "snippets";
18
18
  mode?: string;
19
19
  limit?: number;
20
20
  offset?: number;
@@ -147,6 +147,59 @@ export declare function createToolHandlers(catalog?: import("./catalog.js").Cata
147
147
  text: string;
148
148
  }[];
149
149
  };
150
+ listGoldenPageCatalog: (args?: {
151
+ mode?: string;
152
+ }) => {
153
+ structuredContent?: Record<string, unknown> | undefined;
154
+ content: {
155
+ type: "text";
156
+ text: string;
157
+ }[];
158
+ };
159
+ getGoldenPage: (args: {
160
+ page: string;
161
+ mode?: string;
162
+ }) => {
163
+ structuredContent?: Record<string, unknown> | undefined;
164
+ content: {
165
+ type: "text";
166
+ text: string;
167
+ }[];
168
+ };
169
+ listPageSnippets: (args: {
170
+ query?: string;
171
+ pageType?: string;
172
+ mode?: string;
173
+ limit?: number;
174
+ offset?: number;
175
+ }) => {
176
+ structuredContent?: Record<string, unknown> | undefined;
177
+ content: {
178
+ type: "text";
179
+ text: string;
180
+ }[];
181
+ };
182
+ getPageSnippet: (args: {
183
+ section: string;
184
+ mode?: string;
185
+ includeScript?: boolean;
186
+ }) => {
187
+ structuredContent?: Record<string, unknown> | undefined;
188
+ content: {
189
+ type: "text";
190
+ text: string;
191
+ }[];
192
+ };
193
+ validatePage: (args: {
194
+ code?: string;
195
+ mode?: string;
196
+ }) => {
197
+ structuredContent?: Record<string, unknown> | undefined;
198
+ content: {
199
+ type: "text";
200
+ text: string;
201
+ }[];
202
+ };
150
203
  version: () => {
151
204
  structuredContent?: Record<string, unknown> | undefined;
152
205
  content: {