@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.
- package/canvas/ui/toolbar.js +0 -15
- package/omd/core/omdEquationStack.js +11 -2
- package/omd/display/omdDisplay.js +75 -29
- package/omd/nodes/omdEquationNode.js +46 -6
- package/omd/step-visualizer/omdStepVisualizer.js +312 -29
- package/omd/step-visualizer/omdStepVisualizerLayout.js +122 -110
- package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +46 -8
- package/omd/utils/omdStepVisualizerInteractiveSteps.js +318 -121
- package/package.json +1 -1
- package/src/omdBalanceHanger.js +31 -1
- package/src/omdColor.js +1 -0
- package/src/omdCoordinatePlane.js +53 -3
- package/src/omdMetaExpression.js +8 -4
- package/src/omdTable.js +182 -52
|
@@ -5,26 +5,27 @@ import { jsvgLayoutGroup, jsvgTextBox, jsvgRect } from '@teachinglab/jsvg';
|
|
|
5
5
|
* Each step is a separate jsvgTextBox that can have hover interactions with the omdSequence
|
|
6
6
|
*/
|
|
7
7
|
export class omdStepVisualizerInteractiveSteps {
|
|
8
|
-
constructor(stepVisualizer, simplificationData) {
|
|
8
|
+
constructor(stepVisualizer, simplificationData, stylingOptions = {}) {
|
|
9
9
|
this.stepVisualizer = stepVisualizer;
|
|
10
10
|
this.simplificationData = simplificationData || {};
|
|
11
|
+
this.stylingOptions = stylingOptions || {};
|
|
11
12
|
this.messages = this._extractMessages(simplificationData);
|
|
12
13
|
this.ruleNames = this._extractRuleNames(simplificationData);
|
|
13
14
|
this.stepElements = [];
|
|
14
15
|
this.layoutGroup = new jsvgLayoutGroup();
|
|
15
|
-
this.layoutGroup.setSpacer(
|
|
16
|
-
|
|
17
|
-
// Styling configuration
|
|
18
|
-
this.stepWidth =
|
|
19
|
-
this.baseStepHeight =
|
|
20
|
-
this.headerHeight =
|
|
21
|
-
this.fontSize = 14;
|
|
16
|
+
this.layoutGroup.setSpacer(4); // Minimal spacing for tight layout
|
|
17
|
+
|
|
18
|
+
// Styling configuration with defaults that can be overridden
|
|
19
|
+
this.stepWidth = this.stylingOptions.maxWidth || 300; // Use maxWidth from styling options
|
|
20
|
+
this.baseStepHeight = 30; // Minimal height for tight fit
|
|
21
|
+
this.headerHeight = 28; // Minimal header height
|
|
22
|
+
this.fontSize = this.stylingOptions.fontSize || 14;
|
|
22
23
|
this.smallFontSize = 12;
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
this.setupLayoutGroup();
|
|
25
26
|
this.createStepElements();
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
/**
|
|
29
30
|
* Extracts messages from simplification data
|
|
30
31
|
* @param {Object} data - Simplification data
|
|
@@ -33,14 +34,14 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
33
34
|
*/
|
|
34
35
|
_extractMessages(data) {
|
|
35
36
|
if (!data) return [];
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
let messages = [];
|
|
38
39
|
if (data.rawMessages && Array.isArray(data.rawMessages)) {
|
|
39
40
|
messages = data.rawMessages;
|
|
40
41
|
} else if (data.message) {
|
|
41
42
|
messages = [data.message];
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
// Clean up messages - remove HTML tags and bullet points
|
|
45
46
|
return messages.map(msg => {
|
|
46
47
|
let clean = msg.replace(/<[^>]*>/g, ''); // Strip HTML tags
|
|
@@ -57,11 +58,11 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
57
58
|
*/
|
|
58
59
|
_extractRuleNames(data) {
|
|
59
60
|
if (!data) return ['Operation'];
|
|
60
|
-
|
|
61
|
+
|
|
61
62
|
if (data.ruleNames && Array.isArray(data.ruleNames)) {
|
|
62
63
|
return data.ruleNames;
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
// Default based on data type
|
|
66
67
|
if (data.multipleSimplifications) {
|
|
67
68
|
return ['Multiple Rules'];
|
|
@@ -75,45 +76,63 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
75
76
|
* @private
|
|
76
77
|
*/
|
|
77
78
|
setupLayoutGroup() {
|
|
78
|
-
// Add background using
|
|
79
|
+
// Add background using styling options for the entire step group
|
|
79
80
|
this.backgroundRect = new jsvgRect();
|
|
80
|
-
this.backgroundRect.setWidthAndHeight(this.stepWidth +
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
81
|
+
this.backgroundRect.setWidthAndHeight(this.stepWidth + 16, 60); // Minimal padding and height for tight fit
|
|
82
|
+
|
|
83
|
+
// Apply styling options to the background container
|
|
84
|
+
const backgroundColor = this.stylingOptions.backgroundColor || omdColor.lightGray;
|
|
85
|
+
const borderColor = this.stylingOptions.borderColor || '#e0e0e0';
|
|
86
|
+
const borderWidth = this.stylingOptions.borderWidth || 1;
|
|
87
|
+
const borderRadius = this.stylingOptions.borderRadius || 6;
|
|
88
|
+
|
|
89
|
+
this.backgroundRect.setFillColor(backgroundColor);
|
|
90
|
+
this.backgroundRect.setStrokeColor(borderColor);
|
|
91
|
+
this.backgroundRect.setStrokeWidth(borderWidth);
|
|
92
|
+
this.backgroundRect.setCornerRadius(borderRadius);
|
|
85
93
|
this.backgroundRect.setPosition(0, 0); // Start at origin, not negative offset
|
|
94
|
+
|
|
95
|
+
// Apply drop shadow to the SVG element if requested
|
|
96
|
+
if (this.stylingOptions.dropShadow && this.backgroundRect.svgObject) {
|
|
97
|
+
this.backgroundRect.svgObject.style.filter = 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))';
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
this.layoutGroup.addChild(this.backgroundRect);
|
|
87
101
|
}
|
|
88
|
-
|
|
102
|
+
|
|
89
103
|
/**
|
|
90
104
|
* Creates individual step elements from the messages array
|
|
91
105
|
* @private
|
|
92
106
|
*/
|
|
93
107
|
createStepElements() {
|
|
94
108
|
if (!this.messages || this.messages.length === 0) return;
|
|
95
|
-
|
|
109
|
+
|
|
96
110
|
// Create content container to separate from background
|
|
97
111
|
this.contentGroup = new jsvgLayoutGroup();
|
|
98
|
-
this.contentGroup.setSpacer(
|
|
99
|
-
this.contentGroup.setPosition(
|
|
100
|
-
|
|
112
|
+
this.contentGroup.setSpacer(2); // Minimal spacing between elements
|
|
113
|
+
this.contentGroup.setPosition(8, 8); // Minimal offset for tight fit
|
|
114
|
+
|
|
101
115
|
if (this.messages.length === 1) {
|
|
102
|
-
|
|
116
|
+
|
|
103
117
|
this.createSingleStepElement(this.messages[0], 0);
|
|
104
118
|
} else {
|
|
105
119
|
|
|
106
120
|
this.createMultipleStepElements();
|
|
107
121
|
}
|
|
108
|
-
|
|
122
|
+
|
|
109
123
|
this.contentGroup.doVerticalLayout();
|
|
110
124
|
this.layoutGroup.addChild(this.contentGroup);
|
|
111
125
|
this.updateBackgroundSize();
|
|
112
|
-
|
|
126
|
+
|
|
127
|
+
// Apply drop shadow after SVG element is created
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
this.applyDropShadowIfNeeded();
|
|
130
|
+
}, 10);
|
|
131
|
+
|
|
113
132
|
// Debug logging
|
|
114
133
|
|
|
115
134
|
}
|
|
116
|
-
|
|
135
|
+
|
|
117
136
|
/**
|
|
118
137
|
* Creates a single step element with header
|
|
119
138
|
* @param {string} message - The step message
|
|
@@ -125,13 +144,13 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
125
144
|
const ruleName = this.ruleNames[0] || 'Operation';
|
|
126
145
|
const headerBox = this.createHeaderBox(ruleName + ':');
|
|
127
146
|
this.contentGroup.addChild(headerBox);
|
|
128
|
-
|
|
147
|
+
|
|
129
148
|
// Create the step box
|
|
130
149
|
const stepBox = this.createStepTextBox(message, index, false);
|
|
131
150
|
this.stepElements.push(stepBox);
|
|
132
151
|
this.contentGroup.addChild(stepBox);
|
|
133
152
|
}
|
|
134
|
-
|
|
153
|
+
|
|
135
154
|
/**
|
|
136
155
|
* Creates multiple step elements with header
|
|
137
156
|
* @private
|
|
@@ -148,11 +167,11 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
148
167
|
} else {
|
|
149
168
|
headerText = `${this.ruleNames.length} Rules Applied:`;
|
|
150
169
|
}
|
|
151
|
-
|
|
170
|
+
|
|
152
171
|
const headerBox = this.createHeaderBox(headerText);
|
|
153
172
|
this.contentGroup.addChild(headerBox);
|
|
154
173
|
}
|
|
155
|
-
|
|
174
|
+
|
|
156
175
|
// Create individual step elements
|
|
157
176
|
this.messages.forEach((message, index) => {
|
|
158
177
|
const stepBox = this.createStepTextBox(message, index, this.messages.length > 1);
|
|
@@ -160,7 +179,7 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
160
179
|
this.contentGroup.addChild(stepBox);
|
|
161
180
|
});
|
|
162
181
|
}
|
|
163
|
-
|
|
182
|
+
|
|
164
183
|
/**
|
|
165
184
|
* Creates a header box with custom text
|
|
166
185
|
* @param {string} headerText - Text to display in header
|
|
@@ -169,20 +188,20 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
169
188
|
*/
|
|
170
189
|
createHeaderBox(headerText = 'Operation:') {
|
|
171
190
|
const headerBox = new jsvgTextBox();
|
|
172
|
-
const headerHeight = Math.max(this.headerHeight,
|
|
173
|
-
|
|
174
|
-
|
|
191
|
+
const headerHeight = Math.max(this.headerHeight, 50); // Increased minimum height
|
|
192
|
+
|
|
193
|
+
|
|
175
194
|
headerBox.setWidthAndHeight(this.stepWidth, headerHeight);
|
|
176
195
|
headerBox.setText(headerText);
|
|
177
196
|
headerBox.setFontSize(this.fontSize);
|
|
178
197
|
headerBox.setFontWeight('600');
|
|
179
198
|
headerBox.setFontColor('#2c3e50');
|
|
180
|
-
|
|
181
|
-
// Style the header with border
|
|
199
|
+
|
|
200
|
+
// Style the header with border and minimal spacing
|
|
182
201
|
if (headerBox.div) {
|
|
183
202
|
Object.assign(headerBox.div.style, {
|
|
184
203
|
borderBottom: '1px solid #e0e0e0',
|
|
185
|
-
padding: '8px
|
|
204
|
+
padding: '6px 8px 4px 8px', // Minimal padding for tight fit
|
|
186
205
|
margin: '0',
|
|
187
206
|
boxSizing: 'border-box',
|
|
188
207
|
minHeight: `${headerHeight}px`,
|
|
@@ -190,14 +209,19 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
190
209
|
whiteSpace: 'normal',
|
|
191
210
|
wordWrap: 'break-word',
|
|
192
211
|
overflowWrap: 'break-word',
|
|
193
|
-
width: '100%'
|
|
212
|
+
width: '100%',
|
|
213
|
+
lineHeight: '1.2', // Tight line height
|
|
214
|
+
fontFamily: 'Albert Sans, Arial, sans-serif',
|
|
215
|
+
display: 'flex',
|
|
216
|
+
alignItems: 'center', // Center text vertically
|
|
217
|
+
justifyContent: 'flex-start'
|
|
194
218
|
});
|
|
195
219
|
}
|
|
196
220
|
|
|
197
|
-
|
|
221
|
+
|
|
198
222
|
return headerBox;
|
|
199
223
|
}
|
|
200
|
-
|
|
224
|
+
|
|
201
225
|
/**
|
|
202
226
|
* Creates an individual step text box
|
|
203
227
|
* @param {string} message - Step message
|
|
@@ -208,33 +232,63 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
208
232
|
*/
|
|
209
233
|
createStepTextBox(message, index, isMultiple) {
|
|
210
234
|
const stepBox = new jsvgTextBox();
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
// Calculate actual height needed for content with minimal padding
|
|
236
|
+
const contentHeight = this.calculateContentHeight(message, index, isMultiple);
|
|
237
|
+
const height = Math.max(contentHeight, this.baseStepHeight); // Use calculated height
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
215
241
|
stepBox.setWidthAndHeight(this.stepWidth, height);
|
|
216
242
|
stepBox.setFontSize(this.fontSize);
|
|
217
243
|
stepBox.setFontColor('#2c3e50');
|
|
218
|
-
|
|
244
|
+
|
|
219
245
|
// Store step data for interactions
|
|
220
246
|
stepBox.stepIndex = index;
|
|
221
247
|
stepBox.stepMessage = message;
|
|
222
248
|
stepBox.isMultiple = isMultiple;
|
|
223
|
-
|
|
249
|
+
|
|
224
250
|
// Format the step content
|
|
225
251
|
const formattedContent = this.formatStepContent(message, index, isMultiple);
|
|
226
|
-
|
|
252
|
+
|
|
227
253
|
// Apply styling and content
|
|
228
254
|
if (stepBox.div) {
|
|
229
|
-
this.applyStepStyling(stepBox, formattedContent, isMultiple);
|
|
255
|
+
this.applyStepStyling(stepBox, formattedContent, isMultiple, height);
|
|
230
256
|
this.setupStepInteractions(stepBox);
|
|
231
|
-
|
|
257
|
+
|
|
258
|
+
// Force the jsvgTextBox to respect our sizing
|
|
259
|
+
stepBox.div.style.height = `${height}px`;
|
|
260
|
+
stepBox.div.style.minHeight = `${height}px`;
|
|
261
|
+
stepBox.div.style.display = 'block';
|
|
262
|
+
|
|
263
|
+
// Add a more aggressive override after a delay to ensure it sticks
|
|
264
|
+
if (stepBox.div) {
|
|
265
|
+
const actualPadding = this.stylingOptions.padding || 6; // Get padding from styling options
|
|
266
|
+
stepBox.div.style.cssText += `
|
|
267
|
+
height: ${height}px !important;
|
|
268
|
+
min-height: ${height}px !important;
|
|
269
|
+
max-height: ${height}px !important;
|
|
270
|
+
padding: ${actualPadding}px ${actualPadding + 2}px !important;
|
|
271
|
+
line-height: 1.3 !important;
|
|
272
|
+
font-size: ${this.fontSize}px !important;
|
|
273
|
+
font-family: Albert Sans, Arial, sans-serif !important;
|
|
274
|
+
box-sizing: border-box !important;
|
|
275
|
+
display: flex !important;
|
|
276
|
+
flex-direction: column !important;
|
|
277
|
+
justify-content: center !important;
|
|
278
|
+
align-items: flex-start !important;
|
|
279
|
+
word-spacing: normal !important;
|
|
280
|
+
letter-spacing: normal !important;
|
|
281
|
+
transition: none !important;
|
|
282
|
+
transform: none !important;
|
|
283
|
+
animation: none !important;
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
232
286
|
|
|
233
287
|
}
|
|
234
|
-
|
|
288
|
+
|
|
235
289
|
return stepBox;
|
|
236
290
|
}
|
|
237
|
-
|
|
291
|
+
|
|
238
292
|
/**
|
|
239
293
|
* Formats the content for a step
|
|
240
294
|
* @param {string} message - Raw message
|
|
@@ -246,16 +300,16 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
246
300
|
formatStepContent(message, index, isMultiple) {
|
|
247
301
|
const cleanMessage = message.trim();
|
|
248
302
|
let content = '';
|
|
249
|
-
|
|
303
|
+
|
|
250
304
|
// Only show step numbers for multiple steps
|
|
251
305
|
if (isMultiple && this.messages.length > 1) {
|
|
252
|
-
content += `<div class="step-number" style="color: #666; font-size: ${this.smallFontSize}px; margin: 0 0 2px 0; font-weight: 500;">Step ${index + 1}</div>`;
|
|
306
|
+
content += `<div class="step-number" style="color: #666; font-size: ${this.smallFontSize}px; margin: 0 0 2px 0; font-weight: 500; line-height: 1.2; font-family: Albert Sans, Arial, sans-serif;">Step ${index + 1}</div>`; // Minimal margin
|
|
253
307
|
}
|
|
254
|
-
|
|
255
|
-
content += '<div class="step-content" style="display: flex; align-items:
|
|
256
|
-
content += '<span class="bullet" style="color: #666; font-weight: bold; flex-shrink: 0;
|
|
257
|
-
content += '<div class="step-text" style="margin: 0; flex: 1; min-width: 0; word-wrap: break-word; overflow-wrap: break-word;">';
|
|
258
|
-
|
|
308
|
+
|
|
309
|
+
content += '<div class="step-content" style="display: flex; align-items: center; gap: 6px; margin: 0; width: 100%; line-height: 1.3; font-family: Albert Sans, Arial, sans-serif;">'; // Center align and minimal spacing
|
|
310
|
+
content += '<span class="bullet" style="color: #666; font-weight: bold; flex-shrink: 0; font-size: 10px;">•</span>'; // Smaller bullet
|
|
311
|
+
content += '<div class="step-text" style="margin: 0; flex: 1; min-width: 0; word-wrap: break-word; overflow-wrap: break-word; line-height: 1.3; padding: 0;">';
|
|
312
|
+
|
|
259
313
|
// Parse operation details
|
|
260
314
|
if (this.isOperationMessage(cleanMessage)) {
|
|
261
315
|
const action = this.extractOperationAction(cleanMessage);
|
|
@@ -263,54 +317,109 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
263
317
|
const valueNode = this.extractOperationValueNode(cleanMessage);
|
|
264
318
|
|
|
265
319
|
if (action && (value || valueNode)) {
|
|
266
|
-
content += `<span style="font-weight: 600; color: #2c3e50;">${action}</span> `;
|
|
320
|
+
content += `<span style="font-weight: 600; color: #2c3e50; margin-right: 4px;">${action}</span> `;
|
|
267
321
|
const displayValue = valueNode ? valueNode.toString() : value;
|
|
268
|
-
content += `<span style="background: #f5f5f5; padding:
|
|
269
|
-
content += `<span style="color: #666; font-size: ${this.smallFontSize}px;"> to both sides</span>`;
|
|
322
|
+
content += `<span style="background: #f5f5f5; padding: 3px 8px; border-radius: 4px; font-family: 'Courier New', monospace; color: #d63384; margin: 0 3px;">${displayValue}</span>`;
|
|
323
|
+
content += `<span style="color: #666; font-size: ${this.smallFontSize}px; margin-left: 4px;"> to both sides</span>`;
|
|
270
324
|
} else {
|
|
271
|
-
content += `<span>${cleanMessage}</span>`;
|
|
325
|
+
content += `<span style="padding: 2px 0;">${cleanMessage}</span>`;
|
|
272
326
|
}
|
|
273
327
|
} else {
|
|
274
|
-
content += `<span style="font-weight: 500;">${cleanMessage}</span>`;
|
|
328
|
+
content += `<span style="font-weight: 500; padding: 2px 0;">${cleanMessage}</span>`;
|
|
275
329
|
}
|
|
276
|
-
|
|
330
|
+
|
|
277
331
|
content += '</div></div>';
|
|
278
332
|
return content;
|
|
279
333
|
}
|
|
280
|
-
|
|
334
|
+
|
|
281
335
|
/**
|
|
282
336
|
* Applies styling to a step text box
|
|
283
337
|
* @param {jsvgTextBox} stepBox - The step box
|
|
284
338
|
* @param {string} content - Formatted content
|
|
285
339
|
* @param {boolean} isMultiple - Whether part of multiple steps
|
|
340
|
+
* @param {number} height - The calculated height for the step box
|
|
286
341
|
* @private
|
|
287
342
|
*/
|
|
288
|
-
applyStepStyling(stepBox, content, isMultiple) {
|
|
343
|
+
applyStepStyling(stepBox, content, isMultiple, height) {
|
|
344
|
+
const backgroundColor = this.stylingOptions.backgroundColor || omdColor.white;
|
|
345
|
+
const borderColor = this.stylingOptions.borderColor || omdColor.lightGray;
|
|
346
|
+
const borderWidth = this.stylingOptions.borderWidth || 1;
|
|
347
|
+
const borderRadius = this.stylingOptions.borderRadius || 4;
|
|
348
|
+
const padding = this.stylingOptions.padding || 6; // Minimal padding for tight fit
|
|
349
|
+
|
|
289
350
|
const baseStyles = {
|
|
290
|
-
padding:
|
|
291
|
-
borderRadius:
|
|
292
|
-
|
|
351
|
+
padding: `${padding}px ${padding + 2}px !important`, // Minimal padding for tight fit
|
|
352
|
+
borderRadius: `${borderRadius}px`,
|
|
353
|
+
border: `${borderWidth}px solid ${borderColor}`,
|
|
354
|
+
backgroundColor: backgroundColor,
|
|
293
355
|
cursor: 'pointer',
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
356
|
+
transition: 'none !important', // Explicitly disable all transitions
|
|
357
|
+
transform: 'none !important', // Explicitly disable all transforms
|
|
358
|
+
animation: 'none !important', // Explicitly disable all animations
|
|
359
|
+
lineHeight: '1.3 !important', // Tight line height
|
|
360
|
+
margin: '0',
|
|
297
361
|
boxSizing: 'border-box',
|
|
298
362
|
overflow: 'visible',
|
|
299
|
-
minHeight: `${
|
|
363
|
+
minHeight: `${height}px !important`, // Use calculated height
|
|
364
|
+
height: `${height}px !important`, // Fixed height to content
|
|
300
365
|
width: '100%',
|
|
301
|
-
whiteSpace: 'normal',
|
|
302
|
-
wordWrap: 'break-word',
|
|
303
|
-
overflowWrap: 'break-word',
|
|
304
|
-
maxWidth: '100%'
|
|
366
|
+
whiteSpace: 'normal',
|
|
367
|
+
wordWrap: 'break-word',
|
|
368
|
+
overflowWrap: 'break-word',
|
|
369
|
+
maxWidth: '100%',
|
|
370
|
+
fontSize: `${this.fontSize}px !important`, // Force font size
|
|
371
|
+
fontFamily: 'Albert Sans, Arial, sans-serif !important', // Albert Sans font
|
|
372
|
+
display: 'flex !important', // Use flex for centering
|
|
373
|
+
flexDirection: 'column !important',
|
|
374
|
+
justifyContent: 'center !important', // Center content vertically
|
|
375
|
+
alignItems: 'flex-start !important'
|
|
305
376
|
};
|
|
306
|
-
|
|
377
|
+
|
|
378
|
+
// Add drop shadow if requested
|
|
379
|
+
if (this.stylingOptions.dropShadow) {
|
|
380
|
+
baseStyles.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Set font family if specified
|
|
384
|
+
if (this.stylingOptions.fontFamily) {
|
|
385
|
+
baseStyles.fontFamily = this.stylingOptions.fontFamily;
|
|
386
|
+
}
|
|
387
|
+
|
|
307
388
|
Object.assign(stepBox.div.style, baseStyles);
|
|
308
389
|
stepBox.div.innerHTML = content;
|
|
309
|
-
|
|
390
|
+
|
|
391
|
+
// Additional CSS to force proper text spacing
|
|
392
|
+
if (stepBox.div) {
|
|
393
|
+
stepBox.div.style.cssText += `
|
|
394
|
+
padding: ${padding + 6}px ${padding + 10}px !important;
|
|
395
|
+
line-height: 1.7 !important;
|
|
396
|
+
min-height: ${this.baseStepHeight + 20}px !important;
|
|
397
|
+
font-size: ${this.fontSize}px !important;
|
|
398
|
+
display: flex !important;
|
|
399
|
+
flex-direction: column !important;
|
|
400
|
+
`;
|
|
401
|
+
|
|
402
|
+
// Apply proper layout to nested content - DO NOT use position absolute
|
|
403
|
+
const contentElements = stepBox.div.querySelectorAll('.step-content, .step-text');
|
|
404
|
+
contentElements.forEach(el => {
|
|
405
|
+
el.style.lineHeight = '1.3 !important';
|
|
406
|
+
el.style.margin = '0 !important';
|
|
407
|
+
el.style.fontFamily = 'Albert Sans, Arial, sans-serif !important';
|
|
408
|
+
// Remove any position absolute that might be inherited
|
|
409
|
+
el.style.position = 'static !important';
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Ensure bullet points and text spans stay in normal flow
|
|
413
|
+
const allSpans = stepBox.div.querySelectorAll('span');
|
|
414
|
+
allSpans.forEach(span => {
|
|
415
|
+
span.style.position = 'static !important';
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
310
419
|
// Force a reflow to ensure proper sizing
|
|
311
420
|
stepBox.div.offsetHeight;
|
|
312
421
|
}
|
|
313
|
-
|
|
422
|
+
|
|
314
423
|
/**
|
|
315
424
|
* Sets up hover and click interactions for a step
|
|
316
425
|
* @param {jsvgTextBox} stepBox - The step box
|
|
@@ -323,76 +432,97 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
323
432
|
// Hover effects
|
|
324
433
|
stepBox.div.addEventListener('mouseenter', () => {
|
|
325
434
|
stepBox.div.style.backgroundColor = omdColor.mediumGray; // Slightly darker version of explainColor
|
|
326
|
-
|
|
327
|
-
|
|
435
|
+
|
|
328
436
|
// Call hover callback if provided
|
|
329
437
|
this.onStepHover?.(stepBox.stepIndex, stepBox.stepMessage, true);
|
|
330
438
|
});
|
|
331
|
-
|
|
439
|
+
|
|
332
440
|
stepBox.div.addEventListener('mouseleave', () => {
|
|
333
441
|
// Restore the original background color instead of setting to transparent
|
|
334
442
|
stepBox.div.style.backgroundColor = originalBackgroundColor;
|
|
335
|
-
|
|
336
|
-
|
|
443
|
+
|
|
337
444
|
// Call hover callback if provided
|
|
338
445
|
this.onStepHover?.(stepBox.stepIndex, stepBox.stepMessage, false);
|
|
339
446
|
});
|
|
340
|
-
|
|
447
|
+
|
|
341
448
|
// Click interactions
|
|
342
449
|
stepBox.div.addEventListener('click', () => {
|
|
343
450
|
this.onStepClick?.(stepBox.stepIndex, stepBox.stepMessage);
|
|
344
451
|
});
|
|
345
452
|
}
|
|
346
|
-
|
|
453
|
+
|
|
347
454
|
/**
|
|
348
|
-
* Calculates the height needed for
|
|
455
|
+
* Calculates the exact height needed for content with minimal padding
|
|
349
456
|
* @param {string} message - Step message
|
|
350
|
-
* @
|
|
457
|
+
* @param {number} index - Step index
|
|
458
|
+
* @param {boolean} isMultiple - Whether part of multiple steps
|
|
459
|
+
* @returns {number} Tight-fitting height in pixels
|
|
351
460
|
* @private
|
|
352
461
|
*/
|
|
353
|
-
|
|
462
|
+
calculateContentHeight(message, index, isMultiple) {
|
|
354
463
|
// Create a temporary element to measure actual text height
|
|
355
464
|
const tempDiv = document.createElement('div');
|
|
356
465
|
tempDiv.style.position = 'absolute';
|
|
357
466
|
tempDiv.style.visibility = 'hidden';
|
|
358
|
-
tempDiv.style.width = `${this.stepWidth -
|
|
467
|
+
tempDiv.style.width = `${this.stepWidth - 16}px`; // Account for minimal padding
|
|
359
468
|
tempDiv.style.fontSize = `${this.fontSize}px`;
|
|
360
|
-
tempDiv.style.lineHeight = '1.
|
|
361
|
-
tempDiv.style.fontFamily = '
|
|
469
|
+
tempDiv.style.lineHeight = '1.3'; // Tight line height
|
|
470
|
+
tempDiv.style.fontFamily = 'Albert Sans, Arial, sans-serif';
|
|
362
471
|
tempDiv.style.whiteSpace = 'normal';
|
|
363
472
|
tempDiv.style.wordWrap = 'break-word';
|
|
364
473
|
tempDiv.style.overflowWrap = 'break-word';
|
|
365
|
-
tempDiv.style.padding = '8px
|
|
474
|
+
tempDiv.style.padding = '6px 8px'; // Match the minimal padding
|
|
366
475
|
tempDiv.style.boxSizing = 'border-box';
|
|
367
|
-
|
|
476
|
+
tempDiv.style.display = 'flex';
|
|
477
|
+
tempDiv.style.flexDirection = 'column';
|
|
478
|
+
tempDiv.style.justifyContent = 'center';
|
|
479
|
+
|
|
368
480
|
// Use actual formatted content for measurement
|
|
369
|
-
const
|
|
370
|
-
const formattedContent = this.formatStepContent(message, 0, isMultiple);
|
|
481
|
+
const formattedContent = this.formatStepContent(message, index, isMultiple);
|
|
371
482
|
tempDiv.innerHTML = formattedContent;
|
|
372
|
-
|
|
483
|
+
|
|
373
484
|
// Append to document to measure
|
|
374
485
|
document.body.appendChild(tempDiv);
|
|
375
486
|
const measuredHeight = tempDiv.offsetHeight;
|
|
376
487
|
document.body.removeChild(tempDiv);
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return finalHeight;
|
|
488
|
+
|
|
489
|
+
// Return exact measured height with minimal buffer
|
|
490
|
+
return Math.max(this.baseStepHeight, measuredHeight + 2); // Just 2px buffer
|
|
382
491
|
}
|
|
383
|
-
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Calculates the height needed for a step box (legacy method, kept for compatibility)
|
|
495
|
+
* @param {string} message - Step message
|
|
496
|
+
* @returns {number} Height in pixels
|
|
497
|
+
* @private
|
|
498
|
+
*/
|
|
499
|
+
calculateStepHeight(message) {
|
|
500
|
+
// Use the new tight-fitting calculation
|
|
501
|
+
return this.calculateContentHeight(message, 0, false);
|
|
502
|
+
}
|
|
503
|
+
|
|
384
504
|
/**
|
|
385
505
|
* Updates the background rectangle size after layout
|
|
386
506
|
* @private
|
|
387
507
|
*/
|
|
388
508
|
updateBackgroundSize() {
|
|
389
509
|
if (this.backgroundRect && this.contentGroup) {
|
|
390
|
-
const totalHeight = this.contentGroup.height +
|
|
391
|
-
const totalWidth = this.stepWidth +
|
|
510
|
+
const totalHeight = this.contentGroup.height + 16; // Minimal padding for tight fit
|
|
511
|
+
const totalWidth = this.stepWidth + 16; // Minimal padding for tight fit
|
|
392
512
|
this.backgroundRect.setWidthAndHeight(totalWidth, totalHeight);
|
|
393
513
|
}
|
|
394
514
|
}
|
|
395
|
-
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Applies drop shadow to the background container if SVG element exists
|
|
518
|
+
* @private
|
|
519
|
+
*/
|
|
520
|
+
applyDropShadowIfNeeded() {
|
|
521
|
+
if (this.stylingOptions.dropShadow && this.backgroundRect && this.backgroundRect.svgObject) {
|
|
522
|
+
this.backgroundRect.svgObject.style.filter = 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))';
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
396
526
|
/**
|
|
397
527
|
* Sets callback for step hover events
|
|
398
528
|
* @param {Function} callback - Function called with (stepIndex, message, isEntering)
|
|
@@ -400,7 +530,7 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
400
530
|
setOnStepHover(callback) {
|
|
401
531
|
this.onStepHover = callback;
|
|
402
532
|
}
|
|
403
|
-
|
|
533
|
+
|
|
404
534
|
/**
|
|
405
535
|
* Sets callback for step click events
|
|
406
536
|
* @param {Function} callback - Function called with (stepIndex, message)
|
|
@@ -408,7 +538,7 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
408
538
|
setOnStepClick(callback) {
|
|
409
539
|
this.onStepClick = callback;
|
|
410
540
|
}
|
|
411
|
-
|
|
541
|
+
|
|
412
542
|
/**
|
|
413
543
|
* Gets the main layout group for adding to parent containers
|
|
414
544
|
* @returns {jsvgLayoutGroup} The layout group
|
|
@@ -416,7 +546,7 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
416
546
|
getLayoutGroup() {
|
|
417
547
|
return this.layoutGroup;
|
|
418
548
|
}
|
|
419
|
-
|
|
549
|
+
|
|
420
550
|
/**
|
|
421
551
|
* Sets the position of the entire step group
|
|
422
552
|
* @param {number} x - X position
|
|
@@ -425,7 +555,7 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
425
555
|
setPosition(x, y) {
|
|
426
556
|
this.layoutGroup.setPosition(x, y);
|
|
427
557
|
}
|
|
428
|
-
|
|
558
|
+
|
|
429
559
|
/**
|
|
430
560
|
* Gets the dimensions of the step group
|
|
431
561
|
* @returns {Object} Width and height
|
|
@@ -436,21 +566,88 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
436
566
|
height: this.backgroundRect ? this.backgroundRect.height : 100
|
|
437
567
|
};
|
|
438
568
|
}
|
|
439
|
-
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Updates the styling options and re-applies them to existing elements
|
|
572
|
+
* @param {Object} newStylingOptions - New styling options
|
|
573
|
+
*/
|
|
574
|
+
updateStyling(newStylingOptions = {}) {
|
|
575
|
+
this.stylingOptions = { ...this.stylingOptions, ...newStylingOptions };
|
|
576
|
+
|
|
577
|
+
// Update width if maxWidth changed
|
|
578
|
+
if (newStylingOptions.maxWidth) {
|
|
579
|
+
this.stepWidth = newStylingOptions.maxWidth;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Update font size if changed
|
|
583
|
+
if (newStylingOptions.fontSize) {
|
|
584
|
+
this.fontSize = newStylingOptions.fontSize;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Update background container styling
|
|
588
|
+
if (this.backgroundRect) {
|
|
589
|
+
const backgroundColor = this.stylingOptions.backgroundColor || omdColor.lightGray;
|
|
590
|
+
const borderColor = this.stylingOptions.borderColor || '#e0e0e0';
|
|
591
|
+
const borderWidth = this.stylingOptions.borderWidth || 1;
|
|
592
|
+
const borderRadius = this.stylingOptions.borderRadius || 6;
|
|
593
|
+
|
|
594
|
+
this.backgroundRect.setFillColor(backgroundColor);
|
|
595
|
+
this.backgroundRect.setStrokeColor(borderColor);
|
|
596
|
+
this.backgroundRect.setStrokeWidth(borderWidth);
|
|
597
|
+
this.backgroundRect.setCornerRadius(borderRadius);
|
|
598
|
+
|
|
599
|
+
// Apply or remove drop shadow
|
|
600
|
+
if (this.backgroundRect.svgObject) {
|
|
601
|
+
if (this.stylingOptions.dropShadow) {
|
|
602
|
+
this.backgroundRect.svgObject.style.filter = 'drop-shadow(0 2px 8px rgba(0,0,0,0.15))';
|
|
603
|
+
} else {
|
|
604
|
+
this.backgroundRect.svgObject.style.filter = '';
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Re-apply styling to all existing step elements
|
|
610
|
+
this.stepElements.forEach((stepBox, index) => {
|
|
611
|
+
if (stepBox.div) {
|
|
612
|
+
const content = stepBox.div.innerHTML;
|
|
613
|
+
// Calculate new height for the step
|
|
614
|
+
const height = this.calculateContentHeight(stepBox.stepMessage, index, stepBox.isMultiple);
|
|
615
|
+
this.applyStepStyling(stepBox, content, stepBox.isMultiple, height);
|
|
616
|
+
|
|
617
|
+
// Update font size
|
|
618
|
+
stepBox.setFontSize(this.fontSize);
|
|
619
|
+
|
|
620
|
+
// Update dimensions if needed
|
|
621
|
+
stepBox.setWidthAndHeight(this.stepWidth, height);
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// Update background size
|
|
626
|
+
this.updateBackgroundSize();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Gets the current styling options
|
|
631
|
+
* @returns {Object} Current styling options
|
|
632
|
+
*/
|
|
633
|
+
getStyling() {
|
|
634
|
+
return { ...this.stylingOptions };
|
|
635
|
+
}
|
|
636
|
+
|
|
440
637
|
// Helper methods for message parsing (same as in formatter)
|
|
441
|
-
|
|
638
|
+
|
|
442
639
|
isOperationMessage(message) {
|
|
443
640
|
const operationKeywords = ['Applied', 'added', 'subtracted', 'multiplied', 'divided', 'both sides'];
|
|
444
|
-
return operationKeywords.some(keyword =>
|
|
641
|
+
return operationKeywords.some(keyword =>
|
|
445
642
|
message.toLowerCase().includes(keyword.toLowerCase())
|
|
446
643
|
);
|
|
447
644
|
}
|
|
448
|
-
|
|
645
|
+
|
|
449
646
|
extractOperationAction(message) {
|
|
450
647
|
const match = message.match(/^(Added|Subtracted|Multiplied|Divided)/i);
|
|
451
648
|
return match ? match[0] : null;
|
|
452
649
|
}
|
|
453
|
-
|
|
650
|
+
|
|
454
651
|
extractOperationValue(message) {
|
|
455
652
|
// Updated regex to handle simple values and expressions
|
|
456
653
|
const match = message.match(/(?:Added|Subtracted|Multiplied|Divided)\s(.*?)\s(?:to|by)/i);
|
|
@@ -463,14 +660,14 @@ export class omdStepVisualizerInteractiveSteps {
|
|
|
463
660
|
}
|
|
464
661
|
return null;
|
|
465
662
|
}
|
|
466
|
-
|
|
663
|
+
|
|
467
664
|
extractOperationValueNode(message) {
|
|
468
665
|
if (this.simplificationData && this.simplificationData.operationValueNode) {
|
|
469
666
|
return this.simplificationData.operationValueNode;
|
|
470
667
|
}
|
|
471
668
|
return null;
|
|
472
669
|
}
|
|
473
|
-
|
|
670
|
+
|
|
474
671
|
/**
|
|
475
672
|
* Destroys the step group and cleans up resources
|
|
476
673
|
*/
|