react-os-shell 0.2.46 → 0.2.47
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.
- package/dist/{Files-CEID4TC3.js → Files-QMEAFYY4.js} +4 -4
- package/dist/{Files-CEID4TC3.js.map → Files-QMEAFYY4.js.map} +1 -1
- package/dist/Preview-KX753OG4.js +7 -0
- package/dist/{Preview-B5DUW2AR.js.map → Preview-KX753OG4.js.map} +1 -1
- package/dist/apps/index.js +4 -4
- package/dist/{chunk-6AJUSDEM.js → chunk-FWOHEXUL.js} +3 -3
- package/dist/{chunk-6AJUSDEM.js.map → chunk-FWOHEXUL.js.map} +1 -1
- package/dist/{chunk-QBH7KERS.js → chunk-G6WVMTJU.js} +387 -2
- package/dist/chunk-G6WVMTJU.js.map +1 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/Preview-B5DUW2AR.js +0 -7
- package/dist/chunk-QBH7KERS.js.map +0 -1
|
@@ -410,6 +410,10 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
|
|
|
410
410
|
const [layers, setLayers] = useState([]);
|
|
411
411
|
const [showLayers, setShowLayers] = useState(false);
|
|
412
412
|
const [showHint, setShowHint] = useState(true);
|
|
413
|
+
const [measureEnabled, setMeasureEnabled] = useState(false);
|
|
414
|
+
const [measureMode, setMeasureMode] = useState("point");
|
|
415
|
+
const [measureDistance, setMeasureDistance] = useState(null);
|
|
416
|
+
const measureRef = useRef(null);
|
|
413
417
|
useEffect(() => {
|
|
414
418
|
let cancelled = false;
|
|
415
419
|
let viewer = null;
|
|
@@ -512,6 +516,348 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
|
|
|
512
516
|
const t = setTimeout(() => setShowHint(false), 5e3);
|
|
513
517
|
return () => clearTimeout(t);
|
|
514
518
|
}, [showHint, loading]);
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
const v = viewerRef.current;
|
|
521
|
+
if (loading || error || !v) return;
|
|
522
|
+
const scene = v.GetScene?.();
|
|
523
|
+
const camera = v.GetCamera?.();
|
|
524
|
+
const canvas = v.GetCanvas?.();
|
|
525
|
+
if (!scene || !camera || !canvas || !containerRef.current) return;
|
|
526
|
+
const teardown = () => {
|
|
527
|
+
const s = measureRef.current;
|
|
528
|
+
if (!s) return;
|
|
529
|
+
if (s.overlay && s.overlay.parentElement) s.overlay.parentElement.removeChild(s.overlay);
|
|
530
|
+
measureRef.current = null;
|
|
531
|
+
};
|
|
532
|
+
if (!measureEnabled) {
|
|
533
|
+
teardown();
|
|
534
|
+
setMeasureDistance(null);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const overlay = document.createElement("div");
|
|
538
|
+
overlay.style.cssText = "position:absolute;inset:0;pointer-events:none;z-index:5;";
|
|
539
|
+
containerRef.current.appendChild(overlay);
|
|
540
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
541
|
+
svg.setAttribute("style", "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;");
|
|
542
|
+
overlay.appendChild(svg);
|
|
543
|
+
const refLineEl = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
544
|
+
refLineEl.setAttribute("stroke", "#ff8800");
|
|
545
|
+
refLineEl.setAttribute("stroke-width", "1");
|
|
546
|
+
refLineEl.setAttribute("stroke-dasharray", "6,4");
|
|
547
|
+
refLineEl.setAttribute("opacity", "0.55");
|
|
548
|
+
refLineEl.style.display = "none";
|
|
549
|
+
svg.appendChild(refLineEl);
|
|
550
|
+
const lineEl = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
551
|
+
lineEl.setAttribute("stroke", "#ff8800");
|
|
552
|
+
lineEl.setAttribute("stroke-width", "1.5");
|
|
553
|
+
lineEl.setAttribute("stroke-linecap", "round");
|
|
554
|
+
lineEl.style.display = "none";
|
|
555
|
+
svg.appendChild(lineEl);
|
|
556
|
+
const snapEl = document.createElement("div");
|
|
557
|
+
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;`;
|
|
558
|
+
overlay.appendChild(snapEl);
|
|
559
|
+
measureRef.current = {
|
|
560
|
+
picks: [],
|
|
561
|
+
lineDir: null,
|
|
562
|
+
overlay,
|
|
563
|
+
svg,
|
|
564
|
+
line: lineEl,
|
|
565
|
+
refLine: refLineEl,
|
|
566
|
+
markers: [],
|
|
567
|
+
label: null,
|
|
568
|
+
snap: snapEl,
|
|
569
|
+
segments: []
|
|
570
|
+
};
|
|
571
|
+
let THREE = null;
|
|
572
|
+
let ready = false;
|
|
573
|
+
const pxFromScene = (sx, sy) => {
|
|
574
|
+
if (!THREE) return { x: 0, y: 0 };
|
|
575
|
+
const v3 = new THREE.Vector3(sx, sy, 0).project(camera);
|
|
576
|
+
const w = canvas.clientWidth, h = canvas.clientHeight;
|
|
577
|
+
return { x: (v3.x + 1) / 2 * w, y: (-v3.y + 1) / 2 * h };
|
|
578
|
+
};
|
|
579
|
+
(async () => {
|
|
580
|
+
try {
|
|
581
|
+
THREE = await import(
|
|
582
|
+
/* @vite-ignore */
|
|
583
|
+
'three'
|
|
584
|
+
);
|
|
585
|
+
const segs = measureRef.current?.segments;
|
|
586
|
+
if (!segs) return;
|
|
587
|
+
const va = new THREE.Vector3(), vb = new THREE.Vector3();
|
|
588
|
+
scene.traverse((obj) => {
|
|
589
|
+
if (!obj?.isLineSegments) return;
|
|
590
|
+
const pos = obj.geometry?.attributes?.position;
|
|
591
|
+
if (!pos) return;
|
|
592
|
+
const arr = pos.array;
|
|
593
|
+
obj.updateMatrixWorld?.();
|
|
594
|
+
const m = obj.matrixWorld;
|
|
595
|
+
for (let i = 0; i < arr.length; i += 6) {
|
|
596
|
+
va.set(arr[i], arr[i + 1], arr[i + 2]).applyMatrix4(m);
|
|
597
|
+
vb.set(arr[i + 3], arr[i + 4], arr[i + 5]).applyMatrix4(m);
|
|
598
|
+
segs.push({ ax: va.x, ay: va.y, bx: vb.x, by: vb.y });
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
ready = true;
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
})();
|
|
605
|
+
const SNAP_PX = 12;
|
|
606
|
+
const findSnap = (cx, cy) => {
|
|
607
|
+
const s = measureRef.current;
|
|
608
|
+
if (!s || !ready) return null;
|
|
609
|
+
let best = null;
|
|
610
|
+
let bestD2 = SNAP_PX * SNAP_PX;
|
|
611
|
+
for (const seg of s.segments) {
|
|
612
|
+
const ap = pxFromScene(seg.ax, seg.ay);
|
|
613
|
+
const bp = pxFromScene(seg.bx, seg.by);
|
|
614
|
+
const ldx = seg.bx - seg.ax, ldy = seg.by - seg.ay;
|
|
615
|
+
const llen = Math.sqrt(ldx * ldx + ldy * ldy) || 1;
|
|
616
|
+
const segDir = { dx: ldx / llen, dy: ldy / llen };
|
|
617
|
+
let dx = cx - ap.x, dy = cy - ap.y;
|
|
618
|
+
let d2 = dx * dx + dy * dy;
|
|
619
|
+
if (d2 < bestD2) {
|
|
620
|
+
best = { sx: seg.ax, sy: seg.ay, type: "endpoint", dir: segDir };
|
|
621
|
+
bestD2 = d2;
|
|
622
|
+
}
|
|
623
|
+
dx = cx - bp.x;
|
|
624
|
+
dy = cy - bp.y;
|
|
625
|
+
d2 = dx * dx + dy * dy;
|
|
626
|
+
if (d2 < bestD2) {
|
|
627
|
+
best = { sx: seg.bx, sy: seg.by, type: "endpoint", dir: segDir };
|
|
628
|
+
bestD2 = d2;
|
|
629
|
+
}
|
|
630
|
+
const sdx = bp.x - ap.x, sdy = bp.y - ap.y;
|
|
631
|
+
const len2 = sdx * sdx + sdy * sdy;
|
|
632
|
+
if (len2 > 0) {
|
|
633
|
+
const t = ((cx - ap.x) * sdx + (cy - ap.y) * sdy) / len2;
|
|
634
|
+
if (t > 0 && t < 1) {
|
|
635
|
+
const px = ap.x + t * sdx, py = ap.y + t * sdy;
|
|
636
|
+
dx = cx - px;
|
|
637
|
+
dy = cy - py;
|
|
638
|
+
d2 = dx * dx + dy * dy;
|
|
639
|
+
if (d2 < bestD2) {
|
|
640
|
+
const sx = seg.ax + t * (seg.bx - seg.ax);
|
|
641
|
+
const sy = seg.ay + t * (seg.by - seg.ay);
|
|
642
|
+
best = { sx, sy, type: "line", dir: segDir };
|
|
643
|
+
bestD2 = d2;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return best;
|
|
649
|
+
};
|
|
650
|
+
const makeMarker = () => {
|
|
651
|
+
const el = document.createElement("div");
|
|
652
|
+
el.style.cssText = `position:absolute;width:10px;height:10px;border-radius:50%;background:#ff8800;border:2px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.25);transform:translate(-50%,-50%);pointer-events:none;`;
|
|
653
|
+
overlay.appendChild(el);
|
|
654
|
+
return el;
|
|
655
|
+
};
|
|
656
|
+
const ensureLabel = () => {
|
|
657
|
+
const s = measureRef.current;
|
|
658
|
+
if (s.label) return s.label;
|
|
659
|
+
const el = document.createElement("div");
|
|
660
|
+
el.style.cssText = `position:absolute;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.95);color:#fff;border-radius:4px;white-space:nowrap;box-shadow:0 1px 4px rgba(0,0,0,0.25);pointer-events:none;`;
|
|
661
|
+
overlay.appendChild(el);
|
|
662
|
+
s.label = el;
|
|
663
|
+
return el;
|
|
664
|
+
};
|
|
665
|
+
const positionMarker = (el, x, y) => {
|
|
666
|
+
const p = pxFromScene(x, y);
|
|
667
|
+
el.style.left = `${p.x}px`;
|
|
668
|
+
el.style.top = `${p.y}px`;
|
|
669
|
+
};
|
|
670
|
+
const computeRenderedEnds = () => {
|
|
671
|
+
const s = measureRef.current;
|
|
672
|
+
const a = s.picks[0];
|
|
673
|
+
let b = s.picks[1];
|
|
674
|
+
if (measureMode === "perp" && s.lineDir) {
|
|
675
|
+
const d = s.lineDir;
|
|
676
|
+
const ax = a.x, ay = a.y;
|
|
677
|
+
const t = (b.x - ax) * d.dx + (b.y - ay) * d.dy;
|
|
678
|
+
const fx = ax + d.dx * t;
|
|
679
|
+
const fy = ay + d.dy * t;
|
|
680
|
+
return { from: { x: fx, y: fy }, to: { x: b.x, y: b.y } };
|
|
681
|
+
}
|
|
682
|
+
return { from: a, to: b };
|
|
683
|
+
};
|
|
684
|
+
const updateOverlay = () => {
|
|
685
|
+
const s = measureRef.current;
|
|
686
|
+
if (!s) return;
|
|
687
|
+
if (s.markers[0]) positionMarker(s.markers[0], s.picks[0].x, s.picks[0].y);
|
|
688
|
+
if (s.markers[1]) positionMarker(s.markers[1], s.picks[1].x, s.picks[1].y);
|
|
689
|
+
if (measureMode === "perp" && s.picks.length >= 1 && s.lineDir && s.refLine) {
|
|
690
|
+
const a = s.picks[0];
|
|
691
|
+
const w = canvas.clientWidth, h = canvas.clientHeight;
|
|
692
|
+
const screenSpan = Math.hypot(w, h) * 4;
|
|
693
|
+
const ap = pxFromScene(a.x, a.y);
|
|
694
|
+
const probe = pxFromScene(a.x + s.lineDir.dx, a.y + s.lineDir.dy);
|
|
695
|
+
const pxLen = Math.hypot(probe.x - ap.x, probe.y - ap.y) || 1;
|
|
696
|
+
const sceneStep = screenSpan / pxLen;
|
|
697
|
+
const x0 = a.x - s.lineDir.dx * sceneStep;
|
|
698
|
+
const y0 = a.y - s.lineDir.dy * sceneStep;
|
|
699
|
+
const x1 = a.x + s.lineDir.dx * sceneStep;
|
|
700
|
+
const y1 = a.y + s.lineDir.dy * sceneStep;
|
|
701
|
+
const p0 = pxFromScene(x0, y0);
|
|
702
|
+
const p1 = pxFromScene(x1, y1);
|
|
703
|
+
s.refLine.setAttribute("x1", String(p0.x));
|
|
704
|
+
s.refLine.setAttribute("y1", String(p0.y));
|
|
705
|
+
s.refLine.setAttribute("x2", String(p1.x));
|
|
706
|
+
s.refLine.setAttribute("y2", String(p1.y));
|
|
707
|
+
s.refLine.style.display = "";
|
|
708
|
+
} else if (s.refLine) {
|
|
709
|
+
s.refLine.style.display = "none";
|
|
710
|
+
}
|
|
711
|
+
if (s.picks.length === 2) {
|
|
712
|
+
const ends = computeRenderedEnds();
|
|
713
|
+
const fp = pxFromScene(ends.from.x, ends.from.y);
|
|
714
|
+
const tp = pxFromScene(ends.to.x, ends.to.y);
|
|
715
|
+
s.line.setAttribute("x1", String(fp.x));
|
|
716
|
+
s.line.setAttribute("y1", String(fp.y));
|
|
717
|
+
s.line.setAttribute("x2", String(tp.x));
|
|
718
|
+
s.line.setAttribute("y2", String(tp.y));
|
|
719
|
+
s.line.style.display = "";
|
|
720
|
+
if (s.label) {
|
|
721
|
+
s.label.style.left = `${(fp.x + tp.x) / 2}px`;
|
|
722
|
+
s.label.style.top = `${(fp.y + tp.y) / 2}px`;
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
s.line.style.display = "none";
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
const DRAG_TOL = 4;
|
|
729
|
+
const DRAG_TIME = 350;
|
|
730
|
+
let downX = 0, downY = 0, downTime = 0, downActive = false, dragging = false;
|
|
731
|
+
let lastSnap = null;
|
|
732
|
+
const handlePointerDown = (ev) => {
|
|
733
|
+
const d = ev?.detail;
|
|
734
|
+
if (!d || d.domEvent?.button !== 0) return;
|
|
735
|
+
downX = d.canvasCoord.x;
|
|
736
|
+
downY = d.canvasCoord.y;
|
|
737
|
+
downTime = performance.now();
|
|
738
|
+
dragging = false;
|
|
739
|
+
downActive = true;
|
|
740
|
+
};
|
|
741
|
+
const handlePointerMove = (ev) => {
|
|
742
|
+
const rect = canvas.getBoundingClientRect();
|
|
743
|
+
const cx = ev.clientX - rect.left;
|
|
744
|
+
const cy = ev.clientY - rect.top;
|
|
745
|
+
if (downActive) {
|
|
746
|
+
if ((cx - downX) ** 2 + (cy - downY) ** 2 > DRAG_TOL * DRAG_TOL) dragging = true;
|
|
747
|
+
}
|
|
748
|
+
const s = measureRef.current;
|
|
749
|
+
if (!s || !s.snap) return;
|
|
750
|
+
if (downActive && dragging) {
|
|
751
|
+
s.snap.style.display = "none";
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
lastSnap = findSnap(cx, cy);
|
|
755
|
+
if (lastSnap) {
|
|
756
|
+
const p = pxFromScene(lastSnap.sx, lastSnap.sy);
|
|
757
|
+
s.snap.style.left = `${p.x}px`;
|
|
758
|
+
s.snap.style.top = `${p.y}px`;
|
|
759
|
+
s.snap.style.display = "";
|
|
760
|
+
} else {
|
|
761
|
+
s.snap.style.display = "none";
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
const handlePointerUp = (ev) => {
|
|
765
|
+
if (!downActive) return;
|
|
766
|
+
downActive = false;
|
|
767
|
+
const elapsed = performance.now() - downTime;
|
|
768
|
+
if (dragging || elapsed > DRAG_TIME) return;
|
|
769
|
+
const raw = ev?.detail?.position;
|
|
770
|
+
if (!raw) return;
|
|
771
|
+
const useSnap = lastSnap && Math.hypot(downX - canvas.getBoundingClientRect().width, 0) >= 0;
|
|
772
|
+
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 };
|
|
773
|
+
doPick(picked);
|
|
774
|
+
};
|
|
775
|
+
const doPick = (p) => {
|
|
776
|
+
const s = measureRef.current;
|
|
777
|
+
if (!s) return;
|
|
778
|
+
if (s.picks.length === 2) {
|
|
779
|
+
for (const m of s.markers) m.parentElement?.removeChild(m);
|
|
780
|
+
s.markers = [];
|
|
781
|
+
s.picks = [];
|
|
782
|
+
s.lineDir = null;
|
|
783
|
+
s.line.style.display = "none";
|
|
784
|
+
if (s.refLine) s.refLine.style.display = "none";
|
|
785
|
+
if (s.label) s.label.style.opacity = "0";
|
|
786
|
+
setMeasureDistance(null);
|
|
787
|
+
}
|
|
788
|
+
if (measureMode === "perp" && s.picks.length === 0) {
|
|
789
|
+
if (!p.lineDir) {
|
|
790
|
+
const label = ensureLabel();
|
|
791
|
+
label.style.opacity = "1";
|
|
792
|
+
label.style.left = `${pxFromScene(p.x, p.y).x}px`;
|
|
793
|
+
label.style.top = `${pxFromScene(p.x, p.y).y - 18}px`;
|
|
794
|
+
label.textContent = "\u22A5: snap to a line or corner first";
|
|
795
|
+
setTimeout(() => {
|
|
796
|
+
if (s.label && s.picks.length === 0) s.label.style.opacity = "0";
|
|
797
|
+
}, 1500);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
s.lineDir = p.lineDir;
|
|
801
|
+
}
|
|
802
|
+
s.picks.push({ x: p.x, y: p.y });
|
|
803
|
+
s.markers.push(makeMarker());
|
|
804
|
+
if (s.picks.length === 2) {
|
|
805
|
+
const a = s.picks[0], b = s.picks[1];
|
|
806
|
+
let dist;
|
|
807
|
+
let suffix = "";
|
|
808
|
+
if (measureMode === "perp" && s.lineDir) {
|
|
809
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
810
|
+
dist = Math.abs(dx * s.lineDir.dy - dy * s.lineDir.dx);
|
|
811
|
+
suffix = " \u22A5";
|
|
812
|
+
} else {
|
|
813
|
+
const dx = b.x - a.x, dy = b.y - a.y;
|
|
814
|
+
dist = Math.sqrt(dx * dx + dy * dy);
|
|
815
|
+
}
|
|
816
|
+
setMeasureDistance(dist);
|
|
817
|
+
const label = ensureLabel();
|
|
818
|
+
label.style.opacity = "1";
|
|
819
|
+
label.textContent = `${formatMeasureDistance(dist)}${suffix}`;
|
|
820
|
+
}
|
|
821
|
+
updateOverlay();
|
|
822
|
+
};
|
|
823
|
+
const onKeyDown = (ev) => {
|
|
824
|
+
if (ev.key === "Escape") setMeasureEnabled(false);
|
|
825
|
+
};
|
|
826
|
+
canvas.style.cursor = "crosshair";
|
|
827
|
+
try {
|
|
828
|
+
v.Subscribe?.("pointerdown", handlePointerDown);
|
|
829
|
+
} catch {
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
v.Subscribe?.("pointerup", handlePointerUp);
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
v.Subscribe?.("viewChanged", updateOverlay);
|
|
837
|
+
} catch {
|
|
838
|
+
}
|
|
839
|
+
canvas.addEventListener("pointermove", handlePointerMove);
|
|
840
|
+
window.addEventListener("keydown", onKeyDown);
|
|
841
|
+
return () => {
|
|
842
|
+
canvas.style.cursor = "";
|
|
843
|
+
try {
|
|
844
|
+
v.Unsubscribe?.("pointerdown", handlePointerDown);
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
try {
|
|
848
|
+
v.Unsubscribe?.("pointerup", handlePointerUp);
|
|
849
|
+
} catch {
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
v.Unsubscribe?.("viewChanged", updateOverlay);
|
|
853
|
+
} catch {
|
|
854
|
+
}
|
|
855
|
+
canvas.removeEventListener("pointermove", handlePointerMove);
|
|
856
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
857
|
+
teardown();
|
|
858
|
+
setMeasureDistance(null);
|
|
859
|
+
};
|
|
860
|
+
}, [measureEnabled, measureMode, loading, error]);
|
|
515
861
|
const toggleLayer = (name) => {
|
|
516
862
|
setLayers((prev) => prev.map((l) => {
|
|
517
863
|
if (l.name !== name) return l;
|
|
@@ -586,6 +932,45 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
|
|
|
586
932
|
}
|
|
587
933
|
),
|
|
588
934
|
/* @__PURE__ */ jsx("button", { onClick: () => setShowHint((s) => !s), className: btn, title: "How to navigate", children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" }) }) }),
|
|
935
|
+
/* @__PURE__ */ jsxs(
|
|
936
|
+
"button",
|
|
937
|
+
{
|
|
938
|
+
onClick: () => setMeasureEnabled((m) => !m),
|
|
939
|
+
className: btn + (measureEnabled ? " bg-gray-200" : ""),
|
|
940
|
+
title: measureEnabled ? "Stop measuring (Esc)" : "Measure distance \u2014 click two points on the drawing",
|
|
941
|
+
children: [
|
|
942
|
+
/* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [
|
|
943
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 14.25l6-6 6 6 4.5-4.5M9.75 8.25v3M12.75 11.25v3M15.75 14.25v3" }),
|
|
944
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.25 18.75h19.5" })
|
|
945
|
+
] }),
|
|
946
|
+
"Measure"
|
|
947
|
+
]
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
measureEnabled && /* @__PURE__ */ jsxs("div", { className: "flex items-stretch h-7 rounded border border-gray-200 overflow-hidden text-[11px] font-semibold", children: [
|
|
951
|
+
/* @__PURE__ */ jsx(
|
|
952
|
+
"button",
|
|
953
|
+
{
|
|
954
|
+
onClick: () => setMeasureMode("point"),
|
|
955
|
+
className: `px-2 transition-colors ${measureMode === "point" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
|
|
956
|
+
title: "Point \u2014 straight-line distance between two picks",
|
|
957
|
+
children: "Point"
|
|
958
|
+
}
|
|
959
|
+
),
|
|
960
|
+
/* @__PURE__ */ jsx(
|
|
961
|
+
"button",
|
|
962
|
+
{
|
|
963
|
+
onClick: () => setMeasureMode("perp"),
|
|
964
|
+
className: `px-2 transition-colors ${measureMode === "perp" ? "bg-orange-500 text-white" : "bg-white text-gray-600 hover:bg-gray-50"}`,
|
|
965
|
+
title: "Perpendicular \u2014 click on a line first, then pick a point. Reports the perpendicular distance.",
|
|
966
|
+
children: "\u22A5"
|
|
967
|
+
}
|
|
968
|
+
)
|
|
969
|
+
] }),
|
|
970
|
+
measureEnabled && measureDistance !== null && /* @__PURE__ */ jsxs("div", { className: "px-2 py-1 text-[11px] font-mono font-semibold text-orange-600 bg-orange-50 border border-orange-200 rounded whitespace-nowrap", title: measureMode === "perp" ? "Perpendicular distance from second pick to first line" : "Straight-line distance between the two picked points", children: [
|
|
971
|
+
formatMeasureDistance(measureDistance),
|
|
972
|
+
measureMode === "perp" ? " \u22A5" : ""
|
|
973
|
+
] }),
|
|
589
974
|
/* @__PURE__ */ jsx("button", { onClick: handleResetView, className: btn, title: "Fit drawing to view", children: "Fit" }),
|
|
590
975
|
/* @__PURE__ */ jsxs("button", { onClick: onDownload ?? handleDefaultDownload, className: btn, children: [
|
|
591
976
|
/* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" }) }),
|
|
@@ -1909,5 +2294,5 @@ function ImagePanel({ url, filename, onDownload, onEmail }) {
|
|
|
1909
2294
|
}
|
|
1910
2295
|
|
|
1911
2296
|
export { Preview, setPdfPreview };
|
|
1912
|
-
//# sourceMappingURL=chunk-
|
|
1913
|
-
//# sourceMappingURL=chunk-
|
|
2297
|
+
//# sourceMappingURL=chunk-G6WVMTJU.js.map
|
|
2298
|
+
//# sourceMappingURL=chunk-G6WVMTJU.js.map
|