@xom11/whiteboard 0.24.1 → 0.24.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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/ai.d.mts +616 -0
  3. package/dist/ai.d.ts +616 -0
  4. package/dist/ai.js +1036 -0
  5. package/dist/ai.js.map +1 -0
  6. package/dist/ai.mjs +999 -0
  7. package/dist/ai.mjs.map +1 -0
  8. package/dist/catalog.json +4 -4
  9. package/dist/{chunk-4D5CSIJO.mjs → chunk-2WF6KIGF.mjs} +4 -3
  10. package/dist/chunk-2WF6KIGF.mjs.map +1 -0
  11. package/dist/{chunk-WWMQ2VHZ.mjs → chunk-45CGKJ7S.mjs} +4 -4
  12. package/dist/{chunk-WWMQ2VHZ.mjs.map → chunk-45CGKJ7S.mjs.map} +1 -1
  13. package/dist/{chunk-CRAPWQKJ.mjs → chunk-4DS3MKID.mjs} +4 -4
  14. package/dist/{chunk-CRAPWQKJ.mjs.map → chunk-4DS3MKID.mjs.map} +1 -1
  15. package/dist/chunk-73Q7ADVL.mjs +35 -0
  16. package/dist/chunk-73Q7ADVL.mjs.map +1 -0
  17. package/dist/{chunk-YIPI3WUL.mjs → chunk-7WQXXEVR.mjs} +4 -4
  18. package/dist/{chunk-YIPI3WUL.mjs.map → chunk-7WQXXEVR.mjs.map} +1 -1
  19. package/dist/{chunk-CSCF3YFZ.mjs → chunk-BEZSQKPY.mjs} +4 -3
  20. package/dist/chunk-BEZSQKPY.mjs.map +1 -0
  21. package/dist/{chunk-MFOGFFIL.mjs → chunk-CGZZO4BX.mjs} +5 -4
  22. package/dist/chunk-CGZZO4BX.mjs.map +1 -0
  23. package/dist/{chunk-IBTRMWD6.mjs → chunk-KRC2XOIG.mjs} +3 -3
  24. package/dist/{chunk-IBTRMWD6.mjs.map → chunk-KRC2XOIG.mjs.map} +1 -1
  25. package/dist/{chunk-6V4SH4JJ.mjs → chunk-WM2VDYQA.mjs} +4 -34
  26. package/dist/chunk-WM2VDYQA.mjs.map +1 -0
  27. package/dist/geometry-2d.d.mts +2 -1
  28. package/dist/geometry-2d.d.ts +2 -1
  29. package/dist/geometry-2d.mjs +5 -4
  30. package/dist/geometry-3d.d.mts +2 -1
  31. package/dist/geometry-3d.d.ts +2 -1
  32. package/dist/geometry-3d.mjs +4 -3
  33. package/dist/graph-2d.d.mts +2 -1
  34. package/dist/graph-2d.d.ts +2 -1
  35. package/dist/graph-2d.mjs +4 -3
  36. package/dist/{host-DOAYVL35.mjs → host-EPZCNFLH.mjs} +8 -7
  37. package/dist/host-EPZCNFLH.mjs.map +1 -0
  38. package/dist/{host-GKNQBBUE.mjs → host-LKCMYEAV.mjs} +7 -6
  39. package/dist/host-LKCMYEAV.mjs.map +1 -0
  40. package/dist/{host-TLIXN4CF.mjs → host-ZIQ77W33.mjs} +6 -5
  41. package/dist/host-ZIQ77W33.mjs.map +1 -0
  42. package/dist/index.d.mts +4 -616
  43. package/dist/index.d.ts +4 -616
  44. package/dist/index.js +28 -1028
  45. package/dist/index.js.map +1 -1
  46. package/dist/index.mjs +17 -1009
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/latex.d.mts +2 -1
  49. package/dist/latex.d.ts +2 -1
  50. package/dist/serialize-JAVOU22E.mjs +7 -0
  51. package/dist/{serialize-3NZS6A6Q.mjs.map → serialize-JAVOU22E.mjs.map} +1 -1
  52. package/dist/{types-rA4slL08.d.mts → types-Crbefnfe.d.ts} +2 -49
  53. package/dist/types-DxlMPh-6.d.mts +49 -0
  54. package/dist/types-DxlMPh-6.d.ts +49 -0
  55. package/dist/{types-rA4slL08.d.ts → types-vtvyKGAA.d.mts} +2 -49
  56. package/package.json +6 -1
  57. package/dist/chunk-4D5CSIJO.mjs.map +0 -1
  58. package/dist/chunk-6V4SH4JJ.mjs.map +0 -1
  59. package/dist/chunk-CSCF3YFZ.mjs.map +0 -1
  60. package/dist/chunk-MFOGFFIL.mjs.map +0 -1
  61. package/dist/host-DOAYVL35.mjs.map +0 -1
  62. package/dist/host-GKNQBBUE.mjs.map +0 -1
  63. package/dist/host-TLIXN4CF.mjs.map +0 -1
  64. package/dist/serialize-3NZS6A6Q.mjs +0 -6
package/dist/index.js CHANGED
@@ -8,11 +8,6 @@ var jsxRuntime = require('react/jsx-runtime');
8
8
  var reactDom = require('react-dom');
9
9
  var excalidraw = require('@excalidraw/excalidraw');
10
10
  require('@excalidraw/excalidraw/index.css');
11
- var zod = require('zod');
12
- var Anthropic = require('@anthropic-ai/sdk');
13
- var zodToJsonSchema = require('zod-to-json-schema');
14
-
15
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
11
 
17
12
  function _interopNamespace(e) {
18
13
  if (e && e.__esModule) return e;
@@ -33,7 +28,6 @@ function _interopNamespace(e) {
33
28
  }
34
29
 
35
30
  var React18__namespace = /*#__PURE__*/_interopNamespace(React18);
36
- var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
37
31
 
38
32
  var __defProp = Object.defineProperty;
39
33
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -115,7 +109,7 @@ function collectDependents(state, rootId) {
115
109
  continue;
116
110
  }
117
111
  const refs = kindDef.dependsOn(obj.attrs);
118
- if (refs.some((r2) => dependents.has(r2))) {
112
+ if (refs.some((r) => dependents.has(r))) {
119
113
  dependents.add(obj.id);
120
114
  grew = true;
121
115
  }
@@ -789,15 +783,15 @@ var init_cylinder3d = __esm({
789
783
  const view = ctx.jxg;
790
784
  const a = ctx.resolveRef(obj.attrs.baseCenter);
791
785
  const b = ctx.resolveRef(obj.attrs.topCenter);
792
- const r2 = obj.attrs.radius;
786
+ const r = obj.attrs.radius;
793
787
  const ax = a.X?.() ?? 0, ay = a.Y?.() ?? 0, az = a.Z?.() ?? 0;
794
788
  const bx = b.X?.() ?? 0, by = b.Y?.() ?? 0, bz = b.Z?.() ?? 0;
795
789
  const baseRing = [];
796
790
  const topRing = [];
797
791
  for (let i = 0; i < CURVED_SEGMENTS; i++) {
798
792
  const theta = i / CURVED_SEGMENTS * Math.PI * 2;
799
- const dx = r2 * Math.cos(theta);
800
- const dy = r2 * Math.sin(theta);
793
+ const dx = r * Math.cos(theta);
794
+ const dy = r * Math.sin(theta);
801
795
  baseRing.push([ax + dx, ay + dy, az]);
802
796
  topRing.push([bx + dx, by + dy, bz]);
803
797
  }
@@ -853,7 +847,7 @@ var init_cone3d = __esm({
853
847
  const view = ctx.jxg;
854
848
  const base = ctx.resolveRef(obj.attrs.baseCenter);
855
849
  const apexPt = ctx.resolveRef(obj.attrs.apex);
856
- const r2 = obj.attrs.radius;
850
+ const r = obj.attrs.radius;
857
851
  const bx = base.X?.() ?? 0, by = base.Y?.() ?? 0, bz = base.Z?.() ?? 0;
858
852
  const apexCoords = [
859
853
  apexPt.X?.() ?? 0,
@@ -863,7 +857,7 @@ var init_cone3d = __esm({
863
857
  const baseRing = [];
864
858
  for (let i = 0; i < CURVED_SEGMENTS2; i++) {
865
859
  const theta = i / CURVED_SEGMENTS2 * Math.PI * 2;
866
- baseRing.push([bx + r2 * Math.cos(theta), by + r2 * Math.sin(theta), bz]);
860
+ baseRing.push([bx + r * Math.cos(theta), by + r * Math.sin(theta), bz]);
867
861
  }
868
862
  const apexIdx = baseRing.length;
869
863
  const vertices = [...baseRing, apexCoords];
@@ -2700,7 +2694,7 @@ var init_JxgRenderer = __esm({
2700
2694
  if (obj.kind !== "function2d") continue;
2701
2695
  const expr = obj.attrs.expression;
2702
2696
  const refs = collectFreeVars(expr);
2703
- if (refs.some((r2) => changedParams.has(r2))) {
2697
+ if (refs.some((r) => changedParams.has(r))) {
2704
2698
  this.remove(id);
2705
2699
  this.create(obj);
2706
2700
  }
@@ -3519,8 +3513,8 @@ function useToolHoverTooltip() {
3519
3513
  const showHover = React18.useCallback((el, t) => {
3520
3514
  if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);
3521
3515
  hoverTimerRef.current = setTimeout(() => {
3522
- const r2 = el.getBoundingClientRect();
3523
- setHover({ label: t.label, hint: t.hint, x: r2.right, y: r2.top + r2.height / 2 });
3516
+ const r = el.getBoundingClientRect();
3517
+ setHover({ label: t.label, hint: t.hint, x: r.right, y: r.top + r.height / 2 });
3524
3518
  }, TOOLTIP_DELAY_MS);
3525
3519
  }, []);
3526
3520
  const hideHover = React18.useCallback(() => {
@@ -4730,11 +4724,11 @@ function classifyPointVsCircle(point, circle) {
4730
4724
  const dx = point.X() - circle.center.X();
4731
4725
  const dy = point.Y() - circle.center.Y();
4732
4726
  const d = Math.hypot(dx, dy);
4733
- const r2 = typeof circle.Radius === "function" ? circle.Radius() : Number(circle.radius);
4734
- if (!Number.isFinite(d) || !Number.isFinite(r2)) return "inside";
4735
- const eps = Math.max(1e-9, 1e-6 * r2);
4736
- if (Math.abs(d - r2) <= eps) return "on";
4737
- return d < r2 ? "inside" : "outside";
4727
+ const r = typeof circle.Radius === "function" ? circle.Radius() : Number(circle.radius);
4728
+ if (!Number.isFinite(d) || !Number.isFinite(r)) return "inside";
4729
+ const eps = Math.max(1e-9, 1e-6 * r);
4730
+ if (Math.abs(d - r) <= eps) return "on";
4731
+ return d < r ? "inside" : "outside";
4738
4732
  }
4739
4733
  var init_classifyPointVsCircle = __esm({
4740
4734
  "src/stamps/geometry-2d/editor/handlers/classifyPointVsCircle.ts"() {
@@ -6147,9 +6141,9 @@ function useJxgSceneIdMap({ store, rendererRef }) {
6147
6141
  const jxgIdToSceneRef = React18.useRef(/* @__PURE__ */ new Map());
6148
6142
  React18.useEffect(() => {
6149
6143
  const rebuild = () => {
6150
- const r2 = rendererRef.current;
6151
- if (!r2) return;
6152
- const elements = r2.elements;
6144
+ const r = rendererRef.current;
6145
+ if (!r) return;
6146
+ const elements = r.elements;
6153
6147
  const next = /* @__PURE__ */ new Map();
6154
6148
  if (elements) {
6155
6149
  for (const [sid, jxg] of elements) {
@@ -7897,9 +7891,9 @@ var init_host = __esm({
7897
7891
  const [canUndo, setCanUndo] = React18.useState(false);
7898
7892
  const [canRedo, setCanRedo] = React18.useState(false);
7899
7893
  const [selectedObjectId, setSelectedObjectId] = React18.useState(void 0);
7900
- const handleHistoryChange = React18.useCallback((u, r2) => {
7894
+ const handleHistoryChange = React18.useCallback((u, r) => {
7901
7895
  setCanUndo(u);
7902
- setCanRedo(r2);
7896
+ setCanRedo(r);
7903
7897
  }, []);
7904
7898
  const handleUndo = React18.useCallback(() => sceneStore.undo(), [sceneStore]);
7905
7899
  const handleRedo = React18.useCallback(() => sceneStore.redo(), [sceneStore]);
@@ -9220,8 +9214,8 @@ function constraintToWorld(c, state) {
9220
9214
  const radius = norm(sub(surface, center));
9221
9215
  const x = center[0] + radius * Math.sin(c.phi) * Math.cos(c.theta);
9222
9216
  const y = center[1] + radius * Math.sin(c.phi) * Math.sin(c.theta);
9223
- const z3 = center[2] + radius * Math.cos(c.phi);
9224
- return [x, y, z3];
9217
+ const z = center[2] + radius * Math.cos(c.phi);
9218
+ return [x, y, z];
9225
9219
  }
9226
9220
  }
9227
9221
  }
@@ -10143,8 +10137,8 @@ function hitTest(screen, view, state) {
10143
10137
  const relX = bestSphere.world[0] - center[0];
10144
10138
  const relY = bestSphere.world[1] - center[1];
10145
10139
  const relZ = bestSphere.world[2] - center[2];
10146
- const r2 = Math.hypot(relX, relY, relZ);
10147
- const phi = r2 === 0 ? 0 : Math.acos(relZ / r2);
10140
+ const r = Math.hypot(relX, relY, relZ);
10141
+ const phi = r === 0 ? 0 : Math.acos(relZ / r);
10148
10142
  const theta = Math.atan2(relY, relX);
10149
10143
  return { kind: "onSphere", sphereId: bestSphere.id, theta, phi, world: bestSphere.world };
10150
10144
  }
@@ -11229,7 +11223,7 @@ var init_icons2 = __esm({
11229
11223
  children
11230
11224
  }
11231
11225
  );
11232
- dot4 = (cx, cy, r2 = 1.4) => /* @__PURE__ */ jsxRuntime.jsx("circle", { cx, cy, r: r2, fill: "currentColor", stroke: "none" });
11226
+ dot4 = (cx, cy, r = 1.4) => /* @__PURE__ */ jsxRuntime.jsx("circle", { cx, cy, r, fill: "currentColor", stroke: "none" });
11233
11227
  ToolIcons = {
11234
11228
  move: wrap(
11235
11229
  /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 4 L5 14 L8 11 L10 16 L13 15 L11 10 L15 10 Z" }) })
@@ -11427,9 +11421,9 @@ var init_host3 = __esm({
11427
11421
  const [canUndo, setCanUndo] = React18.useState(false);
11428
11422
  const [canRedo, setCanRedo] = React18.useState(false);
11429
11423
  const [selectedObjectId, setSelectedObjectId] = React18.useState(void 0);
11430
- const handleHistoryChange = React18.useCallback((u, r2) => {
11424
+ const handleHistoryChange = React18.useCallback((u, r) => {
11431
11425
  setCanUndo(u);
11432
- setCanRedo(r2);
11426
+ setCanRedo(r);
11433
11427
  }, []);
11434
11428
  const handleObjectSelect = React18.useCallback((id) => {
11435
11429
  setSelectedObjectId(id ?? void 0);
@@ -12691,9 +12685,9 @@ var init_host4 = __esm({
12691
12685
  const [showGrid, setShowGridState] = React18.useState(initialView?.showGrid ?? true);
12692
12686
  const [canUndo, setCanUndo] = React18.useState(false);
12693
12687
  const [canRedo, setCanRedo] = React18.useState(false);
12694
- const handleHistoryChange = React18.useCallback((u, r2) => {
12688
+ const handleHistoryChange = React18.useCallback((u, r) => {
12695
12689
  setCanUndo(u);
12696
- setCanRedo(r2);
12690
+ setCanRedo(r);
12697
12691
  }, []);
12698
12692
  const handleUndo = React18.useCallback(() => sceneStore.undo(), [sceneStore]);
12699
12693
  const handleRedo = React18.useCallback(() => sceneStore.redo(), [sceneStore]);
@@ -15185,999 +15179,6 @@ function Whiteboard({
15185
15179
  )
15186
15180
  ] });
15187
15181
  }
15188
- var NameZ = zod.z.string().regex(/^[A-Za-z][A-Za-z0-9_'₀-₉]{0,11}$/);
15189
- var DslPoint = zod.z.discriminatedUnion("kind", [
15190
- zod.z.object({
15191
- name: NameZ,
15192
- kind: zod.z.literal("free"),
15193
- x: zod.z.number().finite(),
15194
- y: zod.z.number().finite()
15195
- }),
15196
- zod.z.object({
15197
- name: NameZ,
15198
- kind: zod.z.literal("midpoint"),
15199
- p1: NameZ,
15200
- p2: NameZ
15201
- }),
15202
- zod.z.object({
15203
- name: NameZ,
15204
- kind: zod.z.literal("onSegment"),
15205
- segmentId: NameZ,
15206
- t: zod.z.number().min(0).max(1)
15207
- }),
15208
- zod.z.object({
15209
- name: NameZ,
15210
- kind: zod.z.literal("onLine"),
15211
- lineId: NameZ,
15212
- t: zod.z.number().finite()
15213
- }),
15214
- zod.z.object({
15215
- name: NameZ,
15216
- kind: zod.z.literal("onCircle"),
15217
- circleId: NameZ,
15218
- theta: zod.z.number().finite()
15219
- }),
15220
- zod.z.object({
15221
- name: NameZ,
15222
- kind: zod.z.literal("perpFoot"),
15223
- from: NameZ,
15224
- onLine: NameZ
15225
- }),
15226
- zod.z.object({
15227
- name: NameZ,
15228
- kind: zod.z.literal("circumcenter"),
15229
- vertices: zod.z.tuple([NameZ, NameZ, NameZ])
15230
- }),
15231
- zod.z.object({
15232
- name: NameZ,
15233
- kind: zod.z.literal("incenter"),
15234
- vertices: zod.z.tuple([NameZ, NameZ, NameZ])
15235
- }),
15236
- zod.z.object({
15237
- name: NameZ,
15238
- kind: zod.z.literal("centroid"),
15239
- vertices: zod.z.tuple([NameZ, NameZ, NameZ])
15240
- }),
15241
- zod.z.object({
15242
- name: NameZ,
15243
- kind: zod.z.literal("orthocenter"),
15244
- vertices: zod.z.tuple([NameZ, NameZ, NameZ])
15245
- }),
15246
- zod.z.object({
15247
- name: NameZ,
15248
- kind: zod.z.literal("intersection"),
15249
- ref1: NameZ,
15250
- ref2: NameZ,
15251
- branch: zod.z.union([zod.z.literal(0), zod.z.literal(1)]).optional()
15252
- })
15253
- ]);
15254
- var DslShape = zod.z.discriminatedUnion("kind", [
15255
- zod.z.object({
15256
- name: NameZ,
15257
- kind: zod.z.literal("segment"),
15258
- p1: NameZ,
15259
- p2: NameZ
15260
- }),
15261
- zod.z.object({
15262
- name: NameZ,
15263
- kind: zod.z.literal("line"),
15264
- p1: NameZ,
15265
- p2: NameZ
15266
- }),
15267
- zod.z.object({
15268
- name: NameZ,
15269
- kind: zod.z.literal("ray"),
15270
- origin: NameZ,
15271
- through: NameZ
15272
- }),
15273
- zod.z.object({
15274
- name: NameZ,
15275
- kind: zod.z.literal("polygon"),
15276
- vertices: zod.z.array(NameZ).min(3)
15277
- }),
15278
- // Line constructions
15279
- zod.z.object({
15280
- name: NameZ,
15281
- kind: zod.z.literal("perpendicular"),
15282
- throughPoint: NameZ,
15283
- toLine: NameZ
15284
- }),
15285
- zod.z.object({
15286
- name: NameZ,
15287
- kind: zod.z.literal("parallel"),
15288
- throughPoint: NameZ,
15289
- toLine: NameZ
15290
- }),
15291
- zod.z.object({
15292
- name: NameZ,
15293
- kind: zod.z.literal("perpBisector"),
15294
- p1: NameZ,
15295
- p2: NameZ
15296
- }),
15297
- zod.z.object({
15298
- name: NameZ,
15299
- kind: zod.z.literal("angleBisector"),
15300
- p1: NameZ,
15301
- vertex: NameZ,
15302
- p2: NameZ
15303
- }),
15304
- zod.z.object({
15305
- name: NameZ,
15306
- kind: zod.z.literal("tangent"),
15307
- throughPoint: NameZ,
15308
- toCircle: NameZ,
15309
- branch: zod.z.union([zod.z.literal(0), zod.z.literal(1), zod.z.literal("on")]).optional()
15310
- }),
15311
- // Circle constructions
15312
- zod.z.object({
15313
- name: NameZ,
15314
- kind: zod.z.literal("circleCP"),
15315
- center: NameZ,
15316
- surfacePoint: NameZ
15317
- }),
15318
- zod.z.object({
15319
- name: NameZ,
15320
- kind: zod.z.literal("circle3"),
15321
- p1: NameZ,
15322
- p2: NameZ,
15323
- p3: NameZ
15324
- })
15325
- ]);
15326
- var DslInput = zod.z.object({
15327
- version: zod.z.literal(1),
15328
- points: zod.z.array(DslPoint),
15329
- shapes: zod.z.array(DslShape).default([])
15330
- });
15331
-
15332
- // src/stamps/geometry-2d/dsl/transpile.ts
15333
- init_types();
15334
-
15335
- // src/stamps/geometry-2d/dsl/transpile/errors.ts
15336
- function mkError(code, message, opts) {
15337
- return { code, message, path: opts?.path, hint: opts?.hint };
15338
- }
15339
-
15340
- // src/stamps/geometry-2d/dsl/transpile/symbols.ts
15341
- function buildSymbols(dsl) {
15342
- const symbols = /* @__PURE__ */ new Map();
15343
- const errors = [];
15344
- let counter = 0;
15345
- for (const p of dsl.points) {
15346
- if (symbols.has(p.name)) {
15347
- errors.push(mkError("DUPLICATE_NAME", `T\xEAn tr\xF9ng: "${p.name}"`, { path: [p.name] }));
15348
- continue;
15349
- }
15350
- symbols.set(p.name, { name: p.name, role: "point", entity: p, index: counter++ });
15351
- }
15352
- for (const s of dsl.shapes) {
15353
- if (symbols.has(s.name)) {
15354
- errors.push(mkError("DUPLICATE_NAME", `T\xEAn tr\xF9ng: "${s.name}"`, { path: [s.name] }));
15355
- continue;
15356
- }
15357
- symbols.set(s.name, { name: s.name, role: "shape", entity: s, index: counter++ });
15358
- }
15359
- return { symbols, errors };
15360
- }
15361
-
15362
- // src/stamps/geometry-2d/dsl/transpile/refs.ts
15363
- function isPointLike(sym) {
15364
- return !!sym && sym.role === "point";
15365
- }
15366
- var LINE_LIKE_SHAPE_KINDS = /* @__PURE__ */ new Set([
15367
- "line",
15368
- "segment",
15369
- "ray",
15370
- "perpendicular",
15371
- "parallel",
15372
- "perpBisector",
15373
- "angleBisector",
15374
- "tangent"
15375
- ]);
15376
- var CIRCLE_KINDS = /* @__PURE__ */ new Set(["circleCP", "circle3"]);
15377
- function isLineLike(sym) {
15378
- if (!sym || sym.role !== "shape") return false;
15379
- return LINE_LIKE_SHAPE_KINDS.has(sym.entity.kind);
15380
- }
15381
- function isCircleLike(sym) {
15382
- if (!sym || sym.role !== "shape") return false;
15383
- return CIRCLE_KINDS.has(sym.entity.kind);
15384
- }
15385
- function isSegmentExact(sym) {
15386
- return !!sym && sym.role === "shape" && sym.entity.kind === "segment";
15387
- }
15388
- function validateRefs(dsl, symbols) {
15389
- const errors = [];
15390
- const check = (owner, field, refName, predicate, expected) => {
15391
- const sym = symbols.get(refName);
15392
- if (!sym) {
15393
- errors.push(mkError(
15394
- "UNKNOWN_REF",
15395
- `${owner}.${field} tham chi\u1EBFu "${refName}" kh\xF4ng t\u1ED3n t\u1EA1i`,
15396
- { path: [owner, field] }
15397
- ));
15398
- return;
15399
- }
15400
- if (!predicate(sym)) {
15401
- errors.push(mkError(
15402
- "KIND_MISMATCH",
15403
- `${owner}.${field}="${refName}" sai ki\u1EC3u (c\u1EA7n ${expected}, g\u1EB7p ${sym.role === "point" ? "point" : sym.entity.kind})`,
15404
- { path: [owner, field] }
15405
- ));
15406
- }
15407
- };
15408
- for (const p of dsl.points) {
15409
- switch (p.kind) {
15410
- case "free":
15411
- break;
15412
- case "midpoint":
15413
- check(p.name, "p1", p.p1, isPointLike, "point");
15414
- check(p.name, "p2", p.p2, isPointLike, "point");
15415
- break;
15416
- case "onSegment":
15417
- check(p.name, "segmentId", p.segmentId, isSegmentExact, "segment");
15418
- break;
15419
- case "onLine":
15420
- check(p.name, "lineId", p.lineId, isLineLike, "line-like");
15421
- break;
15422
- case "onCircle":
15423
- check(p.name, "circleId", p.circleId, isCircleLike, "circle");
15424
- break;
15425
- case "perpFoot":
15426
- check(p.name, "from", p.from, isPointLike, "point");
15427
- check(p.name, "onLine", p.onLine, isLineLike, "line-like");
15428
- break;
15429
- case "circumcenter":
15430
- case "incenter":
15431
- case "centroid":
15432
- case "orthocenter":
15433
- for (let i = 0; i < 3; i++) {
15434
- check(p.name, `vertices[${i}]`, p.vertices[i], isPointLike, "point");
15435
- }
15436
- break;
15437
- case "intersection": {
15438
- const refPredicate = (s) => isLineLike(s) || isCircleLike(s);
15439
- check(p.name, "ref1", p.ref1, refPredicate, "line-like ho\u1EB7c circle");
15440
- check(p.name, "ref2", p.ref2, refPredicate, "line-like ho\u1EB7c circle");
15441
- break;
15442
- }
15443
- }
15444
- }
15445
- for (const s of dsl.shapes) {
15446
- switch (s.kind) {
15447
- case "segment":
15448
- case "line":
15449
- check(s.name, "p1", s.p1, isPointLike, "point");
15450
- check(s.name, "p2", s.p2, isPointLike, "point");
15451
- break;
15452
- case "ray":
15453
- check(s.name, "origin", s.origin, isPointLike, "point");
15454
- check(s.name, "through", s.through, isPointLike, "point");
15455
- break;
15456
- case "polygon":
15457
- s.vertices.forEach((v, i) => check(s.name, `vertices[${i}]`, v, isPointLike, "point"));
15458
- break;
15459
- case "perpendicular":
15460
- case "parallel":
15461
- check(s.name, "throughPoint", s.throughPoint, isPointLike, "point");
15462
- check(s.name, "toLine", s.toLine, isLineLike, "line-like");
15463
- break;
15464
- case "perpBisector":
15465
- check(s.name, "p1", s.p1, isPointLike, "point");
15466
- check(s.name, "p2", s.p2, isPointLike, "point");
15467
- break;
15468
- case "angleBisector":
15469
- check(s.name, "p1", s.p1, isPointLike, "point");
15470
- check(s.name, "vertex", s.vertex, isPointLike, "point");
15471
- check(s.name, "p2", s.p2, isPointLike, "point");
15472
- break;
15473
- case "tangent":
15474
- check(s.name, "throughPoint", s.throughPoint, isPointLike, "point");
15475
- check(s.name, "toCircle", s.toCircle, isCircleLike, "circle");
15476
- break;
15477
- case "circleCP":
15478
- check(s.name, "center", s.center, isPointLike, "point");
15479
- check(s.name, "surfacePoint", s.surfacePoint, isPointLike, "point");
15480
- break;
15481
- case "circle3":
15482
- check(s.name, "p1", s.p1, isPointLike, "point");
15483
- check(s.name, "p2", s.p2, isPointLike, "point");
15484
- check(s.name, "p3", s.p3, isPointLike, "point");
15485
- break;
15486
- }
15487
- }
15488
- return { errors };
15489
- }
15490
- function collectRefs(entity) {
15491
- if ("kind" in entity) {
15492
- switch (entity.kind) {
15493
- case "free":
15494
- return [];
15495
- case "midpoint":
15496
- return [entity.p1, entity.p2];
15497
- case "onSegment":
15498
- return [entity.segmentId];
15499
- case "onLine":
15500
- return [entity.lineId];
15501
- case "onCircle":
15502
- return [entity.circleId];
15503
- case "perpFoot":
15504
- return [entity.from, entity.onLine];
15505
- case "circumcenter":
15506
- case "incenter":
15507
- case "centroid":
15508
- case "orthocenter":
15509
- return [...entity.vertices];
15510
- case "intersection":
15511
- return [entity.ref1, entity.ref2];
15512
- case "segment":
15513
- case "line":
15514
- return [entity.p1, entity.p2];
15515
- case "ray":
15516
- return [entity.origin, entity.through];
15517
- case "polygon":
15518
- return [...entity.vertices];
15519
- case "perpendicular":
15520
- case "parallel":
15521
- return [entity.throughPoint, entity.toLine];
15522
- case "perpBisector":
15523
- return [entity.p1, entity.p2];
15524
- case "angleBisector":
15525
- return [entity.p1, entity.vertex, entity.p2];
15526
- case "tangent":
15527
- return [entity.throughPoint, entity.toCircle];
15528
- case "circleCP":
15529
- return [entity.center, entity.surfacePoint];
15530
- case "circle3":
15531
- return [entity.p1, entity.p2, entity.p3];
15532
- }
15533
- }
15534
- return [];
15535
- }
15536
-
15537
- // src/stamps/geometry-2d/dsl/transpile/cycles.ts
15538
- function detectCycles(symbols) {
15539
- const color = /* @__PURE__ */ new Map();
15540
- const parent = /* @__PURE__ */ new Map();
15541
- const errors = [];
15542
- const reportedCycles = /* @__PURE__ */ new Set();
15543
- for (const name of symbols.keys()) color.set(name, "white");
15544
- function reportCycle(start, hit) {
15545
- const chain = [start];
15546
- let cur = parent.get(start);
15547
- while (cur && cur !== hit && chain.length < symbols.size + 2) {
15548
- chain.push(cur);
15549
- cur = parent.get(cur);
15550
- }
15551
- chain.push(hit);
15552
- const minIdx = chain.indexOf(chain.reduce((a, b) => a < b ? a : b));
15553
- const rotated = [...chain.slice(minIdx), ...chain.slice(0, minIdx)];
15554
- const key = rotated.join("\u2192");
15555
- if (reportedCycles.has(key)) return;
15556
- reportedCycles.add(key);
15557
- errors.push(mkError(
15558
- "CYCLE",
15559
- `Ph\u1EE5 thu\u1ED9c v\xF2ng: ${chain.reverse().join(" \u2192 ")}`,
15560
- { path: [...chain], hint: "Ki\u1EC3m tra l\u1EA1i quan h\u1EC7 midpoint/perpFoot/intersection." }
15561
- ));
15562
- }
15563
- function dfs(name) {
15564
- color.set(name, "gray");
15565
- const sym = symbols.get(name);
15566
- if (sym) {
15567
- for (const ref of collectRefs(sym.entity)) {
15568
- if (!symbols.has(ref)) continue;
15569
- const c = color.get(ref);
15570
- if (c === "gray") {
15571
- reportCycle(name, ref);
15572
- continue;
15573
- }
15574
- if (c === "white") {
15575
- parent.set(ref, name);
15576
- dfs(ref);
15577
- }
15578
- }
15579
- }
15580
- color.set(name, "black");
15581
- }
15582
- for (const name of symbols.keys()) {
15583
- if (color.get(name) === "white") {
15584
- parent.set(name, null);
15585
- dfs(name);
15586
- }
15587
- }
15588
- return { errors };
15589
- }
15590
-
15591
- // src/stamps/geometry-2d/dsl/transpile/ids.ts
15592
- function prefixFor(sym) {
15593
- if (sym.role === "point") {
15594
- const p = sym.entity;
15595
- return p.kind === "intersection" ? "i" : "p";
15596
- }
15597
- const s = sym.entity;
15598
- switch (s.kind) {
15599
- case "segment":
15600
- return "s";
15601
- case "ray":
15602
- return "r";
15603
- case "polygon":
15604
- return "poly";
15605
- case "circleCP":
15606
- case "circle3":
15607
- return "c";
15608
- // line + 5 line-constructions all share 'l'
15609
- case "line":
15610
- case "perpendicular":
15611
- case "parallel":
15612
- case "perpBisector":
15613
- case "angleBisector":
15614
- case "tangent":
15615
- return "l";
15616
- }
15617
- }
15618
- function assignIds(symbols) {
15619
- const counters = { p: 0, i: 0, s: 0, l: 0, r: 0, poly: 0, c: 0 };
15620
- const ids = /* @__PURE__ */ new Map();
15621
- for (const [name, sym] of symbols.entries()) {
15622
- const prefix = prefixFor(sym);
15623
- counters[prefix] += 1;
15624
- ids.set(name, `${prefix}${counters[prefix]}`);
15625
- }
15626
- return ids;
15627
- }
15628
-
15629
- // src/stamps/geometry-2d/dsl/transpile/emitPoint.ts
15630
- function resolveId(ids, name) {
15631
- const id = ids.get(name);
15632
- if (!id) throw new Error(`emitPoint: id not assigned for "${name}"`);
15633
- return id;
15634
- }
15635
- function emitPoint(p, ids, kindHints) {
15636
- const baseId = resolveId(ids, p.name);
15637
- const baseFields = {
15638
- label: p.name,
15639
- visible: true,
15640
- locked: false,
15641
- layer: "default",
15642
- schemaVersion: 1
15643
- };
15644
- if (p.kind === "intersection") {
15645
- const r1Hint = kindHints.get(p.ref1);
15646
- const r2Hint = kindHints.get(p.ref2);
15647
- const r1IsCircle = r1Hint === "circle";
15648
- const r2IsCircle = r2Hint === "circle";
15649
- let intersectKind;
15650
- if (r1IsCircle && r2IsCircle) intersectKind = "circleCircle";
15651
- else if (r1IsCircle || r2IsCircle) intersectKind = "lineCircle";
15652
- else intersectKind = "lineLine";
15653
- const attrs = {
15654
- kind: intersectKind,
15655
- ref1: resolveId(ids, p.ref1),
15656
- ref2: resolveId(ids, p.ref2)
15657
- };
15658
- if (intersectKind !== "lineLine") {
15659
- attrs.branch = p.branch ?? 0;
15660
- }
15661
- return {
15662
- id: baseId,
15663
- kind: "intersection",
15664
- ...baseFields,
15665
- attrs
15666
- };
15667
- }
15668
- let constraint;
15669
- switch (p.kind) {
15670
- case "free":
15671
- constraint = { kind: "free", x: p.x, y: p.y };
15672
- break;
15673
- case "midpoint":
15674
- constraint = { kind: "midpoint", p1: resolveId(ids, p.p1), p2: resolveId(ids, p.p2) };
15675
- break;
15676
- case "onSegment":
15677
- constraint = { kind: "onSegment", segmentId: resolveId(ids, p.segmentId), t: p.t };
15678
- break;
15679
- case "onLine":
15680
- constraint = { kind: "onLine", lineId: resolveId(ids, p.lineId), t: p.t };
15681
- break;
15682
- case "onCircle":
15683
- constraint = { kind: "onCircle", circleId: resolveId(ids, p.circleId), theta: p.theta };
15684
- break;
15685
- case "perpFoot":
15686
- constraint = { kind: "perpFoot", from: resolveId(ids, p.from), onLine: resolveId(ids, p.onLine) };
15687
- break;
15688
- case "circumcenter":
15689
- case "incenter":
15690
- case "centroid":
15691
- case "orthocenter":
15692
- constraint = {
15693
- kind: p.kind,
15694
- vertices: [resolveId(ids, p.vertices[0]), resolveId(ids, p.vertices[1]), resolveId(ids, p.vertices[2])]
15695
- };
15696
- break;
15697
- }
15698
- return {
15699
- id: baseId,
15700
- kind: "point",
15701
- ...baseFields,
15702
- attrs: { constraint }
15703
- };
15704
- }
15705
-
15706
- // src/stamps/geometry-2d/dsl/transpile/emitShape.ts
15707
- function r(ids, name) {
15708
- const id = ids.get(name);
15709
- if (!id) throw new Error(`emitShape: id not assigned for "${name}"`);
15710
- return id;
15711
- }
15712
- function emitShape(s, ids) {
15713
- const id = r(ids, s.name);
15714
- const base = {
15715
- label: s.name,
15716
- visible: true,
15717
- locked: false,
15718
- layer: "default",
15719
- schemaVersion: 1
15720
- };
15721
- switch (s.kind) {
15722
- case "segment":
15723
- return { id, kind: "segment", ...base, attrs: { p1: r(ids, s.p1), p2: r(ids, s.p2) } };
15724
- case "line":
15725
- return { id, kind: "line", ...base, attrs: { p1: r(ids, s.p1), p2: r(ids, s.p2) } };
15726
- case "ray":
15727
- return { id, kind: "ray", ...base, attrs: { origin: r(ids, s.origin), through: r(ids, s.through) } };
15728
- case "polygon":
15729
- return { id, kind: "polygon", ...base, attrs: { vertices: s.vertices.map((v) => r(ids, v)) } };
15730
- case "perpendicular":
15731
- case "parallel":
15732
- return {
15733
- id,
15734
- kind: "line",
15735
- ...base,
15736
- attrs: { construction: { kind: s.kind, throughPoint: r(ids, s.throughPoint), toLine: r(ids, s.toLine) } }
15737
- };
15738
- case "perpBisector":
15739
- return {
15740
- id,
15741
- kind: "line",
15742
- ...base,
15743
- attrs: { construction: { kind: "perpBisector", p1: r(ids, s.p1), p2: r(ids, s.p2) } }
15744
- };
15745
- case "angleBisector":
15746
- return {
15747
- id,
15748
- kind: "line",
15749
- ...base,
15750
- attrs: { construction: { kind: "angleBisector", p1: r(ids, s.p1), vertex: r(ids, s.vertex), p2: r(ids, s.p2) } }
15751
- };
15752
- case "tangent": {
15753
- const construction = {
15754
- kind: "tangent",
15755
- throughPoint: r(ids, s.throughPoint),
15756
- toCircle: r(ids, s.toCircle)
15757
- };
15758
- if (s.branch !== void 0) construction.branch = s.branch;
15759
- return { id, kind: "line", ...base, attrs: { construction } };
15760
- }
15761
- case "circleCP":
15762
- return {
15763
- id,
15764
- kind: "circle",
15765
- ...base,
15766
- attrs: { center: r(ids, s.center), surfacePoint: r(ids, s.surfacePoint) }
15767
- };
15768
- case "circle3":
15769
- return {
15770
- id,
15771
- kind: "circle",
15772
- ...base,
15773
- attrs: { construction: { kind: "circumscribed", p1: r(ids, s.p1), p2: r(ids, s.p2), p3: r(ids, s.p3) } }
15774
- };
15775
- }
15776
- }
15777
-
15778
- // src/stamps/geometry-2d/dsl/transpile.ts
15779
- function hintOf(entity) {
15780
- if ("kind" in entity) {
15781
- switch (entity.kind) {
15782
- case "free":
15783
- case "midpoint":
15784
- case "onSegment":
15785
- case "onLine":
15786
- case "onCircle":
15787
- case "perpFoot":
15788
- case "circumcenter":
15789
- case "incenter":
15790
- case "centroid":
15791
- case "orthocenter":
15792
- case "intersection":
15793
- return "point";
15794
- case "segment":
15795
- return "segment";
15796
- case "line":
15797
- return "line";
15798
- case "ray":
15799
- return "ray";
15800
- case "polygon":
15801
- return "point";
15802
- // not used as ref target in MVP
15803
- case "perpendicular":
15804
- case "parallel":
15805
- case "perpBisector":
15806
- case "angleBisector":
15807
- case "tangent":
15808
- return "lineConstruction";
15809
- case "circleCP":
15810
- case "circle3":
15811
- return "circle";
15812
- }
15813
- }
15814
- return "point";
15815
- }
15816
- function transpile(dslRaw) {
15817
- const parsed = DslInput.safeParse(dslRaw);
15818
- if (!parsed.success) {
15819
- const errors = parsed.error.issues.map(
15820
- (iss) => mkError("SCHEMA", iss.message, { path: iss.path.map(String) })
15821
- );
15822
- return { ok: false, errors };
15823
- }
15824
- const dsl = parsed.data;
15825
- const { symbols, errors: dupErrors } = buildSymbols(dsl);
15826
- const { errors: refErrors } = validateRefs(dsl, symbols);
15827
- const { errors: cycleErrors } = detectCycles(symbols);
15828
- const allErrors = [...dupErrors, ...refErrors, ...cycleErrors];
15829
- if (allErrors.length > 0) return { ok: false, errors: allErrors };
15830
- const ids = assignIds(symbols);
15831
- const kindHints = /* @__PURE__ */ new Map();
15832
- for (const [name, sym] of symbols.entries()) {
15833
- kindHints.set(name, hintOf(sym.entity));
15834
- }
15835
- const objects = {};
15836
- const order = [];
15837
- for (const p of dsl.points) {
15838
- const obj = emitPoint(p, ids, kindHints);
15839
- objects[obj.id] = obj;
15840
- order.push(obj.id);
15841
- }
15842
- for (const s of dsl.shapes) {
15843
- const obj = emitShape(s, ids);
15844
- objects[obj.id] = obj;
15845
- order.push(obj.id);
15846
- }
15847
- const empty = createEmptyState("2d");
15848
- const state = {
15849
- objects,
15850
- order,
15851
- counter: order.length,
15852
- meta: empty.meta
15853
- };
15854
- return { ok: true, state };
15855
- }
15856
- async function callProvider(args) {
15857
- const client = new Anthropic__default.default({ apiKey: args.apiKey });
15858
- const resp = await client.messages.create(
15859
- {
15860
- model: args.model,
15861
- max_tokens: args.maxTokens,
15862
- system: args.system,
15863
- tools: args.tools,
15864
- tool_choice: args.toolChoice,
15865
- messages: args.messages
15866
- },
15867
- args.signal ? { signal: args.signal } : void 0
15868
- );
15869
- return resp;
15870
- }
15871
-
15872
- // src/stamps/geometry-2d/dsl/fixtures/triangle-equilateral.ts
15873
- var fixture = {
15874
- problem: "Cho tam gi\xE1c \u0111\u1EC1u ABC c\u1EA1nh 4.",
15875
- dsl: {
15876
- version: 1,
15877
- points: [
15878
- { name: "A", kind: "free", x: 0, y: 0 },
15879
- { name: "B", kind: "free", x: 4, y: 0 },
15880
- { name: "C", kind: "free", x: 2, y: 3.464 }
15881
- ],
15882
- shapes: [
15883
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
15884
- ]
15885
- }
15886
- };
15887
-
15888
- // src/stamps/geometry-2d/dsl/fixtures/triangle-median.ts
15889
- var fixture2 = {
15890
- problem: "Tam gi\xE1c ABC, M l\xE0 trung \u0111i\u1EC3m BC. V\u1EBD AM.",
15891
- dsl: {
15892
- version: 1,
15893
- points: [
15894
- { name: "A", kind: "free", x: 0, y: 3 },
15895
- { name: "B", kind: "free", x: -2, y: 0 },
15896
- { name: "C", kind: "free", x: 3, y: 0 },
15897
- { name: "M", kind: "midpoint", p1: "B", p2: "C" }
15898
- ],
15899
- shapes: [
15900
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
15901
- { name: "AM", kind: "segment", p1: "A", p2: "M" }
15902
- ]
15903
- }
15904
- };
15905
-
15906
- // src/stamps/geometry-2d/dsl/fixtures/triangle-altitude.ts
15907
- var fixture3 = {
15908
- problem: "Tam gi\xE1c ABC, AH l\xE0 \u0111\u01B0\u1EDDng cao xu\u1ED1ng BC.",
15909
- dsl: {
15910
- version: 1,
15911
- points: [
15912
- { name: "A", kind: "free", x: 1, y: 3 },
15913
- { name: "B", kind: "free", x: -2, y: 0 },
15914
- { name: "C", kind: "free", x: 3, y: 0 },
15915
- { name: "H", kind: "perpFoot", from: "A", onLine: "BC" }
15916
- ],
15917
- shapes: [
15918
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
15919
- { name: "BC", kind: "segment", p1: "B", p2: "C" },
15920
- { name: "AH", kind: "segment", p1: "A", p2: "H" }
15921
- ]
15922
- }
15923
- };
15924
-
15925
- // src/stamps/geometry-2d/dsl/fixtures/triangle-centroid.ts
15926
- var fixture4 = {
15927
- problem: "Tam gi\xE1c ABC, G l\xE0 tr\u1ECDng t\xE2m.",
15928
- dsl: {
15929
- version: 1,
15930
- points: [
15931
- { name: "A", kind: "free", x: 0, y: 3 },
15932
- { name: "B", kind: "free", x: -2, y: 0 },
15933
- { name: "C", kind: "free", x: 3, y: 0 },
15934
- { name: "G", kind: "centroid", vertices: ["A", "B", "C"] }
15935
- ],
15936
- shapes: [
15937
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
15938
- ]
15939
- }
15940
- };
15941
-
15942
- // src/stamps/geometry-2d/dsl/fixtures/triangle-orthocenter.ts
15943
- var fixture5 = {
15944
- problem: "Tam gi\xE1c ABC, H l\xE0 tr\u1EF1c t\xE2m.",
15945
- dsl: {
15946
- version: 1,
15947
- points: [
15948
- { name: "A", kind: "free", x: 0, y: 3 },
15949
- { name: "B", kind: "free", x: -2, y: 0 },
15950
- { name: "C", kind: "free", x: 3, y: 0 },
15951
- { name: "H", kind: "orthocenter", vertices: ["A", "B", "C"] }
15952
- ],
15953
- shapes: [
15954
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
15955
- ]
15956
- }
15957
- };
15958
-
15959
- // src/stamps/geometry-2d/dsl/fixtures/triangle-circumcircle.ts
15960
- var fixture6 = {
15961
- problem: "Tam gi\xE1c ABC n\u1ED9i ti\u1EBFp \u0111\u01B0\u1EDDng tr\xF2n t\xE2m O.",
15962
- dsl: {
15963
- version: 1,
15964
- points: [
15965
- { name: "A", kind: "free", x: 0, y: 3 },
15966
- { name: "B", kind: "free", x: -2, y: 0 },
15967
- { name: "C", kind: "free", x: 3, y: 0 },
15968
- { name: "O", kind: "circumcenter", vertices: ["A", "B", "C"] }
15969
- ],
15970
- shapes: [
15971
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
15972
- { name: "k", kind: "circle3", p1: "A", p2: "B", p3: "C" }
15973
- ]
15974
- }
15975
- };
15976
-
15977
- // src/stamps/geometry-2d/dsl/fixtures/triangle-incircle.ts
15978
- var fixture7 = {
15979
- problem: "Tam gi\xE1c ABC, I l\xE0 t\xE2m n\u1ED9i ti\u1EBFp, \u0111\u01B0\u1EDDng tr\xF2n (I) ti\u1EBFp x\xFAc BC t\u1EA1i D.",
15980
- dsl: {
15981
- version: 1,
15982
- points: [
15983
- { name: "A", kind: "free", x: 0, y: 3 },
15984
- { name: "B", kind: "free", x: -2, y: 0 },
15985
- { name: "C", kind: "free", x: 3, y: 0 },
15986
- { name: "I", kind: "incenter", vertices: ["A", "B", "C"] },
15987
- { name: "D", kind: "perpFoot", from: "I", onLine: "BC" }
15988
- ],
15989
- shapes: [
15990
- { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
15991
- { name: "BC", kind: "segment", p1: "B", p2: "C" },
15992
- { name: "incircle", kind: "circleCP", center: "I", surfacePoint: "D" }
15993
- ]
15994
- }
15995
- };
15996
-
15997
- // src/stamps/geometry-2d/dsl/fixtures/parallelogram.ts
15998
- var fixture8 = {
15999
- problem: "H\xECnh b\xECnh h\xE0nh ABCD, hai \u0111\u01B0\u1EDDng ch\xE9o AC, BD c\u1EAFt nhau t\u1EA1i O.",
16000
- dsl: {
16001
- version: 1,
16002
- points: [
16003
- { name: "A", kind: "free", x: 0, y: 0 },
16004
- { name: "B", kind: "free", x: 4, y: 0 },
16005
- { name: "C", kind: "free", x: 5, y: 2 },
16006
- { name: "D", kind: "free", x: 1, y: 2 },
16007
- { name: "O", kind: "intersection", ref1: "AC", ref2: "BD" }
16008
- ],
16009
- shapes: [
16010
- { name: "ABCD", kind: "polygon", vertices: ["A", "B", "C", "D"] },
16011
- { name: "AC", kind: "segment", p1: "A", p2: "C" },
16012
- { name: "BD", kind: "segment", p1: "B", p2: "D" }
16013
- ]
16014
- }
16015
- };
16016
-
16017
- // src/stamps/geometry-2d/dsl/fixtures/two-circles-intersect.ts
16018
- var fixture9 = {
16019
- problem: "Hai \u0111\u01B0\u1EDDng tr\xF2n (O\u2081), (O\u2082) c\u1EAFt nhau t\u1EA1i P, Q.",
16020
- dsl: {
16021
- version: 1,
16022
- points: [
16023
- { name: "O1", kind: "free", x: 0, y: 0 },
16024
- { name: "A1", kind: "free", x: 2, y: 0 },
16025
- { name: "O2", kind: "free", x: 3, y: 0 },
16026
- { name: "A2", kind: "free", x: 5, y: 0 },
16027
- { name: "P", kind: "intersection", ref1: "k1", ref2: "k2", branch: 0 },
16028
- { name: "Q", kind: "intersection", ref1: "k1", ref2: "k2", branch: 1 }
16029
- ],
16030
- shapes: [
16031
- { name: "k1", kind: "circleCP", center: "O1", surfacePoint: "A1" },
16032
- { name: "k2", kind: "circleCP", center: "O2", surfacePoint: "A2" }
16033
- ]
16034
- }
16035
- };
16036
-
16037
- // src/stamps/geometry-2d/ai/prompt.ts
16038
- var FIXTURES = [fixture, fixture2, fixture3, fixture4, fixture5, fixture6, fixture7, fixture8, fixture9];
16039
- function buildSystemPrompt() {
16040
- const examples = FIXTURES.map(
16041
- (f, i) => `### V\xED d\u1EE5 ${i + 1}
16042
- **\u0110\u1EC1:** ${f.problem}
16043
- **DSL:**
16044
- \`\`\`json
16045
- ${JSON.stringify(f.dsl, null, 2)}
16046
- \`\`\``
16047
- ).join("\n\n");
16048
- return `B\u1EA1n l\xE0 tr\u1EE3 l\xFD v\u1EBD h\xECnh h\u1ECDc 2D cho h\u1ECDc sinh THCS v\xE0 l\u1EDBp 10 Vi\u1EC7t Nam.
16049
-
16050
- ## Nhi\u1EC7m v\u1EE5
16051
- \u0110\u1ECDc \u0111\u1EC1 b\xE0i ti\u1EBFng Vi\u1EC7t \u2192 emit DSL JSON m\xF4 t\u1EA3 h\xECnh. H\u1EC7 th\u1ED1ng s\u1EBD render h\xECnh t\u1EEB DSL.
16052
-
16053
- ## Quy t\u1EAFc
16054
- 1. D\xF9ng tool \`build_figure\` khi v\u1EBD \u0111\u01B0\u1EE3c. D\xF9ng tool \`refuse\` khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c ho\u1EB7c \u0111\u1EC1 ngo\xE0i ph\u1EA1m vi (3D, l\u01B0\u1EE3ng gi\xE1c, ph\xE9p bi\u1EBFn h\xECnh l\u1EDBp 11+, \u0111\u1EA1i s\u1ED1).
16055
- 2. \u01AFu ti\xEAn derived points (midpoint, perpFoot, circumcenter, ...) thay v\xEC t\u1EF1 compute to\u1EA1 \u0111\u1ED9.
16056
- 3. Anchor (free) ch\u1EC9 d\xF9ng cho \u0111i\u1EC3m g\u1ED1c (th\u01B0\u1EDDng A, B, C c\u1EE7a tam gi\xE1c). \u0110\u1EB7t coord h\u1EE3p l\xFD quanh g\u1ED1c (-5..5).
16057
- 4. M\u1ECDi \u0111i\u1EC3m + h\xECnh ph\u1EA3i c\xF3 \`name\` (label "A", "M", "O\u2081", ...). Tham chi\u1EBFu b\u1EB1ng name, kh\xF4ng ph\u1EA3i id.
16058
- 5. Tam gi\xE1c: emit c\u1EA3 \`polygon\` (v\u1EBD vi\u1EC1n) + segment/\u0111\u01B0\u1EDDng ph\u1EE5 ri\xEAng n\u1EBFu \u0111\u1EC1 y\xEAu c\u1EA7u (\u0111\u01B0\u1EDDng cao, trung tuy\u1EBFn).
16059
- 6. \u0110\u01B0\u1EDDng tr\xF2n (O; R) cho tr\u01B0\u1EDBc b\xE1n k\xEDnh s\u1ED1: emit anchor helper tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n r\u1ED3i d\xF9ng \`circleCP\` (DSL kh\xF4ng h\u1ED7 tr\u1EE3 radius numeric tr\u1EF1c ti\u1EBFp).
16060
- 7. N\u1EBFu \u0111\u1EC1 m\u01A1 h\u1ED3: ch\u1ECDn case ph\u1ED5 bi\u1EBFn nh\u1EA5t, kh\xF4ng h\u1ECFi l\u1EA1i.
16061
-
16062
- ## Primitives s\u1EB5n c\xF3
16063
- **Points:** free, midpoint, onSegment, onLine, onCircle, perpFoot, circumcenter, incenter, centroid, orthocenter, intersection
16064
- **Shapes:** segment, line, ray, polygon, perpendicular, parallel, perpBisector, angleBisector, tangent, circleCP, circle3
16065
-
16066
- ## 9 v\xED d\u1EE5
16067
- ${examples}
16068
-
16069
- ## Khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c
16070
- G\u1ECDi \`refuse\` v\u1EDBi \`reason\` ti\u1EBFng Vi\u1EC7t gi\u1EA3i th\xEDch c\u1EE5 th\u1EC3 (vd: "\u0110\u1EC1 thu\u1ED9c l\u1EDBp 11, ngo\xE0i ph\u1EA1m vi MVP" ho\u1EB7c "\u0110\u1EC1 kh\xF4ng r\xF5 v\u1ECB tr\xED \u0111i\u1EC3m M").`;
16071
- }
16072
- var BUILD_FIGURE_TOOL = {
16073
- name: "build_figure",
16074
- description: "V\u1EBD h\xECnh h\u1ECDc 2D theo \u0111\u1EC1 b\xE0i. Emit DSL JSON m\xF4 t\u1EA3 c\xE1c \u0111i\u1EC3m v\xE0 h\xECnh.",
16075
- input_schema: zodToJsonSchema.zodToJsonSchema(DslInput, {
16076
- target: "jsonSchema7",
16077
- $refStrategy: "none"
16078
- })
16079
- };
16080
- var RefuseInputZ = zod.z.object({
16081
- reason: zod.z.string().min(1).describe("L\xFD do kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c (ti\u1EBFng Vi\u1EC7t)")
16082
- });
16083
- var REFUSE_TOOL = {
16084
- name: "refuse",
16085
- description: "T\u1EEB ch\u1ED1i khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c ho\u1EB7c \u0111\u1EC1 ngo\xE0i ph\u1EA1m vi (3D, l\u01B0\u1EE3ng gi\xE1c, l\u1EDBp 11+).",
16086
- input_schema: zodToJsonSchema.zodToJsonSchema(RefuseInputZ, { target: "jsonSchema7" })
16087
- };
16088
- var TOOLS4 = [BUILD_FIGURE_TOOL, REFUSE_TOOL];
16089
-
16090
- // src/stamps/geometry-2d/ai/buildFigure.ts
16091
- var DEFAULT_MODEL = "claude-opus-4-7";
16092
- var DEFAULT_MAX_TOKENS = 8192;
16093
- function toUsage(u) {
16094
- return {
16095
- inputTokens: u.input_tokens,
16096
- outputTokens: u.output_tokens,
16097
- cacheReadTokens: u.cache_read_input_tokens ?? 0,
16098
- cacheCreationTokens: u.cache_creation_input_tokens ?? 0
16099
- };
16100
- }
16101
- async function generateFigure(problem, opts) {
16102
- if (!opts.apiKey) {
16103
- return { ok: false, reason: "api_error", message: "apiKey b\u1EAFt bu\u1ED9c" };
16104
- }
16105
- if (!problem || !problem.trim()) {
16106
- return { ok: false, reason: "api_error", message: "\u0110\u1EC1 b\xE0i r\u1ED7ng" };
16107
- }
16108
- const systemText = buildSystemPrompt();
16109
- const enableCaching = opts.enableCaching !== false;
16110
- const systemBlock = enableCaching ? { type: "text", text: systemText, cache_control: { type: "ephemeral" } } : { type: "text", text: systemText };
16111
- let response;
16112
- try {
16113
- response = await callProvider({
16114
- apiKey: opts.apiKey,
16115
- model: opts.model ?? DEFAULT_MODEL,
16116
- maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
16117
- system: [systemBlock],
16118
- tools: TOOLS4,
16119
- toolChoice: { type: "any" },
16120
- messages: [{ role: "user", content: problem }],
16121
- signal: opts.signal
16122
- });
16123
- } catch (e) {
16124
- const err = e;
16125
- return {
16126
- ok: false,
16127
- reason: "api_error",
16128
- message: err.message ?? "L\u1ED7i g\u1ECDi Claude API",
16129
- ...err.status !== void 0 ? { status: err.status } : {}
16130
- };
16131
- }
16132
- const usage = toUsage(response.usage);
16133
- const toolUse = response.content.find((c) => c.type === "tool_use");
16134
- if (!toolUse || toolUse.type !== "tool_use") {
16135
- const text = response.content.find((c) => c.type === "text");
16136
- const textStr = text?.type === "text" ? text.text : "(empty)";
16137
- return {
16138
- ok: false,
16139
- reason: "parse_error",
16140
- message: "AI kh\xF4ng g\u1ECDi tool n\xE0o. Response: " + textStr,
16141
- raw: response.content,
16142
- usage
16143
- };
16144
- }
16145
- if (toolUse.name === "refuse") {
16146
- const input = toolUse.input;
16147
- return {
16148
- ok: false,
16149
- reason: "refused",
16150
- message: input.reason ?? "AI t\u1EEB ch\u1ED1i kh\xF4ng n\xEAu l\xFD do",
16151
- usage
16152
- };
16153
- }
16154
- if (toolUse.name !== "build_figure") {
16155
- return {
16156
- ok: false,
16157
- reason: "parse_error",
16158
- message: `Tool kh\xF4ng x\xE1c \u0111\u1ECBnh: "${toolUse.name}"`,
16159
- raw: toolUse,
16160
- usage
16161
- };
16162
- }
16163
- const tResult = transpile(toolUse.input);
16164
- if (!tResult.ok) {
16165
- return {
16166
- ok: false,
16167
- reason: "transpile_error",
16168
- message: "DSL t\u1EEB AI kh\xF4ng h\u1EE3p l\u1EC7",
16169
- errors: tResult.errors,
16170
- dsl: toolUse.input,
16171
- usage
16172
- };
16173
- }
16174
- return {
16175
- ok: true,
16176
- state: tResult.state,
16177
- dsl: toolUse.input,
16178
- usage
16179
- };
16180
- }
16181
15182
 
16182
15183
  exports.ALL_STAMPS = ALL_STAMPS;
16183
15184
  exports.DEFAULT_STAMPS = DEFAULT_STAMPS;
@@ -16189,7 +15190,6 @@ exports.closePdfDocument = closePdfDocument;
16189
15190
  exports.configurePdfWorker = configurePdfWorker;
16190
15191
  exports.findCatalogEntry = findCatalogEntry;
16191
15192
  exports.findStampForCustomData = findStampForCustomData;
16192
- exports.generateFigure = generateFigure;
16193
15193
  exports.geometry3dStamp = geometry3dStamp;
16194
15194
  exports.geometryStamp = geometryStamp;
16195
15195
  exports.graph2dStamp = graph2dStamp;