@teachinglab/omd 0.6.0 → 0.6.2

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.
Files changed (198) hide show
  1. package/README.md +257 -251
  2. package/README.old.md +137 -137
  3. package/canvas/core/canvasConfig.js +202 -202
  4. package/canvas/drawing/segment.js +167 -167
  5. package/canvas/drawing/stroke.js +385 -385
  6. package/canvas/events/eventManager.js +444 -444
  7. package/canvas/events/pointerEventHandler.js +262 -262
  8. package/canvas/index.js +48 -48
  9. package/canvas/tools/PointerTool.js +71 -71
  10. package/canvas/tools/tool.js +222 -222
  11. package/canvas/utils/boundingBox.js +377 -377
  12. package/canvas/utils/mathUtils.js +258 -258
  13. package/docs/api/configuration-options.md +198 -198
  14. package/docs/api/eventManager.md +82 -82
  15. package/docs/api/focusFrameManager.md +144 -144
  16. package/docs/api/index.md +105 -105
  17. package/docs/api/main.md +62 -62
  18. package/docs/api/omdBinaryExpressionNode.md +86 -86
  19. package/docs/api/omdCanvas.md +83 -83
  20. package/docs/api/omdConfigManager.md +112 -112
  21. package/docs/api/omdConstantNode.md +52 -52
  22. package/docs/api/omdDisplay.md +87 -87
  23. package/docs/api/omdEquationNode.md +174 -174
  24. package/docs/api/omdEquationSequenceNode.md +258 -258
  25. package/docs/api/omdEquationStack.md +192 -192
  26. package/docs/api/omdFunctionNode.md +82 -82
  27. package/docs/api/omdGroupNode.md +78 -78
  28. package/docs/api/omdHelpers.md +87 -87
  29. package/docs/api/omdLeafNode.md +85 -85
  30. package/docs/api/omdNode.md +201 -201
  31. package/docs/api/omdOperationDisplayNode.md +117 -117
  32. package/docs/api/omdOperatorNode.md +91 -91
  33. package/docs/api/omdParenthesisNode.md +133 -133
  34. package/docs/api/omdPopup.md +191 -191
  35. package/docs/api/omdPowerNode.md +131 -131
  36. package/docs/api/omdRationalNode.md +144 -144
  37. package/docs/api/omdSequenceNode.md +128 -128
  38. package/docs/api/omdSimplification.md +78 -78
  39. package/docs/api/omdSqrtNode.md +144 -144
  40. package/docs/api/omdStepVisualizer.md +146 -146
  41. package/docs/api/omdStepVisualizerHighlighting.md +65 -65
  42. package/docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
  43. package/docs/api/omdStepVisualizerLayout.md +70 -70
  44. package/docs/api/omdStepVisualizerNodeUtils.md +140 -140
  45. package/docs/api/omdStepVisualizerTextBoxes.md +76 -76
  46. package/docs/api/omdToolbar.md +130 -130
  47. package/docs/api/omdTranscriptionService.md +95 -95
  48. package/docs/api/omdTreeDiff.md +169 -169
  49. package/docs/api/omdUnaryExpressionNode.md +137 -137
  50. package/docs/api/omdUtilities.md +82 -82
  51. package/docs/api/omdVariableNode.md +123 -123
  52. package/docs/api/selectTool.md +74 -74
  53. package/docs/api/simplificationEngine.md +97 -97
  54. package/docs/api/simplificationRules.md +76 -76
  55. package/docs/api/simplificationUtils.md +64 -64
  56. package/docs/api/transcribe.md +43 -43
  57. package/docs/api-reference.md +85 -85
  58. package/docs/index.html +453 -453
  59. package/docs/index.md +38 -38
  60. package/docs/omd-objects.md +258 -258
  61. package/index.js +79 -79
  62. package/jsvg/index.js +3 -0
  63. package/jsvg/jsvg.js +898 -898
  64. package/jsvg/jsvgComponents.js +357 -358
  65. package/npm-docs/DOCUMENTATION_SUMMARY.md +220 -220
  66. package/npm-docs/README.md +251 -251
  67. package/npm-docs/api/api-reference.md +85 -85
  68. package/npm-docs/api/configuration-options.md +198 -198
  69. package/npm-docs/api/eventManager.md +82 -82
  70. package/npm-docs/api/expression-nodes.md +561 -561
  71. package/npm-docs/api/focusFrameManager.md +144 -144
  72. package/npm-docs/api/index.md +105 -105
  73. package/npm-docs/api/main.md +62 -62
  74. package/npm-docs/api/omdBinaryExpressionNode.md +86 -86
  75. package/npm-docs/api/omdCanvas.md +83 -83
  76. package/npm-docs/api/omdConfigManager.md +112 -112
  77. package/npm-docs/api/omdConstantNode.md +52 -52
  78. package/npm-docs/api/omdDisplay.md +87 -87
  79. package/npm-docs/api/omdEquationNode.md +174 -174
  80. package/npm-docs/api/omdEquationSequenceNode.md +258 -258
  81. package/npm-docs/api/omdEquationStack.md +192 -192
  82. package/npm-docs/api/omdFunctionNode.md +82 -82
  83. package/npm-docs/api/omdGroupNode.md +78 -78
  84. package/npm-docs/api/omdHelpers.md +87 -87
  85. package/npm-docs/api/omdLeafNode.md +85 -85
  86. package/npm-docs/api/omdNode.md +201 -201
  87. package/npm-docs/api/omdOperationDisplayNode.md +117 -117
  88. package/npm-docs/api/omdOperatorNode.md +91 -91
  89. package/npm-docs/api/omdParenthesisNode.md +133 -133
  90. package/npm-docs/api/omdPopup.md +191 -191
  91. package/npm-docs/api/omdPowerNode.md +131 -131
  92. package/npm-docs/api/omdRationalNode.md +144 -144
  93. package/npm-docs/api/omdSequenceNode.md +128 -128
  94. package/npm-docs/api/omdSimplification.md +78 -78
  95. package/npm-docs/api/omdSqrtNode.md +144 -144
  96. package/npm-docs/api/omdStepVisualizer.md +146 -146
  97. package/npm-docs/api/omdStepVisualizerHighlighting.md +65 -65
  98. package/npm-docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
  99. package/npm-docs/api/omdStepVisualizerLayout.md +70 -70
  100. package/npm-docs/api/omdStepVisualizerNodeUtils.md +140 -140
  101. package/npm-docs/api/omdStepVisualizerTextBoxes.md +76 -76
  102. package/npm-docs/api/omdToolbar.md +130 -130
  103. package/npm-docs/api/omdTranscriptionService.md +95 -95
  104. package/npm-docs/api/omdTreeDiff.md +169 -169
  105. package/npm-docs/api/omdUnaryExpressionNode.md +137 -137
  106. package/npm-docs/api/omdUtilities.md +82 -82
  107. package/npm-docs/api/omdVariableNode.md +123 -123
  108. package/npm-docs/api/selectTool.md +74 -74
  109. package/npm-docs/api/simplificationEngine.md +97 -97
  110. package/npm-docs/api/simplificationRules.md +76 -76
  111. package/npm-docs/api/simplificationUtils.md +64 -64
  112. package/npm-docs/api/transcribe.md +43 -43
  113. package/npm-docs/guides/equations.md +854 -854
  114. package/npm-docs/guides/factory-functions.md +354 -354
  115. package/npm-docs/guides/getting-started.md +318 -318
  116. package/npm-docs/guides/quick-examples.md +525 -525
  117. package/npm-docs/guides/visualizations.md +682 -682
  118. package/npm-docs/index.html +12 -0
  119. package/npm-docs/json-schemas.md +826 -826
  120. package/omd/config/omdConfigManager.js +279 -267
  121. package/omd/core/index.js +158 -158
  122. package/omd/core/omdEquationStack.js +546 -546
  123. package/omd/core/omdUtilities.js +113 -113
  124. package/omd/display/omdDisplay.js +969 -962
  125. package/omd/display/omdToolbar.js +501 -501
  126. package/omd/nodes/omdBinaryExpressionNode.js +459 -459
  127. package/omd/nodes/omdConstantNode.js +141 -141
  128. package/omd/nodes/omdEquationNode.js +1327 -1327
  129. package/omd/nodes/omdFunctionNode.js +351 -351
  130. package/omd/nodes/omdGroupNode.js +67 -67
  131. package/omd/nodes/omdLeafNode.js +76 -76
  132. package/omd/nodes/omdNode.js +556 -556
  133. package/omd/nodes/omdOperationDisplayNode.js +321 -321
  134. package/omd/nodes/omdOperatorNode.js +108 -108
  135. package/omd/nodes/omdParenthesisNode.js +292 -292
  136. package/omd/nodes/omdPowerNode.js +235 -235
  137. package/omd/nodes/omdRationalNode.js +295 -295
  138. package/omd/nodes/omdSqrtNode.js +307 -307
  139. package/omd/nodes/omdUnaryExpressionNode.js +227 -227
  140. package/omd/nodes/omdVariableNode.js +122 -122
  141. package/omd/simplification/omdSimplification.js +140 -140
  142. package/omd/simplification/omdSimplificationEngine.js +887 -887
  143. package/omd/simplification/package.json +5 -5
  144. package/omd/simplification/rules/binaryRules.js +1037 -1037
  145. package/omd/simplification/rules/functionRules.js +111 -111
  146. package/omd/simplification/rules/index.js +48 -48
  147. package/omd/simplification/rules/parenthesisRules.js +19 -19
  148. package/omd/simplification/rules/powerRules.js +143 -143
  149. package/omd/simplification/rules/rationalRules.js +725 -725
  150. package/omd/simplification/rules/sqrtRules.js +48 -48
  151. package/omd/simplification/rules/unaryRules.js +37 -37
  152. package/omd/simplification/simplificationRules.js +31 -31
  153. package/omd/simplification/simplificationUtils.js +1055 -1055
  154. package/omd/step-visualizer/omdStepVisualizer.js +947 -947
  155. package/omd/step-visualizer/omdStepVisualizerHighlighting.js +246 -246
  156. package/omd/step-visualizer/omdStepVisualizerLayout.js +892 -892
  157. package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +200 -200
  158. package/omd/utils/aiNextEquationStep.js +106 -106
  159. package/omd/utils/omdNodeOverlay.js +638 -638
  160. package/omd/utils/omdPopup.js +1203 -1203
  161. package/omd/utils/omdStepVisualizerInteractiveSteps.js +684 -684
  162. package/omd/utils/omdStepVisualizerNodeUtils.js +267 -267
  163. package/omd/utils/omdTranscriptionService.js +123 -123
  164. package/omd/utils/omdTreeDiff.js +733 -733
  165. package/package.json +59 -56
  166. package/readme.html +184 -120
  167. package/src/index.js +74 -74
  168. package/src/json-schemas.md +576 -576
  169. package/src/omd-json-samples.js +147 -147
  170. package/src/omdApp.js +391 -391
  171. package/src/omdAppCanvas.js +335 -335
  172. package/src/omdBalanceHanger.js +199 -199
  173. package/src/omdColor.js +13 -13
  174. package/src/omdCoordinatePlane.js +541 -541
  175. package/src/omdExpression.js +115 -115
  176. package/src/omdFactory.js +150 -150
  177. package/src/omdFunction.js +114 -114
  178. package/src/omdMetaExpression.js +290 -290
  179. package/src/omdNaturalExpression.js +563 -563
  180. package/src/omdNode.js +383 -383
  181. package/src/omdNumber.js +52 -52
  182. package/src/omdNumberLine.js +114 -112
  183. package/src/omdNumberTile.js +118 -118
  184. package/src/omdOperator.js +72 -72
  185. package/src/omdPowerExpression.js +91 -91
  186. package/src/omdProblem.js +259 -259
  187. package/src/omdRatioChart.js +251 -251
  188. package/src/omdRationalExpression.js +114 -114
  189. package/src/omdSampleData.js +215 -215
  190. package/src/omdShapes.js +512 -512
  191. package/src/omdSpinner.js +151 -151
  192. package/src/omdString.js +49 -49
  193. package/src/omdTable.js +498 -498
  194. package/src/omdTapeDiagram.js +244 -244
  195. package/src/omdTerm.js +91 -91
  196. package/src/omdTileEquation.js +349 -349
  197. package/src/omdUtils.js +84 -84
  198. package/src/omdVariable.js +51 -51
@@ -1,267 +1,279 @@
1
- /**
2
- * OMD Configuration Manager
3
- * Dynamically loads and manages configuration from JSON file
4
- */
5
-
6
- // Configuration data - will be loaded from JSON or provided by user
7
- let configData = null;
8
- let configLoadPromise = null;
9
-
10
- // Default configuration (fallback when no config file is available)
11
- const defaultConfig = {
12
- multiplication: {
13
- symbol: "·",
14
- forceImplicit: false,
15
- implicitCombinations: {
16
- constantVariable: true,
17
- variableConstant: false,
18
- parenthesisAfterVariable: true,
19
- parenthesisAfterConstant: true,
20
- variableParenthesis: true,
21
- parenthesisParenthesis: true,
22
- parenthesisVariable: true,
23
- parenthesisConstant: true,
24
- variableVariable: true
25
- }
26
- },
27
- stepVisualizer: {
28
- dotSizes: {
29
- level0: 8,
30
- level1: 8,
31
- level2: 8
32
- },
33
- fontWeights: {
34
- level0: 400,
35
- level1: 400,
36
- level2: 400
37
- }
38
- }
39
- };
40
-
41
- /**
42
- * Loads configuration from JSON file or uses default
43
- * @param {string|Object} configSource - Optional path to config file or config object
44
- * @returns {Promise<Object>} Promise that resolves to the configuration object
45
- */
46
- async function loadConfig(configSource = null) {
47
- if (configLoadPromise && !configSource) {
48
- return configLoadPromise;
49
- }
50
-
51
- // If a config object is provided directly, use it
52
- if (configSource && typeof configSource === 'object') {
53
- configData = { ...defaultConfig, ...configSource };
54
- return configData;
55
- }
56
-
57
- configLoadPromise = (async () => {
58
- try {
59
- // Detect environment and use appropriate loading method
60
- if (typeof window !== 'undefined') {
61
- // Browser environment - use fetch
62
- const configPath = configSource || './omd/config/omdConfig.json';
63
- const response = await fetch(configPath);
64
- if (!response.ok) {
65
- console.warn(`Config file not found at ${configPath}, using default configuration`);
66
- configData = defaultConfig;
67
- return configData;
68
- }
69
-
70
- const configText = await response.text();
71
- const loadedConfig = JSON.parse(configText);
72
- configData = { ...defaultConfig, ...loadedConfig };
73
- return configData;
74
-
75
- } else {
76
- // Node.js environment - use fs
77
- const fs = await import('fs');
78
- const path = await import('path');
79
- const { fileURLToPath } = await import('url');
80
-
81
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
82
- const configPath = configSource || path.join(__dirname, 'omdConfig.json');
83
-
84
- try {
85
- const fileContent = await fs.promises.readFile(configPath, 'utf-8');
86
- const loadedConfig = JSON.parse(fileContent);
87
- configData = { ...defaultConfig, ...loadedConfig };
88
- } catch (err) {
89
- console.warn(`Config file not found at ${configPath}, using default configuration`);
90
- configData = defaultConfig;
91
- }
92
- return configData;
93
- }
94
- } catch (error) {
95
- console.warn('Error loading config, using default configuration:', error);
96
- configData = defaultConfig;
97
- return configData;
98
- }
99
- })();
100
-
101
- return configLoadPromise;
102
- }
103
-
104
- /**
105
- * Gets the configuration object, loading it if necessary
106
- * @returns {Promise<Object>} Promise that resolves to the configuration object
107
- */
108
- async function getConfig() {
109
- if (!configData) {
110
- await loadConfig();
111
- }
112
- return configData;
113
- }
114
-
115
- /**
116
- * Gets the configuration object synchronously (for backwards compatibility)
117
- * If config hasn't been loaded yet, returns default config
118
- * @returns {Object} The configuration object
119
- */
120
- function getConfigSync() {
121
- if (!configData) {
122
- // Auto-initialize with defaults if not loaded
123
- configData = defaultConfig;
124
- }
125
- return configData;
126
- }
127
-
128
- /**
129
- * Initialize configuration loading (call this early in app lifecycle)
130
- * @param {string|Object} configSource - Optional path to config file or config object
131
- * @returns {Promise<void>}
132
- */
133
- export async function initializeConfig(configSource = null) {
134
- await loadConfig(configSource);
135
- }
136
-
137
- /**
138
- * Set configuration directly without loading from file
139
- * @param {Object} config - Configuration object
140
- */
141
- export function setConfig(config) {
142
- configData = { ...defaultConfig, ...config };
143
- }
144
-
145
- /**
146
- * Get the default configuration
147
- * @returns {Object} The default configuration object
148
- */
149
- export function getDefaultConfig() {
150
- return { ...defaultConfig };
151
- }
152
-
153
- /**
154
- * Utility function to check if a specific feature is enabled
155
- * @param {string} category - The configuration category (e.g., 'multiplication', 'simplification')
156
- * @param {string} setting - The specific setting to check
157
- * @returns {boolean} Whether the setting is enabled
158
- */
159
- export function isEnabled(category, setting) {
160
- const config = getConfigSync();
161
- return config[category]?.[setting] ?? false;
162
- }
163
-
164
- /**
165
- * Check if implicit multiplication should be used for a specific combination
166
- * @param {string} combination - The type of combination (e.g., 'constantVariable')
167
- * @returns {boolean} Whether implicit multiplication should be used
168
- */
169
- export function useImplicitMultiplication(combination = null) {
170
- const config = getConfigSync();
171
- if (combination) {
172
- return config.multiplication.implicitCombinations[combination] ?? false;
173
- }
174
- return config.multiplication.forceImplicit;
175
- }
176
-
177
- /**
178
- * Get the configured multiplication symbol
179
- * @returns {string} The multiplication symbol to use for display
180
- */
181
- export function getMultiplicationSymbol() {
182
- const config = getConfigSync();
183
- return config.multiplication.symbol;
184
- }
185
-
186
- /**
187
- * Update configuration settings at runtime
188
- * @param {string} category - The configuration category
189
- * @param {string} setting - The setting to update
190
- * @param {any} value - The new value
191
- */
192
- export function updateConfig(category, setting, value) {
193
- const config = getConfigSync();
194
- if (config[category] && config[category].hasOwnProperty(setting)) {
195
- config[category][setting] = value;
196
- }
197
- }
198
-
199
- /**
200
- * Get a configuration value by path
201
- * @param {string} path - Dot-separated path to the config value (e.g., 'multiplication.symbol')
202
- * @returns {any} The configuration value
203
- */
204
- export function getConfigValue(path) {
205
- const config = getConfigSync();
206
- return path.split('.').reduce((obj, key) => obj?.[key], config);
207
- }
208
-
209
- /**
210
- * Set a configuration value by path
211
- * @param {string} path - Dot-separated path to the config value
212
- * @param {any} value - The new value
213
- */
214
- export function setConfigValue(path, value) {
215
- const config = getConfigSync();
216
- const keys = path.split('.');
217
- const lastKey = keys.pop();
218
- const target = keys.reduce((obj, key) => obj[key] = obj[key] || {}, config);
219
- target[lastKey] = value;
220
- }
221
-
222
- /**
223
- * Reset configuration to defaults
224
- */
225
- export function resetConfig() {
226
- throw new Error('resetConfig not available - edit omdConfig.json file directly');
227
- }
228
-
229
- /**
230
- * Reload configuration from JSON file
231
- * @returns {Promise<Object>} Promise that resolves to the reloaded configuration
232
- */
233
- export async function reloadConfig() {
234
- configData = null;
235
- configLoadPromise = null;
236
- return await loadConfig();
237
- }
238
-
239
- /**
240
- * Get the raw configuration object (async)
241
- * @returns {Promise<Object>} Promise that resolves to the configuration object
242
- */
243
- export async function getConfigAsync() {
244
- return await getConfig();
245
- }
246
-
247
- /**
248
- * Get dot radius for a given step level (0,1,2)
249
- * @param {number} level
250
- * @returns {number}
251
- */
252
- export function getDotRadius(level=0){
253
- const cfg=getConfigSync();
254
- const key=`level${level}`;
255
- return cfg.stepVisualizer?.dotSizes?.[key]??6;
256
- }
257
-
258
- /**
259
- * Get font weight for a given step level (0,1,2)
260
- * @param {number} level
261
- * @returns {number}
262
- */
263
- export function getFontWeight(level=0){
264
- const cfg=getConfigSync();
265
- const key=`level${level}`;
266
- return cfg.stepVisualizer?.fontWeights?.[key]??400;
267
- }
1
+ /**
2
+ * OMD Configuration Manager
3
+ * Dynamically loads and manages configuration from JSON file
4
+ */
5
+
6
+ // Configuration data - will be loaded from JSON or provided by user
7
+ let configData = null;
8
+ let configLoadPromise = null;
9
+
10
+ // Default configuration (fallback when no config file is available)
11
+ const defaultConfig = {
12
+ multiplication: {
13
+ symbol: "·",
14
+ forceImplicit: false,
15
+ implicitCombinations: {
16
+ constantVariable: true,
17
+ variableConstant: false,
18
+ parenthesisAfterVariable: true,
19
+ parenthesisAfterConstant: true,
20
+ variableParenthesis: true,
21
+ parenthesisParenthesis: true,
22
+ parenthesisVariable: true,
23
+ parenthesisConstant: true,
24
+ variableVariable: true
25
+ }
26
+ },
27
+ stepVisualizer: {
28
+ dotSizes: {
29
+ level0: 8,
30
+ level1: 8,
31
+ level2: 8
32
+ },
33
+ fontWeights: {
34
+ level0: 400,
35
+ level1: 400,
36
+ level2: 400
37
+ }
38
+ }
39
+ };
40
+
41
+ /**
42
+ * Loads configuration from JSON file or uses default
43
+ * @param {string|Object} configSource - Optional path to config file or config object
44
+ * @returns {Promise<Object>} Promise that resolves to the configuration object
45
+ */
46
+ async function loadConfig(configSource = null) {
47
+ if (configLoadPromise && !configSource) {
48
+ return configLoadPromise;
49
+ }
50
+
51
+ // If a config object is provided directly, use it
52
+ if (configSource && typeof configSource === 'object') {
53
+ configData = { ...defaultConfig, ...configSource };
54
+ return configData;
55
+ }
56
+
57
+ configLoadPromise = (async () => {
58
+ try {
59
+ // Detect environment and use appropriate loading method
60
+ if (typeof window !== 'undefined') {
61
+ // Browser environment - use fetch
62
+ const configPath = configSource || './omd/config/omdConfig.json';
63
+ const response = await fetch(configPath);
64
+ if (!response.ok) {
65
+ console.warn(`Config file not found at ${configPath}, using default configuration`);
66
+ configData = defaultConfig;
67
+ return configData;
68
+ }
69
+
70
+ const configText = await response.text();
71
+ const loadedConfig = JSON.parse(configText);
72
+ configData = { ...defaultConfig, ...loadedConfig };
73
+ return configData;
74
+
75
+ } else {
76
+ // Server/Node.js environment - use filesystem access when available
77
+ const isNodeEnvironment = typeof process !== 'undefined' && !!(process.versions && process.versions.node);
78
+ if (!isNodeEnvironment) {
79
+ console.warn('Config loading skipped: filesystem APIs unavailable in this environment. Using default configuration.');
80
+ configData = defaultConfig;
81
+ return configData;
82
+ }
83
+
84
+ try {
85
+ const fs = await import('node:fs/promises');
86
+ const path = await import('node:path');
87
+ const { fileURLToPath } = await import('node:url');
88
+
89
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
90
+ const configPath = configSource || path.join(__dirname, 'omdConfig.json');
91
+
92
+ try {
93
+ const fileContent = await fs.readFile(configPath, 'utf-8');
94
+ const loadedConfig = JSON.parse(fileContent);
95
+ configData = { ...defaultConfig, ...loadedConfig };
96
+ } catch (err) {
97
+ console.warn(`Config file not found at ${configPath}, using default configuration`);
98
+ configData = defaultConfig;
99
+ }
100
+ } catch (nodeImportError) {
101
+ console.warn('Config loading skipped: unable to access Node filesystem APIs. Using default configuration.', nodeImportError);
102
+ configData = defaultConfig;
103
+ }
104
+ return configData;
105
+ }
106
+ } catch (error) {
107
+ console.warn('Error loading config, using default configuration:', error);
108
+ configData = defaultConfig;
109
+ return configData;
110
+ }
111
+ })();
112
+
113
+ return configLoadPromise;
114
+ }
115
+
116
+ /**
117
+ * Gets the configuration object, loading it if necessary
118
+ * @returns {Promise<Object>} Promise that resolves to the configuration object
119
+ */
120
+ async function getConfig() {
121
+ if (!configData) {
122
+ await loadConfig();
123
+ }
124
+ return configData;
125
+ }
126
+
127
+ /**
128
+ * Gets the configuration object synchronously (for backwards compatibility)
129
+ * If config hasn't been loaded yet, returns default config
130
+ * @returns {Object} The configuration object
131
+ */
132
+ function getConfigSync() {
133
+ if (!configData) {
134
+ // Auto-initialize with defaults if not loaded
135
+ configData = defaultConfig;
136
+ }
137
+ return configData;
138
+ }
139
+
140
+ /**
141
+ * Initialize configuration loading (call this early in app lifecycle)
142
+ * @param {string|Object} configSource - Optional path to config file or config object
143
+ * @returns {Promise<void>}
144
+ */
145
+ export async function initializeConfig(configSource = null) {
146
+ await loadConfig(configSource);
147
+ }
148
+
149
+ /**
150
+ * Set configuration directly without loading from file
151
+ * @param {Object} config - Configuration object
152
+ */
153
+ export function setConfig(config) {
154
+ configData = { ...defaultConfig, ...config };
155
+ }
156
+
157
+ /**
158
+ * Get the default configuration
159
+ * @returns {Object} The default configuration object
160
+ */
161
+ export function getDefaultConfig() {
162
+ return { ...defaultConfig };
163
+ }
164
+
165
+ /**
166
+ * Utility function to check if a specific feature is enabled
167
+ * @param {string} category - The configuration category (e.g., 'multiplication', 'simplification')
168
+ * @param {string} setting - The specific setting to check
169
+ * @returns {boolean} Whether the setting is enabled
170
+ */
171
+ export function isEnabled(category, setting) {
172
+ const config = getConfigSync();
173
+ return config[category]?.[setting] ?? false;
174
+ }
175
+
176
+ /**
177
+ * Check if implicit multiplication should be used for a specific combination
178
+ * @param {string} combination - The type of combination (e.g., 'constantVariable')
179
+ * @returns {boolean} Whether implicit multiplication should be used
180
+ */
181
+ export function useImplicitMultiplication(combination = null) {
182
+ const config = getConfigSync();
183
+ if (combination) {
184
+ return config.multiplication.implicitCombinations[combination] ?? false;
185
+ }
186
+ return config.multiplication.forceImplicit;
187
+ }
188
+
189
+ /**
190
+ * Get the configured multiplication symbol
191
+ * @returns {string} The multiplication symbol to use for display
192
+ */
193
+ export function getMultiplicationSymbol() {
194
+ const config = getConfigSync();
195
+ return config.multiplication.symbol;
196
+ }
197
+
198
+ /**
199
+ * Update configuration settings at runtime
200
+ * @param {string} category - The configuration category
201
+ * @param {string} setting - The setting to update
202
+ * @param {any} value - The new value
203
+ */
204
+ export function updateConfig(category, setting, value) {
205
+ const config = getConfigSync();
206
+ if (config[category] && config[category].hasOwnProperty(setting)) {
207
+ config[category][setting] = value;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Get a configuration value by path
213
+ * @param {string} path - Dot-separated path to the config value (e.g., 'multiplication.symbol')
214
+ * @returns {any} The configuration value
215
+ */
216
+ export function getConfigValue(path) {
217
+ const config = getConfigSync();
218
+ return path.split('.').reduce((obj, key) => obj?.[key], config);
219
+ }
220
+
221
+ /**
222
+ * Set a configuration value by path
223
+ * @param {string} path - Dot-separated path to the config value
224
+ * @param {any} value - The new value
225
+ */
226
+ export function setConfigValue(path, value) {
227
+ const config = getConfigSync();
228
+ const keys = path.split('.');
229
+ const lastKey = keys.pop();
230
+ const target = keys.reduce((obj, key) => obj[key] = obj[key] || {}, config);
231
+ target[lastKey] = value;
232
+ }
233
+
234
+ /**
235
+ * Reset configuration to defaults
236
+ */
237
+ export function resetConfig() {
238
+ throw new Error('resetConfig not available - edit omdConfig.json file directly');
239
+ }
240
+
241
+ /**
242
+ * Reload configuration from JSON file
243
+ * @returns {Promise<Object>} Promise that resolves to the reloaded configuration
244
+ */
245
+ export async function reloadConfig() {
246
+ configData = null;
247
+ configLoadPromise = null;
248
+ return await loadConfig();
249
+ }
250
+
251
+ /**
252
+ * Get the raw configuration object (async)
253
+ * @returns {Promise<Object>} Promise that resolves to the configuration object
254
+ */
255
+ export async function getConfigAsync() {
256
+ return await getConfig();
257
+ }
258
+
259
+ /**
260
+ * Get dot radius for a given step level (0,1,2)
261
+ * @param {number} level
262
+ * @returns {number}
263
+ */
264
+ export function getDotRadius(level=0){
265
+ const cfg=getConfigSync();
266
+ const key=`level${level}`;
267
+ return cfg.stepVisualizer?.dotSizes?.[key]??6;
268
+ }
269
+
270
+ /**
271
+ * Get font weight for a given step level (0,1,2)
272
+ * @param {number} level
273
+ * @returns {number}
274
+ */
275
+ export function getFontWeight(level=0){
276
+ const cfg=getConfigSync();
277
+ const key=`level${level}`;
278
+ return cfg.stepVisualizer?.fontWeights?.[key]??400;
279
+ }