@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,268 @@
1
+ /**
2
+ * Utility functions for node operations in step visualizations
3
+ */
4
+ export class omdStepVisualizerNodeUtils {
5
+ /**
6
+ * Checks if a node is a leaf node (constant, variable, etc.)
7
+ * @param {omdNode} node - Node to check
8
+ * @returns {boolean} True if it's a leaf node
9
+ */
10
+ static isLeafNode(node) {
11
+ if (!node) return false;
12
+
13
+ const leafTypes = [
14
+ 'omdConstantNode',
15
+ 'omdVariableNode',
16
+ 'omdOperatorNode'
17
+ ];
18
+
19
+ if (node.constructor && leafTypes.includes(node.type)) {
20
+ return true;
21
+ }
22
+
23
+ if (node.type === 'constant' || node.type === 'variable' || node.type === 'operator') {
24
+ return true;
25
+ }
26
+
27
+ if (!node.left && !node.right && !node.argument && !node.expression) {
28
+ return true;
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ /**
35
+ * Checks if a node is a binary operation node
36
+ * @param {omdNode} node - Node to check
37
+ * @returns {boolean} True if it's a binary node
38
+ */
39
+ static isBinaryNode(node) {
40
+ if (!node) return false;
41
+ return node.type === 'omdBinaryExpressionNode' && node.left && node.right;
42
+ }
43
+
44
+ /**
45
+ * Checks if a node is a unary operation node
46
+ * @param {omdNode} node - Node to check
47
+ * @returns {boolean} True if it's a unary node
48
+ */
49
+ static isUnaryNode(node) {
50
+ if (!node) return false;
51
+ return node.type === 'omdUnaryExpressionNode' && node.argument;
52
+ }
53
+
54
+ /**
55
+ * Checks if a node has an expression property
56
+ * @param {omdNode} node - Node to check
57
+ * @returns {boolean} True if it has an expression
58
+ */
59
+ static hasExpression(node) {
60
+ if (!node) return false;
61
+ return node.expression !== undefined;
62
+ }
63
+
64
+ /**
65
+ * Gets the value of a leaf node for comparison
66
+ * @param {omdNode} node - Node to get value from
67
+ * @returns {string} String representation of the node's value
68
+ */
69
+ static getNodeValue(node) {
70
+ if (!node) return 'undefined';
71
+
72
+ try {
73
+ if (node.constructor && node.type === 'omdVariableNode' && node.name !== undefined) {
74
+ return String(node.name);
75
+ }
76
+
77
+ if (node.constructor && node.type === 'omdConstantNode' && typeof node.number === 'number') {
78
+ return String(node.number);
79
+ }
80
+
81
+ if (node.isConstant && node.isConstant() && node.getValue && typeof node.getValue === 'function') {
82
+ return String(node.getValue());
83
+ }
84
+
85
+ if (node.toString && typeof node.toString === 'function') {
86
+ return node.toString();
87
+ }
88
+
89
+ if (node.value !== undefined) {
90
+ return String(node.value);
91
+ }
92
+
93
+ if (node.number !== undefined) {
94
+ return String(node.number);
95
+ }
96
+
97
+ return node.constructor ? node.type : 'unknown';
98
+ } catch (err) {
99
+ console.error('Error getting node value:', err);
100
+ return 'error';
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Finds all leaf nodes in a tree
106
+ * @param {omdNode} node - Root node to search from
107
+ * @returns {Array} Array of leaf nodes
108
+ */
109
+ static findLeafNodes(node) {
110
+ if (!node) return [];
111
+
112
+ if (this.isLeafNode(node)) {
113
+ return [node];
114
+ }
115
+
116
+ const leafNodes = [];
117
+
118
+ try {
119
+ if (this.isBinaryNode(node)) {
120
+ leafNodes.push(...this.findLeafNodes(node.left));
121
+ leafNodes.push(...this.findLeafNodes(node.right));
122
+ } else if (this.isUnaryNode(node)) {
123
+ leafNodes.push(...this.findLeafNodes(node.argument));
124
+ } else if (this.hasExpression(node)) {
125
+ leafNodes.push(...this.findLeafNodes(node.expression));
126
+ }
127
+ } catch (err) {
128
+ console.error('Error finding leaf nodes:', err, node);
129
+ }
130
+
131
+ return leafNodes;
132
+ }
133
+
134
+ /**
135
+ * Finds all variable nodes in a tree
136
+ * @param {omdNode} node - Root node to search from
137
+ * @returns {Array} Array of variable nodes
138
+ */
139
+ static findVariableNodes(node) {
140
+ const leaves = this.findLeafNodes(node);
141
+ return leaves.filter(leaf => leaf.constructor && leaf.type === 'omdVariableNode');
142
+ }
143
+
144
+ /**
145
+ * Finds all constant nodes in a tree
146
+ * @param {omdNode} node - Root node to search from
147
+ * @returns {Array} Array of constant nodes
148
+ */
149
+ static findConstantNodes(node) {
150
+ const leaves = this.findLeafNodes(node);
151
+ return leaves.filter(leaf => leaf.constructor && leaf.type === 'omdConstantNode');
152
+ }
153
+
154
+ /**
155
+ * Finds leaf nodes with a specific value
156
+ * @param {omdNode} node - Root node to search from
157
+ * @param {string} value - Value to search for
158
+ * @returns {Array} Array of matching leaf nodes
159
+ */
160
+ static findLeafNodesWithValue(node, value) {
161
+ const leaves = this.findLeafNodes(node);
162
+
163
+ const exactMatches = leaves.filter(leaf => leaf.toString() === value);
164
+ if (exactMatches.length > 0) {
165
+ return exactMatches;
166
+ }
167
+
168
+ const numValue = parseFloat(value);
169
+ if (!isNaN(numValue)) {
170
+ const numericMatches = leaves.filter(leaf => {
171
+ if (leaf.constructor && leaf.type === 'omdConstantNode') {
172
+ if (leaf.number !== undefined) {
173
+ return Math.abs(leaf.number - numValue) < 0.0001;
174
+ }
175
+ }
176
+ return false;
177
+ });
178
+
179
+ if (numericMatches.length > 0) {
180
+ return numericMatches;
181
+ }
182
+ }
183
+
184
+ return leaves.filter(leaf => {
185
+ const leafStr = leaf.toString();
186
+ return leafStr.includes(value) || value.includes(leafStr);
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Finds all nodes in a tree (not just leaf nodes)
192
+ * @param {omdNode} node - Root node to search from
193
+ * @returns {Array} Array of all nodes in the tree
194
+ */
195
+ static findAllNodes(node) {
196
+ if (!node) return [];
197
+
198
+ const allNodes = [node];
199
+
200
+ try {
201
+ if (node.childList && Array.isArray(node.childList)) {
202
+ node.childList.forEach(child => {
203
+ if (child && child !== node.backRect) {
204
+ allNodes.push(...this.findAllNodes(child));
205
+ }
206
+ });
207
+ }
208
+
209
+ if (node.argumentNodeList) {
210
+ Object.values(node.argumentNodeList).forEach(child => {
211
+ if (child && child !== node) {
212
+ if (Array.isArray(child)) {
213
+ child.forEach(item => {
214
+ if (item && item !== node) {
215
+ allNodes.push(...this.findAllNodes(item));
216
+ }
217
+ });
218
+ } else {
219
+ allNodes.push(...this.findAllNodes(child));
220
+ }
221
+ }
222
+ });
223
+ }
224
+ } catch (err) {
225
+ console.error('Error finding all nodes:', err, node);
226
+ }
227
+
228
+ return allNodes;
229
+ }
230
+
231
+ /**
232
+ * Finds the rightmost leaf node with a specific value in a tree
233
+ * @param {omdNode} node - Root node to search from
234
+ * @param {string} value - Value to search for
235
+ * @returns {omdNode|null} The rightmost matching leaf node, or null if not found
236
+ */
237
+ static findRightmostNodeWithValue(node, value) {
238
+ const leaves = this.findLeafNodes(node);
239
+
240
+ const matchingNodes = leaves.filter(leaf => {
241
+ const leafStr = leaf.toString();
242
+ return leafStr === value ||
243
+ (leaf.constructor &&
244
+ leaf.type === 'omdConstantNode' &&
245
+ leaf.number !== undefined &&
246
+ Math.abs(leaf.number - parseFloat(value)) < 0.0001);
247
+ });
248
+
249
+ if (matchingNodes.length === 0) {
250
+ return null;
251
+ }
252
+
253
+ const subtractedNodes = matchingNodes.filter(leaf => {
254
+ const parent = leaf.parent;
255
+ return parent &&
256
+ parent.constructor &&
257
+ parent.type === 'omdBinaryExpressionNode' &&
258
+ parent.operation === 'subtract' &&
259
+ parent.right === leaf;
260
+ });
261
+
262
+ if (subtractedNodes.length > 0) {
263
+ return subtractedNodes[subtractedNodes.length - 1];
264
+ }
265
+
266
+ return matchingNodes[matchingNodes.length - 1];
267
+ }
268
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * omdTranscriptionService - AI-powered transcription service for handwritten content
3
+ */
4
+
5
+ /**
6
+ * Transcription service using server endpoint
7
+ */
8
+ export class omdTranscriptionService {
9
+ constructor(options = {}) {
10
+ this.options = {
11
+ endpoint: options.endpoint || this._getDefaultEndpoint(),
12
+ defaultProvider: options.defaultProvider || 'gemini',
13
+ ...options
14
+ };
15
+
16
+ console.log('omdTranscriptionService constructor called with options:', {
17
+ endpoint: this.options.endpoint,
18
+ defaultProvider: this.options.defaultProvider
19
+ });
20
+ }
21
+
22
+ /**
23
+ * Get the default endpoint based on current environment
24
+ * @private
25
+ */
26
+ _getDefaultEndpoint() {
27
+ return '/.netlify/functions/transcribe';
28
+ }
29
+
30
+ /**
31
+ * Convert blob to base64
32
+ * @private
33
+ */
34
+ async _blobToBase64(blob) {
35
+ return new Promise((resolve, reject) => {
36
+ const reader = new FileReader();
37
+ reader.onload = () => {
38
+ const base64 = reader.result.split(',')[1];
39
+ resolve(base64);
40
+ };
41
+ reader.onerror = reject;
42
+ reader.readAsDataURL(blob);
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Transcribe an image using the server endpoint
48
+ * @param {Blob} imageBlob - The image blob to transcribe
49
+ * @param {Object} options - Transcription options
50
+ * @param {string} options.prompt - Custom prompt for transcription
51
+ * @returns {Promise<Object>} Transcription result
52
+ */
53
+ async transcribe(imageBlob, options = {}) {
54
+ try {
55
+ console.log('Starting transcription with endpoint:', this.options.endpoint);
56
+
57
+ // Convert blob to base64
58
+ const base64Image = await this._blobToBase64(imageBlob);
59
+
60
+ // Call server endpoint
61
+ const response = await fetch(this.options.endpoint, {
62
+ method: 'POST',
63
+ headers: {
64
+ 'Content-Type': 'application/json'
65
+ },
66
+ body: JSON.stringify({
67
+ imageBase64: base64Image,
68
+ prompt: options.prompt || 'Please transcribe the handwritten mathematical expression in this image. Return only the mathematical expression in pure format (no LaTeX, no dollar signs). For powers use ^, for fractions use /, for square roots use sqrt().'
69
+ })
70
+ });
71
+
72
+ if (!response.ok) {
73
+ throw new Error(`Transcription API error: ${response.status} ${response.statusText}`);
74
+ }
75
+
76
+ const result = await response.json();
77
+ console.log('Transcription result:', result);
78
+
79
+ return {
80
+ text: result.text,
81
+ provider: result.provider || 'gemini',
82
+ confidence: result.confidence || 1.0
83
+ };
84
+
85
+ } catch (error) {
86
+ console.error('Transcription error:', error);
87
+ throw error;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Transcribe with fallback (same as transcribe for now)
93
+ * @param {Blob} imageBlob - The image blob to transcribe
94
+ * @param {Object} options - Transcription options
95
+ * @returns {Promise<Object>} Transcription result
96
+ */
97
+ async transcribeWithFallback(imageBlob, options = {}) {
98
+ return this.transcribe(imageBlob, options);
99
+ }
100
+
101
+ /**
102
+ * Check if transcription service is available
103
+ * @returns {boolean} True if service is available
104
+ */
105
+ isAvailable() {
106
+ return true; // Always available since we use server endpoint
107
+ }
108
+
109
+ /**
110
+ * Get available providers (simplified)
111
+ * @returns {Array} List of available providers
112
+ */
113
+ getAvailableProviders() {
114
+ return ['gemini']; // Server handles the provider selection
115
+ }
116
+
117
+ /**
118
+ * Check if a specific provider is available
119
+ * @param {string} provider - Provider name
120
+ * @returns {boolean} True if provider is available
121
+ */
122
+ isProviderAvailable(provider) {
123
+ return provider === 'gemini'; // Only gemini is supported via server
124
+ }
125
+ }