react-os-shell 0.3.17 → 0.3.19

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,11 +1,11 @@
1
- export { Files as default, openFilesInTrashMode } from './chunk-SXXFZDYG.js';
2
- import './chunk-SNPQ53HC.js';
3
- import './chunk-TB72OMUU.js';
1
+ export { Files as default, openFilesInTrashMode } from './chunk-XHHU2QLD.js';
2
+ import './chunk-UVJCI67Z.js';
3
+ import './chunk-ATKI25NG.js';
4
4
  import './chunk-KUIPWCTJ.js';
5
5
  import './chunk-WIJ45SYD.js';
6
6
  import './chunk-6YY6A6SV.js';
7
7
  import './chunk-GI7ABQPU.js';
8
8
  import './chunk-PLGHQ7QW.js';
9
9
  import './chunk-SSA762W5.js';
10
- //# sourceMappingURL=Files-Q2C63BC6.js.map
11
- //# sourceMappingURL=Files-Q2C63BC6.js.map
10
+ //# sourceMappingURL=Files-XNTNB33C.js.map
11
+ //# sourceMappingURL=Files-XNTNB33C.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Files-Q2C63BC6.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Files-XNTNB33C.js"}
@@ -0,0 +1,8 @@
1
+ export { Preview as default, setPdfPreview } from './chunk-ATKI25NG.js';
2
+ import './chunk-KUIPWCTJ.js';
3
+ import './chunk-WIJ45SYD.js';
4
+ import './chunk-GI7ABQPU.js';
5
+ import './chunk-PLGHQ7QW.js';
6
+ import './chunk-SSA762W5.js';
7
+ //# sourceMappingURL=Preview-UV4X3NCN.js.map
8
+ //# sourceMappingURL=Preview-UV4X3NCN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-DMUQXKAA.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-UV4X3NCN.js"}
@@ -1,6 +1,6 @@
1
- export { openFilesInTrashMode } from '../chunk-SXXFZDYG.js';
2
- import '../chunk-SNPQ53HC.js';
3
- export { setPdfPreview } from '../chunk-TB72OMUU.js';
1
+ export { openFilesInTrashMode } from '../chunk-XHHU2QLD.js';
2
+ import '../chunk-UVJCI67Z.js';
3
+ export { setPdfPreview } from '../chunk-ATKI25NG.js';
4
4
  import '../chunk-KUIPWCTJ.js';
5
5
  import '../chunk-WIJ45SYD.js';
6
6
  export { setSpreadsheetPreview } from '../chunk-6YY6A6SV.js';
@@ -25,9 +25,9 @@ var Game2048 = lazy(() => import('../Game2048-3RH3ELRD.js'));
25
25
  var Minesweeper = lazy(() => import('../Minesweeper-ZDK33A6S.js'));
26
26
  var Email = lazy(() => import('../Email-7FJHS6Y7.js'));
27
27
  var Calendar = lazy(() => import('../Calendar-R4IBIPIU.js'));
28
- var Preview = lazy(() => import('../Preview-DMUQXKAA.js'));
28
+ var Preview = lazy(() => import('../Preview-UV4X3NCN.js'));
29
29
  var Documents = lazy(() => import('../Documents-AUK2YPDI.js'));
30
- var Files = lazy(() => import('../Files-Q2C63BC6.js'));
30
+ var Files = lazy(() => import('../Files-XNTNB33C.js'));
31
31
  var Browser = lazy(() => import('../Browser-6JQTSHQF.js'));
32
32
  var utilityApps = {
33
33
  "/calculator": { component: Calculator, label: "Calculator", size: "sm", allowPinOnTop: true, utility: true, widget: true, autoHeight: true, dimensions: [280, 420] },
@@ -513,8 +513,19 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
513
513
  const [showLayers, setShowLayers] = useState(false);
514
514
  const [showHint, setShowHint] = useState(true);
515
515
  const [measureEnabled, setMeasureEnabled] = useState(false);
516
- const [measureMode, setMeasureMode] = useState("point");
516
+ const [measureMode, setMeasureMode] = useState("horizontal");
517
517
  const [measureDistance, setMeasureDistance] = useState(null);
518
+ const [measureFixedDist, setMeasureFixedDist] = useState(null);
519
+ const [measureFixedInput, setMeasureFixedInput] = useState("");
520
+ const measureModeRef = useRef(measureMode);
521
+ const measureFixedDistRef = useRef(measureFixedDist);
522
+ useEffect(() => {
523
+ measureModeRef.current = measureMode;
524
+ }, [measureMode]);
525
+ useEffect(() => {
526
+ measureFixedDistRef.current = measureFixedDist;
527
+ }, [measureFixedDist]);
528
+ const measureRedrawRef = useRef(null);
518
529
  const measureRef = useRef(null);
519
530
  useEffect(() => {
520
531
  let cancelled = false;
@@ -641,6 +652,21 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
641
652
  containerRef.current.appendChild(overlay);
642
653
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
643
654
  svg.setAttribute("style", "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;");
655
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
656
+ const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
657
+ marker.setAttribute("id", "dxf-measure-arrow");
658
+ marker.setAttribute("viewBox", "0 0 10 10");
659
+ marker.setAttribute("refX", "9");
660
+ marker.setAttribute("refY", "5");
661
+ marker.setAttribute("markerWidth", "6");
662
+ marker.setAttribute("markerHeight", "6");
663
+ marker.setAttribute("orient", "auto-start-reverse");
664
+ const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
665
+ arrowPath.setAttribute("d", "M 0 0 L 10 5 L 0 10 z");
666
+ arrowPath.setAttribute("fill", "#ff8800");
667
+ marker.appendChild(arrowPath);
668
+ defs.appendChild(marker);
669
+ svg.appendChild(defs);
644
670
  overlay.appendChild(svg);
645
671
  const refLineEl = document.createElementNS("http://www.w3.org/2000/svg", "line");
646
672
  refLineEl.setAttribute("stroke", "#ff8800");
@@ -649,22 +675,44 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
649
675
  refLineEl.setAttribute("opacity", "0.55");
650
676
  refLineEl.style.display = "none";
651
677
  svg.appendChild(refLineEl);
678
+ const extLineA = document.createElementNS("http://www.w3.org/2000/svg", "line");
679
+ extLineA.setAttribute("stroke", "#ff8800");
680
+ extLineA.setAttribute("stroke-width", "1");
681
+ extLineA.setAttribute("opacity", "0.85");
682
+ extLineA.style.display = "none";
683
+ svg.appendChild(extLineA);
684
+ const extLineB = document.createElementNS("http://www.w3.org/2000/svg", "line");
685
+ extLineB.setAttribute("stroke", "#ff8800");
686
+ extLineB.setAttribute("stroke-width", "1");
687
+ extLineB.setAttribute("opacity", "0.85");
688
+ extLineB.style.display = "none";
689
+ svg.appendChild(extLineB);
652
690
  const lineEl = document.createElementNS("http://www.w3.org/2000/svg", "line");
653
691
  lineEl.setAttribute("stroke", "#ff8800");
654
692
  lineEl.setAttribute("stroke-width", "1.5");
655
693
  lineEl.setAttribute("stroke-linecap", "round");
656
694
  lineEl.style.display = "none";
657
695
  svg.appendChild(lineEl);
696
+ const fixedLineEl = document.createElementNS("http://www.w3.org/2000/svg", "line");
697
+ fixedLineEl.setAttribute("stroke", "#ff8800");
698
+ fixedLineEl.setAttribute("stroke-width", "1.5");
699
+ fixedLineEl.setAttribute("stroke-linecap", "round");
700
+ fixedLineEl.style.display = "none";
701
+ svg.appendChild(fixedLineEl);
658
702
  const snapEl = document.createElement("div");
659
703
  snapEl.style.cssText = `position:absolute;width:14px;height:14px;border:2px solid #ff8800;background:rgba(255,255,255,0.7);transform:translate(-50%,-50%) rotate(45deg);box-sizing:border-box;display:none;`;
660
704
  overlay.appendChild(snapEl);
661
705
  measureRef.current = {
662
706
  picks: [],
663
- lineDir: null,
707
+ rawSecondClick: null,
664
708
  overlay,
665
709
  svg,
666
710
  line: lineEl,
711
+ fixedLine: fixedLineEl,
712
+ fixedLabel: null,
667
713
  refLine: refLineEl,
714
+ extLineA,
715
+ extLineB,
668
716
  markers: [],
669
717
  label: null,
670
718
  snap: snapEl,
@@ -769,32 +817,51 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
769
817
  el.style.left = `${p.x}px`;
770
818
  el.style.top = `${p.y}px`;
771
819
  };
772
- const computeRenderedEnds = () => {
820
+ const reconcileLockedPick = () => {
773
821
  const s = measureRef.current;
822
+ if (!s || !s.rawSecondClick || s.picks.length < 2) return;
774
823
  const a = s.picks[0];
775
- const b = s.picks[1];
776
- if (measureMode === "perp" && s.lineDir) {
777
- const d = s.lineDir;
778
- const ax = a.x, ay = a.y;
779
- const t = (b.x - ax) * d.dx + (b.y - ay) * d.dy;
780
- const fx = ax + d.dx * t;
781
- const fy = ay + d.dy * t;
782
- return { from: { x: fx, y: fy }, to: { x: b.x, y: b.y } };
824
+ const raw = s.rawSecondClick;
825
+ const fixed = measureFixedDistRef.current;
826
+ const mode = measureModeRef.current;
827
+ if (fixed !== null && Number.isFinite(fixed) && (mode === "horizontal" || mode === "vertical")) {
828
+ if (mode === "horizontal") {
829
+ const sign = raw.x >= a.x ? 1 : -1;
830
+ s.picks[1] = { x: a.x + sign * Math.abs(fixed), y: raw.y };
831
+ } else {
832
+ const sign = raw.y >= a.y ? 1 : -1;
833
+ s.picks[1] = { x: raw.x, y: a.y + sign * Math.abs(fixed) };
834
+ }
835
+ } else {
836
+ s.picks[1] = { x: raw.x, y: raw.y };
783
837
  }
784
- if (measureMode === "horizontal") {
785
- return { from: a, to: { x: b.x, y: a.y } };
838
+ };
839
+ const computeMainDimEnds = () => {
840
+ const s = measureRef.current;
841
+ const a = s.picks[0];
842
+ const b = s.picks[1];
843
+ const mode = measureModeRef.current;
844
+ const fixed = measureFixedDistRef.current;
845
+ if (fixed !== null && Number.isFinite(fixed) && mode === "horizontal") {
846
+ return { from: { x: b.x, y: a.y }, to: { x: b.x, y: b.y } };
786
847
  }
787
- if (measureMode === "vertical") {
788
- return { from: a, to: { x: a.x, y: b.y } };
848
+ if (fixed !== null && Number.isFinite(fixed) && mode === "vertical") {
849
+ return { from: { x: a.x, y: b.y }, to: { x: b.x, y: b.y } };
789
850
  }
851
+ if (mode === "horizontal") return { from: a, to: { x: b.x, y: a.y } };
852
+ if (mode === "vertical") return { from: a, to: { x: a.x, y: b.y } };
790
853
  return { from: a, to: b };
791
854
  };
792
855
  const updateOverlay = () => {
793
856
  const s = measureRef.current;
794
857
  if (!s) return;
858
+ reconcileLockedPick();
859
+ const mode = measureModeRef.current;
860
+ const fixed = measureFixedDistRef.current;
861
+ const fixedActive = fixed !== null && Number.isFinite(fixed) && (mode === "horizontal" || mode === "vertical");
795
862
  if (s.markers[0]) positionMarker(s.markers[0], s.picks[0].x, s.picks[0].y);
796
863
  if (s.markers[1]) positionMarker(s.markers[1], s.picks[1].x, s.picks[1].y);
797
- const refDir = measureMode === "horizontal" ? { dx: 1, dy: 0 } : measureMode === "vertical" ? { dx: 0, dy: 1 } : measureMode === "perp" ? s.lineDir : null;
864
+ const refDir = mode === "horizontal" ? { dx: 1, dy: 0 } : mode === "vertical" ? { dx: 0, dy: 1 } : null;
798
865
  if (refDir && s.picks.length >= 1 && s.refLine) {
799
866
  const a = s.picks[0];
800
867
  const w = canvas.clientWidth, h = canvas.clientHeight;
@@ -818,14 +885,65 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
818
885
  s.refLine.style.display = "none";
819
886
  }
820
887
  if (s.picks.length === 2) {
821
- const ends = computeRenderedEnds();
888
+ const a = s.picks[0], b = s.picks[1];
889
+ const ends = computeMainDimEnds();
822
890
  const fp = pxFromScene(ends.from.x, ends.from.y);
823
891
  const tp = pxFromScene(ends.to.x, ends.to.y);
824
892
  s.line.setAttribute("x1", String(fp.x));
825
893
  s.line.setAttribute("y1", String(fp.y));
826
894
  s.line.setAttribute("x2", String(tp.x));
827
895
  s.line.setAttribute("y2", String(tp.y));
828
- s.line.style.display = "";
896
+ s.line.setAttribute("marker-start", "url(#dxf-measure-arrow)");
897
+ s.line.setAttribute("marker-end", "url(#dxf-measure-arrow)");
898
+ const mainLen = Math.hypot(tp.x - fp.x, tp.y - fp.y);
899
+ s.line.style.display = mainLen > 0.5 ? "" : "none";
900
+ if (fixedActive && s.fixedLine) {
901
+ const r = mode === "horizontal" ? { x: b.x, y: a.y } : { x: a.x, y: b.y };
902
+ const ap = pxFromScene(a.x, a.y);
903
+ const rp = pxFromScene(r.x, r.y);
904
+ s.fixedLine.setAttribute("x1", String(ap.x));
905
+ s.fixedLine.setAttribute("y1", String(ap.y));
906
+ s.fixedLine.setAttribute("x2", String(rp.x));
907
+ s.fixedLine.setAttribute("y2", String(rp.y));
908
+ s.fixedLine.setAttribute("marker-start", "url(#dxf-measure-arrow)");
909
+ s.fixedLine.setAttribute("marker-end", "url(#dxf-measure-arrow)");
910
+ s.fixedLine.style.display = "";
911
+ const fLabel = ensureFixedLabel();
912
+ fLabel.textContent = formatMeasureDistance(Math.abs(fixed));
913
+ const fcx = (ap.x + rp.x) / 2;
914
+ const fcy = (ap.y + rp.y) / 2;
915
+ fLabel.style.left = `${fcx}px`;
916
+ fLabel.style.top = `${fcy}px`;
917
+ const w = canvas.clientWidth, h = canvas.clientHeight;
918
+ fLabel.style.display = fcx < 0 || fcy < 0 || fcx > w || fcy > h ? "none" : "";
919
+ } else {
920
+ if (s.fixedLine) s.fixedLine.style.display = "none";
921
+ if (s.fixedLabel) s.fixedLabel.style.display = "none";
922
+ }
923
+ const extA = s.extLineA;
924
+ const extB = s.extLineB;
925
+ if (!fixedActive && mode === "horizontal" && Math.abs(b.y - a.y) > 1e-9) {
926
+ const p = pxFromScene(b.x, a.y);
927
+ const q = pxFromScene(b.x, b.y);
928
+ extB.setAttribute("x1", String(p.x));
929
+ extB.setAttribute("y1", String(p.y));
930
+ extB.setAttribute("x2", String(q.x));
931
+ extB.setAttribute("y2", String(q.y));
932
+ extB.style.display = "";
933
+ extA.style.display = "none";
934
+ } else if (!fixedActive && mode === "vertical" && Math.abs(b.x - a.x) > 1e-9) {
935
+ const p = pxFromScene(a.x, b.y);
936
+ const q = pxFromScene(b.x, b.y);
937
+ extB.setAttribute("x1", String(p.x));
938
+ extB.setAttribute("y1", String(p.y));
939
+ extB.setAttribute("x2", String(q.x));
940
+ extB.setAttribute("y2", String(q.y));
941
+ extB.style.display = "";
942
+ extA.style.display = "none";
943
+ } else {
944
+ extA.style.display = "none";
945
+ extB.style.display = "none";
946
+ }
829
947
  if (s.label) {
830
948
  const cx = (fp.x + tp.x) / 2;
831
949
  const cy = (fp.y + tp.y) / 2;
@@ -833,11 +951,25 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
833
951
  s.label.style.top = `${cy}px`;
834
952
  const w = canvas.clientWidth, h = canvas.clientHeight;
835
953
  s.label.style.display = cx < 0 || cy < 0 || cx > w || cy > h ? "none" : "";
954
+ if (mainLen <= 0.5) s.label.style.display = "none";
836
955
  }
837
956
  } else {
838
957
  s.line.style.display = "none";
958
+ if (s.fixedLine) s.fixedLine.style.display = "none";
959
+ if (s.fixedLabel) s.fixedLabel.style.display = "none";
960
+ if (s.extLineA) s.extLineA.style.display = "none";
961
+ if (s.extLineB) s.extLineB.style.display = "none";
839
962
  }
840
963
  };
964
+ const ensureFixedLabel = () => {
965
+ const s = measureRef.current;
966
+ if (s.fixedLabel) return s.fixedLabel;
967
+ const el = document.createElement("div");
968
+ el.style.cssText = `position:absolute;left:-9999px;top:-9999px;transform:translate(-50%,-50%);padding:2px 6px;font-size:11px;font-weight:600;font-family:system-ui,-apple-system,sans-serif;background:rgba(255,136,0,0.75);color:#fff;border-radius:4px;white-space:nowrap;box-shadow:0 1px 4px rgba(0,0,0,0.25);pointer-events:none;`;
969
+ overlay.appendChild(el);
970
+ s.fixedLabel = el;
971
+ return el;
972
+ };
841
973
  const DRAG_TOL = 4;
842
974
  const DRAG_TIME = 350;
843
975
  let downX = 0, downY = 0, downTime = 0, downActive = false, dragging = false;
@@ -881,10 +1013,44 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
881
1013
  if (dragging || elapsed > DRAG_TIME) return;
882
1014
  const raw = ev?.detail?.position;
883
1015
  if (!raw) return;
884
- const useSnap = lastSnap && Math.hypot(downX - canvas.getBoundingClientRect().width, 0) >= 0;
885
- const picked = useSnap && lastSnap ? { x: lastSnap.sx, y: lastSnap.sy, snapType: lastSnap.type, lineDir: lastSnap.dir } : { x: raw.x, y: raw.y, lineDir: void 0 };
1016
+ const picked = lastSnap ? { x: lastSnap.sx, y: lastSnap.sy } : { x: raw.x, y: raw.y };
886
1017
  doPick(picked);
887
1018
  };
1019
+ const recomputeLabel = () => {
1020
+ const s = measureRef.current;
1021
+ if (!s || s.picks.length !== 2) return;
1022
+ reconcileLockedPick();
1023
+ const a = s.picks[0], b = s.picks[1];
1024
+ const mode = measureModeRef.current;
1025
+ const fixed = measureFixedDistRef.current;
1026
+ const fixedActive = fixed !== null && Number.isFinite(fixed) && (mode === "horizontal" || mode === "vertical");
1027
+ let dist;
1028
+ let suffix = "";
1029
+ if (fixedActive && mode === "horizontal") {
1030
+ dist = Math.abs(b.y - a.y);
1031
+ suffix = " \u2195";
1032
+ } else if (fixedActive && mode === "vertical") {
1033
+ dist = Math.abs(b.x - a.x);
1034
+ suffix = " \u2194";
1035
+ } else if (mode === "horizontal") {
1036
+ dist = Math.abs(b.x - a.x);
1037
+ suffix = " \u2194";
1038
+ } else if (mode === "vertical") {
1039
+ dist = Math.abs(b.y - a.y);
1040
+ suffix = " \u2195";
1041
+ } else {
1042
+ const dx = b.x - a.x, dy = b.y - a.y;
1043
+ dist = Math.sqrt(dx * dx + dy * dy);
1044
+ }
1045
+ setMeasureDistance(dist);
1046
+ const label = ensureLabel();
1047
+ label.style.opacity = "1";
1048
+ label.textContent = `${formatMeasureDistance(dist)}${suffix}`;
1049
+ };
1050
+ measureRedrawRef.current = () => {
1051
+ recomputeLabel();
1052
+ updateOverlay();
1053
+ };
888
1054
  const doPick = (p) => {
889
1055
  const s = measureRef.current;
890
1056
  if (!s) return;
@@ -892,51 +1058,24 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
892
1058
  for (const m of s.markers) m.parentElement?.removeChild(m);
893
1059
  s.markers = [];
894
1060
  s.picks = [];
895
- s.lineDir = null;
1061
+ s.rawSecondClick = null;
896
1062
  s.line.style.display = "none";
1063
+ if (s.fixedLine) s.fixedLine.style.display = "none";
1064
+ if (s.fixedLabel) s.fixedLabel.style.display = "none";
897
1065
  if (s.refLine) s.refLine.style.display = "none";
1066
+ if (s.extLineA) s.extLineA.style.display = "none";
1067
+ if (s.extLineB) s.extLineB.style.display = "none";
898
1068
  if (s.label) s.label.style.opacity = "0";
899
1069
  setMeasureDistance(null);
900
1070
  }
901
- if (measureMode === "perp" && s.picks.length === 0) {
902
- if (!p.lineDir) {
903
- const label = ensureLabel();
904
- label.style.display = "";
905
- label.style.opacity = "1";
906
- label.style.left = `${pxFromScene(p.x, p.y).x}px`;
907
- label.style.top = `${pxFromScene(p.x, p.y).y - 18}px`;
908
- label.textContent = "\u22A5: snap to a line or corner first";
909
- setTimeout(() => {
910
- if (s.label && s.picks.length === 0) s.label.style.opacity = "0";
911
- }, 1500);
912
- return;
913
- }
914
- s.lineDir = p.lineDir;
915
- }
916
- s.picks.push({ x: p.x, y: p.y });
917
- s.markers.push(makeMarker());
918
- if (s.picks.length === 2) {
919
- const a = s.picks[0], b = s.picks[1];
920
- let dist;
921
- let suffix = "";
922
- if (measureMode === "perp" && s.lineDir) {
923
- const dx = b.x - a.x, dy = b.y - a.y;
924
- dist = Math.abs(dx * s.lineDir.dy - dy * s.lineDir.dx);
925
- suffix = " \u22A5";
926
- } else if (measureMode === "horizontal") {
927
- dist = Math.abs(b.x - a.x);
928
- suffix = " \u2194";
929
- } else if (measureMode === "vertical") {
930
- dist = Math.abs(b.y - a.y);
931
- suffix = " \u2195";
932
- } else {
933
- const dx = b.x - a.x, dy = b.y - a.y;
934
- dist = Math.sqrt(dx * dx + dy * dy);
935
- }
936
- setMeasureDistance(dist);
937
- const label = ensureLabel();
938
- label.style.opacity = "1";
939
- label.textContent = `${formatMeasureDistance(dist)}${suffix}`;
1071
+ if (s.picks.length === 0) {
1072
+ s.picks.push({ x: p.x, y: p.y });
1073
+ s.markers.push(makeMarker());
1074
+ } else {
1075
+ s.rawSecondClick = { x: p.x, y: p.y };
1076
+ s.picks.push({ x: p.x, y: p.y });
1077
+ s.markers.push(makeMarker());
1078
+ recomputeLabel();
940
1079
  }
941
1080
  updateOverlay();
942
1081
  };
@@ -974,10 +1113,14 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
974
1113
  }
975
1114
  canvas.removeEventListener("pointermove", handlePointerMove);
976
1115
  window.removeEventListener("keydown", onKeyDown);
1116
+ measureRedrawRef.current = null;
977
1117
  teardown();
978
1118
  setMeasureDistance(null);
979
1119
  };
980
- }, [measureEnabled, measureMode, loading, error]);
1120
+ }, [measureEnabled, loading, error]);
1121
+ useEffect(() => {
1122
+ measureRedrawRef.current?.();
1123
+ }, [measureMode, measureFixedDist]);
981
1124
  const toggleLayer = (name) => {
982
1125
  setLayers((prev) => prev.map((l) => {
983
1126
  if (l.name !== name) return l;
@@ -1067,52 +1210,80 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
1067
1210
  ]
1068
1211
  }
1069
1212
  ),
1070
- measureEnabled && /* @__PURE__ */ jsxs("div", { className: "flex items-stretch h-7 rounded border border-gray-200 overflow-hidden text-[11px] font-semibold", children: [
1071
- /* @__PURE__ */ jsx(
1072
- "button",
1073
- {
1074
- onClick: () => setMeasureMode("point"),
1075
- className: `px-2 transition-colors ${measureMode === "point" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1076
- title: "Point \u2014 straight-line distance between two picks",
1077
- children: "Point"
1078
- }
1079
- ),
1080
- /* @__PURE__ */ jsx(
1081
- "button",
1082
- {
1083
- onClick: () => setMeasureMode("perp"),
1084
- className: `px-2 transition-colors ${measureMode === "perp" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1085
- title: "Perpendicular \u2014 click on a line first, then pick a point. Reports the perpendicular distance.",
1086
- children: "\u22A5"
1087
- }
1088
- ),
1089
- /* @__PURE__ */ jsx(
1090
- "button",
1091
- {
1092
- onClick: () => setMeasureMode("horizontal"),
1093
- className: `px-2 transition-colors ${measureMode === "horizontal" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1094
- title: "Horizontal \u2014 distance along the X axis between two picks (AutoCAD DIMLINEAR horizontal)",
1095
- children: "H"
1096
- }
1097
- ),
1098
- /* @__PURE__ */ jsx(
1099
- "button",
1100
- {
1101
- onClick: () => setMeasureMode("vertical"),
1102
- className: `px-2 transition-colors ${measureMode === "vertical" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1103
- title: "Vertical \u2014 distance along the Y axis between two picks (AutoCAD DIMLINEAR vertical)",
1104
- children: "V"
1105
- }
1106
- )
1213
+ measureEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
1214
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch h-7 rounded border border-gray-200 overflow-hidden text-[11px] font-semibold", children: [
1215
+ /* @__PURE__ */ jsx(
1216
+ "button",
1217
+ {
1218
+ onClick: () => setMeasureMode("point"),
1219
+ className: `px-2 transition-colors ${measureMode === "point" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1220
+ title: "Point \u2014 straight-line (Euclidean) distance between two picks",
1221
+ children: "Point"
1222
+ }
1223
+ ),
1224
+ /* @__PURE__ */ jsx(
1225
+ "button",
1226
+ {
1227
+ onClick: () => setMeasureMode("horizontal"),
1228
+ className: `px-2 transition-colors ${measureMode === "horizontal" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1229
+ title: "Horizontal \u2014 distance along the X axis between two picks (AutoCAD DIMLINEAR horizontal)",
1230
+ children: "H"
1231
+ }
1232
+ ),
1233
+ /* @__PURE__ */ jsx(
1234
+ "button",
1235
+ {
1236
+ onClick: () => setMeasureMode("vertical"),
1237
+ className: `px-2 transition-colors ${measureMode === "vertical" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
1238
+ title: "Vertical \u2014 distance along the Y axis between two picks (AutoCAD DIMLINEAR vertical)",
1239
+ children: "V"
1240
+ }
1241
+ )
1242
+ ] }),
1243
+ (measureMode === "horizontal" || measureMode === "vertical") && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1244
+ /* @__PURE__ */ jsx(
1245
+ "input",
1246
+ {
1247
+ type: "number",
1248
+ inputMode: "decimal",
1249
+ step: "any",
1250
+ value: measureFixedInput,
1251
+ placeholder: measureMode === "horizontal" ? "fix \u0394x" : "fix \u0394y",
1252
+ onChange: (e) => {
1253
+ const raw = e.target.value;
1254
+ setMeasureFixedInput(raw);
1255
+ if (raw.trim() === "") setMeasureFixedDist(null);
1256
+ else {
1257
+ const n = parseFloat(raw);
1258
+ setMeasureFixedDist(Number.isFinite(n) && n !== 0 ? n : null);
1259
+ }
1260
+ },
1261
+ className: "h-7 w-20 px-1.5 text-[11px] font-mono rounded border border-gray-200 bg-white text-gray-700 focus:outline-none focus:border-orange-400",
1262
+ title: "Lock the second pick's axis coord to first pick + this value (mm). The reported measurement becomes the perpendicular distance."
1263
+ }
1264
+ ),
1265
+ measureFixedDist !== null && /* @__PURE__ */ jsx(
1266
+ "button",
1267
+ {
1268
+ onClick: () => {
1269
+ setMeasureFixedDist(null);
1270
+ setMeasureFixedInput("");
1271
+ },
1272
+ className: "text-gray-400 hover:text-gray-600 text-[11px] px-1",
1273
+ title: "Clear fixed distance",
1274
+ children: "\xD7"
1275
+ }
1276
+ )
1277
+ ] })
1107
1278
  ] }),
1108
1279
  measureEnabled && measureDistance !== null && /* @__PURE__ */ jsxs(
1109
1280
  "div",
1110
1281
  {
1111
1282
  className: "px-2 py-1 text-[11px] font-mono font-semibold text-orange-600 bg-orange-50 border border-orange-200 rounded whitespace-nowrap",
1112
- title: measureMode === "perp" ? "Perpendicular distance from second pick to first line" : measureMode === "horizontal" ? "Horizontal distance (\u0394x) between the two picked points" : measureMode === "vertical" ? "Vertical distance (\u0394y) between the two picked points" : "Straight-line distance between the two picked points",
1283
+ title: measureMode === "horizontal" ? "Horizontal distance (\u0394x) between the two picked points" : measureMode === "vertical" ? "Vertical distance (\u0394y) between the two picked points" : "Straight-line distance between the two picked points",
1113
1284
  children: [
1114
1285
  formatMeasureDistance(measureDistance),
1115
- measureMode === "perp" ? " \u22A5" : measureMode === "horizontal" ? " \u2194" : measureMode === "vertical" ? " \u2195" : ""
1286
+ measureMode === "horizontal" ? " \u2194" : measureMode === "vertical" ? " \u2195" : ""
1116
1287
  ]
1117
1288
  }
1118
1289
  ),
@@ -2476,5 +2647,5 @@ function ImagePanel({ url, filename, onDownload, onEmail }) {
2476
2647
  }
2477
2648
 
2478
2649
  export { Preview, setPdfPreview };
2479
- //# sourceMappingURL=chunk-TB72OMUU.js.map
2480
- //# sourceMappingURL=chunk-TB72OMUU.js.map
2650
+ //# sourceMappingURL=chunk-ATKI25NG.js.map
2651
+ //# sourceMappingURL=chunk-ATKI25NG.js.map