@xom11/whiteboard 0.7.0 → 0.9.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 (49) hide show
  1. package/README.md +51 -1
  2. package/dist/chunk-74VEEZBV.mjs +619 -0
  3. package/dist/chunk-74VEEZBV.mjs.map +1 -0
  4. package/dist/chunk-DU2NFHRR.mjs +103 -0
  5. package/dist/chunk-DU2NFHRR.mjs.map +1 -0
  6. package/dist/{chunk-SHFOGORM.mjs → chunk-DU3RHKT5.mjs} +4 -4
  7. package/dist/{chunk-SHFOGORM.mjs.map → chunk-DU3RHKT5.mjs.map} +1 -1
  8. package/dist/{chunk-HYXFHEDJ.mjs → chunk-IUVV52HO.mjs} +22 -7
  9. package/dist/chunk-IUVV52HO.mjs.map +1 -0
  10. package/dist/{chunk-BJX4YNA5.mjs → chunk-KEYZ5EZT.mjs} +26 -9
  11. package/dist/chunk-KEYZ5EZT.mjs.map +1 -0
  12. package/dist/{chunk-LPM4MM45.mjs → chunk-SBDMF4NQ.mjs} +3 -2
  13. package/dist/chunk-SBDMF4NQ.mjs.map +1 -0
  14. package/dist/{chunk-3SSQKRRO.mjs → chunk-ZVN356JZ.mjs} +4 -4
  15. package/dist/{chunk-3SSQKRRO.mjs.map → chunk-ZVN356JZ.mjs.map} +1 -1
  16. package/dist/geometry-2d.js +250 -218
  17. package/dist/geometry-2d.js.map +1 -1
  18. package/dist/geometry-2d.mjs +2 -2
  19. package/dist/geometry-3d.d.mts +1 -1
  20. package/dist/geometry-3d.d.ts +1 -1
  21. package/dist/geometry-3d.js +3276 -1201
  22. package/dist/geometry-3d.js.map +1 -1
  23. package/dist/geometry-3d.mjs +3 -2
  24. package/dist/graph-2d.js +360 -66
  25. package/dist/graph-2d.js.map +1 -1
  26. package/dist/graph-2d.mjs +2 -2
  27. package/dist/{host-2QGKMGCT.mjs → host-LZH2FZ2N.mjs} +3 -3
  28. package/dist/{host-2QGKMGCT.mjs.map → host-LZH2FZ2N.mjs.map} +1 -1
  29. package/dist/host-PIIDSMVE.mjs +3187 -0
  30. package/dist/host-PIIDSMVE.mjs.map +1 -0
  31. package/dist/{host-T2W6R6SO.mjs → host-VDNAJMLC.mjs} +221 -216
  32. package/dist/host-VDNAJMLC.mjs.map +1 -0
  33. package/dist/index.d.mts +6 -5
  34. package/dist/index.d.ts +6 -5
  35. package/dist/index.js +4365 -1821
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +246 -102
  38. package/dist/index.mjs.map +1 -1
  39. package/package.json +6 -6
  40. package/dist/chunk-BJX4YNA5.mjs.map +0 -1
  41. package/dist/chunk-DJTBZEAR.mjs +0 -25
  42. package/dist/chunk-DJTBZEAR.mjs.map +0 -1
  43. package/dist/chunk-HM7RIXJE.mjs +0 -331
  44. package/dist/chunk-HM7RIXJE.mjs.map +0 -1
  45. package/dist/chunk-HYXFHEDJ.mjs.map +0 -1
  46. package/dist/chunk-LPM4MM45.mjs.map +0 -1
  47. package/dist/host-T2W6R6SO.mjs.map +0 -1
  48. package/dist/host-XUFON6CQ.mjs +0 -1422
  49. package/dist/host-XUFON6CQ.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData } from './chunk-BJX4YNA5.mjs';
3
- import { useChordShortcut, MobileToolDrawer } from './chunk-LPM4MM45.mjs';
2
+ import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData, safeJsx } from './chunk-KEYZ5EZT.mjs';
3
+ import { useChordShortcut, MobileToolDrawer } from './chunk-SBDMF4NQ.mjs';
4
4
  import { resolveAttrColors, paletteFor, themeLabel, themeAxis, themeGrid } from './chunk-HTBLO5JO.mjs';
5
5
  import { useIsMobile } from './chunk-P2AOIF7S.mjs';
6
6
  import { insertStampImage } from './chunk-C6SCVOMC.mjs';
@@ -320,14 +320,8 @@ function handleDown(ctx, e) {
320
320
  const tmp1 = ctx.boardRef.current.create("intersection", [a, b, 1], { visible: false, withLabel: false });
321
321
  const d0 = Math.hypot((tmp0.X?.() ?? 0) - x, (tmp0.Y?.() ?? 0) - y);
322
322
  const d1 = Math.hypot((tmp1.X?.() ?? 0) - x, (tmp1.Y?.() ?? 0) - y);
323
- try {
324
- ctx.boardRef.current.removeObject(tmp0);
325
- } catch {
326
- }
327
- try {
328
- ctx.boardRef.current.removeObject(tmp1);
329
- } catch {
330
- }
323
+ safeJsx("handlers.removeObject(intersect.tmp0)", () => ctx.boardRef.current.removeObject(tmp0));
324
+ safeJsx("handlers.removeObject(intersect.tmp1)", () => ctx.boardRef.current.removeObject(tmp1));
331
325
  const idx = d0 <= d1 ? 0 : 1;
332
326
  ctx.create("intersection", [aId, bId, idx], attrs);
333
327
  }
@@ -364,7 +358,7 @@ function handleDown(ctx, e) {
364
358
  })();
365
359
  if (ctx.pendingRef.current.length > 0 && ctx.boardRef.current) {
366
360
  const prev = ctx.pendingRef.current[ctx.pendingRef.current.length - 1];
367
- try {
361
+ safeJsx("handlers.createPreviewSegment", () => {
368
362
  const seg = ctx.boardRef.current.create("segment", [prev, pick2], {
369
363
  strokeColor: "#3b82f6",
370
364
  strokeWidth: 1.5,
@@ -374,8 +368,7 @@ function handleDown(ctx, e) {
374
368
  withLabel: false
375
369
  });
376
370
  ctx.previewSegRef.current.push(seg);
377
- } catch {
378
- }
371
+ });
379
372
  }
380
373
  ctx.pendingRef.current.push(pick2);
381
374
  ctx.setPendingCount(ctx.pendingRef.current.length);
@@ -486,10 +479,7 @@ function handleUp(ctx, e) {
486
479
  if (!sc2) return;
487
480
  const [ex, ey] = sc2;
488
481
  if (mq.rect) {
489
- try {
490
- ctx.boardRef.current?.removeObject(mq.rect);
491
- } catch {
492
- }
482
+ safeJsx("handlers.removeObject(marquee.rect)", () => ctx.boardRef.current?.removeObject(mq.rect));
493
483
  }
494
484
  if (Math.hypot(ex - mq.startSx, ey - mq.startSy) < 4) return;
495
485
  const x1 = Math.min(mq.startSx, ex), x2 = Math.max(mq.startSx, ex);
@@ -522,10 +512,7 @@ function handleUp(ctx, e) {
522
512
  }
523
513
  }
524
514
  ctx.setSelectionTick((tt) => tt + 1);
525
- try {
526
- board.update();
527
- } catch {
528
- }
515
+ safeJsx("handlers.board.update(marquee)", () => board.update());
529
516
  return;
530
517
  }
531
518
  if (t !== "move") return;
@@ -572,12 +559,9 @@ function handleMove(ctx, e) {
572
559
  const [x2u, y2u] = ux2 && ux2.length >= 2 ? [ux2[0], ux2[1]] : toUsr(Math.max(startSx, sx), Math.max(startSy, sy));
573
560
  const rect = ctx.marqueeRef.current.rect;
574
561
  if (rect) {
575
- try {
576
- ctx.boardRef.current.removeObject(rect);
577
- } catch {
578
- }
562
+ safeJsx("handlers.removeObject(marquee.prevRect)", () => ctx.boardRef.current.removeObject(rect));
579
563
  }
580
- try {
564
+ safeJsx("handlers.createMarqueePolygon", () => {
581
565
  ctx.marqueeRef.current.rect = ctx.boardRef.current.create("polygon", [
582
566
  [x1u, y1u],
583
567
  [x2u, y1u],
@@ -592,8 +576,7 @@ function handleMove(ctx, e) {
592
576
  highlight: false,
593
577
  withLabel: false
594
578
  });
595
- } catch {
596
- }
579
+ });
597
580
  }
598
581
  return;
599
582
  }
@@ -603,14 +586,13 @@ function handleMove(ctx, e) {
603
586
  ctx.previewRafRef.current = requestAnimationFrame(() => {
604
587
  ctx.previewRafRef.current = null;
605
588
  if (!ctx.boardRef.current || !ctx.phantomRef.current) return;
606
- try {
589
+ safeJsx("handlers.phantomMove", () => {
607
590
  const coords = ctx.boardRef.current.getUsrCoordsOfMouse(e);
608
591
  const JXG = ctx.jxgRef.current;
609
592
  if (!JXG) return;
610
593
  ctx.phantomRef.current.setPositionDirectly(JXG.COORDS_BY_USER, [coords[0], coords[1]]);
611
594
  ctx.boardRef.current.update();
612
- } catch {
613
- }
595
+ });
614
596
  });
615
597
  }
616
598
  var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
@@ -622,6 +604,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
622
604
  const jxgRef = useRef(null);
623
605
  const axisObjsRef = useRef({});
624
606
  const creationLogRef = useRef([]);
607
+ const redoStackRef = useRef([]);
625
608
  const [tool, setTool] = useState("move");
626
609
  const toolRef = useRef("move");
627
610
  toolRef.current = tool;
@@ -671,13 +654,17 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
671
654
  return a;
672
655
  });
673
656
  }, []);
657
+ const pushCreationLog = useCallback((entry) => {
658
+ creationLogRef.current.push(entry);
659
+ redoStackRef.current = [];
660
+ }, []);
674
661
  const pushLog = useCallback(
675
662
  (id, type, args, attrs, obj) => {
676
- creationLogRef.current.push({ id, type, args, attrs });
663
+ pushCreationLog({ id, type, args, attrs });
677
664
  objMapRef.current.set(id, obj);
678
665
  setHistoryTick((t) => t + 1);
679
666
  },
680
- []
667
+ [pushCreationLog]
681
668
  );
682
669
  const create = useCallback(
683
670
  (type, args, attrs = {}) => {
@@ -760,16 +747,10 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
760
747
  if (patch.remove) {
761
748
  const vl = valueLabelsRef.current.get(o);
762
749
  if (vl) {
763
- try {
764
- boardRef.current.removeObject(vl);
765
- } catch {
766
- }
750
+ safeJsx("MiniBoard.removeObject(valueLabel)", () => boardRef.current.removeObject(vl));
767
751
  valueLabelsRef.current.delete(o);
768
752
  }
769
- try {
770
- boardRef.current.removeObject(o);
771
- } catch {
772
- }
753
+ safeJsx("MiniBoard.removeObject(target)", () => boardRef.current.removeObject(o));
773
754
  const board = boardRef.current;
774
755
  const aliveIds = /* @__PURE__ */ new Set();
775
756
  for (const [id, obj2] of objMapRef.current.entries()) {
@@ -794,7 +775,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
794
775
  const targetId = localIdOf(o);
795
776
  if (targetId) {
796
777
  const id = nextLocalId();
797
- creationLogRef.current.push({ id, type: "valueLabel", args: [targetId], attrs: {} });
778
+ pushCreationLog({ id, type: "valueLabel", args: [targetId], attrs: {} });
798
779
  objMapRef.current.set(id, txt);
799
780
  setHistoryTick((t) => t + 1);
800
781
  }
@@ -803,10 +784,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
803
784
  const txt = valueLabelsRef.current.get(o);
804
785
  valueLabelsRef.current.delete(o);
805
786
  if (txt) {
806
- try {
807
- boardRef.current.removeObject(txt);
808
- } catch {
809
- }
787
+ safeJsx("MiniBoard.removeObject(valueLabel.text)", () => boardRef.current.removeObject(txt));
810
788
  const txtId = localIdOf(txt);
811
789
  if (txtId) {
812
790
  creationLogRef.current = creationLogRef.current.filter((e) => e.id !== txtId);
@@ -817,10 +795,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
817
795
  }
818
796
  }
819
797
  if (patch.attrs) {
820
- try {
821
- o.setAttribute(patch.attrs);
822
- } catch {
823
- }
798
+ safeJsx("MiniBoard.setAttribute", () => o.setAttribute(patch.attrs));
824
799
  const id = localIdOf(o);
825
800
  if (id) {
826
801
  const entry = creationLogRef.current.find((e) => e.id === id);
@@ -828,19 +803,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
828
803
  setHistoryTick((t) => t + 1);
829
804
  }
830
805
  }
831
- try {
832
- boardRef.current.update();
833
- } catch {
834
- }
806
+ safeJsx("MiniBoard.board.update(mutate)", () => boardRef.current.update());
835
807
  }, [createValueLabelFor, localIdOf, nextLocalId]);
836
808
  const clearPreviewSegs = useCallback(() => {
837
809
  const b = boardRef.current;
838
810
  if (!b) return;
839
811
  for (const s of previewSegRef.current) {
840
- try {
841
- b.removeObject(s);
842
- } catch {
843
- }
812
+ safeJsx("MiniBoard.removeObject(previewSeg)", () => b.removeObject(s));
844
813
  }
845
814
  previewSegRef.current = [];
846
815
  }, []);
@@ -848,17 +817,11 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
848
817
  const b = boardRef.current;
849
818
  if (!b) return;
850
819
  if (previewShapeRef.current) {
851
- try {
852
- b.removeObject(previewShapeRef.current);
853
- } catch {
854
- }
820
+ safeJsx("MiniBoard.removeObject(previewShape)", () => b.removeObject(previewShapeRef.current));
855
821
  previewShapeRef.current = null;
856
822
  }
857
823
  if (phantomRef.current) {
858
- try {
859
- b.removeObject(phantomRef.current);
860
- } catch {
861
- }
824
+ safeJsx("MiniBoard.removeObject(phantom)", () => b.removeObject(phantomRef.current));
862
825
  phantomRef.current = null;
863
826
  }
864
827
  }, []);
@@ -870,7 +833,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
870
833
  }, [clearPreviewSegs, removePhantom]);
871
834
  const applySelectionStyle = useCallback((obj) => {
872
835
  if (!obj || selOriginalRef.current.has(obj)) return;
873
- try {
836
+ safeJsx("MiniBoard.applySelectionStyle", () => {
874
837
  const visProp = obj.visProp ?? {};
875
838
  selOriginalRef.current.set(obj, {
876
839
  strokeColor: visProp.strokecolor,
@@ -882,19 +845,17 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
882
845
  } else {
883
846
  obj.setAttribute({ strokeColor: "#06b6d4", strokeWidth: 3 });
884
847
  }
885
- } catch {
886
- }
848
+ });
887
849
  }, []);
888
850
  const restoreSelectionStyle = useCallback((obj) => {
889
851
  const orig = selOriginalRef.current.get(obj);
890
852
  if (!orig) return;
891
- try {
853
+ safeJsx("MiniBoard.restoreSelectionStyle", () => {
892
854
  const attrs = {};
893
855
  if (orig.strokeColor !== void 0) attrs.strokeColor = orig.strokeColor;
894
856
  if (orig.strokeWidth !== void 0) attrs.strokeWidth = orig.strokeWidth;
895
857
  obj.setAttribute(attrs);
896
- } catch {
897
- }
858
+ });
898
859
  selOriginalRef.current.delete(obj);
899
860
  }, []);
900
861
  const clearSelection = useCallback(() => {
@@ -903,10 +864,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
903
864
  }
904
865
  selectedSetRef.current.clear();
905
866
  setSelectionTick((t) => t + 1);
906
- try {
907
- boardRef.current?.update();
908
- } catch {
909
- }
867
+ safeJsx("MiniBoard.board.update(clearSelection)", () => boardRef.current?.update());
910
868
  }, [restoreSelectionStyle]);
911
869
  const toggleSelect = useCallback((obj, additive) => {
912
870
  if (!obj) return;
@@ -926,10 +884,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
926
884
  }
927
885
  }
928
886
  setSelectionTick((t) => t + 1);
929
- try {
930
- boardRef.current?.update();
931
- } catch {
932
- }
887
+ safeJsx("MiniBoard.board.update(toggleSelect)", () => boardRef.current?.update());
933
888
  }, [applySelectionStyle, restoreSelectionStyle]);
934
889
  const deleteSelected = useCallback(() => {
935
890
  const board = boardRef.current;
@@ -937,10 +892,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
937
892
  if (selectedSetRef.current.size === 0) return;
938
893
  for (const o of selectedSetRef.current) selOriginalRef.current.delete(o);
939
894
  for (const o of selectedSetRef.current) {
940
- try {
941
- board.removeObject(o);
942
- } catch {
943
- }
895
+ safeJsx("MiniBoard.removeObject(selected)", () => board.removeObject(o));
944
896
  }
945
897
  selectedSetRef.current.clear();
946
898
  const aliveIds = /* @__PURE__ */ new Set();
@@ -1013,10 +965,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1013
965
  const b = boardRef.current;
1014
966
  if (!b) return;
1015
967
  if (previewShapeRef.current) {
1016
- try {
1017
- b.removeObject(previewShapeRef.current);
1018
- } catch {
1019
- }
968
+ safeJsx("MiniBoard.removeObject(refreshPreview)", () => b.removeObject(previewShapeRef.current));
1020
969
  previewShapeRef.current = null;
1021
970
  }
1022
971
  const t = toolRef.current;
@@ -1135,7 +1084,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1135
1084
  }
1136
1085
  case "toggleLabel": {
1137
1086
  const obj = picks[0];
1138
- try {
1087
+ safeJsx("MiniBoard.toggleLabel", () => {
1139
1088
  if (obj.label) {
1140
1089
  const visible = obj.label.visProp.visible !== false;
1141
1090
  obj.label.setAttribute({ visible: !visible });
@@ -1144,23 +1093,21 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1144
1093
  obj.setAttribute({ withLabel: !cur });
1145
1094
  }
1146
1095
  boardRef.current.update();
1147
- } catch {
1148
- }
1096
+ });
1149
1097
  break;
1150
1098
  }
1151
1099
  case "toggleVisible": {
1152
1100
  const obj = picks[0];
1153
- try {
1101
+ safeJsx("MiniBoard.toggleVisible", () => {
1154
1102
  const visible = obj.visProp.visible !== false;
1155
1103
  obj.setAttribute({ visible: !visible });
1156
1104
  boardRef.current.update();
1157
- } catch {
1158
- }
1105
+ });
1159
1106
  break;
1160
1107
  }
1161
1108
  case "delete": {
1162
1109
  const obj = picks[0];
1163
- try {
1110
+ safeJsx("MiniBoard.deleteOne", () => {
1164
1111
  boardRef.current.removeObject(obj);
1165
1112
  const board = boardRef.current;
1166
1113
  const aliveIds = /* @__PURE__ */ new Set();
@@ -1175,8 +1122,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1175
1122
  if (!aliveIds.has(id)) objMapRef.current.delete(id);
1176
1123
  }
1177
1124
  setHistoryTick((t) => t + 1);
1178
- } catch {
1179
- }
1125
+ });
1180
1126
  break;
1181
1127
  }
1182
1128
  }
@@ -1211,7 +1157,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1211
1157
  }
1212
1158
  const stepId = nextLocalId();
1213
1159
  const stepObj = boardRef.current.create("transform", step.params, step.attrs);
1214
- creationLogRef.current.push({ id: stepId, type: "transform", args: stepLogArgs, attrs: step.attrs });
1160
+ pushCreationLog({ id: stepId, type: "transform", args: stepLogArgs, attrs: step.attrs });
1215
1161
  objMapRef.current.set(stepId, stepObj);
1216
1162
  transformObjs.push(stepObj);
1217
1163
  transformIds.push(stepId);
@@ -1225,7 +1171,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1225
1171
  const newName = srcName ? `${srcName}'` : nextLabel();
1226
1172
  const attrs = { name: newName, size: 3, color: "#0ea5e9", strokeColor: "#0ea5e9", fillColor: "#0ea5e9" };
1227
1173
  const obj = boardRef.current.create("point", [src, transformParent], attrs);
1228
- creationLogRef.current.push({ id, type: "point", args: [srcId ?? src, transformLogRef], attrs });
1174
+ pushCreationLog({ id, type: "point", args: [srcId ?? src, transformLogRef], attrs });
1229
1175
  objMapRef.current.set(id, obj);
1230
1176
  return obj;
1231
1177
  });
@@ -1256,6 +1202,30 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1256
1202
  }
1257
1203
  setHistoryTick((t) => t + 1);
1258
1204
  }, [create, flashWarn, localIdOf, nextLabel, nextLocalId]);
1205
+ const recreateFromLogEntry = useCallback((el) => {
1206
+ const board = boardRef.current;
1207
+ if (!board) return false;
1208
+ const idMap = objMapRef.current;
1209
+ const resolved = el.args.map((a) => typeof a === "string" && idMap.has(a) ? idMap.get(a) : a);
1210
+ try {
1211
+ if (el.type === "valueLabel") {
1212
+ const target = resolved[0];
1213
+ if (!target) return false;
1214
+ const txt = createValueLabelFor(target);
1215
+ if (!txt) return false;
1216
+ idMap.set(el.id, txt);
1217
+ valueLabelsRef.current.set(target, txt);
1218
+ return true;
1219
+ }
1220
+ const themedAttrs = resolveAttrColors({ ...el.attrs }, paletteFor(isDarkRef.current));
1221
+ const obj = board.create(el.type, resolved, themedAttrs);
1222
+ idMap.set(el.id, obj);
1223
+ return true;
1224
+ } catch (err) {
1225
+ console.warn("Recreate failed for", el.type, err);
1226
+ return false;
1227
+ }
1228
+ }, [createValueLabelFor]);
1259
1229
  const undoLast = useCallback(() => {
1260
1230
  const b = boardRef.current;
1261
1231
  if (!b) return;
@@ -1265,21 +1235,31 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1265
1235
  const obj = objMapRef.current.get(last.id);
1266
1236
  objMapRef.current.delete(last.id);
1267
1237
  if (obj) {
1268
- try {
1269
- b.removeObject(obj);
1270
- } catch {
1271
- }
1238
+ safeJsx("MiniBoard.removeObject(undo)", () => b.removeObject(obj));
1272
1239
  clearPending();
1240
+ redoStackRef.current.push(last);
1273
1241
  setHistoryTick((t) => t + 1);
1274
- try {
1275
- b.update();
1276
- } catch {
1277
- }
1242
+ safeJsx("MiniBoard.board.update(undo)", () => b.update());
1278
1243
  return;
1279
1244
  }
1280
1245
  }
1281
1246
  setHistoryTick((t) => t + 1);
1282
1247
  }, [clearPending]);
1248
+ const redoNext = useCallback(() => {
1249
+ const b = boardRef.current;
1250
+ if (!b) return;
1251
+ const entry = redoStackRef.current.pop();
1252
+ if (!entry) {
1253
+ setHistoryTick((t) => t + 1);
1254
+ return;
1255
+ }
1256
+ const ok = recreateFromLogEntry(entry);
1257
+ if (ok) {
1258
+ creationLogRef.current.push(entry);
1259
+ }
1260
+ setHistoryTick((t) => t + 1);
1261
+ safeJsx("MiniBoard.board.update(redo)", () => b.update());
1262
+ }, [recreateFromLogEntry]);
1283
1263
  useEffect(() => {
1284
1264
  const onKey = (e) => {
1285
1265
  const ae = document.activeElement;
@@ -1291,6 +1271,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1291
1271
  undoLastRef.current();
1292
1272
  return;
1293
1273
  }
1274
+ if ((e.metaKey || e.ctrlKey) && (e.key.toLowerCase() === "z" && e.shiftKey || e.key.toLowerCase() === "y" && !e.shiftKey)) {
1275
+ if (inField) return;
1276
+ e.preventDefault();
1277
+ e.stopPropagation();
1278
+ redoNextRef.current();
1279
+ return;
1280
+ }
1294
1281
  if (e.key === "Escape" && !inField) {
1295
1282
  if (pendingRef.current.length > 0) {
1296
1283
  e.preventDefault();
@@ -1337,16 +1324,14 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1337
1324
  if (!sc) return [];
1338
1325
  const [sx, sy] = sc;
1339
1326
  const list = [];
1340
- try {
1327
+ safeJsx("MiniBoard.objectsAt.loop", () => {
1341
1328
  const objs = b.objectsList || [];
1342
1329
  for (const o of objs) {
1343
- try {
1330
+ safeJsx("MiniBoard.objectsAt.hasPoint", () => {
1344
1331
  if (o.hasPoint && o.hasPoint(sx, sy)) list.push(o);
1345
- } catch {
1346
- }
1332
+ });
1347
1333
  }
1348
- } catch {
1349
- }
1334
+ });
1350
1335
  return list;
1351
1336
  }, [screenCoordsOf]);
1352
1337
  const findNearestPoint = useCallback((evt, tolPx = 12) => {
@@ -1356,24 +1341,23 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1356
1341
  if (!sc) return null;
1357
1342
  const [sx, sy] = sc;
1358
1343
  const tol2 = tolPx * tolPx;
1359
- let best = null;
1360
- try {
1344
+ const bestRef = { current: null };
1345
+ safeJsx("MiniBoard.findNearestPoint.loop", () => {
1361
1346
  const objs = b.objectsList || [];
1362
1347
  for (const o of objs) {
1363
- try {
1364
- if (objKind(o) !== "point") continue;
1348
+ safeJsx("MiniBoard.findNearestPoint.iter", () => {
1349
+ if (objKind(o) !== "point") return;
1365
1350
  const pc = o.coords?.scrCoords;
1366
- if (!pc) continue;
1351
+ if (!pc) return;
1367
1352
  const dx = pc[1] - sx;
1368
1353
  const dy = pc[2] - sy;
1369
1354
  const d2 = dx * dx + dy * dy;
1370
- if (d2 <= tol2 && (!best || d2 < best.d2)) best = { obj: o, d2 };
1371
- } catch {
1372
- }
1355
+ const cur = bestRef.current;
1356
+ if (d2 <= tol2 && (!cur || d2 < cur.d2)) bestRef.current = { obj: o, d2 };
1357
+ });
1373
1358
  }
1374
- } catch {
1375
- }
1376
- return best ? best.obj : null;
1359
+ });
1360
+ return bestRef.current ? bestRef.current.obj : null;
1377
1361
  }, [screenCoordsOf]);
1378
1362
  const promoteLabel = useCallback((o) => {
1379
1363
  if (!o) return o;
@@ -1381,31 +1365,25 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1381
1365
  if (t !== "text") return o;
1382
1366
  const b = boardRef.current;
1383
1367
  if (!b) return o;
1384
- try {
1368
+ const promoted = safeJsx("MiniBoard.promoteLabel", () => {
1385
1369
  for (const c of b.objectsList || []) {
1386
1370
  if (c.label === o) return c;
1387
1371
  }
1388
- } catch {
1389
- }
1390
- return o;
1372
+ return null;
1373
+ }, null);
1374
+ return promoted ?? o;
1391
1375
  }, []);
1392
1376
  const pendingTransformRef = useRef(null);
1393
1377
  const transformSubsRef = useRef(/* @__PURE__ */ new Set());
1394
1378
  const emitTransform = useCallback((info) => {
1395
1379
  transformSubsRef.current.forEach((cb) => {
1396
- try {
1397
- cb(info);
1398
- } catch {
1399
- }
1380
+ safeJsx("MiniBoard.emitTransform.cb", () => cb(info));
1400
1381
  });
1401
1382
  }, []);
1402
1383
  const selectSubsRef = useRef(/* @__PURE__ */ new Set());
1403
1384
  const emitSelect = useCallback((snap) => {
1404
1385
  selectSubsRef.current.forEach((cb) => {
1405
- try {
1406
- cb(snap);
1407
- } catch {
1408
- }
1386
+ safeJsx("MiniBoard.emitSelect.cb", () => cb(snap));
1409
1387
  });
1410
1388
  }, []);
1411
1389
  const moveDownRef = useRef(null);
@@ -1417,7 +1395,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1417
1395
  const JXG = (await import('jsxgraph')).default;
1418
1396
  if (cancelled || !containerRef.current) return;
1419
1397
  jxgRef.current = JXG;
1420
- try {
1398
+ safeJsx("MiniBoard.applyJxgOptions", () => {
1421
1399
  const opts = JXG.Options;
1422
1400
  if (opts) {
1423
1401
  opts.text = opts.text || {};
@@ -1430,8 +1408,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1430
1408
  opts.label.strokeColor = themeLabel(isDarkRef.current);
1431
1409
  opts.text.strokeColor = themeLabel(isDarkRef.current);
1432
1410
  }
1433
- } catch {
1434
- }
1411
+ });
1435
1412
  const board = JXG.JSXGraph.initBoard(containerId, {
1436
1413
  boundingbox: initialState?.bbox ?? [-10, 10, 10, -10],
1437
1414
  axis: false,
@@ -1452,43 +1429,20 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1452
1429
  });
1453
1430
  boardRef.current = board;
1454
1431
  if (initialState && initialState.elements.length > 0) {
1455
- const idMap = objMapRef.current;
1456
1432
  for (const el of initialState.elements) {
1457
- const resolved = el.args.map((a) => typeof a === "string" && idMap.has(a) ? idMap.get(a) : a);
1458
- try {
1459
- if (el.type === "valueLabel") {
1460
- const target = resolved[0];
1461
- if (target) {
1462
- const txt = createValueLabelFor(target);
1463
- if (txt) {
1464
- idMap.set(el.id, txt);
1465
- valueLabelsRef.current.set(target, txt);
1466
- }
1467
- }
1468
- continue;
1469
- }
1470
- const themedAttrs = resolveAttrColors({ ...el.attrs }, paletteFor(isDarkRef.current));
1471
- const obj = board.create(el.type, resolved, themedAttrs);
1472
- idMap.set(el.id, obj);
1473
- } catch (err) {
1474
- console.warn("Replay failed for", el.type, err);
1475
- }
1433
+ recreateFromLogEntry(el);
1476
1434
  }
1477
1435
  creationLogRef.current = [...initialState.elements];
1478
1436
  labelIdxRef.current = initialState.elements.filter((e) => e.type === "point").length;
1479
1437
  }
1480
1438
  if (showAxisRef.current) {
1481
- try {
1439
+ safeJsx("MiniBoard.initAxes", () => {
1482
1440
  axisObjsRef.current.x = board.create("axis", [[0, 0], [1, 0]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
1483
1441
  axisObjsRef.current.y = board.create("axis", [[0, 0], [0, 1]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
1484
- } catch {
1485
- }
1442
+ });
1486
1443
  }
1487
1444
  if (showGridRef.current) {
1488
- try {
1489
- board.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 });
1490
- } catch {
1491
- }
1445
+ safeJsx("MiniBoard.initGrid", () => board.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 }));
1492
1446
  }
1493
1447
  board.on("down", (e) => {
1494
1448
  const ctx = {
@@ -1639,6 +1593,8 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1639
1593
  setShowGrid: (b) => setShowGridRef.current(b),
1640
1594
  undo: () => undoLastRef.current(),
1641
1595
  canUndo: () => creationLogRef.current.length > 0,
1596
+ redo: () => redoNextRef.current(),
1597
+ canRedo: () => redoStackRef.current.length > 0,
1642
1598
  subscribe: (cb) => {
1643
1599
  subscribersRef.current.add(cb);
1644
1600
  return () => {
@@ -1651,15 +1607,14 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1651
1607
  const b = boardRef.current;
1652
1608
  if (!b) return [];
1653
1609
  const out = [];
1654
- try {
1610
+ safeJsx("MiniBoard.getAllPointNames", () => {
1655
1611
  const objs = b.objectsList || [];
1656
1612
  for (const o of objs) {
1657
1613
  if (objKind(o) === "point" && typeof o.name === "string" && o.name) {
1658
1614
  out.push(o.name);
1659
1615
  }
1660
1616
  }
1661
- } catch {
1662
- }
1617
+ });
1663
1618
  return out;
1664
1619
  },
1665
1620
  onSelect: (cb) => {
@@ -1720,10 +1675,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1720
1675
  previewRafRef.current = null;
1721
1676
  }
1722
1677
  if (boardRef.current && jxgRef.current) {
1723
- try {
1724
- jxgRef.current.JSXGraph.freeBoard(boardRef.current);
1725
- } catch {
1726
- }
1678
+ safeJsx("MiniBoard.freeBoard", () => jxgRef.current.JSXGraph.freeBoard(boardRef.current));
1727
1679
  boardRef.current = null;
1728
1680
  }
1729
1681
  };
@@ -1731,19 +1683,13 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1731
1683
  useEffect(() => {
1732
1684
  const b = boardRef.current;
1733
1685
  if (!b) return;
1734
- try {
1686
+ safeJsx("MiniBoard.toggleAxis", () => {
1735
1687
  if (axisObjsRef.current.x) {
1736
- try {
1737
- b.removeObject(axisObjsRef.current.x);
1738
- } catch {
1739
- }
1688
+ safeJsx("MiniBoard.removeObject(axisX)", () => b.removeObject(axisObjsRef.current.x));
1740
1689
  axisObjsRef.current.x = void 0;
1741
1690
  }
1742
1691
  if (axisObjsRef.current.y) {
1743
- try {
1744
- b.removeObject(axisObjsRef.current.y);
1745
- } catch {
1746
- }
1692
+ safeJsx("MiniBoard.removeObject(axisY)", () => b.removeObject(axisObjsRef.current.y));
1747
1693
  axisObjsRef.current.y = void 0;
1748
1694
  }
1749
1695
  if (showAxis) {
@@ -1751,28 +1697,23 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1751
1697
  axisObjsRef.current.y = b.create("axis", [[0, 0], [0, 1]], { strokeColor: themeAxis(isDarkRef.current), name: "", withLabel: false });
1752
1698
  }
1753
1699
  b.update();
1754
- } catch {
1755
- }
1700
+ });
1756
1701
  }, [showAxis]);
1757
1702
  useEffect(() => {
1758
1703
  const b = boardRef.current;
1759
1704
  if (!b) return;
1760
- try {
1705
+ safeJsx("MiniBoard.toggleGrid", () => {
1761
1706
  const objs = Object.values(b.objects || {});
1762
1707
  for (const o of objs) {
1763
1708
  if (o && (o.elType === "grid" || o.type === "grid" || o.visProp && o.visProp.type === "grid")) {
1764
- try {
1765
- b.removeObject(o);
1766
- } catch {
1767
- }
1709
+ safeJsx("MiniBoard.removeObject(grid)", () => b.removeObject(o));
1768
1710
  }
1769
1711
  }
1770
1712
  if (showGrid) {
1771
1713
  b.create("grid", [], { strokeColor: themeGrid(isDarkRef.current), strokeOpacity: 1 });
1772
1714
  }
1773
1715
  b.update();
1774
- } catch {
1775
- }
1716
+ });
1776
1717
  }, [showGrid]);
1777
1718
  const handleToolChange = useCallback((t) => {
1778
1719
  clearPending();
@@ -1780,10 +1721,9 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1780
1721
  setTool(t);
1781
1722
  const b = boardRef.current;
1782
1723
  if (b) {
1783
- try {
1724
+ safeJsx("MiniBoard.setPanForTool", () => {
1784
1725
  if (b.attr?.pan) b.attr.pan.enabled = t !== "select";
1785
- } catch {
1786
- }
1726
+ });
1787
1727
  }
1788
1728
  }, [clearPending]);
1789
1729
  const handleToolChangeRef = useRef(handleToolChange);
@@ -1791,10 +1731,7 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1791
1731
  const subscribersRef = useRef(/* @__PURE__ */ new Set());
1792
1732
  const notifySubscribers = useCallback(() => {
1793
1733
  subscribersRef.current.forEach((cb) => {
1794
- try {
1795
- cb();
1796
- } catch {
1797
- }
1734
+ safeJsx("MiniBoard.notifySubscriber.cb", () => cb());
1798
1735
  });
1799
1736
  }, []);
1800
1737
  useEffect(() => {
@@ -1802,6 +1739,8 @@ var JSXGraphMiniBoard = ({ onReady, initialState, isDark }) => {
1802
1739
  }, [tool, showAxis, showGrid, historyTick, notifySubscribers]);
1803
1740
  const undoLastRef = useRef(undoLast);
1804
1741
  undoLastRef.current = undoLast;
1742
+ const redoNextRef = useRef(redoNext);
1743
+ redoNextRef.current = redoNext;
1805
1744
  const clearPendingRef = useRef(clearPending);
1806
1745
  clearPendingRef.current = clearPending;
1807
1746
  const finalizeTransformCreateRef = useRef(finalizeTransformCreate);
@@ -1885,6 +1824,12 @@ function UndoIcon() {
1885
1824
  /* @__PURE__ */ jsx("path", { d: "M3.51 13a9 9 0 1 0 2.13-9.36L3 7" })
1886
1825
  ] });
1887
1826
  }
1827
+ function RedoIcon() {
1828
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1829
+ /* @__PURE__ */ jsx("polyline", { points: "21 7 21 13 15 13" }),
1830
+ /* @__PURE__ */ jsx("path", { d: "M20.49 13a9 9 0 1 1-2.13-9.36L21 7" })
1831
+ ] });
1832
+ }
1888
1833
  function AxisIcon() {
1889
1834
  return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
1890
1835
  /* @__PURE__ */ jsx("line", { x1: "4", y1: "20", x2: "20", y2: "20" }),
@@ -1929,7 +1874,7 @@ function useToolHoverTooltip() {
1929
1874
  return { hover, portalReady, showHover, hideHover };
1930
1875
  }
1931
1876
  function DesktopGeometryPanel(props) {
1932
- const { activeTool, onToolChange, showAxis, showGrid, onShowAxisChange, onShowGridChange, onUndo, canUndo, onClose, isDark, chordGroup } = props;
1877
+ const { activeTool, onToolChange, showAxis, showGrid, onShowAxisChange, onShowGridChange, onUndo, canUndo, onRedo, canRedo, onClose, isDark, chordGroup } = props;
1933
1878
  const grouped = useMemo(() => {
1934
1879
  return TOOLS.reduce((acc, t) => {
1935
1880
  var _a;
@@ -1978,9 +1923,23 @@ function DesktopGeometryPanel(props) {
1978
1923
  disabled: !canUndo,
1979
1924
  title: "Ho\xE0n t\xE1c (Ctrl/Cmd+Z)",
1980
1925
  "aria-label": "Ho\xE0n t\xE1c",
1926
+ "data-testid": "undo-btn",
1981
1927
  className: "ml-auto inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
1982
1928
  children: /* @__PURE__ */ jsx(UndoIcon, {})
1983
1929
  }
1930
+ ),
1931
+ /* @__PURE__ */ jsx(
1932
+ "button",
1933
+ {
1934
+ type: "button",
1935
+ onClick: onRedo,
1936
+ disabled: !canRedo,
1937
+ title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
1938
+ "aria-label": "L\xE0m l\u1EA1i",
1939
+ "data-testid": "redo-btn",
1940
+ className: "inline-flex items-center justify-center rounded p-1 text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 disabled:cursor-not-allowed disabled:text-slate-300 disabled:hover:bg-transparent",
1941
+ children: /* @__PURE__ */ jsx(RedoIcon, {})
1942
+ }
1984
1943
  )
1985
1944
  ] }) }),
1986
1945
  groupKeys.map((group) => {
@@ -2101,6 +2060,8 @@ function MobileGeometryPanel(props) {
2101
2060
  onShowGridChange,
2102
2061
  onUndo,
2103
2062
  canUndo,
2063
+ onRedo,
2064
+ canRedo,
2104
2065
  isDark,
2105
2066
  drawerOpen,
2106
2067
  onDrawerClose
@@ -2149,6 +2110,13 @@ function MobileGeometryPanel(props) {
2149
2110
  icon: /* @__PURE__ */ jsx(UndoIcon, {}),
2150
2111
  onClick: onUndo,
2151
2112
  disabled: !canUndo
2113
+ },
2114
+ {
2115
+ label: "L\xE0m l\u1EA1i",
2116
+ title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
2117
+ icon: /* @__PURE__ */ jsx(RedoIcon, {}),
2118
+ onClick: onRedo,
2119
+ disabled: !canRedo
2152
2120
  }
2153
2121
  ],
2154
2122
  groups,
@@ -2533,7 +2501,7 @@ var TransformParamPopover = ({ kind, anchor, defaultValue, onConfirm, onCancel,
2533
2501
  return createPortal(node, document.body);
2534
2502
  };
2535
2503
  var GeometryEditorPanel = forwardRef(
2536
- function GeometryEditorPanel2({ initialState, onInsert, onClose, withLeftPanel = false, onStateChange, isDark, isMobile = false, onOpenDrawer }, ref) {
2504
+ function GeometryEditorPanel2({ initialState, onInsert, onClose, withLeftPanel = false, onStateChange, isDark, isMobile = false, onOpenDrawer, onUndo, onRedo, canUndo, canRedo }, ref) {
2537
2505
  const handleRef = useRef(null);
2538
2506
  const [ready, setReady] = useState(false);
2539
2507
  const [propsPopover, setPropsPopover] = useState(null);
@@ -2550,7 +2518,8 @@ var GeometryEditorPanel = forwardRef(
2550
2518
  tool: h.getTool(),
2551
2519
  showAxis: h.getShowAxis(),
2552
2520
  showGrid: h.getShowGrid(),
2553
- canUndo: h.canUndo()
2521
+ canUndo: h.canUndo(),
2522
+ canRedo: h.canRedo()
2554
2523
  });
2555
2524
  }, []);
2556
2525
  const handleReady = useCallback((h) => {
@@ -2592,6 +2561,7 @@ var GeometryEditorPanel = forwardRef(
2592
2561
  setShowAxis: (b) => handleRef.current?.setShowAxis(b),
2593
2562
  setShowGrid: (b) => handleRef.current?.setShowGrid(b),
2594
2563
  undo: () => handleRef.current?.undo(),
2564
+ redo: () => handleRef.current?.redo(),
2595
2565
  insert: performInsert,
2596
2566
  hasContent: () => (handleRef.current?.getCreationLog().length ?? 0) > 0
2597
2567
  }), [performInsert]);
@@ -2641,17 +2611,45 @@ var GeometryEditorPanel = forwardRef(
2641
2611
  ] }),
2642
2612
  "D\u1EF1ng h\xECnh h\u1ECDc"
2643
2613
  ] }),
2644
- isMobile && /* @__PURE__ */ jsx(
2645
- "button",
2646
- {
2647
- type: "button",
2648
- onClick: handleInsert,
2649
- disabled: !ready,
2650
- "data-testid": "geometry-insert-btn-mobile",
2651
- className: "rounded bg-white/15 px-3 py-1.5 text-xs font-semibold transition hover:bg-white/25 disabled:opacity-50",
2652
- children: "Ch\xE8n"
2653
- }
2654
- ),
2614
+ isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
2615
+ /* @__PURE__ */ jsx(
2616
+ "button",
2617
+ {
2618
+ type: "button",
2619
+ onClick: onUndo,
2620
+ disabled: !canUndo,
2621
+ "aria-label": "Ho\xE0n t\xE1c",
2622
+ title: "Ho\xE0n t\xE1c (Ctrl/Cmd+Z)",
2623
+ "data-testid": "undo-btn-mobile",
2624
+ className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15 disabled:opacity-40",
2625
+ children: /* @__PURE__ */ jsx(UndoIcon, {})
2626
+ }
2627
+ ),
2628
+ /* @__PURE__ */ jsx(
2629
+ "button",
2630
+ {
2631
+ type: "button",
2632
+ onClick: onRedo,
2633
+ disabled: !canRedo,
2634
+ "aria-label": "L\xE0m l\u1EA1i",
2635
+ title: "L\xE0m l\u1EA1i (Ctrl/Cmd+Shift+Z)",
2636
+ "data-testid": "redo-btn-mobile",
2637
+ className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15 disabled:opacity-40",
2638
+ children: /* @__PURE__ */ jsx(RedoIcon, {})
2639
+ }
2640
+ ),
2641
+ /* @__PURE__ */ jsx(
2642
+ "button",
2643
+ {
2644
+ type: "button",
2645
+ onClick: handleInsert,
2646
+ disabled: !ready,
2647
+ "data-testid": "geometry-insert-btn-mobile",
2648
+ className: "rounded bg-white/15 px-3 py-1.5 text-xs font-semibold transition hover:bg-white/25 disabled:opacity-50",
2649
+ children: "Ch\xE8n"
2650
+ }
2651
+ )
2652
+ ] }),
2655
2653
  /* @__PURE__ */ jsx("button", { onClick: onClose, "aria-label": "\u0110\xF3ng", className: "inline-flex h-9 w-9 items-center justify-center rounded transition hover:bg-white/15", children: /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2656
2654
  /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
2657
2655
  /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })
@@ -2762,7 +2760,8 @@ var INITIAL_GEOM_STATE = {
2762
2760
  tool: "move",
2763
2761
  showAxis: false,
2764
2762
  showGrid: false,
2765
- canUndo: false
2763
+ canUndo: false,
2764
+ canRedo: false
2766
2765
  };
2767
2766
  var GeometryStampHost = forwardRef(
2768
2767
  function GeometryStampHost2({ api, editingElement, onClose, isDark }, ref) {
@@ -2828,6 +2827,8 @@ var GeometryStampHost = forwardRef(
2828
2827
  onShowGridChange: (b) => panelRef.current?.setShowGrid(b),
2829
2828
  onUndo: () => panelRef.current?.undo(),
2830
2829
  canUndo: geomState.canUndo,
2830
+ onRedo: () => panelRef.current?.redo(),
2831
+ canRedo: geomState.canRedo,
2831
2832
  onClose,
2832
2833
  isDark,
2833
2834
  isMobile,
@@ -2847,7 +2848,11 @@ var GeometryStampHost = forwardRef(
2847
2848
  withLeftPanel: !isMobile,
2848
2849
  isDark,
2849
2850
  isMobile,
2850
- onOpenDrawer: () => setDrawerOpen(true)
2851
+ onOpenDrawer: () => setDrawerOpen(true),
2852
+ onUndo: () => panelRef.current?.undo(),
2853
+ onRedo: () => panelRef.current?.redo(),
2854
+ canUndo: geomState.canUndo,
2855
+ canRedo: geomState.canRedo
2851
2856
  }
2852
2857
  )
2853
2858
  ] });
@@ -2855,5 +2860,5 @@ var GeometryStampHost = forwardRef(
2855
2860
  );
2856
2861
 
2857
2862
  export { GeometryStampHost };
2858
- //# sourceMappingURL=host-T2W6R6SO.mjs.map
2859
- //# sourceMappingURL=host-T2W6R6SO.mjs.map
2863
+ //# sourceMappingURL=host-VDNAJMLC.mjs.map
2864
+ //# sourceMappingURL=host-VDNAJMLC.mjs.map