@xom11/whiteboard 0.27.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 (60) hide show
  1. package/dist/ai.d.mts +1 -1
  2. package/dist/ai.d.ts +1 -1
  3. package/dist/ai.js +54 -23
  4. package/dist/ai.js.map +1 -1
  5. package/dist/ai.mjs +54 -23
  6. package/dist/ai.mjs.map +1 -1
  7. package/dist/catalog.json +4 -4
  8. package/dist/{chunk-D5JLJ3PT.mjs → chunk-5JM35CXV.mjs} +4 -4
  9. package/dist/{chunk-D5JLJ3PT.mjs.map → chunk-5JM35CXV.mjs.map} +1 -1
  10. package/dist/{chunk-KYMBUTPO.mjs → chunk-BU5KLO3P.mjs} +3 -3
  11. package/dist/{chunk-KYMBUTPO.mjs.map → chunk-BU5KLO3P.mjs.map} +1 -1
  12. package/dist/{chunk-AYJPOHCI.mjs → chunk-H22OZYTW.mjs} +3 -3
  13. package/dist/{chunk-AYJPOHCI.mjs.map → chunk-H22OZYTW.mjs.map} +1 -1
  14. package/dist/{chunk-I3L56GVH.mjs → chunk-OQIQNKPQ.mjs} +3 -3
  15. package/dist/{chunk-I3L56GVH.mjs.map → chunk-OQIQNKPQ.mjs.map} +1 -1
  16. package/dist/{chunk-KZGPSTZI.mjs → chunk-QCZVFEN4.mjs} +4 -4
  17. package/dist/{chunk-KZGPSTZI.mjs.map → chunk-QCZVFEN4.mjs.map} +1 -1
  18. package/dist/{chunk-4ETJ4CDY.mjs → chunk-QRUAEXLR.mjs} +4 -4
  19. package/dist/{chunk-4ETJ4CDY.mjs.map → chunk-QRUAEXLR.mjs.map} +1 -1
  20. package/dist/{chunk-HLAOGXEK.mjs → chunk-V3YJ6JFL.mjs} +3 -3
  21. package/dist/{chunk-HLAOGXEK.mjs.map → chunk-V3YJ6JFL.mjs.map} +1 -1
  22. package/dist/{chunk-D5LWSN2Y.mjs → chunk-ZTQBUKLJ.mjs} +30 -13
  23. package/dist/chunk-ZTQBUKLJ.mjs.map +1 -0
  24. package/dist/geometry-2d.d.mts +1 -1
  25. package/dist/geometry-2d.d.ts +1 -1
  26. package/dist/geometry-2d.js +143 -33
  27. package/dist/geometry-2d.js.map +1 -1
  28. package/dist/geometry-2d.mjs +3 -3
  29. package/dist/geometry-3d.d.mts +1 -1
  30. package/dist/geometry-3d.d.ts +1 -1
  31. package/dist/geometry-3d.js +28 -11
  32. package/dist/geometry-3d.js.map +1 -1
  33. package/dist/geometry-3d.mjs +3 -3
  34. package/dist/graph-2d.d.mts +1 -1
  35. package/dist/graph-2d.d.ts +1 -1
  36. package/dist/graph-2d.js +28 -11
  37. package/dist/graph-2d.js.map +1 -1
  38. package/dist/graph-2d.mjs +3 -3
  39. package/dist/{host-M26FS244.mjs → host-2ISGVO7O.mjs} +5 -5
  40. package/dist/{host-M26FS244.mjs.map → host-2ISGVO7O.mjs.map} +1 -1
  41. package/dist/{host-HAYCJJ2T.mjs → host-4P766V4J.mjs} +110 -27
  42. package/dist/host-4P766V4J.mjs.map +1 -0
  43. package/dist/{host-LTJHAY5A.mjs → host-HOSJHQ5H.mjs} +6 -6
  44. package/dist/{host-LTJHAY5A.mjs.map → host-HOSJHQ5H.mjs.map} +1 -1
  45. package/dist/index.d.mts +9 -4
  46. package/dist/index.d.ts +9 -4
  47. package/dist/index.js +147 -35
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +17 -15
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/latex.d.mts +1 -1
  52. package/dist/latex.d.ts +1 -1
  53. package/dist/serialize-N4G6RFBB.mjs +9 -0
  54. package/dist/{serialize-C3LSUMSA.mjs.map → serialize-N4G6RFBB.mjs.map} +1 -1
  55. package/dist/{types-zc_Pa0mp.d.mts → types-BHYC2Fiw.d.mts} +25 -1
  56. package/dist/{types-zc_Pa0mp.d.ts → types-BHYC2Fiw.d.ts} +25 -1
  57. package/package.json +1 -1
  58. package/dist/chunk-D5LWSN2Y.mjs.map +0 -1
  59. package/dist/host-HAYCJJ2T.mjs.map +0 -1
  60. package/dist/serialize-C3LSUMSA.mjs +0 -9
package/dist/index.js CHANGED
@@ -2296,9 +2296,11 @@ var init_circle = __esm({
2296
2296
  },
2297
2297
  render: (obj, ctx) => {
2298
2298
  const board = ctx.jxg;
2299
+ const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
2300
+ const isCenter = isCenterLabel(obj.label);
2299
2301
  const baseOpts = {
2300
2302
  name: obj.label,
2301
- withLabel: obj.attrs.showLabel ?? false,
2303
+ withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
2302
2304
  strokeColor: obj.attrs.color ?? "#0f172a",
2303
2305
  strokeWidth: obj.attrs.width ?? 2,
2304
2306
  dash: obj.attrs.dash ?? 0,
@@ -2311,22 +2313,37 @@ var init_circle = __esm({
2311
2313
  const p1 = ctx.resolveRef(c.p1);
2312
2314
  const p2 = ctx.resolveRef(c.p2);
2313
2315
  const p3 = ctx.resolveRef(c.p3);
2316
+ if (isCenter) {
2317
+ const center2 = board.create("circumcenter", [p1, p2, p3], {
2318
+ visible: obj.visible,
2319
+ withLabel: true,
2320
+ fixed: true,
2321
+ name: obj.label
2322
+ });
2323
+ const circ = board.create("circumcircle", [p1, p2, p3], { ...baseOpts, withLabel: false });
2324
+ circ.center = circ.center ?? center2;
2325
+ circ._helpers = [center2];
2326
+ return circ;
2327
+ }
2314
2328
  return board.create("circumcircle", [p1, p2, p3], baseOpts);
2315
2329
  }
2316
2330
  if (c?.kind === "incircle") {
2317
2331
  const p1 = ctx.resolveRef(c.p1);
2318
2332
  const p2 = ctx.resolveRef(c.p2);
2319
2333
  const p3 = ctx.resolveRef(c.p3);
2320
- const center2 = board.create("incenter", [p1, p2, p3], {
2321
- visible: obj.visible,
2322
- withLabel: true,
2323
- fixed: true,
2324
- name: obj.label
2325
- });
2326
- const circ = board.create("incircle", [p1, p2, p3], baseOpts);
2327
- circ.center = circ.center ?? center2;
2328
- circ._helpers = [center2];
2329
- return circ;
2334
+ if (isCenter) {
2335
+ const center2 = board.create("incenter", [p1, p2, p3], {
2336
+ visible: obj.visible,
2337
+ withLabel: true,
2338
+ fixed: true,
2339
+ name: obj.label
2340
+ });
2341
+ const circ = board.create("incircle", [p1, p2, p3], { ...baseOpts, withLabel: false });
2342
+ circ.center = circ.center ?? center2;
2343
+ circ._helpers = [center2];
2344
+ return circ;
2345
+ }
2346
+ return board.create("incircle", [p1, p2, p3], baseOpts);
2330
2347
  }
2331
2348
  if (c?.kind === "excircle") {
2332
2349
  const P = [ctx.resolveRef(c.p1), ctx.resolveRef(c.p2), ctx.resolveRef(c.p3)];
@@ -12552,6 +12569,94 @@ var init_AiFigurePrompt = __esm({
12552
12569
  StopIcon = (props) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
12553
12570
  }
12554
12571
  });
12572
+
12573
+ // src/stamps/geometry-2d/draft.ts
12574
+ function svgIntrinsicSize(svg) {
12575
+ const w = svg.match(/<svg[^>]*\swidth="([\d.]+)"/);
12576
+ const h = svg.match(/<svg[^>]*\sheight="([\d.]+)"/);
12577
+ if (w && h) return { width: parseFloat(w[1]), height: parseFloat(h[1]) };
12578
+ const vb = svg.match(/viewBox="0 0 ([\d.]+) ([\d.]+)"/);
12579
+ if (vb) return { width: parseFloat(vb[1]), height: parseFloat(vb[2]) };
12580
+ return { width: 300, height: 200 };
12581
+ }
12582
+ function draftFromViewport(svg, appState, seq) {
12583
+ const { width, height } = svgIntrinsicSize(svg);
12584
+ const zoom = appState.zoom?.value ?? 1;
12585
+ const vw = appState.width ?? 800;
12586
+ const vh = appState.height ?? 600;
12587
+ const cx = appState.scrollX + vw / 2 / zoom;
12588
+ const cy = appState.scrollY + vh / 2 / zoom;
12589
+ return { svg, width, height, x: cx - width / 2, y: cy - height / 2, seq };
12590
+ }
12591
+ function didStateChange(seen, jsonState) {
12592
+ if (seen.last === jsonState) return false;
12593
+ seen.last = jsonState;
12594
+ return true;
12595
+ }
12596
+ var init_draft = __esm({
12597
+ "src/stamps/geometry-2d/draft.ts"() {
12598
+ }
12599
+ });
12600
+ function useGeometryDraftEmit({
12601
+ store,
12602
+ handleRef,
12603
+ api,
12604
+ showAxis,
12605
+ showGrid,
12606
+ onGeometryDraft,
12607
+ debounceMs = 350
12608
+ }) {
12609
+ const seqRef = React19.useRef(0);
12610
+ const seenRef = React19.useRef({ last: null });
12611
+ const timerRef = React19.useRef(null);
12612
+ const cbRef = React19.useRef(onGeometryDraft);
12613
+ cbRef.current = onGeometryDraft;
12614
+ React19.useEffect(() => {
12615
+ if (!cbRef.current) return;
12616
+ const emit = () => {
12617
+ const h = handleRef.current;
12618
+ if (!h) return;
12619
+ const state = h.getState();
12620
+ if (Object.keys(state.objects).length === 0) {
12621
+ if (seenRef.current.last !== null) {
12622
+ seenRef.current.last = null;
12623
+ cbRef.current?.(null);
12624
+ }
12625
+ return;
12626
+ }
12627
+ const bbox = h.getBbox();
12628
+ const jsonState = serializeBoard(state, { bbox, showAxis, showGrid });
12629
+ if (!didStateChange(seenRef.current, jsonState)) return;
12630
+ void (async () => {
12631
+ try {
12632
+ const svg = await renderGeometrySvgFromState(jsonState);
12633
+ const appState = api?.getAppState?.() ?? { scrollX: 0, scrollY: 0, width: 800, height: 600, zoom: { value: 1 } };
12634
+ seqRef.current += 1;
12635
+ cbRef.current?.(draftFromViewport(svg, appState, seqRef.current));
12636
+ } catch (err) {
12637
+ console.warn("[geometry] draft render failed:", err);
12638
+ }
12639
+ })();
12640
+ };
12641
+ const schedule = () => {
12642
+ if (timerRef.current) clearTimeout(timerRef.current);
12643
+ timerRef.current = setTimeout(emit, debounceMs);
12644
+ };
12645
+ const unsub = store.subscribe(schedule);
12646
+ return () => {
12647
+ unsub();
12648
+ if (timerRef.current) clearTimeout(timerRef.current);
12649
+ cbRef.current?.(null);
12650
+ };
12651
+ }, [store, handleRef, api, showAxis, showGrid, debounceMs]);
12652
+ }
12653
+ var init_useGeometryDraftEmit = __esm({
12654
+ "src/stamps/geometry-2d/editor/useGeometryDraftEmit.ts"() {
12655
+ init_serialize();
12656
+ init_render();
12657
+ init_draft();
12658
+ }
12659
+ });
12555
12660
  var GeometryEditorPanelInner, GeometryEditorPanel;
12556
12661
  var init_EditorPanel = __esm({
12557
12662
  "src/stamps/geometry-2d/editor/EditorPanel.tsx"() {
@@ -12568,6 +12673,7 @@ var init_EditorPanel = __esm({
12568
12673
  init_constants();
12569
12674
  init_Toast2();
12570
12675
  init_AiFigurePrompt();
12676
+ init_useGeometryDraftEmit();
12571
12677
  GeometryEditorPanelInner = React19.forwardRef(
12572
12678
  function GeometryEditorPanelInner2({
12573
12679
  store,
@@ -12586,7 +12692,9 @@ var init_EditorPanel = __esm({
12586
12692
  canUndo,
12587
12693
  canRedo,
12588
12694
  onSelectionChange,
12589
- generateGeometryFigure
12695
+ generateGeometryFigure,
12696
+ api,
12697
+ onGeometryDraft
12590
12698
  }, ref) {
12591
12699
  const { showToast } = useToast();
12592
12700
  const handleRef = React19.useRef(null);
@@ -12599,12 +12707,14 @@ var init_EditorPanel = __esm({
12599
12707
  React19.useEffect(() => {
12600
12708
  onSelectionChangeRef.current = onSelectionChange;
12601
12709
  }, [onSelectionChange]);
12710
+ const onGeometryDraftRef = React19.useRef(onGeometryDraft);
12711
+ React19.useEffect(() => {
12712
+ onGeometryDraftRef.current = onGeometryDraft;
12713
+ }, [onGeometryDraft]);
12602
12714
  useEditorState({ store, onHistoryChange });
12603
- const currentSceneState = React19.useSyncExternalStore(
12604
- (cb) => store.subscribe(cb),
12605
- () => store.getState(),
12606
- () => store.getState()
12607
- );
12715
+ useGeometryDraftEmit({ store, handleRef, api, showAxis, showGrid, onGeometryDraft });
12716
+ const snap = () => store.getState();
12717
+ const currentSceneState = React19.useSyncExternalStore((cb) => store.subscribe(cb), snap, snap);
12608
12718
  React19.useEffect(() => {
12609
12719
  const sync = () => setHasContent(Object.keys(store.getState().objects).length > 0);
12610
12720
  sync();
@@ -12614,22 +12724,22 @@ var init_EditorPanel = __esm({
12614
12724
  const h = handleRef.current;
12615
12725
  if (!h) return;
12616
12726
  setReady(true);
12617
- h.onSelect((snap) => {
12618
- setPropsPopover(snap);
12727
+ h.onSelect((snap2) => {
12728
+ setPropsPopover(snap2);
12619
12729
  setMultiSelection(null);
12620
- onSelectionChangeRef.current?.(snap.id);
12730
+ onSelectionChangeRef.current?.(snap2.id);
12621
12731
  });
12622
12732
  h.onTransformParam((info) => setTransformPopover(info));
12623
- h.onSelectionState((snap) => {
12624
- if (!snap || snap.ids.length === 0) {
12733
+ h.onSelectionState((snap2) => {
12734
+ if (!snap2 || snap2.ids.length === 0) {
12625
12735
  setPropsPopover(null);
12626
12736
  setMultiSelection(null);
12627
12737
  onSelectionChangeRef.current?.(void 0);
12628
12738
  return;
12629
12739
  }
12630
- if (snap.ids.length === 1) {
12631
- const id = snap.ids[0];
12632
- const single = buildObjectSnapshot(store.getState(), id, snap.anchor);
12740
+ if (snap2.ids.length === 1) {
12741
+ const id = snap2.ids[0];
12742
+ const single = buildObjectSnapshot(store.getState(), id, snap2.anchor);
12633
12743
  if (single) {
12634
12744
  setPropsPopover(single);
12635
12745
  setMultiSelection(null);
@@ -12637,7 +12747,7 @@ var init_EditorPanel = __esm({
12637
12747
  }
12638
12748
  return;
12639
12749
  }
12640
- setMultiSelection(snap);
12750
+ setMultiSelection(snap2);
12641
12751
  setPropsPopover(null);
12642
12752
  onSelectionChangeRef.current?.(void 0);
12643
12753
  });
@@ -12681,15 +12791,13 @@ var init_EditorPanel = __esm({
12681
12791
  try {
12682
12792
  const svgString = await renderGeometrySvgFromState(jsonState);
12683
12793
  onInsert(jsonState, svgString);
12794
+ onGeometryDraftRef.current?.(null);
12684
12795
  } catch (err) {
12685
12796
  console.error("Geometry insert failed:", err);
12686
12797
  }
12687
12798
  })();
12688
12799
  return true;
12689
12800
  }, [onInsert, showAxis, showGrid]);
12690
- const handleInsert = React19.useCallback(() => {
12691
- performInsert();
12692
- }, [performInsert]);
12693
12801
  const loadAiFigure = React19.useCallback((generated) => {
12694
12802
  handleRef.current?.clearSelection();
12695
12803
  setPropsPopover(null);
@@ -12784,7 +12892,7 @@ var init_EditorPanel = __esm({
12784
12892
  "button",
12785
12893
  {
12786
12894
  type: "button",
12787
- onClick: handleInsert,
12895
+ onClick: performInsert,
12788
12896
  disabled: !ready || !hasContent,
12789
12897
  title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
12790
12898
  "data-testid": "geometry-insert-btn-mobile",
@@ -12902,7 +13010,7 @@ var init_EditorPanel = __esm({
12902
13010
  /* @__PURE__ */ jsxRuntime.jsx(
12903
13011
  "button",
12904
13012
  {
12905
- onClick: handleInsert,
13013
+ onClick: performInsert,
12906
13014
  disabled: !ready || !hasContent,
12907
13015
  title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
12908
13016
  "data-testid": "geometry-insert-btn",
@@ -13263,7 +13371,7 @@ var init_host = __esm({
13263
13371
  init_useStampStore();
13264
13372
  init_dslRenderRow();
13265
13373
  GeometryStampHost = React19.forwardRef(
13266
- function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure }, ref) {
13374
+ function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure, onGeometryDraft }, ref) {
13267
13375
  const panelRef = React19.useRef(null);
13268
13376
  const { isMobile } = useIsMobile();
13269
13377
  const [drawerOpen, setDrawerOpen] = React19.useState(false);
@@ -13378,7 +13486,9 @@ var init_host = __esm({
13378
13486
  canUndo,
13379
13487
  canRedo,
13380
13488
  onSelectionChange: setSelectedObjectId,
13381
- generateGeometryFigure
13489
+ generateGeometryFigure,
13490
+ api,
13491
+ onGeometryDraft
13382
13492
  }
13383
13493
  )
13384
13494
  ] });
@@ -20388,7 +20498,8 @@ function Whiteboard({
20388
20498
  stamps = DEFAULT_STAMPS,
20389
20499
  initialScene,
20390
20500
  initialFiles,
20391
- generateGeometryFigure
20501
+ generateGeometryFigure,
20502
+ onGeometryDraft
20392
20503
  }) {
20393
20504
  const { api, apiRef, isDark, setApiFromExcalidraw, syncThemeFromAppState } = useExcalidrawApi({ onApi });
20394
20505
  const {
@@ -20561,7 +20672,8 @@ function Whiteboard({
20561
20672
  editingElement,
20562
20673
  onClose: closeStamp,
20563
20674
  isDark,
20564
- generateGeometryFigure
20675
+ generateGeometryFigure,
20676
+ onGeometryDraft
20565
20677
  }
20566
20678
  )
20567
20679
  ] });