@pixel-normal-edit/mcp 2.0.10 → 2.1.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/rules/index.js CHANGED
@@ -2,16 +2,16 @@
2
2
  /**
3
3
  * rules/index.js — Rules Module Aggregator
4
4
  *
5
- * Aggregates export tất cả các rules, workflow engine, prerequisite tools.
5
+ * Aggregates and exports all rules, workflow engine, and prerequisite tools.
6
6
  *
7
- * Module này cung cấp:
8
- * 1. rules.js — Định nghĩa rules (naming, constraints, validators)
9
- * 2. workflow.js — Workflow engine (quản thứ tự bước)
10
- * 3. prerequisites.js — MCP Tools cho workflow (start, status, advance, validate)
7
+ * This module provides:
8
+ * 1. rules.js — Defines rules (naming, constraints, validators)
9
+ * 2. workflow.js — Workflow engine (manages step order)
10
+ * 3. prerequisites.js — MCP Tools for workflow (start, status, advance, validate)
11
11
  *
12
- * Cách thêm rule mới:
13
- * - Thêm vào rules.js (NAMING_RULES, WORKFLOW_STEPS, DRAWING_CONSTRAINTS)
14
- * - Nếu cần logic phức tạp, tạo file mới trong rules/ import vào đây
12
+ * How to add a new rule:
13
+ * - Add to rules.js (NAMING_RULES, WORKFLOW_STEPS, DRAWING_CONSTRAINTS)
14
+ * - If complex logic is needed, create a new file in rules/ and import it here
15
15
  */
16
16
 
17
17
  const rules = require('./rules');
@@ -19,7 +19,7 @@ const workflow = require('./workflow');
19
19
  const prerequisites = require('./prerequisites');
20
20
 
21
21
  /**
22
- * Register tất cả workflow/prerequisite tools lên MCP server
22
+ * Register all workflow/prerequisite tools to the MCP server
23
23
  * @param {McpServer} server
24
24
  */
25
25
  function registerAll(server) {
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * prerequisites.js — Prerequisites Check (Điều kiện để chạy)
3
+ * prerequisites.js — Prerequisites Check
4
4
  *
5
- * Đây MCP Tool để agent gọi trước khi bắt đầu vẽ.
6
- * sẽ kiểm tra mọi điều kiện tiên quyết và trả về hướng dẫn chi tiết.
5
+ * This is an MCP Tool for the agent to call before starting to draw.
6
+ * It will check all prerequisites and return detailed instructions.
7
7
  *
8
- * QUAN TRỌNG: Agent BẮT BUỘC phải gọi tool này TRƯỚC TIÊN
9
- * trước khi làm bất kỳ thao tác vẽ nào.
8
+ * IMPORTANT: The Agent MUST call this tool FIRST
9
+ * before performing any drawing operations.
10
10
  */
11
11
 
12
12
  const { z } = require('zod');
13
- const { validateCanvasName, validateCanvasSize, DRAWING_CONSTRAINTS, WORKFLOW_STEPS } = require('./rules');
13
+ const { validateCanvasName, validateCanvasSize, DRAWING_CONSTRAINTS, MODES, DRAWING_PHASES } = require('./rules');
14
14
  const workflow = require('./workflow');
15
15
  const { registerTool } = require('../core/server');
16
16
 
@@ -19,53 +19,39 @@ const { registerTool } = require('../core/server');
19
19
  * @param {McpServer} server
20
20
  */
21
21
  function register(server) {
22
- // ── Tool 1: workflow_start — Bắt đầu workflow ─────────────────────────
23
22
  registerTool(server, 'workflow_start',
24
- `🚀 BẮT BUỘC: Gọi tool này ĐẦU TIÊN trước khi vẽ bất cứ thứ gì.
25
- Tool này khởi tạo một workflow vẽ với các bước tuần tự.
26
- Agent PHẢI làm theo đúng thứ tự các bước.
27
-
28
- Các bước workflow:
29
- 1. create_canvas — Tạo canvas mới
30
- 2. name_canvas — Đặt tên cho canvas
31
- 3. resize_canvas — Resize nếu cần
32
- 4. enable_animation — Bật animation nếu cần
33
- 5. setup_layers — Thiết lập layers
34
- 6. prepare_colors — Chuẩn bị màu sắc
35
- 7. draw — Vẽ (chỉ được vẽ sau các bước trên)
36
- 8. review — Kiểm tra kết quả
37
- 9. finalize — Lưu và hoàn tất`,
38
- {},
39
- () => {
23
+ `🚀 REQUIRED: Call this tool FIRST before drawing anything.
24
+ This tool initializes a drawing workflow with sequential steps for a specific mode.
25
+ Modes: CREATE_IMAGE, EDIT_IMAGE, CREATE_ANIMATION`,
26
+ { mode: z.enum([MODES.CREATE_IMAGE, MODES.EDIT_IMAGE, MODES.CREATE_ANIMATION]).describe('Workflow mode to start') },
27
+ (p) => {
40
28
  const session = require('../core/command-bus').SESSION;
41
- const state = workflow.start(session);
29
+ const state = workflow.start(session, p.mode);
42
30
  return {
43
- content: [{ type: 'text', text: `🚀 **Workflow đã bắt đầu!**\n\n${workflow.getProgressReport(session)}\n\n---\nBắt đầu với bước: **${state.steps[0].label}** → ${state.steps[0].description}` }]
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}` }]
44
32
  };
45
33
  });
46
34
 
47
- // ── Tool 2: workflow_status — Xem trạng thái hiện tại ─────────────────
48
35
  registerTool(server, 'workflow_status',
49
- 'Xem tiến trình workflow hiện tại: đang bước nào, đã làm gì, cần làm gì tiếp theo.',
36
+ 'View current workflow progress: current mode, layer, frame, steps and statuses.',
50
37
  {},
51
38
  () => {
52
39
  const session = require('../core/command-bus').SESSION;
53
40
  const report = workflow.getProgressReport(session);
54
41
  const current = workflow.getCurrentStep(session);
55
42
  if (!current) {
56
- return { content: [{ type: 'text', text: '⚠️ Chưa bắt đầu workflow. Gọi **workflow_start** trước.' }] };
43
+ return { content: [{ type: 'text', text: '⚠️ Workflow has not started. Call **workflow_start** first.' }] };
57
44
  }
58
45
  return {
59
46
  content: [{
60
47
  type: 'text',
61
- text: `${report}\n\n👉 **Đang cần:** ${current.label} — ${current.description}`
48
+ text: `${report}\n\n👉 **Pending:** ${current.label} — ${current.description}`
62
49
  }]
63
50
  };
64
51
  });
65
52
 
66
- // ── Tool 3: workflow_advance — Hoàn thành bước hiện tại ───────────────
67
53
  registerTool(server, 'workflow_advance',
68
- '✅ Đánh dấu bước hiện tại hoàn thành chuyển sang bước tiếp theo. Chỉ gọi SAU KHI đã làm xong bước hiện tại.',
54
+ '✅ Mark current step as complete and move to the next step. Only call AFTER successfully finishing the current step.',
69
55
  {},
70
56
  () => {
71
57
  const session = require('../core/command-bus').SESSION;
@@ -79,66 +65,137 @@ Các bước workflow:
79
65
  return { content: [{ type: 'text', text: result.message }] };
80
66
  });
81
67
 
82
- // ── Tool 4: workflow_validate — Kiểm tra trước khi gọi tool ──────────
83
68
  registerTool(server, 'workflow_validate',
84
- `🔍 Kiểm tra xem agent được phép thực hiện một hành động nào đó không.
85
- Gọi tool này TRƯỚC khi làm bất kỳ thao tác vẽ nào để đảm bảo đúng thứ tự workflow.
86
-
87
- Các stepId hợp lệ:
88
- - create_canvas
89
- - name_canvas
90
- - resize_canvas
91
- - enable_animation
92
- - setup_layers
93
- - prepare_colors
94
- - draw
95
- - review
96
- - finalize`,
97
- { stepId: z.string().describe('ID của bước muốn kiểm tra (vd: "draw", "resize_canvas")') },
69
+ `🔍 Check if the agent is allowed to perform a certain action.
70
+ Call this tool BEFORE performing operations to ensure correct workflow order.`,
71
+ { stepId: z.string().describe('ID of the step to validate (e.g., "INITIALIZE_CANVAS")') },
98
72
  (p) => {
99
73
  const session = require('../core/command-bus').SESSION;
100
74
  const result = workflow.validateStep(session, p.stepId);
101
75
  if (!result.allowed) {
102
76
  return { isError: true, content: [{ type: 'text', text: result.message }] };
103
77
  }
104
- return { content: [{ type: 'text', text: result.message || `✅ Bạn thể thực hiện "${p.stepId}". Hãy tiếp tục!` }] };
78
+ return { content: [{ type: 'text', text: result.message || `✅ You can perform "${p.stepId}". Please proceed!` }] };
79
+ });
80
+
81
+ registerTool(server, 'workflow_set_layer',
82
+ 'Set the active layer for the workflow tracking.',
83
+ { layer: z.number().int().describe('The layer index') },
84
+ (p) => {
85
+ const session = require('../core/command-bus').SESSION;
86
+ const success = workflow.setLayerAndFrame(session, p.layer, undefined);
87
+ if (!success) {
88
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
89
+ }
90
+ return { content: [{ type: 'text', text: `✅ Active layer set to ${p.layer}` }] };
91
+ });
92
+
93
+ registerTool(server, 'workflow_set_frame',
94
+ 'Set the active frame for the workflow tracking (useful for animation mode).',
95
+ { frame: z.number().int().describe('The frame index') },
96
+ (p) => {
97
+ const session = require('../core/command-bus').SESSION;
98
+ const success = workflow.setLayerAndFrame(session, undefined, p.frame);
99
+ if (!success) {
100
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
101
+ }
102
+ return { content: [{ type: 'text', text: `✅ Active frame set to ${p.frame}` }] };
103
+ });
104
+
105
+ registerTool(server, 'workflow_set_phase',
106
+ 'Set the active drawing phase (e.g. PHASE_0_SKETCH_BLOCKING, PHASE_1_BACKGROUND, etc.).',
107
+ { phase: z.enum(DRAWING_PHASES).describe('The drawing phase') },
108
+ (p) => {
109
+ const session = require('../core/command-bus').SESSION;
110
+ const success = workflow.setPhase(session, p.phase);
111
+ if (!success) {
112
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
113
+ }
114
+ return { content: [{ type: 'text', text: `✅ Active phase set to ${p.phase}` }] };
115
+ });
116
+
117
+ registerTool(server, 'workflow_setup_animation',
118
+ 'Declare animation metadata for tracking (fps, total_frames, loop).',
119
+ {
120
+ fps: z.number().int().optional().describe('Frames per second'),
121
+ total_frames: z.number().int().optional().describe('Total frames in the animation'),
122
+ loop: z.boolean().optional().describe('Whether the animation loops')
123
+ },
124
+ (p) => {
125
+ const session = require('../core/command-bus').SESSION;
126
+ const success = workflow.setAnimationMeta(session, p.fps, p.total_frames, p.loop);
127
+ if (!success) {
128
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
129
+ }
130
+ return { content: [{ type: 'text', text: `✅ Animation meta updated: FPS=${p.fps}, Frames=${p.total_frames}, Loop=${p.loop}` }] };
131
+ });
132
+
133
+ registerTool(server, 'workflow_create_input_request',
134
+ 'Pause the workflow and ask the user for specific input via the UI (e.g. CANVAS_SIZE, APPROVE_PLAN, SELECT_EDIT_REGION).',
135
+ {
136
+ type: z.enum(['CANVAS_SIZE', 'APPROVE_PLAN', 'REVIEW_RESULT', 'SELECT_EDIT_REGION', 'ANIMATION_SETUP']).describe('The type of request'),
137
+ fields: z.record(z.any()).optional().describe('Context or fields for the request')
138
+ },
139
+ async (p) => {
140
+ const { sendCommand, SESSION } = require('../core/command-bus');
141
+ const reqId = workflow.createInputRequest(SESSION, p.type, p.fields || {});
142
+ if (!reqId) {
143
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
144
+ }
145
+
146
+ const res = await sendCommand({ action: 'showUserInputRequest', type: p.type, fields: p.fields || {}, reqId }, 35000);
147
+
148
+ if (!res.isError) {
149
+ workflow.completeInputRequest(SESSION, reqId, res.content[0].text);
150
+ }
151
+
152
+ return res;
153
+ });
154
+
155
+ registerTool(server, 'workflow_require_approval',
156
+ 'Pause the workflow and require user approval before continuing.',
157
+ { required: z.boolean().describe('True to pause and wait for user, False to resume') },
158
+ (p) => {
159
+ const session = require('../core/command-bus').SESSION;
160
+ const success = workflow.setApprovalRequired(session, p.required);
161
+ if (!success) {
162
+ return { isError: true, content: [{ type: 'text', text: '❌ Workflow not started' }] };
163
+ }
164
+ return { content: [{ type: 'text', text: p.required ? '✅ Workflow paused, waiting for user approval.' : '✅ Workflow resumed.' }] };
105
165
  });
106
166
 
107
- // ── Tool 5: workflow_reset — Làm lại từ đầu ──────────────────────────
108
167
  registerTool(server, 'workflow_reset',
109
- '🔄 Reset workflow về trạng thái ban đầu. Xoá toàn bộ tiến trình đã làm.',
168
+ '🔄 Reset workflow to initial state. Clears all progress.',
110
169
  {},
111
170
  () => {
112
171
  const session = require('../core/command-bus').SESSION;
113
172
  workflow.reset(session);
114
- return { content: [{ type: 'text', text: '🔄 Workflow đã được reset. Hãy bắt đầu lại từ đầu.' }] };
173
+ return { content: [{ type: 'text', text: '🔄 Workflow reset. Please start from the beginning.' }] };
115
174
  });
116
175
 
117
- // ── Tool 6: validate_canvas_name — Kiểm tra tên canvas ───────────────
118
176
  registerTool(server, 'validate_canvas_name',
119
- `Kiểm tra tên canvas hợp lệ không trước khi đặt.
120
- Quy tắc: 3-64 tự, chỉ gồm chữ, số, _, -, khoảng trắng.
121
- dụ: "Bức_tranh_1", "My Canvas 2024"`,
122
- { name: z.string().describe('Tên canvas muốn kiểm tra') },
177
+ `Validate canvas name before setting it.
178
+ Rules: 3-64 characters, letters, numbers, _, -, and spaces only.
179
+ Example: "Painting_1", "My Canvas 2024"`,
180
+ { name: z.string().describe('Canvas name to validate') },
123
181
  (p) => {
124
182
  const result = validateCanvasName(p.name);
125
183
  if (!result.valid) {
126
184
  return { isError: true, content: [{ type: 'text', text: `❌ ${result.message}` }] };
127
185
  }
128
- return { content: [{ type: 'text', text: `✅ Tên "${p.name}" hợp lệ!` }] };
186
+ return { content: [{ type: 'text', text: `✅ Name "${p.name}" is valid!` }] };
129
187
  });
130
188
 
131
- // ── Tool 7: validate_canvas_size — Kiểm tra kích thước canvas ────────
132
189
  registerTool(server, 'validate_canvas_size',
133
- `Kiểm tra kích thước canvas nằm trong giới hạn cho phép không.
134
- Giới hạn: ${DRAWING_CONSTRAINTS.minCanvasSize.width}x${DRAWING_CONSTRAINTS.minCanvasSize.height} đến ${DRAWING_CONSTRAINTS.maxCanvasSize.width}x${DRAWING_CONSTRAINTS.maxCanvasSize.height}`,
135
- { width: z.number().int().describe('Chiều rộng'), height: z.number().int().describe('Chiều cao') },
190
+ `Validate if canvas size is within allowed limits.
191
+ Limits: ${DRAWING_CONSTRAINTS.minCanvasSize.width}x${DRAWING_CONSTRAINTS.minCanvasSize.height} to ${DRAWING_CONSTRAINTS.maxCanvasSize.width}x${DRAWING_CONSTRAINTS.maxCanvasSize.height}`,
192
+ { width: z.number().int().describe('Width'), height: z.number().int().describe('Height') },
136
193
  (p) => {
137
194
  const result = validateCanvasSize(p.width, p.height);
138
195
  if (!result.valid) {
139
196
  return { isError: true, content: [{ type: 'text', text: `❌ ${result.message}` }] };
140
197
  }
141
- return { content: [{ type: 'text', text: `✅ Kích thước ${p.width}x${p.height} hợp lệ!` }] };
198
+ return { content: [{ type: 'text', text: `✅ Size ${p.width}x${p.height} is valid!` }] };
142
199
  });
143
200
  }
144
201
 
package/rules/rules.js CHANGED
@@ -2,66 +2,103 @@
2
2
  /**
3
3
  * rules.js — Core Drawing Rules & Constraints
4
4
  *
5
- * QUAN TRỌNG: Mọi function trong đây luật BẮT BUỘC.
6
- * Agent KHÔNG được tự ý bỏ qua hay làm sai thứ tự.
7
- *
8
- * Các rules được chia làm 3 loại:
9
- * 1. naming — Quy tắc đặt tên tab/canvas
10
- * 2. workflow — Quy tắc thứ tự các bước
11
- * 3. drawing — Quy tắc về vẽ (kích thước, màu sắc, v.v.)
5
+ * IMPORTANT: Every function in here is a STRICT rule.
6
+ * The Agent MUST NOT skip or execute them out of order.
12
7
  */
13
8
 
14
- // ── Loại 1: Naming Rules ────────────────────────────────────────────────────
15
9
  const NAMING_RULES = {
16
10
  canvasTab: {
17
11
  pattern: /^[A-Za-z0-9_\-\sÀ-ỹ]{3,64}$/,
18
- message: 'Tên canvas phải từ 3-64 tự, chỉ gồm chữ, số, _, -, khoảng trắng',
19
- example: 'Bức_tranh_1',
12
+ message: 'Canvas name must be 3-64 characters long, containing only letters, numbers, _, -, and spaces',
13
+ example: 'Painting_1',
20
14
  },
21
15
  stamp: {
22
16
  pattern: /^[a-z][a-z0-9_]{2,32}$/,
23
- message: 'Tên stamp phải bắt đầu bằng chữ thường, 3-32 tự, chỉ gồm a-z, 0-9, _',
17
+ message: 'Stamp name must start with a lowercase letter, 3-32 characters long, containing only a-z, 0-9, _',
24
18
  example: 'my_tree_01',
25
19
  },
26
20
  anchor: {
27
21
  pattern: /^[a-z][a-z0-9_]{2,32}$/,
28
- message: 'Tên anchor phải bắt đầu bằng chữ thường, 3-32 tự',
22
+ message: 'Anchor name must start with a lowercase letter, 3-32 characters long',
29
23
  example: 'head_top',
30
24
  },
31
25
  };
32
26
 
33
- // ── Loại 2: Workflow Steps (thứ tự bắt buộc) ──────────────────────────────
34
- const WORKFLOW_STEPS = [
35
- { id: 'create_canvas', label: 'Tạo canvas', description: 'Tạo tab mới với kích thước phù hợp' },
36
- { id: 'name_canvas', label: 'Đặt tên canvas', description: 'Đặt tên cho canvas (3-64 ký tự)' },
37
- { id: 'resize_canvas', label: 'Resize canvas', description: 'Điều chỉnh kích thước canvas nếu cần' },
38
- { id: 'enable_animation', label: 'Bật animation', description: 'Bật chế độ animation nếu cần nhiều frame' },
39
- { id: 'setup_layers', label: 'Thiết lập layers', description: 'Tạo/sắp xếp layers trước khi vẽ' },
40
- { id: 'prepare_colors', label: 'Chuẩn bị màu sắc', description: 'Chọn màu chính/phụ trước khi vẽ' },
41
- { id: 'draw', label: 'Vẽ', description: 'Thực hiện vẽ theo đúng kỹ thuật' },
42
- { id: 'review', label: 'Kiểm tra', description: 'Xem lại kết quả sau khi vẽ' },
43
- { id: 'finalize', label: 'Hoàn tất', description: 'Lưu và hoàn tất tác phẩm' },
27
+ const MODES = {
28
+ CREATE_IMAGE: 'CREATE_IMAGE',
29
+ EDIT_IMAGE: 'EDIT_IMAGE',
30
+ CREATE_ANIMATION: 'CREATE_ANIMATION'
31
+ };
32
+
33
+ const STATUSES = {
34
+ PENDING: 'PENDING',
35
+ IN_PROGRESS: 'IN_PROGRESS',
36
+ WAITING_FOR_USER: 'WAITING_FOR_USER',
37
+ COMPLETED: 'COMPLETED',
38
+ FAILED: 'FAILED',
39
+ CANCELLED: 'CANCELLED'
40
+ };
41
+
42
+ const DRAWING_PHASES = [
43
+ 'PHASE_0_SKETCH_BLOCKING',
44
+ 'PHASE_1_BACKGROUND',
45
+ 'PHASE_2_MAIN_SHAPES',
46
+ 'PHASE_3_LINE_SILHOUETTE',
47
+ 'PHASE_4_BASE_COLORS',
48
+ 'PHASE_5_SHADING',
49
+ 'PHASE_6_LIGHTING',
50
+ 'PHASE_7_DETAILS',
51
+ 'PHASE_8_FINAL_REVIEW'
52
+ ];
53
+
54
+ // Workflow Steps are now categorized by MODE
55
+ const WORKFLOW_STEPS = {
56
+ CREATE_IMAGE: [
57
+ { id: 'INITIALIZE_CANVAS', label: 'Khởi tạo Canvas', description: 'Kiểm tra và khởi tạo kích thước canvas' },
58
+ { id: 'ANALYZE_REQUEST', label: 'Phân tích yêu cầu', description: 'Phân tích yêu cầu của người dùng thành các thành phần có cấu trúc' },
59
+ { id: 'PLAN_DRAWING', label: 'Lập kế hoạch vẽ', description: 'Tạo kế hoạch vẽ theo thứ tự từ tổng thể đến chi tiết (cần user xác nhận)' },
60
+ { id: 'EXECUTE_PHASES', label: 'Thực hiện vẽ theo giai đoạn', description: 'Vẽ theo từng giai đoạn (PHASE_0 -> PHASE_8)' },
61
+ { id: 'FINALIZE', label: 'Hoàn thiện', description: 'Lưu và hoàn tất ảnh' }
62
+ ],
63
+ EDIT_IMAGE: [
64
+ { id: 'ANALYZE_REQUEST', label: 'Nhận yêu cầu sửa', description: 'Phân tích chính xác nội dung cần sửa' },
65
+ { id: 'GET_CURRENT_IMAGE', label: 'Lấy ảnh hiện tại', description: 'Lấy ảnh hiện tại làm nguồn gốc' },
66
+ { id: 'COMPARE_AND_LOCATE', label: 'So sánh và xác định', description: 'Xác định vùng hoặc layer cần chỉnh sửa' },
67
+ { id: 'EDIT_TARGET_AREA', label: 'Thực hiện chỉnh sửa', description: 'Chỉ sửa phần được yêu cầu trong phạm vi khoanh vùng' },
68
+ { id: 'CHECK_HARMONY', label: 'Kiểm tra sự tương thích', description: 'Kiểm tra sự hòa hợp với các vùng xung quanh và điều chỉnh phần chuyển tiếp' },
69
+ { id: 'FINALIZE', label: 'Cập nhật thay đổi', description: 'Cập nhật thay đổi vào ảnh hiện tại và kiểm tra kết quả' }
70
+ ],
71
+ CREATE_ANIMATION: [
72
+ { id: 'INITIALIZE_CANVAS_AND_ANIMATION', label: 'Khởi tạo Canvas và Animation', description: 'Xác định kích thước, fps, total_frames, loop' },
73
+ { id: 'ANALYZE_ANIMATION', label: 'Phân tích animation', description: 'Phân tích đối tượng, chuyển động, keyframe, layer...' },
74
+ { id: 'PROCESS_FRAMES', label: 'Tạo và xử lý từng frame', description: 'Xử lý từng frame theo đúng thứ tự (0 -> n)' },
75
+ { id: 'CHECK_CONTINUITY', label: 'Kiểm tra tính liên tục', description: 'Kiểm tra tính liên tục của animation giữa các frame' },
76
+ { id: 'FINALIZE', label: 'Hoàn thiện', description: 'Lưu và hoàn tất animation' }
77
+ ]
78
+ };
79
+
80
+ const DRAWING_STEPS = [
81
+ 'EXECUTE_PHASES',
82
+ 'EDIT_TARGET_AREA',
83
+ 'PROCESS_FRAMES'
44
84
  ];
45
85
 
46
- // ── Loại 3: Drawing Constraints ─────────────────────────────────────────────
47
86
  const DRAWING_CONSTRAINTS = {
48
87
  maxCanvasSize: { width: 1024, height: 1024 },
49
88
  minCanvasSize: { width: 8, height: 8 },
50
- allowedColors: null, // null = all hex colors allowed
89
+ allowedColors: null,
51
90
  maxStampNameLength: 32,
52
91
  maxAnchorNameLength: 32,
53
92
  };
54
93
 
55
- // ── Validator Functions ──────────────────────────────────────────────────────
56
-
57
94
  /**
58
- * Kiểm tra tên canvas hợp lệ không
95
+ * Validate if the canvas name is valid
59
96
  * @param {string} name
60
97
  * @returns {{ valid: boolean, message?: string }}
61
98
  */
62
99
  function validateCanvasName(name) {
63
100
  if (!name || typeof name !== 'string') {
64
- return { valid: false, message: 'Tên canvas không được để trống' };
101
+ return { valid: false, message: 'Canvas name cannot be empty' };
65
102
  }
66
103
  if (!NAMING_RULES.canvasTab.pattern.test(name)) {
67
104
  return { valid: false, message: NAMING_RULES.canvasTab.message };
@@ -70,13 +107,13 @@ function validateCanvasName(name) {
70
107
  }
71
108
 
72
109
  /**
73
- * Kiểm tra tên stamp hợp lệ không
110
+ * Validate if the stamp name is valid
74
111
  * @param {string} name
75
112
  * @returns {{ valid: boolean, message?: string }}
76
113
  */
77
114
  function validateStampName(name) {
78
115
  if (!name || typeof name !== 'string') {
79
- return { valid: false, message: 'Tên stamp không được để trống' };
116
+ return { valid: false, message: 'Stamp name cannot be empty' };
80
117
  }
81
118
  if (!NAMING_RULES.stamp.pattern.test(name)) {
82
119
  return { valid: false, message: NAMING_RULES.stamp.message };
@@ -85,7 +122,7 @@ function validateStampName(name) {
85
122
  }
86
123
 
87
124
  /**
88
- * Kiểm tra kích thước canvas nằm trong giới hạn không
125
+ * Validate if the canvas size is within limits
89
126
  * @param {number} w
90
127
  * @param {number} h
91
128
  * @returns {{ valid: boolean, message?: string }}
@@ -93,16 +130,20 @@ function validateStampName(name) {
93
130
  function validateCanvasSize(w, h) {
94
131
  const { minCanvasSize, maxCanvasSize } = DRAWING_CONSTRAINTS;
95
132
  if (w < minCanvasSize.width || h < minCanvasSize.height) {
96
- return { valid: false, message: `Canvas tối thiểu ${minCanvasSize.width}x${minCanvasSize.height}` };
133
+ return { valid: false, message: `Minimum canvas size is ${minCanvasSize.width}x${minCanvasSize.height}` };
97
134
  }
98
135
  if (w > maxCanvasSize.width || h > maxCanvasSize.height) {
99
- return { valid: false, message: `Canvas tối đa ${maxCanvasSize.width}x${maxCanvasSize.height}` };
136
+ return { valid: false, message: `Maximum canvas size is ${maxCanvasSize.width}x${maxCanvasSize.height}` };
100
137
  }
101
138
  return { valid: true };
102
139
  }
103
140
 
104
141
  module.exports = {
105
142
  NAMING_RULES,
143
+ MODES,
144
+ STATUSES,
145
+ DRAWING_PHASES,
146
+ DRAWING_STEPS,
106
147
  WORKFLOW_STEPS,
107
148
  DRAWING_CONSTRAINTS,
108
149
  validateCanvasName,