@xom11/whiteboard 0.25.0 → 0.28.0

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 (93) hide show
  1. package/dist/{ExcalidrawWithMenus-WENZRYYE.mjs → ExcalidrawWithMenus-2QPPTXJM.mjs} +3 -2
  2. package/dist/ExcalidrawWithMenus-2QPPTXJM.mjs.map +1 -0
  3. package/dist/ai.d.mts +3035 -108
  4. package/dist/ai.d.ts +3035 -108
  5. package/dist/ai.js +6780 -788
  6. package/dist/ai.js.map +1 -1
  7. package/dist/ai.mjs +5140 -577
  8. package/dist/ai.mjs.map +1 -1
  9. package/dist/catalog.json +5 -5
  10. package/dist/{chunk-NDEZJKNY.mjs → chunk-5JM35CXV.mjs} +4 -4
  11. package/dist/{chunk-NDEZJKNY.mjs.map → chunk-5JM35CXV.mjs.map} +1 -1
  12. package/dist/{chunk-VNCCIV6O.mjs → chunk-AJAHD35N.mjs} +779 -9
  13. package/dist/chunk-AJAHD35N.mjs.map +1 -0
  14. package/dist/{chunk-M42TGYT6.mjs → chunk-BNBOIDO5.mjs} +3 -3
  15. package/dist/{chunk-M42TGYT6.mjs.map → chunk-BNBOIDO5.mjs.map} +1 -1
  16. package/dist/{chunk-ONBCUWVI.mjs → chunk-BU5KLO3P.mjs} +3 -3
  17. package/dist/{chunk-ONBCUWVI.mjs.map → chunk-BU5KLO3P.mjs.map} +1 -1
  18. package/dist/{chunk-CJBLJUWG.mjs → chunk-CXHNVYMD.mjs} +4 -4
  19. package/dist/{chunk-CJBLJUWG.mjs.map → chunk-CXHNVYMD.mjs.map} +1 -1
  20. package/dist/chunk-H22OZYTW.mjs +265 -0
  21. package/dist/chunk-H22OZYTW.mjs.map +1 -0
  22. package/dist/chunk-J5LGTIGS.mjs +10 -0
  23. package/dist/chunk-J5LGTIGS.mjs.map +1 -0
  24. package/dist/{chunk-TB4CL25L.mjs → chunk-OQIQNKPQ.mjs} +206 -66
  25. package/dist/chunk-OQIQNKPQ.mjs.map +1 -0
  26. package/dist/{chunk-SGFJLHHG.mjs → chunk-PPKHCRRE.mjs} +3 -3
  27. package/dist/{chunk-SGFJLHHG.mjs.map → chunk-PPKHCRRE.mjs.map} +1 -1
  28. package/dist/{chunk-YSJOVBCD.mjs → chunk-QCZVFEN4.mjs} +4 -4
  29. package/dist/{chunk-YSJOVBCD.mjs.map → chunk-QCZVFEN4.mjs.map} +1 -1
  30. package/dist/{chunk-ESVPQWHX.mjs → chunk-QRUAEXLR.mjs} +5 -5
  31. package/dist/{chunk-ESVPQWHX.mjs.map → chunk-QRUAEXLR.mjs.map} +1 -1
  32. package/dist/{chunk-AYSFWUPK.mjs → chunk-SZDAS7LK.mjs} +79 -2
  33. package/dist/chunk-SZDAS7LK.mjs.map +1 -0
  34. package/dist/chunk-T3SOHYB2.mjs +851 -0
  35. package/dist/chunk-T3SOHYB2.mjs.map +1 -0
  36. package/dist/{chunk-I24QOHPU.mjs → chunk-V3YJ6JFL.mjs} +3 -3
  37. package/dist/{chunk-I24QOHPU.mjs.map → chunk-V3YJ6JFL.mjs.map} +1 -1
  38. package/dist/{chunk-REIJZDVZ.mjs → chunk-ZTQBUKLJ.mjs} +960 -196
  39. package/dist/chunk-ZTQBUKLJ.mjs.map +1 -0
  40. package/dist/geometry-2d.d.mts +1 -1
  41. package/dist/geometry-2d.d.ts +1 -1
  42. package/dist/geometry-2d.js +5521 -1384
  43. package/dist/geometry-2d.js.map +1 -1
  44. package/dist/geometry-2d.mjs +5 -4
  45. package/dist/geometry-3d.d.mts +1 -1
  46. package/dist/geometry-3d.d.ts +1 -1
  47. package/dist/geometry-3d.js +1351 -252
  48. package/dist/geometry-3d.js.map +1 -1
  49. package/dist/geometry-3d.mjs +4 -3
  50. package/dist/graph-2d.d.mts +1 -1
  51. package/dist/graph-2d.d.ts +1 -1
  52. package/dist/graph-2d.js +1517 -341
  53. package/dist/graph-2d.js.map +1 -1
  54. package/dist/graph-2d.mjs +7 -6
  55. package/dist/handleExtractProblem-C-U5KluK.d.mts +158 -0
  56. package/dist/handleExtractProblem-C-U5KluK.d.ts +158 -0
  57. package/dist/{host-A64ITWVX.mjs → host-2ISGVO7O.mjs} +6 -5
  58. package/dist/host-2ISGVO7O.mjs.map +1 -0
  59. package/dist/{host-L7FMFZUW.mjs → host-4P766V4J.mjs} +1363 -463
  60. package/dist/host-4P766V4J.mjs.map +1 -0
  61. package/dist/{host-QK53UYMD.mjs → host-HOSJHQ5H.mjs} +10 -9
  62. package/dist/host-HOSJHQ5H.mjs.map +1 -0
  63. package/dist/{host-QS2EOTRJ.mjs → host-ZQCDAT6O.mjs} +3 -2
  64. package/dist/host-ZQCDAT6O.mjs.map +1 -0
  65. package/dist/index.d.mts +10 -4
  66. package/dist/index.d.ts +10 -4
  67. package/dist/index.js +5746 -1603
  68. package/dist/index.js.map +1 -1
  69. package/dist/index.mjs +26 -22
  70. package/dist/index.mjs.map +1 -1
  71. package/dist/latex.d.mts +1 -1
  72. package/dist/latex.d.ts +1 -1
  73. package/dist/latex.mjs +2 -1
  74. package/dist/render-ZX2O2IK7.mjs +10 -0
  75. package/dist/{render-3WTY7NZB.mjs.map → render-ZX2O2IK7.mjs.map} +1 -1
  76. package/dist/serialize-N4G6RFBB.mjs +9 -0
  77. package/dist/{serialize-SRJVKYUG.mjs.map → serialize-N4G6RFBB.mjs.map} +1 -1
  78. package/dist/{types-DWRyCa2m.d.ts → types-BHYC2Fiw.d.mts} +130 -1
  79. package/dist/{types-DWRyCa2m.d.mts → types-BHYC2Fiw.d.ts} +130 -1
  80. package/package.json +10 -1
  81. package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +0 -1
  82. package/dist/chunk-AYSFWUPK.mjs.map +0 -1
  83. package/dist/chunk-REIJZDVZ.mjs.map +0 -1
  84. package/dist/chunk-TB4CL25L.mjs.map +0 -1
  85. package/dist/chunk-VNCCIV6O.mjs.map +0 -1
  86. package/dist/chunk-VRHWDZ66.mjs +0 -96
  87. package/dist/chunk-VRHWDZ66.mjs.map +0 -1
  88. package/dist/host-A64ITWVX.mjs.map +0 -1
  89. package/dist/host-L7FMFZUW.mjs.map +0 -1
  90. package/dist/host-QK53UYMD.mjs.map +0 -1
  91. package/dist/host-QS2EOTRJ.mjs.map +0 -1
  92. package/dist/render-3WTY7NZB.mjs +0 -9
  93. package/dist/serialize-SRJVKYUG.mjs +0 -8
@@ -2,6 +2,79 @@
2
2
  import { getKind } from './chunk-B4NJJZFR.mjs';
3
3
  import { z } from 'zod';
4
4
 
5
+ // src/stamps/geometry-2d/ai/vision/preprocess.ts
6
+ var MAX_EDGE_PX = 2048;
7
+ var MAX_RAW_BYTES = 10 * 1024 * 1024;
8
+ var MAX_ENCODED_BYTES = 3 * 1024 * 1024;
9
+ var ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp"];
10
+ function inferMediaType(file) {
11
+ const t = file.type.toLowerCase();
12
+ if (ALLOWED_TYPES.includes(t)) return t;
13
+ return null;
14
+ }
15
+ function validateFile(file) {
16
+ const mt = inferMediaType(file);
17
+ if (mt == null) {
18
+ return {
19
+ ok: false,
20
+ code: "invalid-format",
21
+ message: "Ch\u1EC9 h\u1ED7 tr\u1EE3 PNG, JPEG, WEBP."
22
+ };
23
+ }
24
+ if (file.size > MAX_RAW_BYTES) {
25
+ return {
26
+ ok: false,
27
+ code: "too-large",
28
+ message: `\u1EA2nh qu\xE1 l\u1EDBn (> ${Math.round(MAX_RAW_BYTES / 1024 / 1024)} MB). Crop ho\u1EB7c resize tr\u01B0\u1EDBc.`
29
+ };
30
+ }
31
+ return { ok: true, mediaType: mt };
32
+ }
33
+ async function fileToImagePart(file) {
34
+ const v = validateFile(file);
35
+ if (!v.ok) throw new Error(v.message);
36
+ const bitmap = await createImageBitmap(file);
37
+ const { width, height } = bitmap;
38
+ const maxEdge = Math.max(width, height);
39
+ const scale = maxEdge > MAX_EDGE_PX ? MAX_EDGE_PX / maxEdge : 1;
40
+ const targetW = Math.round(width * scale);
41
+ const targetH = Math.round(height * scale);
42
+ const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetW, targetH) : Object.assign(document.createElement("canvas"), { width: targetW, height: targetH });
43
+ const ctx = canvas.getContext("2d");
44
+ if (!ctx) throw new Error("Kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c canvas 2D context");
45
+ ctx.drawImage(bitmap, 0, 0, targetW, targetH);
46
+ bitmap.close();
47
+ let outputType = v.mediaType === "image/png" ? "image/png" : "image/jpeg";
48
+ let finalBlob = await canvasToBlob(canvas, outputType, 0.92);
49
+ if (finalBlob.size > MAX_ENCODED_BYTES) {
50
+ outputType = "image/jpeg";
51
+ finalBlob = await canvasToBlob(canvas, "image/jpeg", 0.7);
52
+ }
53
+ const base64 = await blobToBase64(finalBlob);
54
+ return { mediaType: outputType, base64 };
55
+ }
56
+ async function canvasToBlob(canvas, type, quality) {
57
+ if ("convertToBlob" in canvas) {
58
+ return canvas.convertToBlob({ type, quality });
59
+ }
60
+ return new Promise((resolve, reject) => {
61
+ canvas.toBlob(
62
+ (b) => b ? resolve(b) : reject(new Error("Canvas encode fail")),
63
+ type,
64
+ quality
65
+ );
66
+ });
67
+ }
68
+ async function blobToBase64(blob) {
69
+ const buf = await blob.arrayBuffer();
70
+ let binary = "";
71
+ const bytes = new Uint8Array(buf);
72
+ const chunk = 32768;
73
+ for (let i = 0; i < bytes.length; i += chunk) {
74
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
75
+ }
76
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(binary, "binary").toString("base64");
77
+ }
5
78
  var NameZ = z.string().regex(/^[A-Za-z][A-Za-z0-9_'₀-₉]{0,11}$/);
6
79
 
7
80
  // src/stamps/geometry-2d/dsl/kinds/_types.ts
@@ -16,12 +89,13 @@ var POINT_BASE_FIELDS = {
16
89
  layer: "default",
17
90
  schemaVersion: 1
18
91
  };
19
- function emitPointObject(id, name, constraint) {
92
+ function emitPointObject(id, name, constraint, visible = true) {
20
93
  return {
21
94
  id,
22
95
  kind: "point",
23
96
  label: name,
24
97
  ...POINT_BASE_FIELDS,
98
+ visible,
25
99
  attrs: { constraint }
26
100
  };
27
101
  }
@@ -62,7 +136,8 @@ var midpointModule = defineModule({
62
136
  name: NameZ,
63
137
  kind: z.literal("midpoint"),
64
138
  p1: NameZ,
65
- p2: NameZ
139
+ p2: NameZ,
140
+ visible: z.boolean().optional()
66
141
  }),
67
142
  collectRefs: (e) => [e.p1, e.p2],
68
143
  emit: (e, ctx) => [{
@@ -70,7 +145,8 @@ var midpointModule = defineModule({
70
145
  object: emitPointObject(
71
146
  ctx.resolveId(e.name),
72
147
  e.name,
73
- { kind: "midpoint", p1: ctx.resolveId(e.p1), p2: ctx.resolveId(e.p2) }
148
+ { kind: "midpoint", p1: ctx.resolveId(e.p1), p2: ctx.resolveId(e.p2) },
149
+ e.visible ?? true
74
150
  )
75
151
  }]
76
152
  });
@@ -465,6 +541,67 @@ var angleBisectorModule = defineModule({
465
541
  }
466
542
  }]
467
543
  });
544
+ var lineThroughModule = defineModule({
545
+ kind: "lineThrough",
546
+ role: "lineConstruction",
547
+ category: "lines",
548
+ prefix: "l",
549
+ schema: z.object({
550
+ name: NameZ,
551
+ kind: z.literal("lineThrough"),
552
+ points: z.array(NameZ).min(2)
553
+ }),
554
+ collectRefs: (e) => [...e.points],
555
+ refSpecs: [{ field: "points", role: "point", many: true }],
556
+ emit: (e, ctx) => [{
557
+ role: "primary",
558
+ object: {
559
+ id: ctx.resolveId(e.name),
560
+ kind: "line",
561
+ label: e.name,
562
+ ...SHAPE_BASE_FIELDS,
563
+ attrs: {
564
+ construction: {
565
+ kind: "lineThrough",
566
+ points: e.points.map((p) => ctx.resolveId(p))
567
+ }
568
+ }
569
+ }
570
+ }]
571
+ });
572
+ var radicalAxisModule = defineModule({
573
+ kind: "radicalAxis",
574
+ role: "lineConstruction",
575
+ category: "lines",
576
+ prefix: "l",
577
+ schema: z.object({
578
+ name: NameZ,
579
+ kind: z.literal("radicalAxis"),
580
+ circle1: NameZ,
581
+ circle2: NameZ
582
+ }),
583
+ collectRefs: (e) => [e.circle1, e.circle2],
584
+ refSpecs: [
585
+ { field: "circle1", role: "circle" },
586
+ { field: "circle2", role: "circle" }
587
+ ],
588
+ emit: (e, ctx) => [{
589
+ role: "primary",
590
+ object: {
591
+ id: ctx.resolveId(e.name),
592
+ kind: "line",
593
+ label: e.name,
594
+ ...SHAPE_BASE_FIELDS,
595
+ attrs: {
596
+ construction: {
597
+ kind: "radicalAxis",
598
+ circle1: ctx.resolveId(e.circle1),
599
+ circle2: ctx.resolveId(e.circle2)
600
+ }
601
+ }
602
+ }
603
+ }]
604
+ });
468
605
  var tangentModule = defineModule({
469
606
  kind: "tangent",
470
607
  role: "lineConstruction",
@@ -528,7 +665,8 @@ var circleCPModule = defineModule({
528
665
  name: NameZ,
529
666
  kind: z.literal("circleCP"),
530
667
  center: NameZ,
531
- surfacePoint: NameZ
668
+ surfacePoint: NameZ,
669
+ visible: z.boolean().optional()
532
670
  }),
533
671
  collectRefs: (e) => [e.center, e.surfacePoint],
534
672
  emit: (e, ctx) => [{
@@ -538,6 +676,7 @@ var circleCPModule = defineModule({
538
676
  kind: "circle",
539
677
  label: e.name,
540
678
  ...SHAPE_BASE_FIELDS,
679
+ visible: e.visible ?? true,
541
680
  attrs: { center: ctx.resolveId(e.center), surfacePoint: ctx.resolveId(e.surfacePoint) }
542
681
  }
543
682
  }]
@@ -573,6 +712,411 @@ var circle3Module = defineModule({
573
712
  }
574
713
  }]
575
714
  });
715
+ var circleDiameterModule = defineModule({
716
+ kind: "circleDiameter",
717
+ role: "circle",
718
+ category: "circles",
719
+ prefix: "c",
720
+ schema: z.object({
721
+ name: NameZ,
722
+ kind: z.literal("circleDiameter"),
723
+ p1: NameZ,
724
+ p2: NameZ,
725
+ visible: z.boolean().optional()
726
+ }),
727
+ collectRefs: (e) => [e.p1, e.p2],
728
+ emit: (e, ctx) => [{
729
+ role: "primary",
730
+ object: {
731
+ id: ctx.resolveId(e.name),
732
+ kind: "circle",
733
+ label: e.name,
734
+ ...SHAPE_BASE_FIELDS,
735
+ visible: e.visible ?? true,
736
+ attrs: {
737
+ construction: {
738
+ kind: "diameter",
739
+ p1: ctx.resolveId(e.p1),
740
+ p2: ctx.resolveId(e.p2)
741
+ }
742
+ }
743
+ }
744
+ }]
745
+ });
746
+ var secondIntersectionModule = defineModule({
747
+ kind: "secondIntersection",
748
+ role: "point",
749
+ category: "points",
750
+ prefix: "p",
751
+ schema: z.object({
752
+ name: NameZ,
753
+ kind: z.literal("secondIntersection"),
754
+ line: NameZ,
755
+ circle: NameZ,
756
+ other: NameZ
757
+ }),
758
+ collectRefs: (e) => [e.line, e.circle, e.other],
759
+ refSpecs: [
760
+ { field: "line", role: "line-like" },
761
+ { field: "circle", role: "circle" },
762
+ { field: "other", role: "point" }
763
+ ],
764
+ emit: (e, ctx) => [{
765
+ role: "primary",
766
+ object: emitPointObject(
767
+ ctx.resolveId(e.name),
768
+ e.name,
769
+ {
770
+ kind: "secondIntersection",
771
+ line: ctx.resolveId(e.line),
772
+ circle: ctx.resolveId(e.circle),
773
+ other: ctx.resolveId(e.other)
774
+ }
775
+ )
776
+ }]
777
+ });
778
+ var circleIntersectionModule = defineModule({
779
+ kind: "circleIntersection",
780
+ role: "point",
781
+ category: "points",
782
+ prefix: "p",
783
+ schema: z.object({
784
+ name: NameZ,
785
+ kind: z.literal("circleIntersection"),
786
+ c1: NameZ,
787
+ c2: NameZ,
788
+ which: z.union([z.literal(0), z.literal(1)])
789
+ }),
790
+ collectRefs: (e) => [e.c1, e.c2],
791
+ refSpecs: [
792
+ { field: "c1", role: "circle" },
793
+ { field: "c2", role: "circle" }
794
+ ],
795
+ emit: (e, ctx) => [{
796
+ role: "primary",
797
+ object: emitPointObject(
798
+ ctx.resolveId(e.name),
799
+ e.name,
800
+ {
801
+ kind: "circleIntersection",
802
+ c1: ctx.resolveId(e.c1),
803
+ c2: ctx.resolveId(e.c2),
804
+ which: e.which
805
+ }
806
+ )
807
+ }]
808
+ });
809
+ var circleSecondIntersectionModule = defineModule({
810
+ kind: "circleSecondIntersection",
811
+ role: "point",
812
+ category: "points",
813
+ prefix: "p",
814
+ schema: z.object({
815
+ name: NameZ,
816
+ kind: z.literal("circleSecondIntersection"),
817
+ c1: NameZ,
818
+ c2: NameZ,
819
+ exclude: NameZ
820
+ }),
821
+ collectRefs: (e) => [e.c1, e.c2, e.exclude],
822
+ refSpecs: [
823
+ { field: "c1", role: "circle" },
824
+ { field: "c2", role: "circle" },
825
+ { field: "exclude", role: "point" }
826
+ ],
827
+ emit: (e, ctx) => [{
828
+ role: "primary",
829
+ object: emitPointObject(
830
+ ctx.resolveId(e.name),
831
+ e.name,
832
+ {
833
+ kind: "circleSecondIntersection",
834
+ c1: ctx.resolveId(e.c1),
835
+ c2: ctx.resolveId(e.c2),
836
+ exclude: ctx.resolveId(e.exclude)
837
+ }
838
+ )
839
+ }]
840
+ });
841
+ var tangencyPointModule = defineModule({
842
+ kind: "tangencyPoint",
843
+ role: "point",
844
+ category: "points",
845
+ prefix: "p",
846
+ schema: z.object({
847
+ name: NameZ,
848
+ kind: z.literal("tangencyPoint"),
849
+ circle: NameZ,
850
+ onLine: NameZ
851
+ }),
852
+ collectRefs: (e) => [e.circle, e.onLine],
853
+ refSpecs: [
854
+ { field: "circle", role: "circle" },
855
+ { field: "onLine", role: "line-like" }
856
+ ],
857
+ emit: (e, ctx) => [{
858
+ role: "primary",
859
+ object: emitPointObject(
860
+ ctx.resolveId(e.name),
861
+ e.name,
862
+ {
863
+ kind: "tangencyPoint",
864
+ circle: ctx.resolveId(e.circle),
865
+ onLine: ctx.resolveId(e.onLine)
866
+ }
867
+ )
868
+ }]
869
+ });
870
+ var tangentPointExtModule = defineModule({
871
+ kind: "tangentPointExt",
872
+ role: "point",
873
+ category: "points",
874
+ prefix: "p",
875
+ schema: z.object({
876
+ name: NameZ,
877
+ kind: z.literal("tangentPointExt"),
878
+ from: NameZ,
879
+ circle: NameZ,
880
+ which: z.union([z.literal(0), z.literal(1)])
881
+ }),
882
+ collectRefs: (e) => [e.from, e.circle],
883
+ refSpecs: [
884
+ { field: "from", role: "point" },
885
+ { field: "circle", role: "circle" }
886
+ ],
887
+ emit: (e, ctx) => [{
888
+ role: "primary",
889
+ object: emitPointObject(
890
+ ctx.resolveId(e.name),
891
+ e.name,
892
+ {
893
+ kind: "tangentPointExt",
894
+ from: ctx.resolveId(e.from),
895
+ circle: ctx.resolveId(e.circle),
896
+ which: e.which
897
+ }
898
+ )
899
+ }]
900
+ });
901
+ var circleCRModule = defineModule({
902
+ kind: "circleCR",
903
+ role: "circle",
904
+ category: "circles",
905
+ prefix: "c",
906
+ schema: z.object({
907
+ name: NameZ,
908
+ kind: z.literal("circleCR"),
909
+ center: NameZ,
910
+ radius: z.number().positive()
911
+ }),
912
+ collectRefs: (e) => [e.center],
913
+ refSpecs: [{ field: "center", role: "point" }],
914
+ emit: (e, ctx) => [{
915
+ role: "primary",
916
+ object: {
917
+ id: ctx.resolveId(e.name),
918
+ kind: "circle",
919
+ label: e.name,
920
+ ...SHAPE_BASE_FIELDS,
921
+ attrs: { center: ctx.resolveId(e.center), radius: e.radius }
922
+ }
923
+ }]
924
+ });
925
+ var incircleModule = defineModule({
926
+ kind: "incircle",
927
+ role: "circle",
928
+ category: "circles",
929
+ prefix: "c",
930
+ schema: z.object({
931
+ name: NameZ,
932
+ kind: z.literal("incircle"),
933
+ vertices: z.tuple([NameZ, NameZ, NameZ])
934
+ }),
935
+ collectRefs: (e) => [...e.vertices],
936
+ refSpecs: [{ field: "vertices", role: "point", many: true }],
937
+ emit: (e, ctx) => [{
938
+ role: "primary",
939
+ object: {
940
+ id: ctx.resolveId(e.name),
941
+ kind: "circle",
942
+ label: e.name,
943
+ ...SHAPE_BASE_FIELDS,
944
+ attrs: {
945
+ kind: "incircle",
946
+ vertices: [
947
+ ctx.resolveId(e.vertices[0]),
948
+ ctx.resolveId(e.vertices[1]),
949
+ ctx.resolveId(e.vertices[2])
950
+ ]
951
+ }
952
+ }
953
+ }]
954
+ });
955
+ var excircleModule = defineModule({
956
+ kind: "excircle",
957
+ role: "circle",
958
+ category: "circles",
959
+ prefix: "c",
960
+ schema: z.object({
961
+ name: NameZ,
962
+ kind: z.literal("excircle"),
963
+ vertices: z.tuple([NameZ, NameZ, NameZ]),
964
+ opposite: NameZ
965
+ }),
966
+ collectRefs: (e) => [...e.vertices],
967
+ refSpecs: [
968
+ { field: "vertices", role: "point", many: true },
969
+ { field: "opposite", role: "point" }
970
+ ],
971
+ emit: (e, ctx) => [{
972
+ role: "primary",
973
+ object: {
974
+ id: ctx.resolveId(e.name),
975
+ kind: "circle",
976
+ label: e.name,
977
+ ...SHAPE_BASE_FIELDS,
978
+ attrs: {
979
+ construction: {
980
+ kind: "excircle",
981
+ p1: ctx.resolveId(e.vertices[0]),
982
+ p2: ctx.resolveId(e.vertices[1]),
983
+ p3: ctx.resolveId(e.vertices[2]),
984
+ opposite: ctx.resolveId(e.opposite)
985
+ }
986
+ }
987
+ }
988
+ }]
989
+ });
990
+ var arcMidpointModule = defineModule({
991
+ kind: "arcMidpoint",
992
+ role: "point",
993
+ category: "points",
994
+ prefix: "p",
995
+ schema: z.object({
996
+ name: NameZ,
997
+ kind: z.literal("arcMidpoint"),
998
+ circle: NameZ,
999
+ a: NameZ,
1000
+ b: NameZ,
1001
+ notContaining: NameZ
1002
+ }),
1003
+ collectRefs: (e) => [e.circle, e.a, e.b, e.notContaining],
1004
+ emit: (e, ctx) => [{
1005
+ role: "primary",
1006
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1007
+ kind: "arcMidpoint",
1008
+ circle: ctx.resolveId(e.circle),
1009
+ a: ctx.resolveId(e.a),
1010
+ b: ctx.resolveId(e.b),
1011
+ notContaining: ctx.resolveId(e.notContaining)
1012
+ })
1013
+ }]
1014
+ });
1015
+ var excenterModule = defineModule({
1016
+ kind: "excenter",
1017
+ role: "point",
1018
+ category: "points",
1019
+ prefix: "p",
1020
+ schema: z.object({
1021
+ name: NameZ,
1022
+ kind: z.literal("excenter"),
1023
+ vertices: z.tuple([NameZ, NameZ, NameZ]),
1024
+ opposite: NameZ
1025
+ }),
1026
+ collectRefs: (e) => [...e.vertices],
1027
+ emit: (e, ctx) => [{
1028
+ role: "primary",
1029
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1030
+ kind: "excenter",
1031
+ vertices: resolveTriangleVertices(ctx, e.vertices),
1032
+ opposite: ctx.resolveId(e.opposite)
1033
+ })
1034
+ }]
1035
+ });
1036
+ var reflectPointModule = defineModule({
1037
+ kind: "reflectPoint",
1038
+ role: "point",
1039
+ category: "points",
1040
+ prefix: "p",
1041
+ schema: z.object({ name: NameZ, kind: z.literal("reflectPoint"), of: NameZ, through: NameZ }),
1042
+ collectRefs: (e) => [e.of, e.through],
1043
+ emit: (e, ctx) => [{
1044
+ role: "primary",
1045
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1046
+ kind: "transformed",
1047
+ source: ctx.resolveId(e.of),
1048
+ transform: { kind: "reflectPoint", center: ctx.resolveId(e.through) }
1049
+ })
1050
+ }]
1051
+ });
1052
+ var reflectLineModule = defineModule({
1053
+ kind: "reflectLine",
1054
+ role: "point",
1055
+ category: "points",
1056
+ prefix: "p",
1057
+ schema: z.object({ name: NameZ, kind: z.literal("reflectLine"), of: NameZ, through: NameZ }),
1058
+ collectRefs: (e) => [e.of, e.through],
1059
+ emit: (e, ctx) => [{
1060
+ role: "primary",
1061
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1062
+ kind: "transformed",
1063
+ source: ctx.resolveId(e.of),
1064
+ transform: { kind: "reflectLine", line: ctx.resolveId(e.through) }
1065
+ })
1066
+ }]
1067
+ });
1068
+ var ScaleOffsetZ = {
1069
+ scale: z.number().positive().optional(),
1070
+ offset: z.number().optional()
1071
+ };
1072
+ var DistanceZ = z.discriminatedUnion("kind", [
1073
+ z.object({ kind: z.literal("circleRadius"), circle: NameZ, ...ScaleOffsetZ }),
1074
+ z.object({ kind: z.literal("segmentLength"), p1: NameZ, p2: NameZ, ...ScaleOffsetZ }),
1075
+ z.object({ kind: z.literal("literal"), value: z.number().positive(), ...ScaleOffsetZ })
1076
+ ]);
1077
+ function withScaleOffset(base, d) {
1078
+ const out = { ...base };
1079
+ if (d.scale !== void 0) out.scale = d.scale;
1080
+ if (d.offset !== void 0) out.offset = d.offset;
1081
+ return out;
1082
+ }
1083
+ var pointAtDistanceModule = defineModule({
1084
+ kind: "pointAtDistance",
1085
+ role: "point",
1086
+ category: "points",
1087
+ prefix: "p",
1088
+ schema: z.object({
1089
+ name: NameZ,
1090
+ kind: z.literal("pointAtDistance"),
1091
+ from: NameZ,
1092
+ through: NameZ,
1093
+ distance: DistanceZ
1094
+ }),
1095
+ collectRefs: (e) => {
1096
+ const d = e.distance;
1097
+ const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
1098
+ return [e.from, e.through, ...extra];
1099
+ },
1100
+ // TODO(Mức 1 defer): distance.{circle,p1,p2} là nested trong `distance` — refSpec
1101
+ // phẳng đọc top-level không với tới, validate riêng nếu cần. Hiện validate from/through.
1102
+ refSpecs: [
1103
+ { field: "from", role: "point" },
1104
+ { field: "through", role: "point" }
1105
+ ],
1106
+ emit: (e, ctx) => {
1107
+ const d = e.distance;
1108
+ const distance = d.kind === "circleRadius" ? withScaleOffset({ kind: "circleRadius", circle: ctx.resolveId(d.circle) }, d) : d.kind === "segmentLength" ? withScaleOffset({ kind: "segmentLength", p1: ctx.resolveId(d.p1), p2: ctx.resolveId(d.p2) }, d) : withScaleOffset({ kind: "literal", value: d.value }, d);
1109
+ return [{
1110
+ role: "primary",
1111
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1112
+ kind: "pointAtDistance",
1113
+ from: ctx.resolveId(e.from),
1114
+ through: ctx.resolveId(e.through),
1115
+ distance
1116
+ })
1117
+ }];
1118
+ }
1119
+ });
576
1120
 
577
1121
  // src/stamps/geometry-2d/dsl/registry.ts
578
1122
  var ALL_MODULES = [
@@ -587,6 +1131,12 @@ var ALL_MODULES = [
587
1131
  centroidModule,
588
1132
  orthocenterModule,
589
1133
  intersectionModule,
1134
+ // NEW Tier 4+5 points
1135
+ secondIntersectionModule,
1136
+ circleIntersectionModule,
1137
+ circleSecondIntersectionModule,
1138
+ tangencyPointModule,
1139
+ tangentPointExtModule,
590
1140
  segmentModule,
591
1141
  lineModule,
592
1142
  rayModule,
@@ -594,10 +1144,24 @@ var ALL_MODULES = [
594
1144
  parallelModule,
595
1145
  perpBisectorModule,
596
1146
  angleBisectorModule,
1147
+ lineThroughModule,
1148
+ radicalAxisModule,
597
1149
  tangentModule,
598
1150
  polygonModule,
599
1151
  circleCPModule,
600
- circle3Module
1152
+ circle3Module,
1153
+ circleDiameterModule,
1154
+ // NEW Tier 4+5 circles
1155
+ circleCRModule,
1156
+ incircleModule,
1157
+ excircleModule,
1158
+ // Cụm A points
1159
+ arcMidpointModule,
1160
+ excenterModule,
1161
+ reflectPointModule,
1162
+ reflectLineModule,
1163
+ // Cụm B points
1164
+ pointAtDistanceModule
601
1165
  ];
602
1166
  var KIND_REGISTRY = new Map(ALL_MODULES.map((m) => [m.kind, m]));
603
1167
  var POINT_KINDS = new Set(
@@ -683,10 +1247,107 @@ function serializePoint(obj, state) {
683
1247
  }
684
1248
  };
685
1249
  }
1250
+ case "tangentPointExt": {
1251
+ const refs = resolveRefs([c.from, c.circle], state);
1252
+ if (!refs) return fail("unresolved-ref", `${c.from},${c.circle}`);
1253
+ return {
1254
+ ok: true,
1255
+ entity: {
1256
+ name: obj.label,
1257
+ kind: "tangentPointExt",
1258
+ from: refs[0],
1259
+ circle: refs[1],
1260
+ which: c.which
1261
+ }
1262
+ };
1263
+ }
1264
+ case "circleIntersection": {
1265
+ const refs = resolveRefs([c.c1, c.c2], state);
1266
+ if (!refs) return fail("unresolved-ref", `${c.c1},${c.c2}`);
1267
+ return {
1268
+ ok: true,
1269
+ entity: { name: obj.label, kind: "circleIntersection", c1: refs[0], c2: refs[1], which: c.which }
1270
+ };
1271
+ }
1272
+ case "circleSecondIntersection": {
1273
+ const refs = resolveRefs([c.c1, c.c2, c.exclude], state);
1274
+ if (!refs) return fail("unresolved-ref", `${c.c1},${c.c2},${c.exclude}`);
1275
+ return {
1276
+ ok: true,
1277
+ entity: { name: obj.label, kind: "circleSecondIntersection", c1: refs[0], c2: refs[1], exclude: refs[2] }
1278
+ };
1279
+ }
1280
+ case "secondIntersection": {
1281
+ const refs = resolveRefs([c.line, c.circle, c.other], state);
1282
+ if (!refs) return fail("unresolved-ref", `${c.line},${c.circle},${c.other}`);
1283
+ return {
1284
+ ok: true,
1285
+ entity: { name: obj.label, kind: "secondIntersection", line: refs[0], circle: refs[1], other: refs[2] }
1286
+ };
1287
+ }
1288
+ case "tangencyPoint": {
1289
+ const refs = resolveRefs([c.circle, c.onLine], state);
1290
+ if (!refs) return fail("unresolved-ref", `${c.circle},${c.onLine}`);
1291
+ return {
1292
+ ok: true,
1293
+ entity: { name: obj.label, kind: "tangencyPoint", circle: refs[0], onLine: refs[1] }
1294
+ };
1295
+ }
1296
+ case "arcMidpoint": {
1297
+ const refs = resolveRefs([c.circle, c.a, c.b, c.notContaining], state);
1298
+ if (!refs) return fail("unresolved-ref", `${c.circle},${c.a},${c.b},${c.notContaining}`);
1299
+ return {
1300
+ ok: true,
1301
+ entity: {
1302
+ name: obj.label,
1303
+ kind: "arcMidpoint",
1304
+ circle: refs[0],
1305
+ a: refs[1],
1306
+ b: refs[2],
1307
+ notContaining: refs[3]
1308
+ }
1309
+ };
1310
+ }
1311
+ case "excenter": {
1312
+ const refs = resolveRefs([c.vertices[0], c.vertices[1], c.vertices[2], c.opposite], state);
1313
+ if (!refs) return fail("unresolved-ref", `${c.vertices.join(",")},${c.opposite}`);
1314
+ return {
1315
+ ok: true,
1316
+ entity: {
1317
+ name: obj.label,
1318
+ kind: "excenter",
1319
+ vertices: [refs[0], refs[1], refs[2]],
1320
+ opposite: refs[3]
1321
+ }
1322
+ };
1323
+ }
1324
+ case "pointAtDistance": {
1325
+ const d = c.distance;
1326
+ const distRefIds = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
1327
+ const refs = resolveRefs([c.from, c.through, ...distRefIds], state);
1328
+ if (!refs) return fail("unresolved-ref", [c.from, c.through, ...distRefIds].join(","));
1329
+ let distance;
1330
+ if (d.kind === "circleRadius") {
1331
+ distance = { kind: "circleRadius", circle: refs[2] };
1332
+ } else if (d.kind === "segmentLength") {
1333
+ distance = { kind: "segmentLength", p1: refs[2], p2: refs[3] };
1334
+ } else {
1335
+ distance = { kind: "literal", value: d.value };
1336
+ }
1337
+ if (d.scale !== void 0) distance.scale = d.scale;
1338
+ if (d.offset !== void 0) distance.offset = d.offset;
1339
+ return {
1340
+ ok: true,
1341
+ entity: { name: obj.label, kind: "pointAtDistance", from: refs[0], through: refs[1], distance }
1342
+ };
1343
+ }
686
1344
  // Out of DSL v1:
687
1345
  case "onAxis":
688
1346
  case "onPolygon":
689
1347
  case "transformed":
1348
+ case "onPerpendicular":
1349
+ case "onPerpBisector":
1350
+ case "onCircleAroundPoint":
690
1351
  return fail("unsupported-constraint", c.kind);
691
1352
  default: {
692
1353
  return fail("unsupported-constraint");
@@ -771,6 +1432,16 @@ function serializeLine(obj, state) {
771
1432
  } : { name: obj.label, kind: "tangent", throughPoint: refs[0], toCircle: refs[1] };
772
1433
  return { ok: true, entity };
773
1434
  }
1435
+ case "lineThrough": {
1436
+ const refs = resolveRefs(c.points, state);
1437
+ if (!refs) return fail("unresolved-ref", c.points.join(","));
1438
+ return { ok: true, entity: { name: obj.label, kind: "lineThrough", points: refs } };
1439
+ }
1440
+ case "radicalAxis": {
1441
+ const refs = resolveRefs([c.circle1, c.circle2], state);
1442
+ if (!refs) return fail("unresolved-ref", `${c.circle1},${c.circle2}`);
1443
+ return { ok: true, entity: { name: obj.label, kind: "radicalAxis", circle1: refs[0], circle2: refs[1] } };
1444
+ }
774
1445
  case "angleBisectorLines":
775
1446
  return fail("unsupported-construction", "angleBisectorLines");
776
1447
  default: {
@@ -790,8 +1461,45 @@ function serializePolygon(obj, state) {
790
1461
  }
791
1462
  function serializeCircle(obj, state) {
792
1463
  const a = obj.attrs;
793
- const c = a.construction;
1464
+ const c = (() => {
1465
+ if (a.construction) return a.construction;
1466
+ const raw = a;
1467
+ if (raw.kind === "incircle" && raw.vertices) {
1468
+ return {
1469
+ kind: "incircle",
1470
+ p1: raw.vertices[0],
1471
+ p2: raw.vertices[1],
1472
+ p3: raw.vertices[2]
1473
+ };
1474
+ }
1475
+ if (raw.kind === "excircle" && raw.vertices && raw.opposite) {
1476
+ return {
1477
+ kind: "excircle",
1478
+ p1: raw.vertices[0],
1479
+ p2: raw.vertices[1],
1480
+ p3: raw.vertices[2],
1481
+ opposite: raw.opposite
1482
+ };
1483
+ }
1484
+ if (raw.kind === "circleDiameter" && raw.p1 && raw.p2) {
1485
+ return {
1486
+ kind: "diameter",
1487
+ p1: raw.p1,
1488
+ p2: raw.p2
1489
+ };
1490
+ }
1491
+ return void 0;
1492
+ })();
794
1493
  if (!c) {
1494
+ if (typeof a.radius === "number") {
1495
+ if (!a.center) return fail("unsupported-construction", "missing center");
1496
+ const refs2 = resolveRefs([a.center], state);
1497
+ if (!refs2) return fail("unresolved-ref", `${a.center}`);
1498
+ return {
1499
+ ok: true,
1500
+ entity: { name: obj.label, kind: "circleCR", center: refs2[0], radius: a.radius }
1501
+ };
1502
+ }
795
1503
  if (!a.center || !a.surfacePoint) {
796
1504
  return fail("unsupported-construction", "missing center/surfacePoint");
797
1505
  }
@@ -810,6 +1518,30 @@ function serializeCircle(obj, state) {
810
1518
  entity: { name: obj.label, kind: "circle3", p1: refs[0], p2: refs[1], p3: refs[2] }
811
1519
  };
812
1520
  }
1521
+ if (c.kind === "incircle") {
1522
+ const refs = resolveRefs([c.p1, c.p2, c.p3], state);
1523
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2},${c.p3}`);
1524
+ return {
1525
+ ok: true,
1526
+ entity: { name: obj.label, kind: "incircle", vertices: [refs[0], refs[1], refs[2]] }
1527
+ };
1528
+ }
1529
+ if (c.kind === "excircle") {
1530
+ const refs = resolveRefs([c.p1, c.p2, c.p3, c.opposite], state);
1531
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2},${c.p3},${c.opposite}`);
1532
+ return {
1533
+ ok: true,
1534
+ entity: { name: obj.label, kind: "excircle", vertices: [refs[0], refs[1], refs[2]], opposite: refs[3] }
1535
+ };
1536
+ }
1537
+ if (c.kind === "diameter") {
1538
+ const refs = resolveRefs([c.p1, c.p2], state);
1539
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2}`);
1540
+ return {
1541
+ ok: true,
1542
+ entity: { name: obj.label, kind: "circleDiameter", p1: refs[0], p2: refs[1] }
1543
+ };
1544
+ }
813
1545
  return fail("unsupported-construction");
814
1546
  }
815
1547
  function serializeObject(obj, state) {
@@ -908,6 +1640,10 @@ function describeEntity(e) {
908
1640
  return `${e.name} = trung tr\u1EF1c ${e.p1}${e.p2}`;
909
1641
  case "angleBisector":
910
1642
  return `${e.name} = ph\xE2n gi\xE1c \u2220${e.p1}${e.vertex}${e.p2}`;
1643
+ case "lineThrough":
1644
+ return `${e.name} = \u0111\u01B0\u1EDDng qua ${e.points.join("")}`;
1645
+ case "radicalAxis":
1646
+ return `${e.name} = tr\u1EE5c \u0111\u1EB3ng ph\u01B0\u01A1ng ${e.circle1} & ${e.circle2}`;
911
1647
  case "tangent": {
912
1648
  const branch = "branch" in e && e.branch !== void 0 ? ` (nh\xE1nh ${e.branch})` : "";
913
1649
  return `${e.name} = ti\u1EBFp tuy\u1EBFn ${e.toCircle} qua ${e.throughPoint}${branch}`;
@@ -916,6 +1652,40 @@ function describeEntity(e) {
916
1652
  return `${e.name} = (${e.center}; ${e.center}${e.surfacePoint})`;
917
1653
  case "circle3":
918
1654
  return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n qua ${e.p1}${e.p2}${e.p3}`;
1655
+ case "circleDiameter":
1656
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n \u0111\u01B0\u1EDDng k\xEDnh ${e.p1}${e.p2}`;
1657
+ // NEW Tier 4+5
1658
+ case "secondIntersection":
1659
+ return `${e.name} = giao th\u1EE9 hai c\u1EE7a ${e.line} v\xE0 ${e.circle} (kh\xE1c ${e.other})`;
1660
+ case "circleIntersection":
1661
+ return `${e.name} = giao ${e.c1} \u2229 ${e.c2} (nh\xE1nh ${e.which})`;
1662
+ case "circleSecondIntersection":
1663
+ return `${e.name} = giao th\u1EE9 hai ${e.c1} \u2229 ${e.c2} (kh\xE1c ${e.exclude})`;
1664
+ case "tangencyPoint":
1665
+ return `${e.name} = ti\u1EBFp \u0111i\u1EC3m c\u1EE7a ${e.circle} tr\xEAn ${e.onLine}`;
1666
+ case "tangentPointExt":
1667
+ return `${e.name} = ti\u1EBFp \u0111i\u1EC3m t\u1EEB ${e.from} l\xEAn ${e.circle} (nh\xE1nh ${e.which})`;
1668
+ case "circleCR":
1669
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n (${e.center}; r=${e.radius})`;
1670
+ case "incircle":
1671
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n n\u1ED9i ti\u1EBFp ${e.vertices.join("")}`;
1672
+ case "excircle":
1673
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
1674
+ // Cụm A
1675
+ case "arcMidpoint":
1676
+ return `${e.name} = trung \u0111i\u1EC3m cung ${e.a}${e.b} (kh\xF4ng ch\u1EE9a ${e.notContaining}) tr\xEAn ${e.circle}`;
1677
+ case "excenter":
1678
+ return `${e.name} = t\xE2m b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
1679
+ case "reflectPoint":
1680
+ return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111i\u1EC3m ${e.through}`;
1681
+ case "reflectLine":
1682
+ return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111\u01B0\u1EDDng ${e.through}`;
1683
+ // Cụm B
1684
+ case "pointAtDistance": {
1685
+ const d = e.distance;
1686
+ const distStr = d.kind === "circleRadius" ? `r(${d.circle})` : d.kind === "segmentLength" ? `|${d.p1}${d.p2}|` : `${d.value}`;
1687
+ return `${e.name} = \u0111i\u1EC3m tr\xEAn tia ${e.from}${e.through} c\xE1ch ${e.through} m\u1ED9t kho\u1EA3ng ${distStr}`;
1688
+ }
919
1689
  default: {
920
1690
  return "";
921
1691
  }
@@ -933,6 +1703,6 @@ function describeDsl(obj, state) {
933
1703
  return `${base} (kh\xF4ng h\u1ED7 tr\u1EE3 DSL)`;
934
1704
  }
935
1705
 
936
- export { CIRCLE_KINDS, KIND_REGISTRY, LINE_LIKE_SHAPE_KINDS, POINT_KINDS, describeDsl, serializeState };
937
- //# sourceMappingURL=chunk-VNCCIV6O.mjs.map
938
- //# sourceMappingURL=chunk-VNCCIV6O.mjs.map
1706
+ export { CIRCLE_KINDS, KIND_REGISTRY, LINE_LIKE_SHAPE_KINDS, MAX_EDGE_PX, MAX_ENCODED_BYTES, MAX_RAW_BYTES, POINT_KINDS, describeDsl, fileToImagePart, inferMediaType, serializeState, validateFile };
1707
+ //# sourceMappingURL=chunk-AJAHD35N.mjs.map
1708
+ //# sourceMappingURL=chunk-AJAHD35N.mjs.map