@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.
- package/omd/core/omdEquationStack.js +60 -1
- package/omd/display/omdDisplay.js +94 -19
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
+
}
|