@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
@@ -1,4 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-zc_Pa0mp.mjs';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.mjs';
2
2
  import 'zod';
3
3
  import 'react';
4
4
  import '@excalidraw/excalidraw/element/types';
@@ -1,4 +1,4 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-zc_Pa0mp.js';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-BHYC2Fiw.js';
2
2
  import 'zod';
3
3
  import 'react';
4
4
  import '@excalidraw/excalidraw/element/types';
@@ -2293,9 +2293,11 @@ var init_circle = __esm({
2293
2293
  },
2294
2294
  render: (obj, ctx) => {
2295
2295
  const board = ctx.jxg;
2296
+ const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
2297
+ const isCenter = isCenterLabel(obj.label);
2296
2298
  const baseOpts = {
2297
2299
  name: obj.label,
2298
- withLabel: obj.attrs.showLabel ?? false,
2300
+ withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
2299
2301
  strokeColor: obj.attrs.color ?? "#0f172a",
2300
2302
  strokeWidth: obj.attrs.width ?? 2,
2301
2303
  dash: obj.attrs.dash ?? 0,
@@ -2308,22 +2310,37 @@ var init_circle = __esm({
2308
2310
  const p1 = ctx.resolveRef(c.p1);
2309
2311
  const p2 = ctx.resolveRef(c.p2);
2310
2312
  const p3 = ctx.resolveRef(c.p3);
2313
+ if (isCenter) {
2314
+ const center2 = board.create("circumcenter", [p1, p2, p3], {
2315
+ visible: obj.visible,
2316
+ withLabel: true,
2317
+ fixed: true,
2318
+ name: obj.label
2319
+ });
2320
+ const circ = board.create("circumcircle", [p1, p2, p3], { ...baseOpts, withLabel: false });
2321
+ circ.center = circ.center ?? center2;
2322
+ circ._helpers = [center2];
2323
+ return circ;
2324
+ }
2311
2325
  return board.create("circumcircle", [p1, p2, p3], baseOpts);
2312
2326
  }
2313
2327
  if (c?.kind === "incircle") {
2314
2328
  const p1 = ctx.resolveRef(c.p1);
2315
2329
  const p2 = ctx.resolveRef(c.p2);
2316
2330
  const p3 = ctx.resolveRef(c.p3);
2317
- const center2 = board.create("incenter", [p1, p2, p3], {
2318
- visible: obj.visible,
2319
- withLabel: true,
2320
- fixed: true,
2321
- name: obj.label
2322
- });
2323
- const circ = board.create("incircle", [p1, p2, p3], baseOpts);
2324
- circ.center = circ.center ?? center2;
2325
- circ._helpers = [center2];
2326
- return circ;
2331
+ if (isCenter) {
2332
+ const center2 = board.create("incenter", [p1, p2, p3], {
2333
+ visible: obj.visible,
2334
+ withLabel: true,
2335
+ fixed: true,
2336
+ name: obj.label
2337
+ });
2338
+ const circ = board.create("incircle", [p1, p2, p3], { ...baseOpts, withLabel: false });
2339
+ circ.center = circ.center ?? center2;
2340
+ circ._helpers = [center2];
2341
+ return circ;
2342
+ }
2343
+ return board.create("incircle", [p1, p2, p3], baseOpts);
2327
2344
  }
2328
2345
  if (c?.kind === "excircle") {
2329
2346
  const P = [ctx.resolveRef(c.p1), ctx.resolveRef(c.p2), ctx.resolveRef(c.p3)];
@@ -12549,6 +12566,94 @@ var init_AiFigurePrompt = __esm({
12549
12566
  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" }) });
12550
12567
  }
12551
12568
  });
12569
+
12570
+ // src/stamps/geometry-2d/draft.ts
12571
+ function svgIntrinsicSize(svg) {
12572
+ const w = svg.match(/<svg[^>]*\swidth="([\d.]+)"/);
12573
+ const h = svg.match(/<svg[^>]*\sheight="([\d.]+)"/);
12574
+ if (w && h) return { width: parseFloat(w[1]), height: parseFloat(h[1]) };
12575
+ const vb = svg.match(/viewBox="0 0 ([\d.]+) ([\d.]+)"/);
12576
+ if (vb) return { width: parseFloat(vb[1]), height: parseFloat(vb[2]) };
12577
+ return { width: 300, height: 200 };
12578
+ }
12579
+ function draftFromViewport(svg, appState, seq) {
12580
+ const { width, height } = svgIntrinsicSize(svg);
12581
+ const zoom = appState.zoom?.value ?? 1;
12582
+ const vw = appState.width ?? 800;
12583
+ const vh = appState.height ?? 600;
12584
+ const cx = appState.scrollX + vw / 2 / zoom;
12585
+ const cy = appState.scrollY + vh / 2 / zoom;
12586
+ return { svg, width, height, x: cx - width / 2, y: cy - height / 2, seq };
12587
+ }
12588
+ function didStateChange(seen, jsonState) {
12589
+ if (seen.last === jsonState) return false;
12590
+ seen.last = jsonState;
12591
+ return true;
12592
+ }
12593
+ var init_draft = __esm({
12594
+ "src/stamps/geometry-2d/draft.ts"() {
12595
+ }
12596
+ });
12597
+ function useGeometryDraftEmit({
12598
+ store,
12599
+ handleRef,
12600
+ api,
12601
+ showAxis,
12602
+ showGrid,
12603
+ onGeometryDraft,
12604
+ debounceMs = 350
12605
+ }) {
12606
+ const seqRef = React2.useRef(0);
12607
+ const seenRef = React2.useRef({ last: null });
12608
+ const timerRef = React2.useRef(null);
12609
+ const cbRef = React2.useRef(onGeometryDraft);
12610
+ cbRef.current = onGeometryDraft;
12611
+ React2.useEffect(() => {
12612
+ if (!cbRef.current) return;
12613
+ const emit = () => {
12614
+ const h = handleRef.current;
12615
+ if (!h) return;
12616
+ const state = h.getState();
12617
+ if (Object.keys(state.objects).length === 0) {
12618
+ if (seenRef.current.last !== null) {
12619
+ seenRef.current.last = null;
12620
+ cbRef.current?.(null);
12621
+ }
12622
+ return;
12623
+ }
12624
+ const bbox = h.getBbox();
12625
+ const jsonState = serializeBoard(state, { bbox, showAxis, showGrid });
12626
+ if (!didStateChange(seenRef.current, jsonState)) return;
12627
+ void (async () => {
12628
+ try {
12629
+ const svg = await renderGeometrySvgFromState(jsonState);
12630
+ const appState = api?.getAppState?.() ?? { scrollX: 0, scrollY: 0, width: 800, height: 600, zoom: { value: 1 } };
12631
+ seqRef.current += 1;
12632
+ cbRef.current?.(draftFromViewport(svg, appState, seqRef.current));
12633
+ } catch (err) {
12634
+ console.warn("[geometry] draft render failed:", err);
12635
+ }
12636
+ })();
12637
+ };
12638
+ const schedule = () => {
12639
+ if (timerRef.current) clearTimeout(timerRef.current);
12640
+ timerRef.current = setTimeout(emit, debounceMs);
12641
+ };
12642
+ const unsub = store.subscribe(schedule);
12643
+ return () => {
12644
+ unsub();
12645
+ if (timerRef.current) clearTimeout(timerRef.current);
12646
+ cbRef.current?.(null);
12647
+ };
12648
+ }, [store, handleRef, api, showAxis, showGrid, debounceMs]);
12649
+ }
12650
+ var init_useGeometryDraftEmit = __esm({
12651
+ "src/stamps/geometry-2d/editor/useGeometryDraftEmit.ts"() {
12652
+ init_serialize();
12653
+ init_render();
12654
+ init_draft();
12655
+ }
12656
+ });
12552
12657
  var GeometryEditorPanelInner, GeometryEditorPanel;
12553
12658
  var init_EditorPanel = __esm({
12554
12659
  "src/stamps/geometry-2d/editor/EditorPanel.tsx"() {
@@ -12565,6 +12670,7 @@ var init_EditorPanel = __esm({
12565
12670
  init_constants();
12566
12671
  init_Toast2();
12567
12672
  init_AiFigurePrompt();
12673
+ init_useGeometryDraftEmit();
12568
12674
  GeometryEditorPanelInner = React2.forwardRef(
12569
12675
  function GeometryEditorPanelInner2({
12570
12676
  store,
@@ -12583,7 +12689,9 @@ var init_EditorPanel = __esm({
12583
12689
  canUndo,
12584
12690
  canRedo,
12585
12691
  onSelectionChange,
12586
- generateGeometryFigure
12692
+ generateGeometryFigure,
12693
+ api,
12694
+ onGeometryDraft
12587
12695
  }, ref) {
12588
12696
  const { showToast } = useToast();
12589
12697
  const handleRef = React2.useRef(null);
@@ -12596,12 +12704,14 @@ var init_EditorPanel = __esm({
12596
12704
  React2.useEffect(() => {
12597
12705
  onSelectionChangeRef.current = onSelectionChange;
12598
12706
  }, [onSelectionChange]);
12707
+ const onGeometryDraftRef = React2.useRef(onGeometryDraft);
12708
+ React2.useEffect(() => {
12709
+ onGeometryDraftRef.current = onGeometryDraft;
12710
+ }, [onGeometryDraft]);
12599
12711
  useEditorState({ store, onHistoryChange });
12600
- const currentSceneState = React2.useSyncExternalStore(
12601
- (cb) => store.subscribe(cb),
12602
- () => store.getState(),
12603
- () => store.getState()
12604
- );
12712
+ useGeometryDraftEmit({ store, handleRef, api, showAxis, showGrid, onGeometryDraft });
12713
+ const snap = () => store.getState();
12714
+ const currentSceneState = React2.useSyncExternalStore((cb) => store.subscribe(cb), snap, snap);
12605
12715
  React2.useEffect(() => {
12606
12716
  const sync = () => setHasContent(Object.keys(store.getState().objects).length > 0);
12607
12717
  sync();
@@ -12611,22 +12721,22 @@ var init_EditorPanel = __esm({
12611
12721
  const h = handleRef.current;
12612
12722
  if (!h) return;
12613
12723
  setReady(true);
12614
- h.onSelect((snap) => {
12615
- setPropsPopover(snap);
12724
+ h.onSelect((snap2) => {
12725
+ setPropsPopover(snap2);
12616
12726
  setMultiSelection(null);
12617
- onSelectionChangeRef.current?.(snap.id);
12727
+ onSelectionChangeRef.current?.(snap2.id);
12618
12728
  });
12619
12729
  h.onTransformParam((info) => setTransformPopover(info));
12620
- h.onSelectionState((snap) => {
12621
- if (!snap || snap.ids.length === 0) {
12730
+ h.onSelectionState((snap2) => {
12731
+ if (!snap2 || snap2.ids.length === 0) {
12622
12732
  setPropsPopover(null);
12623
12733
  setMultiSelection(null);
12624
12734
  onSelectionChangeRef.current?.(void 0);
12625
12735
  return;
12626
12736
  }
12627
- if (snap.ids.length === 1) {
12628
- const id = snap.ids[0];
12629
- const single = buildObjectSnapshot(store.getState(), id, snap.anchor);
12737
+ if (snap2.ids.length === 1) {
12738
+ const id = snap2.ids[0];
12739
+ const single = buildObjectSnapshot(store.getState(), id, snap2.anchor);
12630
12740
  if (single) {
12631
12741
  setPropsPopover(single);
12632
12742
  setMultiSelection(null);
@@ -12634,7 +12744,7 @@ var init_EditorPanel = __esm({
12634
12744
  }
12635
12745
  return;
12636
12746
  }
12637
- setMultiSelection(snap);
12747
+ setMultiSelection(snap2);
12638
12748
  setPropsPopover(null);
12639
12749
  onSelectionChangeRef.current?.(void 0);
12640
12750
  });
@@ -12678,15 +12788,13 @@ var init_EditorPanel = __esm({
12678
12788
  try {
12679
12789
  const svgString = await renderGeometrySvgFromState(jsonState);
12680
12790
  onInsert(jsonState, svgString);
12791
+ onGeometryDraftRef.current?.(null);
12681
12792
  } catch (err) {
12682
12793
  console.error("Geometry insert failed:", err);
12683
12794
  }
12684
12795
  })();
12685
12796
  return true;
12686
12797
  }, [onInsert, showAxis, showGrid]);
12687
- const handleInsert = React2.useCallback(() => {
12688
- performInsert();
12689
- }, [performInsert]);
12690
12798
  const loadAiFigure = React2.useCallback((generated) => {
12691
12799
  handleRef.current?.clearSelection();
12692
12800
  setPropsPopover(null);
@@ -12781,7 +12889,7 @@ var init_EditorPanel = __esm({
12781
12889
  "button",
12782
12890
  {
12783
12891
  type: "button",
12784
- onClick: handleInsert,
12892
+ onClick: performInsert,
12785
12893
  disabled: !ready || !hasContent,
12786
12894
  title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
12787
12895
  "data-testid": "geometry-insert-btn-mobile",
@@ -12899,7 +13007,7 @@ var init_EditorPanel = __esm({
12899
13007
  /* @__PURE__ */ jsxRuntime.jsx(
12900
13008
  "button",
12901
13009
  {
12902
- onClick: handleInsert,
13010
+ onClick: performInsert,
12903
13011
  disabled: !ready || !hasContent,
12904
13012
  title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
12905
13013
  "data-testid": "geometry-insert-btn",
@@ -13260,7 +13368,7 @@ var init_host = __esm({
13260
13368
  init_useStampStore();
13261
13369
  init_dslRenderRow();
13262
13370
  GeometryStampHost = React2.forwardRef(
13263
- function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure }, ref) {
13371
+ function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure, onGeometryDraft }, ref) {
13264
13372
  const panelRef = React2.useRef(null);
13265
13373
  const { isMobile } = useIsMobile();
13266
13374
  const [drawerOpen, setDrawerOpen] = React2.useState(false);
@@ -13375,7 +13483,9 @@ var init_host = __esm({
13375
13483
  canUndo,
13376
13484
  canRedo,
13377
13485
  onSelectionChange: setSelectedObjectId,
13378
- generateGeometryFigure
13486
+ generateGeometryFigure,
13487
+ api,
13488
+ onGeometryDraft
13379
13489
  }
13380
13490
  )
13381
13491
  ] });