@teachinglab/omd 0.2.7 → 0.2.8

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.
@@ -60,21 +60,6 @@ export class Toolbar {
60
60
 
61
61
  // Add to main SVG so it is rendered
62
62
  this.canvas.svg.appendChild(this.toolbarGroup.svgObject);
63
-
64
-
65
- // Check if the SVG is actually in the DOM
66
- setTimeout(() => {
67
-
68
-
69
- // Check the actual SVG content
70
- console.log('Toolbar SVG innerHTML:', this.toolbarGroup.svgObject.innerHTML);
71
- console.log('Background SVG object:', this.background.svgObject);
72
- console.log('Background SVG innerHTML:', this.background.svgObject.outerHTML);
73
-
74
- // Check if the background is visible
75
- console.log('Background fill color:', this.background.svgObject.getAttribute('fill'));
76
- console.log('Background width/height:', this.background.svgObject.getAttribute('width'), this.background.svgObject.getAttribute('height'));
77
- }, 100);
78
63
  }
79
64
 
80
65
  /**
@@ -34,7 +34,9 @@ export class omdEquationStack extends jsvgGroup {
34
34
 
35
35
  // The sequence is the core. If a visualizer is needed, that's our sequence.
36
36
  if (options.stepVisualizer) {
37
- this.sequence = new omdStepVisualizer(steps, this.stylingOptions || {});
37
+ // Pass through step visualizer styling options
38
+ const stepVisualizerStyling = this.stylingOptions?.stepVisualizer || {};
39
+ this.sequence = new omdStepVisualizer(steps, stepVisualizerStyling);
38
40
  } else {
39
41
  this.sequence = new omdEquationSequenceNode(steps);
40
42
  }
@@ -44,6 +46,13 @@ export class omdEquationStack extends jsvgGroup {
44
46
  this.sequence.setDefaultEquationBackground(this.stylingOptions.equationBackground);
45
47
  }
46
48
 
49
+ // Apply step visualizer background styling if provided
50
+ if (options.stepVisualizer && this.stylingOptions?.stepVisualizerBackground) {
51
+ if (typeof this.sequence.setBackgroundStyle === 'function') {
52
+ this.sequence.setBackgroundStyle(this.stylingOptions.stepVisualizerBackground);
53
+ }
54
+ }
55
+
47
56
  // If a toolbar is needed, create it.
48
57
  if (this.toolbarOptions?.enabled) {
49
58
  // Default undo: call global hook if provided
@@ -508,7 +517,7 @@ export class omdEquationStack extends jsvgGroup {
508
517
  }
509
518
  if (seq.layoutManager) {
510
519
  try {
511
- seq.layoutManager.updateVisualLayout();
520
+ seq.layoutManager.updateVisualLayout(true); // Allow repositioning for equation stack changes
512
521
  seq.layoutManager.updateVisualVisibility();
513
522
  seq.layoutManager.updateAllLinePositions();
514
523
  } catch (_) {}
@@ -20,6 +20,7 @@ export class omdDisplay {
20
20
  autoScale: true, // Automatically scale content to fit container
21
21
  maxScale: 1, // Do not upscale beyond 1 by default
22
22
  edgePadding: 16, // Horizontal padding from edges when scaling
23
+ autoCloseStepVisualizer: true, // Close active step visualizer text boxes before autoscale to avoid shrink
23
24
  ...options
24
25
  };
25
26
 
@@ -399,6 +400,19 @@ export class omdDisplay {
399
400
  const containerWidth = this.container.offsetWidth || 0;
400
401
  const containerHeight = this.container.offsetHeight || 0;
401
402
 
403
+ // Early auto-close of step visualizer UI before measuring dimensions to avoid transient height inflation
404
+ if (this.options.autoCloseStepVisualizer && this.node) {
405
+ try {
406
+ if (typeof this.node.forceCloseAll === 'function') {
407
+ this.node.forceCloseAll();
408
+ } else if (typeof this.node.closeAllTextBoxes === 'function') {
409
+ this.node.closeAllTextBoxes();
410
+ } else if (typeof this.node.closeActiveDot === 'function') {
411
+ this.node.closeActiveDot();
412
+ }
413
+ } catch (e) { /* no-op */ }
414
+ }
415
+
402
416
  // Determine actual content size (prefer sequence/current step when available)
403
417
  let contentWidth = this.node.width || 0;
404
418
  let contentHeight = this.node.height || 0;
@@ -406,38 +420,71 @@ export class omdDisplay {
406
420
  const seq = this.node.getSequence();
407
421
  if (seq) {
408
422
  if (seq.width && seq.height) {
409
- contentWidth = seq.width;
410
- contentHeight = seq.height;
423
+ // For step visualizers, use sequenceWidth/Height instead of total dimensions to exclude visualizer elements from autoscale
424
+ contentWidth = seq.sequenceWidth || seq.width;
425
+ contentHeight = seq.sequenceHeight || seq.height;
426
+ console.log('omdDisplay: Using sequence dimensions for autoscale - sequenceWidth:', seq.sequenceWidth, 'sequenceHeight:', seq.sequenceHeight, 'width:', seq.width, 'height:', seq.height, 'contentWidth:', contentWidth, 'contentHeight:', contentHeight);
411
427
  }
412
428
  if (seq.getCurrentStep) {
413
429
  const step = seq.getCurrentStep();
414
430
  if (step && step.width && step.height) {
415
- contentWidth = Math.max(contentWidth, step.width);
416
- contentHeight = Math.max(contentHeight, step.height);
431
+ // For step visualizers, prioritize sequenceWidth/Height for dimension calculations
432
+ const stepWidth = seq.sequenceWidth || step.width;
433
+ const stepHeight = seq.sequenceHeight || step.height;
434
+ contentWidth = Math.max(contentWidth, stepWidth);
435
+ contentHeight = Math.max(contentHeight, stepHeight);
436
+ console.log('omdDisplay: Considering step dimensions - stepWidth:', stepWidth, 'stepHeight:', stepHeight, 'finalContentWidth:', contentWidth, 'finalContentHeight:', contentHeight);
417
437
  }
418
438
  }
419
439
  }
420
440
  }
441
+ console.log('omdDisplay: Final content dimensions for autoscale - width:', contentWidth, 'height:', contentHeight);
421
442
 
422
443
  // Compute scale to keep within bounds
423
444
  let scale = 1;
424
445
  if (this.options.autoScale && contentWidth > 0 && contentHeight > 0) {
425
- const hPad = this.options.edgePadding || 0;
426
- const vPadTop = this.options.topMargin || 0;
427
- const vPadBottom = this.options.bottomMargin || 0;
428
- // Reserve extra space for overlay toolbar if needed
429
- let reserveBottom = vPadBottom;
430
- if (this.node && typeof this.node.isToolbarOverlay === 'function' && this.node.isToolbarOverlay()) {
431
- const tH = (typeof this.node.getToolbarVisualHeight === 'function') ? this.node.getToolbarVisualHeight() : 0;
432
- reserveBottom += (tH + (this.node.getOverlayPadding ? this.node.getOverlayPadding() : 16));
446
+ // Optionally close any open step visualizer textbox to prevent transient height expansion
447
+ if (this.options.autoCloseStepVisualizer && this.node) {
448
+ try {
449
+ if (typeof this.node.closeActiveDot === 'function') {
450
+ this.node.closeActiveDot();
451
+ } else if (typeof this.node.closeAllTextBoxes === 'function') {
452
+ this.node.closeAllTextBoxes();
453
+ }
454
+ } catch (e) { /* no-op */ }
455
+ }
456
+ // Detect step visualizer directly on node (getSequence returns underlying sequence only)
457
+ let hasStepVisualizer = false;
458
+ if (this.node) {
459
+ const ctorName = this.node.constructor?.name;
460
+ hasStepVisualizer = (ctorName === 'omdStepVisualizer') || this.node.type === 'omdStepVisualizer' || (typeof omdStepVisualizer !== 'undefined' && this.node instanceof omdStepVisualizer);
461
+ console.log('omdDisplay: Step visualizer detection (node) ctor:', ctorName, 'type:', this.node.type, 'detected:', hasStepVisualizer);
462
+ }
463
+
464
+ if (hasStepVisualizer) {
465
+ // Preserve existing scale if already set on node; otherwise lock to 1.
466
+ const existingScale = (this.node && typeof this.node.scale === 'number') ? this.node.scale : undefined;
467
+ scale = (existingScale && existingScale > 0) ? existingScale : 1;
468
+ console.log('omdDisplay: Step visualizer detected - locking scale at', scale);
469
+ } else {
470
+ const hPad = this.options.edgePadding || 0;
471
+ const vPadTop = this.options.topMargin || 0;
472
+ const vPadBottom = this.options.bottomMargin || 0;
473
+ // Reserve extra space for overlay toolbar if needed
474
+ let reserveBottom = vPadBottom;
475
+ if (this.node && typeof this.node.isToolbarOverlay === 'function' && this.node.isToolbarOverlay()) {
476
+ const tH = (typeof this.node.getToolbarVisualHeight === 'function') ? this.node.getToolbarVisualHeight() : 0;
477
+ reserveBottom += (tH + (this.node.getOverlayPadding ? this.node.getOverlayPadding() : 16));
478
+ }
479
+ const availW = Math.max(0, containerWidth - hPad * 2);
480
+ const availH = Math.max(0, containerHeight - (vPadTop + reserveBottom));
481
+ const sx = availW > 0 ? (availW / contentWidth) : 1;
482
+ const sy = availH > 0 ? (availH / contentHeight) : 1;
483
+ const maxScale = (typeof this.options.maxScale === 'number') ? this.options.maxScale : 1;
484
+ scale = Math.min(sx, sy, maxScale);
485
+ if (!isFinite(scale) || scale <= 0) scale = 1;
486
+ console.log('omdDisplay: Autoscale calculation - availW:', availW, 'availH:', availH, 'contentW:', contentWidth, 'contentH:', contentHeight, 'scale:', scale);
433
487
  }
434
- const availW = Math.max(0, containerWidth - hPad * 2);
435
- const availH = Math.max(0, containerHeight - (vPadTop + reserveBottom));
436
- const sx = availW > 0 ? (availW / contentWidth) : 1;
437
- const sy = availH > 0 ? (availH / contentHeight) : 1;
438
- const maxScale = (typeof this.options.maxScale === 'number') ? this.options.maxScale : 1;
439
- scale = Math.min(sx, sy, maxScale);
440
- if (!isFinite(scale) || scale <= 0) scale = 1;
441
488
  }
442
489
 
443
490
  // Apply scale
@@ -451,14 +498,14 @@ export class omdDisplay {
451
498
  const screenCenterX = containerWidth / 2;
452
499
  x = screenCenterX - (this.node.alignPointX * scale);
453
500
  } else {
454
- const scaledWidth = (this.node.width || contentWidth) * scale;
501
+ const scaledWidth = contentWidth * scale;
455
502
  x = (containerWidth - scaledWidth) / 2;
456
503
  }
457
504
 
458
505
  // Decide whether positioning would move content outside container. If so,
459
506
  // prefer expanding the SVG viewBox instead of moving nodes.
460
- const scaledWidthFinal = (this.node.width || contentWidth) * scale;
461
- const scaledHeightFinal = (this.node.height || contentHeight) * scale;
507
+ const scaledWidthFinal = contentWidth * scale;
508
+ const scaledHeightFinal = contentHeight * scale;
462
509
  const totalNeededH = scaledHeightFinal + (this.options.topMargin || 0) + (this.options.bottomMargin || 0);
463
510
 
464
511
  const willOverflowHoriz = scaledWidthFinal > containerWidth;
@@ -517,15 +564,17 @@ export class omdDisplay {
517
564
  if (this.node.getSequence) {
518
565
  const sequence = this.node.getSequence();
519
566
  if (sequence && sequence.width && sequence.height) {
520
- sequenceWidth = sequence.width;
521
- sequenceHeight = sequence.height;
567
+ // For step visualizers, use sequenceWidth/Height instead of total dimensions to exclude visualizer elements from autoscale
568
+ sequenceWidth = sequence.sequenceWidth || sequence.width;
569
+ sequenceHeight = sequence.sequenceHeight || sequence.height;
522
570
 
523
571
  // Check current step dimensions too
524
572
  if (sequence.getCurrentStep) {
525
573
  const currentStep = sequence.getCurrentStep();
526
574
  if (currentStep && currentStep.width && currentStep.height) {
527
- stepWidth = currentStep.width;
528
- stepHeight = currentStep.height;
575
+ // For step visualizers, prioritize sequenceWidth/Height for dimension calculations
576
+ stepWidth = sequence.sequenceWidth || currentStep.width;
577
+ stepHeight = sequence.sequenceHeight || currentStep.height;
529
578
  }
530
579
  }
531
580
 
@@ -624,10 +673,7 @@ export class omdDisplay {
624
673
 
625
674
  // Apply any stored font settings
626
675
  if (this.options.fontFamily) {
627
- // Small delay to ensure SVG elements are fully rendered
628
- setTimeout(() => {
629
676
  this.setFont(this.options.fontFamily, this.options.fontWeight || '400');
630
- }, 10);
631
677
  }
632
678
 
633
679
  // Only use fitToContent for tight sizing when explicitly requested
@@ -1005,7 +1005,7 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1005
1005
  case 'table':
1006
1006
  return this._renderToTable(mergedOptions);
1007
1007
  case 'hanger':
1008
- return this._renderToHanger();
1008
+ return this._renderToHanger(mergedOptions);
1009
1009
  case 'tileequation': {
1010
1010
  const leftExpr = this.getLeft().toString();
1011
1011
  const rightExpr = this.getRight().toString();
@@ -1089,6 +1089,11 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1089
1089
  tickInterval: (options.tickInterval !== undefined) ? options.tickInterval : 1,
1090
1090
  forceAllTickLabels: (options.forceAllTickLabels !== undefined) ? options.forceAllTickLabels : true,
1091
1091
  showTickLabels: (options.showTickLabels !== undefined) ? options.showTickLabels : true,
1092
+ // Background customization options
1093
+ backgroundColor: (options.backgroundColor !== undefined) ? options.backgroundColor : undefined,
1094
+ backgroundCornerRadius: (options.backgroundCornerRadius !== undefined) ? options.backgroundCornerRadius : undefined,
1095
+ backgroundOpacity: (options.backgroundOpacity !== undefined) ? options.backgroundOpacity : undefined,
1096
+ showBackground: (options.showBackground !== undefined) ? options.showBackground : undefined,
1092
1097
  graphEquations,
1093
1098
  lineSegments: [],
1094
1099
  dotValues: [],
@@ -1113,7 +1118,17 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1113
1118
  equation: `y = ${expr}`,
1114
1119
  xMin: options.xMin,
1115
1120
  xMax: options.xMax,
1116
- stepSize: options.stepSize
1121
+ stepSize: options.stepSize,
1122
+ // Background customization options
1123
+ backgroundColor: (options.backgroundColor !== undefined) ? options.backgroundColor : undefined,
1124
+ backgroundCornerRadius: (options.backgroundCornerRadius !== undefined) ? options.backgroundCornerRadius : undefined,
1125
+ backgroundOpacity: (options.backgroundOpacity !== undefined) ? options.backgroundOpacity : undefined,
1126
+ showBackground: (options.showBackground !== undefined) ? options.showBackground : undefined,
1127
+ // Alternating row color options
1128
+ alternatingRowColors: (options.alternatingRowColors !== undefined) ? options.alternatingRowColors : undefined,
1129
+ evenRowColor: (options.evenRowColor !== undefined) ? options.evenRowColor : undefined,
1130
+ oddRowColor: (options.oddRowColor !== undefined) ? options.oddRowColor : undefined,
1131
+ alternatingRowOpacity: (options.alternatingRowOpacity !== undefined) ? options.alternatingRowOpacity : undefined
1117
1132
  };
1118
1133
  } else if (options.side === 'right') {
1119
1134
  const expr = this._normalizeExpressionString(this.getRight().toString());
@@ -1124,7 +1139,17 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1124
1139
  equation: `y = ${expr}`,
1125
1140
  xMin: options.xMin,
1126
1141
  xMax: options.xMax,
1127
- stepSize: options.stepSize
1142
+ stepSize: options.stepSize,
1143
+ // Background customization options
1144
+ backgroundColor: (options.backgroundColor !== undefined) ? options.backgroundColor : undefined,
1145
+ backgroundCornerRadius: (options.backgroundCornerRadius !== undefined) ? options.backgroundCornerRadius : undefined,
1146
+ backgroundOpacity: (options.backgroundOpacity !== undefined) ? options.backgroundOpacity : undefined,
1147
+ showBackground: (options.showBackground !== undefined) ? options.showBackground : undefined,
1148
+ // Alternating row color options
1149
+ alternatingRowColors: (options.alternatingRowColors !== undefined) ? options.alternatingRowColors : undefined,
1150
+ evenRowColor: (options.evenRowColor !== undefined) ? options.evenRowColor : undefined,
1151
+ oddRowColor: (options.oddRowColor !== undefined) ? options.oddRowColor : undefined,
1152
+ alternatingRowOpacity: (options.alternatingRowOpacity !== undefined) ? options.alternatingRowOpacity : undefined
1128
1153
  };
1129
1154
  }
1130
1155
 
@@ -1155,7 +1180,17 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1155
1180
  omdType: "table",
1156
1181
  title: `Equation Table: ${this.toString()}`,
1157
1182
  headers,
1158
- data
1183
+ data,
1184
+ // Background customization options
1185
+ backgroundColor: (options.backgroundColor !== undefined) ? options.backgroundColor : undefined,
1186
+ backgroundCornerRadius: (options.backgroundCornerRadius !== undefined) ? options.backgroundCornerRadius : undefined,
1187
+ backgroundOpacity: (options.backgroundOpacity !== undefined) ? options.backgroundOpacity : undefined,
1188
+ showBackground: (options.showBackground !== undefined) ? options.showBackground : undefined,
1189
+ // Alternating row color options
1190
+ alternatingRowColors: (options.alternatingRowColors !== undefined) ? options.alternatingRowColors : undefined,
1191
+ evenRowColor: (options.evenRowColor !== undefined) ? options.evenRowColor : undefined,
1192
+ oddRowColor: (options.oddRowColor !== undefined) ? options.oddRowColor : undefined,
1193
+ alternatingRowOpacity: (options.alternatingRowOpacity !== undefined) ? options.alternatingRowOpacity : undefined
1159
1194
  };
1160
1195
  }
1161
1196
 
@@ -1184,7 +1219,7 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1184
1219
  * @returns {Object} JSON configuration for omdBalanceHanger
1185
1220
  * @private
1186
1221
  */
1187
- _renderToHanger() {
1222
+ _renderToHanger(options = {}) {
1188
1223
  // Convert equation sides to hanger representation
1189
1224
  const leftValues = this._convertToHangerValues(this.getLeft());
1190
1225
  const rightValues = this._convertToHangerValues(this.getRight());
@@ -1193,7 +1228,12 @@ _propagateBackgroundStyle(style, visited = new Set()) {
1193
1228
  omdType: "balanceHanger",
1194
1229
  leftValues: leftValues,
1195
1230
  rightValues: rightValues,
1196
- tilt: "none" // Equations should be balanced by definition
1231
+ tilt: "none", // Equations should be balanced by definition
1232
+ // Background customization options
1233
+ backgroundColor: (options.backgroundColor !== undefined) ? options.backgroundColor : undefined,
1234
+ backgroundCornerRadius: (options.backgroundCornerRadius !== undefined) ? options.backgroundCornerRadius : undefined,
1235
+ backgroundOpacity: (options.backgroundOpacity !== undefined) ? options.backgroundOpacity : undefined,
1236
+ showBackground: (options.showBackground !== undefined) ? options.showBackground : undefined
1197
1237
  };
1198
1238
  }
1199
1239