@teachinglab/omd 0.1.4 → 0.1.6
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/tools/EraserTool.js +1 -1
- package/canvas/tools/PencilTool.js +1 -1
- package/canvas/tools/SelectTool.js +1 -1
- package/docs/api/configuration-options.md +198 -104
- package/docs/api/eventManager.md +83 -68
- package/docs/api/focusFrameManager.md +145 -150
- package/docs/api/index.md +106 -91
- package/docs/api/main.md +63 -58
- package/docs/api/omdBinaryExpressionNode.md +86 -227
- package/docs/api/omdCanvas.md +84 -142
- package/docs/api/omdConfigManager.md +113 -192
- package/docs/api/omdConstantNode.md +53 -117
- package/docs/api/omdDisplay.md +87 -121
- package/docs/api/omdEquationNode.md +174 -161
- package/docs/api/omdEquationSequenceNode.md +259 -301
- package/docs/api/omdEquationStack.md +157 -103
- package/docs/api/omdFunctionNode.md +83 -141
- package/docs/api/omdGroupNode.md +79 -182
- package/docs/api/omdHelpers.md +88 -96
- package/docs/api/omdLeafNode.md +86 -163
- package/docs/api/omdNode.md +202 -101
- package/docs/api/omdOperationDisplayNode.md +118 -139
- package/docs/api/omdOperatorNode.md +92 -127
- package/docs/api/omdParenthesisNode.md +134 -122
- package/docs/api/omdPopup.md +192 -117
- package/docs/api/omdPowerNode.md +132 -127
- package/docs/api/omdRationalNode.md +145 -128
- package/docs/api/omdSimplification.md +79 -110
- package/docs/api/omdSqrtNode.md +144 -79
- package/docs/api/omdStepVisualizer.md +147 -115
- package/docs/api/omdStepVisualizerHighlighting.md +66 -61
- package/docs/api/omdStepVisualizerInteractiveSteps.md +109 -129
- package/docs/api/omdStepVisualizerLayout.md +71 -60
- package/docs/api/omdStepVisualizerTextBoxes.md +77 -68
- package/docs/api/omdToolbar.md +131 -102
- package/docs/api/omdTranscriptionService.md +96 -76
- package/docs/api/omdTreeDiff.md +170 -134
- package/docs/api/omdUnaryExpressionNode.md +137 -174
- package/docs/api/omdUtilities.md +83 -70
- package/docs/api/omdVariableNode.md +123 -148
- package/index.js +2 -2
- package/omd/core/index.js +9 -0
- package/omd/nodes/omdEquationSequenceNode.js +15 -0
- package/omd/utils/aiNextEquationStep.js +106 -0
- package/package.json +1 -1
- package/src/omdBalanceHanger.js +1 -1
- package/src/omdCoordinatePlane.js +1 -1
package/docs/api/omdUtilities.md
CHANGED
|
@@ -1,70 +1,83 @@
|
|
|
1
|
-
# omdUtilities
|
|
2
|
-
|
|
3
|
-
This module provides a collection of utility functions primarily used for mapping math.js AST nodes to OMD node classes, determining rendering behavior, and calculating text dimensions.
|
|
4
|
-
|
|
5
|
-
## Functions
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Maps a math.js AST node type to its corresponding OMD node class. This function is crucial for dynamically creating the correct `omdNode` subclass based on the parsed expression.
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
# omdUtilities
|
|
2
|
+
|
|
3
|
+
This module provides a collection of utility functions primarily used for mapping math.js AST nodes to OMD node classes, determining rendering behavior, and calculating text dimensions. These functions are internal helpers that support the core OMD node system.
|
|
4
|
+
|
|
5
|
+
## Functions
|
|
6
|
+
|
|
7
|
+
### `astToOmdType(type, ast)`
|
|
8
|
+
|
|
9
|
+
Maps a math.js AST node type to its corresponding OMD node class. This function is crucial for dynamically creating the correct `omdNode` subclass based on the parsed expression.
|
|
10
|
+
|
|
11
|
+
- **`type`** (`string`): The `type` property of the AST node (e.g., `"OperatorNode"`, `"ParenthesisNode"`).
|
|
12
|
+
- **`ast`** (`object`): The full AST node object, which may contain additional context (e.g., `op` for `OperatorNode`, `fn` for `FunctionNode`).
|
|
13
|
+
- **Returns**: `class` - The appropriate `omdNode` class constructor.
|
|
14
|
+
|
|
15
|
+
**Detailed Logic:**
|
|
16
|
+
|
|
17
|
+
- **`AssignmentNode`**: Returns `omdEquationNode`.
|
|
18
|
+
- **`OperatorNode`**:
|
|
19
|
+
- If `op` is `'-'` and `args.length` is `1` (unary minus), returns `omdUnaryExpressionNode`.
|
|
20
|
+
- If `op` is `'='`, returns `omdEquationNode`.
|
|
21
|
+
- If `op` is `'^'`, returns `omdPowerNode`.
|
|
22
|
+
- If `op` is `'/'`, returns `omdRationalNode`.
|
|
23
|
+
- Otherwise (binary operator), returns `omdBinaryExpressionNode`.
|
|
24
|
+
- **`ParenthesisNode`**: Returns `omdParenthesisNode`.
|
|
25
|
+
- **`ConstantNode`**: Returns `omdConstantNode`.
|
|
26
|
+
- **`SymbolNode`**: Returns `omdVariableNode`.
|
|
27
|
+
- **`FunctionNode`**:
|
|
28
|
+
- If `fn.name` or `name` is `'multiply'` and `ast.implicit` is `true` (implicit multiplication), returns `omdBinaryExpressionNode`.
|
|
29
|
+
- If `fn.name` or `name` is `'sqrt'`, returns `omdSqrtNode`.
|
|
30
|
+
- Otherwise, returns `omdFunctionNode`.
|
|
31
|
+
- **Default**: Returns `omdNode`.
|
|
32
|
+
|
|
33
|
+
### `getNodeForAST(ast)`
|
|
34
|
+
|
|
35
|
+
A wrapper function that takes a complete math.js AST node and returns the corresponding OMD node class. It uses `astToOmdType` internally, handling cases where the AST might have a `mathjs` property indicating its type.
|
|
36
|
+
|
|
37
|
+
- **`ast`** (`object`): The math.js AST node.
|
|
38
|
+
- **Returns**: `class` - The appropriate `omdNode` class constructor.
|
|
39
|
+
|
|
40
|
+
### `getTextBounds(text, fontSize)`
|
|
41
|
+
|
|
42
|
+
Calculates the rendered width and height of a given text string at a specific font size. This is achieved by creating a temporary, hidden `<span>` element in the DOM, applying the text and styling, measuring its dimensions, and then removing it.
|
|
43
|
+
|
|
44
|
+
- **`text`** (`string`): The text content to measure.
|
|
45
|
+
- **`fontSize`** (`number`): The font size in pixels.
|
|
46
|
+
- **Returns**: `object` - An object with `width` and `height` properties.
|
|
47
|
+
|
|
48
|
+
### `shouldUseFractionNotation(ast)`
|
|
49
|
+
|
|
50
|
+
Determines whether a division operation (represented by an AST node) should be rendered as a fraction (stacked numerator over denominator) or as a linear division (e.g., `a / b`). This decision is based on the complexity of the numerator and denominator.
|
|
51
|
+
|
|
52
|
+
- **`ast`** (`object`): The AST node representing a division operation.
|
|
53
|
+
- **Returns**: `boolean` - `true` if the division should be rendered as a fraction, `false` otherwise.
|
|
54
|
+
|
|
55
|
+
### `isComplexExpression(ast)`
|
|
56
|
+
|
|
57
|
+
Checks if an AST node represents a "complex" expression, typically one that contains multiple operations or nested structures. This function is used by `shouldUseFractionNotation` to decide on the appropriate rendering style for fractions.
|
|
58
|
+
|
|
59
|
+
- **`ast`** (`object`): The AST node to check.
|
|
60
|
+
- **Returns**: `boolean` - `true` if the expression is considered complex, `false` otherwise.
|
|
61
|
+
|
|
62
|
+
## Example
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
import { getNodeForAST, getTextBounds } from '@teachinglab/omd'; // Assuming @teachinglab/omd exports these
|
|
66
|
+
import * as math from 'mathjs';
|
|
67
|
+
|
|
68
|
+
// Example of getting a node class for an expression
|
|
69
|
+
const astExpression = math.parse('x + 2');
|
|
70
|
+
const NodeClassExpression = getNodeForAST(astExpression);
|
|
71
|
+
const nodeExpression = new NodeClassExpression(astExpression);
|
|
72
|
+
console.log(nodeExpression.type); // e.g., "omdBinaryExpressionNode"
|
|
73
|
+
|
|
74
|
+
// Example of getting a node class for an equation
|
|
75
|
+
const astEquation = math.parse('y = 2x');
|
|
76
|
+
const NodeClassEquation = getNodeForAST(astEquation);
|
|
77
|
+
const nodeEquation = new NodeClassEquation(astEquation);
|
|
78
|
+
console.log(nodeEquation.type); // "omdEquationNode"
|
|
79
|
+
|
|
80
|
+
// Example of getting text bounds
|
|
81
|
+
const bounds = getTextBounds('Hello World', 24);
|
|
82
|
+
console.log(`Text width: ${bounds.width}, height: ${bounds.height}`);
|
|
83
|
+
```
|
|
@@ -1,148 +1,123 @@
|
|
|
1
|
-
# omdVariableNode
|
|
2
|
-
|
|
3
|
-
Represents a variable (like x
|
|
4
|
-
|
|
5
|
-
## Class
|
|
6
|
-
|
|
7
|
-
```javascript
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// From AST data
|
|
126
|
-
const node = new omdVariableNode({
|
|
127
|
-
type: 'SymbolNode',
|
|
128
|
-
name: 'x'
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Direct string name
|
|
132
|
-
const nodeY = new omdVariableNode('y');
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
#### Rendering Variables
|
|
136
|
-
|
|
137
|
-
```javascript
|
|
138
|
-
const node = omdVariableNode.fromName('x');
|
|
139
|
-
node.setFontSize(24);
|
|
140
|
-
node.computeDimensions(); // Calculate size with padding
|
|
141
|
-
node.updateLayout(); // Position the text element
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### See Also
|
|
145
|
-
|
|
146
|
-
- [omdLeafNode](./omdLeafNode.md) - Parent class
|
|
147
|
-
- [omdNode](./omdNode.md) - Base class
|
|
148
|
-
- [omdConstantNode](./omdConstantNode.md) - For numeric values
|
|
1
|
+
# omdVariableNode
|
|
2
|
+
|
|
3
|
+
Represents a variable (like `x`, `y`, `a`, `b`) in mathematical expressions. This node handles the visual representation of variables, their evaluation, and conversion to math.js AST.
|
|
4
|
+
|
|
5
|
+
## Class Definition
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
export class omdVariableNode extends omdLeafNode
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
### `new omdVariableNode(nodeData)`
|
|
14
|
+
|
|
15
|
+
Creates a new `omdVariableNode` instance.
|
|
16
|
+
|
|
17
|
+
- **`nodeData`** (`object` | `string`): The AST node data (from math.js, typically a `SymbolNode` with a `name` property) or the variable name as a string (e.g., `"x"`). The constructor extracts the variable `name` and creates the `textElement` for display.
|
|
18
|
+
|
|
19
|
+
## Static Methods
|
|
20
|
+
|
|
21
|
+
### `fromName(name)`
|
|
22
|
+
|
|
23
|
+
Creates a variable node directly from a given name string. This is a convenience method for creating simple variable nodes without needing to construct a full AST object.
|
|
24
|
+
|
|
25
|
+
- **`name`** (`string`): The variable name (e.g., `"x"`, `"theta"`).
|
|
26
|
+
- **Returns**: `omdVariableNode` - A new instance of `omdVariableNode`.
|
|
27
|
+
|
|
28
|
+
## Public Properties
|
|
29
|
+
|
|
30
|
+
- **`name`** (`string`): The name of the variable (e.g., `'x'`, `'y'`, `'theta'`).
|
|
31
|
+
- **`type`** (`string`): Always `"omdVariableNode"`.
|
|
32
|
+
- **`textElement`** (`jsvgTextLine`): The internal `jsvgTextLine` instance that displays the variable name.
|
|
33
|
+
|
|
34
|
+
## Public Methods
|
|
35
|
+
|
|
36
|
+
### `computeDimensions()`
|
|
37
|
+
|
|
38
|
+
Calculates the dimensions of the node based on its text content, adding a small amount of padding around the variable name to improve visual spacing.
|
|
39
|
+
|
|
40
|
+
- **Overrides**: `omdLeafNode.computeDimensions()`.
|
|
41
|
+
|
|
42
|
+
### `updateLayout()`
|
|
43
|
+
|
|
44
|
+
Updates the layout of the node. This method primarily calls the superclass's `updateLayout`.
|
|
45
|
+
|
|
46
|
+
- **Overrides**: `omdLeafNode.updateLayout()`.
|
|
47
|
+
|
|
48
|
+
### `toMathJSNode()`
|
|
49
|
+
|
|
50
|
+
Converts the `omdVariableNode` to a math.js-compatible AST format. It creates a `SymbolNode` with the variable's `name`, `id`, and `provenance`.
|
|
51
|
+
|
|
52
|
+
- **Returns**: `object` - A math.js-compatible AST node with `type: "SymbolNode"` and `name` set to the variable name. The returned object also includes `id`, `provenance`, and a `clone` method for compatibility.
|
|
53
|
+
|
|
54
|
+
### `highlight(color)`
|
|
55
|
+
|
|
56
|
+
Applies a highlight to the node's background and sets the variable's text color to white for better contrast. This method respects the `isExplainHighlighted` lock.
|
|
57
|
+
|
|
58
|
+
- **`color`** (`string`): The color of the highlight.
|
|
59
|
+
|
|
60
|
+
### `clearProvenanceHighlights()`
|
|
61
|
+
|
|
62
|
+
Clears any provenance-related highlights from the node and resets the variable's text color to its default (black).
|
|
63
|
+
|
|
64
|
+
### `toString()`
|
|
65
|
+
|
|
66
|
+
Converts the variable node to its string representation, which is simply its `name`.
|
|
67
|
+
|
|
68
|
+
- **Returns**: `string` - The variable name.
|
|
69
|
+
|
|
70
|
+
### `evaluate(variables)`
|
|
71
|
+
|
|
72
|
+
Evaluates the variable by looking up its value in the provided `variables` map. If the variable is not defined in the map, it throws an error.
|
|
73
|
+
|
|
74
|
+
- **`variables`** (`object`): A map of variable names to their numeric values.
|
|
75
|
+
- **Returns**: `number` - The value of the variable.
|
|
76
|
+
- **Throws**: `Error` if the variable is not defined in the map.
|
|
77
|
+
|
|
78
|
+
## Internal Methods
|
|
79
|
+
|
|
80
|
+
- **`parseName(nodeData)`**: Extracts the variable name from the constructor's `nodeData`. It handles both string input and AST objects with a `name` property.
|
|
81
|
+
- **`parseType()`**: Sets the node's type. Always returns `"variable"`.
|
|
82
|
+
|
|
83
|
+
## Examples
|
|
84
|
+
|
|
85
|
+
#### Creating Variables
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
import { omdVariableNode } from '@teachinglab/omd';
|
|
89
|
+
import * as math from 'mathjs';
|
|
90
|
+
|
|
91
|
+
// From a name string
|
|
92
|
+
const nodeX = omdVariableNode.fromName('x');
|
|
93
|
+
const nodeTheta = omdVariableNode.fromName('θ');
|
|
94
|
+
|
|
95
|
+
// From AST data (e.g., from math.js parse result)
|
|
96
|
+
const astNode = math.parse('y');
|
|
97
|
+
const nodeY = new omdVariableNode(astNode);
|
|
98
|
+
|
|
99
|
+
// Direct string name (less common, but supported)
|
|
100
|
+
const nodeZ = new omdVariableNode('z');
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Rendering Variables
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { omdVariableNode } from '@teachinglab/omd';
|
|
107
|
+
// import { jsvgContainer } from '@teachinglab/jsvg'; // if rendering directly
|
|
108
|
+
|
|
109
|
+
const node = omdVariableNode.fromName('x');
|
|
110
|
+
node.setFontSize(24);
|
|
111
|
+
node.initialize(); // Computes dimensions and layout
|
|
112
|
+
|
|
113
|
+
// To render, typically add to a parent node or an omdDisplay
|
|
114
|
+
// const svgContainer = new jsvgContainer();
|
|
115
|
+
// svgContainer.addChild(node);
|
|
116
|
+
// document.body.appendChild(svgContainer.svgObject);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## See Also
|
|
120
|
+
|
|
121
|
+
- [`omdLeafNode`](./omdLeafNode.md) - Parent class.
|
|
122
|
+
- [`omdNode`](./omdNode.md) - Base class.
|
|
123
|
+
- [`omdConstantNode`](./omdConstantNode.md) - For numeric values.
|
package/index.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* It exports all core OMD components and visualization tools from a single endpoint.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* import { omdTable, omdBalanceHanger, omdEquationNode, omdDisplay } from 'omd
|
|
8
|
+
* import { omdTable, omdBalanceHanger, omdEquationNode, omdDisplay } from '@teachinglab/omd'
|
|
9
9
|
*
|
|
10
10
|
* Or for dynamic imports:
|
|
11
|
-
* const { omdTable } = await import('omd
|
|
11
|
+
* const { omdTable } = await import('@teachinglab/omd')
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
// Export everything from the core OMD library (equations, nodes, display, etc.)
|
package/omd/core/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { omdEquationStack } from './omdEquationStack.js';
|
|
|
21
21
|
import { omdStepVisualizer } from '../step-visualizer/omdStepVisualizer.js';
|
|
22
22
|
import { omdDisplay } from '../display/omdDisplay.js';
|
|
23
23
|
import { omdToolbar } from '../display/omdToolbar.js';
|
|
24
|
+
import { omdNodeOverlay, omdNodeOverlayPresets } from '../utils/omdNodeOverlay.js';
|
|
24
25
|
|
|
25
26
|
// Utilities
|
|
26
27
|
import { getNodeForAST } from './omdUtilities.js';
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
setConfig,
|
|
31
32
|
getDefaultConfig
|
|
32
33
|
} from '../config/omdConfigManager.js';
|
|
34
|
+
import { aiNextEquationStep } from '../utils/aiNextEquationStep.js';
|
|
33
35
|
|
|
34
36
|
// Expression handling
|
|
35
37
|
import { omdExpression } from '../../src/omdExpression.js';
|
|
@@ -60,6 +62,8 @@ export {
|
|
|
60
62
|
omdStepVisualizer,
|
|
61
63
|
omdDisplay,
|
|
62
64
|
omdToolbar,
|
|
65
|
+
omdNodeOverlay,
|
|
66
|
+
omdNodeOverlayPresets,
|
|
63
67
|
|
|
64
68
|
// Utilities
|
|
65
69
|
getNodeForAST,
|
|
@@ -69,6 +73,8 @@ export {
|
|
|
69
73
|
getDefaultConfig,
|
|
70
74
|
omdExpression,
|
|
71
75
|
omdColor
|
|
76
|
+
,
|
|
77
|
+
aiNextEquationStep
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
// Helper utilities for common operations
|
|
@@ -131,6 +137,8 @@ export default {
|
|
|
131
137
|
omdStepVisualizer,
|
|
132
138
|
omdDisplay,
|
|
133
139
|
omdToolbar,
|
|
140
|
+
omdNodeOverlay,
|
|
141
|
+
omdNodeOverlayPresets,
|
|
134
142
|
|
|
135
143
|
// Utilities
|
|
136
144
|
getNodeForAST,
|
|
@@ -140,6 +148,7 @@ export default {
|
|
|
140
148
|
getDefaultConfig,
|
|
141
149
|
omdExpression,
|
|
142
150
|
omdColor,
|
|
151
|
+
aiNextEquationStep,
|
|
143
152
|
|
|
144
153
|
// Helper functions
|
|
145
154
|
helpers: omdHelpers
|
|
@@ -479,6 +479,21 @@ export class omdEquationSequenceNode extends omdNode {
|
|
|
479
479
|
this.updateLayout();
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Convenience helper: recompute dimensions, update layout, and optionally render via a renderer.
|
|
484
|
+
* Use this instead of calling computeDimensions/updateLayout everywhere.
|
|
485
|
+
* @param {object} [renderer] - Optional renderer (e.g., an omdDisplay instance) to re-render the sequence
|
|
486
|
+
*/
|
|
487
|
+
refresh(renderer, center=true) {
|
|
488
|
+
this.computeDimensions();
|
|
489
|
+
this.updateLayout();
|
|
490
|
+
renderer.render(this);
|
|
491
|
+
|
|
492
|
+
if (center) {
|
|
493
|
+
renderer.centerNode();
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
482
497
|
/**
|
|
483
498
|
* Applies a specified operation to the current equation in the sequence and adds the result as a new step.
|
|
484
499
|
* @param {number|string} value - The constant value or expression string to apply.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ask the AI for the next algebraic step for an equation.
|
|
5
|
+
* This helper can be called in Node (server) with an API key, or can proxy to an endpoint.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} equation - The equation string (e.g. "3x+2=14")
|
|
8
|
+
* @param {object} [options]
|
|
9
|
+
* @param {string} [options.apiKey] - API key for the OpenAI-compatible client (GEMINI_API_KEY)
|
|
10
|
+
* @param {string} [options.baseURL] - Optional baseURL override for the OpenAI client
|
|
11
|
+
* @param {string} [options.model] - Model name (default: "gemini-2.0-flash")
|
|
12
|
+
* @param {string} [options.endpoint] - If provided, POSTs to this endpoint with { equation } and expects the same JSON response
|
|
13
|
+
*/
|
|
14
|
+
export async function aiNextEquationStep(equation, options = {}) {
|
|
15
|
+
if (!equation) throw new Error('equation is required');
|
|
16
|
+
|
|
17
|
+
const prompt = `Given the equation ${equation}, return ONLY the next algebraic step required to solve for the variable, in one of the following strict JSON formats:
|
|
18
|
+
|
|
19
|
+
For an operation (add, subtract, multiply, divide):
|
|
20
|
+
{ "operation": "add|subtract|multiply|divide", "value": number or string }
|
|
21
|
+
|
|
22
|
+
For a function applied to both sides (e.g., sqrt, log, sin):
|
|
23
|
+
{ "function": "functionName" }
|
|
24
|
+
|
|
25
|
+
If the equation is already solved (like x=4) or no valid algebraic step can be applied:
|
|
26
|
+
{ "operation": "none" }
|
|
27
|
+
|
|
28
|
+
Important rules for solving:
|
|
29
|
+
1. When variables appear on both sides, first move all variables to one side by adding/subtracting variable terms
|
|
30
|
+
2. When constants appear on both sides, move all constants to one side by adding/subtracting constants
|
|
31
|
+
3. Focus on isolating the variable systematically
|
|
32
|
+
4. For equations like 2x-5=3x+7, subtract the smaller variable term (2x) from both sides first
|
|
33
|
+
|
|
34
|
+
Do not include any explanation, LaTeX, or extra text. Only output the JSON object.`;
|
|
35
|
+
|
|
36
|
+
// If an endpoint is provided, proxy the request there (useful for browser usage)
|
|
37
|
+
if (options.endpoint) {
|
|
38
|
+
const res = await fetch(options.endpoint, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json'
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ equation })
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const text = await res.text();
|
|
48
|
+
throw new Error(`Remote endpoint error: ${res.status} ${text}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const json = await res.json();
|
|
52
|
+
return json;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const apiKey = options.apiKey || (typeof process !== 'undefined' && process.env && process.env.GEMINI_API_KEY);
|
|
56
|
+
if (!apiKey) throw new Error('API key is required when not using a remote endpoint');
|
|
57
|
+
|
|
58
|
+
const baseURL = options.baseURL || 'https://generativelanguage.googleapis.com/v1beta/openai/';
|
|
59
|
+
const model = options.model || 'gemini-2.0-flash';
|
|
60
|
+
|
|
61
|
+
const client = new OpenAI({ apiKey, baseURL });
|
|
62
|
+
|
|
63
|
+
const messages = [
|
|
64
|
+
{
|
|
65
|
+
role: 'user',
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: 'text',
|
|
69
|
+
text: prompt
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const response = await client.chat.completions.create({
|
|
76
|
+
model,
|
|
77
|
+
messages,
|
|
78
|
+
max_tokens: 150
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Extract JSON object from model output
|
|
82
|
+
let aiText = '';
|
|
83
|
+
try {
|
|
84
|
+
aiText = (response.choices && response.choices[0] && response.choices[0].message && response.choices[0].message.content) || '';
|
|
85
|
+
if (Array.isArray(aiText)) {
|
|
86
|
+
// Some SDKs return arrays of content objects
|
|
87
|
+
aiText = aiText.map(c => c.text || c.content || '').join('');
|
|
88
|
+
}
|
|
89
|
+
if (typeof aiText === 'object' && aiText.text) aiText = aiText.text;
|
|
90
|
+
aiText = String(aiText).trim();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
throw new Error('Unexpected AI response format');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const jsonMatch = aiText.match(/\{[\s\S]*\}/);
|
|
96
|
+
if (!jsonMatch) throw new Error('AI did not return a JSON object');
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
100
|
+
return parsed;
|
|
101
|
+
} catch (e) {
|
|
102
|
+
throw new Error('Failed to parse AI JSON response: ' + e.message + ' -- raw: ' + aiText);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default aiNextEquationStep;
|
package/package.json
CHANGED
package/src/omdBalanceHanger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { omdColor } from "./omdColor.js";
|
|
3
|
-
import { jsvgGroup, jsvgLine, jsvgEllipse, jsvgRect } from "@teachinglab/jsvg";
|
|
3
|
+
import { jsvgGroup, jsvgLine, jsvgEllipse, jsvgRect, jsvgTextBox } from "@teachinglab/jsvg";
|
|
4
4
|
|
|
5
5
|
export class omdBalanceHanger extends jsvgGroup
|
|
6
6
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { omdColor } from "./omdColor.js";
|
|
2
|
-
import { jsvgGroup, jsvgRect, jsvgClipMask, jsvgLine, jsvgEllipse, jsvgTextBox, jsvgPath } from "@teachinglab/jsvg";
|
|
2
|
+
import { jsvgGroup, jsvgRect, jsvgClipMask, jsvgLine, jsvgEllipse, jsvgTextBox, jsvgTextLine, jsvgPath } from "@teachinglab/jsvg";
|
|
3
3
|
import {
|
|
4
4
|
omdRightTriangle,
|
|
5
5
|
omdIsoscelesTriangle,
|