@teachinglab/omd 0.6.2 → 0.6.3

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.
@@ -123,6 +123,65 @@ export class omdEquationStack extends jsvgGroup {
123
123
  this.height = this.layoutGroup.height;
124
124
  }
125
125
 
126
+ /**
127
+ * Initializes the stack and its underlying sequence.
128
+ */
129
+ initialize() {
130
+ if (this.sequence && typeof this.sequence.initialize === 'function') {
131
+ this.sequence.initialize();
132
+ } else if (this.sequence) {
133
+ if (typeof this.sequence.computeDimensions === 'function') {
134
+ this.sequence.computeDimensions();
135
+ }
136
+ if (typeof this.sequence.updateLayout === 'function') {
137
+ this.sequence.updateLayout();
138
+ }
139
+ }
140
+ this.updateLayout();
141
+ }
142
+
143
+ /**
144
+ * Propagates font size changes to the underlying sequence and recomputes layout.
145
+ * @param {number} fontSize
146
+ */
147
+ setFontSize(fontSize) {
148
+ if (this.sequence && typeof this.sequence.setFontSize === 'function') {
149
+ this.sequence.setFontSize(fontSize);
150
+ }
151
+ this.updateLayout();
152
+ }
153
+
154
+ /**
155
+ * Delegate to close the currently active step visualizer dot (if present).
156
+ */
157
+ closeActiveDot() {
158
+ if (this.sequence && typeof this.sequence.closeActiveDot === 'function') {
159
+ this.sequence.closeActiveDot();
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Delegate to close all step visualizer text boxes (if present).
165
+ */
166
+ closeAllTextBoxes() {
167
+ if (this.sequence && typeof this.sequence.closeAllTextBoxes === 'function') {
168
+ this.sequence.closeAllTextBoxes();
169
+ } else if (this.sequence && typeof this.sequence.closeActiveDot === 'function') {
170
+ this.sequence.closeActiveDot();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Delegate to force close all step visualizer UI (if present).
176
+ */
177
+ forceCloseAll() {
178
+ if (this.sequence && typeof this.sequence.forceCloseAll === 'function') {
179
+ this.sequence.forceCloseAll();
180
+ } else {
181
+ this.closeAllTextBoxes();
182
+ }
183
+ }
184
+
126
185
  /**
127
186
  * Returns the underlying sequence instance.
128
187
  * @returns {omdEquationSequenceNode|omdStepVisualizer} The managed sequence instance.
@@ -544,4 +603,4 @@ export class omdEquationStack extends jsvgGroup {
544
603
  getSvg() {
545
604
  return this.svgObject;
546
605
  }
547
- }
606
+ }
@@ -1,4 +1,5 @@
1
1
  import { omdEquationNode } from '../nodes/omdEquationNode.js';
2
+ import { omdEquationStack } from '../core/omdEquationStack.js';
2
3
  import { omdStepVisualizer } from '../step-visualizer/omdStepVisualizer.js';
3
4
  import { getNodeForAST } from '../core/omdUtilities.js';
4
5
  import { jsvgContainer } from '@teachinglab/jsvg';
@@ -11,6 +12,13 @@ import { jsvgContainer } from '@teachinglab/jsvg';
11
12
  export class omdDisplay {
12
13
  constructor(container, options = {}) {
13
14
  this.container = container;
15
+ const {
16
+ stepVisualizer = false,
17
+ stackOptions = null,
18
+ math: mathInstance = (typeof window !== 'undefined' && window.math) ? window.math : null,
19
+ ...otherOptions
20
+ } = options || {};
21
+
14
22
  this.options = {
15
23
  fontSize: 32,
16
24
  centerContent: true,
@@ -21,7 +29,10 @@ export class omdDisplay {
21
29
  maxScale: 1, // Do not upscale beyond 1 by default
22
30
  edgePadding: 16, // Horizontal padding from edges when scaling
23
31
  autoCloseStepVisualizer: true, // Close active step visualizer text boxes before autoscale to avoid shrink
24
- ...options
32
+ stepVisualizer,
33
+ stackOptions,
34
+ math: mathInstance,
35
+ ...otherOptions
25
36
  };
26
37
 
27
38
  // Create SVG container
@@ -612,6 +623,74 @@ export class omdDisplay {
612
623
  this._lastCenterSignature = contentSig;
613
624
  }
614
625
 
626
+ _getMathInstance() {
627
+ if (this.options.math) {
628
+ return this.options.math;
629
+ }
630
+ if (typeof window !== 'undefined' && window.math) {
631
+ return window.math;
632
+ }
633
+ return null;
634
+ }
635
+
636
+ _createNodeFromSegment(segment, mathLib) {
637
+ try {
638
+ if (segment.includes('=')) {
639
+ return omdEquationNode.fromString(segment);
640
+ }
641
+
642
+ if (!mathLib || typeof mathLib.parse !== 'function') {
643
+ throw new Error('math.js parser is unavailable');
644
+ }
645
+
646
+ const ast = mathLib.parse(segment);
647
+ const NodeClass = getNodeForAST(ast);
648
+ return new NodeClass(ast);
649
+ } catch (error) {
650
+ const reason = error?.message || String(error);
651
+ throw new Error(`Failed to parse expression "${segment}": ${reason}`, { cause: error });
652
+ }
653
+ }
654
+
655
+ _createNodesFromString(expressionString) {
656
+ const segments = (expressionString || '')
657
+ .split(';')
658
+ .map(segment => segment.trim())
659
+ .filter(Boolean);
660
+
661
+ if (!segments.length) {
662
+ throw new Error('omdDisplay.render() received an empty expression string.');
663
+ }
664
+
665
+ const mathLib = this._getMathInstance();
666
+ return segments.map(segment => this._createNodeFromSegment(segment, mathLib));
667
+ }
668
+
669
+ _buildStackOptions() {
670
+ const baseOptions = {};
671
+ if (typeof this.options.stepVisualizer === 'boolean') {
672
+ baseOptions.stepVisualizer = this.options.stepVisualizer;
673
+ }
674
+ if (this.options.styling) {
675
+ baseOptions.styling = this.options.styling;
676
+ }
677
+ if (Object.prototype.hasOwnProperty.call(this.options, 'toolbar')) {
678
+ baseOptions.toolbar = this.options.toolbar;
679
+ }
680
+
681
+ if (this.options.stackOptions && typeof this.options.stackOptions === 'object') {
682
+ return { ...baseOptions, ...this.options.stackOptions };
683
+ }
684
+ return baseOptions;
685
+ }
686
+
687
+ _createStackFromSteps(steps) {
688
+ if (!steps || !steps.length) {
689
+ throw new Error('omdDisplay.render() received no steps to render.');
690
+ }
691
+ return new omdEquationStack(steps, this._buildStackOptions());
692
+ }
693
+
615
694
  fitToContent() {
616
695
  if (!this.node) {
617
696
  return;
@@ -705,29 +784,25 @@ export class omdDisplay {
705
784
 
706
785
  // Create node from expression
707
786
  if (typeof expression === 'string') {
708
- if (expression.includes(';')) {
709
- // Multiple equations
710
- const equationStrings = expression.split(';').filter(s => s.trim() !== '');
711
- const steps = equationStrings.map(str => omdEquationNode.fromString(str));
712
- this.node = new omdStepVisualizer(steps, this.options.styling || {});
713
- } else {
714
- // Single expression or equation
715
- if (expression.includes('=')) {
716
- const firstStep = omdEquationNode.fromString(expression);
717
- this.node = new omdStepVisualizer([firstStep], this.options.styling || {});
718
- } else {
719
- // Create node directly from expression
720
- const parsedAST = math.parse(expression);
721
- const NodeClass = getNodeForAST(parsedAST);
722
- const firstStep = new NodeClass(parsedAST);
723
- this.node = new omdStepVisualizer([firstStep], this.options.styling || {});
787
+ const steps = this._createNodesFromString(expression);
788
+ this.node = this._createStackFromSteps(steps);
789
+ } else if (Array.isArray(expression)) {
790
+ const steps = expression.flatMap(item => {
791
+ if (typeof item === 'string') {
792
+ return this._createNodesFromString(item);
724
793
  }
725
- }
794
+ return item;
795
+ }).filter(Boolean);
796
+ this.node = this._createStackFromSteps(steps);
726
797
  } else {
727
798
  // Assume it's already a node
728
799
  this.node = expression;
729
800
  }
730
801
 
802
+ if (!this.node) {
803
+ throw new Error('omdDisplay.render() was unable to create a node from the provided expression.');
804
+ }
805
+
731
806
  // Initialize and render
732
807
  const sequence = this.node.getSequence ? this.node.getSequence() : null;
733
808
  if (sequence) {
@@ -967,4 +1042,4 @@ export class omdDisplay {
967
1042
  getSVG() {
968
1043
  return this.svg.svgObject;
969
1044
  }
970
- }
1045
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",