@lydt/geogebra-studio 0.0.0-stage → 0.4.0-rc.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 (189) hide show
  1. package/.agents/plugins/marketplace.json +21 -0
  2. package/.codex-plugin/plugin.json +21 -0
  3. package/.mcp.json +18 -0
  4. package/LICENSE +339 -0
  5. package/LICENSE-MIT +21 -0
  6. package/README.md +108 -2
  7. package/README.zh-CN.md +89 -0
  8. package/THIRD_PARTY_NOTICES.md +35 -0
  9. package/dist/cli.d.ts +27 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +234 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/export/files.d.ts +4 -0
  14. package/dist/export/files.d.ts.map +1 -0
  15. package/dist/export/files.js +44 -0
  16. package/dist/export/files.js.map +1 -0
  17. package/dist/export/gikz.d.ts +11 -0
  18. package/dist/export/gikz.d.ts.map +1 -0
  19. package/dist/export/gikz.js +28 -0
  20. package/dist/export/gikz.js.map +1 -0
  21. package/dist/export/html.d.ts +14 -0
  22. package/dist/export/html.d.ts.map +1 -0
  23. package/dist/export/html.js +97 -0
  24. package/dist/export/html.js.map +1 -0
  25. package/dist/export/semantic.d.ts +43 -0
  26. package/dist/export/semantic.d.ts.map +1 -0
  27. package/dist/export/semantic.js +212 -0
  28. package/dist/export/semantic.js.map +1 -0
  29. package/dist/export/viewer-themes.d.ts +76 -0
  30. package/dist/export/viewer-themes.d.ts.map +1 -0
  31. package/dist/export/viewer-themes.js +169 -0
  32. package/dist/export/viewer-themes.js.map +1 -0
  33. package/dist/export/workspace.d.ts +4 -0
  34. package/dist/export/workspace.d.ts.map +1 -0
  35. package/dist/export/workspace.js +25 -0
  36. package/dist/export/workspace.js.map +1 -0
  37. package/dist/gikz-browser.js +770 -0
  38. package/dist/index.d.ts +5 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +84 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/server.d.ts +35 -0
  43. package/dist/server.d.ts.map +1 -0
  44. package/dist/server.js +187 -0
  45. package/dist/server.js.map +1 -0
  46. package/dist/studio/artifact.d.ts +37 -0
  47. package/dist/studio/artifact.d.ts.map +1 -0
  48. package/dist/studio/artifact.js +61 -0
  49. package/dist/studio/artifact.js.map +1 -0
  50. package/dist/studio/browser.d.ts +7 -0
  51. package/dist/studio/browser.d.ts.map +1 -0
  52. package/dist/studio/browser.js +67 -0
  53. package/dist/studio/browser.js.map +1 -0
  54. package/dist/studio/install.d.ts +4 -0
  55. package/dist/studio/install.d.ts.map +1 -0
  56. package/dist/studio/install.js +60 -0
  57. package/dist/studio/install.js.map +1 -0
  58. package/dist/tools/apps-api-tools.d.ts +4 -0
  59. package/dist/tools/apps-api-tools.d.ts.map +1 -0
  60. package/dist/tools/apps-api-tools.js +96 -0
  61. package/dist/tools/apps-api-tools.js.map +1 -0
  62. package/dist/tools/educational-templates.d.ts +33 -0
  63. package/dist/tools/educational-templates.d.ts.map +1 -0
  64. package/dist/tools/educational-templates.js +261 -0
  65. package/dist/tools/educational-templates.js.map +1 -0
  66. package/dist/tools/export-tools.d.ts +3 -0
  67. package/dist/tools/export-tools.d.ts.map +1 -0
  68. package/dist/tools/export-tools.js +118 -0
  69. package/dist/tools/export-tools.js.map +1 -0
  70. package/dist/tools/geogebra-tools.d.ts +15 -0
  71. package/dist/tools/geogebra-tools.d.ts.map +1 -0
  72. package/dist/tools/geogebra-tools.js +3442 -0
  73. package/dist/tools/geogebra-tools.js.map +1 -0
  74. package/dist/tools/index.d.ts +43 -0
  75. package/dist/tools/index.d.ts.map +1 -0
  76. package/dist/tools/index.js +183 -0
  77. package/dist/tools/index.js.map +1 -0
  78. package/dist/tools/performance-tools.d.ts +7 -0
  79. package/dist/tools/performance-tools.d.ts.map +1 -0
  80. package/dist/tools/performance-tools.js +312 -0
  81. package/dist/tools/performance-tools.js.map +1 -0
  82. package/dist/tools/templates/algebra-templates.d.ts +7 -0
  83. package/dist/tools/templates/algebra-templates.d.ts.map +1 -0
  84. package/dist/tools/templates/algebra-templates.js +568 -0
  85. package/dist/tools/templates/algebra-templates.js.map +1 -0
  86. package/dist/tools/templates/geometry-templates.d.ts +7 -0
  87. package/dist/tools/templates/geometry-templates.d.ts.map +1 -0
  88. package/dist/tools/templates/geometry-templates.js +492 -0
  89. package/dist/tools/templates/geometry-templates.js.map +1 -0
  90. package/dist/types/geogebra.d.ts +103 -0
  91. package/dist/types/geogebra.d.ts.map +1 -0
  92. package/dist/types/geogebra.js +34 -0
  93. package/dist/types/geogebra.js.map +1 -0
  94. package/dist/types/mcp.d.ts +125 -0
  95. package/dist/types/mcp.d.ts.map +1 -0
  96. package/dist/types/mcp.js +19 -0
  97. package/dist/types/mcp.js.map +1 -0
  98. package/dist/utils/app-info.d.ts +12 -0
  99. package/dist/utils/app-info.d.ts.map +1 -0
  100. package/dist/utils/app-info.js +30 -0
  101. package/dist/utils/app-info.js.map +1 -0
  102. package/dist/utils/errors.d.ts +37 -0
  103. package/dist/utils/errors.d.ts.map +1 -0
  104. package/dist/utils/errors.js +72 -0
  105. package/dist/utils/errors.js.map +1 -0
  106. package/dist/utils/gemini-compatibility.d.ts +39 -0
  107. package/dist/utils/gemini-compatibility.d.ts.map +1 -0
  108. package/dist/utils/gemini-compatibility.js +154 -0
  109. package/dist/utils/gemini-compatibility.js.map +1 -0
  110. package/dist/utils/geogebra-instance.d.ts +252 -0
  111. package/dist/utils/geogebra-instance.d.ts.map +1 -0
  112. package/dist/utils/geogebra-instance.js +1560 -0
  113. package/dist/utils/geogebra-instance.js.map +1 -0
  114. package/dist/utils/geogebra-mock.d.ts +172 -0
  115. package/dist/utils/geogebra-mock.d.ts.map +1 -0
  116. package/dist/utils/geogebra-mock.js +732 -0
  117. package/dist/utils/geogebra-mock.js.map +1 -0
  118. package/dist/utils/logger.d.ts +4 -0
  119. package/dist/utils/logger.d.ts.map +1 -0
  120. package/dist/utils/logger.js +46 -0
  121. package/dist/utils/logger.js.map +1 -0
  122. package/dist/utils/performance/index.d.ts +76 -0
  123. package/dist/utils/performance/index.d.ts.map +1 -0
  124. package/dist/utils/performance/index.js +163 -0
  125. package/dist/utils/performance/index.js.map +1 -0
  126. package/dist/utils/performance/instance-pool.d.ts +78 -0
  127. package/dist/utils/performance/instance-pool.d.ts.map +1 -0
  128. package/dist/utils/performance/instance-pool.js +268 -0
  129. package/dist/utils/performance/instance-pool.js.map +1 -0
  130. package/dist/utils/validation.d.ts +92 -0
  131. package/dist/utils/validation.d.ts.map +1 -0
  132. package/dist/utils/validation.js +557 -0
  133. package/dist/utils/validation.js.map +1 -0
  134. package/dist/widget.html +110 -0
  135. package/docs/APPS-API-BRIDGE.md +28 -0
  136. package/docs/MODERNIZATION.md +15 -0
  137. package/docs/TANGENT-REGRESSION.md +34 -0
  138. package/docs/VALIDATION.md +26 -0
  139. package/docs/VISUAL-STANDARD.md +26 -0
  140. package/docs/images/native-construction.png +0 -0
  141. package/examples/tangent-construction.json +26 -0
  142. package/gikz/LICENSE +339 -0
  143. package/gikz/PROVENANCE.md +8 -0
  144. package/gikz/browser.cjs +2 -0
  145. package/gikz/cli.cjs +195 -0
  146. package/gikz/core.cjs +806 -0
  147. package/mcp.json +20 -0
  148. package/package.json +107 -4
  149. package/plugin.json +35 -0
  150. package/runtime/package.json +4 -0
  151. package/scripts/build-plugin.cjs +15 -0
  152. package/scripts/build-widget.cjs +7 -0
  153. package/skills/geogebra-construct/SKILL.md +40 -0
  154. package/skills/geogebra-setup/SKILL.md +37 -0
  155. package/skills/geometry-export/SKILL.md +31 -0
  156. package/src/cli.ts +212 -0
  157. package/src/export/files.ts +26 -0
  158. package/src/export/gikz.ts +22 -0
  159. package/src/export/html.ts +108 -0
  160. package/src/export/semantic.ts +180 -0
  161. package/src/export/viewer-themes.ts +165 -0
  162. package/src/export/workspace.ts +15 -0
  163. package/src/index.ts +71 -0
  164. package/src/server.ts +168 -0
  165. package/src/studio/artifact.ts +50 -0
  166. package/src/studio/browser.ts +38 -0
  167. package/src/studio/install.ts +43 -0
  168. package/src/tools/apps-api-tools.ts +96 -0
  169. package/src/tools/educational-templates.ts +299 -0
  170. package/src/tools/export-tools.ts +110 -0
  171. package/src/tools/geogebra-tools.ts +3836 -0
  172. package/src/tools/index.ts +192 -0
  173. package/src/tools/performance-tools.ts +322 -0
  174. package/src/tools/templates/algebra-templates.ts +619 -0
  175. package/src/tools/templates/geometry-templates.ts +550 -0
  176. package/src/types/geogebra.ts +140 -0
  177. package/src/types/global.d.ts +31 -0
  178. package/src/types/mcp.ts +141 -0
  179. package/src/utils/app-info.ts +35 -0
  180. package/src/utils/errors.ts +97 -0
  181. package/src/utils/gemini-compatibility.ts +193 -0
  182. package/src/utils/geogebra-instance.ts +1712 -0
  183. package/src/utils/geogebra-mock.ts +867 -0
  184. package/src/utils/logger.ts +60 -0
  185. package/src/utils/performance/index.ts +211 -0
  186. package/src/utils/performance/instance-pool.ts +323 -0
  187. package/src/utils/validation.ts +647 -0
  188. package/tsconfig.json +35 -0
  189. package/widget/main.ts +12 -0
@@ -0,0 +1,867 @@
1
+ import {
2
+ GeoGebraConfig,
3
+ GeoGebraAPI,
4
+ GeoGebraCommandResult,
5
+ GeoGebraObject,
6
+ GeoGebraConnectionError,
7
+ GeoGebraCommandError
8
+ } from '../types/geogebra';
9
+ import logger from './logger';
10
+ import { randomUUID as uuidv4 } from 'crypto';
11
+
12
+ /**
13
+ * Mock GeoGebra Instance for demonstration and testing
14
+ * This provides the same interface as the full implementation
15
+ * but uses in-memory simulation instead of a real browser instance
16
+ */
17
+ export class MockGeoGebraInstance implements GeoGebraAPI {
18
+ private isInitialized: boolean = false;
19
+ private lastActivity: Date = new Date();
20
+ private objects: Map<string, GeoGebraObject> = new Map();
21
+ private animatingObjects: Set<string> = new Set();
22
+ private animationSpeeds: Map<string, number> = new Map();
23
+ private tracedObjects: Set<string> = new Set();
24
+ private isAnimating: boolean = false;
25
+
26
+ public readonly id: string;
27
+ public readonly config: GeoGebraConfig;
28
+
29
+ constructor(config: GeoGebraConfig = {}) {
30
+ this.id = uuidv4();
31
+ this.config = {
32
+ appName: 'graphing',
33
+ width: 800,
34
+ height: 600,
35
+ showMenuBar: false,
36
+ showToolBar: false,
37
+ showAlgebraInput: false,
38
+ showResetIcon: false,
39
+ enableRightClick: true,
40
+ language: 'en',
41
+ ...config
42
+ };
43
+
44
+ logger.info(`Created Mock GeoGebra instance ${this.id}`, { config: this.config });
45
+ }
46
+
47
+ /**
48
+ * Initialize the mock instance
49
+ */
50
+ async initialize(): Promise<void> {
51
+ try {
52
+ logger.info(`Initializing Mock GeoGebra instance ${this.id}`);
53
+
54
+ // Simulate initialization delay
55
+ await new Promise(resolve => setTimeout(resolve, 100));
56
+
57
+ this.isInitialized = true;
58
+ this.updateActivity();
59
+
60
+ logger.info(`Mock GeoGebra instance ${this.id} initialized successfully`);
61
+ } catch (error) {
62
+ logger.error(`Failed to initialize Mock GeoGebra instance ${this.id}`, error);
63
+ throw new GeoGebraConnectionError(
64
+ `Failed to initialize GeoGebra: ${error instanceof Error ? error.message : String(error)}`
65
+ );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Execute a GeoGebra command (mock implementation)
71
+ */
72
+ async evalCommand(command: string): Promise<GeoGebraCommandResult> {
73
+ this.ensureInitialized();
74
+ this.updateActivity();
75
+
76
+ try {
77
+ logger.debug(`Executing mock command on instance ${this.id}: ${command}`);
78
+
79
+ // Parse basic commands
80
+ const result = this.parseCommand(command);
81
+
82
+ logger.debug(`Mock command executed successfully on instance ${this.id}: ${command}`);
83
+ return {
84
+ success: true,
85
+ result: result
86
+ };
87
+ } catch (error) {
88
+ logger.error(`Mock command execution failed on instance ${this.id}`, { command, error });
89
+ return {
90
+ success: false,
91
+ error: error instanceof Error ? error.message : String(error)
92
+ };
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Parse and execute basic GeoGebra commands
98
+ */
99
+ private parseCommand(command: string): string {
100
+ const trimmed = command.trim();
101
+
102
+ // Point creation: A = (x, y)
103
+ const pointMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)$/);
104
+ if (pointMatch && pointMatch[1] && pointMatch[2] && pointMatch[3]) {
105
+ const [, name, x, y] = pointMatch;
106
+ this.objects.set(name, {
107
+ name,
108
+ type: 'point',
109
+ visible: true,
110
+ defined: true,
111
+ x: parseFloat(x),
112
+ y: parseFloat(y),
113
+ z: 0,
114
+ color: '#0000FF'
115
+ });
116
+ return `Point ${name} created at (${x}, ${y})`;
117
+ }
118
+
119
+ // Slider creation: name = Slider(min, max, increment, speed, width, isAngle, horizontal, animating, random)
120
+ const sliderMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Slider\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:\s*,\s*(\d+(?:\.\d+)?))?\s*.*?\)$/);
121
+ if (sliderMatch && sliderMatch[1] && sliderMatch[2] && sliderMatch[3]) {
122
+ const [, name, min, max, _increment] = sliderMatch;
123
+ const defaultValue = parseFloat(min);
124
+ this.objects.set(name, {
125
+ name,
126
+ type: 'slider',
127
+ visible: true,
128
+ defined: true,
129
+ value: defaultValue,
130
+ color: '#0073E6'
131
+ });
132
+ return `Slider ${name} created with range [${min}, ${max}], current value: ${defaultValue}`;
133
+ }
134
+
135
+ // Line creation: l = Line(A, B)
136
+ const lineMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Line\s*\(\s*([A-Za-z]\w*)\s*,\s*([A-Za-z]\w*)\s*\)$/);
137
+ if (lineMatch && lineMatch[1] && lineMatch[2] && lineMatch[3]) {
138
+ const [, name, point1, point2] = lineMatch;
139
+
140
+ if (!this.objects.has(point1) || !this.objects.has(point2)) {
141
+ throw new Error(`Points ${point1} or ${point2} do not exist`);
142
+ }
143
+
144
+ this.objects.set(name, {
145
+ name,
146
+ type: 'line',
147
+ visible: true,
148
+ defined: true,
149
+ color: '#000000'
150
+ });
151
+ return `Line ${name} created through points ${point1} and ${point2}`;
152
+ }
153
+
154
+ // Function creation: f(x) = expression
155
+ const functionMatch = trimmed.match(/^([A-Za-z]\w*)\s*\(\s*x\s*\)\s*=\s*(.+)$/);
156
+ if (functionMatch && functionMatch[1] && functionMatch[2]) {
157
+ const [, name, expression] = functionMatch;
158
+ this.objects.set(name, {
159
+ name,
160
+ type: 'function',
161
+ visible: true,
162
+ defined: true,
163
+ value: expression,
164
+ color: '#FF0000'
165
+ });
166
+ return `Function ${name}(x) = ${expression} created`;
167
+ }
168
+
169
+ // Parametric curve creation: name = Curve(x_expr, y_expr, param, start, end)
170
+ const parametricMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Curve\s*\(\s*(.+?)\s*,\s*(.+?)\s*,\s*([A-Za-z]\w*)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)$/);
171
+ if (parametricMatch && parametricMatch[1] && parametricMatch[2] && parametricMatch[3] && parametricMatch[4] && parametricMatch[5] && parametricMatch[6]) {
172
+ const [, name, xExpr, yExpr, param, tMin, tMax] = parametricMatch;
173
+ this.objects.set(name, {
174
+ name,
175
+ type: 'curve',
176
+ visible: true,
177
+ defined: true,
178
+ value: `Parametric: x(${param}) = ${xExpr}, y(${param}) = ${yExpr}, ${param} ∈ [${tMin}, ${tMax}]`,
179
+ color: '#00AA00'
180
+ });
181
+ return `Parametric curve ${name} created: x(${param}) = ${xExpr}, y(${param}) = ${yExpr}, ${param} ∈ [${tMin}, ${tMax}]`;
182
+ }
183
+
184
+ // Implicit curve creation: name = ImplicitCurve(expression)
185
+ const implicitMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*ImplicitCurve\s*\(\s*(.+)\s*\)$/);
186
+ if (implicitMatch && implicitMatch[1] && implicitMatch[2]) {
187
+ const [, name, expression] = implicitMatch;
188
+ this.objects.set(name, {
189
+ name,
190
+ type: 'implicitcurve',
191
+ visible: true,
192
+ defined: true,
193
+ value: `Implicit: ${expression} = 0`,
194
+ color: '#AA0000'
195
+ });
196
+ return `Implicit curve ${name} created: ${expression} = 0`;
197
+ }
198
+
199
+ // Styling commands: SetColor, SetLineThickness, SetLineStyle
200
+ const setColorMatch = trimmed.match(/^SetColor\s*\(\s*([A-Za-z]\w*)\s*,\s*"([^"]+)"\s*\)$/);
201
+ if (setColorMatch && setColorMatch[1] && setColorMatch[2]) {
202
+ const [, name, color] = setColorMatch;
203
+ const obj = this.objects.get(name);
204
+ if (obj) {
205
+ obj.color = color;
206
+ return `Color of ${name} set to ${color}`;
207
+ }
208
+ return `Object ${name} not found for color setting`;
209
+ }
210
+
211
+ const setThicknessMatch = trimmed.match(/^SetLineThickness\s*\(\s*([A-Za-z]\w*)\s*,\s*(\d+)\s*\)$/);
212
+ if (setThicknessMatch && setThicknessMatch[1] && setThicknessMatch[2]) {
213
+ const [, name, thickness] = setThicknessMatch;
214
+ const obj = this.objects.get(name);
215
+ if (obj) {
216
+ // Store thickness in a custom property (mock doesn't have thickness in type)
217
+ (obj as any).thickness = parseInt(thickness);
218
+ return `Line thickness of ${name} set to ${thickness}`;
219
+ }
220
+ return `Object ${name} not found for thickness setting`;
221
+ }
222
+
223
+ const setStyleMatch = trimmed.match(/^SetLineStyle\s*\(\s*([A-Za-z]\w*)\s*,\s*(\d+)\s*\)$/);
224
+ if (setStyleMatch && setStyleMatch[1] && setStyleMatch[2]) {
225
+ const [, name, lineType] = setStyleMatch;
226
+ const obj = this.objects.get(name);
227
+ if (obj) {
228
+ const style = lineType === '10' ? 'dashed' : lineType === '20' ? 'dotted' : 'solid';
229
+ (obj as any).style = style;
230
+ return `Line style of ${name} set to ${style}`;
231
+ }
232
+ return `Object ${name} not found for style setting`;
233
+ }
234
+
235
+ // Circle creation: Circle((x, y), r)
236
+ const circleMatch = trimmed.match(/^Circle\s*\(\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)\s*,\s*(\d+(?:\.\d+)?)\s*\)$/);
237
+ if (circleMatch && circleMatch[1] && circleMatch[2] && circleMatch[3]) {
238
+ const [, x, y, r] = circleMatch;
239
+ const name = `circle_${Date.now()}`;
240
+ this.objects.set(name, {
241
+ name,
242
+ type: 'circle',
243
+ visible: true,
244
+ defined: true,
245
+ x: parseFloat(x),
246
+ y: parseFloat(y),
247
+ value: parseFloat(r),
248
+ color: '#00FF00'
249
+ });
250
+ return `Circle created at (${x}, ${y}) with radius ${r}`;
251
+ }
252
+
253
+ // CAS Operations
254
+
255
+ // Solve equation: Solve(equation)
256
+ const solveMatch = trimmed.match(/^Solve\s*\(\s*(.+)\s*\)$/);
257
+ if (solveMatch && solveMatch[1]) {
258
+ const [, equation] = solveMatch;
259
+ return this.mockSolveEquation(equation);
260
+ }
261
+
262
+ // Solve equation for variable: Solve(equation, variable)
263
+ const solveVarMatch = trimmed.match(/^Solve\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
264
+ if (solveVarMatch && solveVarMatch[1] && solveVarMatch[2]) {
265
+ const [, equation, variable] = solveVarMatch;
266
+ return this.mockSolveEquation(equation, variable);
267
+ }
268
+
269
+ // Differentiate with respect to variable: Derivative(expression, variable)
270
+ const derivativeVarMatch = trimmed.match(/^Derivative\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
271
+ if (derivativeVarMatch && derivativeVarMatch[1] && derivativeVarMatch[2]) {
272
+ const [, expression, variable] = derivativeVarMatch;
273
+ return this.mockDifferentiate(expression, variable);
274
+ }
275
+
276
+ // Differentiate: Derivative(expression)
277
+ const derivativeMatch = trimmed.match(/^Derivative\s*\(\s*(.+)\s*\)$/);
278
+ if (derivativeMatch && derivativeMatch[1]) {
279
+ const [, expression] = derivativeMatch;
280
+ return this.mockDifferentiate(expression);
281
+ }
282
+
283
+ // Integrate with respect to variable: Integral(expression, variable)
284
+ const integralVarMatch = trimmed.match(/^Integral\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
285
+ if (integralVarMatch && integralVarMatch[1] && integralVarMatch[2]) {
286
+ const [, expression, variable] = integralVarMatch;
287
+ return this.mockIntegrate(expression, variable);
288
+ }
289
+
290
+ // Integrate: Integral(expression)
291
+ const integralMatch = trimmed.match(/^Integral\s*\(\s*(.+)\s*\)$/);
292
+ if (integralMatch && integralMatch[1]) {
293
+ const [, expression] = integralMatch;
294
+ return this.mockIntegrate(expression);
295
+ }
296
+
297
+ // Simplify: Simplify(expression)
298
+ const simplifyMatch = trimmed.match(/^Simplify\s*\(\s*(.+)\s*\)$/);
299
+ if (simplifyMatch && simplifyMatch[1]) {
300
+ const [, expression] = simplifyMatch;
301
+ return this.mockSimplify(expression);
302
+ }
303
+
304
+ // Generic command (just acknowledge)
305
+ return `Command executed: ${command}`;
306
+ }
307
+
308
+ /**
309
+ * Execute command and get labels of created objects
310
+ */
311
+ async evalCommandGetLabels(command: string): Promise<string[]> {
312
+ this.ensureInitialized();
313
+ this.updateActivity();
314
+
315
+ try {
316
+ const beforeObjects = new Set(this.objects.keys());
317
+ await this.evalCommand(command);
318
+ const afterObjects = new Set(this.objects.keys());
319
+
320
+ const newLabels = Array.from(afterObjects).filter(label => !beforeObjects.has(label));
321
+
322
+ logger.debug(`Mock command executed on instance ${this.id}, labels: ${newLabels.join(', ')}`);
323
+ return newLabels;
324
+ } catch (error) {
325
+ logger.error(`Failed to execute mock command with labels on instance ${this.id}`, { command, error });
326
+ throw new GeoGebraCommandError(
327
+ `Failed to execute command: ${error instanceof Error ? error.message : String(error)}`,
328
+ command
329
+ );
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Delete an object
335
+ */
336
+ async deleteObject(objName: string): Promise<boolean> {
337
+ this.ensureInitialized();
338
+ this.updateActivity();
339
+
340
+ try {
341
+ const deleted = this.objects.delete(objName);
342
+ if (deleted) {
343
+ logger.debug(`Deleted object ${objName} from mock instance ${this.id}`);
344
+ }
345
+ return deleted;
346
+ } catch (error) {
347
+ logger.error(`Failed to delete object ${objName} on mock instance ${this.id}`, error);
348
+ return false;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Check if object exists
354
+ */
355
+ async exists(objName: string): Promise<boolean> {
356
+ this.ensureInitialized();
357
+ this.updateActivity();
358
+ return this.objects.has(objName);
359
+ }
360
+
361
+ /**
362
+ * Check if object is defined
363
+ */
364
+ async isDefined(objName: string): Promise<boolean> {
365
+ this.ensureInitialized();
366
+ this.updateActivity();
367
+ const obj = this.objects.get(objName);
368
+ return obj ? obj.defined : false;
369
+ }
370
+
371
+ /**
372
+ * Get all object names
373
+ */
374
+ async getAllObjectNames(type?: string): Promise<string[]> {
375
+ this.ensureInitialized();
376
+ this.updateActivity();
377
+
378
+ if (type) {
379
+ return Array.from(this.objects.values())
380
+ .filter(obj => obj.type === type)
381
+ .map(obj => obj.name);
382
+ }
383
+
384
+ return Array.from(this.objects.keys());
385
+ }
386
+
387
+ /**
388
+ * Get object information
389
+ */
390
+ async getObjectInfo(objName: string): Promise<GeoGebraObject | null> {
391
+ this.ensureInitialized();
392
+ this.updateActivity();
393
+ return this.objects.get(objName) || null;
394
+ }
395
+
396
+ /**
397
+ * Get X coordinate of object
398
+ */
399
+ async getXcoord(objName: string): Promise<number> {
400
+ this.ensureInitialized();
401
+ this.updateActivity();
402
+ const obj = this.objects.get(objName);
403
+ return obj?.x || 0;
404
+ }
405
+
406
+ /**
407
+ * Get Y coordinate of object
408
+ */
409
+ async getYcoord(objName: string): Promise<number> {
410
+ this.ensureInitialized();
411
+ this.updateActivity();
412
+ const obj = this.objects.get(objName);
413
+ return obj?.y || 0;
414
+ }
415
+
416
+ /**
417
+ * Get Z coordinate of object
418
+ */
419
+ async getZcoord(objName: string): Promise<number> {
420
+ this.ensureInitialized();
421
+ this.updateActivity();
422
+ const obj = this.objects.get(objName);
423
+ return obj?.z || 0;
424
+ }
425
+
426
+ /**
427
+ * Get value of object
428
+ */
429
+ async getValue(objName: string): Promise<number> {
430
+ this.ensureInitialized();
431
+ this.updateActivity();
432
+ const obj = this.objects.get(objName);
433
+ return typeof obj?.value === 'number' ? obj.value : 0;
434
+ }
435
+
436
+ /**
437
+ * Get value as string
438
+ */
439
+ async getValueString(objName: string): Promise<string> {
440
+ this.ensureInitialized();
441
+ this.updateActivity();
442
+ const obj = this.objects.get(objName);
443
+ return obj?.value?.toString() || '';
444
+ }
445
+
446
+ /**
447
+ * Create new construction
448
+ */
449
+ async newConstruction(): Promise<void> {
450
+ this.ensureInitialized();
451
+ this.updateActivity();
452
+ this.objects.clear();
453
+ logger.debug(`New construction created on mock instance ${this.id}`);
454
+ }
455
+
456
+ /**
457
+ * Reset to initial state
458
+ */
459
+ async reset(): Promise<void> {
460
+ this.ensureInitialized();
461
+ this.updateActivity();
462
+ this.objects.clear();
463
+ logger.debug(`Mock instance ${this.id} reset to initial state`);
464
+ }
465
+
466
+ /**
467
+ * Refresh all views
468
+ */
469
+ async refreshViews(): Promise<void> {
470
+ this.ensureInitialized();
471
+ this.updateActivity();
472
+ logger.debug(`Views refreshed on mock instance ${this.id}`);
473
+ }
474
+
475
+ /**
476
+ * Set coordinate system bounds (mock implementation)
477
+ */
478
+ async setCoordSystem(xmin: number, xmax: number, ymin: number, ymax: number): Promise<void> {
479
+ this.ensureInitialized();
480
+ this.updateActivity();
481
+ logger.debug(`Mock coordinate system set on instance ${this.id}: x[${xmin}, ${xmax}], y[${ymin}, ${ymax}]`);
482
+ }
483
+
484
+ /**
485
+ * Set axes visibility (mock implementation)
486
+ */
487
+ async setAxesVisible(xAxis: boolean, yAxis: boolean): Promise<void> {
488
+ this.ensureInitialized();
489
+ this.updateActivity();
490
+ logger.debug(`Mock axes visibility set on instance ${this.id}: x=${xAxis}, y=${yAxis}`);
491
+ }
492
+
493
+ /**
494
+ * Set grid visibility (mock implementation)
495
+ */
496
+ async setGridVisible(visible: boolean): Promise<void> {
497
+ this.ensureInitialized();
498
+ this.updateActivity();
499
+ logger.debug(`Mock instance ${this.id}: Set grid visible: ${visible}`);
500
+ }
501
+
502
+ /**
503
+ * Check if instance is ready
504
+ */
505
+ async isReady(): Promise<boolean> {
506
+ return this.isInitialized;
507
+ }
508
+
509
+ /**
510
+ * Export construction as PNG (base64) - Mock implementation with enhanced parameters
511
+ */
512
+ async exportPNG(scale: number = 1, transparent: boolean = false, dpi: number = 72, width?: number, height?: number): Promise<string> {
513
+ this.ensureInitialized();
514
+ this.updateActivity();
515
+
516
+ // Return a simple base64 encoded placeholder PNG
517
+ const placeholderPNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
518
+ logger.debug(`Mock PNG exported from instance ${this.id} with scale ${scale}, transparent ${transparent}, dpi ${dpi}, dimensions ${width}x${height}`);
519
+ return placeholderPNG;
520
+ }
521
+
522
+ /**
523
+ * Export construction as SVG - Mock implementation
524
+ */
525
+ async exportSVG(): Promise<string> {
526
+ this.ensureInitialized();
527
+ this.updateActivity();
528
+
529
+ // Return a simple SVG with mock construction
530
+ const mockSVG = `<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg">
531
+ <rect width="800" height="600" fill="white"/>
532
+ <text x="400" y="300" text-anchor="middle" font-family="Arial" font-size="20">Mock GeoGebra Construction</text>
533
+ ${Array.from(this.objects.values()).map(obj => {
534
+ if (obj.type === 'point') {
535
+ return `<circle cx="${(obj.x || 0) * 50 + 400}" cy="${(obj.y || 0) * -50 + 300}" r="3" fill="${obj.color || '#0000FF'}"/>`;
536
+ }
537
+ return '';
538
+ }).join('\n ')}
539
+ </svg>`;
540
+
541
+ logger.debug(`Mock SVG exported from instance ${this.id}`);
542
+ return mockSVG.trim();
543
+ }
544
+
545
+ /**
546
+ * Export construction as PDF (base64) - Mock implementation
547
+ */
548
+ async exportPDF(): Promise<string> {
549
+ this.ensureInitialized();
550
+ this.updateActivity();
551
+
552
+ logger.debug(`Mock PDF export from instance ${this.id}`);
553
+
554
+ // Return mock PDF data (base64 encoded)
555
+ const mockPdfData = 'JVBERi0xLjQKJcOkw7zDtsOgCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nDPQM1Qo5ypUMFAw0DMwULBVqOZSUDBQykvMTbVSUDC2UshIzStRyMnPS7VVqAUAYQsKZAplbmRzdHJlYW0KZW5kb2JqCgozIDAgb2JqCjI4CmVuZG9iagoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgNCAwIFI+PgplbmRvYmoKCjQgMCBvYmoKPDwvVHlwZS9QYWdlcy9LaWRzWzUgMCBSXS9Db3VudCAxPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvUGFnZS9QYXJlbnQgNCAwIFIvTWVkaWFCb3hbMCAwIDYxMiA3OTJdL0NvbnRlbnRzIDIgMCBSPj4KZW5kb2JqCgp4cmVmCjAgNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAyOTQgMDAwMDAgbiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMjc0IDAwMDAwIG4gCjAwMDAwMDAzNDMgMDAwMDAgbiAKMDAwMDAwMDQwMCAwMDAwMCBuIAp0cmFpbGVyCjw8L1NpemUgNi9Sb290IDEgMCBSPj4Kc3RhcnR4cmVmCjUwMwolJUVPRg==';
556
+
557
+ return mockPdfData;
558
+ }
559
+
560
+ /**
561
+ * Export animation as frames (GEB-17 enhancement)
562
+ */
563
+ async exportAnimation(options: {
564
+ duration?: number;
565
+ frameRate?: number;
566
+ format?: 'gif' | 'frames';
567
+ width?: number;
568
+ height?: number;
569
+ } = {}): Promise<string | string[]> {
570
+ this.ensureInitialized();
571
+ this.updateActivity();
572
+
573
+ const {
574
+ duration = 5000, // 5 seconds
575
+ frameRate = 10, // 10 fps
576
+ format = 'frames',
577
+ width = 800,
578
+ height = 600
579
+ } = options;
580
+
581
+ logger.debug(`Mock animation export from instance ${this.id}`, { duration, frameRate, format, width, height });
582
+
583
+ // Calculate number of frames
584
+ const totalFrames = Math.ceil((duration / 1000) * frameRate);
585
+
586
+ // Generate mock frame data
587
+ const frames: string[] = [];
588
+ for (let i = 0; i < totalFrames; i++) {
589
+ // Generate a simple mock PNG frame (base64 encoded)
590
+ const mockFrameData = `iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==_frame_${i}`;
591
+ frames.push(mockFrameData);
592
+ }
593
+
594
+ logger.debug(`Mock animation export completed: ${frames.length} frames generated`);
595
+
596
+ if (format === 'frames') {
597
+ return frames;
598
+ } else {
599
+ // For GIF format, return a mock GIF data string
600
+ return 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7_mock_gif';
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Cleanup resources
606
+ */
607
+ async cleanup(): Promise<void> {
608
+ logger.info(`Cleaning up Mock GeoGebra instance ${this.id}`);
609
+ this.objects.clear();
610
+ this.isInitialized = false;
611
+ logger.info(`Mock GeoGebra instance ${this.id} cleaned up successfully`);
612
+ }
613
+
614
+ /**
615
+ * Get instance state
616
+ */
617
+ getState() {
618
+ return {
619
+ id: this.id,
620
+ isReady: this.isInitialized,
621
+ lastActivity: this.lastActivity,
622
+ config: this.config
623
+ };
624
+ }
625
+
626
+ /**
627
+ * Update last activity timestamp
628
+ */
629
+ private updateActivity(): void {
630
+ this.lastActivity = new Date();
631
+ }
632
+
633
+ /**
634
+ * Ensure instance is initialized
635
+ */
636
+ private ensureInitialized(): void {
637
+ if (!this.isInitialized) {
638
+ throw new GeoGebraConnectionError('Mock GeoGebra instance not initialized');
639
+ }
640
+ }
641
+
642
+ /**
643
+ * Mock implementation of equation solving
644
+ */
645
+ private mockSolveEquation(equation: string, variable?: string): string {
646
+ logger.debug(`Mock solving equation: ${equation}${variable ? ` for ${variable}` : ''}`);
647
+
648
+ // Simple mock responses for common cases
649
+ if (equation.includes('x^2')) {
650
+ return variable ?
651
+ `{${variable} = -√(solution), ${variable} = √(solution)}` :
652
+ `Solutions: x = -√(solution), x = √(solution)`;
653
+ }
654
+
655
+ if (equation.includes('x =') || equation.includes('= x')) {
656
+ const match = equation.match(/(-?\d+(?:\.\d+)?)/);
657
+ const value = match ? match[1] : '0';
658
+ return variable ?
659
+ `{${variable} = ${value}}` :
660
+ `Solution: x = ${value}`;
661
+ }
662
+
663
+ // Linear equation pattern
664
+ if (equation.match(/\d*x\s*[+-]/)) {
665
+ return variable ?
666
+ `{${variable} = solution_value}` :
667
+ `Solution: x = solution_value`;
668
+ }
669
+
670
+ return `Mock solution for equation: ${equation}`;
671
+ }
672
+
673
+ /**
674
+ * Mock implementation of differentiation
675
+ */
676
+ private mockDifferentiate(expression: string, variable: string = 'x'): string {
677
+ logger.debug(`Mock differentiating: ${expression} with respect to ${variable}`);
678
+
679
+ // Simple mock responses for common cases
680
+ if (expression === variable) {
681
+ return '1';
682
+ }
683
+
684
+ if (expression.includes(`${variable}^2`)) {
685
+ return `2*${variable}`;
686
+ }
687
+
688
+ if (expression.includes(`${variable}^3`)) {
689
+ return `3*${variable}^2`;
690
+ }
691
+
692
+ if (expression.includes(`sin(${variable})`)) {
693
+ return `cos(${variable})`;
694
+ }
695
+
696
+ if (expression.includes(`cos(${variable})`)) {
697
+ return `-sin(${variable})`;
698
+ }
699
+
700
+ if (expression.includes(`e^${variable}`)) {
701
+ return `e^${variable}`;
702
+ }
703
+
704
+ if (!expression.includes(variable)) {
705
+ return '0';
706
+ }
707
+
708
+ return `d/d${variable}(${expression})`;
709
+ }
710
+
711
+ /**
712
+ * Mock implementation of integration
713
+ */
714
+ private mockIntegrate(expression: string, variable: string = 'x'): string {
715
+ logger.debug(`Mock integrating: ${expression} with respect to ${variable}`);
716
+
717
+ // Simple mock responses for common cases
718
+ if (expression === '1') {
719
+ return variable;
720
+ }
721
+
722
+ if (expression === variable) {
723
+ return `${variable}^2/2`;
724
+ }
725
+
726
+ if (expression.includes(`${variable}^2`)) {
727
+ return `${variable}^3/3`;
728
+ }
729
+
730
+ if (expression.includes(`${variable}^3`)) {
731
+ return `${variable}^4/4`;
732
+ }
733
+
734
+ if (expression.includes(`sin(${variable})`)) {
735
+ return `-cos(${variable})`;
736
+ }
737
+
738
+ if (expression.includes(`cos(${variable})`)) {
739
+ return `sin(${variable})`;
740
+ }
741
+
742
+ if (expression.includes(`e^${variable}`)) {
743
+ return `e^${variable}`;
744
+ }
745
+
746
+ return `∫(${expression}, ${variable})dx`;
747
+ }
748
+
749
+ /**
750
+ * Mock implementation of expression simplification
751
+ */
752
+ private mockSimplify(expression: string): string {
753
+ logger.debug(`Mock simplifying: ${expression}`);
754
+
755
+ // Simple mock simplifications
756
+ let simplified = expression;
757
+
758
+ // Remove unnecessary parentheses and spaces
759
+ simplified = simplified.replace(/\s+/g, '');
760
+ simplified = simplified.replace(/\(([^()]+)\)/g, '$1');
761
+
762
+ // Combine like terms (very basic)
763
+ simplified = simplified.replace(/(\d+)\*x\+(\d+)\*x/g, (_match, a, b) => {
764
+ return `${parseInt(a) + parseInt(b)}*x`;
765
+ });
766
+
767
+ // Basic arithmetic
768
+ simplified = simplified.replace(/(\d+)\+(\d+)/g, (_match, a, b) => {
769
+ return `${parseInt(a) + parseInt(b)}`;
770
+ });
771
+
772
+ simplified = simplified.replace(/(\d+)\*1/g, '$1');
773
+ simplified = simplified.replace(/1\*(\d+)/g, '$1');
774
+ simplified = simplified.replace(/\+0/g, '');
775
+ simplified = simplified.replace(/0\+/g, '');
776
+
777
+ return simplified === expression ? expression : simplified;
778
+ }
779
+
780
+ /**
781
+ * Animation Methods
782
+ */
783
+ async setAnimating(objName: string, animate: boolean): Promise<void> {
784
+ this.ensureInitialized();
785
+ this.updateActivity();
786
+
787
+ if (!this.objects.has(objName)) {
788
+ throw new Error(`Object ${objName} does not exist`);
789
+ }
790
+
791
+ if (animate) {
792
+ this.animatingObjects.add(objName);
793
+ if (!this.animationSpeeds.has(objName)) {
794
+ this.animationSpeeds.set(objName, 1); // Default speed
795
+ }
796
+ } else {
797
+ this.animatingObjects.delete(objName);
798
+ }
799
+
800
+ logger.debug(`Mock instance ${this.id}: Set animating for ${objName}: ${animate}`);
801
+ }
802
+
803
+ async setAnimationSpeed(objName: string, speed: number): Promise<void> {
804
+ this.ensureInitialized();
805
+ this.updateActivity();
806
+
807
+ if (!this.objects.has(objName)) {
808
+ throw new Error(`Object ${objName} does not exist`);
809
+ }
810
+
811
+ this.animationSpeeds.set(objName, speed);
812
+ logger.debug(`Mock instance ${this.id}: Set animation speed for ${objName}: ${speed}`);
813
+ }
814
+
815
+ async startAnimation(): Promise<void> {
816
+ this.ensureInitialized();
817
+ this.updateActivity();
818
+
819
+ this.isAnimating = true;
820
+ logger.debug(`Mock instance ${this.id}: Started animation for ${this.animatingObjects.size} objects`);
821
+ }
822
+
823
+ async stopAnimation(): Promise<void> {
824
+ this.ensureInitialized();
825
+ this.updateActivity();
826
+
827
+ this.isAnimating = false;
828
+ logger.debug(`Mock instance ${this.id}: Stopped animation`);
829
+ }
830
+
831
+ async isAnimationRunning(): Promise<boolean> {
832
+ this.ensureInitialized();
833
+ this.updateActivity();
834
+
835
+ return this.isAnimating && this.animatingObjects.size > 0;
836
+ }
837
+
838
+ async setTrace(objName: string, flag: boolean): Promise<void> {
839
+ this.ensureInitialized();
840
+ this.updateActivity();
841
+
842
+ if (!this.objects.has(objName)) {
843
+ throw new Error(`Object ${objName} does not exist`);
844
+ }
845
+
846
+ if (flag) {
847
+ this.tracedObjects.add(objName);
848
+ } else {
849
+ this.tracedObjects.delete(objName);
850
+ }
851
+
852
+ logger.debug(`Mock instance ${this.id}: Set trace for ${objName}: ${flag}`);
853
+ }
854
+
855
+ async setValue(objName: string, value: number): Promise<void> {
856
+ this.ensureInitialized();
857
+ this.updateActivity();
858
+
859
+ const obj = this.objects.get(objName);
860
+ if (!obj) {
861
+ throw new Error(`Object ${objName} does not exist`);
862
+ }
863
+
864
+ obj.value = value;
865
+ logger.debug(`Mock instance ${this.id}: Set value for ${objName}: ${value}`);
866
+ }
867
+ }