@pixel-normal-edit/mcp 2.1.0 → 2.2.0
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/clear-firebase.js +37 -0
- package/domains/art-tree/README.md +134 -0
- package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
- package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
- package/domains/art-tree/index.js +58 -58
- package/domains/art-tree/lessons/3d/box-properties.js +38 -0
- package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
- package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
- package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
- package/domains/art-tree/lessons/advanced/angles.js +30 -0
- package/domains/art-tree/lessons/advanced/distances.js +26 -0
- package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
- package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
- package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
- package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
- package/domains/art-tree/lessons/basic/circles.js +26 -0
- package/domains/art-tree/lessons/basic/composition.js +34 -0
- package/domains/art-tree/lessons/basic/curves.js +31 -0
- package/domains/art-tree/lessons/basic/ellipses.js +26 -0
- package/domains/art-tree/lessons/basic/freehand.js +19 -0
- package/domains/art-tree/lessons/basic/index.js +36 -0
- package/domains/art-tree/lessons/basic/lines.js +38 -0
- package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
- package/domains/art-tree/lessons/basic/polygons.js +25 -0
- package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
- package/domains/art-tree/lessons/basic/spiral.js +21 -0
- package/domains/art-tree/lessons/basic/squares.js +28 -0
- package/domains/art-tree/lessons/basic/strokes.js +23 -0
- package/domains/art-tree/lessons/basic/triangles.js +30 -0
- package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
- package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
- package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
- package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
- package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
- package/domains/art-tree/lessons/curves/curve-types.js +44 -0
- package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
- package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
- package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
- package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
- package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
- package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
- package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
- package/domains/art-tree/lessons/index.js +42 -0
- package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
- package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
- package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
- package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
- package/domains/art-tree/lessons/materials/material-glass.js +21 -0
- package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
- package/domains/art-tree/lessons/materials/material-texture.js +26 -0
- package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
- package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
- package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
- package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
- package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
- package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
- package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
- package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
- package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
- package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
- package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
- package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
- package/domains/art-tree/lessons/transforms/bend.js +27 -0
- package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
- package/domains/art-tree/lessons/transforms/cut.js +25 -0
- package/domains/art-tree/lessons/transforms/hollow.js +24 -0
- package/domains/art-tree/lessons/transforms/rotate.js +16 -0
- package/domains/art-tree/lessons/transforms/squash.js +31 -0
- package/domains/art-tree/lessons/transforms/stretch.js +31 -0
- package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
- package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
- package/domains/art-tree/lib/tool-register.js +104 -0
- package/domains/art-tree/lib/validators.js +78 -0
- package/domains/art-tree/shapes/grid.js +37 -0
- package/domains/art-tree/shapes/polyline.js +26 -0
- package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
- package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
- package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
- package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
- package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
- package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
- package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
- package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
- package/domains/art-tree/tools/index.js +53 -0
- package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
- package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
- package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
- package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
- package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
- package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
- package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
- package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
- package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
- package/package.json +1 -1
- package/rules/prerequisites.js +30 -30
- package/rules/rules.js +1 -1
- package/rules/workflow.js +28 -20
- package/tools/canvas-tools.js +2 -2
- package/tools/drawing-tools.js +23 -12
- package/tools/workspace-tools.js +5 -4
- package/domains/art-tree/3d_lessons.js +0 -114
- package/domains/art-tree/advanced_lessons.js +0 -124
- package/domains/art-tree/cross_section_lessons.js +0 -65
- package/domains/art-tree/curve_lessons.js +0 -138
- package/domains/art-tree/ellipse_lessons.js +0 -105
- package/domains/art-tree/hidden_lessons.js +0 -55
- package/domains/art-tree/lessons.js +0 -305
- package/domains/art-tree/light_lessons.js +0 -92
- package/domains/art-tree/material_lessons.js +0 -71
- package/domains/art-tree/perspective_lessons.js +0 -138
- package/domains/art-tree/sky_lessons_1.js +0 -82
- package/domains/art-tree/surface_lessons.js +0 -112
- package/domains/art-tree/transform_lessons_1.js +0 -108
- /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
- /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
- /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
- /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
- /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
- /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
- /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
- /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
- /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
- /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
- /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
- /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
- /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
- /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
- /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
- /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
- /package/domains/art-tree/{vocab_shapes.js → shapes/vocab.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const materialLessons = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const materialLessons = require('../lessons/material');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register all material & surface rendering tools on the MCP server.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const perspLessons = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const perspLessons = require('../lessons/perspective');
|
|
4
4
|
|
|
5
5
|
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const skyLessons1 = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const skyLessons1 = require('../lessons/sky_1');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register all sky and sun tools on the MCP server.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const structureLessons = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const structureLessons = require('../lessons/structure');
|
|
4
4
|
|
|
5
5
|
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const surfaceLessons = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const surfaceLessons = require('../lessons/surface');
|
|
4
4
|
|
|
5
5
|
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
|
|
6
6
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const lessons1 = require('
|
|
4
|
-
const lessons2 = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const lessons1 = require('../lessons/transform_1');
|
|
4
|
+
const lessons2 = require('../lessons/transform_2');
|
|
5
5
|
|
|
6
6
|
const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { z } = require('zod');
|
|
2
|
-
const { registerTool, sendCommand } = require('
|
|
3
|
-
const vocabLessons = require('
|
|
2
|
+
const { registerTool, sendCommand } = require('../../../core/server');
|
|
3
|
+
const vocabLessons = require('../lessons/vocab');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register all visual vocabulary tools on the MCP server.
|
package/package.json
CHANGED
package/rules/prerequisites.js
CHANGED
|
@@ -20,7 +20,7 @@ const { registerTool } = require('../core/server');
|
|
|
20
20
|
*/
|
|
21
21
|
function register(server) {
|
|
22
22
|
registerTool(server, 'workflow_start',
|
|
23
|
-
|
|
23
|
+
`REQUIRED: Call this tool FIRST before drawing anything.
|
|
24
24
|
This tool initializes a drawing workflow with sequential steps for a specific mode.
|
|
25
25
|
Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
26
26
|
{ mode: z.enum([MODES.CREATE_IMAGE, MODES.EDIT_IMAGE, MODES.CREATE_ANIMATION]).describe('Workflow mode to start') },
|
|
@@ -28,7 +28,7 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
28
28
|
const session = require('../core/command-bus').SESSION;
|
|
29
29
|
const state = workflow.start(session, p.mode);
|
|
30
30
|
return {
|
|
31
|
-
content: [{ type: 'text', text:
|
|
31
|
+
content: [{ type: 'text', text: `**Workflow started in mode: ${p.mode}!**\n\n${workflow.getProgressReport(session)}\n\n---\nStart with step: **${state.steps[0].label}** → ${state.steps[0].description}` }]
|
|
32
32
|
};
|
|
33
33
|
});
|
|
34
34
|
|
|
@@ -40,24 +40,24 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
40
40
|
const report = workflow.getProgressReport(session);
|
|
41
41
|
const current = workflow.getCurrentStep(session);
|
|
42
42
|
if (!current) {
|
|
43
|
-
return { content: [{ type: 'text', text: '
|
|
43
|
+
return { content: [{ type: 'text', text: ' Workflow has not started. Call **workflow_start** first.' }] };
|
|
44
44
|
}
|
|
45
45
|
return {
|
|
46
46
|
content: [{
|
|
47
47
|
type: 'text',
|
|
48
|
-
text: `${report}\n\n
|
|
48
|
+
text: `${report}\n\n **Pending:** ${current.label} — ${current.description}`
|
|
49
49
|
}]
|
|
50
50
|
};
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
registerTool(server, 'workflow_advance',
|
|
54
|
-
'
|
|
54
|
+
' Mark current step as complete and move to the next step. Only call AFTER successfully finishing the current step.',
|
|
55
55
|
{},
|
|
56
56
|
() => {
|
|
57
57
|
const session = require('../core/command-bus').SESSION;
|
|
58
58
|
const result = workflow.advance(session);
|
|
59
59
|
if (!result.success) {
|
|
60
|
-
return { isError: true, content: [{ type: 'text', text:
|
|
60
|
+
return { isError: true, content: [{ type: 'text', text: ` ${result.message}` }] };
|
|
61
61
|
}
|
|
62
62
|
if (!result.nextStep) {
|
|
63
63
|
return { content: [{ type: 'text', text: `🎉 ${result.message}` }] };
|
|
@@ -66,7 +66,7 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
registerTool(server, 'workflow_validate',
|
|
69
|
-
|
|
69
|
+
` Check if the agent is allowed to perform a certain action.
|
|
70
70
|
Call this tool BEFORE performing operations to ensure correct workflow order.`,
|
|
71
71
|
{ stepId: z.string().describe('ID of the step to validate (e.g., "INITIALIZE_CANVAS")') },
|
|
72
72
|
(p) => {
|
|
@@ -75,7 +75,7 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
75
75
|
if (!result.allowed) {
|
|
76
76
|
return { isError: true, content: [{ type: 'text', text: result.message }] };
|
|
77
77
|
}
|
|
78
|
-
return { content: [{ type: 'text', text: result.message ||
|
|
78
|
+
return { content: [{ type: 'text', text: result.message || ` You can perform "${p.stepId}". Please proceed!` }] };
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
registerTool(server, 'workflow_set_layer',
|
|
@@ -85,9 +85,9 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
85
85
|
const session = require('../core/command-bus').SESSION;
|
|
86
86
|
const success = workflow.setLayerAndFrame(session, p.layer, undefined);
|
|
87
87
|
if (!success) {
|
|
88
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
88
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
89
89
|
}
|
|
90
|
-
return { content: [{ type: 'text', text:
|
|
90
|
+
return { content: [{ type: 'text', text: ` Active layer set to ${p.layer}` }] };
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
registerTool(server, 'workflow_set_frame',
|
|
@@ -97,9 +97,9 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
97
97
|
const session = require('../core/command-bus').SESSION;
|
|
98
98
|
const success = workflow.setLayerAndFrame(session, undefined, p.frame);
|
|
99
99
|
if (!success) {
|
|
100
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
100
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
101
101
|
}
|
|
102
|
-
return { content: [{ type: 'text', text:
|
|
102
|
+
return { content: [{ type: 'text', text: ` Active frame set to ${p.frame}` }] };
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
registerTool(server, 'workflow_set_phase',
|
|
@@ -109,14 +109,14 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
109
109
|
const session = require('../core/command-bus').SESSION;
|
|
110
110
|
const success = workflow.setPhase(session, p.phase);
|
|
111
111
|
if (!success) {
|
|
112
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
112
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
113
113
|
}
|
|
114
|
-
return { content: [{ type: 'text', text:
|
|
114
|
+
return { content: [{ type: 'text', text: ` Active phase set to ${p.phase}` }] };
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
registerTool(server, 'workflow_setup_animation',
|
|
118
118
|
'Declare animation metadata for tracking (fps, total_frames, loop).',
|
|
119
|
-
{
|
|
119
|
+
{
|
|
120
120
|
fps: z.number().int().optional().describe('Frames per second'),
|
|
121
121
|
total_frames: z.number().int().optional().describe('Total frames in the animation'),
|
|
122
122
|
loop: z.boolean().optional().describe('Whether the animation loops')
|
|
@@ -125,14 +125,14 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
125
125
|
const session = require('../core/command-bus').SESSION;
|
|
126
126
|
const success = workflow.setAnimationMeta(session, p.fps, p.total_frames, p.loop);
|
|
127
127
|
if (!success) {
|
|
128
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
128
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
129
129
|
}
|
|
130
|
-
return { content: [{ type: 'text', text:
|
|
130
|
+
return { content: [{ type: 'text', text: ` Animation meta updated: FPS=${p.fps}, Frames=${p.total_frames}, Loop=${p.loop}` }] };
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
registerTool(server, 'workflow_create_input_request',
|
|
134
134
|
'Pause the workflow and ask the user for specific input via the UI (e.g. CANVAS_SIZE, APPROVE_PLAN, SELECT_EDIT_REGION).',
|
|
135
|
-
{
|
|
135
|
+
{
|
|
136
136
|
type: z.enum(['CANVAS_SIZE', 'APPROVE_PLAN', 'REVIEW_RESULT', 'SELECT_EDIT_REGION', 'ANIMATION_SETUP']).describe('The type of request'),
|
|
137
137
|
fields: z.record(z.any()).optional().describe('Context or fields for the request')
|
|
138
138
|
},
|
|
@@ -140,15 +140,15 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
140
140
|
const { sendCommand, SESSION } = require('../core/command-bus');
|
|
141
141
|
const reqId = workflow.createInputRequest(SESSION, p.type, p.fields || {});
|
|
142
142
|
if (!reqId) {
|
|
143
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
143
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
const res = await sendCommand({ action: 'showUserInputRequest', type: p.type, fields: p.fields || {}, reqId }, 35000);
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
if (!res.isError) {
|
|
149
149
|
workflow.completeInputRequest(SESSION, reqId, res.content[0].text);
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
return res;
|
|
153
153
|
});
|
|
154
154
|
|
|
@@ -159,18 +159,18 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
159
159
|
const session = require('../core/command-bus').SESSION;
|
|
160
160
|
const success = workflow.setApprovalRequired(session, p.required);
|
|
161
161
|
if (!success) {
|
|
162
|
-
return { isError: true, content: [{ type: 'text', text: '
|
|
162
|
+
return { isError: true, content: [{ type: 'text', text: ' Workflow not started' }] };
|
|
163
163
|
}
|
|
164
|
-
return { content: [{ type: 'text', text: p.required ? '
|
|
164
|
+
return { content: [{ type: 'text', text: p.required ? ' Workflow paused, waiting for user approval.' : ' Workflow resumed.' }] };
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
registerTool(server, 'workflow_reset',
|
|
168
|
-
'
|
|
168
|
+
' Reset workflow to initial state. Clears all progress.',
|
|
169
169
|
{},
|
|
170
170
|
() => {
|
|
171
171
|
const session = require('../core/command-bus').SESSION;
|
|
172
172
|
workflow.reset(session);
|
|
173
|
-
return { content: [{ type: 'text', text: '
|
|
173
|
+
return { content: [{ type: 'text', text: ' Workflow reset. Please start from the beginning.' }] };
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
registerTool(server, 'validate_canvas_name',
|
|
@@ -181,9 +181,9 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
181
181
|
(p) => {
|
|
182
182
|
const result = validateCanvasName(p.name);
|
|
183
183
|
if (!result.valid) {
|
|
184
|
-
return { isError: true, content: [{ type: 'text', text:
|
|
184
|
+
return { isError: true, content: [{ type: 'text', text: ` ${result.message}` }] };
|
|
185
185
|
}
|
|
186
|
-
return { content: [{ type: 'text', text:
|
|
186
|
+
return { content: [{ type: 'text', text: ` Name "${p.name}" is valid!` }] };
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
registerTool(server, 'validate_canvas_size',
|
|
@@ -193,9 +193,9 @@ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
|
|
|
193
193
|
(p) => {
|
|
194
194
|
const result = validateCanvasSize(p.width, p.height);
|
|
195
195
|
if (!result.valid) {
|
|
196
|
-
return { isError: true, content: [{ type: 'text', text:
|
|
196
|
+
return { isError: true, content: [{ type: 'text', text: ` ${result.message}` }] };
|
|
197
197
|
}
|
|
198
|
-
return { content: [{ type: 'text', text:
|
|
198
|
+
return { content: [{ type: 'text', text: ` Size ${p.width}x${p.height} is valid!` }] };
|
|
199
199
|
});
|
|
200
200
|
}
|
|
201
201
|
|
package/rules/rules.js
CHANGED
package/rules/workflow.js
CHANGED
|
@@ -66,7 +66,7 @@ function start(sessionId, mode = MODES.CREATE_IMAGE) {
|
|
|
66
66
|
history: [],
|
|
67
67
|
startedAt: Date.now(),
|
|
68
68
|
};
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
sessions.set(sessionId, state);
|
|
71
71
|
saveState();
|
|
72
72
|
return getState(sessionId);
|
|
@@ -185,7 +185,7 @@ function createInputRequest(sessionId, type, fields = {}) {
|
|
|
185
185
|
if (!state) return false;
|
|
186
186
|
|
|
187
187
|
state.user_input_request = {
|
|
188
|
-
id: `req_${Date.now()}_${Math.floor(Math.random()*1000)}`,
|
|
188
|
+
id: `req_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
|
|
189
189
|
type,
|
|
190
190
|
fields,
|
|
191
191
|
status: 'WAITING_FOR_USER',
|
|
@@ -210,7 +210,15 @@ function completeInputRequest(sessionId, reqId, response) {
|
|
|
210
210
|
state.user_input_request.status = 'COMPLETED';
|
|
211
211
|
state.user_input_request.response = response;
|
|
212
212
|
state.step_status = STATUSES.IN_PROGRESS; // Unpause workflow
|
|
213
|
-
|
|
213
|
+
|
|
214
|
+
// Auto-set flags based on completed request type
|
|
215
|
+
if (state.user_input_request.type === 'CANVAS_SIZE') {
|
|
216
|
+
state.step_completion_flags.canvas_initialized = true;
|
|
217
|
+
}
|
|
218
|
+
if (state.user_input_request.type === 'APPROVE_PLAN') {
|
|
219
|
+
state.step_completion_flags.plan_created = true;
|
|
220
|
+
}
|
|
221
|
+
|
|
214
222
|
state.history.push({ event: 'input_request_completed', reqId, time: Date.now() });
|
|
215
223
|
saveState();
|
|
216
224
|
return true;
|
|
@@ -222,18 +230,18 @@ function completeInputRequest(sessionId, reqId, response) {
|
|
|
222
230
|
function setLayerAndFrame(sessionId, layer, frame = null) {
|
|
223
231
|
const state = sessions.get(sessionId);
|
|
224
232
|
if (!state) return false;
|
|
225
|
-
|
|
233
|
+
|
|
226
234
|
if (layer !== undefined && layer !== null) {
|
|
227
235
|
state.current_layer = layer;
|
|
228
236
|
}
|
|
229
237
|
if (frame !== undefined && frame !== null) {
|
|
230
238
|
state.current_frame = frame;
|
|
231
239
|
}
|
|
232
|
-
state.history.push({
|
|
233
|
-
event: 'layer_frame_change',
|
|
234
|
-
layer: state.current_layer,
|
|
235
|
-
frame: state.current_frame,
|
|
236
|
-
time: Date.now()
|
|
240
|
+
state.history.push({
|
|
241
|
+
event: 'layer_frame_change',
|
|
242
|
+
layer: state.current_layer,
|
|
243
|
+
frame: state.current_frame,
|
|
244
|
+
time: Date.now()
|
|
237
245
|
});
|
|
238
246
|
saveState();
|
|
239
247
|
return true;
|
|
@@ -245,12 +253,12 @@ function setLayerAndFrame(sessionId, layer, frame = null) {
|
|
|
245
253
|
function setPhase(sessionId, phase) {
|
|
246
254
|
const state = sessions.get(sessionId);
|
|
247
255
|
if (!state) return false;
|
|
248
|
-
|
|
256
|
+
|
|
249
257
|
state.current_phase = phase;
|
|
250
|
-
state.history.push({
|
|
251
|
-
event: 'phase_change',
|
|
252
|
-
phase: phase,
|
|
253
|
-
time: Date.now()
|
|
258
|
+
state.history.push({
|
|
259
|
+
event: 'phase_change',
|
|
260
|
+
phase: phase,
|
|
261
|
+
time: Date.now()
|
|
254
262
|
});
|
|
255
263
|
saveState();
|
|
256
264
|
return true;
|
|
@@ -262,11 +270,11 @@ function setPhase(sessionId, phase) {
|
|
|
262
270
|
function setAnimationMeta(sessionId, fps, total_frames, loop) {
|
|
263
271
|
const state = sessions.get(sessionId);
|
|
264
272
|
if (!state) return false;
|
|
265
|
-
|
|
273
|
+
|
|
266
274
|
if (fps !== undefined) state.animation_meta.fps = fps;
|
|
267
275
|
if (total_frames !== undefined) state.animation_meta.total_frames = total_frames;
|
|
268
276
|
if (loop !== undefined) state.animation_meta.loop = loop;
|
|
269
|
-
|
|
277
|
+
|
|
270
278
|
saveState();
|
|
271
279
|
return true;
|
|
272
280
|
}
|
|
@@ -292,7 +300,7 @@ function advance(sessionId) {
|
|
|
292
300
|
if (!state) {
|
|
293
301
|
return { success: false, message: 'Workflow has not started' };
|
|
294
302
|
}
|
|
295
|
-
|
|
303
|
+
|
|
296
304
|
if (state.user_approval_required) {
|
|
297
305
|
return { success: false, message: 'Cannot advance: Waiting for user approval.' };
|
|
298
306
|
}
|
|
@@ -308,7 +316,7 @@ function advance(sessionId) {
|
|
|
308
316
|
|
|
309
317
|
// Pre-condition validation block
|
|
310
318
|
if (current.id === 'INITIALIZE_CANVAS' && !state.step_completion_flags.canvas_initialized) {
|
|
311
|
-
return { success: false, message: 'Cannot advance: Canvas has not been initialized.
|
|
319
|
+
return { success: false, message: 'Cannot advance: Canvas has not been initialized. You MUST use workflow_create_input_request with type CANVAS_SIZE first.' };
|
|
312
320
|
}
|
|
313
321
|
if (current.id === 'PLAN_DRAWING' && !state.step_completion_flags.plan_created) {
|
|
314
322
|
return { success: false, message: 'Cannot advance: Drawing plan has not been finalized.' };
|
|
@@ -374,10 +382,10 @@ function getProgressReport(sessionId) {
|
|
|
374
382
|
state.user_input_request && state.user_input_request.status === 'WAITING_FOR_USER' ? `⚠️ PENDING REQUEST: ${state.user_input_request.type}` : '',
|
|
375
383
|
`\n**Steps:**`
|
|
376
384
|
];
|
|
377
|
-
|
|
385
|
+
|
|
378
386
|
state.steps.forEach((step, i) => {
|
|
379
387
|
const icon = step.status === STATUSES.COMPLETED ? '✅' :
|
|
380
|
-
|
|
388
|
+
step.status === STATUSES.IN_PROGRESS ? '🔄' : '⬜';
|
|
381
389
|
const marker = i === state.currentStepIndex ? ' ← **IN PROGRESS**' : '';
|
|
382
390
|
lines.push(`${icon} **${step.label}**${marker}`);
|
|
383
391
|
if (step.status === STATUSES.IN_PROGRESS || i === state.currentStepIndex) {
|
package/tools/canvas-tools.js
CHANGED
|
@@ -16,8 +16,8 @@ function register(server) {
|
|
|
16
16
|
registerTool(server, 'canvas_resize',
|
|
17
17
|
'Resize canvas to new dimensions. mode=clear resets pixels, extend keeps them.',
|
|
18
18
|
{
|
|
19
|
-
width: z.number().int().min(1).max(
|
|
20
|
-
height: z.number().int().min(1).max(
|
|
19
|
+
width: z.number().int().min(1).max(256).describe('New width in pixels (max 256)'),
|
|
20
|
+
height: z.number().int().min(1).max(256).describe('New height in pixels (max 256)'),
|
|
21
21
|
mode: z.enum(['clear', 'extend', 'fit']).default('clear').describe('How to handle existing content'),
|
|
22
22
|
dx: z.number().int().default(0).describe('X offset for content when extending'),
|
|
23
23
|
dy: z.number().int().default(0).describe('Y offset for content when extending')
|
package/tools/drawing-tools.js
CHANGED
|
@@ -14,54 +14,65 @@ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g.
|
|
|
14
14
|
function register(server) {
|
|
15
15
|
registerTool(server, 'draw_pixel',
|
|
16
16
|
'Set a single pixel to a color',
|
|
17
|
-
{ x: z.number().int().describe('X coordinate'), y: z.number().int().describe('Y coordinate'), color: hexColor },
|
|
17
|
+
{ x: z.number().int().max(1024).describe('X coordinate'), y: z.number().int().max(1024).describe('Y coordinate'), color: hexColor },
|
|
18
18
|
(p) => ({ action: 'drawPixel', ...p }));
|
|
19
19
|
|
|
20
20
|
registerTool(server, 'draw_erase',
|
|
21
21
|
'Erase a single pixel (make transparent)',
|
|
22
|
-
{ x: z.number().int(), y: z.number().int() },
|
|
22
|
+
{ x: z.number().int().max(1024), y: z.number().int().max(1024) },
|
|
23
23
|
(p) => ({ action: 'erasePixel', ...p }));
|
|
24
24
|
|
|
25
|
+
registerTool(server, 'draw_pixels_bulk',
|
|
26
|
+
'Set multiple pixels at once (much faster than calling draw_pixel repeatedly)',
|
|
27
|
+
{
|
|
28
|
+
pixels: z.array(z.object({
|
|
29
|
+
x: z.number().int().max(1024),
|
|
30
|
+
y: z.number().int().max(1024),
|
|
31
|
+
color: hexColor.optional()
|
|
32
|
+
})).max(5000).describe('Array of pixels {x, y, color}. Omit color to erase.')
|
|
33
|
+
},
|
|
34
|
+
(p) => ({ action: 'drawPixelsBulk', pixels: p.pixels }));
|
|
35
|
+
|
|
25
36
|
registerTool(server, 'draw_line',
|
|
26
37
|
'Draw a straight line using Bresenham algorithm',
|
|
27
|
-
{ x0: z.number().int(), y0: z.number().int(), x1: z.number().int(), y1: z.number().int(), color: hexColor },
|
|
38
|
+
{ x0: z.number().int().max(1024), y0: z.number().int().max(1024), x1: z.number().int().max(1024), y1: z.number().int().max(1024), color: hexColor },
|
|
28
39
|
(p) => ({ action: 'drawLine', ...p }));
|
|
29
40
|
|
|
30
41
|
registerTool(server, 'draw_rect',
|
|
31
42
|
'Draw a rectangle (outline or filled)',
|
|
32
43
|
{
|
|
33
|
-
x: z.number().int().describe('Top-left X'), y: z.number().int().describe('Top-left Y'),
|
|
34
|
-
w: z.number().int().min(1).describe('Width'), h: z.number().int().min(1).describe('Height'),
|
|
44
|
+
x: z.number().int().max(1024).describe('Top-left X'), y: z.number().int().max(1024).describe('Top-left Y'),
|
|
45
|
+
w: z.number().int().min(1).max(256).describe('Width (max 256)'), h: z.number().int().min(1).max(256).describe('Height (max 256)'),
|
|
35
46
|
color: hexColor, filled: z.boolean().default(false).describe('Fill interior?')
|
|
36
47
|
},
|
|
37
48
|
(p) => ({ action: 'drawRect', ...p }));
|
|
38
49
|
|
|
39
50
|
registerTool(server, 'draw_circle',
|
|
40
51
|
'Draw a circle (outline or filled)',
|
|
41
|
-
{ cx: z.number().int().describe('Center X'), cy: z.number().int().describe('Center Y'),
|
|
42
|
-
r: z.number().int().min(1).describe('Radius'), color: hexColor, filled: z.boolean().default(false) },
|
|
52
|
+
{ cx: z.number().int().max(1024).describe('Center X'), cy: z.number().int().max(1024).describe('Center Y'),
|
|
53
|
+
r: z.number().int().min(1).max(128).describe('Radius (max 128)'), color: hexColor, filled: z.boolean().default(false) },
|
|
43
54
|
(p) => ({ action: 'drawCircle', ...p }));
|
|
44
55
|
|
|
45
56
|
registerTool(server, 'draw_ellipse',
|
|
46
57
|
'Draw an ellipse (outline or filled)',
|
|
47
|
-
{ cx: z.number().int(), cy: z.number().int(), rx: z.number().int().min(1).describe('Horizontal radius'),
|
|
48
|
-
ry: z.number().int().min(1).describe('Vertical radius'), color: hexColor, filled: z.boolean().default(false) },
|
|
58
|
+
{ cx: z.number().int().max(1024), cy: z.number().int().max(1024), rx: z.number().int().min(1).max(128).describe('Horizontal radius'),
|
|
59
|
+
ry: z.number().int().min(1).max(128).describe('Vertical radius'), color: hexColor, filled: z.boolean().default(false) },
|
|
49
60
|
(p) => ({ action: 'drawEllipse', ...p }));
|
|
50
61
|
|
|
51
62
|
registerTool(server, 'draw_polygon',
|
|
52
63
|
'Draw a polygon from a list of points (outline or filled)',
|
|
53
|
-
{ points: z.array(z.object({ x: z.number().int(), y: z.number().int() })).min(3).describe('Array of {x,y} vertices'),
|
|
64
|
+
{ points: z.array(z.object({ x: z.number().int().max(1024), y: z.number().int().max(1024) })).min(3).describe('Array of {x,y} vertices'),
|
|
54
65
|
color: hexColor, filled: z.boolean().default(false) },
|
|
55
66
|
(p) => ({ action: 'drawPolygon', ...p }));
|
|
56
67
|
|
|
57
68
|
registerTool(server, 'draw_fill',
|
|
58
69
|
'Flood-fill starting from (x,y) with a color',
|
|
59
|
-
{ x: z.number().int(), y: z.number().int(), color: hexColor },
|
|
70
|
+
{ x: z.number().int().max(1024), y: z.number().int().max(1024), color: hexColor },
|
|
60
71
|
(p) => ({ action: 'fill', ...p }));
|
|
61
72
|
|
|
62
73
|
registerTool(server, 'draw_gradient_rect',
|
|
63
74
|
'Draw a rectangle filled with a smooth gradient',
|
|
64
|
-
{ x: z.number().int(), y: z.number().int(), w: z.number().int().min(1), h: z.number().int().min(1),
|
|
75
|
+
{ x: z.number().int().max(1024), y: z.number().int().max(1024), w: z.number().int().min(1).max(256).describe('Width (max 256)'), h: z.number().int().min(1).max(256).describe('Height (max 256)'),
|
|
65
76
|
colorFrom: hexColor.describe('Start color'), colorTo: hexColor.describe('End color'),
|
|
66
77
|
direction: z.enum(['h', 'v']).default('h').describe('h=horizontal, v=vertical') },
|
|
67
78
|
(p) => ({ action: 'drawGradientRect', ...p }));
|
package/tools/workspace-tools.js
CHANGED
|
@@ -19,21 +19,22 @@ function register(server) {
|
|
|
19
19
|
{},
|
|
20
20
|
() => ({ action: 'getActiveTabId' }));
|
|
21
21
|
|
|
22
|
-
server
|
|
22
|
+
registerTool(server, 'workspace_create_tab',
|
|
23
23
|
'Create a new blank canvas tab',
|
|
24
24
|
{
|
|
25
25
|
name: z.string().optional().describe('Tab name'),
|
|
26
|
-
width: z.number().int().optional().describe('Canvas width'),
|
|
27
|
-
height: z.number().int().optional().describe('Canvas height')
|
|
26
|
+
width: z.number().int().min(1).max(256).optional().describe('Canvas width (max 256)'),
|
|
27
|
+
height: z.number().int().min(1).max(256).optional().describe('Canvas height (max 256)')
|
|
28
28
|
},
|
|
29
29
|
async (p) => {
|
|
30
30
|
if (p.width === undefined || p.height === undefined) {
|
|
31
31
|
return {
|
|
32
|
+
action: 'noop',
|
|
32
33
|
isError: true,
|
|
33
34
|
content: [{ type: 'text', text: 'Please provide the width and height dimensions (resize) when creating a new canvas.' }]
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
|
-
return
|
|
37
|
+
return { action: 'createTab', ...p };
|
|
37
38
|
}
|
|
38
39
|
);
|
|
39
40
|
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const shapes3d = require('./3d_shapes');
|
|
2
|
-
const advShapes = require('./advanced_shapes');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Lesson: Box Properties (Width, Height, Depth, Planes, Edges)
|
|
6
|
-
*/
|
|
7
|
-
function lessonBoxProperties(canvasSize = 32, color = '#2196f3') {
|
|
8
|
-
const commands = [];
|
|
9
|
-
const cx = Math.floor(canvasSize / 2);
|
|
10
|
-
const cy = Math.floor(canvasSize / 2);
|
|
11
|
-
const w = Math.floor(canvasSize * 0.4);
|
|
12
|
-
const h = Math.floor(canvasSize * 0.4);
|
|
13
|
-
const d = Math.floor(canvasSize * 0.3);
|
|
14
|
-
|
|
15
|
-
// Draw wireframe box
|
|
16
|
-
commands.push(...shapes3d.drawWireframeBox(cx, cy, w, h, d, color));
|
|
17
|
-
|
|
18
|
-
// Highlight dimensions
|
|
19
|
-
const fX = cx - Math.floor(w/2);
|
|
20
|
-
const fY = cy - Math.floor(h/2);
|
|
21
|
-
|
|
22
|
-
// Width
|
|
23
|
-
commands.push(...advShapes.drawDistance(fX, fY + h + 2, fX + w, fY + h + 2, '#4caf50'));
|
|
24
|
-
// Height
|
|
25
|
-
commands.push(...advShapes.drawDistance(fX - 2, fY, fX - 2, fY + h, '#ff9800'));
|
|
26
|
-
|
|
27
|
-
// Depth (approximate along the oblique edge)
|
|
28
|
-
const dx = Math.floor(d * 0.7);
|
|
29
|
-
const dy = Math.floor(d * 0.5);
|
|
30
|
-
commands.push(...advShapes.drawDistance(fX + w + 2, fY + h, fX + w + 2 + dx, fY + h - dy, '#e91e63'));
|
|
31
|
-
|
|
32
|
-
return commands;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Lesson: Sphere Properties (Center, Radius, Cross contours, Axis)
|
|
37
|
-
*/
|
|
38
|
-
function lessonSphereProperties(canvasSize = 32, color = '#9c27b0') {
|
|
39
|
-
const commands = [];
|
|
40
|
-
const cx = Math.floor(canvasSize / 2);
|
|
41
|
-
const cy = Math.floor(canvasSize / 2);
|
|
42
|
-
const r = Math.floor(canvasSize * 0.35);
|
|
43
|
-
|
|
44
|
-
// Draw sphere with volume lines
|
|
45
|
-
commands.push(...shapes3d.drawSphereVolume(cx, cy, r, color));
|
|
46
|
-
|
|
47
|
-
// Highlight radius
|
|
48
|
-
commands.push(...advShapes.drawDistance(cx, cy, cx + r, cy, '#ff9800'));
|
|
49
|
-
|
|
50
|
-
return commands;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Lesson: Cylinder Properties (Central axis, Bases, Perspective change)
|
|
55
|
-
*/
|
|
56
|
-
function lessonCylinderProperties(canvasSize = 32, color = '#00bcd4') {
|
|
57
|
-
const commands = [];
|
|
58
|
-
const cx = Math.floor(canvasSize / 2);
|
|
59
|
-
const cy = Math.floor(canvasSize / 2);
|
|
60
|
-
const r = Math.floor(canvasSize * 0.3);
|
|
61
|
-
const h = Math.floor(canvasSize * 0.6);
|
|
62
|
-
|
|
63
|
-
// Draw cylinder volume
|
|
64
|
-
commands.push(...shapes3d.drawCylinderVolume(cx, cy, r, h, color));
|
|
65
|
-
|
|
66
|
-
// Highlight Height
|
|
67
|
-
commands.push(...advShapes.drawDistance(cx + r + 4, cy - Math.floor(h/2), cx + r + 4, cy + Math.floor(h/2), '#4caf50'));
|
|
68
|
-
|
|
69
|
-
// Highlight Radius (top base)
|
|
70
|
-
commands.push(...advShapes.drawDistance(cx, cy - Math.floor(h/2), cx + r, cy - Math.floor(h/2), '#ff9800'));
|
|
71
|
-
|
|
72
|
-
return commands;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Lesson: Cone Properties (Apex, Base, Axis)
|
|
77
|
-
*/
|
|
78
|
-
function lessonConeProperties(canvasSize = 32, color = '#ff5722') {
|
|
79
|
-
const commands = [];
|
|
80
|
-
const cx = Math.floor(canvasSize / 2);
|
|
81
|
-
const cy = Math.floor(canvasSize / 2);
|
|
82
|
-
const r = Math.floor(canvasSize * 0.35);
|
|
83
|
-
const h = Math.floor(canvasSize * 0.6);
|
|
84
|
-
|
|
85
|
-
// Draw cone volume
|
|
86
|
-
commands.push(...shapes3d.drawConeVolume(cx, cy, r, h, color));
|
|
87
|
-
|
|
88
|
-
// Highlight Height
|
|
89
|
-
const apexY = cy - Math.floor(h/2);
|
|
90
|
-
const baseY = cy + Math.floor(h/2);
|
|
91
|
-
commands.push(...advShapes.drawDistance(cx + r + 4, apexY, cx + r + 4, baseY, '#4caf50'));
|
|
92
|
-
|
|
93
|
-
// Highlight Radius (base)
|
|
94
|
-
commands.push(...advShapes.drawDistance(cx, baseY, cx + r, baseY, '#ff9800'));
|
|
95
|
-
|
|
96
|
-
return commands;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function get3DLessonCatalog() {
|
|
100
|
-
return [
|
|
101
|
-
{ id: '3d_box', name: '3D Box Properties', description: 'Width, height, depth, planes, inner structure', fn: lessonBoxProperties },
|
|
102
|
-
{ id: '3d_sphere', name: '3D Sphere Properties', description: 'Center, radius, axis, cross contours', fn: lessonSphereProperties },
|
|
103
|
-
{ id: '3d_cylinder', name: '3D Cylinder Properties', description: 'Axis, bases, height, perspective changes', fn: lessonCylinderProperties },
|
|
104
|
-
{ id: '3d_cone', name: '3D Cone Properties', description: 'Apex, base, axis, radius', fn: lessonConeProperties },
|
|
105
|
-
];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
module.exports = {
|
|
109
|
-
lessonBoxProperties,
|
|
110
|
-
lessonSphereProperties,
|
|
111
|
-
lessonCylinderProperties,
|
|
112
|
-
lessonConeProperties,
|
|
113
|
-
get3DLessonCatalog
|
|
114
|
-
};
|