@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.
@@ -0,0 +1,454 @@
1
+ export const pageSnippets = [
2
+ {
3
+ id: 'list-filters',
4
+ title: '列表页筛选区',
5
+ titleEn: 'List page filter bar',
6
+ description: '在 MPageContent 内追加或替换筛选区,使用 MPageFilters + MSpace 包裹控件。',
7
+ descriptionEn: 'Add or replace a filter region inside MPageContent using MPageFilters and MSpace.',
8
+ pageTypes: ['list'],
9
+ keywords: ['筛选', '搜索', '查询', 'filter', 'search', 'keyword', 'reset'],
10
+ imports: ['MPageFilters', 'MSpace', 'MInput', 'MSelect', 'MButton'],
11
+ scriptSetup: `const keyword = ref('')
12
+ const status = ref<string | undefined>()
13
+ const statusOptions = [
14
+ { label: '全部', value: '' },
15
+ { label: '启用', value: 'active' },
16
+ { label: '停用', value: 'inactive' },
17
+ ]`,
18
+ template: `<MPageFilters aria-label="筛选">
19
+ <MSpace wrap>
20
+ <MInput v-model="keyword" placeholder="搜索名称" clearable style="width: 14rem" />
21
+ <MSelect
22
+ v-model="status"
23
+ :options="statusOptions"
24
+ placeholder="状态"
25
+ clearable
26
+ style="width: 10rem"
27
+ />
28
+ <MButton severity="primary">查询</MButton>
29
+ <MButton severity="secondary">重置</MButton>
30
+ </MSpace>
31
+ </MPageFilters>`,
32
+ rules: ['放在 MPageContent 内、MPageToolbar 之前', '不要外包 MCard', '筛选项宽度用 inline style,区块样式交给 MPageFilters'],
33
+ rulesEn: ['Place inside MPageContent before MPageToolbar', 'Do not wrap with MCard', 'Use inline width on controls; let MPageFilters own the surface'],
34
+ avoid: ['不要手写 .page-filters 类', '不要用 MCard 包筛选区'],
35
+ avoidEn: ['Do not hand-write .page-filters classes', 'Do not wrap filters in MCard'],
36
+ },
37
+ {
38
+ id: 'list-toolbar',
39
+ title: '列表页工具栏',
40
+ titleEn: 'List page toolbar',
41
+ description: '标题 + 主操作按钮,放在筛选区之后、表格之前。',
42
+ descriptionEn: 'Page title and primary action between filters and the table.',
43
+ pageTypes: ['list'],
44
+ keywords: ['工具栏', '标题', '新建', 'toolbar', 'title', 'create', 'actions'],
45
+ imports: ['MPageToolbar', 'MButton'],
46
+ template: `<MPageToolbar title="用户管理">
47
+ <template #actions>
48
+ <MButton severity="primary">新建用户</MButton>
49
+ </template>
50
+ </MPageToolbar>`,
51
+ rules: ['主操作放 #actions', '标题优先用 title prop,不用裸 h1 + flex'],
52
+ rulesEn: ['Put primary actions in #actions', 'Prefer the title prop over raw h1 + flex'],
53
+ avoid: ['不要再用自定义 toolbar CSS'],
54
+ avoidEn: ['Do not add custom toolbar CSS'],
55
+ },
56
+ {
57
+ id: 'list-table',
58
+ title: '列表页表格',
59
+ titleEn: 'List page table',
60
+ description: '标准 MTable,直接放在 MPageContent 内,不额外包 Card。',
61
+ descriptionEn: 'Standard MTable placed directly in MPageContent without an extra Card wrapper.',
62
+ pageTypes: ['list'],
63
+ keywords: ['表格', 'table', 'pagination', 'paginator', 'empty', 'columns', 'rows'],
64
+ imports: ['MTable'],
65
+ scriptSetup: `const columns = [
66
+ { key: 'name', label: '名称' },
67
+ { key: 'status', label: '状态' },
68
+ { key: 'updatedAt', label: '更新时间' },
69
+ { key: 'actions', label: '操作', width: 128 },
70
+ ]
71
+ const rows = ref<Record<string, unknown>[]>([])
72
+ const loading = ref(false)`,
73
+ template: `<MTable
74
+ :columns="columns"
75
+ :rows="rows"
76
+ :loading="loading"
77
+ paginator
78
+ :rows-per-page="10"
79
+ striped
80
+ bordered
81
+ row-key="id"
82
+ aria-label="数据列表"
83
+ >
84
+ <template #empty>
85
+ <p style="margin:0;padding:var(--m-space-8);text-align:center;color:var(--m-color-text-muted)">
86
+ 暂无数据
87
+ </p>
88
+ </template>
89
+ </MTable>`,
90
+ rules: ['表格直接放在 MPageContent 内', '空态用 #empty,不要留空白区域'],
91
+ rulesEn: ['Place the table directly in MPageContent', 'Use #empty for zero-data states'],
92
+ avoid: ['不要用 MCard 包裹 bordered MTable'],
93
+ avoidEn: ['Do not wrap a bordered MTable with MCard'],
94
+ },
95
+ {
96
+ id: 'list-row-actions',
97
+ title: '表格行操作列',
98
+ titleEn: 'Table row actions cell',
99
+ description: '在 #cell-actions 插槽中放置编辑/删除等按钮组。',
100
+ descriptionEn: 'Edit/delete buttons inside the #cell-actions slot.',
101
+ pageTypes: ['list'],
102
+ keywords: ['行操作', '编辑', '删除', 'actions', 'row', 'cell'],
103
+ imports: ['MSpace', 'MButton'],
104
+ template: `<template #cell-actions>
105
+ <MSpace>
106
+ <MButton severity="secondary" size="small">编辑</MButton>
107
+ <MButton severity="danger" size="small">删除</MButton>
108
+ </MSpace>
109
+ </template>`,
110
+ rules: ['行内操作用 size="small"', '危险操作使用 severity="danger" 并配合确认弹窗'],
111
+ rulesEn: ['Use size="small" for row actions', 'Use severity="danger" with confirmation for destructive actions'],
112
+ avoid: ['不要用 Dropdown 代替明确的行内按钮组,除非操作很多'],
113
+ avoidEn: ['Do not replace explicit row buttons with Dropdown unless there are many actions'],
114
+ },
115
+ {
116
+ id: 'list-status-tag',
117
+ title: '表格状态列 Tag',
118
+ titleEn: 'Table status tag cell',
119
+ description: '在 #cell-status 中用 MTag 展示业务状态。',
120
+ descriptionEn: 'Render business status with MTag in #cell-status.',
121
+ pageTypes: ['list'],
122
+ keywords: ['状态', 'tag', 'status', 'cell-status'],
123
+ imports: ['MTag'],
124
+ template: `<template #cell-status="{ value }">
125
+ <MTag
126
+ :value="value === 'active' ? '启用' : '停用'"
127
+ :severity="value === 'active' ? 'success' : 'secondary'"
128
+ />
129
+ </template>`,
130
+ rules: ['状态用 MTag,不要用 Button 颜色表达状态'],
131
+ rulesEn: ['Use MTag for status, not Button colors'],
132
+ avoid: ['不要用裸文本颜色区分状态'],
133
+ avoidEn: ['Do not rely on raw text color for status'],
134
+ },
135
+ {
136
+ id: 'form-header',
137
+ title: '表单页标题区',
138
+ titleEn: 'Form page header',
139
+ description: '表单页顶部标题与说明,放在 MPageContent width="narrow" 内。',
140
+ descriptionEn: 'Form page title and description inside narrow MPageContent.',
141
+ pageTypes: ['form'],
142
+ keywords: ['标题', '说明', 'intro', 'header', 'description'],
143
+ imports: ['MPageHeader'],
144
+ template: `<MPageHeader title="新建用户" description="填写基本信息并分配角色。" />`,
145
+ rules: ['放在 MPageSection 之前', '说明文案用 description prop'],
146
+ rulesEn: ['Place before MPageSection', 'Use the description prop for helper copy'],
147
+ avoid: ['不要裸 h1 + p 手写 margin'],
148
+ avoidEn: ['Do not hand-write h1 + p margins'],
149
+ },
150
+ {
151
+ id: 'form-body',
152
+ title: '表单主体',
153
+ titleEn: 'Form body surface',
154
+ description: 'MPageSection variant="form" 包裹 MForm 字段。',
155
+ descriptionEn: 'Wrap MForm fields with MPageSection variant="form".',
156
+ pageTypes: ['form'],
157
+ keywords: ['表单', 'form', 'fields', 'formitem'],
158
+ imports: ['MPageSection', 'MForm', 'MFormItem', 'MInput'],
159
+ scriptSetup: `const model = reactive({ name: '' })
160
+ const submitting = ref(false)`,
161
+ template: `<MPageSection variant="form">
162
+ <MForm @submit.prevent="onSubmit">
163
+ <MFormItem label="姓名" name="name" required>
164
+ <MInput v-model="model.name" placeholder="请输入姓名" fluid />
165
+ </MFormItem>
166
+ <!-- 更多字段 -->
167
+ </MForm>
168
+ </MPageSection>`,
169
+ rules: ['字段用 MFormItem + fluid 控件', '表单表面由 MPageSection 提供,不要重复 MCard'],
170
+ rulesEn: ['Use MFormItem with fluid controls', 'Let MPageSection provide the surface; do not duplicate MCard'],
171
+ avoid: ['不要绕过 MForm 手写校验状态'],
172
+ avoidEn: ['Do not bypass MForm for validation state'],
173
+ },
174
+ {
175
+ id: 'form-actions',
176
+ title: '表单底栏操作',
177
+ titleEn: 'Form footer actions',
178
+ description: '保存/取消按钮区,放在 MForm 内底部。',
179
+ descriptionEn: 'Save/cancel actions at the bottom of MForm.',
180
+ pageTypes: ['form'],
181
+ keywords: ['保存', '取消', 'submit', 'actions', 'footer'],
182
+ imports: ['MPageSection', 'MSpace', 'MButton'],
183
+ template: `<MPageSection variant="actions">
184
+ <MSpace>
185
+ <MButton native-type="submit" severity="primary" :loading="submitting">保存</MButton>
186
+ <MButton severity="secondary">取消</MButton>
187
+ </MSpace>
188
+ </MPageSection>`,
189
+ rules: ['提交按钮在前,取消 secondary', '保存中传 loading 防重复提交'],
190
+ rulesEn: ['Submit first, cancel secondary', 'Pass loading while saving to prevent duplicate submits'],
191
+ avoid: ['不要手写 border-top + margin-top'],
192
+ avoidEn: ['Do not hand-write border-top and margin-top'],
193
+ },
194
+ {
195
+ id: 'dashboard-kpi-grid',
196
+ title: '仪表盘 KPI 栅格',
197
+ titleEn: 'Dashboard KPI grid',
198
+ description: 'MGrid + MPageStat 展示关键指标。',
199
+ descriptionEn: 'Key metrics with MGrid and MPageStat.',
200
+ pageTypes: ['dashboard'],
201
+ keywords: ['kpi', '指标', 'stat', 'grid', 'metrics'],
202
+ imports: ['MGrid', 'MGridItem', 'MPageStat'],
203
+ scriptSetup: `const metrics = ref([
204
+ { label: '总用户', value: '12,480', trend: '+8.2%', icon: 'users' },
205
+ { label: '今日活跃', value: '1,926', trend: '+3.1%', icon: 'activity' },
206
+ ])`,
207
+ template: `<MGrid :cols="4" :x-gap="16" :y-gap="16" responsive="screen">
208
+ <MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
209
+ <MPageStat
210
+ :label="metric.label"
211
+ :value="metric.value"
212
+ :trend="metric.trend"
213
+ icon="activity"
214
+ />
215
+ </MGridItem>
216
+ </MGrid>`,
217
+ rules: ['KPI 用 MPageStat,不要每页自定义 stat CSS', 'responsive="screen" 便于窄屏降列'],
218
+ rulesEn: ['Use MPageStat for KPIs', 'Use responsive="screen" for narrow layouts'],
219
+ avoid: ['不要每个指标手写 font-size/颜色'],
220
+ avoidEn: ['Do not hand-write font sizes/colors per metric'],
221
+ },
222
+ {
223
+ id: 'dashboard-chart-card',
224
+ title: '仪表盘图表卡片',
225
+ titleEn: 'Dashboard chart card',
226
+ description: 'MCard 标题 + MPagePlaceholder 作为图表占位。',
227
+ descriptionEn: 'MCard title with MPagePlaceholder for chart area.',
228
+ pageTypes: ['dashboard'],
229
+ keywords: ['chart', '图表', 'placeholder', 'card', 'trend'],
230
+ imports: ['MCard', 'MPagePlaceholder', 'MSkeleton'],
231
+ template: `<MCard title="趋势概览">
232
+ <MSkeleton v-if="loading" height="8rem" />
233
+ <MPagePlaceholder
234
+ v-else
235
+ aria-label="图表占位"
236
+ description="图表区域(接入 ECharts / 业务组件)"
237
+ />
238
+ </MCard>`,
239
+ rules: ['图表区适合 MCard,因为需要 card 标题', '加载中用 MSkeleton'],
240
+ rulesEn: ['Charts belong in MCard when a card title is needed', 'Use MSkeleton while loading'],
241
+ avoid: ['不要用 MPageFilters 风格包裹图表'],
242
+ avoidEn: ['Do not wrap charts with MPageFilters styling'],
243
+ },
244
+ {
245
+ id: 'dashboard-recent-table',
246
+ title: '仪表盘最近列表',
247
+ titleEn: 'Dashboard recent list card',
248
+ description: 'MCard 内嵌小尺寸 MTable 展示最近记录。',
249
+ descriptionEn: 'Recent records in a small MTable inside MCard.',
250
+ pageTypes: ['dashboard'],
251
+ keywords: ['recent', '最近', 'table', 'card', 'list'],
252
+ imports: ['MCard', 'MTable', 'MTag'],
253
+ scriptSetup: `const recentColumns = [
254
+ { key: 'id', label: '工单号', width: 96 },
255
+ { key: 'title', label: '标题' },
256
+ { key: 'status', label: '状态', width: 96 },
257
+ ]
258
+ const recentRows = ref<Record<string, unknown>[]>([])`,
259
+ template: `<MCard title="最近工单">
260
+ <MTable :columns="recentColumns" :rows="recentRows" size="small" :paginator="false" bordered />
261
+ </MCard>`,
262
+ rules: ['明细列表放 MCard 内合理', 'size="small" 适合卡片内表格'],
263
+ rulesEn: ['Detail lists inside MCard are appropriate', 'Use size="small" for in-card tables'],
264
+ avoid: ['卡片内表格不要再外包一层 MPageContent'],
265
+ avoidEn: ['Do not wrap in-card tables with another MPageContent'],
266
+ },
267
+ {
268
+ id: 'page-content-list',
269
+ title: '列表页内容容器',
270
+ titleEn: 'List page content stack',
271
+ description: 'MLayoutContent 内的标准垂直堆叠容器。',
272
+ descriptionEn: 'Standard vertical stack container inside MLayoutContent.',
273
+ pageTypes: ['list', 'common'],
274
+ keywords: ['page content', 'stack', 'layout content', '容器'],
275
+ imports: ['MPageContent'],
276
+ template: `<MLayoutContent>
277
+ <MPageContent>
278
+ <!-- MPageFilters → MPageToolbar → MTable -->
279
+ </MPageContent>
280
+ </MLayoutContent>`,
281
+ rules: ['所有列表区块放在 MPageContent 内', '区块顺序:筛选 → 工具栏 → 表格'],
282
+ rulesEn: ['Keep all list sections inside MPageContent', 'Order: filters → toolbar → table'],
283
+ avoid: ['不要在 MLayoutContent 上手写 padding/gap'],
284
+ avoidEn: ['Do not hand-write padding/gap on MLayoutContent'],
285
+ },
286
+ {
287
+ id: 'page-content-form',
288
+ title: '表单页内容容器',
289
+ titleEn: 'Form page content stack',
290
+ description: '窄宽表单页容器。',
291
+ descriptionEn: 'Narrow form page container.',
292
+ pageTypes: ['form', 'common'],
293
+ keywords: ['narrow', 'form content', 'max-width'],
294
+ imports: ['MPageContent'],
295
+ template: `<MLayoutContent>
296
+ <MPageContent width="narrow">
297
+ <!-- MPageHeader → MPageSection form → actions -->
298
+ </MPageContent>
299
+ </MLayoutContent>`,
300
+ rules: ['表单页使用 width="narrow"', '不要手写 max-width CSS'],
301
+ rulesEn: ['Use width="narrow" on form pages', 'Do not hand-write max-width CSS'],
302
+ avoid: ['不要让表单字段铺满超宽视口'],
303
+ avoidEn: ['Do not let form fields stretch across an ultra-wide viewport'],
304
+ },
305
+ {
306
+ id: 'detail-toolbar',
307
+ title: '详情页顶栏',
308
+ titleEn: 'Detail page header actions',
309
+ description: '详情页标题与编辑操作。',
310
+ descriptionEn: 'Detail page title and edit action.',
311
+ pageTypes: ['detail'],
312
+ keywords: ['detail', '详情', 'edit', 'toolbar'],
313
+ imports: ['MPageToolbar', 'MButton', 'MTag'],
314
+ template: `<MPageToolbar title="示例资源">
315
+ <template #actions>
316
+ <MButton severity="primary" outlined>编辑</MButton>
317
+ </template>
318
+ </MPageToolbar>
319
+ <MTag value="正常" severity="success" />`,
320
+ rules: ['状态 Tag 放在标题区附近', '编辑为主操作,删除放低强调区域'],
321
+ rulesEn: ['Keep status Tag near the header area', 'Edit is primary; delete stays low emphasis'],
322
+ avoid: ['不要把操作散落到多个无关区域'],
323
+ avoidEn: ['Do not scatter actions across unrelated areas'],
324
+ },
325
+ {
326
+ id: 'layout-app-shell',
327
+ title: '后台应用骨架',
328
+ titleEn: 'Admin app shell',
329
+ description: 'MLayout fillViewport + Header + Content 的最小骨架。',
330
+ descriptionEn: 'Minimal MLayout fillViewport shell with header and content.',
331
+ pageTypes: ['common'],
332
+ keywords: ['layout', 'shell', 'breadcrumb', 'sider', 'app'],
333
+ imports: ['MLayout', 'MLayoutHeader', 'MLayoutContent', 'MBreadcrumb', 'MConfigProvider', 'zhCN'],
334
+ template: `<MConfigProvider :locale="zhCN">
335
+ <MLayout fill-viewport>
336
+ <MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
337
+ <MBreadcrumb :model="[{ label: '首页', to: '/' }, { label: '当前页' }]" />
338
+ </MLayoutHeader>
339
+ <MLayoutContent>
340
+ <MPageContent>
341
+ <!-- page sections -->
342
+ </MPageContent>
343
+ </MLayoutContent>
344
+ </MLayout>
345
+ </MConfigProvider>`,
346
+ rules: ['根布局用 fillViewport', '面包屑放 MLayoutHeader'],
347
+ rulesEn: ['Use fillViewport on the root layout', 'Place breadcrumb in MLayoutHeader'],
348
+ avoid: ['不要手写 min-height: 100vh'],
349
+ avoidEn: ['Do not hand-write min-height: 100vh'],
350
+ },
351
+ {
352
+ id: 'scrollable-panel',
353
+ title: '局部可滚动面板',
354
+ titleEn: 'Scrollable local panel',
355
+ description: '业务自行限高滚动时的可选写法:显式使用 MScrollbar。组件内置滚动区无需再包。',
356
+ descriptionEn: 'Optional pattern when the app wants a themed capped scroll region. Do not wrap component-owned scroll chrome again.',
357
+ pageTypes: ['list', 'form', 'dashboard', 'detail', 'common'],
358
+ keywords: [
359
+ 'scroll',
360
+ 'scrollbar',
361
+ '滚动',
362
+ '滚动条',
363
+ 'overflow',
364
+ 'max-height',
365
+ 'panel',
366
+ 'card body',
367
+ 'sidebar',
368
+ 'log',
369
+ ],
370
+ imports: ['MScrollbar', 'MCard'],
371
+ template: `<MCard title="活动日志">
372
+ <MScrollbar max-height="16rem">
373
+ <ul style="margin:0;padding:0;list-style:none">
374
+ <li v-for="item in logLines" :key="item.id" style="padding:var(--m-space-2) 0;border-bottom:1px solid var(--m-color-border)">
375
+ {{ item.text }}
376
+ </li>
377
+ </ul>
378
+ </MScrollbar>
379
+ </MCard>`,
380
+ scriptSetup: `const logLines = [
381
+ { id: '1', text: '用户 admin 登录成功' },
382
+ { id: '2', text: '导出任务已完成' },
383
+ { id: '3', text: '配置已保存' },
384
+ ]`,
385
+ rules: [
386
+ '整页主滚动推荐 MLayout fillViewport',
387
+ '固定高度用 height;仅超出时才滚动用 max-height',
388
+ 'MTable / Layout / 菜单下拉等通常已内置滚动,一般无需再包一层',
389
+ 'Dialog / Drawer 内容滚动由业务自行决定,需要主题滚动时再包 MScrollbar',
390
+ ],
391
+ rulesEn: [
392
+ 'Main page scroll: prefer MLayout fillViewport',
393
+ 'Use height for fixed viewports; use max-height when scroll should appear only on overflow',
394
+ 'MTable / Layout / menu popups usually scroll internally; an extra wrapper is often unnecessary',
395
+ 'Dialog / Drawer content scrolling is app-owned; wrap MScrollbar only when themed scroll is desired',
396
+ ],
397
+ avoid: ['整页壳写 overflow:auto', '重复包裹已内置滚动的组件'],
398
+ avoidEn: ['overflow:auto on the page shell', 'Double-wrapping components that already scroll'],
399
+ },
400
+ ];
401
+ export function findPageSnippet(id) {
402
+ const key = id.trim().toLowerCase().replace(/[-_\s]/g, '');
403
+ return pageSnippets.find((item) => item.id.replace(/[-_\s]/g, '') === key);
404
+ }
405
+ export function scorePageSnippet(snippet, query) {
406
+ const normalized = query.toLowerCase().trim();
407
+ if (!normalized)
408
+ return 0;
409
+ let score = 0;
410
+ if (snippet.id.replace(/[-_\s]/g, '') === normalized.replace(/[-_\s]/g, ''))
411
+ score += 100;
412
+ for (const keyword of snippet.keywords) {
413
+ const key = keyword.toLowerCase();
414
+ if (normalized === key)
415
+ score += 40;
416
+ else if (normalized.includes(key) || key.includes(normalized))
417
+ score += 15;
418
+ }
419
+ for (const type of snippet.pageTypes) {
420
+ if (normalized.includes(type))
421
+ score += 10;
422
+ }
423
+ if (snippet.title.toLowerCase().includes(normalized) || snippet.titleEn.toLowerCase().includes(normalized)) {
424
+ score += 25;
425
+ }
426
+ return score;
427
+ }
428
+ export function filterPageSnippets(args) {
429
+ const pageType = args.pageType?.trim().toLowerCase();
430
+ let items = pageSnippets.filter((item) => {
431
+ if (!pageType)
432
+ return true;
433
+ if (item.pageTypes.includes(pageType))
434
+ return true;
435
+ return item.pageTypes.length === 1 && item.pageTypes[0] === 'common';
436
+ });
437
+ if (args.query?.trim()) {
438
+ items = items
439
+ .map((item) => ({ item, score: scorePageSnippet(item, args.query) }))
440
+ .filter((entry) => entry.score > 0)
441
+ .sort((a, b) => b.score - a.score || a.item.id.localeCompare(b.item.id))
442
+ .map((entry) => entry.item);
443
+ }
444
+ const offset = Math.max(args.offset ?? 0, 0);
445
+ const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
446
+ return {
447
+ total: items.length,
448
+ items: items.slice(offset, offset + limit),
449
+ offset,
450
+ limit,
451
+ hasMore: offset + limit < items.length,
452
+ nextOffset: offset + limit < items.length ? offset + limit : undefined,
453
+ };
454
+ }
@@ -20,7 +20,29 @@ export interface PagePattern {
20
20
  avoid: string[];
21
21
  }
22
22
  export declare const pagePatterns: PagePattern[];
23
+ export interface PageStandard {
24
+ id: string;
25
+ title: string;
26
+ titleEn: string;
27
+ recommend: string[];
28
+ recommendEn: string[];
29
+ discouraged?: string[];
30
+ discouragedEn?: string[];
31
+ mcp?: {
32
+ snippet?: string;
33
+ decision?: string;
34
+ };
35
+ }
36
+ /** Advisory page-writing standards for MCP and human authors — not enforced blockers. */
37
+ export declare const pageStandards: PageStandard[];
23
38
  export declare const designRules: {
39
+ readonly meta: {
40
+ readonly nature: "recommended";
41
+ readonly noteZh: "standards 为页面书写标准(推荐实践);特殊场景可偏离。validate_page 仅给出参考建议。";
42
+ readonly noteEn: "standards are recommended page-writing practices; deviations are fine when justified. validate_page is advisory only.";
43
+ };
44
+ /** Page writing standards — single source for layout, scroll, tokens, feedback, a11y, etc. */
45
+ readonly standards: PageStandard[];
24
46
  readonly tokens: {
25
47
  readonly colors: readonly ["--m-color-primary", "--m-color-surface", "--m-color-text", "--m-color-border"];
26
48
  readonly spacing: "--m-space-*";
@@ -28,6 +50,26 @@ export declare const designRules: {
28
50
  readonly typography: "--m-font-size-xs/sm/md/lg";
29
51
  readonly motion: "--m-motion-fast/normal";
30
52
  };
53
+ readonly composition: {
54
+ readonly workflow: readonly ["For full pages: recommend_page → get_golden_page → get_design_rules.", "For local edits: get_page_snippet(section) for filters/toolbar/form-actions/KPI/scrollable-panel blocks.", "Use MLayout fillViewport as the app shell; put MPageContent inside MLayoutContent.", "Prefer MPage* components over scoped CSS for filters, toolbars, headers, form surfaces, and KPI cards.", "Use MSpace or MFlex for control groups inside MPageFilters; use MPageToolbar for title + primary action."];
55
+ readonly snippets: readonly ["list-filters", "list-toolbar", "list-table", "list-row-actions", "form-header", "form-body", "form-actions", "dashboard-kpi-grid", "dashboard-chart-card", "layout-app-shell", "scrollable-panel"];
56
+ readonly pageStack: {
57
+ readonly list: readonly ["MLayout", "MLayoutHeader", "MLayoutContent", "MPageContent", "MPageFilters", "MPageToolbar", "MTable"];
58
+ readonly form: readonly ["MLayout", "MLayoutHeader", "MLayoutContent", "MPageContent", "MPageHeader", "MPageSection", "MForm"];
59
+ readonly dashboard: readonly ["MLayout", "MLayoutHeader", "MLayoutContent", "MPageContent", "MPageHeader", "MGrid", "MPageStat", "MCard"];
60
+ };
61
+ readonly spacing: {
62
+ readonly pageContent: "MPageContent handles padding (--m-space-6) and gap (--m-space-4); spacious uses --m-space-6 gap";
63
+ readonly filters: "MPageFilters handles padded muted surface; do not wrap filters in MCard";
64
+ readonly toolbar: "MPageToolbar handles title/actions alignment with --m-space-4 gap";
65
+ readonly form: "MPageSection variant=\"form\" handles form padding/border/shadow";
66
+ };
67
+ readonly borders: {
68
+ readonly do: readonly ["MLayoutHeader bordered (default)", "MPageFilters for filter regions", "MPageSection variant=\"form\" for forms", "MTable bordered when table needs grid lines"];
69
+ readonly avoid: readonly ["MCard wrapping MTable that is already bordered", "Nested MCard with bordered=true on both levels", "Hand-written section borders when MPageFilters or MPageSection applies", "Double borders on filter + table wrapper"];
70
+ };
71
+ readonly goldenPages: readonly ["list-page", "form-page", "dashboard-page"];
72
+ };
31
73
  readonly actions: {
32
74
  readonly primary: {
33
75
  readonly component: "MButton";
@@ -74,7 +116,7 @@ export declare const designRules: {
74
116
  };
75
117
  readonly doc: "docs/feedback-message-vs-toast.md";
76
118
  };
77
- readonly global: readonly ["优先使用组件库组件和 --m-* Token,不重复维护第二套色板", "操作反馈默认 message;无 detail 禁止用 toast.add({ summary only })", "图标按钮必须提供 aria-label 或 ariaLabel", "表单字段必须有可见 label 或等价的可访问名称", "浮层默认 Teleport 到 body;只有有明确布局约束时才改 appendTo", "优先使用组件的 documented variant,不通过深层 CSS 覆盖内部样式"];
119
+ readonly global: readonly ["优先使用组件库组件和 --m-* Token", "操作反馈默认 message;仅一行文案时优先于 toast", "图标按钮建议提供 aria-label;表单字段建议有可见 label", "浮层默认 Teleport 到 body;有明确布局约束时再改 appendTo", "优先使用组件 documented variant,少写深层 CSS 覆盖"];
78
120
  };
79
121
  export declare function findPattern(input: string): PagePattern | undefined;
80
122
  export declare function scorePattern(pattern: PagePattern, query: string): number;