@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/tools.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { findComponent, findGuide, loadCatalog, normalizeName, resolveLocale, textResult, toKebab, } from './catalog.js';
|
|
2
2
|
import { componentDecisions, findDecision, scoreDecision } from './decisions.js';
|
|
3
|
-
import { listGoldenPages, readGoldenPageSource } from './golden-pages.js';
|
|
3
|
+
import { listGoldenPages, readGoldenPageSource, resolveGoldenPageId } from './golden-pages.js';
|
|
4
4
|
import { filterPageSnippets, findPageSnippet, pageSnippets, scorePageSnippet } from './page-snippets.js';
|
|
5
5
|
import { designRules, findPattern, pagePatterns, scorePattern } from './patterns.js';
|
|
6
6
|
import { countCatalogResources, countCatalogResourceTemplates } from './resources.js';
|
|
7
|
+
import { findStylePreset, resolveStyleDirection, stylePresets, styleResolution, } from './style-presets.js';
|
|
7
8
|
function inspectButtonIconOnlyUsage(code, issues) {
|
|
8
9
|
const pairedTagRe = /<MButton\b([^>]*)>([\s\S]*?)<\/MButton>/gi;
|
|
9
10
|
let match = pairedTagRe.exec(code);
|
|
@@ -92,149 +93,149 @@ function generatedPageCode(patternId, intent, locale) {
|
|
|
92
93
|
const wizardImports = isWizard ? ', MStepper, MSpace' : '';
|
|
93
94
|
const settingsImports = isSettings ? ', MTabs' : '';
|
|
94
95
|
const statusImports = isList || isDetail || isDashboard ? ', MStatus' : '';
|
|
95
|
-
const script = `<script setup lang="ts">
|
|
96
|
-
import { ref } from 'vue'
|
|
97
|
-
import { MButton, MCard, MConfigProvider, MInput, MTag, zhCN${layoutImports}${pageImports}${listImports}${formImports}${dashboardImports}${detailImports}${emptyImports}${wizardImports}${settingsImports}${statusImports} } from 'morya-ui'
|
|
98
|
-
|
|
99
|
-
const loading = ref(false)
|
|
100
|
-
const error = ref('')
|
|
101
|
-
const title = ${JSON.stringify(title)}
|
|
102
|
-
${isList ? `const keyword = ref('')
|
|
103
|
-
const rows = ref<Record<string, unknown>[]>([])
|
|
104
|
-
const columns = [{ key: 'name', label: '${zh ? '名称' : 'Name'}' }, { key: 'status', label: '${zh ? '状态' : 'Status'}' }]
|
|
105
|
-
` : ''}${isForm || isAuth || isWizard ? `const model = ref({ name: '' })
|
|
106
|
-
` : ''}${isDashboard ? `const metrics = ref([
|
|
107
|
-
{ label: '${zh ? '总用户' : 'Users'}', value: '0' },
|
|
108
|
-
{ label: '${zh ? '今日活跃' : 'Active today'}', value: '0' },
|
|
109
|
-
])
|
|
110
|
-
` : ''}${isSettings ? `const activeTab = ref('general')
|
|
111
|
-
const settingTabs = [{ label: '${zh ? '常规' : 'General'}', value: 'general' }]
|
|
112
|
-
` : ''}${isWizard ? `const activeStep = ref(0)
|
|
113
|
-
const wizardSteps = [{ label: '${zh ? '基本信息' : 'Details'}' }, { label: '${zh ? '确认' : 'Confirm'}' }]
|
|
114
|
-
` : ''}
|
|
115
|
-
|
|
116
|
-
async function submit() {
|
|
117
|
-
loading.value = true
|
|
118
|
-
error.value = ''
|
|
119
|
-
try {
|
|
120
|
-
// Replace with the page API request.
|
|
121
|
-
} finally {
|
|
122
|
-
loading.value = false
|
|
123
|
-
}
|
|
124
|
-
}
|
|
96
|
+
const script = `<script setup lang="ts">
|
|
97
|
+
import { ref } from 'vue'
|
|
98
|
+
import { MButton, MCard, MConfigProvider, MInput, MTag, zhCN${layoutImports}${pageImports}${listImports}${formImports}${dashboardImports}${detailImports}${emptyImports}${wizardImports}${settingsImports}${statusImports} } from 'morya-ui'
|
|
99
|
+
|
|
100
|
+
const loading = ref(false)
|
|
101
|
+
const error = ref('')
|
|
102
|
+
const title = ${JSON.stringify(title)}
|
|
103
|
+
${isList ? `const keyword = ref('')
|
|
104
|
+
const rows = ref<Record<string, unknown>[]>([])
|
|
105
|
+
const columns = [{ key: 'name', label: '${zh ? '名称' : 'Name'}' }, { key: 'status', label: '${zh ? '状态' : 'Status'}' }]
|
|
106
|
+
` : ''}${isForm || isAuth || isWizard ? `const model = ref({ name: '' })
|
|
107
|
+
` : ''}${isDashboard ? `const metrics = ref([
|
|
108
|
+
{ label: '${zh ? '总用户' : 'Users'}', value: '0' },
|
|
109
|
+
{ label: '${zh ? '今日活跃' : 'Active today'}', value: '0' },
|
|
110
|
+
])
|
|
111
|
+
` : ''}${isSettings ? `const activeTab = ref('general')
|
|
112
|
+
const settingTabs = [{ label: '${zh ? '常规' : 'General'}', value: 'general' }]
|
|
113
|
+
` : ''}${isWizard ? `const activeStep = ref(0)
|
|
114
|
+
const wizardSteps = [{ label: '${zh ? '基本信息' : 'Details'}' }, { label: '${zh ? '确认' : 'Confirm'}' }]
|
|
115
|
+
` : ''}
|
|
116
|
+
|
|
117
|
+
async function submit() {
|
|
118
|
+
loading.value = true
|
|
119
|
+
error.value = ''
|
|
120
|
+
try {
|
|
121
|
+
// Replace with the page API request.
|
|
122
|
+
} finally {
|
|
123
|
+
loading.value = false
|
|
124
|
+
}
|
|
125
|
+
}
|
|
125
126
|
</script>`;
|
|
126
|
-
const listContent = ` <MPageHeader :title="title">
|
|
127
|
-
<template #actions>
|
|
128
|
-
<MButton severity="primary">${zh ? '新建' : 'Create'}</MButton>
|
|
129
|
-
</template>
|
|
130
|
-
</MPageHeader>
|
|
131
|
-
<MPageFilters variant="filled" :aria-label="${zh ? '筛选' : 'Filters'}">
|
|
132
|
-
<MSpace wrap>
|
|
133
|
-
<MInput v-model="keyword" placeholder="${zh ? '搜索关键词' : 'Search keyword'}" clearable style="width: 14rem" />
|
|
134
|
-
<MButton severity="secondary">${zh ? '查询' : 'Search'}</MButton>
|
|
135
|
-
<MButton severity="secondary">${zh ? '重置' : 'Reset'}</MButton>
|
|
136
|
-
</MSpace>
|
|
137
|
-
</MPageFilters>
|
|
138
|
-
<MTable :columns="columns" :rows="rows" :loading="loading" paginator :rows-per-page="10" striped bordered row-key="id">
|
|
139
|
-
<template #cell-status="{ value }">
|
|
140
|
-
<MStatus :label="String(value ?? '')" :severity="value === 'active' ? 'success' : 'secondary'" />
|
|
141
|
-
</template>
|
|
142
|
-
<template #empty>
|
|
143
|
-
<MEmpty
|
|
144
|
-
:title="${zh ? '暂无数据' : 'No data yet'}"
|
|
145
|
-
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
146
|
-
>
|
|
147
|
-
<template #extra>
|
|
148
|
-
<MButton severity="primary">${zh ? '新建' : 'Create'}</MButton>
|
|
149
|
-
</template>
|
|
150
|
-
</MEmpty>
|
|
151
|
-
</template>
|
|
127
|
+
const listContent = ` <MPageHeader :title="title">
|
|
128
|
+
<template #actions>
|
|
129
|
+
<MButton severity="primary">${zh ? '新建' : 'Create'}</MButton>
|
|
130
|
+
</template>
|
|
131
|
+
</MPageHeader>
|
|
132
|
+
<MPageFilters variant="filled" :aria-label="${zh ? '筛选' : 'Filters'}">
|
|
133
|
+
<MSpace wrap>
|
|
134
|
+
<MInput v-model="keyword" placeholder="${zh ? '搜索关键词' : 'Search keyword'}" clearable style="width: 14rem" />
|
|
135
|
+
<MButton severity="secondary">${zh ? '查询' : 'Search'}</MButton>
|
|
136
|
+
<MButton severity="secondary">${zh ? '重置' : 'Reset'}</MButton>
|
|
137
|
+
</MSpace>
|
|
138
|
+
</MPageFilters>
|
|
139
|
+
<MTable :columns="columns" :rows="rows" :loading="loading" fill paginator :rows-per-page="10" striped bordered row-key="id">
|
|
140
|
+
<template #cell-status="{ value }">
|
|
141
|
+
<MStatus :label="String(value ?? '')" :severity="value === 'active' ? 'success' : 'secondary'" />
|
|
142
|
+
</template>
|
|
143
|
+
<template #empty>
|
|
144
|
+
<MEmpty
|
|
145
|
+
:title="${zh ? '暂无数据' : 'No data yet'}"
|
|
146
|
+
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
147
|
+
>
|
|
148
|
+
<template #extra>
|
|
149
|
+
<MButton severity="primary">${zh ? '新建' : 'Create'}</MButton>
|
|
150
|
+
</template>
|
|
151
|
+
</MEmpty>
|
|
152
|
+
</template>
|
|
152
153
|
</MTable>`;
|
|
153
|
-
const formContent = ` <MPageHeader :title="title" :description="${zh ? '填写表单并保存。' : 'Fill in the form and save.'}" />
|
|
154
|
-
<MPageSection variant="form">
|
|
155
|
-
<MForm @submit.prevent="submit">
|
|
156
|
-
<MFormItem label="${zh ? '名称' : 'Name'}" name="name" required>
|
|
157
|
-
<MInput v-model="model.name" fluid />
|
|
158
|
-
</MFormItem>
|
|
159
|
-
<MPageSection variant="actions">
|
|
160
|
-
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '保存' : 'Save'}</MButton>
|
|
161
|
-
<MButton severity="secondary">${zh ? '取消' : 'Cancel'}</MButton>
|
|
162
|
-
</MPageSection>
|
|
163
|
-
</MForm>
|
|
154
|
+
const formContent = ` <MPageHeader :title="title" :description="${zh ? '填写表单并保存。' : 'Fill in the form and save.'}" />
|
|
155
|
+
<MPageSection variant="form">
|
|
156
|
+
<MForm @submit.prevent="submit">
|
|
157
|
+
<MFormItem label="${zh ? '名称' : 'Name'}" name="name" required>
|
|
158
|
+
<MInput v-model="model.name" fluid />
|
|
159
|
+
</MFormItem>
|
|
160
|
+
<MPageSection variant="actions">
|
|
161
|
+
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '保存' : 'Save'}</MButton>
|
|
162
|
+
<MButton severity="secondary">${zh ? '取消' : 'Cancel'}</MButton>
|
|
163
|
+
</MPageSection>
|
|
164
|
+
</MForm>
|
|
164
165
|
</MPageSection>`;
|
|
165
|
-
const dashboardContent = ` <MPageHeader :title="title" />
|
|
166
|
-
<MGrid :cols="2" :x-gap="16" :y-gap="16" responsive="screen">
|
|
167
|
-
<MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
|
|
168
|
-
<MPageStat :label="metric.label" :value="metric.value" icon="activity" />
|
|
169
|
-
</MGridItem>
|
|
170
|
-
</MGrid>
|
|
171
|
-
<MCard shadow="always" :title="${zh ? '趋势概览' : 'Trend overview'}">
|
|
172
|
-
<MSkeleton v-if="loading" height="8rem" />
|
|
173
|
-
<MEmpty
|
|
174
|
-
v-else
|
|
175
|
-
:title="${zh ? '暂无图表数据' : 'No chart data yet'}"
|
|
176
|
-
:description="${zh ? '接入图表或业务组件。' : 'Connect charts or business widgets here.'}"
|
|
177
|
-
aria-label="${zh ? '图表占位' : 'Chart placeholder'}"
|
|
178
|
-
/>
|
|
166
|
+
const dashboardContent = ` <MPageHeader :title="title" />
|
|
167
|
+
<MGrid :cols="2" :x-gap="16" :y-gap="16" responsive="screen">
|
|
168
|
+
<MGridItem v-for="metric in metrics" :key="metric.label" :span="1">
|
|
169
|
+
<MPageStat :label="metric.label" :value="metric.value" icon="activity" />
|
|
170
|
+
</MGridItem>
|
|
171
|
+
</MGrid>
|
|
172
|
+
<MCard shadow="always" :title="${zh ? '趋势概览' : 'Trend overview'}">
|
|
173
|
+
<MSkeleton v-if="loading" height="8rem" />
|
|
174
|
+
<MEmpty
|
|
175
|
+
v-else
|
|
176
|
+
:title="${zh ? '暂无图表数据' : 'No chart data yet'}"
|
|
177
|
+
:description="${zh ? '接入图表或业务组件。' : 'Connect charts or business widgets here.'}"
|
|
178
|
+
aria-label="${zh ? '图表占位' : 'Chart placeholder'}"
|
|
179
|
+
/>
|
|
179
180
|
</MCard>`;
|
|
180
|
-
const detailContent = ` <MPageHeader :title="title" :description="${zh ? '查看资源摘要与属性。' : 'Review summary and properties.'}">
|
|
181
|
-
<template #actions>
|
|
182
|
-
<MSpace>
|
|
183
|
-
<MStatus :label="${zh ? '启用' : 'Active'}" severity="success" />
|
|
184
|
-
<MButton severity="primary">${zh ? '编辑' : 'Edit'}</MButton>
|
|
185
|
-
<MButton severity="secondary">${zh ? '返回' : 'Back'}</MButton>
|
|
186
|
-
</MSpace>
|
|
187
|
-
</template>
|
|
188
|
-
</MPageHeader>
|
|
189
|
-
<MCard :title="${zh ? '基本信息' : 'Basics'}">
|
|
190
|
-
<p style="margin:0;color:var(--m-color-text-muted)">${zh ? '用定义列表或网格展示属性;相关列表用 MTable + rows。' : 'Use a definition grid; related lists use MTable + rows.'}</p>
|
|
181
|
+
const detailContent = ` <MPageHeader :title="title" :description="${zh ? '查看资源摘要与属性。' : 'Review summary and properties.'}">
|
|
182
|
+
<template #actions>
|
|
183
|
+
<MSpace>
|
|
184
|
+
<MStatus :label="${zh ? '启用' : 'Active'}" severity="success" />
|
|
185
|
+
<MButton severity="primary">${zh ? '编辑' : 'Edit'}</MButton>
|
|
186
|
+
<MButton severity="secondary">${zh ? '返回' : 'Back'}</MButton>
|
|
187
|
+
</MSpace>
|
|
188
|
+
</template>
|
|
189
|
+
</MPageHeader>
|
|
190
|
+
<MCard :title="${zh ? '基本信息' : 'Basics'}">
|
|
191
|
+
<p style="margin:0;color:var(--m-color-text-muted)">${zh ? '用定义列表或网格展示属性;相关列表用 MTable + rows。' : 'Use a definition grid; related lists use MTable + rows.'}</p>
|
|
191
192
|
</MCard>`;
|
|
192
|
-
const settingsContent = ` <MPageHeader :title="title" :description="${zh ? '按域分组配置偏好。' : 'Grouped preference settings.'}" />
|
|
193
|
-
<MTabs v-model="activeTab" :tabs="settingTabs">
|
|
194
|
-
<template #default="{ activeValue }">
|
|
195
|
-
<MPageSection v-if="activeValue === 'general'" variant="form" :title="${zh ? '常规' : 'General'}">
|
|
196
|
-
<MForm @submit.prevent="submit">
|
|
197
|
-
<MFormItem label="${zh ? '显示名称' : 'Display name'}" name="name" required>
|
|
198
|
-
<MInput v-model="model.name" fluid />
|
|
199
|
-
</MFormItem>
|
|
200
|
-
<MPageSection variant="actions">
|
|
201
|
-
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '保存' : 'Save'}</MButton>
|
|
202
|
-
</MPageSection>
|
|
203
|
-
</MForm>
|
|
204
|
-
</MPageSection>
|
|
205
|
-
</template>
|
|
193
|
+
const settingsContent = ` <MPageHeader :title="title" :description="${zh ? '按域分组配置偏好。' : 'Grouped preference settings.'}" />
|
|
194
|
+
<MTabs v-model="activeTab" :tabs="settingTabs">
|
|
195
|
+
<template #default="{ activeValue }">
|
|
196
|
+
<MPageSection v-if="activeValue === 'general'" variant="form" :title="${zh ? '常规' : 'General'}">
|
|
197
|
+
<MForm @submit.prevent="submit">
|
|
198
|
+
<MFormItem label="${zh ? '显示名称' : 'Display name'}" name="name" required>
|
|
199
|
+
<MInput v-model="model.name" fluid />
|
|
200
|
+
</MFormItem>
|
|
201
|
+
<MPageSection variant="actions">
|
|
202
|
+
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '保存' : 'Save'}</MButton>
|
|
203
|
+
</MPageSection>
|
|
204
|
+
</MForm>
|
|
205
|
+
</MPageSection>
|
|
206
|
+
</template>
|
|
206
207
|
</MTabs>`;
|
|
207
|
-
const wizardContent = ` <MPageHeader :title="title" :description="${zh ? '每步一个主任务。' : 'One job per step.'}" />
|
|
208
|
-
<MStepper v-model="activeStep" :steps="wizardSteps" linear />
|
|
209
|
-
<MPageSection variant="form">
|
|
210
|
-
<MForm @submit.prevent="submit">
|
|
211
|
-
<MFormItem label="${zh ? '名称' : 'Name'}" name="name" required>
|
|
212
|
-
<MInput v-model="model.name" fluid />
|
|
213
|
-
</MFormItem>
|
|
214
|
-
<MPageSection variant="actions">
|
|
215
|
-
<MSpace>
|
|
216
|
-
<MButton severity="secondary" text :disabled="activeStep === 0" @click="activeStep = Math.max(activeStep - 1, 0)">${zh ? '上一步' : 'Back'}</MButton>
|
|
217
|
-
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '下一步' : 'Next'}</MButton>
|
|
218
|
-
</MSpace>
|
|
219
|
-
</MPageSection>
|
|
220
|
-
</MForm>
|
|
208
|
+
const wizardContent = ` <MPageHeader :title="title" :description="${zh ? '每步一个主任务。' : 'One job per step.'}" />
|
|
209
|
+
<MStepper v-model="activeStep" :steps="wizardSteps" linear />
|
|
210
|
+
<MPageSection variant="form">
|
|
211
|
+
<MForm @submit.prevent="submit">
|
|
212
|
+
<MFormItem label="${zh ? '名称' : 'Name'}" name="name" required>
|
|
213
|
+
<MInput v-model="model.name" fluid />
|
|
214
|
+
</MFormItem>
|
|
215
|
+
<MPageSection variant="actions">
|
|
216
|
+
<MSpace>
|
|
217
|
+
<MButton severity="secondary" text :disabled="activeStep === 0" @click="activeStep = Math.max(activeStep - 1, 0)">${zh ? '上一步' : 'Back'}</MButton>
|
|
218
|
+
<MButton native-type="submit" severity="primary" :loading="loading">${zh ? '下一步' : 'Next'}</MButton>
|
|
219
|
+
</MSpace>
|
|
220
|
+
</MPageSection>
|
|
221
|
+
</MForm>
|
|
221
222
|
</MPageSection>`;
|
|
222
223
|
let innerTemplate = '';
|
|
223
224
|
if (isList) {
|
|
224
|
-
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
225
|
-
<MLayout has-sider fill-viewport>
|
|
226
|
-
<MLayoutSider bordered />
|
|
227
|
-
<MLayout>
|
|
228
|
-
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
229
|
-
<MBreadcrumb :model="[{ label: '${zh ? '首页' : 'Home'}', to: '/' }, { label: '${title}' }]" />
|
|
230
|
-
</MLayoutHeader>
|
|
231
|
-
<MLayoutContent>
|
|
232
|
-
<MPageContent>
|
|
233
|
-
${listContent}
|
|
234
|
-
</MPageContent>
|
|
235
|
-
</MLayoutContent>
|
|
236
|
-
</MLayout>
|
|
237
|
-
</MLayout>
|
|
225
|
+
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
226
|
+
<MLayout has-sider fill-viewport>
|
|
227
|
+
<MLayoutSider bordered />
|
|
228
|
+
<MLayout>
|
|
229
|
+
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
230
|
+
<MBreadcrumb :model="[{ label: '${zh ? '首页' : 'Home'}', to: '/' }, { label: '${title}' }]" />
|
|
231
|
+
</MLayoutHeader>
|
|
232
|
+
<MLayoutContent>
|
|
233
|
+
<MPageContent fill>
|
|
234
|
+
${listContent}
|
|
235
|
+
</MPageContent>
|
|
236
|
+
</MLayoutContent>
|
|
237
|
+
</MLayout>
|
|
238
|
+
</MLayout>
|
|
238
239
|
</MConfigProvider>`;
|
|
239
240
|
}
|
|
240
241
|
else if (useLayoutShell) {
|
|
@@ -248,76 +249,76 @@ ${listContent}
|
|
|
248
249
|
? wizardContent
|
|
249
250
|
: formContent;
|
|
250
251
|
const pageContentAttrs = isForm || isSettings || isWizard ? ' width="narrow"' : isDashboard ? ' density="spacious"' : '';
|
|
251
|
-
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
252
|
-
<MLayout fill-viewport>
|
|
253
|
-
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
254
|
-
<MBreadcrumb :model="[{ label: '${zh ? '首页' : 'Home'}', to: '/' }, { label: title }]" />
|
|
255
|
-
</MLayoutHeader>
|
|
256
|
-
<MLayoutContent>
|
|
257
|
-
<MPageContent${pageContentAttrs}>
|
|
258
|
-
${content}
|
|
259
|
-
</MPageContent>
|
|
260
|
-
</MLayoutContent>
|
|
261
|
-
</MLayout>
|
|
252
|
+
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
253
|
+
<MLayout fill-viewport>
|
|
254
|
+
<MLayoutHeader :padding="'var(--m-space-4) var(--m-space-6)'">
|
|
255
|
+
<MBreadcrumb :model="[{ label: '${zh ? '首页' : 'Home'}', to: '/' }, { label: title }]" />
|
|
256
|
+
</MLayoutHeader>
|
|
257
|
+
<MLayoutContent>
|
|
258
|
+
<MPageContent${pageContentAttrs}>
|
|
259
|
+
${content}
|
|
260
|
+
</MPageContent>
|
|
261
|
+
</MLayoutContent>
|
|
262
|
+
</MLayout>
|
|
262
263
|
</MConfigProvider>`;
|
|
263
264
|
}
|
|
264
265
|
else if (isAuth) {
|
|
265
|
-
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
266
|
-
<main class="m-generated-page m-generated-auth">
|
|
267
|
-
<MCard>
|
|
268
|
-
<MForm label-position="top" @submit.prevent="submit">
|
|
269
|
-
<MFormItem label="${zh ? '邮箱' : 'Email'}" name="email">
|
|
270
|
-
<MInput type="email" fluid />
|
|
271
|
-
</MFormItem>
|
|
272
|
-
<MFormItem label="${zh ? '密码' : 'Password'}" name="password">
|
|
273
|
-
<MInput type="password" fluid />
|
|
274
|
-
</MFormItem>
|
|
275
|
-
<MButton native-type="submit" severity="primary" :loading="loading" fluid>${zh ? '登录' : 'Sign in'}</MButton>
|
|
276
|
-
</MForm>
|
|
277
|
-
</MCard>
|
|
278
|
-
</main>
|
|
266
|
+
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
267
|
+
<main class="m-generated-page m-generated-auth">
|
|
268
|
+
<MCard>
|
|
269
|
+
<MForm label-position="top" @submit.prevent="submit">
|
|
270
|
+
<MFormItem label="${zh ? '邮箱' : 'Email'}" name="email">
|
|
271
|
+
<MInput type="email" fluid />
|
|
272
|
+
</MFormItem>
|
|
273
|
+
<MFormItem label="${zh ? '密码' : 'Password'}" name="password">
|
|
274
|
+
<MInput type="password" fluid />
|
|
275
|
+
</MFormItem>
|
|
276
|
+
<MButton native-type="submit" severity="primary" :loading="loading" fluid>${zh ? '登录' : 'Sign in'}</MButton>
|
|
277
|
+
</MForm>
|
|
278
|
+
</MCard>
|
|
279
|
+
</main>
|
|
279
280
|
</MConfigProvider>`;
|
|
280
281
|
}
|
|
281
282
|
else if (isEmpty) {
|
|
282
|
-
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
283
|
-
<main class="m-generated-page">
|
|
284
|
-
<MPageContent>
|
|
285
|
-
<MPageHeader :title="title">
|
|
286
|
-
<template #actions>
|
|
287
|
-
<MButton severity="primary" @click="submit">${zh ? '新建' : 'Create'}</MButton>
|
|
288
|
-
</template>
|
|
289
|
-
</MPageHeader>
|
|
290
|
-
<MEmpty
|
|
291
|
-
:title="${zh ? '暂无内容' : 'Nothing here yet'}"
|
|
292
|
-
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
293
|
-
icon="database"
|
|
294
|
-
>
|
|
295
|
-
<template #extra>
|
|
296
|
-
<MButton severity="primary" @click="submit">${zh ? '创建' : 'Create'}</MButton>
|
|
297
|
-
</template>
|
|
298
|
-
</MEmpty>
|
|
299
|
-
</MPageContent>
|
|
300
|
-
</main>
|
|
283
|
+
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
284
|
+
<main class="m-generated-page">
|
|
285
|
+
<MPageContent>
|
|
286
|
+
<MPageHeader :title="title">
|
|
287
|
+
<template #actions>
|
|
288
|
+
<MButton severity="primary" @click="submit">${zh ? '新建' : 'Create'}</MButton>
|
|
289
|
+
</template>
|
|
290
|
+
</MPageHeader>
|
|
291
|
+
<MEmpty
|
|
292
|
+
:title="${zh ? '暂无内容' : 'Nothing here yet'}"
|
|
293
|
+
:description="${zh ? '创建第一条记录开始使用。' : 'Create your first record to get started.'}"
|
|
294
|
+
icon="database"
|
|
295
|
+
>
|
|
296
|
+
<template #extra>
|
|
297
|
+
<MButton severity="primary" @click="submit">${zh ? '创建' : 'Create'}</MButton>
|
|
298
|
+
</template>
|
|
299
|
+
</MEmpty>
|
|
300
|
+
</MPageContent>
|
|
301
|
+
</main>
|
|
301
302
|
</MConfigProvider>`;
|
|
302
303
|
}
|
|
303
304
|
else {
|
|
304
|
-
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
305
|
-
<main class="m-generated-page">
|
|
306
|
-
<MCard>
|
|
307
|
-
<p class="m-generated-muted">${zh ? '将此区域替换为页面内容。' : 'Replace this area with page content.'}</p>
|
|
308
|
-
<MTag value="${zh ? '示例' : 'Example'}" severity="info" />
|
|
309
|
-
</MCard>
|
|
310
|
-
</main>
|
|
305
|
+
innerTemplate = `<MConfigProvider :locale="zhCN">
|
|
306
|
+
<main class="m-generated-page">
|
|
307
|
+
<MCard>
|
|
308
|
+
<p class="m-generated-muted">${zh ? '将此区域替换为页面内容。' : 'Replace this area with page content.'}</p>
|
|
309
|
+
<MTag value="${zh ? '示例' : 'Example'}" severity="info" />
|
|
310
|
+
</MCard>
|
|
311
|
+
</main>
|
|
311
312
|
</MConfigProvider>`;
|
|
312
313
|
}
|
|
313
|
-
const template = `<template>
|
|
314
|
-
${innerTemplate}
|
|
315
|
-
<p v-if="error" role="alert" class="m-generated-error">${'{{ error }}'}</p>
|
|
314
|
+
const template = `<template>
|
|
315
|
+
${innerTemplate}
|
|
316
|
+
<p v-if="error" role="alert" class="m-generated-error">${'{{ error }}'}</p>
|
|
316
317
|
</template>`;
|
|
317
|
-
const style = `<style scoped>
|
|
318
|
-
.m-generated-auth { display: grid; place-items: center; padding: var(--m-space-8); max-width: 24rem; margin: 0 auto; min-height: 100vh; }
|
|
319
|
-
.m-generated-empty { display: grid; gap: var(--m-space-2); justify-items: center; padding: var(--m-space-8); text-align: center; }
|
|
320
|
-
.m-generated-error { color: var(--m-color-danger); padding: 0 var(--m-space-6); }
|
|
318
|
+
const style = `<style scoped>
|
|
319
|
+
.m-generated-auth { display: grid; place-items: center; padding: var(--m-space-8); max-width: 24rem; margin: 0 auto; min-height: 100vh; }
|
|
320
|
+
.m-generated-empty { display: grid; gap: var(--m-space-2); justify-items: center; padding: var(--m-space-8); text-align: center; }
|
|
321
|
+
.m-generated-error { color: var(--m-color-danger); padding: 0 var(--m-space-6); }
|
|
321
322
|
</style>`;
|
|
322
323
|
return { script, template, style };
|
|
323
324
|
}
|
|
@@ -729,13 +730,47 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
729
730
|
});
|
|
730
731
|
}
|
|
731
732
|
const locale = resolveLocale(args.mode);
|
|
733
|
+
const styleDirection = resolveStyleDirection({ intent: args.intent, style: args.style });
|
|
734
|
+
const stylePayload = {
|
|
735
|
+
resolution: styleDirection.resolution,
|
|
736
|
+
confidence: styleDirection.confidence,
|
|
737
|
+
preset: styleDirection.preset
|
|
738
|
+
? {
|
|
739
|
+
id: styleDirection.preset.id,
|
|
740
|
+
title: locale === 'en-US' ? styleDirection.preset.titleEn : styleDirection.preset.title,
|
|
741
|
+
summary: locale === 'en-US' ? styleDirection.preset.summaryEn : styleDirection.preset.summary,
|
|
742
|
+
apply: locale === 'en-US' ? styleDirection.preset.applyEn : styleDirection.preset.apply,
|
|
743
|
+
avoid: locale === 'en-US' ? styleDirection.preset.avoidEn : styleDirection.preset.avoid,
|
|
744
|
+
}
|
|
745
|
+
: null,
|
|
746
|
+
candidates: styleDirection.candidates.map((item) => ({
|
|
747
|
+
id: item.id,
|
|
748
|
+
title: locale === 'en-US' ? item.titleEn : item.title,
|
|
749
|
+
score: item.score,
|
|
750
|
+
})),
|
|
751
|
+
guidance: locale === 'en-US' ? styleDirection.guidanceEn : styleDirection.guidanceZh,
|
|
752
|
+
priority: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
753
|
+
note: locale === 'en-US'
|
|
754
|
+
? 'If the user supplied a visual reference, follow that first (map to --m-* + M*). Golden pages lock structure, not the only aesthetic.'
|
|
755
|
+
: '若用户提供了参考样式,优先跟参考(映射到 --m-* + M*)。黄金样例锁结构,不是唯一审美。',
|
|
756
|
+
};
|
|
757
|
+
const resolvedGoldenId = resolveGoldenPageId({
|
|
758
|
+
patternId: best.pattern.id,
|
|
759
|
+
canonicalGoldenId: best.pattern.goldenPage,
|
|
760
|
+
styleId: styleDirection.preset?.id || args.style || null,
|
|
761
|
+
});
|
|
762
|
+
const resolvedGoldenPath = resolvedGoldenId
|
|
763
|
+
? `docs/golden-pages/${resolvedGoldenId}.vue`
|
|
764
|
+
: best.pattern.goldenPage;
|
|
732
765
|
const result = {
|
|
733
766
|
intent: args.intent,
|
|
734
767
|
pageType: args.pageType,
|
|
768
|
+
style: args.style,
|
|
735
769
|
matchedPattern: best.pattern.id,
|
|
736
770
|
title: locale === 'en-US' ? best.pattern.titleEn : best.pattern.title,
|
|
737
771
|
confidence: best.score,
|
|
738
|
-
goldenPage:
|
|
772
|
+
goldenPage: resolvedGoldenPath,
|
|
773
|
+
styleDirection: stylePayload,
|
|
739
774
|
components: best.pattern.components,
|
|
740
775
|
structure: best.pattern.structure,
|
|
741
776
|
layout: best.pattern.layout,
|
|
@@ -744,23 +779,26 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
744
779
|
avoid: best.pattern.avoid,
|
|
745
780
|
alternatives: ranked.slice(1, 3).filter((item) => item.score > 0).map((item) => ({ id: item.pattern.id, score: item.score })),
|
|
746
781
|
nextStep: (() => {
|
|
747
|
-
const goldenId =
|
|
748
|
-
|
|
749
|
-
|
|
782
|
+
const goldenId = resolvedGoldenId;
|
|
783
|
+
const styleHint = locale === 'en-US'
|
|
784
|
+
? styleDirection.resolution === 'offer'
|
|
785
|
+
? ' Resolve style first (reference → preset → ask among quiet/soft/dense/rail/studio/ink).'
|
|
786
|
+
: ` Apply styleDirection preset "${styleDirection.preset?.id || 'n/a'}".`
|
|
787
|
+
: styleDirection.resolution === 'offer'
|
|
788
|
+
? ' 先定风格(参考 → 预设 → 请用户从 quiet/soft/dense/rail/studio/ink 选)。'
|
|
789
|
+
: ` 套用 styleDirection 预设「${styleDirection.preset?.id || 'n/a'}」。`;
|
|
750
790
|
if (locale === 'en-US') {
|
|
751
791
|
return goldenId
|
|
752
|
-
? `Call get_golden_page("${goldenId}") and mirror
|
|
753
|
-
: `No golden page for this pattern: use get_pattern + get_design_rules
|
|
792
|
+
? `Call get_golden_page("${goldenId}") and mirror structure.${styleHint} Look up unfamiliar APIs with get_component/get_example, then always run validate_usage and validate_page.`
|
|
793
|
+
: `No golden page for this pattern: use get_pattern + get_design_rules.${styleHint} Look up APIs with get_component/get_example, then always run validate_usage and validate_page.`;
|
|
754
794
|
}
|
|
755
795
|
return goldenId
|
|
756
|
-
? `调用 get_golden_page("${goldenId}")
|
|
757
|
-
: `该模式暂无黄金样例:用 get_pattern + get_design_rules
|
|
796
|
+
? `调用 get_golden_page("${goldenId}") 并镜像结构。${styleHint} 不熟悉的 API 用 get_component/get_example 核对,然后必须跑 validate_usage 与 validate_page。`
|
|
797
|
+
: `该模式暂无黄金样例:用 get_pattern + get_design_rules。${styleHint} API 用 get_component/get_example 核对,然后必须跑 validate_usage 与 validate_page。`;
|
|
758
798
|
})(),
|
|
759
799
|
};
|
|
760
800
|
if (args.includeScaffold) {
|
|
761
|
-
const goldenId =
|
|
762
|
-
listGoldenPages().find((item) => item.patternId === best.pattern.id)?.id ||
|
|
763
|
-
null;
|
|
801
|
+
const goldenId = resolvedGoldenId;
|
|
764
802
|
const golden = goldenId ? readGoldenPageSource(goldenId) : null;
|
|
765
803
|
if (golden) {
|
|
766
804
|
result.scaffold = {
|
|
@@ -805,6 +843,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
805
843
|
patternId: payload.record.patternId,
|
|
806
844
|
title: locale === 'en-US' ? payload.record.titleEn : payload.record.title,
|
|
807
845
|
file: payload.record.file,
|
|
846
|
+
style: payload.record.style ?? null,
|
|
847
|
+
variantOf: payload.record.variantOf ?? null,
|
|
808
848
|
source: payload.source,
|
|
809
849
|
nextStep: locale === 'en-US'
|
|
810
850
|
? 'Mirror this structure and replace business data. Look up unfamiliar APIs with get_component/get_example, then always run validate_usage and validate_page. See get_design_rules for page standards.'
|
|
@@ -819,6 +859,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
819
859
|
patternId: item.patternId,
|
|
820
860
|
title: locale === 'en-US' ? item.titleEn : item.title,
|
|
821
861
|
file: item.file,
|
|
862
|
+
style: item.style ?? null,
|
|
863
|
+
variantOf: item.variantOf ?? null,
|
|
822
864
|
})),
|
|
823
865
|
});
|
|
824
866
|
}
|
|
@@ -1033,6 +1075,19 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1033
1075
|
: '契约:MTable 行数据使用 rows,不要用 data。见黄金样例 list-page / get_component Table。',
|
|
1034
1076
|
});
|
|
1035
1077
|
}
|
|
1078
|
+
if (/fill-viewport|fillViewport/i.test(code)
|
|
1079
|
+
&& /<MTable\b[^>]*\bpaginator\b/i.test(code)
|
|
1080
|
+
&& /<MPageContent\b/i.test(code)
|
|
1081
|
+
&& !(/<MPageContent\b[^>]*\bfill\b/i.test(code)
|
|
1082
|
+
&& /<MTable\b[^>]*\bfill\b/i.test(code))) {
|
|
1083
|
+
suggestions.push({
|
|
1084
|
+
standardId: 'page-sections',
|
|
1085
|
+
type: 'list-fill-height',
|
|
1086
|
+
message: locale === 'en-US'
|
|
1087
|
+
? 'Advisory: for a full-viewport admin list whose main job is one table, consider MPageContent fill + MTable fill so the body scrolls and pagination stays at the page bottom. Skip fill for embedded/short/document-scroll tables (see page-scroll-choice / golden list-page).'
|
|
1088
|
+
: '参考建议:若这是全视口后台主列表且表格是页面主任务,可考虑 MPageContent fill + MTable fill(表体滚动、分页贴底)。嵌入表/短页/整页文档滚动则跳过 fill(见 page-scroll-choice / 黄金样例 list-page)。',
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1036
1091
|
if (/<MMessage\b[^>]*\bseverity\b/i.test(code)) {
|
|
1037
1092
|
suggestions.push({
|
|
1038
1093
|
standardId: 'feedback',
|
|
@@ -1097,10 +1152,21 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1097
1152
|
}
|
|
1098
1153
|
function getDesignRules(args = {}) {
|
|
1099
1154
|
const locale = resolveLocale(args.mode);
|
|
1155
|
+
const styles = {
|
|
1156
|
+
resolution: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1157
|
+
hardRules: locale === 'en-US' ? styleResolution.hardRulesEn : styleResolution.hardRulesZh,
|
|
1158
|
+
presets: stylePresets.map((preset) => ({
|
|
1159
|
+
id: preset.id,
|
|
1160
|
+
title: locale === 'en-US' ? preset.titleEn : preset.title,
|
|
1161
|
+
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1162
|
+
lanes: preset.lanes,
|
|
1163
|
+
})),
|
|
1164
|
+
};
|
|
1100
1165
|
if (locale === 'zh-CN') {
|
|
1101
1166
|
return textResult({
|
|
1102
1167
|
meta: designRules.meta,
|
|
1103
1168
|
standards: designRules.standards,
|
|
1169
|
+
styles,
|
|
1104
1170
|
tokens: designRules.tokens,
|
|
1105
1171
|
composition: designRules.composition,
|
|
1106
1172
|
actions: designRules.actions,
|
|
@@ -1118,6 +1184,7 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1118
1184
|
discouraged: item.discouragedEn,
|
|
1119
1185
|
mcp: item.mcp,
|
|
1120
1186
|
})),
|
|
1187
|
+
styles,
|
|
1121
1188
|
tokens: {
|
|
1122
1189
|
colors: ['--m-color-primary', '--m-color-surface', '--m-color-text', '--m-color-border'],
|
|
1123
1190
|
spacing: '--m-space-*',
|
|
@@ -1159,9 +1226,47 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1159
1226
|
'Form controls should have a visible label or an equivalent accessible name.',
|
|
1160
1227
|
'Overlays teleport to body by default; only change appendTo for a clear layout constraint.',
|
|
1161
1228
|
'Prefer documented component variants over deep CSS overrides.',
|
|
1229
|
+
'Resolve visual style via reference → preset → prompt cues → offer quiet/soft/dense/rail/studio/ink; golden pages lock structure, not aesthetics.',
|
|
1162
1230
|
],
|
|
1163
1231
|
});
|
|
1164
1232
|
}
|
|
1233
|
+
function listStylePresets(args = {}) {
|
|
1234
|
+
const locale = resolveLocale(args.mode);
|
|
1235
|
+
return textResult({
|
|
1236
|
+
resolution: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1237
|
+
hardRules: locale === 'en-US' ? styleResolution.hardRulesEn : styleResolution.hardRulesZh,
|
|
1238
|
+
presets: stylePresets.map((preset) => ({
|
|
1239
|
+
id: preset.id,
|
|
1240
|
+
title: locale === 'en-US' ? preset.titleEn : preset.title,
|
|
1241
|
+
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1242
|
+
lanes: preset.lanes,
|
|
1243
|
+
keywords: preset.keywords,
|
|
1244
|
+
listGoldenPage: preset.listGoldenPage ?? null,
|
|
1245
|
+
})),
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
function getStylePreset(args) {
|
|
1249
|
+
const locale = resolveLocale(args.mode);
|
|
1250
|
+
const preset = findStylePreset(args.style);
|
|
1251
|
+
if (!preset) {
|
|
1252
|
+
return textResult({
|
|
1253
|
+
error: `Style preset not found: ${args.style}`,
|
|
1254
|
+
available: stylePresets.map((item) => item.id),
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
return textResult({
|
|
1258
|
+
id: preset.id,
|
|
1259
|
+
title: locale === 'en-US' ? preset.titleEn : preset.title,
|
|
1260
|
+
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1261
|
+
lanes: preset.lanes,
|
|
1262
|
+
keywords: preset.keywords,
|
|
1263
|
+
listGoldenPage: preset.listGoldenPage ?? null,
|
|
1264
|
+
cues: locale === 'en-US' ? preset.cuesEn : preset.cues,
|
|
1265
|
+
apply: locale === 'en-US' ? preset.applyEn : preset.apply,
|
|
1266
|
+
avoid: locale === 'en-US' ? preset.avoidEn : preset.avoid,
|
|
1267
|
+
resolution: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1165
1270
|
function recommendComponent(args) {
|
|
1166
1271
|
const locale = resolveLocale(args.mode);
|
|
1167
1272
|
if (!args.query && !args.decision) {
|
|
@@ -1380,6 +1485,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1380
1485
|
'get_pattern',
|
|
1381
1486
|
'recommend_page',
|
|
1382
1487
|
'get_design_rules',
|
|
1488
|
+
'list_style_presets',
|
|
1489
|
+
'get_style_preset',
|
|
1383
1490
|
'recommend_component',
|
|
1384
1491
|
'list_golden_pages',
|
|
1385
1492
|
'get_golden_page',
|
|
@@ -1403,6 +1510,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1403
1510
|
getPattern,
|
|
1404
1511
|
recommendPage,
|
|
1405
1512
|
getDesignRules,
|
|
1513
|
+
listStylePresets,
|
|
1514
|
+
getStylePreset,
|
|
1406
1515
|
recommendComponent,
|
|
1407
1516
|
listGoldenPageCatalog,
|
|
1408
1517
|
getGoldenPage,
|