@morya-ui/mcp 0.2.1 → 0.2.3

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 CHANGED
@@ -69,6 +69,11 @@ Resource templates:
69
69
  | `recommend_page` | Recommend a pattern from page intent; optional starter scaffold |
70
70
  | `get_design_rules` | Design-token and composition rules |
71
71
  | `recommend_component` | List, read, or recommend component selection guides |
72
+ | `list_golden_pages` | List golden page samples (list / form / dashboard) |
73
+ | `get_golden_page` | Read golden page Vue source |
74
+ | `list_page_snippets` | List section snippets for local page edits |
75
+ | `get_page_snippet` | Read a section snippet (`filters`, `toolbar`, `form-actions`…) |
76
+ | `validate_page` | Check page composition, spacing, and border anti-patterns |
72
77
 
73
78
  `mode`: `zh` (default) or `en`.
74
79
 
@@ -85,10 +90,19 @@ Component lookup accepts common aliases such as `DataTable`, `数据表格`, `Pa
85
90
  **Plan a page:**
86
91
 
87
92
  1. `recommend_page` with business intent, page type, and features
88
- 2. `get_pattern` for the returned `matchedPattern`
89
- 3. `get_component` / `get_example` for core components
90
- 4. `get_design_rules` before custom layout or CSS
91
- 5. `recommend_component` when choosing between similar components
93
+ 2. `get_golden_page` for the matched golden sample (`list-page`, `form-page`, `dashboard-page`)
94
+ 3. `get_pattern` for the returned `matchedPattern`
95
+ 4. `get_design_rules` for MPage* composition recipes
96
+ 5. `get_component` / `get_example` for core components (including `Page`)
97
+ 6. `recommend_component` when choosing between similar components
98
+ 7. `validate_page` on generated Vue code before finishing
99
+
100
+ **Edit one section of an existing page:**
101
+
102
+ 1. `list_page_snippets` with `query` or `pageType`
103
+ 2. `get_page_snippet` for the matched section (`list-filters`, `form-actions`, …)
104
+ 3. `get_component` / `get_example` for unfamiliar imports
105
+ 4. `validate_usage` + `validate_page` on the merged result
92
106
 
93
107
  For a starter Vue file, pass `includeScaffold: true` to `recommend_page`:
94
108
 
package/data/catalog.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
- "generatedAt": "2026-09-11T07:53:05.288Z",
2
+ "generatedAt": "2026-09-14T03:20:02.421Z",
3
3
  "library": {
4
4
  "name": "morya-ui",
5
- "version": "0.2.1"
5
+ "version": "0.2.3"
6
6
  },
7
7
  "mcp": {
8
8
  "name": "@morya-ui/mcp",
9
- "version": "0.2.1"
9
+ "version": "0.2.3"
10
10
  },
11
11
  "components": [
12
12
  {
@@ -12448,7 +12448,7 @@
12448
12448
  "sectionId": "引入",
12449
12449
  "lang": "ts",
12450
12450
  "preview": false,
12451
- "code": "import { MMenu, type MenuItem } from 'morya-ui'",
12451
+ "code": "import type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'",
12452
12452
  "locale": "zh-CN"
12453
12453
  },
12454
12454
  {
@@ -12457,7 +12457,7 @@
12457
12457
  "sectionId": "导航选中",
12458
12458
  "lang": "vue",
12459
12459
  "preview": true,
12460
- "code": "<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>",
12460
+ "code": "<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>",
12461
12461
  "locale": "zh-CN"
12462
12462
  },
12463
12463
  {
@@ -12538,7 +12538,7 @@
12538
12538
  "sectionId": "import",
12539
12539
  "lang": "ts",
12540
12540
  "preview": false,
12541
- "code": "import { MMenu, type MenuItem } from 'morya-ui'",
12541
+ "code": "import type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'",
12542
12542
  "locale": "en-US"
12543
12543
  },
12544
12544
  {
@@ -12636,12 +12636,12 @@
12636
12636
  {
12637
12637
  "id": "引入",
12638
12638
  "title": "引入",
12639
- "body": "```ts\nimport { MMenu, type MenuItem } from 'morya-ui'\n```"
12639
+ "body": "```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```"
12640
12640
  },
12641
12641
  {
12642
12642
  "id": "导航选中",
12643
12643
  "title": "导航选中",
12644
- "body": "为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。"
12644
+ "body": "为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。"
12645
12645
  },
12646
12646
  {
12647
12647
  "id": "嵌套子菜单",
@@ -12656,7 +12656,7 @@
12656
12656
  {
12657
12657
  "id": "折叠与飞出层",
12658
12658
  "title": "折叠与飞出层",
12659
- "body": "`collapsed` 隐藏文案,仅保留图标;悬停/聚焦带子项的节点时,在右侧弹出飞出层(`.m-menu--flyout`)。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
12659
+ "body": "`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout`)。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
12660
12660
  },
12661
12661
  {
12662
12662
  "id": "嵌入-layout-侧栏",
@@ -12704,7 +12704,7 @@
12704
12704
  "body": "<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。"
12705
12705
  }
12706
12706
  ],
12707
- "markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: 垂直/水平导航菜单,支持多级嵌套、受控选中、手风琴展开与折叠侧栏飞出层。\n---\n\n# Menu\n\n基于 `model` 渲染的**导航菜单**,适合后台侧栏、顶栏导航等场景。支持:\n\n- 多级嵌套 `items` 与受控 `selectedKey`\n- 展开路径自动跟随选中项;`accordion` 手风琴\n- `collapsed` 图标模式 + 右侧飞出子菜单(Popover)\n- 非 popup 时默认 `embedded`,无边框铺满 `MLayoutSider`\n\n> 单层悬停子菜单见 [TieredMenu](/components/TieredMenu);顶栏菜单见 [Menubar](/components/Menubar);操作列表见 [Dropdown](/components/Dropdown)。\n\n## 引入\n\n```ts\nimport { MMenu, type MenuItem } from 'morya-ui'\n```\n\n## 导航选中\n\n为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。\n\n## 嵌套子菜单\n\n点击带子项的节点可展开/收起;选中子项时父级会显示 `child-active` 高亮。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: '数据分析',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: '报表', icon: 'file-text' },\n { key: 'monitor', label: '监控', icon: 'activity' },\n ],\n },\n { key: 'settings', label: '系统设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## 手风琴与展开控制\n\n`accordion` 同时只保留一个一级子菜单展开。`defaultExpandedKeys` / `v-model:expanded-keys` 可受控展开项;变更 `selectedKey` 时会自动展开其祖先路径。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: '模块 A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: '模块 B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## 折叠与飞出层\n\n`collapsed` 隐藏文案,仅保留图标;悬停/聚焦带子项的节点时,在右侧弹出飞出层(`.m-menu--flyout`)。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## 嵌入 Layout 侧栏\n\n推荐结构:**全局 Header + 下方 `has-sider` Layout**。菜单放在 `MLayoutSider` 内,与 `v-model:collapsed` 联动。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: '系统',\n icon: 'settings',\n items: [\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'roles', label: '角色', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">头部菜单</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"120\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n 当前选中:<strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## 水平菜单\n\n`mode=\"horizontal\"` 用于顶栏一级导航;子菜单以下拉飞出层展示,选中后自动关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: '首页', icon: 'home' },\n {\n key: 'products',\n label: '产品',\n icon: 'box',\n items: [\n { key: 'cloud', label: '云服务', icon: 'cloud' },\n { key: 'edge', label: '边缘计算', icon: 'server' },\n ],\n },\n { key: 'about', label: '关于', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## 反色(深色侧栏)\n\n`inverted` 配合 `MLayoutSider` 的 `inverted`,用于深色背景侧栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 将菜单作为浮层,默认 Teleport 到 `body` 并相对**默认插槽触发器**定位(无插槽时回退到最后一次指针位置)。点击外部或选中叶子项后关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: '复制', command: () => undefined },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"更多操作\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | 菜单项,可嵌套 `items`。 |\n| `popup` | `boolean` | `false` | 浮层模式;配合 `v-model` 控制显隐。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | popup 相对触发器的位置。 |\n| `selectedKey` | `string \\| null` | `null` | 当前选中项 key(`v-model:selected-key`)。 |\n| `collapsed` | `boolean` | `false` | 图标模式;子菜单以右侧飞出层展示。 |\n| `collapsedWidth` | `number` | `80` | 折叠宽度(px),用于居中图标。 |\n| `indent` | `number` | `12` | 每层额外左内边距(px)。 |\n| `rootIndent` | `number` | `16` | 根级左内边距(px)。 |\n| `accordion` | `boolean` | `false` | 手风琴:同时只展开一个一级子菜单。 |\n| `defaultExpandedKeys` | `string[]` | `[]` | 默认展开的 submenu keys。 |\n| `expandedKeys` | `string[]` | — | 受控展开 keys(`v-model:expanded-keys`)。 |\n| `defaultExpandAll` | `boolean` | `false` | 初始展开全部子菜单。 |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | 布局方向。 |\n| `inverted` | `boolean` | `false` | 反色样式,适合深色侧栏。 |\n| `embedded` | `boolean` | `!popup` | 嵌入布局:去边框与最小宽度。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport 到 `appendTo`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport 目标;未传时使用 ConfigProvider。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n| `update:selectedKey` | `string \\| null` | 选中项变化。 |\n| `update:expandedKeys` | `string[]` | 展开项变化。 |\n| `select` | `MenuItem` | 点击叶子项(非 disabled / separator)。 |\n\n## Slots\n\n| 插槽 | 说明 |\n| --- | --- |\n| `default` | popup 模式的触发器锚点(如按钮);菜单相对其定位。 |\n\n## MenuItem\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `key` | `string` | 唯一标识;未传时使用 `label`。 |\n| `label` | `string` | 展示文本。 |\n| `icon` | `string` | [Tabler 图标名](/components/Icon) 或字符。 |\n| `command` | `() => void` | 点击回调(与 `select` 事件同时触发)。 |\n| `disabled` | `boolean` | 禁用。 |\n| `separator` | `boolean` | 分隔线(忽略其他字段)。 |\n| `items` | `MenuItem[]` | 子菜单。 |\n\n## 类型\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。\n"
12707
+ "markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: 垂直/水平导航菜单,支持多级嵌套、受控选中、手风琴展开与折叠侧栏飞出层。\n---\n\n# Menu\n\n基于 `model` 渲染的**导航菜单**,适合后台侧栏、顶栏导航等场景。支持:\n\n- 多级嵌套 `items` 与受控 `selectedKey`\n- 展开路径自动跟随选中项;`accordion` 手风琴\n- `collapsed` 图标模式 + 右侧飞出子菜单(Popover)\n- 非 popup 时默认 `embedded`,无边框铺满 `MLayoutSider`\n\n> 单层悬停子菜单见 [TieredMenu](/components/TieredMenu);顶栏菜单见 [Menubar](/components/Menubar);操作列表见 [Dropdown](/components/Dropdown)。\n\n## 引入\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## 导航选中\n\n为叶子项设置稳定的 `key`,用 `v-model:selected-key` 与路由同步;点击时触发 `select`。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'settings', label: '设置', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\n未提供 `key` 时会回退到 `label`;生产环境建议始终显式设置 `key`。\n\n## 嵌套子菜单\n\n点击带子项的节点可展开/收起;选中子项时父级会显示 `child-active` 高亮。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: '数据分析',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: '报表', icon: 'file-text' },\n { key: 'monitor', label: '监控', icon: 'activity' },\n ],\n },\n { key: 'settings', label: '系统设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## 手风琴与展开控制\n\n`accordion` 同时只保留一个一级子菜单展开。`defaultExpandedKeys` / `v-model:expanded-keys` 可受控展开项;变更 `selectedKey` 时会自动展开其祖先路径。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: '模块 A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: '模块 B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## 折叠与飞出层\n\n`collapsed` 隐藏文案,仅保留图标;悬停时在右侧显示 `MTooltip` 标签,带子项的节点还会弹出飞出层(`.m-menu--flyout`)。`collapsed-width` 应与侧栏折叠宽度一致,用于居中图标。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: '工作区',\n icon: 'folder',\n items: [\n { key: 'home', label: '首页', icon: 'home' },\n { key: 'docs', label: '文档', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: '设置', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n 展开\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n 折叠\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## 嵌入 Layout 侧栏\n\n推荐结构:**全局 Header + 下方 `has-sider` Layout**。菜单放在 `MLayoutSider` 内,与 `v-model:collapsed` 联动。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: '系统',\n icon: 'settings',\n items: [\n { key: 'users', label: '用户', icon: 'users' },\n { key: 'roles', label: '角色', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">头部菜单</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"120\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n 当前选中:<strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## 水平菜单\n\n`mode=\"horizontal\"` 用于顶栏一级导航;子菜单以下拉飞出层展示,选中后自动关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: '首页', icon: 'home' },\n {\n key: 'products',\n label: '产品',\n icon: 'box',\n items: [\n { key: 'cloud', label: '云服务', icon: 'cloud' },\n { key: 'edge', label: '边缘计算', icon: 'server' },\n ],\n },\n { key: 'about', label: '关于', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## 反色(深色侧栏)\n\n`inverted` 配合 `MLayoutSider` 的 `inverted`,用于深色背景侧栏。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: '仪表盘', icon: 'layout-dashboard' },\n { key: 'users', label: '用户', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## 弹出模式\n\n`popup` + `v-model` 将菜单作为浮层,默认 Teleport 到 `body` 并相对**默认插槽触发器**定位(无插槽时回退到最后一次指针位置)。点击外部或选中叶子项后关闭。\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: '复制', command: () => undefined },\n { separator: true },\n { label: '删除', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"更多操作\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | 菜单项,可嵌套 `items`。 |\n| `popup` | `boolean` | `false` | 浮层模式;配合 `v-model` 控制显隐。 |\n| `modelValue` | `boolean` | `false` | popup 可见性(`v-model`)。 |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | popup 相对触发器的位置。 |\n| `selectedKey` | `string \\| null` | `null` | 当前选中项 key(`v-model:selected-key`)。 |\n| `collapsed` | `boolean` | `false` | 图标模式;子菜单以右侧飞出层展示。 |\n| `collapsedWidth` | `number` | `80` | 折叠宽度(px),用于居中图标。 |\n| `indent` | `number` | `12` | 每层额外左内边距(px)。 |\n| `rootIndent` | `number` | `16` | 根级左内边距(px)。 |\n| `accordion` | `boolean` | `false` | 手风琴:同时只展开一个一级子菜单。 |\n| `defaultExpandedKeys` | `string[]` | `[]` | 默认展开的 submenu keys。 |\n| `expandedKeys` | `string[]` | — | 受控展开 keys(`v-model:expanded-keys`)。 |\n| `defaultExpandAll` | `boolean` | `false` | 初始展开全部子菜单。 |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | 布局方向。 |\n| `inverted` | `boolean` | `false` | 反色样式,适合深色侧栏。 |\n| `embedded` | `boolean` | `!popup` | 嵌入布局:去边框与最小宽度。 |\n| `teleport` | `boolean` | `true` | popup 时 Teleport 到 `appendTo`。 |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport 目标;未传时使用 ConfigProvider。 |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | DOM 透传,见 [样式与 attrs](/docs/attrs). |\n\n\n## Events\n\n| 事件名 | 参数 | 说明 |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | popup 可见性变化。 |\n| `update:selectedKey` | `string \\| null` | 选中项变化。 |\n| `update:expandedKeys` | `string[]` | 展开项变化。 |\n| `select` | `MenuItem` | 点击叶子项(非 disabled / separator)。 |\n\n## Slots\n\n| 插槽 | 说明 |\n| --- | --- |\n| `default` | popup 模式的触发器锚点(如按钮);菜单相对其定位。 |\n\n## MenuItem\n\n| 字段 | 类型 | 说明 |\n| --- | --- | --- |\n| `key` | `string` | 唯一标识;未传时使用 `label`。 |\n| `label` | `string` | 展示文本。 |\n| `icon` | `string` | [Tabler 图标名](/components/Icon) 或字符。 |\n| `command` | `() => void` | 点击回调(与 `select` 事件同时触发)。 |\n| `disabled` | `boolean` | 禁用。 |\n| `separator` | `boolean` | 分隔线(忽略其他字段)。 |\n| `items` | `MenuItem[]` | 子菜单。 |\n\n## 类型\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\n`model` 数组项,支持嵌套:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // 有 vue-router 时用 RouterLink\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\n`key` 未传时用 `label` 作为标识。`separator: true` 时渲染分隔线。更多见 [API 类型](/docs/types)。\n"
12708
12708
  },
12709
12709
  "en-US": {
12710
12710
  "title": "Menu",
@@ -12718,7 +12718,7 @@
12718
12718
  {
12719
12719
  "id": "import",
12720
12720
  "title": "Import",
12721
- "body": "```ts\nimport { MMenu, type MenuItem } from 'morya-ui'\n```"
12721
+ "body": "```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```"
12722
12722
  },
12723
12723
  {
12724
12724
  "id": "selection",
@@ -12738,7 +12738,7 @@
12738
12738
  {
12739
12739
  "id": "collapsed-flyout",
12740
12740
  "title": "Collapsed & flyout",
12741
- "body": "`collapsed` hides labels and keeps icons. Hover/focus on a group opens a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
12741
+ "body": "`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```"
12742
12742
  },
12743
12743
  {
12744
12744
  "id": "embed-in-layout-sider",
@@ -12786,7 +12786,7 @@
12786
12786
  "body": "<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types)."
12787
12787
  }
12788
12788
  ],
12789
- "markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: Vertical/horizontal navigation menu with nested items, controlled selection, accordion, and collapsed flyout submenus.\n---\n\n# Menu\n\nNavigation menu rendered from a `model`. Typical uses: admin sidebar, top navigation.\n\n- Nested `items` with controlled `selectedKey`\n- Auto-expand ancestor path when selection changes; optional `accordion`\n- `collapsed` icon rail with right-side flyout submenus (Popover)\n- Non-popup menus default to `embedded` (borderless, full-width in `MLayoutSider`)\n\n> One-level hover submenus: [TieredMenu](/components/TieredMenu). Top bar: [Menubar](/components/Menubar). Action lists: [Dropdown](/components/Dropdown).\n\n## Import\n\n```ts\nimport { MMenu, type MenuItem } from 'morya-ui'\n```\n\n## Selection\n\nGive leaf items stable `key` values; sync with routing via `v-model:selected-key`. Emits `select` on click.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'settings', label: 'Settings', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\nIf `key` is omitted, `label` is used as fallback. Prefer explicit keys in production.\n\n## Nested submenus\n\nClick a group to expand/collapse. When a child is selected, the parent shows `child-active` styling.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: 'Analytics',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: 'Reports', icon: 'file-text' },\n { key: 'monitor', label: 'Monitor', icon: 'activity' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## Accordion & expanded keys\n\n`accordion` keeps at most one top-level group open. Use `defaultExpandedKeys` or `v-model:expanded-keys` for controlled expansion. Changing `selectedKey` auto-expands its ancestor path.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: 'Module A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: 'Module B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## Collapsed & flyout\n\n`collapsed` hides labels and keeps icons. Hover/focus on a group opens a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## Embed in Layout sider\n\nRecommended shell: **global Header + inner `has-sider` Layout**. Bind menu `collapsed` to `MLayoutSider`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: 'System',\n icon: 'settings',\n items: [\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'roles', label: 'Roles', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">Morya UI</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"64\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n accordion\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n Selected: <strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Horizontal mode\n\n`mode=\"horizontal\"` for top nav bars. Submenus open in a dropdown flyout and close after selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: 'Home', icon: 'home' },\n {\n key: 'products',\n label: 'Products',\n icon: 'box',\n items: [\n { key: 'cloud', label: 'Cloud', icon: 'cloud' },\n { key: 'edge', label: 'Edge', icon: 'server' },\n ],\n },\n { key: 'about', label: 'About', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## Inverted (dark sider)\n\nUse `inverted` with `MLayoutSider`'s `inverted` on dark backgrounds.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## Popup mode\n\n`popup` + `v-model` renders a floating menu, teleported to `body` by default and positioned against the **default-slot trigger** (falls back to the last pointer position when no slot). Closes on outside click or leaf selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: 'Copy', command: () => undefined },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"More\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | Menu items; may nest `items`. |\n| `popup` | `boolean` | `false` | Overlay mode; use with `v-model`. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | Popup position relative to the trigger. |\n| `selectedKey` | `string \\| null` | `null` | Selected item key (`v-model:selected-key`). |\n| `collapsed` | `boolean` | `false` | Icon-only; submenus in right flyout. |\n| `collapsedWidth` | `number` | `80` | Collapsed width (px) for icon centering. |\n| `indent` | `number` | `12` | Extra padding-left per level (px). |\n| `rootIndent` | `number` | `16` | Root item padding-left (px). |\n| `accordion` | `boolean` | `false` | Only one top-level submenu open at a time. |\n| `defaultExpandedKeys` | `string[]` | `[]` | Initially expanded submenu keys. |\n| `expandedKeys` | `string[]` | — | Controlled expanded keys (`v-model:expanded-keys`). |\n| `defaultExpandAll` | `boolean` | `false` | Expand all submenus initially. |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | Layout direction. |\n| `inverted` | `boolean` | `false` | Inverted colors for dark sider. |\n| `embedded` | `boolean` | `!popup` | Embed in layout (no border/min-width). |\n| `teleport` | `boolean` | `true` | Teleport popup to `appendTo`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport target; falls back to ConfigProvider. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility changed. |\n| `update:selectedKey` | `string \\| null` | Selected item changed. |\n| `update:expandedKeys` | `string[]` | Expanded keys changed. |\n| `select` | `MenuItem` | Leaf clicked (not disabled / separator). |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Popup trigger anchor (e.g. a button); menu positions against it. |\n\n## MenuItem\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `key` | `string` | Unique id; falls back to `label`. |\n| `label` | `string` | Display text. |\n| `icon` | `string` | [Tabler icon name](/components/Icon) or character. |\n| `command` | `() => void` | Click handler (also emits `select`). |\n| `disabled` | `boolean` | Disabled state. |\n| `separator` | `boolean` | Separator line (ignores other fields). |\n| `items` | `MenuItem[]` | Child menu items. |\n\n## Types\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types).\n"
12789
+ "markdown": "---\ntitle: Menu\ncategory: 04 / NAVIGATION\ndescription: Vertical/horizontal navigation menu with nested items, controlled selection, accordion, and collapsed flyout submenus.\n---\n\n# Menu\n\nNavigation menu rendered from a `model`. Typical uses: admin sidebar, top navigation.\n\n- Nested `items` with controlled `selectedKey`\n- Auto-expand ancestor path when selection changes; optional `accordion`\n- `collapsed` icon rail with right-side flyout submenus (Popover)\n- Non-popup menus default to `embedded` (borderless, full-width in `MLayoutSider`)\n\n> One-level hover submenus: [TieredMenu](/components/TieredMenu). Top bar: [Menubar](/components/Menubar). Action lists: [Dropdown](/components/Dropdown).\n\n## Import\n\n```ts\nimport type {MenuItem} from 'morya-ui';\nimport { MMenu } from 'morya-ui'\n```\n\n## Selection\n\nGive leaf items stable `key` values; sync with routing via `v-model:selected-key`. Emits `select` on click.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'settings', label: 'Settings', icon: 'settings', disabled: true },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 15rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n embedded\n @select=\"(item) => console.log('select', item.key)\"\n />\n </div>\n</template>\n```\n\nIf `key` is omitted, `label` is used as fallback. Prefer explicit keys in production.\n\n## Nested submenus\n\nClick a group to expand/collapse. When a child is selected, the parent shows `child-active` styling.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('reports')\nconst model = [\n {\n key: 'analytics',\n label: 'Analytics',\n icon: 'chart-bar',\n items: [\n { key: 'reports', label: 'Reports', icon: 'file-text' },\n { key: 'monitor', label: 'Monitor', icon: 'activity' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 16rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n</template>\n```\n\n## Accordion & expanded keys\n\n`accordion` keeps at most one top-level group open. Use `defaultExpandedKeys` or `v-model:expanded-keys` for controlled expansion. Changing `selectedKey` auto-expands its ancestor path.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('a1')\nconst model = [\n { key: 'a', label: 'Module A', items: [{ key: 'a1', label: 'A-1' }] },\n { key: 'b', label: 'Module B', items: [{ key: 'b1', label: 'B-1' }] },\n]\n</script>\n\n<template>\n <div\n style=\"\n width: 14rem;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" accordion embedded />\n </div>\n</template>\n```\n\n## Collapsed & flyout\n\n`collapsed` hides labels and keeps icons. Hover shows an `MTooltip` with the label; groups also open a right flyout (`.m-menu--flyout`). Set `collapsed-width` to match the sider width for centered icons.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n {\n key: 'workspace',\n label: 'Workspace',\n icon: 'folder',\n items: [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'docs', label: 'Docs', icon: 'file-text' },\n ],\n },\n { key: 'settings', label: 'Settings', icon: 'settings' },\n]\n</script>\n\n<template>\n <div style=\"display: flex; gap: var(--m-space-4); align-items: stretch\">\n <div\n style=\"\n flex: 1;\n min-width: 0;\n padding: var(--m-space-3);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n \"\n >\n Expanded\n </p>\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" embedded />\n </div>\n <div\n style=\"\n width: 4.5rem;\n padding: var(--m-space-3) var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <p\n style=\"\n margin: 0 0 var(--m-space-3);\n font-size: var(--m-font-size-xs);\n color: var(--m-color-text-muted);\n text-align: center;\n \"\n >\n Collapsed\n </p>\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n collapsed\n embedded\n :collapsed-width=\"64\"\n />\n </div>\n </div>\n</template>\n```\n\n## Embed in Layout sider\n\nRecommended shell: **global Header + inner `has-sider` Layout**. Bind menu `collapsed` to `MLayoutSider`.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MLayout,\n MLayoutContent,\n MLayoutHeader,\n MLayoutSider,\n MMenu,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst collapsed = ref(false)\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n {\n key: 'system',\n label: 'System',\n icon: 'settings',\n items: [\n { key: 'users', label: 'Users', icon: 'users' },\n { key: 'roles', label: 'Roles', icon: 'shield' },\n ],\n },\n]\n</script>\n\n<template>\n <MLayout\n style=\"\n height: 14rem;\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutHeader\n bordered\n style=\"\n padding: 0 var(--m-space-4);\n display: flex;\n align-items: center;\n min-height: var(--m-layout-header-height);\n \"\n >\n <strong style=\"color: var(--m-color-primary); font-size: var(--m-font-size-md)\">Morya UI</strong>\n </MLayoutHeader>\n <MLayout has-sider>\n <MLayoutSider\n v-model:collapsed=\"collapsed\"\n bordered\n show-trigger=\"arrow-circle\"\n collapse-mode=\"width\"\n :collapsed-width=\"64\"\n >\n <MMenu\n v-model:selected-key=\"selectedKey\"\n :model=\"model\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"64\"\n accordion\n />\n </MLayoutSider>\n <MLayoutContent embedded content-style=\"padding: var(--m-space-4)\">\n <p style=\"margin: 0; color: var(--m-color-text-muted); font-size: var(--m-font-size-sm)\">\n Selected: <strong style=\"color: var(--m-color-text)\">{{ selectedKey }}</strong>\n </p>\n </MLayoutContent>\n </MLayout>\n </MLayout>\n</template>\n```\n\n## Horizontal mode\n\n`mode=\"horizontal\"` for top nav bars. Submenus open in a dropdown flyout and close after selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('home')\nconst model = [\n { key: 'home', label: 'Home', icon: 'home' },\n {\n key: 'products',\n label: 'Products',\n icon: 'box',\n items: [\n { key: 'cloud', label: 'Cloud', icon: 'cloud' },\n { key: 'edge', label: 'Edge', icon: 'server' },\n ],\n },\n { key: 'about', label: 'About', icon: 'info-circle' },\n]\n</script>\n\n<template>\n <div\n style=\"\n padding: 0 var(--m-space-2);\n background: var(--m-color-surface);\n border: 1px solid var(--m-color-border);\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n \"\n >\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" mode=\"horizontal\" embedded />\n </div>\n</template>\n```\n\n## Inverted (dark sider)\n\nUse `inverted` with `MLayoutSider`'s `inverted` on dark backgrounds.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MLayout, MLayoutSider, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst selectedKey = ref('dashboard')\nconst model = [\n { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard' },\n { key: 'users', label: 'Users', icon: 'users' },\n]\n</script>\n\n<template>\n <MLayout\n has-sider\n style=\"\n height: 10rem;\n border-radius: var(--m-radius-lg);\n box-shadow: var(--m-shadow-sm);\n overflow: hidden;\n \"\n >\n <MLayoutSider inverted bordered style=\"width: 12rem\">\n <MMenu v-model:selected-key=\"selectedKey\" :model=\"model\" inverted />\n </MLayoutSider>\n </MLayout>\n</template>\n```\n\n## Popup mode\n\n`popup` + `v-model` renders a floating menu, teleported to `body` by default and positioned against the **default-slot trigger** (falls back to the last pointer position when no slot). Closes on outside click or leaf selection.\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MButton, MMenu } from 'morya-ui'\nimport { ref } from 'vue'\n\nconst open = ref(false)\nconst model = [\n { label: 'Copy', command: () => undefined },\n { separator: true },\n { label: 'Delete', disabled: true },\n]\n</script>\n\n<template>\n <MMenu v-model=\"open\" popup :model=\"model\">\n <MButton label=\"More\" @click=\"open = !open\" />\n </MMenu>\n</template>\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `model` | `MenuItem[]` | — | Menu items; may nest `items`. |\n| `popup` | `boolean` | `false` | Overlay mode; use with `v-model`. |\n| `modelValue` | `boolean` | `false` | Popup visibility (`v-model`). |\n| `placement` | `'bottom-start' \\| 'bottom-end' \\| 'top-start' \\| 'top-end'` | `'bottom-start'` | Popup position relative to the trigger. |\n| `selectedKey` | `string \\| null` | `null` | Selected item key (`v-model:selected-key`). |\n| `collapsed` | `boolean` | `false` | Icon-only; submenus in right flyout. |\n| `collapsedWidth` | `number` | `80` | Collapsed width (px) for icon centering. |\n| `indent` | `number` | `12` | Extra padding-left per level (px). |\n| `rootIndent` | `number` | `16` | Root item padding-left (px). |\n| `accordion` | `boolean` | `false` | Only one top-level submenu open at a time. |\n| `defaultExpandedKeys` | `string[]` | `[]` | Initially expanded submenu keys. |\n| `expandedKeys` | `string[]` | — | Controlled expanded keys (`v-model:expanded-keys`). |\n| `defaultExpandAll` | `boolean` | `false` | Expand all submenus initially. |\n| `mode` | `'vertical' \\| 'horizontal'` | `'vertical'` | Layout direction. |\n| `inverted` | `boolean` | `false` | Inverted colors for dark sider. |\n| `embedded` | `boolean` | `!popup` | Embed in layout (no border/min-width). |\n| `teleport` | `boolean` | `true` | Teleport popup to `appendTo`. |\n| `appendTo` | `string \\| HTMLElement \\| 'self' \\| false` | `'body'` | Teleport target; falls back to ConfigProvider. |\n| `pt` | [RootPassThrough](/docs/types#RootPassThrough) `{ root? }` | — | Pass-through; see [Styling & attrs](/docs/attrs). |\n\n\n## Events\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `update:modelValue` | `boolean` | Popup visibility changed. |\n| `update:selectedKey` | `string \\| null` | Selected item changed. |\n| `update:expandedKeys` | `string[]` | Expanded keys changed. |\n| `select` | `MenuItem` | Leaf clicked (not disabled / separator). |\n\n## Slots\n\n| Slot | Description |\n| --- | --- |\n| `default` | Popup trigger anchor (e.g. a button); menu positions against it. |\n\n## MenuItem\n\n| Field | Type | Description |\n| --- | --- | --- |\n| `key` | `string` | Unique id; falls back to `label`. |\n| `label` | `string` | Display text. |\n| `icon` | `string` | [Tabler icon name](/components/Icon) or character. |\n| `command` | `() => void` | Click handler (also emits `select`). |\n| `disabled` | `boolean` | Disabled state. |\n| `separator` | `boolean` | Separator line (ignores other fields). |\n| `items` | `MenuItem[]` | Child menu items. |\n\n## Types\n\n<h4 id=\"MenuItem\">MenuItem</h4>\n\nEach entry in `model`; supports nesting:\n\n```ts\ninterface MenuItem {\n key?: string\n label?: string\n icon?: string\n to?: string | RouteLocationRaw // RouterLink when vue-router is installed\n command?: () => void\n disabled?: boolean\n separator?: boolean\n items?: MenuItem[]\n}\n```\n\nWhen `key` is omitted, `label` is used as the identifier. `separator: true` renders a divider. See also [API types](/docs/types).\n"
12790
12790
  }
12791
12791
  }
12792
12792
  },
@@ -13553,6 +13553,150 @@
13553
13553
  }
13554
13554
  }
13555
13555
  },
13556
+ {
13557
+ "id": "Page",
13558
+ "name": "Page",
13559
+ "exportName": "MPageContent",
13560
+ "category": "06 / LAYOUT",
13561
+ "description": "业务页面组合区块,封装间距、边框与标题层级,少写或不写页面 CSS。",
13562
+ "descriptionEn": "Composable page sections for spacing, borders, and headings with little or no custom CSS.",
13563
+ "import": "import { MPageContent } from 'morya-ui'",
13564
+ "props": [],
13565
+ "events": [],
13566
+ "slots": [],
13567
+ "examples": [
13568
+ {
13569
+ "id": "引入-1",
13570
+ "section": "引入",
13571
+ "sectionId": "引入",
13572
+ "lang": "ts",
13573
+ "preview": false,
13574
+ "code": "import {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'",
13575
+ "locale": "zh-CN"
13576
+ },
13577
+ {
13578
+ "id": "列表页组合-1",
13579
+ "section": "列表页组合",
13580
+ "sectionId": "列表页组合",
13581
+ "lang": "vue",
13582
+ "preview": true,
13583
+ "code": "<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"筛选\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"搜索\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">查询</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"用户管理\">\n <template #actions>\n <MButton severity=\"primary\">新建</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>",
13584
+ "locale": "zh-CN"
13585
+ },
13586
+ {
13587
+ "id": "表单页组合-1",
13588
+ "section": "表单页组合",
13589
+ "sectionId": "表单页组合",
13590
+ "lang": "vue",
13591
+ "preview": true,
13592
+ "code": "<script setup lang=\"ts\">\nimport {\n MButton,\n MForm,\n MFormItem,\n MInput,\n MPageContent,\n MPageHeader,\n MPageSection,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst name = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent width=\"narrow\">\n <MPageHeader title=\"新建用户\" description=\"填写基本信息。\" />\n <MPageSection variant=\"form\">\n <MForm @submit.prevent>\n <MFormItem label=\"姓名\" name=\"name\">\n <MInput v-model=\"name\" fluid />\n </MFormItem>\n <MPageSection variant=\"actions\">\n <MSpace>\n <MButton native-type=\"submit\" severity=\"primary\">保存</MButton>\n <MButton severity=\"secondary\">取消</MButton>\n </MSpace>\n </MPageSection>\n </MForm>\n </MPageSection>\n </MPageContent>\n </div>\n</template>",
13593
+ "locale": "zh-CN"
13594
+ },
13595
+ {
13596
+ "id": "仪表盘-kpi-1",
13597
+ "section": "仪表盘 KPI",
13598
+ "sectionId": "仪表盘-kpi",
13599
+ "lang": "vue",
13600
+ "preview": true,
13601
+ "code": "<script setup lang=\"ts\">\nimport { MPageContent, MPageStat } from 'morya-ui'\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent density=\"compact\">\n <MPageStat label=\"总用户\" value=\"12,480\" trend=\"+8.2%\" icon=\"users\" />\n </MPageContent>\n </div>\n</template>",
13602
+ "locale": "zh-CN"
13603
+ },
13604
+ {
13605
+ "id": "import-1",
13606
+ "section": "Import",
13607
+ "sectionId": "import",
13608
+ "lang": "ts",
13609
+ "preview": false,
13610
+ "code": "import {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'",
13611
+ "locale": "en-US"
13612
+ },
13613
+ {
13614
+ "id": "list-page-stack-1",
13615
+ "section": "List page stack",
13616
+ "sectionId": "list-page-stack",
13617
+ "lang": "vue",
13618
+ "preview": true,
13619
+ "code": "<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"Filters\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"Search\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">Search</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"Users\">\n <template #actions>\n <MButton severity=\"primary\">Create</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>",
13620
+ "locale": "en-US"
13621
+ }
13622
+ ],
13623
+ "locales": {
13624
+ "zh-CN": {
13625
+ "title": "Page",
13626
+ "description": "业务页面组合区块,封装间距、边框与标题层级,少写或不写页面 CSS。",
13627
+ "sections": [
13628
+ {
13629
+ "id": "overview",
13630
+ "title": "",
13631
+ "body": "# Page\n\n业务页面组合组件。与 `MLayout` 搭配使用,把列表页筛选区、工具栏、表单分组、KPI 卡片等常见区块的间距和边框内聚到组件内,**优先用 Page 组件拼装页面,而不是每页手写 scoped CSS**。"
13632
+ },
13633
+ {
13634
+ "id": "引入",
13635
+ "title": "引入",
13636
+ "body": "```ts\nimport {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'\n```"
13637
+ },
13638
+ {
13639
+ "id": "列表页组合",
13640
+ "title": "列表页组合",
13641
+ "body": "`MPageContent` 负责内容区 **padding**(默认 `--m-space-6`)与子区块 **gap**(默认 `--m-space-4`),文档预览中外框仅模拟 `MLayoutContent` 边界。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"筛选\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"搜索\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">查询</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"用户管理\">\n <template #actions>\n <MButton severity=\"primary\">新建</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>\n```"
13642
+ },
13643
+ {
13644
+ "id": "表单页组合",
13645
+ "title": "表单页组合",
13646
+ "body": "`MPageContent width=\"narrow\"` 限制表单宽度;`MPageSection variant=\"form\"` 提供表单表面;`variant=\"actions\"` 用于底栏操作。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MForm,\n MFormItem,\n MInput,\n MPageContent,\n MPageHeader,\n MPageSection,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst name = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent width=\"narrow\">\n <MPageHeader title=\"新建用户\" description=\"填写基本信息。\" />\n <MPageSection variant=\"form\">\n <MForm @submit.prevent>\n <MFormItem label=\"姓名\" name=\"name\">\n <MInput v-model=\"name\" fluid />\n </MFormItem>\n <MPageSection variant=\"actions\">\n <MSpace>\n <MButton native-type=\"submit\" severity=\"primary\">保存</MButton>\n <MButton severity=\"secondary\">取消</MButton>\n </MSpace>\n </MPageSection>\n </MForm>\n </MPageSection>\n </MPageContent>\n </div>\n</template>\n```"
13647
+ },
13648
+ {
13649
+ "id": "仪表盘-kpi",
13650
+ "title": "仪表盘 KPI",
13651
+ "body": "```vue preview\n<script setup lang=\"ts\">\nimport { MPageContent, MPageStat } from 'morya-ui'\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent density=\"compact\">\n <MPageStat label=\"总用户\" value=\"12,480\" trend=\"+8.2%\" icon=\"users\" />\n </MPageContent>\n </div>\n</template>\n```"
13652
+ },
13653
+ {
13654
+ "id": "组合规则",
13655
+ "title": "组合规则",
13656
+ "body": "| 场景 | 推荐组件 | 避免 |\n| --- | --- | --- |\n| 内容区垂直堆叠 | `MPageContent` | 手写 `gap` / `padding` |\n| 筛选区 | `MPageFilters` + `MSpace` / `MFlex` | 额外包一层 `MCard` |\n| 标题 + 主操作 | `MPageToolbar` | 裸 `div` + `justify-content` |\n| 页面说明 | `MPageHeader` | 无层级的裸 `h1` |\n| 表单主体 | `MPageSection variant=\"form\"` | 重复边框的 `MCard` 嵌套 |\n| 列表表格 | 直接放 `MTable` | `MCard` 再包 `MTable bordered` |\n| KPI | `MPageStat` | 每页自定义 stat CSS |\n| 图表占位 | `MPagePlaceholder` | dashed border 手写样式 |\n\n完整黄金样例见 `docs/golden-pages/`(MCP:`get_golden_page`)。**局部修改**时用 MCP `get_page_snippet`(如 `filters`、`toolbar`、`form-actions`)。"
13657
+ },
13658
+ {
13659
+ "id": "api",
13660
+ "title": "API",
13661
+ "body": "### MPageContent\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `density` | `'default' \\| 'compact' \\| 'spacious'` | `'default'` | 子区块垂直间距。 |\n| `width` | `'full' \\| 'narrow'` | `'full'` | `narrow` 约 42rem,适合表单页。 |\n\n### MPageFilters\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `ariaLabel` | `string` | — | 筛选区无障碍名称。 |\n\n### MPageToolbar\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `title` | `string` | — | 页面标题。 |\n| `headingLevel` | `1–6` | `1` | 标题层级。 |\n\n| Slot | 说明 |\n| --- | --- |\n| `default` | 无 `title` 时的左侧内容。 |\n| `actions` | 右侧操作区。 |\n\n### MPageHeader\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `title` | `string` | — | 页面标题。 |\n| `description` | `string` | — | 副标题/说明。 |\n| `headingLevel` | `1–6` | `1` | 标题层级。 |\n\n| Slot | 说明 |\n| --- | --- |\n| `actions` | 右侧操作。 |\n\n### MPageSection\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `variant` | `'default' \\| 'muted' \\| 'form' \\| 'actions'` | `'default'` | 视觉变体。 |\n| `title` | `string` | — | 可选分组标题。 |\n\n### MPageStat\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `label` | `string` | — | 指标名称。 |\n| `value` | `string \\| number` | — | 主数值。 |\n| `trend` | `string` | — | 变化量或说明。 |\n| `trendSeverity` | `'primary' \\| 'success' \\| 'warn' \\| 'danger' \\| 'secondary'` | `'primary'` | 趋势色。 |\n| `icon` | `string` | — | 右侧图标名。 |\n\n### MPagePlaceholder\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `description` | `string` | — | 占位说明。 |\n| `ariaLabel` | `string` | `'Placeholder'` | 无障碍标签。 |\n| `minHeight` | `number \\| string` | `'12rem'` | 最小高度。 |"
13662
+ }
13663
+ ],
13664
+ "markdown": "---\ntitle: Page\ncategory: 06 / LAYOUT\ndescription: 业务页面组合区块,封装间距、边框与标题层级,少写或不写页面 CSS。\n---\n\n# Page\n\n业务页面组合组件。与 `MLayout` 搭配使用,把列表页筛选区、工具栏、表单分组、KPI 卡片等常见区块的间距和边框内聚到组件内,**优先用 Page 组件拼装页面,而不是每页手写 scoped CSS**。\n\n## 引入\n\n```ts\nimport {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'\n```\n\n## 列表页组合\n\n`MPageContent` 负责内容区 **padding**(默认 `--m-space-6`)与子区块 **gap**(默认 `--m-space-4`),文档预览中外框仅模拟 `MLayoutContent` 边界。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"筛选\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"搜索\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">查询</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"用户管理\">\n <template #actions>\n <MButton severity=\"primary\">新建</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>\n```\n\n## 表单页组合\n\n`MPageContent width=\"narrow\"` 限制表单宽度;`MPageSection variant=\"form\"` 提供表单表面;`variant=\"actions\"` 用于底栏操作。\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MForm,\n MFormItem,\n MInput,\n MPageContent,\n MPageHeader,\n MPageSection,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst name = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent width=\"narrow\">\n <MPageHeader title=\"新建用户\" description=\"填写基本信息。\" />\n <MPageSection variant=\"form\">\n <MForm @submit.prevent>\n <MFormItem label=\"姓名\" name=\"name\">\n <MInput v-model=\"name\" fluid />\n </MFormItem>\n <MPageSection variant=\"actions\">\n <MSpace>\n <MButton native-type=\"submit\" severity=\"primary\">保存</MButton>\n <MButton severity=\"secondary\">取消</MButton>\n </MSpace>\n </MPageSection>\n </MForm>\n </MPageSection>\n </MPageContent>\n </div>\n</template>\n```\n\n## 仪表盘 KPI\n\n```vue preview\n<script setup lang=\"ts\">\nimport { MPageContent, MPageStat } from 'morya-ui'\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent density=\"compact\">\n <MPageStat label=\"总用户\" value=\"12,480\" trend=\"+8.2%\" icon=\"users\" />\n </MPageContent>\n </div>\n</template>\n```\n\n## 组合规则\n\n| 场景 | 推荐组件 | 避免 |\n| --- | --- | --- |\n| 内容区垂直堆叠 | `MPageContent` | 手写 `gap` / `padding` |\n| 筛选区 | `MPageFilters` + `MSpace` / `MFlex` | 额外包一层 `MCard` |\n| 标题 + 主操作 | `MPageToolbar` | 裸 `div` + `justify-content` |\n| 页面说明 | `MPageHeader` | 无层级的裸 `h1` |\n| 表单主体 | `MPageSection variant=\"form\"` | 重复边框的 `MCard` 嵌套 |\n| 列表表格 | 直接放 `MTable` | `MCard` 再包 `MTable bordered` |\n| KPI | `MPageStat` | 每页自定义 stat CSS |\n| 图表占位 | `MPagePlaceholder` | dashed border 手写样式 |\n\n完整黄金样例见 `docs/golden-pages/`(MCP:`get_golden_page`)。**局部修改**时用 MCP `get_page_snippet`(如 `filters`、`toolbar`、`form-actions`)。\n\n## API\n\n### MPageContent\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `density` | `'default' \\| 'compact' \\| 'spacious'` | `'default'` | 子区块垂直间距。 |\n| `width` | `'full' \\| 'narrow'` | `'full'` | `narrow` 约 42rem,适合表单页。 |\n\n### MPageFilters\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `ariaLabel` | `string` | — | 筛选区无障碍名称。 |\n\n### MPageToolbar\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `title` | `string` | — | 页面标题。 |\n| `headingLevel` | `1–6` | `1` | 标题层级。 |\n\n| Slot | 说明 |\n| --- | --- |\n| `default` | 无 `title` 时的左侧内容。 |\n| `actions` | 右侧操作区。 |\n\n### MPageHeader\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `title` | `string` | — | 页面标题。 |\n| `description` | `string` | — | 副标题/说明。 |\n| `headingLevel` | `1–6` | `1` | 标题层级。 |\n\n| Slot | 说明 |\n| --- | --- |\n| `actions` | 右侧操作。 |\n\n### MPageSection\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `variant` | `'default' \\| 'muted' \\| 'form' \\| 'actions'` | `'default'` | 视觉变体。 |\n| `title` | `string` | — | 可选分组标题。 |\n\n### MPageStat\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `label` | `string` | — | 指标名称。 |\n| `value` | `string \\| number` | — | 主数值。 |\n| `trend` | `string` | — | 变化量或说明。 |\n| `trendSeverity` | `'primary' \\| 'success' \\| 'warn' \\| 'danger' \\| 'secondary'` | `'primary'` | 趋势色。 |\n| `icon` | `string` | — | 右侧图标名。 |\n\n### MPagePlaceholder\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `description` | `string` | — | 占位说明。 |\n| `ariaLabel` | `string` | `'Placeholder'` | 无障碍标签。 |\n| `minHeight` | `number \\| string` | `'12rem'` | 最小高度。 |\n"
13665
+ },
13666
+ "en-US": {
13667
+ "title": "Page",
13668
+ "description": "Composable page sections for spacing, borders, and headings with little or no custom CSS.",
13669
+ "sections": [
13670
+ {
13671
+ "id": "overview",
13672
+ "title": "",
13673
+ "body": "# Page\n\nPage composition components. Use them with `MLayout` to encode filter bars, toolbars, form surfaces, KPI cards, and other admin-page blocks **without rewriting scoped CSS on every page**."
13674
+ },
13675
+ {
13676
+ "id": "import",
13677
+ "title": "Import",
13678
+ "body": "```ts\nimport {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'\n```"
13679
+ },
13680
+ {
13681
+ "id": "list-page-stack",
13682
+ "title": "List page stack",
13683
+ "body": "`MPageContent` owns content **padding** (default `--m-space-6`) and section **gap** (default `--m-space-4`). The bordered frame in previews simulates `MLayoutContent` only.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"Filters\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"Search\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">Search</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"Users\">\n <template #actions>\n <MButton severity=\"primary\">Create</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>\n```"
13684
+ },
13685
+ {
13686
+ "id": "form-page-stack",
13687
+ "title": "Form page stack",
13688
+ "body": "Use `MPageContent width=\"narrow\"`, `MPageSection variant=\"form\"`, and `variant=\"actions\"` for the footer."
13689
+ },
13690
+ {
13691
+ "id": "composition-rules",
13692
+ "title": "Composition rules",
13693
+ "body": "| Scenario | Use | Avoid |\n| --- | --- | --- |\n| Vertical page stack | `MPageContent` | Hand-written `gap` / `padding` |\n| Filters | `MPageFilters` | Extra bordered `MCard` wrapper |\n| Title + actions | `MPageToolbar` | Raw flex divs |\n| Form surface | `MPageSection variant=\"form\"` | Nested bordered cards |\n| Data table | `MTable` directly | `MCard` around bordered table |\n| KPI metric | `MPageStat` | Custom stat CSS per page |\n\nGolden references: `docs/golden-pages/` (MCP: `get_golden_page`)."
13694
+ }
13695
+ ],
13696
+ "markdown": "---\ntitle: Page\ncategory: 06 / LAYOUT\ndescription: Composable page sections for spacing, borders, and headings with little or no custom CSS.\n---\n\n# Page\n\nPage composition components. Use them with `MLayout` to encode filter bars, toolbars, form surfaces, KPI cards, and other admin-page blocks **without rewriting scoped CSS on every page**.\n\n## Import\n\n```ts\nimport {\n MPageContent,\n MPageFilters,\n MPageHeader,\n MPagePlaceholder,\n MPageSection,\n MPageStat,\n MPageToolbar,\n} from 'morya-ui'\n```\n\n## List page stack\n\n`MPageContent` owns content **padding** (default `--m-space-6`) and section **gap** (default `--m-space-4`). The bordered frame in previews simulates `MLayoutContent` only.\n\n```vue preview\n<script setup lang=\"ts\">\nimport {\n MButton,\n MInput,\n MPageContent,\n MPageFilters,\n MPageToolbar,\n MSpace,\n} from 'morya-ui'\nimport { ref } from 'vue'\n\nconst keyword = ref('')\n</script>\n\n<template>\n <div\n class=\"doc-demo-frame\"\n style=\"border:1px solid var(--m-color-border);border-radius:var(--m-radius-md);overflow:hidden;background:var(--m-color-surface)\"\n >\n <MPageContent>\n <MPageFilters aria-label=\"Filters\">\n <MSpace wrap>\n <MInput v-model=\"keyword\" placeholder=\"Search\" clearable style=\"width:12rem\" />\n <MButton severity=\"primary\">Search</MButton>\n </MSpace>\n </MPageFilters>\n <MPageToolbar title=\"Users\">\n <template #actions>\n <MButton severity=\"primary\">Create</MButton>\n </template>\n </MPageToolbar>\n </MPageContent>\n </div>\n</template>\n```\n\n## Form page stack\n\nUse `MPageContent width=\"narrow\"`, `MPageSection variant=\"form\"`, and `variant=\"actions\"` for the footer.\n\n## Composition rules\n\n| Scenario | Use | Avoid |\n| --- | --- | --- |\n| Vertical page stack | `MPageContent` | Hand-written `gap` / `padding` |\n| Filters | `MPageFilters` | Extra bordered `MCard` wrapper |\n| Title + actions | `MPageToolbar` | Raw flex divs |\n| Form surface | `MPageSection variant=\"form\"` | Nested bordered cards |\n| Data table | `MTable` directly | `MCard` around bordered table |\n| KPI metric | `MPageStat` | Custom stat CSS per page |\n\nGolden references: `docs/golden-pages/` (MCP: `get_golden_page`).\n"
13697
+ }
13698
+ }
13699
+ },
13556
13700
  {
13557
13701
  "id": "Pagination",
13558
13702
  "name": "Pagination",
@@ -24417,7 +24561,7 @@
24417
24561
  "zh-CN": {
24418
24562
  "title": "MCP",
24419
24563
  "description": "可选的 MCP 服务,供支持 Model Context Protocol 的 AI 客户端检索本库文档。",
24420
- "markdown": "---\ntitle: MCP\norder: 10\ndescription: 可选的 MCP 服务,供支持 Model Context Protocol 的 AI 客户端检索本库文档。\n---\n\n# MCP\n\n[`@morya-ui/mcp`](https://www.npmjs.com/package/@morya-ui/mcp) 是可选的 [Model Context Protocol](https://modelcontextprotocol.io/)(stdio)服务。它把本站组件文档、示例与指南做成可检索工具,方便 **支持 MCP 的 AI 客户端** 按真实 API 生成代码。\n\n日常使用组件库 **不需要** 安装或配置 MCP。应用里仍然只依赖:\n\n```bash\npnpm add morya-ui\n```\n\n```ts\nimport 'morya-ui/styles.css'\n```\n\n## 接入方式\n\nMCP 客户端通过 stdio 启动本包即可:\n\n```bash\nnpx -y @morya-ui/mcp\n```\n\n通用写法:\n\n```json\n{\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n}\n```\n\n字段名因客户端而异,只要支持 MCP stdio 即可接入。\n\n### 常见客户端配置示例\n\n以下为常见产品的配置片段,键名可能随版本变化,以各产品官方文档为准。\n\n**Cursor**(`.cursor/mcp.json` 或用户级 MCP 配置):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Claude Desktop / Claude Code**(`claude_desktop_config.json` 等):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Windsurf**(MCP 设置中的 servers 配置):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Cline**(VS Code 扩展设置中的 MCP servers):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Zed**(`settings.json` → `context_servers`):\n\n```json\n{\n \"context_servers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Continue**(`config.json` / YAML 中的 MCP servers,字段名以当前版本为准):\n\n```json\n{\n \"mcpServers\": [\n {\n \"name\": \"morya-ui\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n ]\n}\n```\n\n## 可用工具\n\n### 基础 — 查组件文档\n\n| 工具 | 作用 |\n| --- | --- |\n| `list` | 列出组件 / 指南 / 示例 / 分类 / 页面模式 |\n| `search` | 搜索文档、示例、页面模式与组件选型指南 |\n| `get_component` | 读取组件说明与 API |\n| `get_example` | 获取源码示例 |\n| `get_guide` | 读取指南 |\n| `get_setup` | 安装与初始化说明 |\n| `validate_usage` | 对照文档粗检 props / events 用法 |\n| `version` | 版本与目录状态 |\n\n### 高级 — 页面组合(可选)\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_patterns` | 列出可复用的页面组合模式 |\n| `get_pattern` | 读取模式的结构、布局与交互规则 |\n| `recommend_page` | 根据页面意图推荐模式;可附带 starter 脚手架 |\n| `get_design_rules` | 设计令牌与组合规则 |\n| `recommend_component` | 列出、阅读或推荐组件选型指南 |\n\n多数工具支持 `mode`:`zh`(默认)或 `en`。\n\n### 推荐工作流\n\n**查单个组件:** `search` / `get_component` → `get_example` → `validate_usage`\n\n**规划整页:** `recommend_page` → `get_pattern` → `get_component` / `get_example` → `get_design_rules`;组件取舍不明时调用 `recommend_component`\n\n需要 starter 代码时,给 `recommend_page` 传 `includeScaffold: true`:\n\n```json\n{\n \"intent\": \"油井管理列表\",\n \"pageType\": \"list\",\n \"features\": [\"筛选\", \"新增\", \"分页\"],\n \"includeScaffold\": true\n}\n```\n\n`recommend_component` 用法:\n\n- 不传 `query` 和 `decision` → 列出全部选型指南\n- 只传 `decision`(如 `overlay-choice`)→ 阅读某一指南\n- 传 `query` → 根据问题推荐组件\n\n## 对话示例\n\n接入后,可直接让助手调用本服务,例如:\n\n> 用 morya-ui 的 MCP 查一下 Dialog 的 props,并给一个带确认 / 取消按钮的示例。\n\n> 搜索和「日期」相关的组件,选一个适合表单的,按文档写出最小用法。\n\n> 根据 MCP 里 Button 的文档,写一个 `severity=\"danger\"` 的删除按钮,并校验 props 是否合法。\n\n助手应先调用工具,再基于返回内容生成类似:\n\n```vue\n<script setup lang=\"ts\">\nimport { MButton } from 'morya-ui'\n</script>\n\n<template>\n <MButton label=\"删除\" severity=\"danger\" />\n</template>\n```\n\n## 与文档站的关系\n\n目录与本站同源(组件 `docs/` + 指南 Markdown)。官网文档更新后,维护者重新发布 `@morya-ui/mcp`,客户端通过 `npx -y` 即可拿到新版本。\n\n更多实现细节见仓库内 [packages/ui-mcp/README.md](https://github.com/morya-space/morya-ui/tree/main/packages/ui-mcp)。\n\n## 下一步\n\n- [快速上手](/docs/quick-start):在应用中安装并使用组件\n- [组件](/components):浏览全部组件与交互示例\n",
24564
+ "markdown": "---\ntitle: MCP\norder: 10\ndescription: 可选的 MCP 服务,供支持 Model Context Protocol 的 AI 客户端检索本库文档。\n---\n\n# MCP\n\n[`@morya-ui/mcp`](https://www.npmjs.com/package/@morya-ui/mcp) 是可选的 [Model Context Protocol](https://modelcontextprotocol.io/)(stdio)服务。它把本站组件文档、示例与指南做成可检索工具,方便 **支持 MCP 的 AI 客户端** 按真实 API 生成代码。\n\n日常使用组件库 **不需要** 安装或配置 MCP。应用里仍然只依赖:\n\n```bash\npnpm add morya-ui\n```\n\n```ts\nimport 'morya-ui/styles.css'\n```\n\n## 接入方式\n\nMCP 客户端通过 stdio 启动本包即可:\n\n```bash\nnpx -y @morya-ui/mcp\n```\n\n通用写法:\n\n```json\n{\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n}\n```\n\n字段名因客户端而异,只要支持 MCP stdio 即可接入。\n\n### 常见客户端配置示例\n\n以下为常见产品的配置片段,键名可能随版本变化,以各产品官方文档为准。\n\n**Cursor**(`.cursor/mcp.json` 或用户级 MCP 配置):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Claude Desktop / Claude Code**(`claude_desktop_config.json` 等):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Windsurf**(MCP 设置中的 servers 配置):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Cline**(VS Code 扩展设置中的 MCP servers):\n\n```json\n{\n \"mcpServers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Zed**(`settings.json` → `context_servers`):\n\n```json\n{\n \"context_servers\": {\n \"morya-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n }\n}\n```\n\n**Continue**(`config.json` / YAML 中的 MCP servers,字段名以当前版本为准):\n\n```json\n{\n \"mcpServers\": [\n {\n \"name\": \"morya-ui\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@morya-ui/mcp\"]\n }\n ]\n}\n```\n\n## 可用工具\n\n### 基础 — 查组件文档\n\n| 工具 | 作用 |\n| --- | --- |\n| `list` | 列出组件 / 指南 / 示例 / 分类 / 页面模式 |\n| `search` | 搜索文档、示例、页面模式与组件选型指南 |\n| `get_component` | 读取组件说明与 API |\n| `get_example` | 获取源码示例 |\n| `get_guide` | 读取指南 |\n| `get_setup` | 安装与初始化说明 |\n| `validate_usage` | 对照文档粗检 props / events 用法 |\n| `version` | 版本与目录状态 |\n\n### 高级 — 页面组合(可选)\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_patterns` | 列出可复用的页面组合模式 |\n| `get_pattern` | 读取模式的结构、布局与交互规则 |\n| `recommend_page` | 根据页面意图推荐模式;可附带 starter 脚手架 |\n| `get_design_rules` | 设计令牌与 MPage* 组合配方 |\n| `recommend_component` | 列出、阅读或推荐组件选型指南 |\n| `list_golden_pages` | 列出黄金样例页面 |\n| `get_golden_page` | 读取黄金样例 Vue 源码 |\n| `list_page_snippets` | 列出可复用的页面区块 snippet |\n| `get_page_snippet` | 读取局部区块 snippet(筛选区、工具栏等) |\n| `validate_page` | 校验页面组合、间距与双边框问题 |\n\n多数工具支持 `mode`:`zh`(默认)或 `en`。\n\n### 推荐工作流\n\n**查单个组件:** `search` / `get_component` → `get_example` → `validate_usage`\n\n**规划整页:** `recommend_page` → `get_golden_page` → `get_pattern` → `get_design_rules` → `get_component` / `get_example` → `validate_page`\n\n**改局部区块:** `get_page_snippet(section)` → `get_component` / `get_example` → `validate_usage` → `validate_page`\n\n需要 starter 代码时,给 `recommend_page` 传 `includeScaffold: true`:\n\n```json\n{\n \"intent\": \"油井管理列表\",\n \"pageType\": \"list\",\n \"features\": [\"筛选\", \"新增\", \"分页\"],\n \"includeScaffold\": true\n}\n```\n\n`recommend_component` 用法:\n\n- 不传 `query` 和 `decision` → 列出全部选型指南\n- 只传 `decision`(如 `overlay-choice`)→ 阅读某一指南\n- 传 `query` → 根据问题推荐组件\n\n## 对话示例\n\n接入后,可直接让助手调用本服务,例如:\n\n> 用 morya-ui 的 MCP 查一下 Dialog 的 props,并给一个带确认 / 取消按钮的示例。\n\n> 搜索和「日期」相关的组件,选一个适合表单的,按文档写出最小用法。\n\n> 根据 MCP 里 Button 的文档,写一个 `severity=\"danger\"` 的删除按钮,并校验 props 是否合法。\n\n助手应先调用工具,再基于返回内容生成类似:\n\n```vue\n<script setup lang=\"ts\">\nimport { MButton } from 'morya-ui'\n</script>\n\n<template>\n <MButton label=\"删除\" severity=\"danger\" />\n</template>\n```\n\n## 与文档站的关系\n\n目录与本站同源(组件 `docs/` + 指南 Markdown)。官网文档更新后,维护者重新发布 `@morya-ui/mcp`,客户端通过 `npx -y` 即可拿到新版本。\n\n更多实现细节见仓库内 [packages/ui-mcp/README.md](https://github.com/morya-space/morya-ui/tree/main/packages/ui-mcp)。\n\n## 下一步\n\n- [快速上手](/docs/quick-start):在应用中安装并使用组件\n- [组件](/components):浏览全部组件与交互示例\n",
24421
24565
  "sections": [
24422
24566
  {
24423
24567
  "title": "",
@@ -24432,7 +24576,7 @@
24432
24576
  {
24433
24577
  "title": "可用工具",
24434
24578
  "id": "可用工具",
24435
- "body": "### 基础 — 查组件文档\n\n| 工具 | 作用 |\n| --- | --- |\n| `list` | 列出组件 / 指南 / 示例 / 分类 / 页面模式 |\n| `search` | 搜索文档、示例、页面模式与组件选型指南 |\n| `get_component` | 读取组件说明与 API |\n| `get_example` | 获取源码示例 |\n| `get_guide` | 读取指南 |\n| `get_setup` | 安装与初始化说明 |\n| `validate_usage` | 对照文档粗检 props / events 用法 |\n| `version` | 版本与目录状态 |\n\n### 高级 — 页面组合(可选)\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_patterns` | 列出可复用的页面组合模式 |\n| `get_pattern` | 读取模式的结构、布局与交互规则 |\n| `recommend_page` | 根据页面意图推荐模式;可附带 starter 脚手架 |\n| `get_design_rules` | 设计令牌与组合规则 |\n| `recommend_component` | 列出、阅读或推荐组件选型指南 |\n\n多数工具支持 `mode`:`zh`(默认)或 `en`。\n\n### 推荐工作流\n\n**查单个组件:** `search` / `get_component` → `get_example` → `validate_usage`\n\n**规划整页:** `recommend_page` → `get_pattern` → `get_component` / `get_example` → `get_design_rules`;组件取舍不明时调用 `recommend_component`\n\n需要 starter 代码时,给 `recommend_page` 传 `includeScaffold: true`:\n\n```json\n{\n \"intent\": \"油井管理列表\",\n \"pageType\": \"list\",\n \"features\": [\"筛选\", \"新增\", \"分页\"],\n \"includeScaffold\": true\n}\n```\n\n`recommend_component` 用法:\n\n- 不传 `query` 和 `decision` → 列出全部选型指南\n- 只传 `decision`(如 `overlay-choice`)→ 阅读某一指南\n- 传 `query` → 根据问题推荐组件"
24579
+ "body": "### 基础 — 查组件文档\n\n| 工具 | 作用 |\n| --- | --- |\n| `list` | 列出组件 / 指南 / 示例 / 分类 / 页面模式 |\n| `search` | 搜索文档、示例、页面模式与组件选型指南 |\n| `get_component` | 读取组件说明与 API |\n| `get_example` | 获取源码示例 |\n| `get_guide` | 读取指南 |\n| `get_setup` | 安装与初始化说明 |\n| `validate_usage` | 对照文档粗检 props / events 用法 |\n| `version` | 版本与目录状态 |\n\n### 高级 — 页面组合(可选)\n\n| 工具 | 作用 |\n| --- | --- |\n| `list_patterns` | 列出可复用的页面组合模式 |\n| `get_pattern` | 读取模式的结构、布局与交互规则 |\n| `recommend_page` | 根据页面意图推荐模式;可附带 starter 脚手架 |\n| `get_design_rules` | 设计令牌与 MPage* 组合配方 |\n| `recommend_component` | 列出、阅读或推荐组件选型指南 |\n| `list_golden_pages` | 列出黄金样例页面 |\n| `get_golden_page` | 读取黄金样例 Vue 源码 |\n| `list_page_snippets` | 列出可复用的页面区块 snippet |\n| `get_page_snippet` | 读取局部区块 snippet(筛选区、工具栏等) |\n| `validate_page` | 校验页面组合、间距与双边框问题 |\n\n多数工具支持 `mode`:`zh`(默认)或 `en`。\n\n### 推荐工作流\n\n**查单个组件:** `search` / `get_component` → `get_example` → `validate_usage`\n\n**规划整页:** `recommend_page` → `get_golden_page` → `get_pattern` → `get_design_rules` → `get_component` / `get_example` → `validate_page`\n\n**改局部区块:** `get_page_snippet(section)` → `get_component` / `get_example` → `validate_usage` → `validate_page`\n\n需要 starter 代码时,给 `recommend_page` 传 `includeScaffold: true`:\n\n```json\n{\n \"intent\": \"油井管理列表\",\n \"pageType\": \"list\",\n \"features\": [\"筛选\", \"新增\", \"分页\"],\n \"includeScaffold\": true\n}\n```\n\n`recommend_component` 用法:\n\n- 不传 `query` 和 `decision` → 列出全部选型指南\n- 只传 `decision`(如 `overlay-choice`)→ 阅读某一指南\n- 传 `query` → 根据问题推荐组件"
24436
24580
  },
24437
24581
  {
24438
24582
  "title": "对话示例",
@@ -1,6 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-09-11T07:52:45.232Z",
3
- "catalogGeneratedAt": "2026-09-11T07:52:42.355Z",
2
+ "generatedAt": "2026-09-14T03:19:40.333Z",
3
+ "catalogGeneratedAt": "2026-09-14T03:19:36.680Z",
4
4
  "components": [
5
5
  {
6
6
  "id": "Accordion",
@@ -1762,6 +1762,31 @@
1762
1762
  "missing": []
1763
1763
  }
1764
1764
  },
1765
+ {
1766
+ "id": "Page",
1767
+ "exportName": "MPageContent",
1768
+ "examples": 6,
1769
+ "props": {
1770
+ "total": 0,
1771
+ "covered": 0,
1772
+ "missing": []
1773
+ },
1774
+ "events": {
1775
+ "total": 0,
1776
+ "covered": 0,
1777
+ "missing": []
1778
+ },
1779
+ "slots": {
1780
+ "total": 0,
1781
+ "covered": 0,
1782
+ "missing": []
1783
+ },
1784
+ "methods": {
1785
+ "total": 0,
1786
+ "covered": 0,
1787
+ "missing": []
1788
+ }
1789
+ },
1765
1790
  {
1766
1791
  "id": "Pagination",
1767
1792
  "exportName": "MPagination",
@@ -3075,8 +3100,8 @@
3075
3100
  }
3076
3101
  ],
3077
3102
  "summary": {
3078
- "components": 88,
3103
+ "components": 89,
3079
3104
  "componentsWithMissingCoverage": 84,
3080
- "totalExamples": 751
3105
+ "totalExamples": 757
3081
3106
  }
3082
3107
  }