@morya-ui/setup 0.3.5 → 0.3.7

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.
Files changed (33) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +119 -107
  3. package/bin/morya-ui-setup.js +14 -14
  4. package/catalog/skills.json +46 -46
  5. package/package.json +4 -3
  6. package/src/__tests__/mcp.test.mjs +141 -0
  7. package/src/cli.mjs +330 -312
  8. package/src/copy-template.mjs +78 -78
  9. package/src/fs-utils.mjs +24 -24
  10. package/src/install.mjs +134 -134
  11. package/src/mcp.mjs +220 -50
  12. package/src/package-json.mjs +30 -30
  13. package/src/skills.mjs +224 -223
  14. package/src/styles.mjs +119 -119
  15. package/template/.agents/skills/morya-ui-pages/SKILL.md +191 -188
  16. package/template/.agents/skills/morya-ui-pages/evals/evals.json +89 -89
  17. package/template/.agents/skills/morya-ui-pages/references/component-index.md +99 -99
  18. package/template/.agents/skills/morya-ui-pages/references/decision-recipes.md +6 -27
  19. package/template/.agents/skills/morya-ui-pages/references/design-system.md +101 -101
  20. package/template/.agents/skills/morya-ui-pages/references/feedback.md +68 -68
  21. package/template/.agents/skills/morya-ui-pages/references/optional-companions.md +62 -62
  22. package/template/.agents/skills/morya-ui-pages/references/page-layouts.md +133 -133
  23. package/template/.agents/skills/morya-ui-pages/references/review-checklist.md +76 -63
  24. package/template/.agents/skills/morya-ui-pages/references/style-presets.md +68 -45
  25. package/template/.agents/skills/morya-ui-pages/references/surfaces.md +91 -91
  26. package/template/.agents/skills/morya-ui-pages/references/visual-craft.md +131 -129
  27. package/template/.cursor/rules/coding-style.mdc +41 -41
  28. package/template/.cursor/rules/component-usage.mdc +41 -41
  29. package/template/.cursor/rules/design-system.mdc +18 -17
  30. package/template/.cursor/rules/page-layout.mdc +97 -97
  31. package/template/AGENTS.md +33 -0
  32. package/template/DESIGN.md +81 -59
  33. package/template/scripts/check-raw-colors.mjs +74 -74
@@ -1,17 +1,18 @@
1
- ---
2
- description: Morya UI 设计系统第一信源。生成或修改 UI 前必读 DESIGN.md。
3
- alwaysApply: true
4
- ---
5
-
6
- # Morya UI 设计系统
7
-
8
- 1. **先读** 项目根目录 `DESIGN.md`。组件选型用 MCP `recommend_component`(含 recipe + `relatedSnippets`),或 skill `references/decision-recipes.md` / `component-index.md`。
9
- 2. **只用** `morya-ui` 的 `M*` 组件;全局样式 `morya-ui/styles.css`。
10
- 3. **颜色/间距/圆角** 使用 `--m-*` CSS 变量,禁止裸 hex/rgb(见 `scripts/check-raw-colors.mjs`)。
11
- 4. **页面组装(composition-first)**:用 MCP `recommend_page` → `suggestedSnippets` / `get_page_snippet` + `recommend_component` 拼装;`page-layouts` 块顺序作 checklist。`get_golden_page` **可选**(核对整页块顺序或用户明确要求对齐样例时)。**视觉方向**:有明确参考或明确描述 → 必须跟用户;提示词有清晰线索 → 推断并说明;不确定 → 先问(`get_style_direction`)。**没有命名风格预设表。** 禁止未询问就套 AI 默认渐变/霓虹/毛玻璃。已安装 `frontend-design` / `impeccable` 时只加深已定方向,**不得**换组件库或推翻块顺序 / 用户指定。
12
- 5. **表单选项** 用 `MSelect` / `MTreeSelect`;**动作菜单** 用 `MDropdown`。
13
- 6. 不确定 API 时查文档站或 MCP `get_component`,**禁止臆造 prop**;写完后必须 `validate_usage`。
14
- 7. 根节点用 `MConfigProvider`;后台布局用 `MLayout` 系列。
15
- 8. **操作反馈默认 `message` API**;仅 `summary + detail` 或异步通知用 `toast`;表单常驻错误用字段 `errorMessage` 或 token 样式的 `role="alert"`(`<MMessage>` 只是 message 宿主)。见 skill `references/feedback.md`。
16
-
17
- Token / 风格 / companion:MCP `get_design_rules`、`get_style_direction`;skill `optional-companions.md`(运行时以 `morya-ui/styles.css` 为准)
1
+ ---
2
+ description: Morya UI 设计系统第一信源。生成或修改 UI 前必读 DESIGN.md。
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Morya UI 设计系统
7
+
8
+ 1. **先读** 项目根目录 `DESIGN.md`。组件选型用 MCP `recommend_component`(含 recipe + `relatedSnippets`),或 skill `references/decision-recipes.md` / `component-index.md`。
9
+ 2. **只用** `morya-ui` 的 `M*` 组件;全局样式 `morya-ui/styles.css`。
10
+ 3. **颜色/间距/圆角** 使用 `--m-*` CSS 变量,禁止裸 hex/rgb(见 `scripts/check-raw-colors.mjs`)。品牌差异优先主题覆盖 `--m-color-primary` / 字体(见 DESIGN.md § 主题覆盖)。
11
+ 4. **页面组装(composition-first)**:有参考时先 `map_reference`(或 `recommend_page` + `brief` / `density`)→ 按 `mapping` / `suggestedSnippets` 用 `get_page_snippet` 拼装;`page-layouts` 作块顺序 checklist。`get_golden_page` **可选**。Account / Express / Flow 签名壳用 `get_style_shells`。**视觉方向**:明确参考/描述 → 跟用户;线索清晰 → 推断并说明;不确定 → 先问(`get_style_direction`)。**没有命名风格预设表。** 禁止未询问就套 AI 默认渐变/霓虹/毛玻璃。
12
+ 5. **表单选项** 用 `MSelect` / `MTreeSelect`;**动作菜单** 用 `MDropdown`。
13
+ 6. 不确定 API 时查文档站或 MCP `get_component`,**禁止臆造 prop**;写完后必须 `validate_usage`。
14
+ 7. 根节点用 `MConfigProvider`;后台布局用 `MLayout` 系列。
15
+ 8. **操作反馈默认 `message` API**;仅 `summary + detail` 或异步通知用 `toast`;表单常驻错误用字段 `errorMessage` 或 token 样式的 `role="alert"`(`<MMessage>` 只是 message 宿主)。见 skill `references/feedback.md`。
16
+ 9. 交付前 `validate_page`(同一 `brief`):契约 `ok` 必须为 true;工艺建议可说明后放过。
17
+
18
+ Token / 风格 / companion:MCP `get_design_rules`、`get_style_direction`、`get_style_shells`、`map_reference`;skill `optional-companions.md`(运行时以 `morya-ui/styles.css` 为准)
@@ -1,97 +1,97 @@
1
- ---
2
- description: 生成 Morya UI 业务页面(列表/表单/仪表盘)时的布局规范
3
- alwaysApply: false
4
- ---
5
-
6
- # 页面布局(Agent Requested)
7
-
8
- 生成完整页面时,先确定类型,按 **块顺序 checklist** 用 MCP **`get_page_snippet`** / `recommend_page.suggestedSnippets` 拼装(composition-first)。仅在不确定整页顺序或用户要求对齐样例时再读 `get_golden_page`。
9
-
10
- | 类型 | 主要 snippets | 可选黄金样例 |
11
- | --- | --- | --- |
12
- | 列表页 | `layout-app-shell` / `page-header-actions` / **`list-filters-stack`**(dense → `list-filters-dense`)/ `list-table` / `form-in-dialog` / `confirm-delete` | `list-page` |
13
- | 表单页 | `form-header` / `form-body` / `form-actions` | `form-page`(仅长表单 / 独立配置页) |
14
- | 列表内弹窗表单 | **`form-in-dialog`** | `form-in-dialog` |
15
- | 详情页 | `detail-toolbar` / `form-in-dialog` | `detail-page` |
16
- | 结果 / 阻断页 | **`result-block`** | `result-page` |
17
- | 设置页 | `form-header` / `form-body` / `form-actions` | `settings-page` |
18
- | 分步向导 | **`wizard-steps`** | `wizard-form` |
19
- | 仪表盘 | `dashboard-kpi-grid` / `dashboard-chart-card` / `dashboard-recent-table` | `dashboard-page` |
20
- | 登录页 | **`auth-split-shell`** | `login-page` |
21
- | 营销落地 | —(见 surfaces) | `landing-page` |
22
- | 空状态 | **`empty-block`** | `empty-state` |
23
-
24
- ## 列表内新建 / 编辑(默认)
25
-
26
- 后台 CRUD **优先**:列表页 + `MDialog`(或 `MDrawer`)内 `MForm`(snippets `form-in-dialog` / `form-in-drawer`),不要把「新建用户 / 编辑角色」做成单独路由,除非字段很多或多步骤。
27
-
28
- ## 列表页区块顺序
29
-
30
- 1. `MLayout fillViewport` + 可选 `MLayoutSider bordered`
31
- 2. 侧栏 `MMenu`
32
- 3. `MLayoutHeader` → `MBreadcrumb`
33
- 4. `MLayoutContent` → `MPageContent`(**仅在下方高度规则适用时**再加 `fill`)
34
- 5. `MPageHeader`(页面标题 + `#actions` 主操作;视口内 **唯一** filled primary)→ `page-header-actions`
35
- 6. `MPageFilters`(默认 **`list-filters-stack`**:折叠高级 + FilterChips;查询/重置 secondary;dense → `list-filters-dense`)
36
- 7. 可选独立 `MPageFilterChips`(若未用 stack)
37
- 8. 可选 `MPageToolbar`(仅批量操作,**不要**页面标题)
38
- 9. `MTable`(直接放置;`#empty` → `MEmpty`)
39
- 10. 分页由 `MTable paginator` 或同级 `MPagination`
40
- 11. 短新建/编辑:同页 `MDialog` + `MForm`;删除:`MConfirmDialog`(`confirm-delete`)
41
-
42
- ### 列表高度 — 先判断,再决定是否 fill
43
-
44
- 仅当 **多数** 条件成立时使用 `MPageContent fill` + `MTable fill paginator`:
45
-
46
- - 全视口后台列表(`MLayout fillViewport`),页面主任务就是浏览一张表
47
- - 表格按内容高度会留下大块空白、分页悬在中间不好看
48
- - 希望表体内部滚动、分页贴在页面最下方
49
-
50
- 以下情况 **不要** 用 `fill`:
51
-
52
- - 嵌入/次要表格(仪表盘「最近」、详情关联表、Card 内)
53
- - 内容本身很短,内容高度表格即可
54
- - 整页应作为文档滚动(长筛选 + 说明 + 表格)
55
- - Dialog / Drawer 内表格
56
- - 表格不是中间唯一主区域的混合布局
57
-
58
- 适合 `fill` 时不要手写 `min-height` / `calc`;不适合时不要硬套 `fill`。
59
-
60
- ## 表单页区块顺序
61
-
62
- 1. `MLayout fillViewport` → `MLayoutHeader` → `MBreadcrumb`
63
- 2. `MPageContent width="narrow"`
64
- 3. `MPageHeader`(标题 + 说明)
65
- 4. `MPageSection variant="form"` → `MForm`
66
- 5. `MPageSection variant="actions"`:保存(primary)+ 取消(secondary)
67
-
68
- ## 仪表盘区块顺序
69
-
70
- 1. `MLayout fillViewport` → `MLayoutHeader` → `MBreadcrumb`
71
- 2. `MPageContent density="spacious"` → `MPageHeader`
72
- 3. KPI:`MGrid` + `MPageStat`(4 列或响应式)
73
- 4. 主区两栏:`MCard shadow="always"` + `MEmpty`(图表未接入)/ 最近列表 `MTable`
74
-
75
- ## 页面标准
76
-
77
- 推荐实践(非强制),完整列表见 MCP `get_design_rules` → `standards`:
78
-
79
- | 主题 | 推荐 | 通常不必 |
80
- | --- | --- | --- |
81
- | 骨架 | `MLayout fillViewport` + `MPageContent` | `MLayoutContent` 再写 padding |
82
- | 区块 | `MPageFilters` / `MPageToolbar` / `MPageSection` | 手写 `.page-*`;`MCard` 再包一层 |
83
- | 列表表格 | `MTable` 直接放 `MPageContent`;高度规则适用时再 `fill` | 用 `MCard` 再包 bordered 表格;不适合时硬套 `fill` |
84
- | 控件间距 | 同行用 `MSpace` / `MFlex`;区块靠 `MPageContent` gap | 外包 padded `div` 叠间距 |
85
- | 滚动 | 全视口主列表可表体滚动(`fill`);否则 Layout / 局部 `MScrollbar` | 在用户内容区强行内置滚动;无理由双滚动条 |
86
- | 颜色 | `--m-*` Token | 页面级 hex / 裸 rgb() |
87
- | 反馈 | 单行结果用 `message`;危险操作用 `MConfirmDialog` | 仅一行文案却用 toast |
88
- | 无障碍 | 图标按钮 `aria-label`、表单可见 label | — |
89
-
90
- 仅允许控件宽度等局部 inline style(如筛选项 `width: 14rem`)。
91
-
92
- ## MCP
93
-
94
- - 整页:`recommend_page`(可传 `style`)→ **`suggestedSnippets` / `get_page_snippet`** + `recommend_component` → `get_design_rules` / `get_style_direction`;可选 `get_golden_page` 核对块顺序
95
- - 风格:有明确参考或明确描述 → 跟用户;有提示词线索 → 推断并说明;不确定 → 先问(无预设表)
96
- - 局部:`get_page_snippet`(如 `form-in-dialog` / `confirm-delete` / `list-filters` / `wizard-steps`)
97
- - 可选:`validate_page`(参考建议,不阻断)
1
+ ---
2
+ description: 生成 Morya UI 业务页面(列表/表单/仪表盘)时的布局规范
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # 页面布局(Agent Requested)
7
+
8
+ 生成完整页面时,先确定类型,按 **块顺序 checklist** 用 MCP **`get_page_snippet`** / `recommend_page.suggestedSnippets` 拼装(composition-first)。仅在不确定整页顺序或用户要求对齐样例时再读 `get_golden_page`。
9
+
10
+ | 类型 | 主要 snippets | 可选黄金样例 |
11
+ | --- | --- | --- |
12
+ | 列表页 | `layout-app-shell` / `page-header-actions` / **`list-filters-stack`**(dense → `list-filters-dense`)/ `list-table` / `form-in-dialog` / `confirm-delete` | `list-page` |
13
+ | 表单页 | `form-header` / `form-body` / `form-actions` | `form-page`(仅长表单 / 独立配置页) |
14
+ | 列表内弹窗表单 | **`form-in-dialog`** | `form-in-dialog` |
15
+ | 详情页 | `detail-toolbar` / `form-in-dialog` | `detail-page` |
16
+ | 结果 / 阻断页 | **`result-block`** | `result-page` |
17
+ | 设置页 | `form-header` / `form-body` / `form-actions` | `settings-page` |
18
+ | 分步向导 | **`wizard-steps`** | `wizard-form` |
19
+ | 仪表盘 | `dashboard-kpi-grid` / `dashboard-chart-card` / `dashboard-recent-table` | `dashboard-page` |
20
+ | 登录页 | **`auth-split-shell`** | `login-page` |
21
+ | 营销落地 | —(见 surfaces) | `landing-page` |
22
+ | 空状态 | **`empty-block`** | `empty-state` |
23
+
24
+ ## 列表内新建 / 编辑(默认)
25
+
26
+ 后台 CRUD **优先**:列表页 + `MDialog`(或 `MDrawer`)内 `MForm`(snippets `form-in-dialog` / `form-in-drawer`),不要把「新建用户 / 编辑角色」做成单独路由,除非字段很多或多步骤。
27
+
28
+ ## 列表页区块顺序
29
+
30
+ 1. `MLayout fillViewport` + 可选 `MLayoutSider bordered`
31
+ 2. 侧栏 `MMenu`
32
+ 3. `MLayoutHeader` → `MBreadcrumb`
33
+ 4. `MLayoutContent` → `MPageContent`(**仅在下方高度规则适用时**再加 `fill`)
34
+ 5. `MPageHeader`(页面标题 + `#actions` 主操作;视口内 **唯一** filled primary)→ `page-header-actions`
35
+ 6. `MPageFilters`(默认 **`list-filters-stack`**:折叠高级 + FilterChips;查询/重置 secondary;dense → `list-filters-dense`)
36
+ 7. 可选独立 `MPageFilterChips`(若未用 stack)
37
+ 8. 可选 `MPageToolbar`(仅批量操作,**不要**页面标题)
38
+ 9. `MTable`(直接放置;`#empty` → `MEmpty`)
39
+ 10. 分页由 `MTable paginator` 或同级 `MPagination`
40
+ 11. 短新建/编辑:同页 `MDialog` + `MForm`;删除:`MConfirmDialog`(`confirm-delete`)
41
+
42
+ ### 列表高度 — 先判断,再决定是否 fill
43
+
44
+ 仅当 **多数** 条件成立时使用 `MPageContent fill` + `MTable fill paginator`:
45
+
46
+ - 全视口后台列表(`MLayout fillViewport`),页面主任务就是浏览一张表
47
+ - 表格按内容高度会留下大块空白、分页悬在中间不好看
48
+ - 希望表体内部滚动、分页贴在页面最下方
49
+
50
+ 以下情况 **不要** 用 `fill`:
51
+
52
+ - 嵌入/次要表格(仪表盘「最近」、详情关联表、Card 内)
53
+ - 内容本身很短,内容高度表格即可
54
+ - 整页应作为文档滚动(长筛选 + 说明 + 表格)
55
+ - Dialog / Drawer 内表格
56
+ - 表格不是中间唯一主区域的混合布局
57
+
58
+ 适合 `fill` 时不要手写 `min-height` / `calc`;不适合时不要硬套 `fill`。
59
+
60
+ ## 表单页区块顺序
61
+
62
+ 1. `MLayout fillViewport` → `MLayoutHeader` → `MBreadcrumb`
63
+ 2. `MPageContent width="narrow"`
64
+ 3. `MPageHeader`(标题 + 说明)
65
+ 4. `MPageSection variant="form"` → `MForm`
66
+ 5. `MPageSection variant="actions"`:保存(primary)+ 取消(secondary)
67
+
68
+ ## 仪表盘区块顺序
69
+
70
+ 1. `MLayout fillViewport` → `MLayoutHeader` → `MBreadcrumb`
71
+ 2. `MPageContent density="spacious"` → `MPageHeader`
72
+ 3. KPI:`MGrid` + `MPageStat`(4 列或响应式)
73
+ 4. 主区两栏:`MCard shadow="always"` + `MEmpty`(图表未接入)/ 最近列表 `MTable`
74
+
75
+ ## 页面标准
76
+
77
+ 推荐实践(非强制),完整列表见 MCP `get_design_rules` → `standards`:
78
+
79
+ | 主题 | 推荐 | 通常不必 |
80
+ | --- | --- | --- |
81
+ | 骨架 | `MLayout fillViewport` + `MPageContent` | `MLayoutContent` 再写 padding |
82
+ | 区块 | `MPageFilters` / `MPageToolbar` / `MPageSection` | 手写 `.page-*`;`MCard` 再包一层 |
83
+ | 列表表格 | `MTable` 直接放 `MPageContent`;高度规则适用时再 `fill` | 用 `MCard` 再包 bordered 表格;不适合时硬套 `fill` |
84
+ | 控件间距 | 同行用 `MSpace` / `MFlex`;区块靠 `MPageContent` gap | 外包 padded `div` 叠间距 |
85
+ | 滚动 | 全视口主列表可表体滚动(`fill`);否则 Layout / 局部 `MScrollbar` | 在用户内容区强行内置滚动;无理由双滚动条 |
86
+ | 颜色 | `--m-*` Token | 页面级 hex / 裸 rgb() |
87
+ | 反馈 | 单行结果用 `message`;危险操作用 `MConfirmDialog` | 仅一行文案却用 toast |
88
+ | 无障碍 | 图标按钮 `aria-label`、表单可见 label | — |
89
+
90
+ 仅允许控件宽度等局部 inline style(如筛选项 `width: 14rem`)。
91
+
92
+ ## MCP
93
+
94
+ - 整页:`recommend_page`(可传 `style`)→ **`suggestedSnippets` / `get_page_snippet`** + `recommend_component` → `get_design_rules` / `get_style_direction`;可选 `get_golden_page` 核对块顺序
95
+ - 风格:有明确参考或明确描述 → 跟用户;有提示词线索 → 推断并说明;不确定 → 先问(无预设表)
96
+ - 局部:`get_page_snippet`(如 `form-in-dialog` / `confirm-delete` / `list-filters` / `wizard-steps`)
97
+ - 可选:`validate_page`(参考建议,不阻断)
@@ -0,0 +1,33 @@
1
+ # AGENTS — morya-ui consumer projects
2
+
3
+ Editor-neutral always-on guidance for AI agents (Cursor, VS Code, Zed, Copilot, CLI agents). Cursor also ships `.cursor/rules/`; this file is the portable subset.
4
+
5
+ ## Before generating or editing UI
6
+
7
+ 1. Read project-root `DESIGN.md` (design contract).
8
+ 2. Prefer skill `.agents/skills/morya-ui-pages/` for page workflows.
9
+ 3. Prefer MCP `@morya-ui/mcp` for real Props / Events / snippets — never invent API names.
10
+
11
+ ## Hard rules
12
+
13
+ 1. Use only `morya-ui` `M*` components; global styles `morya-ui/styles.css`.
14
+ 2. Colors / spacing / radius use `--m-*` CSS variables — no raw hex/rgb in business UI (see `scripts/check-raw-colors.mjs`). Brand differences go in theme overrides (`--m-color-primary`, fonts) per `DESIGN.md`.
15
+ 3. **Composition-first**: with a reference, call MCP `map_reference` (or `recommend_page` + `brief` / `density`) → assemble via `get_page_snippet` from `mapping` / `suggestedSnippets`. Use `page-layouts` as a block-order checklist. `get_golden_page` is optional. Account / Express / Flow signature shells: `get_style_shells`.
16
+ 4. **Visual direction**: clear reference/description → follow the user; clear cues → infer and say so; unsure → ask (`get_style_direction`). There is no named style-preset table. Do not apply default AI gradients / neon / glass without asking.
17
+ 5. Form options: `MSelect` / `MTreeSelect`. Action menus: `MDropdown`.
18
+ 6. Uncertain API → docs site or MCP `get_component`. After writing code: `validate_usage`.
19
+ 7. Root: `MConfigProvider`. Admin chrome: `MLayout` family.
20
+ 8. Feedback defaults to `message` API; use `toast` only for summary + detail or async feel; persistent form errors use field `errorMessage` or token-styled `role="alert"`.
21
+ 9. Before delivery: `validate_page` with the same `brief` — contract `ok` must be true; craft suggestions may be waived with a short note.
22
+
23
+ ## MCP / skills map
24
+
25
+ | Need | Use |
26
+ | --- | --- |
27
+ | Page plan + snippets | `recommend_page`, `map_reference`, `get_page_snippet` |
28
+ | Signature shells | `get_style_shells` |
29
+ | Tokens / composition | `get_design_rules` |
30
+ | Component pick | `recommend_component`, `get_component`, `get_example` |
31
+ | Offline recipes | skill `references/` under `morya-ui-pages` |
32
+
33
+ Setup writes editor MCP configs (Cursor / VS Code / Zed) and root `.mcp.json`. Reload MCP after `npx @morya-ui/setup ai`.
@@ -1,59 +1,81 @@
1
- # Morya UI 设计系统(AI 第一信源)
2
-
3
- > 生成或审查使用 `morya-ui` 的界面时,**必须先遵守本文**。
4
- > 本文只写与页面类型无关的契约。某一类页面怎么拼、用哪个组件,见 `morya-ui-pages` skill 与 MCP,不要从本文推断出唯一布局。
5
-
6
- ## 1. 设计原则
7
-
8
- 1. **组件优先**:交互与布局使用库内 `M*`,不手写等价 DOM。
9
- 2. **令牌优先**:颜色、间距、圆角、阴影、动效使用 `--m-*`;禁止裸 `#hex` / `rgb()`(`scripts/check-raw-colors.mjs` 可扫描)。
10
- 3. **语义一致**:主操作 `severity="primary"`;破坏性操作用 `severity="danger"` 或确认。
11
- 4. **可访问性**:控件有可访问名称;仅图标的按钮带 `aria-label`;浮层可键盘关闭。
12
- 5. **单一事实源**:组件 API 以 MCP / 文档为准,禁止臆造 prop、event、slot。
13
-
14
- ## 2. 应用根
15
-
16
- - 入口引入 `morya-ui/styles.css`(或按需子路径样式)。
17
- - 应用根使用 `MConfigProvider`,统一 locale、主题、密度与浮层挂载。
18
- - 外壳随场景选择,不要把侧栏后台当成所有页面的默认结构。
19
-
20
- ## 3. 设计令牌(摘要)
21
-
22
- 完整定义见 MCP `get_design_rules`。运行时以已安装的 `morya-ui/styles.css` 为准。
23
-
24
- | 用途 | 变量 |
25
- | --- | --- |
26
- | 页面背景 | `--m-color-surface` |
27
- | 正文 | `--m-color-text` |
28
- | 次要文字 | `--m-color-text-muted` |
29
- | 边框 | `--m-color-border` |
30
- | 品牌 / 链接 | `--m-color-primary` |
31
- | 错误 | `--m-color-danger` |
32
- | 区块间距 | `--m-space-4` / `--m-space-6` |
33
- | 圆角 / 阴影 | `--m-radius-md` / `--m-shadow-md` |
34
- | 动效时长 | `--m-motion-fast/normal/enter/exit` |
35
- | 进出场位移 / 缓动 | `--m-motion-distance` / `--m-motion-ease` |
36
-
37
- 动效强度用 `useMotion`(`data-m-motion`:`full` / `reduced` / `none`),不跟随系统 `prefers-reduced-motion`;进出场预设用 `motion.transitions` / 组件 `transition`(见文档站「动效」)。
38
-
39
- ## 4. 禁止项
40
-
41
- - 禁止同一界面混用第二套 UI 库。
42
- - 禁止业务 CSS 写死主题色;暗色须能随 `[data-theme="dark"]` 生效。
43
- - 禁止用不可聚焦的容器冒充按钮。
44
- - 禁止跳过样式入口。
45
- - 禁止把某一类页面的配方写成所有页面的默认结构。
46
-
47
- ## 5. AI 工作流
48
-
49
- 1. 读本文,确认契约。
50
- 2. 加载 `morya-ui-pages`,由 skill 选定表面与布局;可选 companion 只补充视觉或无障碍,冲突时本文 + skill + MCP 优先。
51
- 3. 用 MCP 查真实 API 与样例后再写代码。
52
- 4. 完成后可运行 `pnpm check:colors`(若已配置)。
53
-
54
- ## 6. 相关资源
55
-
56
- - 包:`morya-ui` · 样式:`morya-ui/styles.css`
57
- - 页面配方:`.agents/skills/morya-ui-pages/`(及 setup 时勾选的 companion)
58
- - 主题 API:`useTheme` / `useDensity` / `useMotion`
59
- - 动效预设:`createMoryaUI({ motion })`、`componentDefaults.*.transition`、浮层 `transition` prop;详情见文档 `/docs/motion`
1
+ # Morya UI 设计系统(AI 第一信源)
2
+
3
+ > 生成或审查使用 `morya-ui` 的界面时,**必须先遵守本文**。
4
+ > 本文只写与页面类型无关的契约。某一类页面怎么拼、用哪个组件,见 `morya-ui-pages` skill 与 MCP,不要从本文推断出唯一布局。
5
+
6
+ ## 1. 设计原则
7
+
8
+ 1. **组件优先**:交互与布局使用库内 `M*`,不手写等价 DOM。
9
+ 2. **令牌优先**:颜色、间距、圆角、阴影、动效使用 `--m-*`;禁止裸 `#hex` / `rgb()`(`scripts/check-raw-colors.mjs` 可扫描)。
10
+ 3. **语义一致**:主操作 `severity="primary"`;破坏性操作用 `severity="danger"` 或确认。
11
+ 4. **可访问性**:控件有可访问名称;仅图标的按钮带 `aria-label`;浮层可键盘关闭。
12
+ 5. **单一事实源**:组件 API 以 MCP / 文档为准,禁止臆造 prop、event、slot。
13
+
14
+ ## 2. 应用根
15
+
16
+ - 入口引入 `morya-ui/styles.css`(或按需子路径样式)。
17
+ - 应用根使用 `MConfigProvider`,统一 locale、主题、密度与浮层挂载。
18
+ - 外壳随场景选择,不要把侧栏后台当成所有页面的默认结构。
19
+
20
+ ## 3. 设计令牌(摘要)
21
+
22
+ 完整定义见 MCP `get_design_rules`。运行时以已安装的 `morya-ui/styles.css` 为准。
23
+
24
+ | 用途 | 变量 |
25
+ | --- | --- |
26
+ | 页面背景 | `--m-color-surface` |
27
+ | 正文 | `--m-color-text` |
28
+ | 次要文字 | `--m-color-text-muted` |
29
+ | 边框 | `--m-color-border` |
30
+ | 品牌 / 链接 | `--m-color-primary` |
31
+ | 错误 | `--m-color-danger` |
32
+ | 区块间距 | `--m-space-4` / `--m-space-6` |
33
+ | 圆角 / 阴影 | `--m-radius-md` / `--m-shadow-md` |
34
+ | 动效时长 | `--m-motion-fast/normal/enter/exit` |
35
+ | 进出场位移 / 缓动 | `--m-motion-distance` / `--m-motion-ease` |
36
+
37
+ 动效强度用 `useMotion`(`data-m-motion`:`full` / `reduced` / `none`),不跟随系统 `prefers-reduced-motion`;进出场预设用 `motion.transitions` / 组件 `transition`(见文档站「动效」)。
38
+
39
+ ## 3.1 主题覆盖(品牌差异放这里)
40
+
41
+ 换品牌时**优先改主题变量**,不要在每个业务页写死色:
42
+
43
+ ```css
44
+ /* 例如 src/styles/brand.css,在 styles.css 之后引入 */
45
+ :root {
46
+ --m-color-primary: #0b6e4f; /* 仅主题入口允许;页面仍用 var(--m-*) */
47
+ --m-font-family: "Your Brand Sans", system-ui, sans-serif;
48
+ }
49
+ [data-theme="dark"] {
50
+ --m-color-primary: #3ecf8e;
51
+ }
52
+ ```
53
+
54
+ 也可用 `createMoryaUI` / `MConfigProvider` 的主题 API(见文档「主题」)。`pnpm check:colors` 扫描业务源码中的裸 hex;主题入口文件可按项目约定排除。
55
+
56
+ 登录 / 落地页的「好看」用 MCP `get_style_shells` 的 token 壳(`account-split`、`express-hero` 等),签名仍映射到 `--m-*`。
57
+
58
+ ## 4. 禁止项
59
+
60
+ - 禁止同一界面混用第二套 UI 库。
61
+ - 禁止业务 CSS 写死主题色;暗色须能随 `[data-theme="dark"]` 生效。
62
+ - 禁止用不可聚焦的容器冒充按钮。
63
+ - 禁止跳过样式入口。
64
+ - 禁止把某一类页面的配方写成所有页面的默认结构。
65
+
66
+ ## 5. AI 工作流
67
+
68
+ 1. 读本文,确认契约与主题覆盖位置。
69
+ 2. 加载 `morya-ui-pages`,由 skill 选定表面与布局;有参考图/描述时先 MCP `map_reference`(或 `recommend_page` + `brief`)。
70
+ 3. 用 MCP 查真实 API 与片段后再写代码;签名壳用 `get_style_shells`。
71
+ 4. `validate_usage` + `validate_page`(同一 `brief`);契约问题必须修,工艺建议可说明后放过。
72
+ 5. 可选:起本地页对照参考截首屏(见 skill review-checklist「截图自检」)。
73
+ 6. 完成后可运行 `pnpm check:colors`(若已配置)。
74
+
75
+ ## 6. 相关资源
76
+
77
+ - 包:`morya-ui` · 样式:`morya-ui/styles.css`
78
+ - 页面配方:`.agents/skills/morya-ui-pages/`(及 setup 时勾选的 companion)
79
+ - 主题 API:`useTheme` / `useDensity` / `useMotion`
80
+ - 动效预设:`createMoryaUI({ motion })`、`componentDefaults.*.transition`、浮层 `transition` prop;详情见文档 `/docs/motion`
81
+ - MCP:`map_reference` · `get_style_shells` · `recommend_page` · `validate_page`
@@ -1,74 +1,74 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Scan source files for raw color literals outside design-tokens/ and theme/.
4
- * Usage: node scripts/check-raw-colors.mjs [dir...]
5
- * Exit 1 if violations found.
6
- */
7
- import { readdirSync, readFileSync, statSync } from 'node:fs'
8
- import path from 'node:path'
9
-
10
- const roots = process.argv.slice(2).length ? process.argv.slice(2) : ['src']
11
- const IGNORE_DIRS = new Set(['node_modules', 'dist', 'coverage', 'design-tokens', 'theme', '.git'])
12
- const EXT = new Set(['.vue', '.css', '.scss', '.ts', '.tsx', '.js', '.jsx'])
13
-
14
- const HEX = /#[0-9a-f]{3,8}\b/gi
15
- const RGB = /\brgb\s*\(/g
16
- const HSL = /\bhsl\s*\(/g
17
-
18
- /** Allow transparent, currentColor, inherit in CSS values */
19
- const ALLOW_LINE = /var\s*\(\s*--m-|color-mix\s*\(|transparent|currentColor|inherit|none/
20
-
21
- /** Skip demo IDs like '#1024' or 'WO-1024' in script/template strings */
22
- const DEMO_ID = /['"]#?[A-Z0-9-]{2,}['"]/
23
-
24
- const violations = []
25
-
26
- function walk(dir) {
27
- for (const name of readdirSync(dir)) {
28
- const full = path.join(dir, name)
29
- const st = statSync(full)
30
- if (st.isDirectory()) {
31
- if (IGNORE_DIRS.has(name)) continue
32
- walk(full)
33
- continue
34
- }
35
- const ext = path.extname(name)
36
- if (!EXT.has(ext)) continue
37
- const normalized = full.replace(/\\/g, '/')
38
- if (normalized.includes('design-tokens/') || normalized.includes('/theme/')) continue
39
- if (normalized.includes('.test.') || normalized.includes('/__tests__/')) continue
40
-
41
- const text = readFileSync(full, 'utf8')
42
- const lines = text.split(/\r?\n/)
43
- lines.forEach((line, index) => {
44
- if (ALLOW_LINE.test(line)) return
45
- if (DEMO_ID.test(line)) return
46
- if (HEX.test(line) || RGB.test(line) || HSL.test(line)) {
47
- HEX.lastIndex = 0
48
- RGB.lastIndex = 0
49
- HSL.lastIndex = 0
50
- violations.push({ file: full, line: index + 1, text: line.trim() })
51
- }
52
- })
53
- }
54
- }
55
-
56
- for (const root of roots) {
57
- try {
58
- walk(path.resolve(root))
59
- } catch (error) {
60
- if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') continue
61
- throw error
62
- }
63
- }
64
-
65
- if (violations.length) {
66
- console.error(`Found ${violations.length} raw color literal(s). Use --m-* tokens instead:\n`)
67
- for (const v of violations) {
68
- console.error(` ${v.file}:${v.line}`)
69
- console.error(` ${v.text}\n`)
70
- }
71
- process.exit(1)
72
- }
73
-
74
- console.log('No raw color literals found.')
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Scan source files for raw color literals outside design-tokens/ and theme/.
4
+ * Usage: node scripts/check-raw-colors.mjs [dir...]
5
+ * Exit 1 if violations found.
6
+ */
7
+ import { readdirSync, readFileSync, statSync } from 'node:fs'
8
+ import path from 'node:path'
9
+
10
+ const roots = process.argv.slice(2).length ? process.argv.slice(2) : ['src']
11
+ const IGNORE_DIRS = new Set(['node_modules', 'dist', 'coverage', 'design-tokens', 'theme', '.git'])
12
+ const EXT = new Set(['.vue', '.css', '.scss', '.ts', '.tsx', '.js', '.jsx'])
13
+
14
+ const HEX = /#[0-9a-f]{3,8}\b/gi
15
+ const RGB = /\brgb\s*\(/g
16
+ const HSL = /\bhsl\s*\(/g
17
+
18
+ /** Allow transparent, currentColor, inherit in CSS values */
19
+ const ALLOW_LINE = /var\s*\(\s*--m-|color-mix\s*\(|transparent|currentColor|inherit|none/
20
+
21
+ /** Skip demo IDs like '#1024' or 'WO-1024' in script/template strings */
22
+ const DEMO_ID = /['"]#?[A-Z0-9-]{2,}['"]/
23
+
24
+ const violations = []
25
+
26
+ function walk(dir) {
27
+ for (const name of readdirSync(dir)) {
28
+ const full = path.join(dir, name)
29
+ const st = statSync(full)
30
+ if (st.isDirectory()) {
31
+ if (IGNORE_DIRS.has(name)) continue
32
+ walk(full)
33
+ continue
34
+ }
35
+ const ext = path.extname(name)
36
+ if (!EXT.has(ext)) continue
37
+ const normalized = full.replace(/\\/g, '/')
38
+ if (normalized.includes('design-tokens/') || normalized.includes('/theme/')) continue
39
+ if (normalized.includes('.test.') || normalized.includes('/__tests__/')) continue
40
+
41
+ const text = readFileSync(full, 'utf8')
42
+ const lines = text.split(/\r?\n/)
43
+ lines.forEach((line, index) => {
44
+ if (ALLOW_LINE.test(line)) return
45
+ if (DEMO_ID.test(line)) return
46
+ if (HEX.test(line) || RGB.test(line) || HSL.test(line)) {
47
+ HEX.lastIndex = 0
48
+ RGB.lastIndex = 0
49
+ HSL.lastIndex = 0
50
+ violations.push({ file: full, line: index + 1, text: line.trim() })
51
+ }
52
+ })
53
+ }
54
+ }
55
+
56
+ for (const root of roots) {
57
+ try {
58
+ walk(path.resolve(root))
59
+ } catch (error) {
60
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') continue
61
+ throw error
62
+ }
63
+ }
64
+
65
+ if (violations.length) {
66
+ console.error(`Found ${violations.length} raw color literal(s). Use --m-* tokens instead:\n`)
67
+ for (const v of violations) {
68
+ console.error(` ${v.file}:${v.line}`)
69
+ console.error(` ${v.text}\n`)
70
+ }
71
+ process.exit(1)
72
+ }
73
+
74
+ console.log('No raw color literals found.')