@xom11/whiteboard 0.11.0 → 0.24.1

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 (112) hide show
  1. package/README.md +67 -0
  2. package/dist/{ExcalidrawWithMenus-EAVPOPJZ.mjs → ExcalidrawWithMenus-WENZRYYE.mjs} +2 -3
  3. package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +1 -0
  4. package/dist/catalog.json +57 -0
  5. package/dist/chunk-4D5CSIJO.mjs +1167 -0
  6. package/dist/chunk-4D5CSIJO.mjs.map +1 -0
  7. package/dist/chunk-5UTGXHLJ.mjs +57 -0
  8. package/dist/chunk-5UTGXHLJ.mjs.map +1 -0
  9. package/dist/chunk-6V4SH4JJ.mjs +1801 -0
  10. package/dist/chunk-6V4SH4JJ.mjs.map +1 -0
  11. package/dist/chunk-AZIARTGX.mjs +23 -0
  12. package/dist/chunk-AZIARTGX.mjs.map +1 -0
  13. package/dist/chunk-BKSXPNPQ.mjs +348 -0
  14. package/dist/chunk-BKSXPNPQ.mjs.map +1 -0
  15. package/dist/{chunk-YVJP7NRG.mjs → chunk-CRAPWQKJ.mjs} +7 -9
  16. package/dist/chunk-CRAPWQKJ.mjs.map +1 -0
  17. package/dist/chunk-CSCF3YFZ.mjs +388 -0
  18. package/dist/chunk-CSCF3YFZ.mjs.map +1 -0
  19. package/dist/chunk-HNQLZIEP.mjs +78 -0
  20. package/dist/chunk-HNQLZIEP.mjs.map +1 -0
  21. package/dist/chunk-IBTRMWD6.mjs +28 -0
  22. package/dist/chunk-IBTRMWD6.mjs.map +1 -0
  23. package/dist/chunk-ICR4CVOE.mjs +57 -0
  24. package/dist/chunk-ICR4CVOE.mjs.map +1 -0
  25. package/dist/chunk-LVNCYP4J.mjs +57 -0
  26. package/dist/chunk-LVNCYP4J.mjs.map +1 -0
  27. package/dist/chunk-MFOGFFIL.mjs +95 -0
  28. package/dist/chunk-MFOGFFIL.mjs.map +1 -0
  29. package/dist/chunk-NVJ7K3DK.mjs +29 -0
  30. package/dist/chunk-NVJ7K3DK.mjs.map +1 -0
  31. package/dist/chunk-O4WIZFRQ.mjs +11 -0
  32. package/dist/chunk-O4WIZFRQ.mjs.map +1 -0
  33. package/dist/{chunk-C6SCVOMC.mjs → chunk-QGNU34T7.mjs} +5 -41
  34. package/dist/chunk-QGNU34T7.mjs.map +1 -0
  35. package/dist/chunk-R5FL6S7L.mjs +22 -0
  36. package/dist/chunk-R5FL6S7L.mjs.map +1 -0
  37. package/dist/{chunk-7P7SQFOW.mjs → chunk-SGFJLHHG.mjs} +3 -3
  38. package/dist/chunk-SGFJLHHG.mjs.map +1 -0
  39. package/dist/{chunk-PWIMZIB6.mjs → chunk-WWMQ2VHZ.mjs} +7 -8
  40. package/dist/chunk-WWMQ2VHZ.mjs.map +1 -0
  41. package/dist/chunk-YIPI3WUL.mjs +61 -0
  42. package/dist/chunk-YIPI3WUL.mjs.map +1 -0
  43. package/dist/chunk-ZBJBQKJ2.mjs +330 -0
  44. package/dist/chunk-ZBJBQKJ2.mjs.map +1 -0
  45. package/dist/geometry-2d.d.mts +3 -6
  46. package/dist/geometry-2d.d.ts +3 -6
  47. package/dist/geometry-2d.js +7007 -2633
  48. package/dist/geometry-2d.js.map +1 -1
  49. package/dist/geometry-2d.mjs +8 -4
  50. package/dist/geometry-3d.d.mts +4 -7
  51. package/dist/geometry-3d.d.ts +4 -7
  52. package/dist/geometry-3d.js +5446 -2507
  53. package/dist/geometry-3d.js.map +1 -1
  54. package/dist/geometry-3d.mjs +7 -4
  55. package/dist/graph-2d.d.mts +4 -7
  56. package/dist/graph-2d.d.ts +4 -7
  57. package/dist/graph-2d.js +5300 -1677
  58. package/dist/graph-2d.js.map +1 -1
  59. package/dist/graph-2d.mjs +10 -3
  60. package/dist/host-DOAYVL35.mjs +3199 -0
  61. package/dist/host-DOAYVL35.mjs.map +1 -0
  62. package/dist/host-GKNQBBUE.mjs +1142 -0
  63. package/dist/host-GKNQBBUE.mjs.map +1 -0
  64. package/dist/{host-Z3TEJKZA.mjs → host-QS2EOTRJ.mjs} +4 -4
  65. package/dist/{host-Z3TEJKZA.mjs.map → host-QS2EOTRJ.mjs.map} +1 -1
  66. package/dist/host-TLIXN4CF.mjs +2374 -0
  67. package/dist/host-TLIXN4CF.mjs.map +1 -0
  68. package/dist/index.css +4 -1
  69. package/dist/index.css.map +1 -1
  70. package/dist/index.d.mts +659 -19
  71. package/dist/index.d.ts +659 -19
  72. package/dist/index.js +13736 -9491
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +1465 -342
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/latex.d.mts +3 -4
  77. package/dist/latex.d.ts +3 -4
  78. package/dist/latex.js +33 -18
  79. package/dist/latex.js.map +1 -1
  80. package/dist/latex.mjs +2 -3
  81. package/dist/render-SA4JTOW3.mjs +8 -0
  82. package/dist/render-SA4JTOW3.mjs.map +1 -0
  83. package/dist/serialize-3NZS6A6Q.mjs +6 -0
  84. package/dist/serialize-3NZS6A6Q.mjs.map +1 -0
  85. package/dist/{types-CinstD7T.d.mts → types-rA4slL08.d.mts} +69 -4
  86. package/dist/{types-CinstD7T.d.ts → types-rA4slL08.d.ts} +69 -4
  87. package/package.json +34 -6
  88. package/dist/ExcalidrawWithMenus-EAVPOPJZ.mjs.map +0 -1
  89. package/dist/chunk-74VEEZBV.mjs +0 -619
  90. package/dist/chunk-74VEEZBV.mjs.map +0 -1
  91. package/dist/chunk-7P7SQFOW.mjs.map +0 -1
  92. package/dist/chunk-BJTO5JO5.mjs +0 -11
  93. package/dist/chunk-BJTO5JO5.mjs.map +0 -1
  94. package/dist/chunk-C6SCVOMC.mjs.map +0 -1
  95. package/dist/chunk-D257NCQW.mjs +0 -58
  96. package/dist/chunk-D257NCQW.mjs.map +0 -1
  97. package/dist/chunk-G7FR3AIV.mjs +0 -193
  98. package/dist/chunk-G7FR3AIV.mjs.map +0 -1
  99. package/dist/chunk-HTBLO5JO.mjs +0 -41
  100. package/dist/chunk-HTBLO5JO.mjs.map +0 -1
  101. package/dist/chunk-PWIMZIB6.mjs.map +0 -1
  102. package/dist/chunk-SBDMF4NQ.mjs +0 -212
  103. package/dist/chunk-SBDMF4NQ.mjs.map +0 -1
  104. package/dist/chunk-WQOABS6N.mjs +0 -197
  105. package/dist/chunk-WQOABS6N.mjs.map +0 -1
  106. package/dist/chunk-YVJP7NRG.mjs.map +0 -1
  107. package/dist/host-N6ACNJKI.mjs +0 -3226
  108. package/dist/host-N6ACNJKI.mjs.map +0 -1
  109. package/dist/host-NKGV6RF2.mjs +0 -1134
  110. package/dist/host-NKGV6RF2.mjs.map +0 -1
  111. package/dist/host-XVK7UCRE.mjs +0 -2908
  112. package/dist/host-XVK7UCRE.mjs.map +0 -1
@@ -0,0 +1,348 @@
1
+ "use client";
2
+ import { getKind, collectFreeVars } from './chunk-ZBJBQKJ2.mjs';
3
+
4
+ // src/core/scene/render/types2d.ts
5
+ var DEFAULT_THEME_2D = {
6
+ stroke: "#0f172a",
7
+ fill: "#60a5fa",
8
+ label: "#0f172a",
9
+ axis: "#94a3b8",
10
+ grid: "#e2e8f0",
11
+ pointFill: "#1e40af"
12
+ };
13
+
14
+ // src/core/scene/render/JxgRenderer.ts
15
+ var JxgRenderer = class {
16
+ constructor(store, board, options = {}) {
17
+ this.elements = /* @__PURE__ */ new Map();
18
+ this.disposed = false;
19
+ /** Chỉ dùng cho domain='graph2d': parameter.label → parameter.value */
20
+ this.paramMap = {};
21
+ /** Chỉ dùng cho domain='graph2d': function2d.id → expression string */
22
+ this.functionExpr = {};
23
+ // Selection halo overlay model: thay vì đổi màu element gốc thành đỏ, tạo
24
+ // một halo element gray phía sau (lower layer) → giữ nguyên màu gốc. Hỗ
25
+ // trợ multi-select cho cả canvas click-selection lẫn ObjectListPanel.
26
+ this.selectedIds = /* @__PURE__ */ new Set();
27
+ this.haloMap = /* @__PURE__ */ new Map();
28
+ this.store = store;
29
+ this.board = board;
30
+ this.theme = options.theme ?? DEFAULT_THEME_2D;
31
+ this.unsubscribe = store.subscribe((next, prev) => this.applyDiff(prev, next));
32
+ this.applyDiff(void 0, store.getState());
33
+ }
34
+ ctx() {
35
+ return {
36
+ jxg: this.board,
37
+ resolveRef: (id) => {
38
+ const m = /^(.+):border:(\d+)$/.exec(id);
39
+ if (m) {
40
+ const poly = this.elements.get(m[1]);
41
+ if (!poly) throw new Error(`[scene/2d] resolveRef: ch\u01B0a render polygon id="${m[1]}"`);
42
+ const borders = poly.borders;
43
+ const idx = parseInt(m[2], 10);
44
+ if (!Array.isArray(borders) || !borders[idx]) {
45
+ throw new Error(`[scene/2d] resolveRef: polygon "${m[1]}" kh\xF4ng c\xF3 border[${idx}]`);
46
+ }
47
+ return borders[idx];
48
+ }
49
+ const el = this.elements.get(id);
50
+ if (!el) throw new Error(`[scene/2d] resolveRef: ch\u01B0a render id="${id}"`);
51
+ return el;
52
+ },
53
+ defaults: { theme: this.theme, _functionExpr: this.functionExpr },
54
+ paramMap: this.paramMap
55
+ };
56
+ }
57
+ /**
58
+ * Rebuild `paramMap` và `functionExpr` từ state hiện tại.
59
+ * Chỉ chạy khi domain='graph2d'. Chi phí thấp vì parameters thường ≤ 8.
60
+ */
61
+ rebuildGraphMaps(state) {
62
+ if (state.meta.domain !== "graph2d") return;
63
+ const params = {};
64
+ const fns = {};
65
+ for (const id of state.order) {
66
+ const obj = state.objects[id];
67
+ if (obj.kind === "parameter") {
68
+ params[obj.label] = obj.attrs.value;
69
+ } else if (obj.kind === "function2d") {
70
+ fns[obj.id] = obj.attrs.expression;
71
+ }
72
+ }
73
+ this.paramMap = params;
74
+ this.functionExpr = fns;
75
+ }
76
+ create(obj) {
77
+ try {
78
+ const def = getKind(obj.kind);
79
+ const el = def.render(obj, this.ctx());
80
+ this.elements.set(obj.id, el);
81
+ this.attachFreePointDragSync(obj, el);
82
+ } catch (err) {
83
+ console.warn(`[scene/render/2d] kh\xF4ng render \u0111\u01B0\u1EE3c ${obj.kind} id="${obj.id}":`, err);
84
+ }
85
+ }
86
+ /**
87
+ * Đồng bộ toạ độ live của free point về scene.constraint khi user kéo bằng
88
+ * tay (Move tool / mobile drag). JSXGraph mutate obj.X()/Y() ngay nhưng
89
+ * constraint vẫn giữ giá trị lúc tạo → serialize sẽ ra SVG y hệt cũ →
90
+ * fileId SHA-256 trùng → Excalidraw bỏ qua refresh. (Regression từ
91
+ * commit f41f366 sau scene v2 port.)
92
+ *
93
+ * Chỉ áp dụng cho free point — glider/intersection/midpoint không drag được
94
+ * trực tiếp (toạ độ derived từ ref khác).
95
+ */
96
+ attachFreePointDragSync(obj, el) {
97
+ if (obj.kind !== "point") return;
98
+ const c = obj.attrs.constraint;
99
+ if (!c || c.kind !== "free") return;
100
+ const point = el;
101
+ if (typeof point.on !== "function") return;
102
+ const sceneId = obj.id;
103
+ point.on("up", () => {
104
+ if (this.disposed) return;
105
+ if (typeof point.X !== "function" || typeof point.Y !== "function") return;
106
+ const x = point.X();
107
+ const y = point.Y();
108
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return;
109
+ const cur = this.store.getState().objects[sceneId];
110
+ if (!cur) return;
111
+ const curC = cur.attrs.constraint;
112
+ if (!curC || curC.kind !== "free") return;
113
+ if (curC.x === x && curC.y === y) return;
114
+ this.store.dispatch({
115
+ type: "UPDATE_ATTRS",
116
+ payload: { id: sceneId, patch: { constraint: { kind: "free", x, y } } }
117
+ });
118
+ });
119
+ }
120
+ remove(id) {
121
+ this.removeHalo(id);
122
+ this.selectedIds.delete(id);
123
+ const el = this.elements.get(id);
124
+ if (!el) return;
125
+ try {
126
+ const helpers = el._helpers;
127
+ this.board.removeObject?.(el);
128
+ if (Array.isArray(helpers)) {
129
+ for (const h of helpers) {
130
+ try {
131
+ this.board.removeObject?.(h);
132
+ } catch {
133
+ }
134
+ }
135
+ }
136
+ } catch (err) {
137
+ console.warn(`[scene/render/2d] kh\xF4ng remove \u0111\u01B0\u1EE3c id="${id}":`, err);
138
+ }
139
+ this.elements.delete(id);
140
+ }
141
+ applyDiff(prev, next) {
142
+ if (this.disposed) return;
143
+ this.rebuildGraphMaps(next);
144
+ const prevObjs = prev?.objects ?? {};
145
+ const nextObjs = next.objects;
146
+ for (const id of Object.keys(prevObjs)) {
147
+ if (!(id in nextObjs)) this.remove(id);
148
+ }
149
+ for (const id of next.order) {
150
+ const cur = nextObjs[id];
151
+ const old = prevObjs[id];
152
+ if (!old) {
153
+ this.create(cur);
154
+ continue;
155
+ }
156
+ if (Object.is(old, cur)) continue;
157
+ const def = getKind(cur.kind);
158
+ const existing = this.elements.get(id);
159
+ if (def.update && existing) {
160
+ try {
161
+ def.update(cur, old, this.ctx(), existing);
162
+ continue;
163
+ } catch (err) {
164
+ console.warn(`[scene/render/2d] update fail, recreate:`, err);
165
+ }
166
+ }
167
+ this.remove(id);
168
+ this.create(cur);
169
+ }
170
+ if (next.meta.domain === "graph2d" && prev) {
171
+ const changedParams = /* @__PURE__ */ new Set();
172
+ for (const id of next.order) {
173
+ const cur = next.objects[id];
174
+ const old = prev.objects[id];
175
+ if (cur.kind !== "parameter" || old?.kind !== "parameter") continue;
176
+ if (cur.attrs.value !== old.attrs.value) {
177
+ changedParams.add(cur.label);
178
+ }
179
+ }
180
+ if (changedParams.size > 0) {
181
+ for (const id of next.order) {
182
+ const obj = next.objects[id];
183
+ if (obj.kind !== "function2d") continue;
184
+ const expr = obj.attrs.expression;
185
+ const refs = collectFreeVars(expr);
186
+ if (refs.some((r) => changedParams.has(r))) {
187
+ this.remove(id);
188
+ this.create(obj);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ dispose() {
195
+ if (this.disposed) return;
196
+ this.unsubscribe();
197
+ for (const id of Array.from(this.elements.keys())) this.remove(id);
198
+ this.disposed = true;
199
+ }
200
+ /** Return the rendered JSXGraph element for a scene id, or null if not found. */
201
+ getElement(id) {
202
+ return this.elements.get(id) ?? null;
203
+ }
204
+ /** Return a read-only view of the scene id → JSXGraph element map (for hit-test). */
205
+ listElements() {
206
+ return this.elements;
207
+ }
208
+ highlight(ids) {
209
+ if (this.disposed) return;
210
+ const newIds = new Set(
211
+ ids == null ? [] : Array.isArray(ids) ? ids : [ids]
212
+ );
213
+ for (const id of this.selectedIds) {
214
+ if (!newIds.has(id)) this.removeHalo(id);
215
+ }
216
+ for (const id of newIds) {
217
+ if (!this.selectedIds.has(id) && this.elements.has(id)) this.addHalo(id);
218
+ }
219
+ this.selectedIds = newIds;
220
+ try {
221
+ this.board.update?.();
222
+ } catch {
223
+ }
224
+ }
225
+ removeHalo(id) {
226
+ const halos = this.haloMap.get(id);
227
+ if (!halos) return;
228
+ const board = this.board;
229
+ for (const h of halos) {
230
+ try {
231
+ board.removeObject?.(h);
232
+ } catch {
233
+ }
234
+ }
235
+ this.haloMap.delete(id);
236
+ }
237
+ addHalo(id) {
238
+ const el = this.elements.get(id);
239
+ if (!el) return;
240
+ const board = this.board;
241
+ if (!board.create) return;
242
+ const SEL_STROKE = "#475569";
243
+ const SEL_FILL = "#cbd5e1";
244
+ const haloBase = {
245
+ strokeColor: SEL_STROKE,
246
+ strokeOpacity: 0.55,
247
+ fillColor: SEL_FILL,
248
+ fillOpacity: 0.3,
249
+ fixed: true,
250
+ withLabel: false,
251
+ name: "",
252
+ highlight: false,
253
+ layer: 4,
254
+ needsRegularUpdate: true
255
+ };
256
+ const halos = [];
257
+ try {
258
+ switch (el.elType) {
259
+ case "point":
260
+ case "glider":
261
+ case "intersection": {
262
+ const baseSize = el.getAttribute?.("size") ?? 4;
263
+ const halo = board.create("point", [
264
+ () => el.X?.() ?? 0,
265
+ () => el.Y?.() ?? 0
266
+ ], {
267
+ ...haloBase,
268
+ size: baseSize + 6,
269
+ face: "o",
270
+ strokeWidth: 2,
271
+ strokeOpacity: 0.75,
272
+ fillOpacity: 0.25
273
+ });
274
+ halos.push(halo);
275
+ break;
276
+ }
277
+ case "segment": {
278
+ if (el.point1 && el.point2) {
279
+ const halo = board.create("segment", [el.point1, el.point2], {
280
+ ...haloBase,
281
+ strokeWidth: 9,
282
+ straightFirst: false,
283
+ straightLast: false
284
+ });
285
+ halos.push(halo);
286
+ }
287
+ break;
288
+ }
289
+ case "line":
290
+ case "arrow":
291
+ case "ray":
292
+ case "vector":
293
+ case "tangent":
294
+ case "normal":
295
+ case "parallel":
296
+ case "perpendicular":
297
+ case "bisector": {
298
+ if (el.point1 && el.point2) {
299
+ const halo = board.create("line", [el.point1, el.point2], {
300
+ ...haloBase,
301
+ strokeWidth: 9
302
+ });
303
+ halos.push(halo);
304
+ }
305
+ break;
306
+ }
307
+ case "circle": {
308
+ if (el.center && typeof el.Radius === "function") {
309
+ const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
310
+ ...haloBase,
311
+ strokeWidth: 9,
312
+ fillOpacity: 0
313
+ });
314
+ halos.push(halo);
315
+ }
316
+ break;
317
+ }
318
+ case "polygon": {
319
+ if (Array.isArray(el.vertices) && el.vertices.length >= 3) {
320
+ const last = el.vertices.length - 1;
321
+ const verts = el.vertices[last] === el.vertices[0] ? el.vertices.slice(0, last) : el.vertices.slice();
322
+ const halo = board.create("polygon", verts, {
323
+ ...haloBase,
324
+ fillOpacity: 0.2,
325
+ borders: {
326
+ strokeColor: SEL_STROKE,
327
+ strokeWidth: 7,
328
+ strokeOpacity: 0.55,
329
+ highlight: false
330
+ }
331
+ });
332
+ halos.push(halo);
333
+ }
334
+ break;
335
+ }
336
+ default:
337
+ break;
338
+ }
339
+ } catch (err) {
340
+ console.warn("[scene/render/2d] halo create fail:", err);
341
+ }
342
+ if (halos.length) this.haloMap.set(id, halos);
343
+ }
344
+ };
345
+
346
+ export { DEFAULT_THEME_2D, JxgRenderer };
347
+ //# sourceMappingURL=chunk-BKSXPNPQ.mjs.map
348
+ //# sourceMappingURL=chunk-BKSXPNPQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/scene/render/types2d.ts","../src/core/scene/render/JxgRenderer.ts"],"names":[],"mappings":"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","file":"chunk-BKSXPNPQ.mjs","sourcesContent":["// src/core/scene/render/types2d.ts\nimport type { RenderCtx } from '../types';\n\nexport type Theme2D = {\n stroke: string;\n fill: string;\n label: string;\n axis: string;\n grid: string;\n pointFill: string;\n};\n\nexport const DEFAULT_THEME_2D: Theme2D = {\n stroke: '#0f172a',\n fill: '#60a5fa',\n label: '#0f172a',\n axis: '#94a3b8',\n grid: '#e2e8f0',\n pointFill: '#1e40af',\n};\n\nexport type RenderCtx2D = RenderCtx & {\n theme: Theme2D;\n};\n","// src/core/scene/render/JxgRenderer.ts\nimport type { Store } from '../store';\nimport type { State, SceneObject, RenderCtx } from '../types';\nimport { getKind } from '../registry';\nimport { DEFAULT_THEME_2D, type Theme2D } from './types2d';\nimport { collectFreeVars } from '../expressions/parser';\n\nexport type JxgRendererOptions = { theme?: Theme2D };\n\nexport class JxgRenderer {\n private board: unknown;\n private store: Store;\n private theme: Theme2D;\n private elements = new Map<string, unknown>();\n private unsubscribe: () => void;\n private disposed = false;\n\n /** Chỉ dùng cho domain='graph2d': parameter.label → parameter.value */\n private paramMap: Record<string, number> = {};\n /** Chỉ dùng cho domain='graph2d': function2d.id → expression string */\n private functionExpr: Record<string, string> = {};\n\n constructor(store: Store, board: unknown, options: JxgRendererOptions = {}) {\n this.store = store;\n this.board = board;\n this.theme = options.theme ?? DEFAULT_THEME_2D;\n this.unsubscribe = store.subscribe((next, prev) => this.applyDiff(prev, next));\n // Render state hiện tại (vd LOAD chạy trước khi subscribe).\n this.applyDiff(undefined, store.getState());\n }\n\n private ctx(): RenderCtx {\n return {\n jxg: this.board,\n resolveRef: (id: string) => {\n // Synthetic \"<polyId>:border:<N>\" → polygon.borders[N]. Polygon edges\n // là sub-segment do JSXGraph auto-tạo bên trong polygon; chúng không\n // có scene id riêng. Synthetic id cho phép construct tools (vd\n // perpendicular qua một cạnh đa giác) tham chiếu cạnh như một line.\n const m = /^(.+):border:(\\d+)$/.exec(id);\n if (m) {\n const poly = this.elements.get(m[1]) as { borders?: unknown[] } | undefined;\n if (!poly) throw new Error(`[scene/2d] resolveRef: chưa render polygon id=\"${m[1]}\"`);\n const borders = poly.borders;\n const idx = parseInt(m[2], 10);\n if (!Array.isArray(borders) || !borders[idx]) {\n throw new Error(`[scene/2d] resolveRef: polygon \"${m[1]}\" không có border[${idx}]`);\n }\n return borders[idx];\n }\n const el = this.elements.get(id);\n if (!el) throw new Error(`[scene/2d] resolveRef: chưa render id=\"${id}\"`);\n return el;\n },\n defaults: { theme: this.theme, _functionExpr: this.functionExpr },\n paramMap: this.paramMap,\n };\n }\n\n /**\n * Rebuild `paramMap` và `functionExpr` từ state hiện tại.\n * Chỉ chạy khi domain='graph2d'. Chi phí thấp vì parameters thường ≤ 8.\n */\n private rebuildGraphMaps(state: State): void {\n if (state.meta.domain !== 'graph2d') return;\n const params: Record<string, number> = {};\n const fns: Record<string, string> = {};\n for (const id of state.order) {\n const obj = state.objects[id];\n if (obj.kind === 'parameter') {\n params[obj.label] = (obj.attrs as { value: number }).value;\n } else if (obj.kind === 'function2d') {\n fns[obj.id] = (obj.attrs as { expression: string }).expression;\n }\n }\n this.paramMap = params;\n this.functionExpr = fns;\n }\n\n private create(obj: SceneObject): void {\n try {\n const def = getKind(obj.kind);\n const el = def.render(obj, this.ctx());\n this.elements.set(obj.id, el);\n this.attachFreePointDragSync(obj, el);\n } catch (err) {\n console.warn(`[scene/render/2d] không render được ${obj.kind} id=\"${obj.id}\":`, err);\n }\n }\n\n /**\n * Đồng bộ toạ độ live của free point về scene.constraint khi user kéo bằng\n * tay (Move tool / mobile drag). JSXGraph mutate obj.X()/Y() ngay nhưng\n * constraint vẫn giữ giá trị lúc tạo → serialize sẽ ra SVG y hệt cũ →\n * fileId SHA-256 trùng → Excalidraw bỏ qua refresh. (Regression từ\n * commit f41f366 sau scene v2 port.)\n *\n * Chỉ áp dụng cho free point — glider/intersection/midpoint không drag được\n * trực tiếp (toạ độ derived từ ref khác).\n */\n private attachFreePointDragSync(obj: SceneObject, el: unknown): void {\n if (obj.kind !== 'point') return;\n const c = (obj.attrs as { constraint?: { kind?: string } }).constraint;\n if (!c || c.kind !== 'free') return;\n \n const point = el as any;\n if (typeof point.on !== 'function') return;\n const sceneId = obj.id;\n point.on('up', () => {\n if (this.disposed) return;\n if (typeof point.X !== 'function' || typeof point.Y !== 'function') return;\n const x = point.X();\n const y = point.Y();\n if (!Number.isFinite(x) || !Number.isFinite(y)) return;\n const cur = this.store.getState().objects[sceneId];\n if (!cur) return;\n const curC = (cur.attrs as { constraint?: { kind?: string; x?: number; y?: number } }).constraint;\n if (!curC || curC.kind !== 'free') return;\n if (curC.x === x && curC.y === y) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { constraint: { kind: 'free', x, y } } },\n });\n });\n }\n\n private remove(id: string): void {\n // Selection halo (nếu có) phải bị xoá TRƯỚC element gốc — halo tham chiếu\n // tới point1/point2/center/vertices của element gốc qua lambda; xoá element\n // gốc trước sẽ làm halo dangling.\n this.removeHalo(id);\n this.selectedIds.delete(id);\n const el = this.elements.get(id);\n if (!el) return;\n try {\n const helpers = (el as Record<string, unknown>)._helpers;\n // Element chính bị remove trước, sau đó helpers (glider phụ trợ cho\n // tangent ...). Helpers thường là parent của element chính — nếu xoá\n // parent trước, JSXGraph có thể phàn nàn dangling reference.\n (this.board as { removeObject?: (e: unknown) => void }).removeObject?.(el);\n if (Array.isArray(helpers)) {\n for (const h of helpers) {\n try {\n (this.board as { removeObject?: (e: unknown) => void }).removeObject?.(h);\n } catch { /* ignore */ }\n }\n }\n } catch (err) {\n console.warn(`[scene/render/2d] không remove được id=\"${id}\":`, err);\n }\n this.elements.delete(id);\n }\n\n private applyDiff(prev: State | undefined, next: State): void {\n if (this.disposed) return;\n\n // Rebuild paramMap + functionExpr TRƯỚC khi diff, để ctx() có đúng\n // paramMap khi render lần đầu tiên.\n this.rebuildGraphMaps(next);\n\n const prevObjs = prev?.objects ?? {};\n const nextObjs = next.objects;\n\n // Xoá ids biến mất.\n for (const id of Object.keys(prevObjs)) {\n if (!(id in nextObjs)) this.remove(id);\n }\n\n // Thêm/cập nhật theo state.order — đảm bảo refs có trước.\n for (const id of next.order) {\n const cur = nextObjs[id];\n const old = prevObjs[id] as SceneObject | undefined;\n if (!old) {\n this.create(cur);\n continue;\n }\n if (Object.is(old, cur)) continue;\n const def = getKind(cur.kind);\n const existing = this.elements.get(id);\n if (def.update && existing) {\n try { def.update(cur, old, this.ctx(), existing); continue; }\n catch (err) { console.warn(`[scene/render/2d] update fail, recreate:`, err); }\n }\n this.remove(id);\n this.create(cur);\n }\n\n // Sau diff bình thường: nếu domain='graph2d', detect parameter value changes\n // và force re-render các function2d phụ thuộc.\n if (next.meta.domain === 'graph2d' && prev) {\n const changedParams = new Set<string>();\n for (const id of next.order) {\n const cur = next.objects[id];\n const old = prev.objects[id] as SceneObject | undefined;\n if (cur.kind !== 'parameter' || old?.kind !== 'parameter') continue;\n if ((cur.attrs as { value: number }).value !== (old.attrs as { value: number }).value) {\n changedParams.add(cur.label);\n }\n }\n if (changedParams.size > 0) {\n for (const id of next.order) {\n const obj = next.objects[id];\n if (obj.kind !== 'function2d') continue;\n const expr = (obj.attrs as { expression: string }).expression;\n const refs = collectFreeVars(expr);\n if (refs.some((r) => changedParams.has(r))) {\n this.remove(id);\n this.create(obj);\n }\n }\n }\n }\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.unsubscribe();\n for (const id of Array.from(this.elements.keys())) this.remove(id);\n this.disposed = true;\n }\n\n /** Return the rendered JSXGraph element for a scene id, or null if not found. */\n getElement(id: string): unknown {\n return this.elements.get(id) ?? null;\n }\n\n /** Return a read-only view of the scene id → JSXGraph element map (for hit-test). */\n listElements(): Map<string, unknown> {\n return this.elements;\n }\n\n // Selection halo overlay model: thay vì đổi màu element gốc thành đỏ, tạo\n // một halo element gray phía sau (lower layer) → giữ nguyên màu gốc. Hỗ\n // trợ multi-select cho cả canvas click-selection lẫn ObjectListPanel.\n private selectedIds: Set<string> = new Set();\n private haloMap: Map<string, unknown[]> = new Map();\n\n highlight(ids: string | string[] | null): void {\n if (this.disposed) return;\n const newIds = new Set<string>(\n ids == null ? [] : Array.isArray(ids) ? ids : [ids],\n );\n // Remove halos cho ids đã bị bỏ chọn.\n for (const id of this.selectedIds) {\n if (!newIds.has(id)) this.removeHalo(id);\n }\n // Add halos cho ids mới chọn.\n for (const id of newIds) {\n if (!this.selectedIds.has(id) && this.elements.has(id)) this.addHalo(id);\n }\n this.selectedIds = newIds;\n try {\n (this.board as { update?: () => void }).update?.();\n } catch { /* ignore */ }\n }\n\n private removeHalo(id: string): void {\n const halos = this.haloMap.get(id);\n if (!halos) return;\n const board = this.board as { removeObject?: (e: unknown) => void };\n for (const h of halos) {\n try { board.removeObject?.(h); } catch { /* ignore */ }\n }\n this.haloMap.delete(id);\n }\n\n private addHalo(id: string): void {\n const el = this.elements.get(id) as\n | {\n elType?: string;\n getAttribute?: (k: string) => unknown;\n X?: () => number;\n Y?: () => number;\n point1?: unknown;\n point2?: unknown;\n center?: unknown;\n Radius?: () => number;\n vertices?: unknown[];\n }\n | undefined;\n if (!el) return;\n const board = this.board as {\n create?: (kind: string, parents: unknown[], attrs?: unknown) => unknown;\n };\n if (!board.create) return;\n\n // Selection palette — gray fill + darker gray border (xem\n // tham chiếu /tmp/ss.png).\n const SEL_STROKE = '#475569'; // slate-600\n const SEL_FILL = '#cbd5e1'; // slate-300\n const haloBase = {\n strokeColor: SEL_STROKE,\n strokeOpacity: 0.55,\n fillColor: SEL_FILL,\n fillOpacity: 0.3,\n fixed: true,\n withLabel: false,\n name: '',\n highlight: false,\n layer: 4,\n needsRegularUpdate: true,\n };\n const halos: unknown[] = [];\n try {\n switch (el.elType) {\n case 'point':\n case 'glider':\n case 'intersection': {\n const baseSize = (el.getAttribute?.('size') as number | undefined) ?? 4;\n const halo = board.create('point', [\n () => el.X?.() ?? 0,\n () => el.Y?.() ?? 0,\n ], {\n ...haloBase,\n size: baseSize + 6,\n face: 'o',\n strokeWidth: 2,\n strokeOpacity: 0.75,\n fillOpacity: 0.25,\n });\n halos.push(halo);\n break;\n }\n case 'segment': {\n if (el.point1 && el.point2) {\n const halo = board.create('segment', [el.point1, el.point2], {\n ...haloBase,\n strokeWidth: 9,\n straightFirst: false,\n straightLast: false,\n });\n halos.push(halo);\n }\n break;\n }\n case 'line':\n case 'arrow':\n case 'ray':\n case 'vector':\n case 'tangent':\n case 'normal':\n case 'parallel':\n case 'perpendicular':\n case 'bisector': {\n if (el.point1 && el.point2) {\n const halo = board.create('line', [el.point1, el.point2], {\n ...haloBase,\n strokeWidth: 9,\n });\n halos.push(halo);\n }\n break;\n }\n case 'circle': {\n if (el.center && typeof el.Radius === 'function') {\n const halo = board.create('circle', [el.center, () => el.Radius?.() ?? 0], {\n ...haloBase,\n strokeWidth: 9,\n fillOpacity: 0,\n });\n halos.push(halo);\n }\n break;\n }\n case 'polygon': {\n if (Array.isArray(el.vertices) && el.vertices.length >= 3) {\n // JSXGraph polygon.vertices có thể append vertex đầu lặp lại ở\n // cuối để đóng path — trim cho an toàn.\n const last = el.vertices.length - 1;\n const verts = el.vertices[last] === el.vertices[0]\n ? el.vertices.slice(0, last)\n : el.vertices.slice();\n const halo = board.create('polygon', verts, {\n ...haloBase,\n fillOpacity: 0.2,\n borders: {\n strokeColor: SEL_STROKE,\n strokeWidth: 7,\n strokeOpacity: 0.55,\n highlight: false,\n },\n });\n halos.push(halo);\n }\n break;\n }\n default:\n // Các kind khác (curve, arc, sector, angle, ...) — chưa hỗ trợ halo.\n break;\n }\n } catch (err) {\n console.warn('[scene/render/2d] halo create fail:', err);\n }\n if (halos.length) this.haloMap.set(id, halos);\n }\n}\n"]}
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { isGeometryCustomData, renderGeometrySvgFromState } from './chunk-G7FR3AIV.mjs';
2
+ import { isGeometryCustomData, renderGeometrySvgFromState } from './chunk-MFOGFFIL.mjs';
3
+ import { svgToStampFile } from './chunk-5UTGXHLJ.mjs';
3
4
  import { lazy } from 'react';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
5
6
 
6
7
  var GeometryStampHost = lazy(
7
- () => import('./host-XVK7UCRE.mjs').then((m) => ({ default: m.GeometryStampHost }))
8
+ () => import('./host-DOAYVL35.mjs').then((m) => ({ default: m.GeometryStampHost }))
8
9
  );
9
10
  var GeometryIcon = /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
10
11
  /* @__PURE__ */ jsx("polygon", { points: "4,20 20,20 12,5" }),
@@ -29,16 +30,13 @@ var geometryStamp = {
29
30
  async restoreFileFromCustomData(element) {
30
31
  const data = element.customData;
31
32
  const fileId = element.fileId;
32
- if (!data || !fileId) return null;
33
- if (!isGeometryCustomData(data)) return null;
33
+ if (!data || !fileId || !isGeometryCustomData(data)) return null;
34
34
  const svgString = await renderGeometrySvgFromState(data.jsonState);
35
- const utf8 = unescape(encodeURIComponent(svgString));
36
- const dataURL = "data:image/svg+xml;base64," + (typeof btoa !== "undefined" ? btoa(utf8) : Buffer.from(utf8).toString("base64"));
37
- return { fileId, dataURL, mimeType: "image/svg+xml" };
35
+ return svgToStampFile(svgString, fileId);
38
36
  },
39
37
  Host: GeometryStampHost
40
38
  };
41
39
 
42
40
  export { geometryStamp };
43
- //# sourceMappingURL=chunk-YVJP7NRG.mjs.map
44
- //# sourceMappingURL=chunk-YVJP7NRG.mjs.map
41
+ //# sourceMappingURL=chunk-CRAPWQKJ.mjs.map
42
+ //# sourceMappingURL=chunk-CRAPWQKJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamps/geometry-2d/index.tsx"],"names":[],"mappings":";;;;;AAgBA,IAAM,iBAAA,GAAoB,IAAA;AAAA,EAAK,MAC7B,OAAO,qBAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,MAAO,EAAE,OAAA,EAAS,CAAA,CAAE,iBAAA,EAAkB,CAAE;AACjE,CAAA;AAEA,IAAM,YAAA,wBACH,KAAA,EAAA,EAAI,KAAA,EAAM,MAAK,MAAA,EAAO,IAAA,EAAK,SAAQ,WAAA,EAAY,IAAA,EAAK,QAAO,MAAA,EAAO,cAAA,EAAe,aAAY,KAAA,EAAM,aAAA,EAAc,SAAQ,cAAA,EAAe,OAAA,EAAQ,eAAY,MAAA,EAC3J,QAAA,EAAA;AAAA,kBAAA,GAAA,CAAC,SAAA,EAAA,EAAQ,QAAO,iBAAA,EAAkB,CAAA;AAAA,kBAClC,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBACjE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO,CAAA;AAAA,kBAClE,GAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,GAAA,EAAI,CAAA,EAAE,KAAA,EAAM,IAAA,EAAK,cAAA,EAAe,MAAA,EAAO,MAAA,EAAO;AAAA,CAAA,EACnE,CAAA;AAGK,IAAM,aAAA,GAA+C;AAAA,EAC1D,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,YAAA,EAAc,GAAA;AAAA,EACd,YAAA,EAAc,8BAAA;AAAA,EACd,WAAA,EAAa,YAAA;AAAA,EACb,aAAA,EAAe,wBAAA;AAAA,EACf,iBAAA,EAAmB,oBAAA;AAAA,EACnB,MAAM,wBAAwB,IAAA,EAAM;AAClC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAI,CAAA,EAAG;AAC/B,MAAA,MAAM,IAAI,MAAM,+EAAuE,CAAA;AAAA,IACzF;AACA,IAAA,OAAO,0BAAA,CAA2B,KAAK,SAAS,CAAA;AAAA,EAClD,CAAA;AAAA,EACA,MAAM,0BAA0B,OAAA,EAA4C;AAC1E,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA;AACrB,IAAA,MAAM,SAAU,OAAA,CAAuC,MAAA;AACvD,IAAA,IAAI,CAAC,QAAQ,CAAC,MAAA,IAAU,CAAC,oBAAA,CAAqB,IAAI,GAAG,OAAO,IAAA;AAC5D,IAAA,MAAM,SAAA,GAAY,MAAM,0BAAA,CAA2B,IAAA,CAAK,SAAS,CAAA;AACjE,IAAA,OAAO,cAAA,CAAe,WAAW,MAAM,CAAA;AAAA,EACzC,CAAA;AAAA,EACA,IAAA,EAAM;AACR","file":"chunk-CRAPWQKJ.mjs","sourcesContent":["'use client';\n\nimport { lazy, type ReactNode } from 'react';\nimport { renderGeometrySvgFromState } from './render';\nimport type {\n RestoredStampFile,\n StampType,\n} from '../shared/types';\nimport { svgToStampFile } from '../shared/svgToStampFile';\nimport {\n isGeometryCustomData,\n type GeometryCustomData,\n} from './types';\n\nexport type { GeometryCustomData };\n\nconst GeometryStampHost = lazy(() =>\n import('./host').then((m) => ({ default: m.GeometryStampHost })),\n);\n\nconst GeometryIcon: ReactNode = (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polygon points=\"4,20 20,20 12,5\" />\n <circle cx=\"4\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"20\" cy=\"20\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"12\" cy=\"5\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n);\n\nexport const geometryStamp: StampType<GeometryCustomData> = {\n kind: 'geometry',\n shortcutKey: 'g',\n toolbarLabel: 'G',\n toolbarTitle: 'Chèn hình học (G)',\n toolbarIcon: GeometryIcon,\n toolbarTestId: 'stamp-toolbar-geometry',\n matchesCustomData: isGeometryCustomData,\n async renderSvgFromCustomData(data) {\n if (!isGeometryCustomData(data)) {\n throw new Error('geometryStamp.renderSvgFromCustomData: customData không phải geometry');\n }\n return renderGeometrySvgFromState(data.jsonState);\n },\n async restoreFileFromCustomData(element): Promise<RestoredStampFile | null> {\n const data = element.customData as GeometryCustomData | undefined;\n const fileId = (element as { fileId?: string | null }).fileId;\n if (!data || !fileId || !isGeometryCustomData(data)) return null;\n const svgString = await renderGeometrySvgFromState(data.jsonState);\n return svgToStampFile(svgString, fileId);\n },\n Host: GeometryStampHost,\n};\n"]}