@morya-ui/mcp 0.2.3 → 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/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 { countCatalogResourceTemplates, countCatalogResources } from './resources.js';
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 ? ', MDataView' : '';
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
- <p style="margin:0;padding:var(--m-space-8);text-align:center;color:var(--m-color-text-muted)">${zh ? '暂无数据' : 'No data yet'}</p>
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
- <MCard>
214
- <MDataView :value="[]">
215
- <template #empty>
216
- <div class="m-generated-empty">
217
- <strong>${zh ? '暂无内容' : 'Nothing here yet'}</strong>
218
- <p class="m-generated-muted">${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}</p>
219
- <MButton severity="primary" @click="submit">${zh ? '创建' : 'Create'}</MButton>
220
- </div>
221
- </template>
222
- </MDataView>
223
- </MCard>
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
  }
@@ -685,8 +699,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
685
699
  listGoldenPages().find((item) => item.patternId === best.pattern.id)?.id ||
686
700
  best.pattern.id;
687
701
  return locale === 'en-US'
688
- ? `Call get_golden_page("${goldenId}"), get_pattern, get_design_rules, then get_component/get_example. Pass includeScaffold: true for MPage* starter code. Run validate_page on the result.`
689
- : `调用 get_golden_page("${goldenId}")、get_pattern、get_design_rules,再用 get_component/get_example 查 API。需要 starter 时传 includeScaffold: true(已使用 MPage* 组件)。生成后请 validate_page 校验。`;
702
+ ? `Call get_golden_page("${goldenId}"), get_pattern, get_design_rules, then get_component/get_example. Pass includeScaffold: true for starter code. Optionally validate_page.`
703
+ : `调用 get_golden_page("${goldenId}")、get_pattern、get_design_rules,再用 get_component/get_example 查 API。需要 starter 时传 includeScaffold: true。可选用 validate_page。`;
690
704
  })(),
691
705
  };
692
706
  if (args.includeScaffold) {
@@ -720,8 +734,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
720
734
  file: payload.record.file,
721
735
  source: payload.source,
722
736
  nextStep: locale === 'en-US'
723
- ? 'Copy this structure and replace business data. Prefer MPage* components over custom scoped CSS.'
724
- : '复制此结构并替换业务数据。优先使用 MPage* 组件,而不是自定义 scoped CSS。',
737
+ ? 'Copy this structure and replace business data. Prefer MPage* components; see get_design_rules for page standards.'
738
+ : '复制此结构并替换业务数据。优先使用 MPage* 组件;页面标准见 get_design_rules。',
725
739
  });
726
740
  }
727
741
  function listGoldenPageCatalog(args = {}) {
@@ -800,75 +814,158 @@ export function createToolHandlers(catalog = loadCatalog()) {
800
814
  function validatePage(args) {
801
815
  const locale = resolveLocale(args.mode);
802
816
  const code = args.code || '';
803
- const issues = [];
817
+ const suggestions = [];
804
818
  if (/#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})\b/i.test(code)) {
805
- issues.push({
819
+ suggestions.push({
820
+ standardId: 'tokens',
806
821
  type: 'raw-color',
807
822
  message: locale === 'en-US'
808
- ? 'Use --m-* design tokens instead of hex colors.'
809
- : '请使用 --m-* 设计令牌,不要写 hex 色值。',
823
+ ? 'Standard: prefer --m-* design tokens over hex colors.'
824
+ : '标准建议:颜色优先使用 --m-* 设计令牌,而非 hex 色值。',
810
825
  });
811
826
  }
812
827
  if (/\brgb\s*\(/i.test(code) && !/--m-/.test(code)) {
813
- issues.push({
828
+ suggestions.push({
829
+ standardId: 'tokens',
814
830
  type: 'raw-color',
815
831
  message: locale === 'en-US'
816
- ? 'Use --m-* tokens instead of raw rgb()/rgba() page styles.'
817
- : '页面样式请使用 --m-* 令牌,不要写裸 rgb()/rgba()。',
832
+ ? 'Standard: prefer --m-* tokens over raw rgb()/rgba() in page styles.'
833
+ : '标准建议:页面样式优先 --m-* 令牌,而非裸 rgb()/rgba()。',
818
834
  });
819
835
  }
820
836
  if (/<MCard\b[^>]*>[\s\S]*?<MTable\b[^>]*\bbordered\b/i.test(code)) {
821
- issues.push({
837
+ suggestions.push({
838
+ standardId: 'page-sections',
822
839
  type: 'double-border',
823
840
  message: locale === 'en-US'
824
- ? 'Avoid wrapping bordered MTable with MCard; place MTable directly in MPageContent.'
825
- : '避免用 MCard 包裹 bordered 的 MTable;表格应直接放在 MPageContent 内。',
841
+ ? 'Standard: place bordered MTable directly in MPageContent instead of wrapping it with MCard.'
842
+ : '标准建议:bordered 的 MTable 直接放在 MPageContent 内,通常不必再包 MCard。',
826
843
  });
827
844
  }
828
845
  if (/<MLayoutContent\b/i.test(code) && !/<MPageContent\b/i.test(code)) {
829
- issues.push({
846
+ suggestions.push({
847
+ standardId: 'layout-shell',
830
848
  type: 'missing-page-content',
831
849
  message: locale === 'en-US'
832
- ? 'Prefer MPageContent inside MLayoutContent for consistent page spacing.'
833
- : '建议在 MLayoutContent 内使用 MPageContent 统一页面间距。',
850
+ ? 'Standard: prefer MPageContent inside MLayoutContent for consistent page spacing.'
851
+ : '标准建议:MLayoutContent 内使用 MPageContent 统一页面间距。',
834
852
  });
835
853
  }
836
854
  if ((/\bpage[-_](?:list|form|dashboard)?[-_]?(?:filters|toolbar|content|header)\b/i.test(code) ||
837
855
  /class="[^"]*filters/i.test(code)) &&
838
856
  !/<MPage(?:Content|Filters|Toolbar|Header|Section|Stat)\b/i.test(code)) {
839
- issues.push({
857
+ suggestions.push({
858
+ standardId: 'page-sections',
840
859
  type: 'custom-page-css',
841
860
  message: locale === 'en-US'
842
- ? 'Replace custom page section CSS with MPageFilters, MPageToolbar, MPageHeader, or MPageSection.'
843
- : '请用 MPageFilters / MPageToolbar / MPageHeader / MPageSection 替代自定义页面区块 CSS。',
861
+ ? 'Standard: prefer MPageFilters, MPageToolbar, MPageHeader, or MPageSection over custom page section CSS.'
862
+ : '标准建议:筛选/工具栏/表单区块优先 MPage* 组件,少写自定义 page section CSS。',
844
863
  });
845
864
  }
846
865
  if (/<MPageFilters\b/i.test(code) && /<MCard\b[^>]*>[\s\S]*?<MPageFilters\b/i.test(code)) {
847
- issues.push({
866
+ suggestions.push({
867
+ standardId: 'page-sections',
848
868
  type: 'redundant-wrapper',
849
869
  message: locale === 'en-US'
850
- ? 'Do not wrap MPageFilters in MCard; MPageFilters already provides the surface.'
851
- : '不要用 MCard 包裹 MPageFilters;MPageFilters 已自带表面样式。',
870
+ ? 'Standard: MPageFilters already provides a surface; wrapping it in MCard is usually redundant.'
871
+ : '标准建议:MPageFilters 已自带表面样式,通常不必再用 MCard 包裹。',
872
+ });
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
+ }
902
+ if (/style="[^"]*overflow(?:-y|-x)?\s*:\s*(auto|scroll)/i.test(code) ||
903
+ /<style\b[^>]*>[\s\S]*?overflow(?:-y|-x)?\s*:\s*(auto|scroll)/i.test(code)) {
904
+ suggestions.push({
905
+ standardId: 'scroll',
906
+ type: 'native-scroll',
907
+ message: locale === 'en-US'
908
+ ? 'Standard: prefer MScrollbar or MLayout scroll regions over overflow:auto/scroll in page styles.'
909
+ : '标准建议:页面滚动优先 MLayout 主滚动或 MScrollbar,少写 overflow:auto/scroll。',
910
+ });
911
+ }
912
+ if (/::-webkit-scrollbar|scrollbar-width\s*:/i.test(code)) {
913
+ suggestions.push({
914
+ standardId: 'scroll',
915
+ type: 'custom-scrollbar-css',
916
+ message: locale === 'en-US'
917
+ ? 'Standard: prefer MScrollbar or layout defaults over native scrollbar CSS in product code.'
918
+ : '标准建议:业务代码优先 MScrollbar 或 Layout 默认滚动,少定制原生滚动条样式。',
919
+ });
920
+ }
921
+ if (/min-height\s*:\s*100vh/i.test(code) && !/<MLayout\b[^>]*fill-viewport/i.test(code)) {
922
+ suggestions.push({
923
+ standardId: 'layout-shell',
924
+ type: 'viewport-height',
925
+ message: locale === 'en-US'
926
+ ? 'Standard: prefer MLayout fillViewport over hand-written min-height:100vh page shells.'
927
+ : '标准建议:整页高度优先 MLayout fillViewport,少写 min-height:100vh。',
852
928
  });
853
929
  }
854
930
  return textResult({
855
- ok: issues.length === 0,
856
- issues,
857
- recommendations: locale === 'en-US'
858
- ? designRules.composition.workflow
859
- : [
860
- '生成页面前先调用 recommend_page get_golden_page。',
861
- 'MLayout 使用 fillViewport;MLayoutContent 内放 MPageContent。',
862
- '筛选区用 MPageFilters,标题+操作用 MPageToolbar,表单用 MPageSection。',
863
- '列表表格不要额外包 MCard。',
864
- ],
931
+ ok: true,
932
+ advisory: true,
933
+ summary: locale === 'en-US'
934
+ ? suggestions.length === 0
935
+ ? 'No deviations from page standards detected.'
936
+ : `${suggestions.length} advisory suggestion(s).`
937
+ : suggestions.length === 0
938
+ ? '未发现与页面标准的明显偏差。'
939
+ : `${suggestions.length} 条参考建议。`,
940
+ suggestions,
941
+ nextStep: locale === 'en-US'
942
+ ? 'See get_design_rules.standards for the full recommended page standards.'
943
+ : '完整页面标准见 get_design_rules.standards。',
865
944
  });
866
945
  }
867
946
  function getDesignRules(args = {}) {
868
947
  const locale = resolveLocale(args.mode);
869
- if (locale === 'zh-CN')
870
- return textResult(designRules);
948
+ if (locale === 'zh-CN') {
949
+ return textResult({
950
+ meta: designRules.meta,
951
+ standards: designRules.standards,
952
+ tokens: designRules.tokens,
953
+ composition: designRules.composition,
954
+ actions: designRules.actions,
955
+ status: designRules.status,
956
+ feedback: designRules.feedback,
957
+ global: designRules.global,
958
+ });
959
+ }
871
960
  return textResult({
961
+ meta: designRules.meta,
962
+ standards: designRules.standards.map((item) => ({
963
+ id: item.id,
964
+ title: item.titleEn,
965
+ recommend: item.recommendEn,
966
+ discouraged: item.discouragedEn,
967
+ mcp: item.mcp,
968
+ })),
872
969
  tokens: {
873
970
  colors: ['--m-color-primary', '--m-color-surface', '--m-color-text', '--m-color-border'],
874
971
  spacing: '--m-space-*',
@@ -888,14 +985,14 @@ export function createToolHandlers(catalog = loadCatalog()) {
888
985
  default: 'message',
889
986
  message: { when: ['single-line action result', 'save/delete/create confirmations'] },
890
987
  toast: { when: ['summary + detail', 'async or background notifications'] },
891
- inlineMessage: { component: 'MMessage', when: ['persistent form/auth errors'] },
988
+ inlineMessage: { api: 'field errorMessage | token-styled role=alert', when: ['persistent form/auth errors'] },
892
989
  doc: 'docs/feedback-message-vs-toast.md',
893
990
  },
894
991
  global: [
895
- 'Prefer library components and --m-* tokens; do not maintain a second color system.',
896
- 'Default action feedback to message; do not use toast with summary-only text.',
897
- 'Icon-only buttons must provide aria-label or ariaLabel.',
898
- 'Form controls must have a visible label or an equivalent accessible name.',
992
+ 'Prefer library components and --m-* tokens.',
993
+ 'Default action feedback to message; prefer message over toast for single-line-only text.',
994
+ 'Icon-only buttons should provide aria-label or ariaLabel.',
995
+ 'Form controls should have a visible label or an equivalent accessible name.',
899
996
  'Overlays teleport to body by default; only change appendTo for a clear layout constraint.',
900
997
  'Prefer documented component variants over deep CSS overrides.',
901
998
  ],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@morya-ui/mcp",
3
3
  "type": "module",
4
- "version": "0.2.3",
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",