@shun-js/aibaiban-server 1.2.2 → 1.2.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/package.json +2 -2
- package/server/service/LLMService.js +168 -1
- package/server/util/prompt-agent.js +206 -5
- package/views/index.html +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shun-js/aibaiban-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "aibaiban.com server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai aibaiban"
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"access": "public",
|
|
46
46
|
"registry": "https://registry.npmjs.org/"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "d9255a37dbd25bf32a5c64b0230440eb34c6de2d"
|
|
49
49
|
}
|
|
@@ -12,9 +12,139 @@ const llm = OpenAIAPI(finalLLMConfig);
|
|
|
12
12
|
const modelName = finalLLMConfig.modelName;
|
|
13
13
|
const thinking = finalLLMConfig.thinking;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* convertToolCallToSkeleton - 将单个 tool call 转为 ExcalidrawElementSkeleton
|
|
17
|
+
*/
|
|
18
|
+
function convertToolCallToSkeleton(name, args) {
|
|
19
|
+
if (name === 'draw_shape') {
|
|
20
|
+
const skeleton = {
|
|
21
|
+
type: args.shape || 'rectangle',
|
|
22
|
+
x: args.x || 0,
|
|
23
|
+
y: args.y || 0,
|
|
24
|
+
};
|
|
25
|
+
if (args.id) skeleton.id = args.id;
|
|
26
|
+
if (args.width) skeleton.width = args.width;
|
|
27
|
+
if (args.height) skeleton.height = args.height;
|
|
28
|
+
if (args.label) skeleton.label = { text: args.label };
|
|
29
|
+
if (args.strokeColor) skeleton.strokeColor = args.strokeColor;
|
|
30
|
+
if (args.backgroundColor) skeleton.backgroundColor = args.backgroundColor;
|
|
31
|
+
if (args.fillStyle) skeleton.fillStyle = args.fillStyle;
|
|
32
|
+
if (args.strokeWidth) skeleton.strokeWidth = args.strokeWidth;
|
|
33
|
+
if (args.strokeStyle) skeleton.strokeStyle = args.strokeStyle;
|
|
34
|
+
if (args.roughness !== undefined) skeleton.roughness = args.roughness;
|
|
35
|
+
if (args.opacity !== undefined) skeleton.opacity = args.opacity;
|
|
36
|
+
if (args.roundness !== undefined) {
|
|
37
|
+
skeleton.roundness = args.roundness ? { type: 3 } : null;
|
|
38
|
+
}
|
|
39
|
+
return [skeleton];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (name === 'draw_arrow') {
|
|
43
|
+
const skeleton = { type: 'arrow' };
|
|
44
|
+
// 坐标:优先用起点坐标,否则默认 0
|
|
45
|
+
skeleton.x = args.startX || 0;
|
|
46
|
+
skeleton.y = args.startY || 0;
|
|
47
|
+
// 绑定
|
|
48
|
+
if (args.startId) skeleton.start = { id: args.startId };
|
|
49
|
+
if (args.endId) skeleton.end = { id: args.endId };
|
|
50
|
+
// 标签
|
|
51
|
+
if (args.label) skeleton.label = { text: args.label };
|
|
52
|
+
// 箭头样式
|
|
53
|
+
if (args.startArrowhead) skeleton.startArrowhead = args.startArrowhead;
|
|
54
|
+
if (args.endArrowhead !== undefined) skeleton.endArrowhead = args.endArrowhead;
|
|
55
|
+
// 如果没有绑定,用坐标计算 points
|
|
56
|
+
if (!args.startId && !args.endId && args.endX !== undefined && args.endY !== undefined) {
|
|
57
|
+
skeleton.width = args.endX - skeleton.x;
|
|
58
|
+
skeleton.height = args.endY - skeleton.y;
|
|
59
|
+
}
|
|
60
|
+
// 通用样式
|
|
61
|
+
if (args.strokeColor) skeleton.strokeColor = args.strokeColor;
|
|
62
|
+
if (args.strokeWidth) skeleton.strokeWidth = args.strokeWidth;
|
|
63
|
+
if (args.strokeStyle) skeleton.strokeStyle = args.strokeStyle;
|
|
64
|
+
if (args.elbowed) skeleton.elbowed = true;
|
|
65
|
+
return [skeleton];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (name === 'draw_line') {
|
|
69
|
+
if (!args.points || args.points.length < 2) return [];
|
|
70
|
+
const skeleton = {
|
|
71
|
+
type: 'line',
|
|
72
|
+
x: args.points[0][0] || 0,
|
|
73
|
+
y: args.points[0][1] || 0,
|
|
74
|
+
points: args.points.map((p) => [p[0] - (args.points[0][0] || 0), p[1] - (args.points[0][1] || 0)]),
|
|
75
|
+
};
|
|
76
|
+
if (args.strokeColor) skeleton.strokeColor = args.strokeColor;
|
|
77
|
+
if (args.strokeWidth) skeleton.strokeWidth = args.strokeWidth;
|
|
78
|
+
if (args.strokeStyle) skeleton.strokeStyle = args.strokeStyle;
|
|
79
|
+
return [skeleton];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (name === 'draw_text') {
|
|
83
|
+
const skeleton = {
|
|
84
|
+
type: 'text',
|
|
85
|
+
x: args.x || 0,
|
|
86
|
+
y: args.y || 0,
|
|
87
|
+
text: args.text || '',
|
|
88
|
+
};
|
|
89
|
+
if (args.id) skeleton.id = args.id;
|
|
90
|
+
if (args.fontSize) skeleton.fontSize = args.fontSize;
|
|
91
|
+
if (args.fontFamily) skeleton.fontFamily = args.fontFamily;
|
|
92
|
+
if (args.textAlign) skeleton.textAlign = args.textAlign;
|
|
93
|
+
if (args.strokeColor) skeleton.strokeColor = args.strokeColor;
|
|
94
|
+
return [skeleton];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (name === 'draw_frame') {
|
|
98
|
+
const skeleton = {
|
|
99
|
+
type: 'frame',
|
|
100
|
+
children: args.childIds || [],
|
|
101
|
+
};
|
|
102
|
+
if (args.name) skeleton.name = args.name;
|
|
103
|
+
if (args.x !== undefined) skeleton.x = args.x;
|
|
104
|
+
if (args.y !== undefined) skeleton.y = args.y;
|
|
105
|
+
if (args.width) skeleton.width = args.width;
|
|
106
|
+
if (args.height) skeleton.height = args.height;
|
|
107
|
+
return [skeleton];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* convertToolCallsToSkeletons - 将所有 tool calls 转为 skeleton 数组
|
|
115
|
+
*/
|
|
116
|
+
function convertToolCallsToSkeletons(toolCalls) {
|
|
117
|
+
const skeletons = [];
|
|
118
|
+
for (const tc of toolCalls) {
|
|
119
|
+
if (tc.function.name === 'clear_canvas') continue;
|
|
120
|
+
try {
|
|
121
|
+
const args = JSON.parse(tc.function.arguments);
|
|
122
|
+
if (tc.function.name === 'draw_group') {
|
|
123
|
+
// draw_group: 递归转换子元素,统一加 groupIds
|
|
124
|
+
const groupId = 'group_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);
|
|
125
|
+
const children = args.elements || [];
|
|
126
|
+
for (const child of children) {
|
|
127
|
+
const toolName = 'draw_' + child.tool;
|
|
128
|
+
const childSkeletons = convertToolCallToSkeleton(toolName, child.args || {});
|
|
129
|
+
for (const s of childSkeletons) {
|
|
130
|
+
s.groupIds = [groupId];
|
|
131
|
+
skeletons.push(s);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
const result = convertToolCallToSkeleton(tc.function.name, args);
|
|
136
|
+
skeletons.push(...result);
|
|
137
|
+
}
|
|
138
|
+
} catch (e) {
|
|
139
|
+
// JSON 解析失败,跳过该 tool call
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return skeletons;
|
|
143
|
+
}
|
|
144
|
+
|
|
15
145
|
/**
|
|
16
146
|
* drawAgent - 对话式 Agent
|
|
17
|
-
* 1 次决策 LLM → reply / generate(elaborate+generate) / irrelevant
|
|
147
|
+
* 1 次决策 LLM → reply / generate(elaborate+generate) / canvas / irrelevant
|
|
18
148
|
*/
|
|
19
149
|
exports.drawAgent = async (req, res) => {
|
|
20
150
|
const methodName = 'drawAgent';
|
|
@@ -134,6 +264,43 @@ exports.drawAgent = async (req, res) => {
|
|
|
134
264
|
},
|
|
135
265
|
},
|
|
136
266
|
]);
|
|
267
|
+
} else if (decision.action === 'canvas') {
|
|
268
|
+
// 3. Canvas - Function Calling 直接画图
|
|
269
|
+
res.streaming(`data: ${JSON.stringify({ type: 'status', step: 'drawing' })}\n\n`);
|
|
270
|
+
req.logger.info(methodName, 'step: canvas function calling');
|
|
271
|
+
|
|
272
|
+
const canvasResponse = await llm.chat({
|
|
273
|
+
model: modelName,
|
|
274
|
+
messages: [
|
|
275
|
+
{ role: 'system', content: prompts.CANVAS_SYSTEM_PROMPT },
|
|
276
|
+
{ role: 'user', content: decision.description },
|
|
277
|
+
],
|
|
278
|
+
tools: prompts.CANVAS_TOOLS,
|
|
279
|
+
tool_choice: 'auto',
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (canvasResponse.tool_calls?.length) {
|
|
283
|
+
req.logger.info(methodName, 'tool_calls', canvasResponse.tool_calls.map((tc) => tc.function.name).join(', '));
|
|
284
|
+
|
|
285
|
+
// 检查是否有 clear_canvas
|
|
286
|
+
const hasClear = canvasResponse.tool_calls.some((tc) => tc.function.name === 'clear_canvas');
|
|
287
|
+
if (hasClear) {
|
|
288
|
+
chatFeishuMsg(req, 'canvas-clear');
|
|
289
|
+
res.streaming(`data: ${JSON.stringify({ type: 'clear' })}\n\n`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// 转换为 skeletons
|
|
293
|
+
const skeletons = convertToolCallsToSkeletons(canvasResponse.tool_calls);
|
|
294
|
+
if (skeletons.length > 0) {
|
|
295
|
+
const duration = Date.now() - startTime;
|
|
296
|
+
req.logger.info(methodName, 'canvas elements', skeletons.length, `${duration}ms`);
|
|
297
|
+
chatFeishuMsg(req, `canvas-${skeletons.length}-elements`);
|
|
298
|
+
res.streaming(`data: ${JSON.stringify({ type: 'draw', elements: skeletons, duration })}\n\n`);
|
|
299
|
+
}
|
|
300
|
+
} else if (canvasResponse.content) {
|
|
301
|
+
// LLM 选择文字回复而非调用函数
|
|
302
|
+
res.streaming(`data: ${JSON.stringify({ type: 'message', content: canvasResponse.content })}\n\n`);
|
|
303
|
+
}
|
|
137
304
|
} else {
|
|
138
305
|
// 未知 action fallback
|
|
139
306
|
res.streaming(
|
|
@@ -7,10 +7,11 @@ module.exports = {
|
|
|
7
7
|
* Agent 决策 prompt
|
|
8
8
|
* 一次 LLM 调用完成意图判断 + 决策
|
|
9
9
|
*/
|
|
10
|
-
AGENT_PROMPT: `你是 AI
|
|
10
|
+
AGENT_PROMPT: `你是 AI 白板助手,帮用户在白板上画图表和图形。
|
|
11
11
|
|
|
12
12
|
## 能力
|
|
13
|
-
支持 4 种图表:flowchart(流程图)、sequence(时序图)、classDiagram(类图)、erDiagram(ER图)。
|
|
13
|
+
1. 支持 4 种图表:flowchart(流程图)、sequence(时序图)、classDiagram(类图)、erDiagram(ER图)。
|
|
14
|
+
2. 支持直接在白板上绘制形状、文字、箭头等基础图形。
|
|
14
15
|
|
|
15
16
|
## 决策规则
|
|
16
17
|
收到用户消息后判断,只回复 JSON:
|
|
@@ -18,10 +19,13 @@ module.exports = {
|
|
|
18
19
|
1. 回复文字(追问、确认、引导):
|
|
19
20
|
{"action":"reply","message":"你的回复内容"}
|
|
20
21
|
|
|
21
|
-
2.
|
|
22
|
+
2. 生成图表(用户要画流程图/时序图/类图/ER图,信息已足够明确):
|
|
22
23
|
{"action":"generate","diagramType":"类型","description":"详细描述,包含所有节点、关系、步骤"}
|
|
23
24
|
|
|
24
|
-
3.
|
|
25
|
+
3. 直接画图(用户要求画简单形状、文字、箭头等,不属于四种图表):
|
|
26
|
+
{"action":"canvas","description":"用户想画什么的完整描述"}
|
|
27
|
+
|
|
28
|
+
4. 与画图完全无关:
|
|
25
29
|
{"action":"irrelevant"}
|
|
26
30
|
|
|
27
31
|
## 判断标准
|
|
@@ -30,6 +34,8 @@ module.exports = {
|
|
|
30
34
|
- 用户只说了内容没说类型 → 你来判断最合适的类型,直接 generate
|
|
31
35
|
- 用户说"帮我画个图" → reply 追问想画什么
|
|
32
36
|
- 用户要修改已有图表 → 根据对话历史理解上下文,generate 完整新图表
|
|
37
|
+
- 用户要画简单形状(圆、矩形、箭头、文字等)→ canvas
|
|
38
|
+
- 用户说"清空白板" → canvas
|
|
33
39
|
- 用户闲聊但话题相关 → reply 自然回应并引导回画图
|
|
34
40
|
- 用户闲聊话题无关 → irrelevant
|
|
35
41
|
|
|
@@ -37,6 +43,7 @@ module.exports = {
|
|
|
37
43
|
- 追问时友好自然,像朋友聊天,不要生硬
|
|
38
44
|
- 可以给建议和示例帮助用户想清楚需求
|
|
39
45
|
- generate 时 description 要尽可能详细完整
|
|
46
|
+
- canvas 时 description 要包含形状、颜色、位置等用户提到的所有细节
|
|
40
47
|
|
|
41
48
|
只回复 JSON。`,
|
|
42
49
|
|
|
@@ -101,5 +108,199 @@ flowchart TD
|
|
|
101
108
|
* 非白板请求的固定回复
|
|
102
109
|
*/
|
|
103
110
|
FIXED_REPLY:
|
|
104
|
-
'你好!👋 我是 AI
|
|
111
|
+
'你好!👋 我是 AI 白板助手,支持以下功能:\n\n📊 流程图 — 例如"画一个用户注册登录的流程图"\n🔄 时序图 — 例如"画一个用户下单支付的时序图"\n🏗️ 类图 — 例如"画一个电商系统的类图"\n🗄️ ER图 — 例如"画一个博客系统的ER图"\n🎨 自由绘图 — 例如"画一个红色的圆"、"画两个矩形用箭头连接"\n\n💡 试试直接输入你的需求吧!',
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Canvas Function Calling 系统提示词
|
|
115
|
+
*/
|
|
116
|
+
CANVAS_SYSTEM_PROMPT: `你是 AI 白板助手,帮用户在白板上直接绘制形状、文字和箭头。
|
|
117
|
+
|
|
118
|
+
## 坐标系
|
|
119
|
+
- 画布从左上角 (0, 0) 开始,x 向右增长,y 向下增长
|
|
120
|
+
- 建议将元素放在 (100-800, 100-600) 的范围内
|
|
121
|
+
- 默认形状大小约 150x80
|
|
122
|
+
|
|
123
|
+
## 颜色建议
|
|
124
|
+
- 红: #ff6b6b 青: #4ecdc4 蓝: #45b7d1 黄: #ffeaa7 深蓝: #1971c2
|
|
125
|
+
- 绿: #51cf66 紫: #9775fa 橙: #ff922b 粉: #f06595 灰: #868e96
|
|
126
|
+
|
|
127
|
+
## 注意事项
|
|
128
|
+
- 圆形用 draw_shape 的 ellipse 类型,宽高相等即可
|
|
129
|
+
- 需要箭头连接的形状必须指定 id
|
|
130
|
+
- 多个相关元素建议用 draw_group 编组
|
|
131
|
+
- 如果用户没指定位置/颜色/大小,选择合理的默认值
|
|
132
|
+
- 形状内文字通过 label 参数设置`,
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Canvas Function Calling 工具定义
|
|
136
|
+
*/
|
|
137
|
+
CANVAS_TOOLS: [
|
|
138
|
+
{
|
|
139
|
+
type: 'function',
|
|
140
|
+
function: {
|
|
141
|
+
name: 'draw_shape',
|
|
142
|
+
description: '在白板上绘制基础形状(矩形、椭圆、菱形),可带文字标签。需要被箭头连接时必须指定 id。',
|
|
143
|
+
parameters: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
id: { type: 'string', description: '元素ID,箭头绑定时需要引用' },
|
|
147
|
+
shape: { type: 'string', enum: ['rectangle', 'ellipse', 'diamond'] },
|
|
148
|
+
x: { type: 'number', description: '左上角X坐标' },
|
|
149
|
+
y: { type: 'number', description: '左上角Y坐标' },
|
|
150
|
+
width: { type: 'number', description: '宽度,默认100' },
|
|
151
|
+
height: { type: 'number', description: '高度,默认100' },
|
|
152
|
+
label: { type: 'string', description: '形状内的文字标签' },
|
|
153
|
+
strokeColor: { type: 'string', description: '边框颜色,默认#1e1e1e' },
|
|
154
|
+
backgroundColor: { type: 'string', description: '填充颜色,默认transparent' },
|
|
155
|
+
fillStyle: { type: 'string', enum: ['hachure', 'cross-hatch', 'solid', 'zigzag'] },
|
|
156
|
+
strokeWidth: { type: 'number', enum: [1, 2, 4], description: '1=细,2=中,4=粗' },
|
|
157
|
+
strokeStyle: { type: 'string', enum: ['solid', 'dashed', 'dotted'] },
|
|
158
|
+
roughness: { type: 'number', enum: [0, 1, 2], description: '0=精确,1=手绘,2=夸张' },
|
|
159
|
+
roundness: { type: 'boolean', description: '是否圆角,默认true' },
|
|
160
|
+
opacity: { type: 'number', description: '不透明度0-100,默认100' },
|
|
161
|
+
},
|
|
162
|
+
required: ['shape', 'x', 'y'],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'function',
|
|
168
|
+
function: {
|
|
169
|
+
name: 'draw_arrow',
|
|
170
|
+
description: '绘制箭头。可通过 startId/endId 绑定到已有形状(箭头自动吸附),也可直接指定坐标点。',
|
|
171
|
+
parameters: {
|
|
172
|
+
type: 'object',
|
|
173
|
+
properties: {
|
|
174
|
+
startX: { type: 'number', description: '起点X(无startId时必填)' },
|
|
175
|
+
startY: { type: 'number', description: '起点Y(无startId时必填)' },
|
|
176
|
+
endX: { type: 'number', description: '终点X(无endId时必填)' },
|
|
177
|
+
endY: { type: 'number', description: '终点Y(无endId时必填)' },
|
|
178
|
+
startId: { type: 'string', description: '起点绑定的形状ID' },
|
|
179
|
+
endId: { type: 'string', description: '终点绑定的形状ID' },
|
|
180
|
+
label: { type: 'string', description: '箭头上的文字标签' },
|
|
181
|
+
startArrowhead: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
enum: ['arrow', 'bar', 'dot', 'triangle', 'diamond'],
|
|
184
|
+
description: '起点箭头样式,默认无',
|
|
185
|
+
},
|
|
186
|
+
endArrowhead: {
|
|
187
|
+
type: 'string',
|
|
188
|
+
enum: ['arrow', 'bar', 'dot', 'triangle', 'diamond'],
|
|
189
|
+
description: '终点箭头样式,默认arrow',
|
|
190
|
+
},
|
|
191
|
+
strokeColor: { type: 'string' },
|
|
192
|
+
strokeWidth: { type: 'number', enum: [1, 2, 4] },
|
|
193
|
+
strokeStyle: { type: 'string', enum: ['solid', 'dashed', 'dotted'] },
|
|
194
|
+
elbowed: { type: 'boolean', description: '是否使用直角折线,默认false' },
|
|
195
|
+
},
|
|
196
|
+
required: [],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
type: 'function',
|
|
202
|
+
function: {
|
|
203
|
+
name: 'draw_line',
|
|
204
|
+
description: '绘制线条(无箭头),支持多个折点',
|
|
205
|
+
parameters: {
|
|
206
|
+
type: 'object',
|
|
207
|
+
properties: {
|
|
208
|
+
points: {
|
|
209
|
+
type: 'array',
|
|
210
|
+
items: { type: 'array', items: { type: 'number' } },
|
|
211
|
+
description: '折点坐标数组,如 [[0,0],[100,50],[200,0]]',
|
|
212
|
+
},
|
|
213
|
+
strokeColor: { type: 'string' },
|
|
214
|
+
strokeWidth: { type: 'number', enum: [1, 2, 4] },
|
|
215
|
+
strokeStyle: { type: 'string', enum: ['solid', 'dashed', 'dotted'] },
|
|
216
|
+
},
|
|
217
|
+
required: ['points'],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
type: 'function',
|
|
223
|
+
function: {
|
|
224
|
+
name: 'draw_text',
|
|
225
|
+
description: '在白板上添加独立文本',
|
|
226
|
+
parameters: {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
id: { type: 'string' },
|
|
230
|
+
x: { type: 'number' },
|
|
231
|
+
y: { type: 'number' },
|
|
232
|
+
text: { type: 'string' },
|
|
233
|
+
fontSize: { type: 'number', description: '字号,默认20' },
|
|
234
|
+
fontFamily: {
|
|
235
|
+
type: 'number',
|
|
236
|
+
enum: [1, 2, 3, 5],
|
|
237
|
+
description: '1=手写,2=常规,3=等宽,5=圆体',
|
|
238
|
+
},
|
|
239
|
+
textAlign: { type: 'string', enum: ['left', 'center', 'right'] },
|
|
240
|
+
strokeColor: { type: 'string' },
|
|
241
|
+
},
|
|
242
|
+
required: ['x', 'y', 'text'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
type: 'function',
|
|
248
|
+
function: {
|
|
249
|
+
name: 'draw_group',
|
|
250
|
+
description: '批量创建多个元素并自动编组(移动时一起移动)。适合画一组相关图形。',
|
|
251
|
+
parameters: {
|
|
252
|
+
type: 'object',
|
|
253
|
+
properties: {
|
|
254
|
+
elements: {
|
|
255
|
+
type: 'array',
|
|
256
|
+
description: '元素数组,每个元素格式同其他 draw 工具的参数',
|
|
257
|
+
items: {
|
|
258
|
+
type: 'object',
|
|
259
|
+
properties: {
|
|
260
|
+
tool: { type: 'string', enum: ['shape', 'arrow', 'line', 'text'] },
|
|
261
|
+
args: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
description: '对应 draw_shape/draw_arrow/draw_line/draw_text 的参数',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
required: ['tool', 'args'],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
required: ['elements'],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: 'function',
|
|
276
|
+
function: {
|
|
277
|
+
name: 'draw_frame',
|
|
278
|
+
description: '创建一个 Frame 容器,包含指定的子元素(子元素会被框在一起)',
|
|
279
|
+
parameters: {
|
|
280
|
+
type: 'object',
|
|
281
|
+
properties: {
|
|
282
|
+
name: { type: 'string', description: 'Frame 名称' },
|
|
283
|
+
childIds: { type: 'array', items: { type: 'string' }, description: '子元素ID数组' },
|
|
284
|
+
x: { type: 'number' },
|
|
285
|
+
y: { type: 'number' },
|
|
286
|
+
width: { type: 'number' },
|
|
287
|
+
height: { type: 'number' },
|
|
288
|
+
},
|
|
289
|
+
required: ['childIds'],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
type: 'function',
|
|
295
|
+
function: {
|
|
296
|
+
name: 'clear_canvas',
|
|
297
|
+
description: '清空白板上的所有内容',
|
|
298
|
+
parameters: {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {},
|
|
301
|
+
required: [],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
],
|
|
105
306
|
};
|
package/views/index.html
CHANGED
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
<script
|
|
106
106
|
type="module"
|
|
107
107
|
crossorigin
|
|
108
|
-
src="https://static-small.vincentqiao.com/aibaiban/static/index-
|
|
108
|
+
src="https://static-small.vincentqiao.com/aibaiban/static/index-Fo5F7gnm.js"
|
|
109
109
|
></script>
|
|
110
110
|
<link
|
|
111
111
|
rel="modulepreload"
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
<link
|
|
131
131
|
rel="modulepreload"
|
|
132
132
|
crossorigin
|
|
133
|
-
href="https://static-small.vincentqiao.com/aibaiban/static/chunks/excalidraw-
|
|
133
|
+
href="https://static-small.vincentqiao.com/aibaiban/static/chunks/excalidraw-XjoTMqq7.js"
|
|
134
134
|
/>
|
|
135
135
|
<link
|
|
136
136
|
rel="stylesheet"
|