@teachinglab/omd 0.6.7 → 0.7.1

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # OMD (On-screen Math Display)
1
+ # OMD (Open Math Display)
2
2
 
3
3
  > A JavaScript library for creating interactive mathematical interfaces in web applications
4
4
 
@@ -204,6 +204,10 @@ export class omdCanvas {
204
204
  addStroke(stroke) {
205
205
  const id = `stroke_${++this.strokeCounter}`;
206
206
  stroke.id = id;
207
+ if (stroke.element) {
208
+ stroke.element.setAttribute('data-stroke-id', id);
209
+ stroke.element.id = id;
210
+ }
207
211
  this.strokes.set(id, stroke);
208
212
  this.drawingLayer.appendChild(stroke.element);
209
213
 
@@ -37,6 +37,10 @@ export class ResizeHandleManager {
37
37
  if (!element || !element.classList.contains('omd-item')) {
38
38
  return;
39
39
  }
40
+
41
+ if (element?.dataset?.locked === 'true') {
42
+ return;
43
+ }
40
44
 
41
45
  this.selectedElement = element;
42
46
  this._createSelectionBorder();
@@ -72,7 +72,10 @@ export class SelectTool extends Tool {
72
72
  }
73
73
 
74
74
  const segmentSelection = this._findSegmentAtPoint(event.x, event.y);
75
- const omdElement = this._findOMDElementAtPoint(event.x, event.y);
75
+ let omdElement = this._findOMDElementAtPoint(event.x, event.y);
76
+ if (omdElement?.dataset?.locked === 'true') {
77
+ omdElement = null;
78
+ }
76
79
 
77
80
  if (segmentSelection) {
78
81
  // Clicking on a stroke segment - clear OMD selection and handle segment selection
@@ -473,8 +476,11 @@ export class SelectTool extends Tool {
473
476
 
474
477
  // Check all OMD items in the layer
475
478
  const omdItems = omdLayer.querySelectorAll('.omd-item');
476
-
479
+
477
480
  for (const item of omdItems) {
481
+ if (item?.dataset?.locked === 'true') {
482
+ continue;
483
+ }
478
484
  try {
479
485
  // Get the bounding box of the item
480
486
  const bbox = item.getBBox();
@@ -11,6 +11,9 @@ export class Toolbar {
11
11
  this.buttons = new Map();
12
12
  this.activeButton = null;
13
13
  this.omdColor = omdColor; // Use omdColor for consistent styling
14
+ this.toolbarWidth = 64;
15
+ this.toolbarHeight = 28;
16
+ this.customPosition = null;
14
17
 
15
18
 
16
19
 
@@ -44,8 +47,7 @@ export class Toolbar {
44
47
 
45
48
 
46
49
  // Initial size, will be updated after buttons are created
47
- this.background.setWidthAndHeight(100, 54);
48
- this.background.setCornerRadius(27); // Pill shape
50
+ this._setToolbarSize(this.toolbarWidth, this.toolbarHeight);
49
51
  this.background.setFillColor(this.omdColor.mediumGray); // Modern dark, semi-transparent
50
52
 
51
53
 
@@ -58,7 +60,7 @@ export class Toolbar {
58
60
 
59
61
 
60
62
  // Position the toolbar at bottom center
61
- this._updatePosition();``
63
+ this._updatePosition();
62
64
 
63
65
  // Add to main SVG so it is rendered
64
66
  this.canvas.svg.appendChild(this.toolbarGroup.svgObject);
@@ -70,16 +72,12 @@ export class Toolbar {
70
72
  */
71
73
  _updatePosition() {
72
74
  const canvasRect = this.canvas.container.getBoundingClientRect();
73
- const toolbarWidth = this.background.width;
74
- const toolbarHeight = this.background.height;
75
+ const toolbarWidth = this.toolbarWidth;
76
+ const toolbarHeight = this.toolbarHeight;
75
77
  // Bottom center, 24px from bottom
76
78
  const x = (canvasRect.width - toolbarWidth) / 2;
77
79
  const y = canvasRect.height - toolbarHeight - 24;
78
-
79
- // Ensure toolbar stays within canvas bounds
80
- const clampedX = Math.max(0, Math.min(x, canvasRect.width - toolbarWidth));
81
- const clampedY = Math.max(0, Math.min(y, canvasRect.height - toolbarHeight));
82
- this.toolbarGroup.setPosition(x, y);
80
+ this._applyPosition(x, y);
83
81
 
84
82
  // Debug the SVG object
85
83
 
@@ -88,14 +86,7 @@ export class Toolbar {
88
86
  this.toolbarGroup.svgObject.style.pointerEvents = 'auto';
89
87
 
90
88
  // Fix the viewBox to include the toolbar position
91
- const bgWidth = this.background.width;
92
- const bgHeight = this.background.height;
93
- const viewBoxX = x;
94
- const viewBoxY = y;
95
- const viewBoxWidth = Math.max(500, x + bgWidth);
96
- const viewBoxHeight = Math.max(500, y + bgHeight);
97
-
98
- this.toolbarGroup.svgObject.setAttribute('viewBox', `${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}`);
89
+ this._updateViewBox(x, y);
99
90
 
100
91
  // Don't set x/y attributes - let setPosition handle it via transform
101
92
  // this.toolbarGroup.svgObject.setAttribute('x', x);
@@ -109,9 +100,9 @@ export class Toolbar {
109
100
  _createToolButtons() {
110
101
  const tools = this.canvas.toolManager.getAllToolMetadata();
111
102
 
112
- const buttonSize = 48;
113
- const spacing = 8;
114
- const padding = 6;
103
+ const buttonSize = 24;
104
+ const spacing = 4;
105
+ const padding = 4;
115
106
  let xPos = padding;
116
107
  const yPos = padding;
117
108
  tools.forEach(toolMeta => {
@@ -124,8 +115,7 @@ export class Toolbar {
124
115
  // Remove last spacing
125
116
  const totalWidth = xPos - spacing + padding;
126
117
  const totalHeight = buttonSize + 2 * padding;
127
- this.background.setWidthAndHeight(totalWidth, totalHeight);
128
- this.background.setCornerRadius(totalHeight / 2);
118
+ this._setToolbarSize(totalWidth, totalHeight);
129
119
  // Reposition after sizing
130
120
  this._updatePosition();
131
121
 
@@ -215,6 +205,33 @@ export class Toolbar {
215
205
  this.activeButton = null;
216
206
  }
217
207
  }
208
+
209
+ _setToolbarSize(width, height) {
210
+ this.toolbarWidth = width;
211
+ this.toolbarHeight = height;
212
+ this.background.setWidthAndHeight(width, height);
213
+ this.background.setCornerRadius(height / 2);
214
+ }
215
+
216
+ _applyPosition(x, y) {
217
+ const canvasRect = this.canvas.container.getBoundingClientRect();
218
+ const clampedX = Math.max(0, Math.min(x, canvasRect.width - this.toolbarWidth));
219
+ const clampedY = Math.max(0, Math.min(y, canvasRect.height - this.toolbarHeight));
220
+ this.customPosition = { x: clampedX, y: clampedY };
221
+ this.toolbarGroup.setPosition(clampedX, clampedY);
222
+ this._updateViewBox(clampedX, clampedY);
223
+ }
224
+
225
+ _updateViewBox(x, y) {
226
+ this.toolbarGroup.svgObject.setAttribute('viewBox', `${x} ${y} ${this.toolbarWidth} ${this.toolbarHeight}`);
227
+ }
228
+
229
+ setBoundaryPosition(lineY) {
230
+ const canvasRect = this.canvas.container.getBoundingClientRect();
231
+ const x = (canvasRect.width - this.toolbarWidth) / 2;
232
+ const y = lineY - this.toolbarHeight;
233
+ this._applyPosition(x, y);
234
+ }
218
235
 
219
236
  /**
220
237
  * Add custom button to toolbar
package/docs/api/index.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OMD Library Entry Point
2
2
 
3
- This module (`omd/core/index.js`) serves as the main entry point for the OMD (On-screen Math Display) library. It re-exports all core classes, visualization components, and utility functions, making them easily accessible from a single import.
3
+ This module (`omd/core/index.js`) serves as the main entry point for the OMD (Open Math Display) library. It re-exports all core classes, visualization components, and utility functions, making them easily accessible from a single import.
4
4
 
5
5
  ## Overview
6
6
 
@@ -1,6 +1,6 @@
1
1
  # OMD Library API Reference
2
2
 
3
- > This is the complete API reference for the OMD (On-screen Math Display) library. Use the table of contents below to navigate to the detailed documentation for each module and class.
3
+ > This is the complete API reference for the OMD (Open Math Display) library. Use the table of contents below to navigate to the detailed documentation for each module and class.
4
4
 
5
5
  ---
6
6
 
package/docs/index.html CHANGED
@@ -244,7 +244,7 @@
244
244
  <body>
245
245
  <div class="header">
246
246
  <h1>OMD Library Documentation</h1>
247
- <p>On-screen Math Display - Interactive Mathematical Expression Rendering</p>
247
+ <p>Open Math Display - Interactive Mathematical Expression Rendering</p>
248
248
  <span class="version-badge">v1.0.0</span>
249
249
  </div>
250
250
 
@@ -304,7 +304,7 @@
304
304
  <main class="content">
305
305
  <section id="overview">
306
306
  <h2>Overview</h2>
307
- <p>OMD (On-screen Math Display) is a powerful JavaScript library for rendering and manipulating mathematical expressions. It provides a flexible and intuitive API for creating interactive math visualizations.</p>
307
+ <p>OMD (Open Math Display) is a powerful JavaScript library for rendering and manipulating mathematical expressions. It provides a flexible and intuitive API for creating interactive math visualizations.</p>
308
308
 
309
309
  <div class="quick-links">
310
310
  <a href="../examples/index.html" class="quick-link">
package/docs/index.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OMD Documentation
2
2
 
3
- Welcome to the On-screen Math Display (OMD) documentation.
3
+ Welcome to the Open Math Display (OMD) documentation.
4
4
 
5
5
  ## Getting Started
6
6
 
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * OMD Library - Main Entry Point
3
3
  *
4
- * This is the main entry point for the OMD (On-screen Math Display) library.
4
+ * This is the main entry point for the OMD (Open Math Display) library.
5
5
  * It exports all core OMD components and visualization tools from a single endpoint.
6
6
  *
7
7
  * Usage:
@@ -29,6 +29,7 @@ export { EventManager } from './canvas/events/eventManager.js';
29
29
 
30
30
  // Export utility components
31
31
  export { omdNodeOverlay, omdNodeOverlayPresets } from './omd/utils/omdNodeOverlay.js';
32
+ export { omdTranscriptionService } from './omd/utils/omdTranscriptionService.js';
32
33
 
33
34
  // Re-export the most commonly used components for easy access
34
35
  export { omdTable } from './src/omdTable.js';
@@ -77,4 +78,4 @@ export default {
77
78
  const { omdDisplay } = await import('./omd/core/index.js');
78
79
  return new omdDisplay(container);
79
80
  }
80
- };
81
+ };
@@ -1,4 +1,5 @@
1
1
  import { jsvgGroup, jsvgRect, jsvgTextLine, jsvgImage, jsvgClipMask } from './jsvg.js';
2
+ export * from './jsvg.js';
2
3
 
3
4
  // ================ jsvgButton ================================= //
4
5
 
@@ -355,4 +356,4 @@ export class jsvgScrollbox extends jsvgGroup
355
356
  }
356
357
  }
357
358
 
358
- }
359
+ }
@@ -1,4 +1,4 @@
1
- # OMD (On-screen Math Display)
1
+ # OMD (Open Math Display)
2
2
 
3
3
  > A JavaScript library for creating interactive mathematical interfaces in web applications
4
4
 
@@ -1,6 +1,6 @@
1
1
  # OMD Library API Reference
2
2
 
3
- > This is the complete API reference for the OMD (On-screen Math Display) library. Use the table of contents below to navigate to the detailed documentation for each module and class.
3
+ > This is the complete API reference for the OMD (Open Math Display) library. Use the table of contents below to navigate to the detailed documentation for each module and class.
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # OMD Library Entry Point
2
2
 
3
- This module (`omd/core/index.js`) serves as the main entry point for the OMD (On-screen Math Display) library. It re-exports all core classes, visualization components, and utility functions, making them easily accessible from a single import.
3
+ This module (`omd/core/index.js`) serves as the main entry point for the OMD (Open Math Display) library. It re-exports all core classes, visualization components, and utility functions, making them easily accessible from a single import.
4
4
 
5
5
  ## Overview
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Getting Started with OMD
2
2
 
3
- This guide will help you install and start using OMD (On-screen Math Display) in your project.
3
+ This guide will help you install and start using OMD (Open Math Display) in your project.
4
4
 
5
5
  ## Installation
6
6
 
@@ -460,54 +460,39 @@ Tabular data display with customizable styling.
460
460
 
461
461
  #### `omdEquation`
462
462
 
463
- Complete mathematical equations with left and right sides.
463
+ Complete mathematical equations. The visual component now uses the same math.js-powered parser and renderer as the interactive core (functions, rationals, roots, etc.).
464
464
 
465
- **Schema (String Form):**
465
+ **Preferred schema (string form):**
466
466
  ```json
467
467
  {
468
- "equation": "2x + 3 = 11"
468
+ "equation": "sin(x) + 2 = 3"
469
469
  }
470
470
  ```
471
471
 
472
- **Schema (Structured Form):**
472
+ **Structured fallback (legacy):**
473
473
  ```typescript
474
474
  {
475
- leftExpression: Expression | string,
476
- rightExpression: Expression | string,
477
- equation?: string
475
+ leftExpression?: Expression | string,
476
+ rightExpression?: Expression | string,
477
+ equation: string
478
478
  }
479
479
  ```
480
480
 
481
481
  **Examples:**
482
482
  ```json
483
- {
484
- "equation": "2x + 3 = 11"
485
- }
483
+ { "equation": "sin(x) + 2 = 3" }
486
484
  ```
487
-
488
485
  ```json
489
- {
490
- "leftExpression": {
491
- "termsAndOpers": [
492
- { "omdType": "term", "coefficient": 2, "variable": "x" },
493
- { "omdType": "operator", "operator": "+" },
494
- { "omdType": "number", "value": 3 }
495
- ]
496
- },
497
- "rightExpression": {
498
- "omdType": "number",
499
- "value": 11
500
- }
501
- }
486
+ { "equation": "(x^2 + 3x - 4)/(2x) = 5" }
502
487
  ```
503
488
 
504
489
  **Usage:**
505
490
  ```javascript
506
- // Simple string form
491
+ // Preferred string form (math.js parsing)
507
492
  const eq = new omdEquation();
508
- eq.loadFromJSON({ equation: '2x + 3 = 11' });
493
+ eq.loadFromJSON({ equation: 'sqrt(x+1) = 4', fontSize: 32 });
509
494
 
510
- // Structured form
495
+ // Legacy structured form (if you already have parsed pieces)
511
496
  eq.loadFromJSON({
512
497
  leftExpression: { omdType: 'term', coefficient: 2, variable: 'x' },
513
498
  rightExpression: { omdType: 'number', value: 11 }
@@ -240,7 +240,7 @@ export class omdEquationStack extends jsvgGroup {
240
240
  const effectivePadding = (typeof padding === 'number') ? padding : this.overlayPadding;
241
241
 
242
242
  // Compute top-left of toolbar in container coordinates using UN-SCALED toolbar size
243
- // because we counter-scale the toolbar by 1/s to keep constant on-screen size.
243
+ // because we counter-scale the toolbar by 1/s to keep constant Open size.
244
244
  let containerX = (containerWidth - toolbarWidth) / 2;
245
245
  let containerY = containerHeight - toolbarHeight - effectivePadding;
246
246
  // Snap to integer pixels to avoid subpixel jitter when scaling
@@ -259,7 +259,7 @@ export class omdEquationStack extends jsvgGroup {
259
259
  const svgViewBox = rootSVG?.getAttribute?.('viewBox') || 'unknown';
260
260
 
261
261
 
262
- // Counter-scale the toolbar so it remains a constant on-screen size
262
+ // Counter-scale the toolbar so it remains a constant Open size
263
263
  if (typeof toolbarGroup.setScale === 'function') {
264
264
  toolbarGroup.setScale(1 / s);
265
265
  }
@@ -287,7 +287,7 @@ export class omdEquationStack extends jsvgGroup {
287
287
  * @param {number} [opts.offsetX=0] - Horizontal offset in screen pixels (positive -> right)
288
288
  * @param {number} [opts.offsetY=0] - Vertical offset in screen pixels (positive -> down)
289
289
  * @param {number} [opts.padding=16] - Padding from edges when computing anchor
290
- * @param {boolean} [opts.counterScale=true] - Whether to counter-scale the child to keep constant on-screen size
290
+ * @param {boolean} [opts.counterScale=true] - Whether to counter-scale the child to keep constant Open size
291
291
  * @param {boolean} [opts.addToStack=true] - Whether to add the child to this stack's children (default true)
292
292
  * @param {{x:number,y:number}|null} [opts.customCoords=null] - If anchor==='custom', use these screen coords
293
293
  * @returns {object|null} The child or null if not applicable
@@ -352,7 +352,7 @@ export class omdEquationStack extends jsvgGroup {
352
352
  const x = (containerX - stackX) / s;
353
353
  const y = (containerY - stackY) / s;
354
354
 
355
- // Optionally counter-scale child to keep constant on-screen size
355
+ // Optionally counter-scale child to keep constant Open size
356
356
  if (counterScale && child && typeof child.setScale === 'function') {
357
357
  try { child.setScale(1 / s); } catch (_) {}
358
358
  }
@@ -55,6 +55,8 @@ export class omdDisplay {
55
55
  const height = this.container.offsetHeight || 600;
56
56
 
57
57
  this.svg.setViewbox(width, height);
58
+ this.svg.svgObject.style.width = '100%';
59
+ this.svg.svgObject.style.height = '100%';
58
60
  this.svg.svgObject.style.verticalAlign = "middle";
59
61
  // Enable internal scrolling via native SVG scrolling if content overflows
60
62
  this.svg.svgObject.style.overflow = 'hidden';
@@ -768,18 +770,7 @@ export class omdDisplay {
768
770
  render(expression) {
769
771
  // Clear previous node
770
772
  if (this.node) {
771
- if (this._contentGroup && this.node && this.node.svgObject) {
772
- try {
773
- if (this.node.svgObject.parentNode === this._contentGroup) {
774
- this._contentGroup.removeChild(this.node.svgObject);
775
- }
776
- } catch (e) {
777
- // Fallback to svg remove
778
- this.svg.removeChild(this.node);
779
- }
780
- } else {
781
- this.svg.removeChild(this.node);
782
- }
773
+ this.removeChild(this.node);
783
774
  }
784
775
 
785
776
  // Create node from expression
@@ -992,7 +983,7 @@ export class omdDisplay {
992
983
  */
993
984
  clear() {
994
985
  if (this.node) {
995
- this.svg.removeChild(this.node);
986
+ this.removeChild(this.node);
996
987
  this.node = null;
997
988
  }
998
989
  }
@@ -165,7 +165,7 @@ export class omdToolbar {
165
165
  }
166
166
 
167
167
  if (popupGroup) {
168
- // Attach to toolbar group so it inherits toolbar counter-scaling (keeps constant on-screen size)
168
+ // Attach to toolbar group so it inherits toolbar counter-scaling (keeps constant Open size)
169
169
  this.elements.toolbarGroup.addChild(popupGroup);
170
170
  this.state.activePopup = { type: popupType, group: popupGroup };
171
171
  // Ensure the toolbar and popup are on top of all siblings inside the SVG
@@ -100,6 +100,7 @@ export class omdFunctionNode extends omdNode {
100
100
  // Calculate dimensions using getTextBounds for consistency
101
101
  const ratio = fontSize / this.getRootFontSize();
102
102
  const spacing = 2 * ratio;
103
+ const parenSpacing = 2 * ratio; // Spacing between function name and opening parenthesis
103
104
 
104
105
  const functionNameBounds = getTextBounds(this.functionName, fontSize);
105
106
  const openParenBounds = getTextBounds('(', fontSize);
@@ -117,7 +118,7 @@ export class omdFunctionNode extends omdNode {
117
118
  }
118
119
  });
119
120
 
120
- const totalWidth = functionNameBounds.width + openParenBounds.width + totalArgWidth + closeParenBounds.width + (spacing * 2);
121
+ const totalWidth = functionNameBounds.width + parenSpacing + openParenBounds.width + totalArgWidth + closeParenBounds.width + (spacing * 2);
121
122
  const totalHeight = Math.max(maxArgHeight, functionNameBounds.height, openParenBounds.height, closeParenBounds.height) + 4 * ratio;
122
123
 
123
124
  this.setWidthAndHeight(totalWidth, totalHeight);
@@ -132,6 +133,7 @@ export class omdFunctionNode extends omdNode {
132
133
  const argFontSize = fontSize * 5/6;
133
134
  const ratio = fontSize / this.getRootFontSize();
134
135
  const spacing = 2 * ratio;
136
+ const parenSpacing = 2 * ratio; // Spacing between function name and opening parenthesis
135
137
 
136
138
  let currentX = 0;
137
139
  const textY = this.height / 2;
@@ -140,6 +142,8 @@ export class omdFunctionNode extends omdNode {
140
142
  this.functionNameElement.setPosition(currentX, textY);
141
143
  currentX += getTextBounds(this.functionName, fontSize).width;
142
144
 
145
+ currentX += parenSpacing;
146
+
143
147
  // Position opening parenthesis
144
148
  this.openParenElement.setPosition(currentX, textY);
145
149
  currentX += getTextBounds('(', fontSize).width;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.6.7",
3
+ "version": "0.7.1",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
package/readme.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>OMD (On-screen Math Display) - OMD Documentation</title>
6
+ <title>OMD (Open Math Display) - OMD Documentation</title>
7
7
  <style>
8
8
  body {
9
9
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
@@ -145,7 +145,7 @@
145
145
  </div>
146
146
 
147
147
  <div class="content">
148
- <h1 id="omd-on-screen-math-display">OMD (On-screen Math Display)</h1>
148
+ <h1 id="omd-on-screen-math-display">OMD (Open Math Display)</h1>
149
149
  <blockquote>
150
150
  <p>A JavaScript library for creating interactive mathematical interfaces in web applications</p>
151
151
  </blockquote>
package/src/index.js CHANGED
@@ -26,6 +26,12 @@ export { omdFunction } from './omdFunction.js';
26
26
  export { omdApp } from './omdApp.js';
27
27
  export { omdAppCanvas } from './omdAppCanvas.js';
28
28
  export { omd } from './omd.js';
29
+ export { omdDisplay } from '../omd/display/omdDisplay.js';
30
+ export { omdEquationNode } from '../omd/nodes/omdEquationNode.js';
31
+ export { omdEquationStack } from '../omd/core/omdEquationStack.js';
32
+ export { omdEquationSequenceNode } from '../omd/nodes/omdEquationSequenceNode.js';
33
+ export { omdConstantNode } from '../omd/nodes/omdConstantNode.js';
34
+ export { omdNodeOverlay, omdNodeOverlayPresets } from '../omd/utils/omdNodeOverlay.js';
29
35
 
30
36
  // OMD Utilities and Helpers
31
37
  export { omdColor } from './omdColor.js';
@@ -73,6 +79,11 @@ export default {
73
79
  app: {
74
80
  omdApp: () => import('./omdApp.js').then(m => m.omdApp),
75
81
  omdAppCanvas: () => import('./omdAppCanvas.js').then(m => m.omdAppCanvas),
76
- omd: () => import('./omd.js').then(m => m.omd)
82
+ omd: () => import('./omd.js').then(m => m.omd),
83
+ async createDisplay(container) {
84
+ const { omdDisplay } = await import('../omd/core/index.js');
85
+ return new omdDisplay(container);
86
+ }
77
87
  }
78
- };
88
+ };
89
+ // Force reload
@@ -677,9 +677,16 @@ This document provides schemas and examples for the `loadFromJSON` method used i
677
677
 
678
678
  ## 20. `omdEquation`
679
679
 
680
- `omdEquation` represents a mathematical equation, such as "x + 2 = 5".
680
+ `omdEquation` represents a mathematical equation and now uses the same math.js-powered parser/renderer as the interactive core (functions, rationals, roots, etc.).
681
681
 
682
- ### Schema
682
+ ### Preferred schema (string form)
683
+ ```json
684
+ {
685
+ "equation": "sin(x) + 2 = 3"
686
+ }
687
+ ```
688
+
689
+ ### Structured fallback (legacy)
683
690
  ```json
684
691
  {
685
692
  "leftExpression": "object",
@@ -688,11 +695,10 @@ This document provides schemas and examples for the `loadFromJSON` method used i
688
695
  }
689
696
  ```
690
697
 
691
- ### Example
698
+ ### Examples
692
699
  ```json
693
- {
694
- "leftExpression": { "omdType": "term", "coefficient": 1, "variable": "x", "exponent": 1 },
695
- "rightExpression": { "omdType": "number", "value": 5 },
696
- "equation": "x + 2 = 5"
697
- }
698
- ```
700
+ { "equation": "sin(x) + 2 = 3" }
701
+ ```
702
+ ```json
703
+ { "equation": "(x^2 + 3x - 4)/(2x) = 5" }
704
+ ```
@@ -9,6 +9,7 @@ import { omdVariable } from "./omdVariable.js";
9
9
  import { omdString } from "./omdString.js";
10
10
  import { omdNumber } from "./omdNumber.js";
11
11
  import { parseEquationString } from "./omdUtils.js";
12
+ import { omdEquationNode } from "../omd/nodes/omdEquationNode.js";
12
13
 
13
14
  export class omdEquation extends omdMetaExpression
14
15
  {
@@ -37,12 +38,24 @@ export class omdEquation extends omdMetaExpression
37
38
 
38
39
  this.rightHolder = new jsvgGroup();
39
40
  this.equationStack.addChild( this.rightHolder );
41
+
42
+ this.equationNode = null;
40
43
  }
41
44
 
42
45
  // make an equation (x + 2) = (2x - 3)
43
46
 
44
47
  loadFromJSON( data )
45
48
  {
49
+ // Prefer math.js parsing into omdEquationNode for richer rendering (functions, rationals, roots)
50
+ if (typeof data.equation === 'string' && data.equation.trim()) {
51
+ try {
52
+ this._renderWithEquationNode(data.equation, data.fontSize);
53
+ return;
54
+ } catch (e) {
55
+ console.warn('⚠️ omdEquation math.js render failed, falling back to legacy parsing:', e?.message || e);
56
+ }
57
+ }
58
+
46
59
  // Helper function to fix operator symbols in termsAndOpers arrays
47
60
  function fixOperatorSymbols(expressionData) {
48
61
  if (expressionData && expressionData.termsAndOpers && Array.isArray(expressionData.termsAndOpers)) {
@@ -136,6 +149,37 @@ export class omdEquation extends omdMetaExpression
136
149
  this.updateLayout();
137
150
  }
138
151
 
152
+ _renderWithEquationNode(equationString, fontSize) {
153
+ if (typeof math === 'undefined' || typeof math.parse !== 'function') {
154
+ throw new Error('math.js is required to parse equation strings');
155
+ }
156
+
157
+ const eqNode = omdEquationNode.fromString(equationString);
158
+ if (typeof fontSize === 'number' && eqNode.setFontSize) {
159
+ eqNode.setFontSize(fontSize);
160
+ }
161
+
162
+ if (eqNode.hideBackgroundByDefault) eqNode.hideBackgroundByDefault();
163
+ if (eqNode.computeDimensions) eqNode.computeDimensions();
164
+ if (eqNode.updateLayout) eqNode.updateLayout();
165
+
166
+ // Clear the legacy stack and render just the parsed node
167
+ if (typeof this.equationStack.removeAllChildren === 'function') {
168
+ this.equationStack.removeAllChildren();
169
+ } else {
170
+ this.equationStack.childList = [];
171
+ }
172
+ this.equationStack.addChild(eqNode);
173
+ this.equationStack.setSpacer(0);
174
+
175
+ this.equationNode = eqNode;
176
+ this.leftExpression = null;
177
+ this.rightExpression = null;
178
+
179
+ this.centerEquation = false;
180
+ this.updateLayout();
181
+ }
182
+
139
183
 
140
184
  setLeftAndRightExpressions( leftExp, rightExp )
141
185
  {
@@ -156,6 +200,25 @@ export class omdEquation extends omdMetaExpression
156
200
 
157
201
  updateLayout()
158
202
  {
203
+ if (this.equationNode) {
204
+ const node = this.equationNode;
205
+ if (node.computeDimensions) node.computeDimensions();
206
+ if (node.updateLayout) node.updateLayout();
207
+
208
+ this.equationStack.doHorizontalLayout();
209
+ this.equationStack.setPosition(this.inset, this.inset);
210
+
211
+ const W = node.width || this.equationStack.width;
212
+ const H = node.height || this.equationStack.height;
213
+
214
+ this.backRect.setWidthAndHeight( W + this.inset*2, H + this.inset*2 );
215
+ this.setWidthAndHeight( this.backRect.width, this.backRect.height );
216
+ this.width = this.backRect.width;
217
+ this.height = this.backRect.height;
218
+ this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
219
+ return;
220
+ }
221
+
159
222
  this.leftHolder.setWidthAndHeight( this.leftExpression.width, this.leftExpression.height );
160
223
  this.rightHolder.setWidthAndHeight( this.rightExpression.width, this.rightExpression.height );
161
224
 
@@ -186,4 +249,4 @@ export class omdEquation extends omdMetaExpression
186
249
  }
187
250
  }
188
251
 
189
- }
252
+ }
package/README.old.md DELETED
@@ -1,138 +0,0 @@
1
- # OMD (On-screen Math Display)
2
-
3
- OMD is a JavaScript library for creating interactive mathematical interfaces in web applications. Build everything from simple equation displays to complex step-by-step solution systems with rich visual feedback and user interaction.
4
-
5
- ![OMD Demo](https://i.imgur.com/CdtEi33.png)
6
-
7
- ## Features
8
-
9
- ### **Interactive Math Rendering**
10
- - High-quality SVG-based mathematical notation
11
- - Real-time expression manipulation and visualization
12
- - Automatic layout and alignment for complex equations
13
-
14
- ### **Step-by-Step Solutions**
15
- - Visual step tracking with detailed explanations
16
- - Simplification engine with rule-based transformations
17
- - Provenance tracking for highlighting related elements
18
-
19
- ### **Rich UI Components**
20
- - Built-in toolbar for common mathematical operations
21
- - Drag & drop interface for intuitive manipulation
22
- - Customizable canvas for multi-expression layouts
23
-
24
- ### **Educational Features**
25
- - Interactive learning experiences
26
- - Progressive step revelation
27
- - Visual operation feedback and highlighting
28
-
29
- ## Installation
30
-
31
- ### npm
32
- ```bash
33
- npm install @teachinglab/omd
34
- ```
35
-
36
- ## Basic Usage
37
- ```javascript
38
- import { omdDisplay } from '@teachinglab/omd';
39
-
40
- // Create a math display
41
- const container = document.getElementById('math-container');
42
- const display = new omdDisplay(container);
43
-
44
- // Render an equation
45
- display.render('2x + 3 = 11');
46
- ```
47
-
48
- ### Step-by-Step Solutions
49
- ```javascript
50
- import { omdEquationStack, omdEquationNode } from '@teachinglab/omd';
51
-
52
- // Create solution steps
53
- const steps = [
54
- omdEquationNode.fromString('2x + 3 = 11'),
55
- omdEquationNode.fromString('2x = 8'),
56
- omdEquationNode.fromString('x = 4')
57
- ];
58
-
59
- // Create interactive equation stack
60
- const stack = new omdEquationStack(steps, {
61
- toolbar: true,
62
- stepVisualizer: true
63
- });
64
-
65
- display.render(stack);
66
- ```
67
-
68
- ## Core Concepts
69
-
70
- ### **Nodes** - Building Blocks
71
- Every mathematical element is a node in an expression tree:
72
- - `omdEquationNode` - Complete equations (e.g., `2x + 3 = 11`)
73
- - `omdConstantNode` - Numbers (e.g., `5`, `3.14`)
74
- - `omdVariableNode` - Variables (e.g., `x`, `y`)
75
- - `omdBinaryExpressionNode` - Operations (e.g., `+`, `-`, `*`, `/`)
76
-
77
- ### **Sequences** - Solution Steps
78
- Group related equations for step-by-step solving:
79
- ```javascript
80
- const sequence = new omdEquationSequenceNode([
81
- equation1, equation2, equation3
82
- ]);
83
- ```
84
-
85
- ### **Display** - Rendering Engine
86
- Handles layout, centering, and visualization:
87
- ```javascript
88
- const display = new omdDisplay(container, {
89
- fontSize: 36,
90
- centerContent: true
91
- });
92
- ```
93
-
94
- ## Interactive Examples
95
-
96
- Explore OMD's capabilities with our comprehensive examples:
97
-
98
- | Category | Example | Description |
99
- |----------|---------|-------------|
100
- | **Getting Started** | [Minimal](examples/minimal.html) | Basic equation rendering |
101
- | | [Simple Usage](examples/simple-usage.html) | Interactive features |
102
- | **Advanced** | [Expression Playground](examples/expression-playground.html) | Full manipulation interface |
103
- | | [Drag & Drop](examples/drag-and-drop-playground.html) | Intuitive interaction |
104
- | **Educational** | [Worked Solutions](examples/worked-solution.html) | Step-by-step solving |
105
- | | [Kids Interactive](examples/kids-interactive.html) | Child-friendly interface |
106
- | **Components** | [Equation Stack](examples/equation-stack-test.html) | Stacked equations |
107
- | | [Canvas Demo](examples/canvas-multiple-nodes.html) | Multi-expression layouts |
108
-
109
- **[Browse All Examples](examples/index.html)**
110
-
111
- ## Documentation
112
-
113
- | Resource | Description |
114
- |----------|-------------|
115
- | **[API Reference](docs/api-reference.md)** | Complete component documentation |
116
- | **[User Guide](docs/user-guide.md)** | Getting started and tutorials |
117
-
118
-
119
- ## Architecture
120
-
121
- ```
122
- OMD Library Structure
123
- ├── Display Layer (omdDisplay)
124
- ├── Node System (Expression tree components)
125
- ├── UI Components (Toolbar, Step visualizer)
126
- ├── Core Systems (Simplification, Layout)
127
- └── Utilities (Configuration, Helpers)
128
- ```
129
-
130
- ## Dependencies
131
-
132
- - **JSVG** - High-performance SVG rendering
133
- - **math.js** - Mathematical expression parsing
134
- - **Modern Browser** - ES6 modules, SVG support
135
-
136
- ---
137
-
138
- **Ready to get started?** Check out our [examples](examples/index.html) or dive into the [documentation](docs/api-reference.md)!