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