@teachinglab/omd 0.3.7 → 0.3.9
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/canvas/core/canvasConfig.js +3 -3
- package/canvas/core/omdCanvas.js +479 -479
- package/canvas/events/eventManager.js +14 -4
- package/canvas/features/focusFrameManager.js +284 -286
- package/canvas/features/resizeHandleManager.js +482 -0
- package/canvas/index.js +2 -1
- package/canvas/tools/EraserTool.js +321 -322
- package/canvas/tools/PencilTool.js +321 -324
- package/canvas/tools/PointerTool.js +71 -0
- package/canvas/tools/SelectTool.js +902 -457
- package/canvas/tools/toolManager.js +389 -393
- package/canvas/ui/cursor.js +462 -437
- package/canvas/ui/toolbar.js +291 -290
- package/docs/omd-objects.md +258 -0
- package/jsvg/jsvg.js +898 -0
- package/jsvg/jsvgComponents.js +359 -0
- package/omd/nodes/omdEquationSequenceNode.js +1280 -1246
- package/package.json +1 -1
- package/src/json-schemas.md +546 -78
- package/src/omd.js +212 -109
- package/src/omdEquation.js +188 -156
- package/src/omdExpression.js +7 -0
- package/src/omdFunction.js +5 -3
- package/src/omdProblem.js +216 -11
- package/src/omdString.js +12 -1
- package/src/omdUtils.js +84 -0
- package/src/omdVariable.js +1 -0
|
@@ -1,394 +1,390 @@
|
|
|
1
|
-
export class ToolManager {
|
|
2
|
-
/**
|
|
3
|
-
* @param {OMDCanvas} canvas - Canvas instance
|
|
4
|
-
*/
|
|
5
|
-
constructor(canvas) {
|
|
6
|
-
this.canvas = canvas;
|
|
7
|
-
this.tools = new Map();
|
|
8
|
-
this.activeTool = null;
|
|
9
|
-
this.previousTool = null;
|
|
10
|
-
this.isDestroyed = false;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Register a tool with the manager
|
|
15
|
-
* @param {string} name - Tool name
|
|
16
|
-
* @param {Tool} tool - Tool instance
|
|
17
|
-
* @returns {boolean} True if tool was registered successfully
|
|
18
|
-
*/
|
|
19
|
-
registerTool(name, tool) {
|
|
20
|
-
if (this.isDestroyed) {
|
|
21
|
-
console.warn('Cannot register tool on destroyed ToolManager');
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!name || typeof name !== 'string') {
|
|
26
|
-
console.error('Tool name must be a non-empty string');
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!tool || typeof tool.onPointerDown !== 'function') {
|
|
31
|
-
console.error('Tool must implement required methods');
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Check if tool is enabled in config
|
|
36
|
-
if (!this.canvas.config.enabledTools.includes(name)) {
|
|
37
|
-
console.warn(`Tool '${name}' is not enabled in canvas configuration`);
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Set tool name and canvas reference
|
|
42
|
-
tool.name = name;
|
|
43
|
-
tool.canvas = this.canvas;
|
|
44
|
-
|
|
45
|
-
// Store tool
|
|
46
|
-
this.tools.set(name, tool);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
* @
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
*
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
*
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
tool.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
*
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (this.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
this.
|
|
387
|
-
this.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
this.isDestroyed = true;
|
|
391
|
-
|
|
392
|
-
console.log('ToolManager destroyed');
|
|
393
|
-
}
|
|
1
|
+
export class ToolManager {
|
|
2
|
+
/**
|
|
3
|
+
* @param {OMDCanvas} canvas - Canvas instance
|
|
4
|
+
*/
|
|
5
|
+
constructor(canvas) {
|
|
6
|
+
this.canvas = canvas;
|
|
7
|
+
this.tools = new Map();
|
|
8
|
+
this.activeTool = null;
|
|
9
|
+
this.previousTool = null;
|
|
10
|
+
this.isDestroyed = false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Register a tool with the manager
|
|
15
|
+
* @param {string} name - Tool name
|
|
16
|
+
* @param {Tool} tool - Tool instance
|
|
17
|
+
* @returns {boolean} True if tool was registered successfully
|
|
18
|
+
*/
|
|
19
|
+
registerTool(name, tool) {
|
|
20
|
+
if (this.isDestroyed) {
|
|
21
|
+
console.warn('Cannot register tool on destroyed ToolManager');
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!name || typeof name !== 'string') {
|
|
26
|
+
console.error('Tool name must be a non-empty string');
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!tool || typeof tool.onPointerDown !== 'function') {
|
|
31
|
+
console.error('Tool must implement required methods');
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check if tool is enabled in config
|
|
36
|
+
if (!this.canvas.config.enabledTools.includes(name)) {
|
|
37
|
+
console.warn(`Tool '${name}' is not enabled in canvas configuration`);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Set tool name and canvas reference
|
|
42
|
+
tool.name = name;
|
|
43
|
+
tool.canvas = this.canvas;
|
|
44
|
+
|
|
45
|
+
// Store tool
|
|
46
|
+
this.tools.set(name, tool);
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Unregister a tool
|
|
53
|
+
* @param {string} name - Tool name
|
|
54
|
+
* @returns {boolean} True if tool was unregistered
|
|
55
|
+
*/
|
|
56
|
+
unregisterTool(name) {
|
|
57
|
+
const tool = this.tools.get(name);
|
|
58
|
+
if (!tool) return false;
|
|
59
|
+
|
|
60
|
+
// Deactivate if it's the active tool
|
|
61
|
+
if (this.activeTool === tool) {
|
|
62
|
+
this.setActiveTool(null);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Remove from tools
|
|
66
|
+
this.tools.delete(name);
|
|
67
|
+
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set the active tool
|
|
73
|
+
* @param {string|null} toolName - Tool name to activate, or null to deactivate all
|
|
74
|
+
* @returns {boolean} True if tool was activated successfully
|
|
75
|
+
*/
|
|
76
|
+
setActiveTool(toolName) {
|
|
77
|
+
// Deactivate current tool
|
|
78
|
+
if (this.activeTool) {
|
|
79
|
+
try {
|
|
80
|
+
this.activeTool.onDeactivate();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Error deactivating tool:', error);
|
|
83
|
+
}
|
|
84
|
+
this.previousTool = this.activeTool;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Clear active tool if null
|
|
88
|
+
if (!toolName) {
|
|
89
|
+
this.activeTool = null;
|
|
90
|
+
this.canvas.emit('toolChanged', { name: null, tool: null, previous: this.previousTool?.name });
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get new tool
|
|
95
|
+
const newTool = this.tools.get(toolName);
|
|
96
|
+
if (!newTool) {
|
|
97
|
+
console.error(`Tool '${toolName}' not found`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Activate new tool
|
|
102
|
+
this.activeTool = newTool;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
this.activeTool.onActivate();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('Error activating tool:', error);
|
|
108
|
+
this.activeTool = null;
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Update cursor if available
|
|
113
|
+
if (this.canvas.cursor && this.activeTool.getCursor) {
|
|
114
|
+
const cursorType = this.activeTool.getCursor();
|
|
115
|
+
this.canvas.cursor.setShape(cursorType);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Update tool config for cursor
|
|
119
|
+
if (this.canvas.cursor && this.activeTool.config) {
|
|
120
|
+
this.canvas.cursor.updateFromToolConfig(this.activeTool.config);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Emit tool change event
|
|
124
|
+
this.canvas.emit('toolChanged', {
|
|
125
|
+
name: toolName,
|
|
126
|
+
tool: newTool,
|
|
127
|
+
previous: this.previousTool?.name
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the currently active tool
|
|
135
|
+
* @returns {Tool|null} Active tool instance
|
|
136
|
+
*/
|
|
137
|
+
getActiveTool() {
|
|
138
|
+
return this.activeTool;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get tool by name
|
|
143
|
+
* @param {string} name - Tool name
|
|
144
|
+
* @returns {Tool|undefined} Tool instance
|
|
145
|
+
*/
|
|
146
|
+
getTool(name) {
|
|
147
|
+
return this.tools.get(name);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get all registered tool names
|
|
152
|
+
* @returns {Array<string>} Array of tool names
|
|
153
|
+
*/
|
|
154
|
+
getToolNames() {
|
|
155
|
+
return Array.from(this.tools.keys());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get all registered tools
|
|
160
|
+
* @returns {Map<string, Tool>} Map of tools
|
|
161
|
+
*/
|
|
162
|
+
getAllTools() {
|
|
163
|
+
return new Map(this.tools);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get metadata for all tools
|
|
168
|
+
* @returns {Array<Object>} Array of tool metadata
|
|
169
|
+
*/
|
|
170
|
+
getAllToolMetadata() {
|
|
171
|
+
return Array.from(this.tools.entries()).map(([name, tool]) => ({
|
|
172
|
+
name,
|
|
173
|
+
displayName: tool.displayName || name,
|
|
174
|
+
description: tool.description || '',
|
|
175
|
+
shortcut: tool.shortcut || '',
|
|
176
|
+
category: tool.category || 'general',
|
|
177
|
+
icon: tool.icon || 'tool'
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Switch to previous tool
|
|
183
|
+
* @returns {boolean} True if switched successfully
|
|
184
|
+
*/
|
|
185
|
+
switchToPreviousTool() {
|
|
186
|
+
if (this.previousTool) {
|
|
187
|
+
return this.setActiveTool(this.previousTool.name);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Temporarily switch to a tool and back
|
|
194
|
+
* @param {string} toolName - Tool to switch to temporarily
|
|
195
|
+
* @param {Function} callback - Function to execute with temporary tool
|
|
196
|
+
* @returns {Promise<any>} Result of callback
|
|
197
|
+
*/
|
|
198
|
+
async withTemporaryTool(toolName, callback) {
|
|
199
|
+
const currentTool = this.activeTool?.name;
|
|
200
|
+
|
|
201
|
+
if (!this.setActiveTool(toolName)) {
|
|
202
|
+
throw new Error(`Failed to activate temporary tool: ${toolName}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const result = await callback(this.activeTool);
|
|
207
|
+
return result;
|
|
208
|
+
} finally {
|
|
209
|
+
// Restore previous tool
|
|
210
|
+
if (currentTool) {
|
|
211
|
+
this.setActiveTool(currentTool);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update tool configuration
|
|
218
|
+
* @param {string} toolName - Tool name
|
|
219
|
+
* @param {Object} config - Configuration updates
|
|
220
|
+
* @returns {boolean} True if updated successfully
|
|
221
|
+
*/
|
|
222
|
+
updateToolConfig(toolName, config) {
|
|
223
|
+
const tool = this.tools.get(toolName);
|
|
224
|
+
if (!tool) {
|
|
225
|
+
console.error(`Tool '${toolName}' not found`);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (tool.updateConfig) {
|
|
230
|
+
tool.updateConfig(config);
|
|
231
|
+
|
|
232
|
+
// Update cursor if this is the active tool
|
|
233
|
+
if (this.activeTool === tool && this.canvas.cursor) {
|
|
234
|
+
this.canvas.cursor.updateFromToolConfig(tool.config);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.warn(`Tool '${toolName}' does not support configuration updates`);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get tool configuration
|
|
246
|
+
* @param {string} toolName - Tool name
|
|
247
|
+
* @returns {Object|null} Tool configuration
|
|
248
|
+
*/
|
|
249
|
+
getToolConfig(toolName) {
|
|
250
|
+
const tool = this.tools.get(toolName);
|
|
251
|
+
return tool ? tool.config || {} : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Check if a tool is registered
|
|
256
|
+
* @param {string} toolName - Tool name
|
|
257
|
+
* @returns {boolean} True if tool is registered
|
|
258
|
+
*/
|
|
259
|
+
hasTool(toolName) {
|
|
260
|
+
return this.tools.has(toolName);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Check if a tool is enabled in configuration
|
|
265
|
+
* @param {string} toolName - Tool name
|
|
266
|
+
* @returns {boolean} True if tool is enabled
|
|
267
|
+
*/
|
|
268
|
+
isToolEnabled(toolName) {
|
|
269
|
+
return this.canvas.config.enabledTools.includes(toolName);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get tool capabilities
|
|
274
|
+
* @param {string} toolName - Tool name
|
|
275
|
+
* @returns {Object|null} Tool capabilities
|
|
276
|
+
*/
|
|
277
|
+
getToolCapabilities(toolName) {
|
|
278
|
+
const tool = this.tools.get(toolName);
|
|
279
|
+
if (!tool) return null;
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
name: tool.name,
|
|
283
|
+
displayName: tool.displayName,
|
|
284
|
+
description: tool.description,
|
|
285
|
+
shortcut: tool.shortcut,
|
|
286
|
+
category: tool.category,
|
|
287
|
+
supportsKeyboardShortcuts: typeof tool.onKeyboardShortcut === 'function',
|
|
288
|
+
supportsPressure: tool.supportsPressure || false,
|
|
289
|
+
supportsMultiTouch: tool.supportsMultiTouch || false,
|
|
290
|
+
configurable: typeof tool.updateConfig === 'function',
|
|
291
|
+
hasHelp: typeof tool.getHelpText === 'function'
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Handle keyboard shortcuts for tools
|
|
297
|
+
* @param {string} key - Key pressed
|
|
298
|
+
* @param {KeyboardEvent} event - Keyboard event
|
|
299
|
+
* @returns {boolean} True if shortcut was handled
|
|
300
|
+
*/
|
|
301
|
+
handleKeyboardShortcut(key, event) {
|
|
302
|
+
// First, check for tool switching shortcuts
|
|
303
|
+
for (const [name, tool] of this.tools) {
|
|
304
|
+
if (tool.shortcut && tool.shortcut.toLowerCase() === key.toLowerCase()) {
|
|
305
|
+
this.setActiveTool(name);
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Then, delegate to active tool
|
|
311
|
+
if (this.activeTool && this.activeTool.onKeyboardShortcut) {
|
|
312
|
+
return this.activeTool.onKeyboardShortcut(key, event);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get help text for all tools or specific tool
|
|
320
|
+
* @param {string} [toolName] - Optional tool name
|
|
321
|
+
* @returns {string|Object} Help text
|
|
322
|
+
*/
|
|
323
|
+
getHelpText(toolName = null) {
|
|
324
|
+
if (toolName) {
|
|
325
|
+
const tool = this.tools.get(toolName);
|
|
326
|
+
if (tool && tool.getHelpText) {
|
|
327
|
+
return tool.getHelpText();
|
|
328
|
+
}
|
|
329
|
+
return `No help available for tool: ${toolName}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Return help for all tools
|
|
333
|
+
const helpTexts = {};
|
|
334
|
+
for (const [name, tool] of this.tools) {
|
|
335
|
+
if (tool.getHelpText) {
|
|
336
|
+
helpTexts[name] = tool.getHelpText();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return helpTexts;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get current tool manager state
|
|
344
|
+
* @returns {Object} Current state
|
|
345
|
+
*/
|
|
346
|
+
getState() {
|
|
347
|
+
return {
|
|
348
|
+
activeToolName: this.activeTool?.name || null,
|
|
349
|
+
previousToolName: this.previousTool?.name || null,
|
|
350
|
+
registeredTools: this.getToolNames(),
|
|
351
|
+
enabledTools: this.canvas.config.enabledTools,
|
|
352
|
+
isDestroyed: this.isDestroyed
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Destroy the tool manager
|
|
358
|
+
*/
|
|
359
|
+
destroy() {
|
|
360
|
+
if (this.isDestroyed) return;
|
|
361
|
+
|
|
362
|
+
// Deactivate current tool
|
|
363
|
+
if (this.activeTool) {
|
|
364
|
+
try {
|
|
365
|
+
this.activeTool.onDeactivate();
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error('Error deactivating tool during destroy:', error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Destroy all tools if they have a destroy method
|
|
372
|
+
for (const [name, tool] of this.tools) {
|
|
373
|
+
if (tool.destroy) {
|
|
374
|
+
try {
|
|
375
|
+
tool.destroy();
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error(`Error destroying tool '${name}':`, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Clear references
|
|
383
|
+
this.tools.clear();
|
|
384
|
+
this.activeTool = null;
|
|
385
|
+
this.previousTool = null;
|
|
386
|
+
this.canvas = null;
|
|
387
|
+
this.isDestroyed = true;
|
|
388
|
+
|
|
389
|
+
}
|
|
394
390
|
}
|