Skip to content

Commit 90c77c4

Browse files
committed
feat: add base resource tools
1 parent 1109de0 commit 90c77c4

File tree

17 files changed

+2311
-49
lines changed

17 files changed

+2311
-49
lines changed

packages/canvas/DesignCanvas/src/mcp/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
changeNodeProps,
88
selectSpecificNode
99
} from './tools'
10+
import resourcesExport from './resources'
1011

1112
export default {
12-
tools: [getCurrentSelectedNode, getPageSchema, queryNodeById, delNode, addNode, changeNodeProps, selectSpecificNode]
13+
tools: [getCurrentSelectedNode, getPageSchema, queryNodeById, delNode, addNode, changeNodeProps, selectSpecificNode],
14+
resources: resourcesExport.resources,
15+
resourceTemplates: resourcesExport.resourceTemplates
1316
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
## State 示例
2+
3+
最小可行的 `add/update/remove`
4+
5+
```json
6+
{
7+
"section": "state",
8+
"strategy": "merge",
9+
"payload": {
10+
"add": { "companyName": "" },
11+
"update": { "buttons": [{ "type": "primary", "text": "主要操作" }] },
12+
"remove": ["deprecatedKey"]
13+
}
14+
}
15+
```
16+
17+
`accessor`(getter/setter)与 `computed`
18+
19+
```json
20+
{
21+
"section": "state",
22+
"strategy": "merge",
23+
"payload": {
24+
"add": {
25+
"fullName": {
26+
"defaultValue": "",
27+
"accessor": {
28+
"getter": { "type": "JSFunction", "value": "function getter(){ this.state.fullName = `${this.props.firstName} ${this.props.lastName}` }" },
29+
"setter": { "type": "JSFunction", "value": "function setter(){ const [firstName,lastName] = this.state.fullName.split(' '); this.emit('update:firstName', firstName); this.emit('update:lastName', lastName) }" }
30+
}
31+
}
32+
},
33+
"update": { "status": { "type": "JSExpression", "value": "this.statusData", "computed": true } }
34+
}
35+
}
36+
```
37+
38+
## CSS 示例
39+
40+
追加与覆盖:
41+
42+
```json
43+
{ "section": "css", "strategy": "merge", "payload": { "css": ".page-base-style{ padding:24px; }" } }
44+
{ "section": "css", "strategy": "replace", "payload": { "css": ".page-base-style{ margin:16px; }" } }
45+
```
46+
47+
## LifeCycles 示例
48+
49+
```json
50+
{
51+
"section": "lifeCycles",
52+
"strategy": "merge",
53+
"payload": {
54+
"add": {
55+
"setup": { "type": "JSFunction", "value": "function({ props, watch, onMounted }){ onMounted(()=>{ this.getTableData && this.getTableData() }) }" }
56+
}
57+
}
58+
}
59+
```
60+
61+
## Methods 示例
62+
63+
```json
64+
{
65+
"section": "methods",
66+
"strategy": "merge",
67+
"payload": {
68+
"add": {
69+
"handleSearch": { "type": "JSFunction", "value": "function(e){ return ['搜索:', this.i18n('operation.search'), e] }" }
70+
}
71+
}
72+
}
73+
```
74+
75+
## 整页 Schema 示例
76+
77+
合并(仅顶层允许键):
78+
79+
```json
80+
{
81+
"section": "schema",
82+
"strategy": "merge",
83+
"payload": {
84+
"schema": { "fileName": "FormTable", "props": { "className": "page" } }
85+
}
86+
}
87+
```
88+
89+
替换(高风险):
90+
91+
```json
92+
{
93+
"section": "schema",
94+
"strategy": "replace",
95+
"payload": {
96+
"schema": { "componentName": "Page", "fileName": "NewPage", "children": [] }
97+
}
98+
}
99+
```
100+
101+
## 出码示例
102+
103+
详见设计器示例(参考 `tools/example.md` 对应片段)。
104+
105+
## Do & Don’t
106+
107+
- Do:在不确定结构时先调用 `get_page_schema` 再决定策略。
108+
- Do:对 CSS 小步追加,避免一次性覆盖。
109+
- Don’t:将 `state` 写成数组;应为对象(map)。
110+
- Don’t:给 `lifeCycles/methods` 传非 `JSFunction` 单元。
111+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import editExamplesMd from './editPageSchemaExample.md?raw'
2+
import { pickSectionByHeading } from './utils'
3+
4+
// 分节枚举与标题映射(左侧为模板入参 section,右侧为文档中的中文/英文二级标题)
5+
const EDIT_EXAMPLE_SECTION_TITLES: Record<string, string> = {
6+
state: 'State 示例',
7+
css: 'CSS 示例',
8+
lifeCycles: 'LifeCycles 示例',
9+
methods: 'Methods 示例',
10+
schema: '整页 Schema 示例',
11+
codegen: '出码示例',
12+
'do-dont': 'Do & Don’t'
13+
}
14+
15+
// 根资源:编辑页面 Schema 的示例(整份文档)
16+
export const editExamplesResources = [
17+
{
18+
uri: 'tinyengine://docs/edit-page-schema-examples',
19+
name: 'edit-page-schema-examples',
20+
title: '编辑页面 Schema 的示例',
21+
description: '围绕 edit_page_schema 工具的结构化示例与注意事项',
22+
mimeType: 'text/markdown',
23+
annotations: { audience: ['assistant'], priority: 0.85 },
24+
readCallback: async () => ({
25+
contents: [
26+
{
27+
uri: 'tinyengine://docs/edit-page-schema-examples',
28+
name: 'edit-page-schema-examples.md',
29+
title: '编辑页面 Schema 的示例',
30+
mimeType: 'text/markdown',
31+
text: editExamplesMd
32+
}
33+
]
34+
})
35+
}
36+
]
37+
38+
// 模板资源:编辑页面 Schema 的示例(分节读取)
39+
export const editExamplesResourceTemplates = [
40+
{
41+
uriTemplate: 'tinyengine://docs/edit-page-schema-examples/{section}',
42+
name: '编辑页面 Schema 的示例(分节)',
43+
title: '编辑页面 Schema 的示例(分节)',
44+
description: '按章节读取 edit_page_schema 的示例',
45+
mimeType: 'text/markdown',
46+
annotations: { audience: ['assistant'], priority: 0.85 },
47+
variables: [
48+
{
49+
name: 'section',
50+
required: true,
51+
type: 'enum',
52+
enumValues: Object.keys(EDIT_EXAMPLE_SECTION_TITLES).map((key) => ({
53+
value: key,
54+
title: EDIT_EXAMPLE_SECTION_TITLES[key]
55+
}))
56+
}
57+
],
58+
readTemplateCallback: async (_uri: URL, variables: Record<string, string>) => {
59+
const section = (variables?.section || '').toString()
60+
const heading = EDIT_EXAMPLE_SECTION_TITLES[section]
61+
if (!heading) {
62+
throw new Error('Invalid template parameter: section')
63+
}
64+
const text = pickSectionByHeading(editExamplesMd, heading)
65+
return {
66+
contents: [
67+
{
68+
uri: `tinyengine://docs/edit-page-schema-examples/${section}`,
69+
name: `edit-page-schema-examples-${section}.md`,
70+
title: `编辑页面 Schema 的示例 - ${heading}`,
71+
mimeType: 'text/markdown',
72+
text
73+
}
74+
]
75+
}
76+
}
77+
}
78+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { pageSchemaResources, pageSchemaResourceTemplates } from './pageSchemaProtocol'
2+
import { editExamplesResources, editExamplesResourceTemplates } from './editPageSchemaExample'
3+
4+
export const resources = [...pageSchemaResources, ...editExamplesResources]
5+
6+
export const resourceTemplates = [...pageSchemaResourceTemplates, ...editExamplesResourceTemplates]
7+
8+
export default { resources, resourceTemplates }
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
## 概览
2+
3+
页面 schema 是 TinyEngine 在画布中表示页面/区块结构与行为的数据模型。其根节点(RootNode)涵盖样式、属性、状态、方法、生命周期、数据源与子节点树等信息。
4+
5+
本协议文档用于帮助模型准确理解字段语义与约束,指导正确调用 `edit_page_schema` 工具进行编辑。
6+
7+
## 字段
8+
9+
```typescript
10+
export interface IFuncType {
11+
type: 'JSFunction'
12+
value: string
13+
}
14+
15+
// 关键字段(简化展示,与 TinyEngine 运行时 RootNode 结构一致):
16+
export interface RootNode {
17+
id?: string
18+
componentName: string // 'Page' | 'Block' | 其他组件名
19+
fileName?: string
20+
css?: string
21+
props?: Record<string, any>
22+
state?: Record<string, any> // 顶层键-值映射
23+
lifeCycles?: Record<string, IFuncType> // { setup?: IFuncType, onMounted?: IFuncType, ... }
24+
methods?: Record<string, IFuncType>
25+
dataSource?: any
26+
children?: Array<Node>
27+
schema?: any
28+
}
29+
```
30+
31+
说明:
32+
- `state` 为对象(map),键是状态名,值可以是字面量或特殊结构(如 `JSResource``JSExpression``computed``accessor`)。`edit_page_schema``merge` 策略对其执行“顶层键”级别的新增、更新与删除。
33+
- `lifeCycles``methods` 的值必须是函数单元:`{ type: 'JSFunction', value: string }`
34+
- `css` 为整页样式字符串,`merge` 策略为“末尾追加”;`replace` 为整体覆盖。
35+
- `children` 是可嵌套的节点树,涉及精细化增删改应使用“节点类工具”(如 `add_node``change_node_props` 等),而非本工具的 `schema` 合并。
36+
37+
## State
38+
39+
- 结构:`Record<string, any>`
40+
- 允许值:字面量、`JSExpression``JSResource``computed``accessor``getter`/`setter`)。
41+
- `merge` 策略:
42+
- `add`:仅在键不存在时新增;
43+
- `update`:仅在键已存在时更新;
44+
- `remove`:删除指定顶层键;
45+
- 不会进行深层递归合并,聚焦于“顶层键”。
46+
47+
示例(节选):
48+
49+
```json
50+
{
51+
"section": "state",
52+
"strategy": "merge",
53+
"payload": {
54+
"add": {
55+
"companyName": "",
56+
"theme": {
57+
"type": "JSExpression",
58+
"value": "props.dark ? 'dark' : 'light'"
59+
}
60+
},
61+
"update": {
62+
"buttons": [{ "type": "primary", "text": "主要操作" }]
63+
},
64+
"remove": ["deprecatedKey"]
65+
}
66+
}
67+
```
68+
69+
## CSS
70+
71+
- `merge`:在原有 CSS 文本末尾追加新片段(必要时自动换行)。
72+
- `replace`:整体覆盖。
73+
74+
示例:
75+
76+
```json
77+
{
78+
"section": "css",
79+
"strategy": "merge",
80+
"payload": {
81+
"css": ".page-base-style{ padding:24px; }"
82+
}
83+
}
84+
```
85+
86+
## LifeCycles
87+
88+
- 结构:`Record<string, IFuncType>`,例如:`setup``onBeforeMount``onMounted` 等。
89+
- 函数单元格式:`{ type: 'JSFunction', value: string }`
90+
- `merge`
91+
- `add`:键不存在时新增;
92+
- `update`:键存在时替换;
93+
- `remove`:删除指定键;
94+
- `replace`:以 `all` 重建或用 `add+update` 重建。
95+
96+
示例:
97+
98+
```json
99+
{
100+
"section": "lifeCycles",
101+
"strategy": "merge",
102+
"payload": {
103+
"add": {
104+
"onMounted": {
105+
"type": "JSFunction",
106+
"value": "function onMounted(){ this.getTableData && this.getTableData() }"
107+
}
108+
}
109+
}
110+
}
111+
```
112+
113+
## Methods
114+
115+
- 结构:`Record<string, IFuncType>``IFuncType` 同上。
116+
- `merge/replace` 行为与 `lifeCycles` 相同。
117+
118+
示例:
119+
120+
```json
121+
{
122+
"section": "methods",
123+
"strategy": "merge",
124+
"payload": {
125+
"add": {
126+
"handleSearch": {
127+
"type": "JSFunction",
128+
"value": "function(e){ return ['搜索:', this.i18n('operation.search'), e] }"
129+
}
130+
}
131+
}
132+
}
133+
```
134+
135+
## Children
136+
137+
`children` 为节点树,元素形如:
138+
139+
```typescript
140+
export interface Node {
141+
id: string
142+
componentName: string
143+
props: Record<string, any>
144+
children?: Node[]
145+
componentType?: 'Block' | 'PageStart' | 'PageSection'
146+
slot?: string | Record<string, any>
147+
loop?: Record<string, any>
148+
loopArgs?: string[]
149+
condition?: boolean | Record<string, any>
150+
}
151+
```
152+
153+
本工具不负责 `children` 的细粒度结构编辑。请优先使用节点类工具(如 `add_node``change_node_props``del_node` 等)。
154+
155+
## Schema 合并策略
156+
157+
`section = 'schema'``strategy = 'merge'` 时,仅支持对“顶层允许键”进行浅层更新:
158+
159+
允许键清单:`css``lifeCycles``methods``state``props``fileName``componentName``dataSource``children`
160+
161+
注意:
162+
- 未在允许清单内的键将被忽略;
163+
- `children` 的细化变更请改用节点类工具;
164+
- `strategy = 'replace'` 会整体替换页面 schema,具有破坏性,须谨慎。
165+
166+
## 常见陷阱
167+
168+
1.`state` 写成数组。应为对象(map)。
169+
2. `lifeCycles/methods` 未按 `JSFunction` 单元传入,或传入空字符串。
170+
3. 误用 `schema.merge` 期望深层合并。实际上仅“顶层允许键”的浅合并。
171+
4. 追加 CSS 时忘记换行导致样式黏连。
172+
5. 使用 `replace` 且未提前读取现有结构,轻易覆盖重要字段。
173+
174+
## FAQ
175+
176+
Q:如何确认当前页面结构?
177+
A:先调用 `get_page_schema` 工具查看再决定编辑策略。
178+
179+
Q:为什么我的 `lifeCycles.update` 不生效?
180+
A:仅对已存在的键有效;若键不存在,请使用 `add` 或在 `replace` 模式下通过 `all` 重建。
181+
182+
Q:是否支持按组件粒度更新 `children`
183+
A:请改用节点类工具,如 `add_node``change_node_props``del_node` 等。

0 commit comments

Comments
 (0)