@pixel-normal-edit/mcp 2.0.7 → 2.0.10
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/domains/art-tree/advanced_lessons.js +11 -11
- package/domains/art-tree/advanced_tools.js +12 -12
- package/domains/art-tree/analysis_lessons.js +32 -0
- package/domains/art-tree/analysis_shapes.js +146 -0
- package/domains/art-tree/analysis_tools.js +49 -0
- package/domains/art-tree/hidden_lessons.js +3 -3
- package/domains/art-tree/index.js +10 -0
- package/domains/art-tree/layer_lessons.js +32 -0
- package/domains/art-tree/layer_shapes.js +117 -0
- package/domains/art-tree/layer_tools.js +43 -0
- package/domains/art-tree/lessons.js +19 -19
- package/domains/art-tree/light_lessons.js +3 -3
- package/domains/art-tree/light_shapes.js +5 -5
- package/domains/art-tree/material_lessons.js +71 -0
- package/domains/art-tree/material_shapes.js +130 -0
- package/domains/art-tree/material_tools.js +49 -0
- package/domains/art-tree/perspective_lessons.js +1 -1
- package/domains/art-tree/perspective_tools.js +1 -1
- package/domains/art-tree/sky_lessons_1.js +82 -0
- package/domains/art-tree/sky_shapes_1.js +68 -0
- package/domains/art-tree/sky_tools.js +51 -0
- package/domains/art-tree/surface_lessons.js +7 -7
- package/domains/art-tree/tools.js +27 -27
- package/domains/art-tree/transform_lessons_1.js +2 -2
- package/domains/art-tree/transform_lessons_2.js +4 -4
- package/domains/art-tree/transform_tools.js +8 -8
- package/domains/art-tree/vocab_lessons.js +32 -0
- package/domains/art-tree/vocab_shapes.js +116 -0
- package/domains/art-tree/vocab_tools.js +41 -0
- package/index.js +4 -0
- package/package.json +1 -1
- package/rules/README.md +403 -0
- package/rules/index.js +34 -0
- package/rules/prerequisites.js +145 -0
- package/rules/rules.js +111 -0
- package/rules/workflow.js +200 -0
- package/tools/{anchors.js → anchor-tools.js} +1 -1
- package/tools/{animation.js → animation-tools.js} +1 -1
- package/tools/{canvas.js → canvas-tools.js} +1 -1
- package/tools/{colors.js → color-tools.js} +1 -1
- package/tools/{drawing.js → drawing-tools.js} +1 -1
- package/tools/{filters.js → filter-tools.js} +1 -1
- package/tools/{health.js → health-tools.js} +1 -1
- package/tools/{history.js → history-tools.js} +1 -1
- package/tools/index.js +31 -13
- package/tools/layer-tools.js +80 -0
- package/tools/{modes.js → mode-tools.js} +1 -1
- package/tools/{query.js → query-tools.js} +1 -1
- package/tools/{region.js → region-tools.js} +1 -1
- package/tools/{sprite.js → sprite-tools.js} +1 -1
- package/tools/{workspace.js → workspace-tools.js} +2 -3
|
@@ -2,7 +2,7 @@ const shapes = require('./shapes');
|
|
|
2
2
|
const surfaceShapes = require('./surface_shapes');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Lesson: Surface Types (
|
|
5
|
+
* Lesson: Surface Types (Flat, convex, concave)
|
|
6
6
|
*/
|
|
7
7
|
function lessonSurfaceTypes(canvasSize = 32, color = '#2196f3') {
|
|
8
8
|
const commands = [];
|
|
@@ -10,20 +10,20 @@ function lessonSurfaceTypes(canvasSize = 32, color = '#2196f3') {
|
|
|
10
10
|
const qX = Math.floor(canvasSize / 4);
|
|
11
11
|
const r = Math.floor(canvasSize * 0.15);
|
|
12
12
|
|
|
13
|
-
// 1. Flat Plane
|
|
13
|
+
// 1. Flat Plane
|
|
14
14
|
const w = Math.floor(canvasSize * 0.2);
|
|
15
15
|
commands.push(shapes.drawRectangle(qX - w/2, midY - w/2, w, w, '#4caf50', false));
|
|
16
16
|
// Add some grid lines to show it's flat
|
|
17
17
|
commands.push(shapes.drawLine(qX, midY - w/2, qX, midY + w/2, '#4caf50'));
|
|
18
18
|
commands.push(shapes.drawLine(qX - w/2, midY, qX + w/2, midY, '#4caf50'));
|
|
19
19
|
|
|
20
|
-
// 2. Convex Surface (
|
|
20
|
+
// 2. Convex Surface (e.g. Dome/Sphere)
|
|
21
21
|
// We use an ellipse that curves outwards (downwards)
|
|
22
22
|
commands.push(shapes.drawEllipse(qX * 2, midY, r, r, '#ff9800', false));
|
|
23
23
|
// Cross contour curving DOWN (convex towards viewer)
|
|
24
24
|
commands.push(shapes.drawEllipse(qX * 2, midY + Math.floor(r*0.2), r, Math.floor(r*0.3), '#ff9800', false));
|
|
25
25
|
|
|
26
|
-
// 3. Concave Surface (
|
|
26
|
+
// 3. Concave Surface (e.g. Bowl)
|
|
27
27
|
// Draw the top rim of a bowl
|
|
28
28
|
commands.push(shapes.drawEllipse(qX * 3, midY, r, Math.floor(r*0.3), '#9c27b0', false));
|
|
29
29
|
// Draw the bottom curve (the inside of the bowl)
|
|
@@ -38,7 +38,7 @@ function lessonSurfaceTypes(canvasSize = 32, color = '#2196f3') {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Lesson: Edge Types (
|
|
41
|
+
* Lesson: Edge Types (Hard sharp corners vs Soft rounded edges)
|
|
42
42
|
*/
|
|
43
43
|
function lessonEdgeTypes(canvasSize = 32, color = '#ff5722') {
|
|
44
44
|
const commands = [];
|
|
@@ -47,10 +47,10 @@ function lessonEdgeTypes(canvasSize = 32, color = '#ff5722') {
|
|
|
47
47
|
const midY = Math.floor(canvasSize / 2);
|
|
48
48
|
const w = Math.floor(canvasSize * 0.25);
|
|
49
49
|
|
|
50
|
-
// 1. Hard Edge (
|
|
50
|
+
// 1. Hard Edge (Sharp corners)
|
|
51
51
|
commands.push(shapes.drawRectangle(qX1 - w/2, midY - w/2, w, w, '#e91e63', false));
|
|
52
52
|
|
|
53
|
-
// 2. Soft Edge / Transitional
|
|
53
|
+
// 2. Soft Edge / Transitional
|
|
54
54
|
// Draw a box with rounded corners (fillet)
|
|
55
55
|
const r = Math.floor(w * 0.2); // Corner radius
|
|
56
56
|
const rx = qX2 - w/2;
|
|
@@ -96,126 +96,126 @@ function register(server) {
|
|
|
96
96
|
// ── Lesson Tools ──────────────────────────────────────────────────────
|
|
97
97
|
|
|
98
98
|
registerTool(server, 'art_tree_lesson_lines',
|
|
99
|
-
'Lesson:
|
|
99
|
+
'Lesson: Lines — Introduction to drawing straight lines',
|
|
100
100
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
101
101
|
color: hexColor.default('#ff0000') },
|
|
102
102
|
async (p) => {
|
|
103
103
|
const commands = lessons.lessonLines(p.canvasSize, p.color);
|
|
104
104
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
105
|
-
return { content: [{ type: 'text', text: `✓
|
|
105
|
+
return { content: [{ type: 'text', text: `✓ Lines lesson completed (${commands.length} strokes)` }] };
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
registerTool(server, 'art_tree_lesson_squares',
|
|
109
|
-
'Lesson:
|
|
109
|
+
'Lesson: Squares & Rectangles — Understanding right angles and proportions',
|
|
110
110
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
111
111
|
color: hexColor.default('#1565c0') },
|
|
112
112
|
async (p) => {
|
|
113
113
|
const commands = lessons.lessonSquares(p.canvasSize, p.color);
|
|
114
114
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
115
|
-
return { content: [{ type: 'text', text: `✓
|
|
115
|
+
return { content: [{ type: 'text', text: `✓ Squares & Rectangles lesson completed (${commands.length} strokes)` }] };
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
registerTool(server, 'art_tree_lesson_circles',
|
|
119
|
-
'Lesson:
|
|
119
|
+
'Lesson: Circles — Learning curves and symmetry',
|
|
120
120
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
121
121
|
color: hexColor.default('#e91e63') },
|
|
122
122
|
async (p) => {
|
|
123
123
|
const commands = lessons.lessonCircles(p.canvasSize, p.color);
|
|
124
124
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
125
|
-
return { content: [{ type: 'text', text: `✓
|
|
125
|
+
return { content: [{ type: 'text', text: `✓ Circles lesson completed (${commands.length} strokes)` }] };
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
registerTool(server, 'art_tree_lesson_ellipses',
|
|
129
|
-
'Lesson:
|
|
129
|
+
'Lesson: Ellipses — Understanding oval shapes and proportions',
|
|
130
130
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
131
131
|
color: hexColor.default('#9c27b0') },
|
|
132
132
|
async (p) => {
|
|
133
133
|
const commands = lessons.lessonEllipses(p.canvasSize, p.color);
|
|
134
134
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
135
|
-
return { content: [{ type: 'text', text: `✓
|
|
135
|
+
return { content: [{ type: 'text', text: `✓ Ellipses lesson completed (${commands.length} strokes)` }] };
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
registerTool(server, 'art_tree_lesson_triangles',
|
|
139
|
-
'Lesson:
|
|
139
|
+
'Lesson: Triangles — Three-point shapes and stability',
|
|
140
140
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
141
141
|
color: hexColor.default('#ff5722') },
|
|
142
142
|
async (p) => {
|
|
143
143
|
const commands = lessons.lessonTriangles(p.canvasSize, p.color);
|
|
144
144
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
145
|
-
return { content: [{ type: 'text', text: `✓
|
|
145
|
+
return { content: [{ type: 'text', text: `✓ Triangles lesson completed (${commands.length} strokes)` }] };
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
registerTool(server, 'art_tree_lesson_polygons',
|
|
149
|
-
'Lesson:
|
|
149
|
+
'Lesson: Polygons — Multi-sided shapes',
|
|
150
150
|
{ canvasSize: z.number().int().min(16).max(64).default(32),
|
|
151
151
|
color: hexColor.default('#607d8b') },
|
|
152
152
|
async (p) => {
|
|
153
153
|
const commands = lessons.lessonPolygons(p.canvasSize, p.color);
|
|
154
154
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
155
|
-
return { content: [{ type: 'text', text: `✓
|
|
155
|
+
return { content: [{ type: 'text', text: `✓ Polygons lesson completed (${commands.length} strokes)` }] };
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
registerTool(server, 'art_tree_lesson_composition',
|
|
159
|
-
'Lesson:
|
|
159
|
+
'Lesson: Composition — Combining basic shapes to create simple objects (house, sun, tree)',
|
|
160
160
|
{ canvasSize: z.number().int().min(16).max(64).default(32) },
|
|
161
161
|
async (p) => {
|
|
162
162
|
const commands = lessons.lessonComposition(p.canvasSize);
|
|
163
163
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
164
|
-
return { content: [{ type: 'text', text: `✓
|
|
164
|
+
return { content: [{ type: 'text', text: `✓ Composition lesson completed (${commands.length} strokes)` }] };
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
registerTool(server, 'art_tree_lesson_freehand',
|
|
168
|
-
'Lesson:
|
|
168
|
+
'Lesson: Freehand Drawing — Practice freehand drawing without rulers',
|
|
169
169
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#795548') },
|
|
170
170
|
async (p) => {
|
|
171
171
|
const commands = lessons.lessonFreehand(p.canvasSize, p.color);
|
|
172
172
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
173
|
-
return { content: [{ type: 'text', text: `✓
|
|
173
|
+
return { content: [{ type: 'text', text: `✓ Freehand drawing lesson completed (${commands.length} small segments)` }] };
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
registerTool(server, 'art_tree_lesson_proportions',
|
|
177
|
-
'Lesson:
|
|
177
|
+
'Lesson: Proportions and Angles — Practice maintaining proportions and angles',
|
|
178
178
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#3f51b5') },
|
|
179
179
|
async (p) => {
|
|
180
180
|
const commands = lessons.lessonProportionsAndAngles(p.canvasSize, p.color);
|
|
181
181
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
182
|
-
return { content: [{ type: 'text', text: `✓
|
|
182
|
+
return { content: [{ type: 'text', text: `✓ Proportions and Angles lesson completed` }] };
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
registerTool(server, 'art_tree_lesson_curves',
|
|
186
|
-
'Lesson:
|
|
186
|
+
'Lesson: Curves — C-curves and S-curves',
|
|
187
187
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
|
|
188
188
|
async (p) => {
|
|
189
189
|
const commands = lessons.lessonCurves(p.canvasSize, p.color);
|
|
190
190
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
191
|
-
return { content: [{ type: 'text', text: `✓
|
|
191
|
+
return { content: [{ type: 'text', text: `✓ Curves (C, S) lesson completed (${commands.length} strokes)` }] };
|
|
192
192
|
});
|
|
193
193
|
|
|
194
194
|
registerTool(server, 'art_tree_lesson_spiral',
|
|
195
|
-
'Lesson:
|
|
195
|
+
'Lesson: Spiral — Drawing spiral curves',
|
|
196
196
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff9800') },
|
|
197
197
|
async (p) => {
|
|
198
198
|
const commands = lessons.lessonSpiral(p.canvasSize, p.color);
|
|
199
199
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
200
|
-
return { content: [{ type: 'text', text: `✓
|
|
200
|
+
return { content: [{ type: 'text', text: `✓ Spiral lesson completed (${commands.length} strokes)` }] };
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
registerTool(server, 'art_tree_lesson_strokes',
|
|
204
|
-
'Lesson:
|
|
204
|
+
'Lesson: Stroke Types — Long strokes, short strokes, thick and thin lines',
|
|
205
205
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
|
|
206
206
|
async (p) => {
|
|
207
207
|
const commands = lessons.lessonStrokes(p.canvasSize, p.color);
|
|
208
208
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
209
|
-
return { content: [{ type: 'text', text: `✓
|
|
209
|
+
return { content: [{ type: 'text', text: `✓ Strokes lesson completed` }] };
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
registerTool(server, 'art_tree_lesson_parallel_intersecting',
|
|
213
|
-
'Lesson:
|
|
213
|
+
'Lesson: Parallel & Intersecting Lines — Drawing parallel and intersecting lines',
|
|
214
214
|
{ canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
|
|
215
215
|
async (p) => {
|
|
216
216
|
const commands = lessons.lessonParallelAndIntersecting(p.canvasSize, p.color);
|
|
217
217
|
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
218
|
-
return { content: [{ type: 'text', text: `✓
|
|
218
|
+
return { content: [{ type: 'text', text: `✓ Parallel & Intersecting Lines lesson completed` }] };
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
registerTool(server, 'art_tree_lesson_catalog',
|
|
@@ -226,7 +226,7 @@ function register(server) {
|
|
|
226
226
|
const text = catalog.map(l =>
|
|
227
227
|
` • ${l.id}: ${l.name} — ${l.description}`
|
|
228
228
|
).join('\n');
|
|
229
|
-
return { content: [{ type: 'text', text: `📚
|
|
229
|
+
return { content: [{ type: 'text', text: `📚 Art Tree Lesson Catalog:\n\n${text}` }] };
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
|
|
@@ -53,7 +53,7 @@ function lessonSquash(canvasSize = 32, color = '#ff9800') {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Lesson: Taper & Swell (
|
|
56
|
+
* Lesson: Taper & Swell (Narrow or bulge)
|
|
57
57
|
* Cylinder -> Bottle
|
|
58
58
|
*/
|
|
59
59
|
function lessonTaperSwell(canvasSize = 32, color = '#00bcd4') {
|
|
@@ -80,7 +80,7 @@ function lessonTaperSwell(canvasSize = 32, color = '#00bcd4') {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* Lesson: Bend (
|
|
83
|
+
* Lesson: Bend (Curved bending)
|
|
84
84
|
* Cylinder -> Bent Tube
|
|
85
85
|
*/
|
|
86
86
|
function lessonBend(canvasSize = 32, color = '#e91e63') {
|
|
@@ -3,7 +3,7 @@ const transformShapes = require('./transform_shapes');
|
|
|
3
3
|
const structureLessons = require('./structure_lessons');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Lesson: Rotate (
|
|
6
|
+
* Lesson: Rotate (Rotation)
|
|
7
7
|
* Analyzing how objects change when rotated around X, Y, Z axes.
|
|
8
8
|
* We can reuse the Axis Orientation lesson as it perfectly demonstrates rotation.
|
|
9
9
|
*/
|
|
@@ -13,7 +13,7 @@ function lessonRotate(canvasSize = 32, color = '#ff5722') {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Lesson: Cut (
|
|
16
|
+
* Lesson: Cut (Slice a form)
|
|
17
17
|
* Sphere -> Truncated Sphere (exposing internal cross-section)
|
|
18
18
|
*/
|
|
19
19
|
function lessonCut(canvasSize = 32, color = '#8bc34a') {
|
|
@@ -32,7 +32,7 @@ function lessonCut(canvasSize = 32, color = '#8bc34a') {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Lesson: Hollow (
|
|
35
|
+
* Lesson: Hollow (Carve interior)
|
|
36
36
|
* Cylinder -> Pipe/Hole
|
|
37
37
|
*/
|
|
38
38
|
function lessonHollow(canvasSize = 32, color = '#3f51b5') {
|
|
@@ -50,7 +50,7 @@ function lessonHollow(canvasSize = 32, color = '#3f51b5') {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Lesson: Combine (
|
|
53
|
+
* Lesson: Combine (Merge forms)
|
|
54
54
|
* Box + Cylinder (e.g., a wheel on an axle, or a tower on a base)
|
|
55
55
|
*/
|
|
56
56
|
function lessonCombine(canvasSize = 32, color = '#607d8b') {
|
|
@@ -89,14 +89,14 @@ function register(server) {
|
|
|
89
89
|
{},
|
|
90
90
|
() => {
|
|
91
91
|
const text = `📚 Transformation Lessons:
|
|
92
|
-
• transform_stretch: Stretch/Elongate (
|
|
93
|
-
• transform_squash: Squash/Compress (
|
|
94
|
-
• transform_taper: Taper/Swell (
|
|
95
|
-
• transform_bend: Bend (
|
|
96
|
-
• transform_rotate: Rotate (
|
|
97
|
-
• transform_cut: Cut/Truncate (
|
|
98
|
-
• transform_hollow: Hollow out (
|
|
99
|
-
• transform_combine: Combine (
|
|
92
|
+
• transform_stretch: Stretch/Elongate (Sphere -> Egg)
|
|
93
|
+
• transform_squash: Squash/Compress (Sphere -> Flat disc)
|
|
94
|
+
• transform_taper: Taper/Swell (Cylinder -> Bottle)
|
|
95
|
+
• transform_bend: Bend (Cylinder -> Bent tube)
|
|
96
|
+
• transform_rotate: Rotate (3D rotation analysis)
|
|
97
|
+
• transform_cut: Cut/Truncate (Slice a form)
|
|
98
|
+
• transform_hollow: Hollow out (Carve interior)
|
|
99
|
+
• transform_combine: Combine (Merge forms)`;
|
|
100
100
|
return { content: [{ type: 'text', text }] };
|
|
101
101
|
});
|
|
102
102
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const vocabShapes = require('./vocab_shapes');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lesson: Visual Vocabulary - House Breakdown
|
|
5
|
+
* A 5-step process of recalling and drawing parts of an object.
|
|
6
|
+
* Users can supply the step number (1 to 5) to see the progressive breakdown.
|
|
7
|
+
*/
|
|
8
|
+
function lessonVocabHouse(canvasSize = 64, step = 1) {
|
|
9
|
+
const cx = Math.floor(canvasSize / 2);
|
|
10
|
+
const cy = Math.floor(canvasSize * 0.45);
|
|
11
|
+
|
|
12
|
+
// Ensure step is between 1 and 5
|
|
13
|
+
const validStep = Math.max(1, Math.min(5, step));
|
|
14
|
+
|
|
15
|
+
return vocabShapes.drawHouseBreakdown(cx, cy, canvasSize, validStep);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getVocabLessonCatalog() {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
id: 'vocab_house',
|
|
22
|
+
name: 'House Breakdown (5 Steps)',
|
|
23
|
+
description: 'Break down a house: Masses -> Structural -> Functional -> Identity -> Materials. Supply step=1..5',
|
|
24
|
+
fn: lessonVocabHouse
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
lessonVocabHouse,
|
|
31
|
+
getVocabLessonCatalog
|
|
32
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const shapes = require('./shapes');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Draws a 5-step breakdown of a House.
|
|
5
|
+
* Step 1: Main Masses (Thân + Mái)
|
|
6
|
+
* Step 2: Structural Parts (Cột, Móng, Hiên/Bậc thềm)
|
|
7
|
+
* Step 3: Functional Parts (Cửa, Cửa sổ)
|
|
8
|
+
* Step 4: Identifying Details (Ống khói, Viền mái, Khung cửa)
|
|
9
|
+
* Step 5: Materials (Ván gỗ, Gạch)
|
|
10
|
+
*/
|
|
11
|
+
function drawHouseBreakdown(cx, cy, scale, step) {
|
|
12
|
+
const commands = [];
|
|
13
|
+
const color = '#37474f';
|
|
14
|
+
|
|
15
|
+
const w = Math.floor(scale * 0.5);
|
|
16
|
+
const h = Math.floor(scale * 0.4);
|
|
17
|
+
const roofH = Math.floor(scale * 0.25);
|
|
18
|
+
|
|
19
|
+
const leftX = cx - Math.floor(w/2);
|
|
20
|
+
const rightX = cx + Math.floor(w/2);
|
|
21
|
+
const topY = cy;
|
|
22
|
+
const botY = cy + h;
|
|
23
|
+
const roofTipY = topY - roofH;
|
|
24
|
+
|
|
25
|
+
// STEP 1: MAIN MASSES
|
|
26
|
+
if (step >= 1) {
|
|
27
|
+
// Main Body Box
|
|
28
|
+
commands.push(shapes.drawRectangle(leftX, topY, w, h, color, false));
|
|
29
|
+
// Roof Triangle
|
|
30
|
+
commands.push(shapes.drawLine(leftX, topY, cx, roofTipY, color));
|
|
31
|
+
commands.push(shapes.drawLine(cx, roofTipY, rightX, topY, color));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// STEP 2: STRUCTURAL PARTS
|
|
35
|
+
if (step >= 2) {
|
|
36
|
+
// Corner Columns (Cột)
|
|
37
|
+
commands.push(shapes.drawLine(leftX - 4, topY, leftX - 4, botY, color));
|
|
38
|
+
commands.push(shapes.drawLine(rightX + 4, topY, rightX + 4, botY, color));
|
|
39
|
+
// Foundation (Móng)
|
|
40
|
+
commands.push(shapes.drawRectangle(leftX - 6, botY, w + 12, 5, color, false));
|
|
41
|
+
// Front Porch Steps (Bậc thềm)
|
|
42
|
+
const pW = Math.floor(w * 0.4);
|
|
43
|
+
const pX = cx - Math.floor(pW/2);
|
|
44
|
+
commands.push(shapes.drawRectangle(pX, botY + 5, pW, 4, color, false));
|
|
45
|
+
commands.push(shapes.drawRectangle(pX - 2, botY + 9, pW + 4, 4, color, false));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// STEP 3: FUNCTIONAL PARTS
|
|
49
|
+
if (step >= 3) {
|
|
50
|
+
// Door (Cửa ra vào)
|
|
51
|
+
const dW = Math.floor(w * 0.25);
|
|
52
|
+
const dH = Math.floor(h * 0.6);
|
|
53
|
+
const dX = cx - Math.floor(dW/2);
|
|
54
|
+
commands.push(shapes.drawRectangle(dX, botY - dH, dW, dH, color, false));
|
|
55
|
+
|
|
56
|
+
// Windows (Cửa sổ)
|
|
57
|
+
const winW = Math.floor(w * 0.2);
|
|
58
|
+
const winH = Math.floor(h * 0.35);
|
|
59
|
+
const winY = topY + Math.floor(h * 0.2);
|
|
60
|
+
// Left window
|
|
61
|
+
commands.push(shapes.drawRectangle(leftX + 8, winY, winW, winH, color, false));
|
|
62
|
+
// Right window
|
|
63
|
+
commands.push(shapes.drawRectangle(rightX - 8 - winW, winY, winW, winH, color, false));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// STEP 4: IDENTIFYING DETAILS
|
|
67
|
+
if (step >= 4) {
|
|
68
|
+
// Chimney (Ống khói)
|
|
69
|
+
const chimW = Math.floor(w * 0.15);
|
|
70
|
+
const chimX = leftX + Math.floor(w * 0.1);
|
|
71
|
+
const chimY = topY - Math.floor(roofH * 0.6);
|
|
72
|
+
commands.push(shapes.drawRectangle(chimX, chimY, chimW, Math.floor(roofH * 0.5), color, false));
|
|
73
|
+
commands.push(shapes.drawRectangle(chimX - 2, chimY - 4, chimW + 4, 4, color, false)); // Chimney cap
|
|
74
|
+
|
|
75
|
+
// Roof Trim (Viền mái)
|
|
76
|
+
commands.push(shapes.drawLine(leftX - 6, topY + 4, cx, roofTipY - 6, color));
|
|
77
|
+
commands.push(shapes.drawLine(cx, roofTipY - 6, rightX + 6, topY + 4, color));
|
|
78
|
+
|
|
79
|
+
// Window/Door Frames & Knobs (Khung cửa)
|
|
80
|
+
const winW = Math.floor(w * 0.2);
|
|
81
|
+
const winH = Math.floor(h * 0.35);
|
|
82
|
+
const winY = topY + Math.floor(h * 0.2);
|
|
83
|
+
// Left window cross
|
|
84
|
+
commands.push(shapes.drawLine(leftX + 8 + Math.floor(winW/2), winY, leftX + 8 + Math.floor(winW/2), winY + winH, color));
|
|
85
|
+
commands.push(shapes.drawLine(leftX + 8, winY + Math.floor(winH/2), leftX + 8 + winW, winY + Math.floor(winH/2), color));
|
|
86
|
+
// Right window cross
|
|
87
|
+
commands.push(shapes.drawLine(rightX - 8 - Math.floor(winW/2), winY, rightX - 8 - Math.floor(winW/2), winY + winH, color));
|
|
88
|
+
commands.push(shapes.drawLine(rightX - 8 - winW, winY + Math.floor(winH/2), rightX - 8, winY + Math.floor(winH/2), color));
|
|
89
|
+
// Door knob
|
|
90
|
+
commands.push(shapes.drawCircle(cx + Math.floor(w * 0.08), botY - Math.floor(h * 0.3), 1, color, true));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// STEP 5: SURFACE MATERIALS
|
|
94
|
+
if (step >= 5) {
|
|
95
|
+
const matColor = '#9e9e9e';
|
|
96
|
+
// Wood Planks on the main body
|
|
97
|
+
for (let i = 1; i < 5; i++) {
|
|
98
|
+
const plankY = topY + i * Math.floor(h / 5);
|
|
99
|
+
commands.push(shapes.drawLine(leftX + 2, plankY, rightX - 2, plankY, matColor));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Brick lines on the chimney
|
|
103
|
+
const chimW = Math.floor(w * 0.15);
|
|
104
|
+
const chimX = leftX + Math.floor(w * 0.1);
|
|
105
|
+
const chimY = topY - Math.floor(roofH * 0.6);
|
|
106
|
+
commands.push(shapes.drawLine(chimX, chimY + 4, chimX + chimW, chimY + 4, matColor));
|
|
107
|
+
commands.push(shapes.drawLine(chimX, chimY + 8, chimX + chimW, chimY + 8, matColor));
|
|
108
|
+
commands.push(shapes.drawLine(chimX + 4, chimY + 4, chimX + 4, chimY + 8, matColor)); // vertical brick line
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return commands;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
drawHouseBreakdown
|
|
116
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const { z } = require('zod');
|
|
2
|
+
const { registerTool, sendCommand } = require('../../core/server');
|
|
3
|
+
const vocabLessons = require('./vocab_lessons');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Register all visual vocabulary tools on the MCP server.
|
|
7
|
+
* @param {McpServer} server
|
|
8
|
+
*/
|
|
9
|
+
function register(server) {
|
|
10
|
+
registerTool(server, 'art_tree_lesson_vocab_house',
|
|
11
|
+
'Lesson: Visual Vocabulary - 5-Step Breakdown of a House. Pass step (1 to 5).',
|
|
12
|
+
{
|
|
13
|
+
canvasSize: z.number().int().min(16).max(128).default(64),
|
|
14
|
+
step: z.number().int().min(1).max(5).describe('The breakdown step (1=Main Masses, 2=Structural, 3=Functional, 4=Identity, 5=Materials)')
|
|
15
|
+
},
|
|
16
|
+
async (p) => {
|
|
17
|
+
const commands = vocabLessons.lessonVocabHouse(p.canvasSize, p.step);
|
|
18
|
+
await Promise.all(commands.map(cmd => sendCommand(cmd)));
|
|
19
|
+
const steps = [
|
|
20
|
+
"1. Main Masses (Khối chính: Thân nhà, Mái)",
|
|
21
|
+
"2. Structural Parts (Bộ phận lớn: Cột, Móng, Bậc thềm)",
|
|
22
|
+
"3. Functional Parts (Chức năng: Cửa, Cửa sổ)",
|
|
23
|
+
"4. Identifying Details (Nhận diện: Ống khói, Viền mái, Khung cửa)",
|
|
24
|
+
"5. Surface Materials (Vật liệu: Ván gỗ, Gạch)"
|
|
25
|
+
];
|
|
26
|
+
return { content: [{ type: 'text', text: `✓ Step ${p.step} Vocabulary breakdown completed:\n${steps[p.step - 1]}` }] };
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
registerTool(server, 'art_tree_lesson_vocab_catalog',
|
|
30
|
+
'Get the catalog of all available Visual Vocabulary lessons',
|
|
31
|
+
{},
|
|
32
|
+
() => {
|
|
33
|
+
const catalog = vocabLessons.getVocabLessonCatalog();
|
|
34
|
+
const text = catalog.map(l =>
|
|
35
|
+
` • ${l.id}: ${l.name} — ${l.description}`
|
|
36
|
+
).join('\n');
|
|
37
|
+
return { content: [{ type: 'text', text: `📚 Visual Vocabulary Lessons:\n\n${text}` }] };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { register };
|
package/index.js
CHANGED
|
@@ -28,6 +28,7 @@ require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') }
|
|
|
28
28
|
const { createServer } = require('./core/server');
|
|
29
29
|
const { registerAll: registerCoreTools } = require('./tools/index');
|
|
30
30
|
const { registerAll: registerArtTree } = require('./domains/art-tree/index');
|
|
31
|
+
const { registerAll: registerRules } = require('./rules/index');
|
|
31
32
|
|
|
32
33
|
// ── Initialize ────────────────────────────────────────────────────────────
|
|
33
34
|
const server = createServer();
|
|
@@ -35,6 +36,9 @@ const server = createServer();
|
|
|
35
36
|
// ── Register Core Tools ───────────────────────────────────────────────────
|
|
36
37
|
registerCoreTools(server);
|
|
37
38
|
|
|
39
|
+
// ── Register Rules / Workflow Tools (BẮT BUỘC: chạy trước khi vẽ) ────────
|
|
40
|
+
registerRules(server);
|
|
41
|
+
|
|
38
42
|
// ── Register Domain Modules ───────────────────────────────────────────────
|
|
39
43
|
registerArtTree(server);
|
|
40
44
|
// Future domains: registerPixelArt(server), registerCharacterDrawing(server), etc.
|