@morya-ui/mcp 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -151
- package/bin/morya-ui-mcp.js +2 -2
- package/data/catalog.json +39 -28
- package/data/example-coverage.json +6 -4
- package/data/golden-pages/dashboard-page.vue +138 -138
- package/data/golden-pages/detail-page.vue +178 -178
- package/data/golden-pages/empty-state.vue +56 -56
- package/data/golden-pages/form-in-dialog.vue +166 -166
- package/data/golden-pages/form-page.vue +107 -107
- package/data/golden-pages/landing-page.vue +358 -358
- package/data/golden-pages/list-page-dense.vue +166 -0
- package/data/golden-pages/list-page-rail.vue +270 -0
- package/data/golden-pages/list-page.vue +220 -216
- package/data/golden-pages/login-page.vue +231 -231
- package/data/golden-pages/result-page.vue +84 -84
- package/data/golden-pages/settings-page.vue +163 -163
- package/data/golden-pages/wizard-form.vue +182 -182
- package/dist/__tests__/tools.test.js +76 -1
- package/dist/decisions.js +42 -1
- package/dist/golden-pages.d.ts +13 -0
- package/dist/golden-pages.js +55 -2
- package/dist/index.js +9 -3
- package/dist/page-snippets.js +254 -225
- package/dist/patterns.d.ts +2 -2
- package/dist/patterns.js +20 -9
- package/dist/style-presets.d.ts +52 -0
- package/dist/style-presets.js +322 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.js +313 -204
- package/package.json +13 -11
- package/LICENSE +0 -21
package/dist/patterns.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export declare const designRules: {
|
|
|
51
51
|
readonly motion: "--m-motion-fast/normal/enter/exit/distance/ease; intensity via useMotion (data-m-motion); enter/exit via motion.transitions / transition prop (fade, scale-fade, zoom, slide-*, dialog, drawer, …)";
|
|
52
52
|
};
|
|
53
53
|
readonly composition: {
|
|
54
|
-
readonly workflow: readonly ["For full pages: recommend_page → get_golden_page → get_design_rules.", "For local edits: get_page_snippet(section) for filters/toolbar/form-actions/KPI/scrollable-panel blocks.", "Use MLayout fillViewport as the app shell; put MPageContent inside MLayoutContent.", "Prefer MPage* components over scoped CSS for filters, toolbars, headers, form surfaces, and KPI cards.", "Use MSpace or MFlex for control groups inside MPageFilters; use MPageToolbar for title + primary action."];
|
|
54
|
+
readonly workflow: readonly ["Resolve style first: reference → named preset → prompt cues → offer quiet/soft/dense/rail/studio/ink (see list_style_presets).", "For full pages: recommend_page({ style? }) → get_golden_page → apply styleDirection → get_design_rules.", "Craft: visual-craft Ops polish or Express design plan; optional frontend-design / impeccable only after contract (companions never replace M*).", "For local edits: get_page_snippet(section) for filters/toolbar/form-actions/KPI/scrollable-panel blocks.", "Use MLayout fillViewport as the app shell; put MPageContent inside MLayoutContent.", "Prefer MPage* components over scoped CSS for filters, toolbars, headers, form surfaces, and KPI cards.", "Use MSpace or MFlex for control groups inside MPageFilters; use MPageToolbar for title + primary action.", "Golden pages lock structure/API, not the only aesthetic — do not emit identical quiet chrome every time."];
|
|
55
55
|
readonly snippets: readonly ["list-filters", "list-toolbar", "list-table", "list-row-actions", "form-header", "form-body", "form-actions", "dashboard-kpi-grid", "dashboard-chart-card", "layout-app-shell", "scrollable-panel"];
|
|
56
56
|
readonly pageStack: {
|
|
57
57
|
readonly list: readonly ["MLayout", "MLayoutHeader", "MLayoutContent", "MPageContent", "MPageFilters", "MPageToolbar", "MTable"];
|
|
@@ -68,7 +68,7 @@ export declare const designRules: {
|
|
|
68
68
|
readonly do: readonly ["MLayoutHeader bordered (default)", "MPageFilters for filter regions", "MPageSection variant=\"form\" for forms", "MTable bordered when table needs grid lines"];
|
|
69
69
|
readonly avoid: readonly ["MCard wrapping MTable that is already bordered", "Nested MCard with bordered=true on both levels", "Hand-written section borders when MPageFilters or MPageSection applies", "Double borders on filter + table wrapper"];
|
|
70
70
|
};
|
|
71
|
-
readonly goldenPages: readonly ["list-page", "form-page", "dashboard-page", "login-page", "landing-page", "empty-state", "detail-page", "form-in-dialog", "result-page", "settings-page", "wizard-form"];
|
|
71
|
+
readonly goldenPages: readonly ["list-page", "list-page-dense", "list-page-rail", "form-page", "dashboard-page", "login-page", "landing-page", "empty-state", "detail-page", "form-in-dialog", "result-page", "settings-page", "wizard-form"];
|
|
72
72
|
};
|
|
73
73
|
readonly actions: {
|
|
74
74
|
readonly primary: {
|
package/dist/patterns.js
CHANGED
|
@@ -51,17 +51,17 @@ export const pagePatterns = [
|
|
|
51
51
|
' └── MLayout',
|
|
52
52
|
' ├── MLayoutHeader → MBreadcrumb',
|
|
53
53
|
' └── MLayoutContent',
|
|
54
|
-
' └── MPageContent',
|
|
54
|
+
' └── MPageContent (+ fill when full-viewport main list)',
|
|
55
55
|
' ├── MPageFilters (MSpace + MInput + MSelect + MButton)',
|
|
56
56
|
' ├── MPageToolbar (title + primary action)',
|
|
57
|
-
' ├── MTable (paginator, #cell-* slots, #empty)',
|
|
57
|
+
' ├── MTable (+ fill when height rule applies; paginator, #cell-* slots, #empty)',
|
|
58
58
|
' └── MDialog (create/edit MForm, optional)',
|
|
59
59
|
],
|
|
60
60
|
layout: {
|
|
61
61
|
page: 'MLayout fillViewport + has-sider;MLayoutContent 内用 MPageContent 堆叠区块',
|
|
62
62
|
header: 'MLayoutHeader 放面包屑;标题与主操作用 MPageToolbar',
|
|
63
63
|
filters: 'MPageFilters 包裹筛选控件;内部 MSpace wrap 或 MFlex',
|
|
64
|
-
data: 'MTable 直接放在 MPageContent 内,不要额外 MCard
|
|
64
|
+
data: 'MTable 直接放在 MPageContent 内,不要额外 MCard;全视口主列表且表格是主任务时再 fill(表体滚、分页贴底),嵌入/短页跳过;paginator + #empty',
|
|
65
65
|
form: '列表新建/编辑默认 MDialog + MForm,不要默认拆独立表单路由',
|
|
66
66
|
},
|
|
67
67
|
styleRules: [
|
|
@@ -538,38 +538,44 @@ export const pageStandards = [
|
|
|
538
538
|
titleEn: 'Page section components',
|
|
539
539
|
recommend: [
|
|
540
540
|
'筛选区用 MPageFilters,标题+操作用 MPageToolbar,表单区用 MPageSection',
|
|
541
|
-
'列表页 MTable 直接放在 MPageContent
|
|
541
|
+
'列表页 MTable 直接放在 MPageContent 内,不要额外 MCard',
|
|
542
|
+
'全视口后台主列表(表格是页面主任务)才用 MPageContent fill + MTable fill;嵌入/短页/整页文档滚动跳过 fill',
|
|
542
543
|
'同行控件用 MSpace / MFlex;区块之间依赖 MPageContent 的 gap',
|
|
543
|
-
'局部区块优先 get_page_snippet(filters / toolbar / form-actions 等)',
|
|
544
|
+
'局部区块优先 get_page_snippet(filters / toolbar / form-actions / list-table 等)',
|
|
544
545
|
],
|
|
545
546
|
recommendEn: [
|
|
546
547
|
'Use MPageFilters, MPageToolbar, and MPageSection for filters, headers/actions, and forms',
|
|
547
|
-
'Place MTable directly in MPageContent on list pages',
|
|
548
|
+
'Place MTable directly in MPageContent on list pages (no extra Card)',
|
|
549
|
+
'Use MPageContent fill + MTable fill only for full-viewport admin lists whose main job is one table; skip fill for embedded/short/document-scroll cases',
|
|
548
550
|
'Use MSpace / MFlex for same-row controls; let MPageContent gap separate sections',
|
|
549
|
-
'Prefer get_page_snippet for local blocks such as filters, toolbar, or
|
|
551
|
+
'Prefer get_page_snippet for local blocks such as filters, toolbar, form-actions, or list-table',
|
|
550
552
|
],
|
|
551
553
|
discouraged: [
|
|
552
554
|
'手写 .page-filters / .page-toolbar 类',
|
|
553
555
|
'用 MCard 或带 padding 的容器再包 MPageFilters / MPageSection / MPageToolbar',
|
|
554
556
|
'在已有 MPage* 区块之间再写 margin 叠间距',
|
|
557
|
+
'对嵌入表/短页/整页滚动硬套 fill,或适合 fill 时却手写 min-height/calc',
|
|
555
558
|
],
|
|
556
559
|
discouragedEn: [
|
|
557
560
|
'Hand-written .page-filters / .page-toolbar classes',
|
|
558
561
|
'Wrapping MPageFilters / MPageSection / MPageToolbar with MCard or extra padded containers',
|
|
559
562
|
'Adding margin between MPage* siblings that PageContent gap already spaces',
|
|
563
|
+
'Forcing fill on embedded/short/document-scroll tables, or hand-writing min-height/calc when fill fits',
|
|
560
564
|
],
|
|
561
|
-
mcp: { decision: '
|
|
565
|
+
mcp: { snippet: 'list-table', decision: 'page-scroll-choice' },
|
|
562
566
|
},
|
|
563
567
|
{
|
|
564
568
|
id: 'scroll',
|
|
565
569
|
title: '滚动',
|
|
566
570
|
titleEn: 'Scroll',
|
|
567
571
|
recommend: [
|
|
572
|
+
'全视口主列表可选用表体滚动:MPageContent fill + MTable fill;否则优先整页 Layout 滚动或局部 MScrollbar',
|
|
568
573
|
'组件内置滚动区(Layout 壳、菜单/下拉面板、Table 视口等)使用内置 MScrollbar',
|
|
569
574
|
'Dialog / Drawer / Popover / Splitter 等内容区不强制主题滚动;需要时由业务自行包 MScrollbar',
|
|
570
575
|
'已内置滚动的组件通常无需再外包一层',
|
|
571
576
|
],
|
|
572
577
|
recommendEn: [
|
|
578
|
+
'Full-viewport main lists may use table-body scroll via MPageContent fill + MTable fill; otherwise prefer layout scroll or local MScrollbar',
|
|
573
579
|
'Component-owned scroll chrome (Layout shell, menu/select panels, Table viewport) uses built-in MScrollbar',
|
|
574
580
|
'Do not force themed scroll into Dialog / Drawer / Popover / Splitter content; apps can wrap MScrollbar when needed',
|
|
575
581
|
'Components that already scroll internally usually need no extra wrapper',
|
|
@@ -649,11 +655,14 @@ export const designRules = {
|
|
|
649
655
|
},
|
|
650
656
|
composition: {
|
|
651
657
|
workflow: [
|
|
652
|
-
'
|
|
658
|
+
'Resolve style first: reference → named preset → prompt cues → offer quiet/soft/dense/rail/studio/ink (see list_style_presets).',
|
|
659
|
+
'For full pages: recommend_page({ style? }) → get_golden_page → apply styleDirection → get_design_rules.',
|
|
660
|
+
'Craft: visual-craft Ops polish or Express design plan; optional frontend-design / impeccable only after contract (companions never replace M*).',
|
|
653
661
|
'For local edits: get_page_snippet(section) for filters/toolbar/form-actions/KPI/scrollable-panel blocks.',
|
|
654
662
|
'Use MLayout fillViewport as the app shell; put MPageContent inside MLayoutContent.',
|
|
655
663
|
'Prefer MPage* components over scoped CSS for filters, toolbars, headers, form surfaces, and KPI cards.',
|
|
656
664
|
'Use MSpace or MFlex for control groups inside MPageFilters; use MPageToolbar for title + primary action.',
|
|
665
|
+
'Golden pages lock structure/API, not the only aesthetic — do not emit identical quiet chrome every time.',
|
|
657
666
|
],
|
|
658
667
|
snippets: [
|
|
659
668
|
'list-filters',
|
|
@@ -690,6 +699,8 @@ export const designRules = {
|
|
|
690
699
|
},
|
|
691
700
|
goldenPages: [
|
|
692
701
|
'list-page',
|
|
702
|
+
'list-page-dense',
|
|
703
|
+
'list-page-rail',
|
|
693
704
|
'form-page',
|
|
694
705
|
'dashboard-page',
|
|
695
706
|
'login-page',
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named visual directions for generated pages.
|
|
3
|
+
* Structure still comes from golden pages; presets only change craft (density, chrome, atmosphere).
|
|
4
|
+
*/
|
|
5
|
+
export interface StylePreset {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
titleEn: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
summaryEn: string;
|
|
11
|
+
lanes: Array<'ops' | 'account' | 'flow' | 'express' | 'system'>;
|
|
12
|
+
keywords: string[];
|
|
13
|
+
cues: string[];
|
|
14
|
+
cuesEn: string[];
|
|
15
|
+
apply: string[];
|
|
16
|
+
applyEn: string[];
|
|
17
|
+
avoid: string[];
|
|
18
|
+
avoidEn: string[];
|
|
19
|
+
/** Preferred list golden craft variant when pattern is admin-list. */
|
|
20
|
+
listGoldenPage?: string;
|
|
21
|
+
}
|
|
22
|
+
/** How agents pick a visual direction before coding. */
|
|
23
|
+
export declare const styleResolution: {
|
|
24
|
+
priorityZh: string[];
|
|
25
|
+
priorityEn: string[];
|
|
26
|
+
hardRulesZh: string[];
|
|
27
|
+
hardRulesEn: string[];
|
|
28
|
+
};
|
|
29
|
+
export declare const stylePresets: StylePreset[];
|
|
30
|
+
export declare function findStylePreset(idOrAlias: string): StylePreset | undefined;
|
|
31
|
+
export declare function scoreStylePreset(preset: StylePreset, query: string): number;
|
|
32
|
+
export interface StyleDirectionResult {
|
|
33
|
+
resolution: 'preset' | 'inferred' | 'offer';
|
|
34
|
+
preset?: StylePreset;
|
|
35
|
+
confidence: number;
|
|
36
|
+
candidates: Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
title: string;
|
|
39
|
+
titleEn: string;
|
|
40
|
+
score: number;
|
|
41
|
+
}>;
|
|
42
|
+
guidanceZh: string;
|
|
43
|
+
guidanceEn: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a named style from an explicit id or free-text intent.
|
|
47
|
+
* Reference styles are handled by the agent (cannot be detected from text alone).
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveStyleDirection(args: {
|
|
50
|
+
intent?: string;
|
|
51
|
+
style?: string;
|
|
52
|
+
}): StyleDirectionResult;
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named visual directions for generated pages.
|
|
3
|
+
* Structure still comes from golden pages; presets only change craft (density, chrome, atmosphere).
|
|
4
|
+
*/
|
|
5
|
+
/** How agents pick a visual direction before coding. */
|
|
6
|
+
export const styleResolution = {
|
|
7
|
+
priorityZh: [
|
|
8
|
+
'用户给了参考样式(截图 / 设计稿 / 现有页 / URL /「像 XX」)→ 提取层级、密度、表面、强调色,映射到 --m-* + M*,不要换组件库',
|
|
9
|
+
'用户点名了风格 id / 中文名(如 soft、柔和留白)→ 套用该预设',
|
|
10
|
+
'提示词里有气质/行业线索(严谨、活泼、医疗、金融…)→ 推断最接近的预设,并在回复里点名;可选 ui-ux-pro-max 只搜关键词',
|
|
11
|
+
'以上都没有 → 用一句话列出 3~4 个可选风格请用户选;若用户明确说「直接写」→ 按域启发式选一个,不要永远 quiet',
|
|
12
|
+
'已安装 frontend-design / impeccable 时:结构与契约仍用 morya-ui-pages + MCP;companion 只加深审美/抛光(见 skill optional-companions)',
|
|
13
|
+
],
|
|
14
|
+
priorityEn: [
|
|
15
|
+
'User supplied a reference (screenshot / mock / existing page / URL / “like X”) → extract hierarchy, density, surfaces, accent; map to --m-* + M*; never switch UI kits',
|
|
16
|
+
'User named a style id / label (e.g. soft) → apply that preset',
|
|
17
|
+
'Prompt has mood / industry cues → infer the closest preset and name it; optional ui-ux-pro-max for keywords only',
|
|
18
|
+
'None of the above → ask once with 3–4 style options; if user says “just ship it” → pick by domain heuristic, do not always use quiet',
|
|
19
|
+
'If frontend-design / impeccable are installed: structure + contract still from morya-ui-pages + MCP; companions only deepen taste/polish (see skill optional-companions)',
|
|
20
|
+
],
|
|
21
|
+
hardRulesZh: [
|
|
22
|
+
'黄金样例管区块顺序与 API,不管死审美;结构可镜像,视觉必须跟参考 / 风格 / 提示词走',
|
|
23
|
+
'任何风格仍只用 morya-ui 的 M* 与 --m-*(可用 color-mix),禁止第二套 UI kit 与裸 hex 主题色',
|
|
24
|
+
'Ops 页不要做成营销落地页;Express 不要假装后台 CRUD',
|
|
25
|
+
'Frontend Design / Impeccable / UI-UX-Pro-Max 与本 skill 冲突时,以 morya-ui-pages + DESIGN.md + MCP 为准',
|
|
26
|
+
],
|
|
27
|
+
hardRulesEn: [
|
|
28
|
+
'Golden pages lock block order and APIs, not the only aesthetic; mirror structure, follow reference / style / prompt for craft',
|
|
29
|
+
'Every style still uses morya-ui M* and --m-* only (color-mix OK); no second UI kit or raw hex theme colors',
|
|
30
|
+
'Ops pages must not become marketing landings; Express must not pretend to be admin CRUD',
|
|
31
|
+
'When Frontend Design / Impeccable / UI-UX-Pro-Max conflict with this skill, morya-ui-pages + DESIGN.md + MCP win',
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
export const stylePresets = [
|
|
35
|
+
{
|
|
36
|
+
id: 'quiet',
|
|
37
|
+
title: '克制经典',
|
|
38
|
+
titleEn: 'Quiet classic',
|
|
39
|
+
summary: '接近黄金样例:扁平 chrome、单主按钮、少装饰,适合严肃后台。',
|
|
40
|
+
summaryEn: 'Closest to golden pages: flat chrome, one primary, little decoration — serious admin.',
|
|
41
|
+
lanes: ['ops', 'system'],
|
|
42
|
+
keywords: ['quiet', 'classic', '克制', '经典', '默认', '朴素', '严肃', 'enterprise', 'plain'],
|
|
43
|
+
cues: ['扁平表面', 'MPageFilters plain 或轻 filled', '默认 density', '无侧栏氛围色'],
|
|
44
|
+
cuesEn: ['Flat surfaces', 'Plain or light filled filters', 'Default density', 'No sider wash'],
|
|
45
|
+
apply: [
|
|
46
|
+
'镜像 list/form 黄金结构与 Ops polish',
|
|
47
|
+
'MPageContent 默认 density;筛选可 plain',
|
|
48
|
+
'不要额外氛围渐变或大段说明',
|
|
49
|
+
],
|
|
50
|
+
applyEn: [
|
|
51
|
+
'Mirror list/form golden structure + Ops polish',
|
|
52
|
+
'Default MPageContent density; filters may stay plain',
|
|
53
|
+
'No atmosphere gradients or long marketing copy',
|
|
54
|
+
],
|
|
55
|
+
avoid: ['营销 hero', '多主色块抢焦点'],
|
|
56
|
+
avoidEn: ['Marketing hero', 'Competing accent blocks'],
|
|
57
|
+
listGoldenPage: 'list-page',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'soft',
|
|
61
|
+
title: '柔和留白',
|
|
62
|
+
titleEn: 'Soft spacious',
|
|
63
|
+
summary: '更松的间距与说明文案,筛选带浅底,表格仍干净,适合运营/内容后台。',
|
|
64
|
+
summaryEn: 'Roomier spacing and descriptions, soft filter band, clean table — ops/content admin.',
|
|
65
|
+
lanes: ['ops', 'account', 'flow'],
|
|
66
|
+
keywords: ['soft', 'spacious', '柔和', '留白', '轻松', '友好', '运营', '内容', 'breathing'],
|
|
67
|
+
cues: ['MPageContent density="spacious"', 'MPageFilters variant="filled"', 'MPageHeader 带短 description', '状态用 MStatus'],
|
|
68
|
+
cuesEn: ['MPageContent density="spacious"', 'Filled filters', 'Short header description', 'MStatus for row state'],
|
|
69
|
+
apply: [
|
|
70
|
+
'标题下保留一句域说明',
|
|
71
|
+
'筛选用 filled;高级项 collapsible',
|
|
72
|
+
'行操作 text/small;空态 MEmpty 带下一步',
|
|
73
|
+
],
|
|
74
|
+
applyEn: [
|
|
75
|
+
'Keep one domain sentence under the title',
|
|
76
|
+
'Filled filters; collapsible advanced fields',
|
|
77
|
+
'Text/small row actions; MEmpty with a next step',
|
|
78
|
+
],
|
|
79
|
+
avoid: ['compact 挤成工具台', '装饰 Card 包表格'],
|
|
80
|
+
avoidEn: ['Forced compact toolkit feel', 'Decorative Card around the table'],
|
|
81
|
+
listGoldenPage: 'list-page',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'dense',
|
|
85
|
+
title: '高密度工具台',
|
|
86
|
+
titleEn: 'Dense toolkit',
|
|
87
|
+
summary: '紧凑控件与表格,信息优先,适合监控、运维、交易员类界面。',
|
|
88
|
+
summaryEn: 'Tight controls and tables, information-first — monitoring, ops, trader-like UIs.',
|
|
89
|
+
lanes: ['ops'],
|
|
90
|
+
keywords: ['dense', 'compact', '高密', '紧凑', '工具台', '运维', '监控', 'power user', 'trading'],
|
|
91
|
+
cues: ['MPageContent density="compact"', '表格 size="small"', '筛选控件更窄', '少 description'],
|
|
92
|
+
cuesEn: ['Compact page density', 'size="small" table', 'Narrower filters', 'Minimal descriptions'],
|
|
93
|
+
apply: [
|
|
94
|
+
'compact + 小尺寸表格/按钮',
|
|
95
|
+
'筛选一行尽量排完;高级项折叠',
|
|
96
|
+
'全视口主列表可考虑 fill',
|
|
97
|
+
],
|
|
98
|
+
applyEn: [
|
|
99
|
+
'Compact density + small table/buttons',
|
|
100
|
+
'Fit filters on one row; collapse advanced',
|
|
101
|
+
'Full-viewport main lists may use fill',
|
|
102
|
+
],
|
|
103
|
+
avoid: ['大留白 KPI 条', '营销文案腔'],
|
|
104
|
+
avoidEn: ['Large airy KPI strips', 'Marketing voice'],
|
|
105
|
+
listGoldenPage: 'list-page-dense',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'rail',
|
|
109
|
+
title: '侧栏强调',
|
|
110
|
+
titleEn: 'Accent rail',
|
|
111
|
+
summary: '侧栏/顶栏用 primary 的 token 氛围,主内容仍克制,适合有品牌色的 B 端。',
|
|
112
|
+
summaryEn: 'Token wash on sider/header, calm content — branded B2B shells.',
|
|
113
|
+
lanes: ['ops', 'account'],
|
|
114
|
+
keywords: ['rail', 'accent', '侧栏', '强调', '品牌', 'brand', 'colored sider', '主色'],
|
|
115
|
+
cues: [
|
|
116
|
+
'侧栏或顶栏 scoped 背景用 color-mix(primary)',
|
|
117
|
+
'菜单项全带 icon',
|
|
118
|
+
'内容区保持安静',
|
|
119
|
+
],
|
|
120
|
+
cuesEn: [
|
|
121
|
+
'Sider/header scoped bg via color-mix(primary)',
|
|
122
|
+
'Menu icons on every item',
|
|
123
|
+
'Content area stays calm',
|
|
124
|
+
],
|
|
125
|
+
apply: [
|
|
126
|
+
'仅 chrome 做氛围,不把表格做成营销块',
|
|
127
|
+
'主按钮仍唯一 primary',
|
|
128
|
+
'颜色只用 --m-* / color-mix',
|
|
129
|
+
],
|
|
130
|
+
applyEn: [
|
|
131
|
+
'Atmosphere on chrome only — not on the data table',
|
|
132
|
+
'Still one filled primary',
|
|
133
|
+
'Colors only via --m-* / color-mix',
|
|
134
|
+
],
|
|
135
|
+
avoid: ['整页渐变背景', '紫蓝 AI 默认渐变'],
|
|
136
|
+
avoidEn: ['Full-page gradient backgrounds', 'Purple-indigo AI default gradients'],
|
|
137
|
+
listGoldenPage: 'list-page-rail',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'studio',
|
|
141
|
+
title: '工作室呼吸感',
|
|
142
|
+
titleEn: 'Studio breath',
|
|
143
|
+
summary: '更强的标题层级与章节感,适合设计/协作/知识类产品后台。',
|
|
144
|
+
summaryEn: 'Stronger title hierarchy and sectioning — design, collab, knowledge products.',
|
|
145
|
+
lanes: ['ops', 'flow', 'express'],
|
|
146
|
+
keywords: ['studio', 'editorial', '工作室', '呼吸', '设计', '协作', '知识', 'creative', 'craft'],
|
|
147
|
+
cues: [
|
|
148
|
+
'MPageHeader 标题更醒目 + description',
|
|
149
|
+
'可用 MPageSection 分组次要块',
|
|
150
|
+
'空态/引导更精致(仍 MEmpty)',
|
|
151
|
+
],
|
|
152
|
+
cuesEn: [
|
|
153
|
+
'Stronger MPageHeader + description',
|
|
154
|
+
'MPageSection for secondary groups',
|
|
155
|
+
'Richer empty/guidance still via MEmpty',
|
|
156
|
+
],
|
|
157
|
+
apply: [
|
|
158
|
+
'层级靠字号与间距,不靠花哨阴影',
|
|
159
|
+
'可加 1 个 token 氛围(径向淡彩)',
|
|
160
|
+
'表格保持直接放 PageContent',
|
|
161
|
+
],
|
|
162
|
+
applyEn: [
|
|
163
|
+
'Hierarchy via type and space, not heavy shadows',
|
|
164
|
+
'Optional one token wash (radial tint)',
|
|
165
|
+
'Keep the table directly in PageContent',
|
|
166
|
+
],
|
|
167
|
+
avoid: ['报纸多栏排版', '一排实心按钮'],
|
|
168
|
+
avoidEn: ['Broadsheet multi-column look', 'Wall of filled buttons'],
|
|
169
|
+
listGoldenPage: 'list-page',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'ink',
|
|
173
|
+
title: '线框极简',
|
|
174
|
+
titleEn: 'Ink minimal',
|
|
175
|
+
summary: '强调边框与结构线,少填充,适合偏工具/文档气质的界面。',
|
|
176
|
+
summaryEn: 'Border-forward, little fill — tool/docs-like calm.',
|
|
177
|
+
lanes: ['ops', 'system', 'flow'],
|
|
178
|
+
keywords: ['ink', 'minimal', '线框', '极简', 'border', 'outline', '文档', 'docs', 'hairline'],
|
|
179
|
+
cues: [
|
|
180
|
+
'MPageFilters variant="plain"',
|
|
181
|
+
'MTable bordered',
|
|
182
|
+
'少用 filled 表面与 muted section',
|
|
183
|
+
],
|
|
184
|
+
cuesEn: [
|
|
185
|
+
'Plain filters',
|
|
186
|
+
'Bordered table',
|
|
187
|
+
'Few filled/muted surfaces',
|
|
188
|
+
],
|
|
189
|
+
apply: [
|
|
190
|
+
'用边框分区,不用大底色块',
|
|
191
|
+
'字重克制;主按钮仍唯一',
|
|
192
|
+
'避免零圆角报纸风(反 AI 默认)',
|
|
193
|
+
],
|
|
194
|
+
applyEn: [
|
|
195
|
+
'Partition with borders, not large fills',
|
|
196
|
+
'Restrained weight; one primary button',
|
|
197
|
+
'Avoid zero-radius broadsheet (anti AI-default)',
|
|
198
|
+
],
|
|
199
|
+
avoid: ['暖奶油衬线套装', '多层阴影当个性'],
|
|
200
|
+
avoidEn: ['Warm cream + serif kit', 'Multi-layer shadows as personality'],
|
|
201
|
+
listGoldenPage: 'list-page',
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
const aliasToId = {
|
|
205
|
+
quiet: 'quiet',
|
|
206
|
+
classic: 'quiet',
|
|
207
|
+
克制: 'quiet',
|
|
208
|
+
经典: 'quiet',
|
|
209
|
+
默认: 'quiet',
|
|
210
|
+
soft: 'soft',
|
|
211
|
+
spacious: 'soft',
|
|
212
|
+
柔和: 'soft',
|
|
213
|
+
留白: 'soft',
|
|
214
|
+
柔和留白: 'soft',
|
|
215
|
+
dense: 'dense',
|
|
216
|
+
compact: 'dense',
|
|
217
|
+
高密: 'dense',
|
|
218
|
+
紧凑: 'dense',
|
|
219
|
+
工具台: 'dense',
|
|
220
|
+
rail: 'rail',
|
|
221
|
+
accent: 'rail',
|
|
222
|
+
侧栏: 'rail',
|
|
223
|
+
强调: 'rail',
|
|
224
|
+
品牌: 'rail',
|
|
225
|
+
studio: 'studio',
|
|
226
|
+
editorial: 'studio',
|
|
227
|
+
工作室: 'studio',
|
|
228
|
+
呼吸: 'studio',
|
|
229
|
+
ink: 'ink',
|
|
230
|
+
minimal: 'ink',
|
|
231
|
+
线框: 'ink',
|
|
232
|
+
极简: 'ink',
|
|
233
|
+
};
|
|
234
|
+
export function findStylePreset(idOrAlias) {
|
|
235
|
+
const raw = idOrAlias.trim();
|
|
236
|
+
if (!raw)
|
|
237
|
+
return undefined;
|
|
238
|
+
const key = raw.toLowerCase().replace(/[-_\s]/g, '');
|
|
239
|
+
const aliased = aliasToId[key];
|
|
240
|
+
if (aliased)
|
|
241
|
+
return stylePresets.find((p) => p.id === aliased);
|
|
242
|
+
const byId = stylePresets.find((p) => p.id.replace(/[-_\s]/g, '') === key);
|
|
243
|
+
if (byId)
|
|
244
|
+
return byId;
|
|
245
|
+
const ranked = stylePresets
|
|
246
|
+
.map((preset) => ({ preset, score: scoreStylePreset(preset, raw) }))
|
|
247
|
+
.sort((a, b) => b.score - a.score);
|
|
248
|
+
return ranked[0] && ranked[0].score >= 25 ? ranked[0].preset : undefined;
|
|
249
|
+
}
|
|
250
|
+
export function scoreStylePreset(preset, query) {
|
|
251
|
+
const normalized = query.toLowerCase().trim();
|
|
252
|
+
if (!normalized)
|
|
253
|
+
return 0;
|
|
254
|
+
let score = 0;
|
|
255
|
+
if (normalized === preset.id || normalized.includes(preset.id))
|
|
256
|
+
score += 50;
|
|
257
|
+
for (const keyword of preset.keywords) {
|
|
258
|
+
const key = keyword.toLowerCase();
|
|
259
|
+
if (normalized === key)
|
|
260
|
+
score += 40;
|
|
261
|
+
else if (normalized.includes(key))
|
|
262
|
+
score += 12;
|
|
263
|
+
}
|
|
264
|
+
if (normalized.includes(preset.title.toLowerCase()))
|
|
265
|
+
score += 25;
|
|
266
|
+
if (normalized.includes(preset.titleEn.toLowerCase()))
|
|
267
|
+
score += 25;
|
|
268
|
+
return score;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Resolve a named style from an explicit id or free-text intent.
|
|
272
|
+
* Reference styles are handled by the agent (cannot be detected from text alone).
|
|
273
|
+
*/
|
|
274
|
+
export function resolveStyleDirection(args) {
|
|
275
|
+
const explicit = args.style?.trim();
|
|
276
|
+
if (explicit) {
|
|
277
|
+
const preset = findStylePreset(explicit);
|
|
278
|
+
if (preset) {
|
|
279
|
+
return {
|
|
280
|
+
resolution: 'preset',
|
|
281
|
+
preset,
|
|
282
|
+
confidence: 100,
|
|
283
|
+
candidates: [{ id: preset.id, title: preset.title, titleEn: preset.titleEn, score: 100 }],
|
|
284
|
+
guidanceZh: `已选风格「${preset.title}」(${preset.id})。在黄金结构上套用该预设的 apply,并遵守 avoid。`,
|
|
285
|
+
guidanceEn: `Style “${preset.titleEn}” (${preset.id}) selected. Apply its cues on the golden structure; honor avoid.`,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const query = [args.style || '', args.intent || ''].join(' ').trim();
|
|
290
|
+
const ranked = stylePresets
|
|
291
|
+
.map((preset) => ({ preset, score: scoreStylePreset(preset, query) }))
|
|
292
|
+
.sort((a, b) => b.score - a.score || a.preset.id.localeCompare(b.preset.id));
|
|
293
|
+
const best = ranked[0];
|
|
294
|
+
const candidates = ranked.slice(0, 4).map((item) => ({
|
|
295
|
+
id: item.preset.id,
|
|
296
|
+
title: item.preset.title,
|
|
297
|
+
titleEn: item.preset.titleEn,
|
|
298
|
+
score: item.score,
|
|
299
|
+
}));
|
|
300
|
+
if (best && best.score >= 20) {
|
|
301
|
+
return {
|
|
302
|
+
resolution: 'inferred',
|
|
303
|
+
preset: best.preset,
|
|
304
|
+
confidence: best.score,
|
|
305
|
+
candidates,
|
|
306
|
+
guidanceZh: `从提示词推断风格「${best.preset.title}」(${best.preset.id})。若不符,用户可改选:${candidates.map((c) => `${c.id}/${c.title}`).join('、')}。`,
|
|
307
|
+
guidanceEn: `Inferred style “${best.preset.titleEn}” (${best.preset.id}). User may switch: ${candidates.map((c) => `${c.id}/${c.titleEn}`).join(', ')}.`,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
resolution: 'offer',
|
|
312
|
+
confidence: 0,
|
|
313
|
+
candidates: stylePresets.slice(0, 4).map((preset) => ({
|
|
314
|
+
id: preset.id,
|
|
315
|
+
title: preset.title,
|
|
316
|
+
titleEn: preset.titleEn,
|
|
317
|
+
score: 0,
|
|
318
|
+
})),
|
|
319
|
+
guidanceZh: '未检测到明确风格。若用户已给参考样式,按参考提取并映射到 --m-*;否则用一句话请用户从 quiet/soft/dense/rail/studio/ink 中选,或按提示词域启发式选择(不要永远 quiet)。',
|
|
320
|
+
guidanceEn: 'No clear style. If the user gave a reference, extract cues onto --m-*; otherwise ask once among quiet/soft/dense/rail/studio/ink, or pick by domain heuristic (do not always use quiet).',
|
|
321
|
+
};
|
|
322
|
+
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export declare function createToolHandlers(catalog?: import("./catalog.js").Cata
|
|
|
116
116
|
intent: string;
|
|
117
117
|
pageType?: string;
|
|
118
118
|
features?: string[];
|
|
119
|
+
style?: string;
|
|
119
120
|
mode?: string;
|
|
120
121
|
includeScaffold?: boolean;
|
|
121
122
|
}) => {
|
|
@@ -134,6 +135,25 @@ export declare function createToolHandlers(catalog?: import("./catalog.js").Cata
|
|
|
134
135
|
text: string;
|
|
135
136
|
}[];
|
|
136
137
|
};
|
|
138
|
+
listStylePresets: (args?: {
|
|
139
|
+
mode?: string;
|
|
140
|
+
}) => {
|
|
141
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
142
|
+
content: {
|
|
143
|
+
type: "text";
|
|
144
|
+
text: string;
|
|
145
|
+
}[];
|
|
146
|
+
};
|
|
147
|
+
getStylePreset: (args: {
|
|
148
|
+
style: string;
|
|
149
|
+
mode?: string;
|
|
150
|
+
}) => {
|
|
151
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
152
|
+
content: {
|
|
153
|
+
type: "text";
|
|
154
|
+
text: string;
|
|
155
|
+
}[];
|
|
156
|
+
};
|
|
137
157
|
recommendComponent: (args: {
|
|
138
158
|
query?: string;
|
|
139
159
|
decision?: string;
|