@xom11/whiteboard 0.9.1 → 0.10.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.
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- export { geometryStamp } from './chunk-DU3RHKT5.mjs';
3
- export { isGeometryCustomData } from './chunk-KEYZ5EZT.mjs';
2
+ export { geometryStamp } from './chunk-PDKKDZ4H.mjs';
3
+ export { isGeometryCustomData } from './chunk-G7FR3AIV.mjs';
4
4
  import './chunk-HTBLO5JO.mjs';
5
5
  import './chunk-BJTO5JO5.mjs';
6
6
  //# sourceMappingURL=geometry-2d.mjs.map
@@ -161,6 +161,108 @@ var init_theme2 = __esm({
161
161
  }
162
162
  });
163
163
 
164
+ // src/stamps/geometry-3d/render.ts
165
+ async function renderGeometry3DSvgFromState(jsonState) {
166
+ const state = parseSerializedBoard3D(jsonState);
167
+ const JXG = (await import('jsxgraph')).default;
168
+ const div = document.createElement("div");
169
+ div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;
170
+ document.body.appendChild(div);
171
+ try {
172
+ JXG.Options.text.display = "internal";
173
+ const board = JXG.JSXGraph.initBoard(div, {
174
+ boundingbox: state.bbox,
175
+ keepaspectratio: true,
176
+ axis: false,
177
+ showCopyright: false,
178
+ showNavigation: false,
179
+ renderer: "svg"
180
+ });
181
+ const baseAttrs = VIEW3D_ATTRS(false);
182
+ const view = board.create(
183
+ "view3d",
184
+ [
185
+ [-5, -5],
186
+ [10, 10],
187
+ [
188
+ [state.view.bbox3D[0], state.view.bbox3D[3]],
189
+ [state.view.bbox3D[1], state.view.bbox3D[4]],
190
+ [state.view.bbox3D[2], state.view.bbox3D[5]]
191
+ ]
192
+ ],
193
+ {
194
+ ...baseAttrs,
195
+ // JSXGraph view3d đọc azimuth/elevation từ az.slider.start (không phải
196
+ // az.value). Nếu pass `value` → JSXGraph bỏ qua → render rơi về default
197
+ // (1.0 rad / 0.3 rad), không khớp góc user xoay trong editor.
198
+ az: { ...baseAttrs.az, slider: { ...baseAttrs.az.slider, start: state.view.azimuth } },
199
+ el: { ...baseAttrs.el, slider: { ...baseAttrs.el.slider, start: state.view.elevation } }
200
+ }
201
+ );
202
+ try {
203
+ const v = view;
204
+ v?.az_slide?.setValue?.(state.view.azimuth);
205
+ v?.el_slide?.setValue?.(state.view.elevation);
206
+ v?.board?.update?.();
207
+ } catch {
208
+ }
209
+ if (!state.showAxes) {
210
+ view.defaultAxes = [];
211
+ }
212
+ try {
213
+ view.create(
214
+ "plane3d",
215
+ [
216
+ [0, 0, 0],
217
+ [1, 0, 0],
218
+ [0, 1, 0],
219
+ GROUND_PLANE_RANGE,
220
+ GROUND_PLANE_RANGE
221
+ ],
222
+ GROUND_PLANE_ATTRS(false)
223
+ );
224
+ } catch {
225
+ }
226
+ const idMap = /* @__PURE__ */ new Map();
227
+ for (const el of state.elements) {
228
+ const parents = el.parents.map(
229
+ (p) => typeof p === "string" && p.startsWith("@id:") ? idMap.get(p.slice(4)) : p
230
+ );
231
+ const obj = view.create(el.type, parents, {
232
+ ...el.attributes,
233
+ id: el.id,
234
+ name: el.label
235
+ });
236
+ idMap.set(el.id, obj);
237
+ }
238
+ const svg = div.querySelector("svg");
239
+ if (!svg) {
240
+ throw new Error("renderGeometry3DSvgFromState: SVG not produced");
241
+ }
242
+ const clone = svg.cloneNode(true);
243
+ clone.setAttribute("width", String(OUTPUT_WIDTH));
244
+ clone.setAttribute("height", String(OUTPUT_HEIGHT));
245
+ const svgString = new XMLSerializer().serializeToString(clone);
246
+ try {
247
+ JXG.JSXGraph.freeBoard(board);
248
+ } catch {
249
+ }
250
+ return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };
251
+ } finally {
252
+ document.body.removeChild(div);
253
+ }
254
+ }
255
+ var OUTPUT_WIDTH, OUTPUT_HEIGHT;
256
+ var init_render = __esm({
257
+ "src/stamps/geometry-3d/render.ts"() {
258
+ "use client";
259
+ init_serialize();
260
+ init_theme2();
261
+ OUTPUT_WIDTH = 1024;
262
+ OUTPUT_HEIGHT = 768;
263
+ }
264
+ });
265
+
164
266
  // src/stamps/geometry-3d/editor/tools/handlers/_ensurePoint.ts
165
267
  function hitToConstraint(hit) {
166
268
  switch (hit.kind) {
@@ -1562,8 +1664,11 @@ var init_MiniBoard3D = __esm({
1562
1664
  ],
1563
1665
  {
1564
1666
  ...baseAttrs,
1565
- az: { ...baseAttrs.az, value: DEFAULT_VIEW3D.azimuth },
1566
- el: { ...baseAttrs.el, value: DEFAULT_VIEW3D.elevation }
1667
+ // JSXGraph view3d đọc giá trị khởi tạo từ az.slider.start (không
1668
+ // phải az.value). Pass nhầm `value` JSXGraph dùng default
1669
+ // 1.0/0.3, khiến DEFAULT_VIEW3D bị bỏ qua.
1670
+ az: { ...baseAttrs.az, slider: { ...baseAttrs.az.slider, start: DEFAULT_VIEW3D.azimuth } },
1671
+ el: { ...baseAttrs.el, slider: { ...baseAttrs.el.slider, start: DEFAULT_VIEW3D.elevation } }
1567
1672
  }
1568
1673
  );
1569
1674
  } catch {
@@ -2200,6 +2305,7 @@ var init_EditorPanel = __esm({
2200
2305
  init_ensurePoint();
2201
2306
  init_MiniBoard3D();
2202
2307
  init_StatusHint();
2308
+ init_theme2();
2203
2309
  init_persistence();
2204
2310
  EditorPanel = React2__namespace.forwardRef(
2205
2311
  function EditorPanel2(props, ref) {
@@ -2305,8 +2411,17 @@ var init_EditorPanel = __esm({
2305
2411
  }, [showAxis, showGrid]);
2306
2412
  const handleView3DReady = React2__namespace.useCallback((view) => {
2307
2413
  rendererRef.current = new JxgRenderer(scene, view);
2414
+ if (initialState) {
2415
+ try {
2416
+ const v = view;
2417
+ v?.az_slide?.setValue?.(initialState.view.azimuth);
2418
+ v?.el_slide?.setValue?.(initialState.view.elevation);
2419
+ v?.board?.update?.();
2420
+ } catch {
2421
+ }
2422
+ }
2308
2423
  onReadyChange?.(true);
2309
- }, [onReadyChange, scene]);
2424
+ }, [onReadyChange, scene, initialState]);
2310
2425
  const handleClick = React2__namespace.useCallback((screen) => {
2311
2426
  const board = boardRef.current;
2312
2427
  if (!board) return;
@@ -2444,8 +2559,10 @@ var init_EditorPanel = __esm({
2444
2559
  const elevation = typeof elSlider?.Value === "function" ? elSlider.Value() : 0;
2445
2560
  return sceneToBoard(
2446
2561
  scene,
2447
- { azimuth, elevation, bbox3D: [-5, -5, -5, 5, 5, 5] },
2448
- [-6, -6, 6, 6]
2562
+ { azimuth, elevation, bbox3D: [...DEFAULT_VIEW3D.bbox3D] },
2563
+ // JSXGraph boundingbox order: [xmin, ymax, xmax, ymin]. Must match
2564
+ // MiniBoard3D.initBoard so render reproduces the editor's view.
2565
+ [-6, 6, 6, -6]
2449
2566
  );
2450
2567
  },
2451
2568
  setTool: (k) => controllerRef.current.selectTool(k),
@@ -3722,6 +3839,7 @@ var init_host = __esm({
3722
3839
  init_useChordShortcut();
3723
3840
  init_insertImage();
3724
3841
  init_useIsMobile();
3842
+ init_render();
3725
3843
  init_serialize();
3726
3844
  Geometry3DStampHost = React2.forwardRef(
3727
3845
  function Geometry3DStampHost2({ api, editingElement, onClose, isDark }, ref) {
@@ -3736,10 +3854,25 @@ var init_host = __esm({
3736
3854
  const [showGrid, setShowGrid] = React2.useState(true);
3737
3855
  const [canUndo, setCanUndo] = React2.useState(false);
3738
3856
  const [canRedo, setCanRedo] = React2.useState(false);
3857
+ const [hasContent, setHasContent] = React2.useState(false);
3739
3858
  const handleHistoryChange = React2.useCallback((u, r) => {
3740
3859
  setCanUndo(u);
3741
3860
  setCanRedo(r);
3742
3861
  }, []);
3862
+ React2.useEffect(() => {
3863
+ const scene = sceneRef.current;
3864
+ if (!scene) return;
3865
+ const sync = () => setHasContent(scene.list().length > 0);
3866
+ sync();
3867
+ const unsubs = [
3868
+ scene.on("add", sync),
3869
+ scene.on("delete", sync),
3870
+ scene.on("reset", sync)
3871
+ ];
3872
+ return () => {
3873
+ for (const u of unsubs) u();
3874
+ };
3875
+ }, []);
3743
3876
  const handleUndo = React2.useCallback(() => {
3744
3877
  editorRef.current?.undo();
3745
3878
  }, []);
@@ -3787,7 +3920,15 @@ var init_host = __esm({
3787
3920
  if (!editorRef.current.hasContent()) return false;
3788
3921
  const board = editorRef.current.serialize();
3789
3922
  if (board.elements.length === 0) return false;
3790
- void performInsert(board, 0, 0, "");
3923
+ void (async () => {
3924
+ try {
3925
+ const jsonState = serializeBoard3D(board);
3926
+ const { svgString, width, height } = await renderGeometry3DSvgFromState(jsonState);
3927
+ await performInsert(board, width, height, svgString);
3928
+ } catch (err) {
3929
+ console.error("Geometry3D insert failed:", err);
3930
+ }
3931
+ })();
3791
3932
  return true;
3792
3933
  }, [performInsert]);
3793
3934
  React2.useImperativeHandle(
@@ -3869,7 +4010,8 @@ var init_host = __esm({
3869
4010
  {
3870
4011
  type: "button",
3871
4012
  onClick: tryInsert,
3872
- disabled: !ready,
4013
+ disabled: !ready || !hasContent,
4014
+ title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
3873
4015
  "data-testid": "geom3d-insert-btn-mobile",
3874
4016
  className: "rounded bg-white/15 px-3 py-1.5 text-xs font-semibold transition hover:bg-white/25 disabled:opacity-50",
3875
4017
  children: "Ch\xE8n"
@@ -3919,7 +4061,8 @@ var init_host = __esm({
3919
4061
  "button",
3920
4062
  {
3921
4063
  onClick: tryInsert,
3922
- disabled: !ready,
4064
+ disabled: !ready || !hasContent,
4065
+ title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
3923
4066
  "data-testid": "geom3d-insert-btn",
3924
4067
  className: "rounded bg-emerald-600 px-3 py-1 text-xs font-medium text-white transition hover:bg-emerald-700 disabled:opacity-50",
3925
4068
  children: "Ch\xE8n"
@@ -3960,91 +4103,7 @@ var init_host = __esm({
3960
4103
 
3961
4104
  // src/stamps/geometry-3d/index.tsx
3962
4105
  init_serialize();
3963
-
3964
- // src/stamps/geometry-3d/render.ts
3965
- init_serialize();
3966
- init_theme2();
3967
- var OUTPUT_WIDTH = 1024;
3968
- var OUTPUT_HEIGHT = 768;
3969
- async function renderGeometry3DSvgFromState(jsonState) {
3970
- const state = parseSerializedBoard3D(jsonState);
3971
- const JXG = (await import('jsxgraph')).default;
3972
- const div = document.createElement("div");
3973
- div.style.cssText = `position:absolute;left:-9999px;top:-9999px;width:${OUTPUT_WIDTH}px;height:${OUTPUT_HEIGHT}px;`;
3974
- document.body.appendChild(div);
3975
- try {
3976
- JXG.Options.text.display = "internal";
3977
- const board = JXG.JSXGraph.initBoard(div, {
3978
- boundingbox: state.bbox,
3979
- axis: false,
3980
- showCopyright: false,
3981
- showNavigation: false,
3982
- renderer: "svg"
3983
- });
3984
- const baseAttrs = VIEW3D_ATTRS(false);
3985
- const view = board.create(
3986
- "view3d",
3987
- [
3988
- [-5, -5],
3989
- [10, 10],
3990
- [
3991
- [state.view.bbox3D[0], state.view.bbox3D[3]],
3992
- [state.view.bbox3D[1], state.view.bbox3D[4]],
3993
- [state.view.bbox3D[2], state.view.bbox3D[5]]
3994
- ]
3995
- ],
3996
- {
3997
- ...baseAttrs,
3998
- az: { ...baseAttrs.az, value: state.view.azimuth },
3999
- el: { ...baseAttrs.el, value: state.view.elevation }
4000
- }
4001
- );
4002
- if (!state.showAxes) {
4003
- view.defaultAxes = [];
4004
- }
4005
- try {
4006
- view.create(
4007
- "plane3d",
4008
- [
4009
- [0, 0, 0],
4010
- [1, 0, 0],
4011
- [0, 1, 0],
4012
- GROUND_PLANE_RANGE,
4013
- GROUND_PLANE_RANGE
4014
- ],
4015
- GROUND_PLANE_ATTRS(false)
4016
- );
4017
- } catch {
4018
- }
4019
- const idMap = /* @__PURE__ */ new Map();
4020
- for (const el of state.elements) {
4021
- const parents = el.parents.map(
4022
- (p) => typeof p === "string" && p.startsWith("@id:") ? idMap.get(p.slice(4)) : p
4023
- );
4024
- const obj = view.create(el.type, parents, {
4025
- ...el.attributes,
4026
- id: el.id,
4027
- name: el.label
4028
- });
4029
- idMap.set(el.id, obj);
4030
- }
4031
- const svg = div.querySelector("svg");
4032
- if (!svg) {
4033
- throw new Error("renderGeometry3DSvgFromState: SVG not produced");
4034
- }
4035
- const clone = svg.cloneNode(true);
4036
- clone.setAttribute("width", String(OUTPUT_WIDTH));
4037
- clone.setAttribute("height", String(OUTPUT_HEIGHT));
4038
- const svgString = new XMLSerializer().serializeToString(clone);
4039
- try {
4040
- JXG.JSXGraph.freeBoard(board);
4041
- } catch {
4042
- }
4043
- return { svgString, width: OUTPUT_WIDTH, height: OUTPUT_HEIGHT };
4044
- } finally {
4045
- document.body.removeChild(div);
4046
- }
4047
- }
4106
+ init_render();
4048
4107
  var Geometry3DStampHost3 = React2.lazy(
4049
4108
  () => Promise.resolve().then(() => (init_host(), host_exports)).then((m) => ({ default: m.Geometry3DStampHost }))
4050
4109
  );