react-os-shell 0.1.29 → 0.1.32
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/Preview-X2TQ32VM.js +6 -0
- package/dist/{Preview-KRBYR47K.js.map → Preview-X2TQ32VM.js.map} +1 -1
- package/dist/apps/index.js +2 -2
- package/dist/{chunk-CP5X55CA.js → chunk-CZKAKVP6.js} +572 -49
- package/dist/chunk-CZKAKVP6.js.map +1 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/Preview-KRBYR47K.js +0 -6
- package/dist/chunk-CP5X55CA.js.map +0 -1
|
@@ -566,6 +566,24 @@ function DxfPanel({ url, filename, onDownload, onEmail }) {
|
|
|
566
566
|
] });
|
|
567
567
|
}
|
|
568
568
|
var DEFAULT_O3DV_LIBS = "https://cdn.jsdelivr.net/npm/online-3d-viewer@0.18.0/libs/";
|
|
569
|
+
function buildTree(node, depth = 0) {
|
|
570
|
+
return {
|
|
571
|
+
id: node.GetId?.() ?? 0,
|
|
572
|
+
name: node.GetName?.() || (depth === 0 ? "Root" : "Node"),
|
|
573
|
+
isMeshNode: !!node.IsMeshNode?.(),
|
|
574
|
+
meshIndices: node.GetMeshIndices?.() ?? [],
|
|
575
|
+
children: (node.GetChildNodes?.() ?? []).map((c) => buildTree(c, depth + 1))
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function collectNodeIds(node, out) {
|
|
579
|
+
out.add(node.id);
|
|
580
|
+
for (const c of node.children) collectNodeIds(c, out);
|
|
581
|
+
}
|
|
582
|
+
function hexToRgb(OV, hex) {
|
|
583
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex);
|
|
584
|
+
const n = m ? parseInt(m[1], 16) : 0;
|
|
585
|
+
return new OV.RGBColor(n >> 16 & 255, n >> 8 & 255, n & 255);
|
|
586
|
+
}
|
|
569
587
|
function StepPanel({ url, filename, onDownload, onEmail }) {
|
|
570
588
|
const containerRef = useRef(null);
|
|
571
589
|
const viewerRef = useRef(null);
|
|
@@ -573,11 +591,29 @@ function StepPanel({ url, filename, onDownload, onEmail }) {
|
|
|
573
591
|
const [loading, setLoading] = useState(true);
|
|
574
592
|
const [error, setError] = useState(null);
|
|
575
593
|
const [showHint, setShowHint] = useState(true);
|
|
594
|
+
const [tree, setTree] = useState(null);
|
|
595
|
+
const [expanded, setExpanded] = useState({});
|
|
596
|
+
const [hidden, setHidden] = useState(/* @__PURE__ */ new Set());
|
|
597
|
+
const [bgColor, setBgColor] = useState("#f5f6f8");
|
|
598
|
+
const [showEdges, setShowEdges] = useState(true);
|
|
599
|
+
const [edgeColor, setEdgeColor] = useState("#000000");
|
|
600
|
+
const [edgeThreshold, setEdgeThreshold] = useState(1);
|
|
601
|
+
const [showMeshes, setShowMeshes] = useState(true);
|
|
602
|
+
const [showSettings, setShowSettings] = useState(true);
|
|
603
|
+
const [sectionEnabled, setSectionEnabled] = useState(false);
|
|
604
|
+
const [sectionAxis, setSectionAxis] = useState("z");
|
|
605
|
+
const [sectionFlip, setSectionFlip] = useState(false);
|
|
606
|
+
const [sectionPosition, setSectionPosition] = useState(0.5);
|
|
607
|
+
const [sectionCapColor, setSectionCapColor] = useState("#9aa6b3");
|
|
608
|
+
const sectionRef = useRef(null);
|
|
576
609
|
useEffect(() => {
|
|
577
610
|
let cancelled = false;
|
|
578
611
|
let viewer = null;
|
|
579
612
|
setLoading(true);
|
|
580
613
|
setError(null);
|
|
614
|
+
setTree(null);
|
|
615
|
+
setExpanded({});
|
|
616
|
+
setHidden(/* @__PURE__ */ new Set());
|
|
581
617
|
(async () => {
|
|
582
618
|
let OV;
|
|
583
619
|
try {
|
|
@@ -606,17 +642,34 @@ function StepPanel({ url, filename, onDownload, onEmail }) {
|
|
|
606
642
|
viewer = new OV.EmbeddedViewer(containerRef.current, {
|
|
607
643
|
backgroundColor: new OV.RGBAColor(245, 246, 248, 255),
|
|
608
644
|
defaultColor: new OV.RGBColor(180, 188, 200),
|
|
609
|
-
edgeSettings: new OV.EdgeSettings(
|
|
645
|
+
edgeSettings: new OV.EdgeSettings(true, new OV.RGBColor(0, 0, 0), 1),
|
|
610
646
|
onModelLoaded: () => {
|
|
611
|
-
if (
|
|
647
|
+
if (cancelled) return;
|
|
648
|
+
try {
|
|
649
|
+
const model = viewer.GetModel?.();
|
|
650
|
+
const root = model?.GetRootNode?.();
|
|
651
|
+
if (root) {
|
|
652
|
+
const t = buildTree(root);
|
|
653
|
+
setTree(t);
|
|
654
|
+
const expandIds = { [t.id]: true };
|
|
655
|
+
for (const c of t.children) expandIds[c.id] = true;
|
|
656
|
+
setExpanded(expandIds);
|
|
657
|
+
}
|
|
658
|
+
} catch (err) {
|
|
659
|
+
console.warn("[Preview] mesh tree extraction failed", err);
|
|
660
|
+
}
|
|
661
|
+
setLoading(false);
|
|
662
|
+
},
|
|
663
|
+
onModelLoadFailed: () => {
|
|
664
|
+
if (!cancelled) {
|
|
665
|
+
setError("Failed to load 3D model.");
|
|
666
|
+
setLoading(false);
|
|
667
|
+
}
|
|
612
668
|
}
|
|
613
669
|
});
|
|
614
670
|
viewerRef.current = viewer;
|
|
615
671
|
const inputFile = new OV.InputFile(filename, OV.FileSource.Url, url);
|
|
616
672
|
viewer.LoadModelFromInputFiles([inputFile]);
|
|
617
|
-
setTimeout(() => {
|
|
618
|
-
if (!cancelled) setLoading(false);
|
|
619
|
-
}, 4e3);
|
|
620
673
|
} catch (e) {
|
|
621
674
|
if (!cancelled) {
|
|
622
675
|
setError(e?.message || "Failed to load 3D model.");
|
|
@@ -633,64 +686,534 @@ function StepPanel({ url, filename, onDownload, onEmail }) {
|
|
|
633
686
|
viewerRef.current = null;
|
|
634
687
|
};
|
|
635
688
|
}, [url, filename]);
|
|
689
|
+
useEffect(() => {
|
|
690
|
+
const v = viewerRef.current;
|
|
691
|
+
if (!v?.viewer) return;
|
|
692
|
+
try {
|
|
693
|
+
const visit = (mesh) => {
|
|
694
|
+
if (mesh.userData?.__sectionHelper) return;
|
|
695
|
+
const ud = mesh.userData?.originalMeshInstance ?? mesh.userData;
|
|
696
|
+
const nodeId = ud?.id?.nodeId ?? ud?.nodeId;
|
|
697
|
+
if (typeof nodeId === "number") {
|
|
698
|
+
mesh.visible = !hidden.has(nodeId);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
v.viewer.mainModel?.EnumerateMeshesAndLines?.(visit);
|
|
702
|
+
v.viewer.mainModel?.EnumerateEdges?.(visit);
|
|
703
|
+
v.viewer.Render?.();
|
|
704
|
+
} catch (err) {
|
|
705
|
+
console.warn("[Preview] visibility update failed", err);
|
|
706
|
+
}
|
|
707
|
+
}, [hidden, tree]);
|
|
708
|
+
useEffect(() => {
|
|
709
|
+
const OV = ovRef.current;
|
|
710
|
+
const v = viewerRef.current;
|
|
711
|
+
if (!OV || !v?.viewer) return;
|
|
712
|
+
try {
|
|
713
|
+
v.viewer.SetEdgeSettings(new OV.EdgeSettings(showEdges, hexToRgb(OV, edgeColor), edgeThreshold));
|
|
714
|
+
v.viewer.Render?.();
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
}, [showEdges, edgeColor, edgeThreshold]);
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
const OV = ovRef.current;
|
|
720
|
+
const v = viewerRef.current;
|
|
721
|
+
if (!OV || !v?.viewer) return;
|
|
722
|
+
try {
|
|
723
|
+
const c = hexToRgb(OV, bgColor);
|
|
724
|
+
v.viewer.SetBackgroundColor(new OV.RGBAColor(c.r, c.g, c.b, 255));
|
|
725
|
+
v.viewer.Render?.();
|
|
726
|
+
} catch {
|
|
727
|
+
}
|
|
728
|
+
}, [bgColor]);
|
|
729
|
+
useEffect(() => {
|
|
730
|
+
const v = viewerRef.current;
|
|
731
|
+
if (!v?.viewer || loading) return;
|
|
732
|
+
let cancelled = false;
|
|
733
|
+
let teardown = null;
|
|
734
|
+
(async () => {
|
|
735
|
+
const THREE = await import('three');
|
|
736
|
+
if (cancelled) return;
|
|
737
|
+
const renderer = v.viewer.renderer;
|
|
738
|
+
const scene = v.viewer.scene;
|
|
739
|
+
if (!renderer || !scene) return;
|
|
740
|
+
if (sectionRef.current) {
|
|
741
|
+
const s = sectionRef.current;
|
|
742
|
+
for (const [mat, prev] of s.materialState.entries()) {
|
|
743
|
+
mat.clippingPlanes = prev.clippingPlanes;
|
|
744
|
+
mat.clipShadows = prev.clipShadows;
|
|
745
|
+
mat.needsUpdate = true;
|
|
746
|
+
}
|
|
747
|
+
for (const helper of s.helpers) {
|
|
748
|
+
helper.parent?.remove(helper);
|
|
749
|
+
helper.geometry?.dispose?.();
|
|
750
|
+
helper.material?.dispose?.();
|
|
751
|
+
}
|
|
752
|
+
if (s.capMesh) {
|
|
753
|
+
scene.remove(s.capMesh);
|
|
754
|
+
s.capMesh.geometry?.dispose?.();
|
|
755
|
+
s.capMesh.material?.dispose?.();
|
|
756
|
+
}
|
|
757
|
+
sectionRef.current = null;
|
|
758
|
+
}
|
|
759
|
+
if (!sectionEnabled) {
|
|
760
|
+
renderer.localClippingEnabled = false;
|
|
761
|
+
v.viewer.Render?.();
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const bbox = v.viewer.GetBoundingBox?.(() => true);
|
|
765
|
+
if (!bbox) return;
|
|
766
|
+
const plane = new THREE.Plane(new THREE.Vector3(0, 0, -1), 0);
|
|
767
|
+
const helpers = [];
|
|
768
|
+
const materialState = /* @__PURE__ */ new Map();
|
|
769
|
+
const applyToMaterial = (mat) => {
|
|
770
|
+
if (!mat || materialState.has(mat)) return;
|
|
771
|
+
materialState.set(mat, {
|
|
772
|
+
clippingPlanes: mat.clippingPlanes,
|
|
773
|
+
clipShadows: mat.clipShadows
|
|
774
|
+
});
|
|
775
|
+
mat.clippingPlanes = [plane];
|
|
776
|
+
mat.clipShadows = true;
|
|
777
|
+
mat.needsUpdate = true;
|
|
778
|
+
};
|
|
779
|
+
const targets = [];
|
|
780
|
+
v.viewer.mainModel?.EnumerateMeshes?.((mesh) => {
|
|
781
|
+
if (mesh.userData?.__sectionHelper) return;
|
|
782
|
+
targets.push(mesh);
|
|
783
|
+
});
|
|
784
|
+
for (const mesh of targets) {
|
|
785
|
+
const mat = mesh.material;
|
|
786
|
+
if (Array.isArray(mat)) for (const m of mat) applyToMaterial(m);
|
|
787
|
+
else applyToMaterial(mat);
|
|
788
|
+
const makeStencil = (side, op) => {
|
|
789
|
+
const m = new THREE.MeshBasicMaterial({
|
|
790
|
+
depthWrite: false,
|
|
791
|
+
depthTest: false,
|
|
792
|
+
colorWrite: false,
|
|
793
|
+
stencilWrite: true,
|
|
794
|
+
stencilFunc: THREE.AlwaysStencilFunc,
|
|
795
|
+
stencilFail: op,
|
|
796
|
+
stencilZFail: op,
|
|
797
|
+
stencilZPass: op,
|
|
798
|
+
side,
|
|
799
|
+
clippingPlanes: [plane]
|
|
800
|
+
});
|
|
801
|
+
const helper = new THREE.Mesh(mesh.geometry, m);
|
|
802
|
+
helper.matrixAutoUpdate = false;
|
|
803
|
+
helper.renderOrder = 1;
|
|
804
|
+
helper.userData.__sectionHelper = true;
|
|
805
|
+
mesh.add(helper);
|
|
806
|
+
helpers.push(helper);
|
|
807
|
+
};
|
|
808
|
+
makeStencil(THREE.BackSide, THREE.IncrementWrapStencilOp);
|
|
809
|
+
makeStencil(THREE.FrontSide, THREE.DecrementWrapStencilOp);
|
|
810
|
+
}
|
|
811
|
+
const dx = bbox.max.x - bbox.min.x;
|
|
812
|
+
const dy = bbox.max.y - bbox.min.y;
|
|
813
|
+
const dz = bbox.max.z - bbox.min.z;
|
|
814
|
+
const capSize = Math.max(dx, dy, dz) * 2 || 1;
|
|
815
|
+
const capGeom = new THREE.PlaneGeometry(capSize, capSize);
|
|
816
|
+
const capMat = new THREE.MeshPhongMaterial({
|
|
817
|
+
color: 10135219,
|
|
818
|
+
side: THREE.DoubleSide,
|
|
819
|
+
stencilWrite: true,
|
|
820
|
+
stencilRef: 0,
|
|
821
|
+
stencilFunc: THREE.NotEqualStencilFunc,
|
|
822
|
+
stencilFail: THREE.ReplaceStencilOp,
|
|
823
|
+
stencilZFail: THREE.ReplaceStencilOp,
|
|
824
|
+
stencilZPass: THREE.ReplaceStencilOp
|
|
825
|
+
});
|
|
826
|
+
const capMesh = new THREE.Mesh(capGeom, capMat);
|
|
827
|
+
capMesh.renderOrder = 2;
|
|
828
|
+
capMesh.userData.__sectionHelper = true;
|
|
829
|
+
scene.add(capMesh);
|
|
830
|
+
renderer.localClippingEnabled = true;
|
|
831
|
+
sectionRef.current = { plane, capMesh, helpers, materialState, bbox };
|
|
832
|
+
v.viewer.Render?.();
|
|
833
|
+
teardown = () => {
|
|
834
|
+
};
|
|
835
|
+
})();
|
|
836
|
+
return () => {
|
|
837
|
+
cancelled = true;
|
|
838
|
+
if (teardown) teardown();
|
|
839
|
+
};
|
|
840
|
+
}, [sectionEnabled, loading, tree]);
|
|
841
|
+
useEffect(() => {
|
|
842
|
+
const v = viewerRef.current;
|
|
843
|
+
const s = sectionRef.current;
|
|
844
|
+
if (!v?.viewer || !s || !sectionEnabled) return;
|
|
845
|
+
try {
|
|
846
|
+
const bbox = s.bbox;
|
|
847
|
+
const axisIdx = sectionAxis === "x" ? 0 : sectionAxis === "y" ? 1 : 2;
|
|
848
|
+
const min = [bbox.min.x, bbox.min.y, bbox.min.z][axisIdx];
|
|
849
|
+
const max = [bbox.max.x, bbox.max.y, bbox.max.z][axisIdx];
|
|
850
|
+
const value = min + (max - min) * sectionPosition;
|
|
851
|
+
const dir = sectionFlip ? 1 : -1;
|
|
852
|
+
const nx = sectionAxis === "x" ? dir : 0;
|
|
853
|
+
const ny = sectionAxis === "y" ? dir : 0;
|
|
854
|
+
const nz = sectionAxis === "z" ? dir : 0;
|
|
855
|
+
s.plane.normal.set(nx, ny, nz);
|
|
856
|
+
s.plane.constant = -dir * value;
|
|
857
|
+
const cx = (bbox.min.x + bbox.max.x) / 2;
|
|
858
|
+
const cy = (bbox.min.y + bbox.max.y) / 2;
|
|
859
|
+
const cz = (bbox.min.z + bbox.max.z) / 2;
|
|
860
|
+
const center = { x: cx, y: cy, z: cz };
|
|
861
|
+
const dist = nx * center.x + ny * center.y + nz * center.z + s.plane.constant;
|
|
862
|
+
const px = center.x - nx * dist;
|
|
863
|
+
const py = center.y - ny * dist;
|
|
864
|
+
const pz = center.z - nz * dist;
|
|
865
|
+
s.capMesh.position.set(px, py, pz);
|
|
866
|
+
s.capMesh.lookAt(px + nx, py + ny, pz + nz);
|
|
867
|
+
try {
|
|
868
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(sectionCapColor);
|
|
869
|
+
const n = m ? parseInt(m[1], 16) : 10135219;
|
|
870
|
+
s.capMesh.material.color.setHex(n);
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
v.viewer.Render?.();
|
|
874
|
+
} catch (err) {
|
|
875
|
+
console.warn("[Preview] section update failed", err);
|
|
876
|
+
}
|
|
877
|
+
}, [sectionEnabled, sectionAxis, sectionFlip, sectionPosition, sectionCapColor]);
|
|
636
878
|
useEffect(() => {
|
|
637
879
|
if (!showHint || loading) return;
|
|
638
880
|
const t = setTimeout(() => setShowHint(false), 5e3);
|
|
639
881
|
return () => clearTimeout(t);
|
|
640
882
|
}, [showHint, loading]);
|
|
641
|
-
const
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
883
|
+
const toggleNodeVisible = (node) => {
|
|
884
|
+
const ids = /* @__PURE__ */ new Set();
|
|
885
|
+
collectNodeIds(node, ids);
|
|
886
|
+
setHidden((prev) => {
|
|
887
|
+
const next = new Set(prev);
|
|
888
|
+
const anyVisible = [...ids].some((id) => !next.has(id));
|
|
889
|
+
for (const id of ids) {
|
|
890
|
+
if (anyVisible) next.add(id);
|
|
891
|
+
else next.delete(id);
|
|
892
|
+
}
|
|
893
|
+
return next;
|
|
894
|
+
});
|
|
646
895
|
};
|
|
647
|
-
const
|
|
896
|
+
const toggleExpanded = (id) => {
|
|
897
|
+
setExpanded((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
898
|
+
};
|
|
899
|
+
const fitNode = (node) => {
|
|
900
|
+
handleFit();
|
|
901
|
+
};
|
|
902
|
+
const handleFit = () => {
|
|
648
903
|
try {
|
|
649
904
|
const v = viewerRef.current;
|
|
650
|
-
v?.
|
|
651
|
-
v
|
|
905
|
+
const sphere = v?.GetBoundingSphere?.(() => true);
|
|
906
|
+
if (sphere) v.viewer.FitSphereToWindow(sphere, true);
|
|
907
|
+
v?.viewer?.Render?.();
|
|
908
|
+
} catch {
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
const setCameraPreset = (preset) => {
|
|
912
|
+
const OV = ovRef.current;
|
|
913
|
+
const v = viewerRef.current;
|
|
914
|
+
if (!OV || !v?.viewer) return;
|
|
915
|
+
try {
|
|
916
|
+
const sphere = v.GetBoundingSphere?.(() => true);
|
|
917
|
+
if (!sphere) return;
|
|
918
|
+
const c = sphere.center;
|
|
919
|
+
const r = sphere.radius || 1;
|
|
920
|
+
const dist = r * 3;
|
|
921
|
+
const cx = c.x ?? 0, cy = c.y ?? 0, cz = c.z ?? 0;
|
|
922
|
+
let eye, up;
|
|
923
|
+
switch (preset) {
|
|
924
|
+
case "top":
|
|
925
|
+
eye = new OV.Coord3D(cx, cy, cz + dist);
|
|
926
|
+
up = new OV.Coord3D(0, 1, 0);
|
|
927
|
+
break;
|
|
928
|
+
case "front":
|
|
929
|
+
eye = new OV.Coord3D(cx, cy - dist, cz);
|
|
930
|
+
up = new OV.Coord3D(0, 0, 1);
|
|
931
|
+
break;
|
|
932
|
+
case "side":
|
|
933
|
+
eye = new OV.Coord3D(cx + dist, cy, cz);
|
|
934
|
+
up = new OV.Coord3D(0, 0, 1);
|
|
935
|
+
break;
|
|
936
|
+
case "iso":
|
|
937
|
+
default: {
|
|
938
|
+
const k = dist / Math.sqrt(3);
|
|
939
|
+
eye = new OV.Coord3D(cx + k, cy - k, cz + k);
|
|
940
|
+
up = new OV.Coord3D(0, 0, 1);
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const center = new OV.Coord3D(cx, cy, cz);
|
|
945
|
+
const cam = new OV.Camera(eye, center, up, 45);
|
|
946
|
+
v.viewer.SetCamera(cam);
|
|
947
|
+
v.viewer.AdjustClippingPlanesToSphere?.(sphere);
|
|
948
|
+
v.viewer.Render?.();
|
|
949
|
+
} catch {
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
const handleSnapshot = () => {
|
|
953
|
+
try {
|
|
954
|
+
const v = viewerRef.current;
|
|
955
|
+
const size = v?.viewer?.GetCanvasSize?.() ?? { width: 1280, height: 720 };
|
|
956
|
+
const dataUrl = v?.viewer?.GetImageAsDataUrl?.(size.width, size.height, false);
|
|
957
|
+
if (!dataUrl) return;
|
|
958
|
+
const a = document.createElement("a");
|
|
959
|
+
a.href = dataUrl;
|
|
960
|
+
a.download = `${filename.replace(/\.[^.]+$/, "")}-snapshot.png`;
|
|
961
|
+
a.click();
|
|
652
962
|
} catch {
|
|
653
963
|
}
|
|
654
964
|
};
|
|
965
|
+
const handleResetDisplay = () => {
|
|
966
|
+
setBgColor("#f5f6f8");
|
|
967
|
+
setShowEdges(true);
|
|
968
|
+
setEdgeColor("#000000");
|
|
969
|
+
setEdgeThreshold(1);
|
|
970
|
+
setSectionEnabled(false);
|
|
971
|
+
setSectionAxis("z");
|
|
972
|
+
setSectionFlip(false);
|
|
973
|
+
setSectionPosition(0.5);
|
|
974
|
+
setSectionCapColor("#9aa6b3");
|
|
975
|
+
};
|
|
976
|
+
const handleDefaultDownload = () => {
|
|
977
|
+
const a = document.createElement("a");
|
|
978
|
+
a.href = url;
|
|
979
|
+
a.download = filename;
|
|
980
|
+
a.click();
|
|
981
|
+
};
|
|
655
982
|
const ext = (filename.split(".").pop() || "").toUpperCase();
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
983
|
+
const renderTreeNode = (node, depth = 0) => {
|
|
984
|
+
const hasChildren = node.children.length > 0;
|
|
985
|
+
const isExpanded = expanded[node.id] !== false;
|
|
986
|
+
const isVisible = !hidden.has(node.id);
|
|
987
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
988
|
+
/* @__PURE__ */ jsxs(
|
|
989
|
+
"div",
|
|
990
|
+
{
|
|
991
|
+
className: "group flex items-center gap-1 px-1.5 py-1 hover:bg-slate-700/50 cursor-default text-[12px] text-slate-200",
|
|
992
|
+
style: { paddingLeft: `${depth * 12 + 6}px` },
|
|
993
|
+
children: [
|
|
994
|
+
hasChildren ? /* @__PURE__ */ jsx(
|
|
995
|
+
"button",
|
|
996
|
+
{
|
|
997
|
+
onClick: () => toggleExpanded(node.id),
|
|
998
|
+
className: "h-4 w-4 shrink-0 flex items-center justify-center text-slate-400 hover:text-slate-100",
|
|
999
|
+
title: isExpanded ? "Collapse" : "Expand",
|
|
1000
|
+
children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: isExpanded ? "M19.5 8.25l-7.5 7.5-7.5-7.5" : "M8.25 4.5l7.5 7.5-7.5 7.5" }) })
|
|
1001
|
+
}
|
|
1002
|
+
) : /* @__PURE__ */ jsx("span", { className: "h-4 w-4 shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "h-1 w-1 rounded-full bg-slate-500" }) }),
|
|
1003
|
+
/* @__PURE__ */ jsx("span", { className: `flex-1 truncate ${isVisible ? "" : "opacity-40"}`, title: node.name, children: node.name }),
|
|
1004
|
+
/* @__PURE__ */ jsx(
|
|
1005
|
+
"button",
|
|
1006
|
+
{
|
|
1007
|
+
onClick: () => fitNode(),
|
|
1008
|
+
className: "h-4 w-4 shrink-0 text-slate-500 hover:text-slate-100 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1009
|
+
title: "Fit to view",
|
|
1010
|
+
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: "M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" }) })
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
1013
|
+
/* @__PURE__ */ jsx(
|
|
1014
|
+
"button",
|
|
1015
|
+
{
|
|
1016
|
+
onClick: () => toggleNodeVisible(node),
|
|
1017
|
+
className: "h-4 w-4 shrink-0 text-slate-400 hover:text-slate-100",
|
|
1018
|
+
title: isVisible ? "Hide" : "Show",
|
|
1019
|
+
children: isVisible ? /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [
|
|
1020
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" }),
|
|
1021
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" })
|
|
1022
|
+
] }) : /* @__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.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88" }) })
|
|
1023
|
+
}
|
|
1024
|
+
)
|
|
1025
|
+
]
|
|
1026
|
+
}
|
|
1027
|
+
),
|
|
1028
|
+
isExpanded && hasChildren && /* @__PURE__ */ jsx("div", { children: node.children.map((c) => renderTreeNode(c, depth + 1)) })
|
|
1029
|
+
] }, node.id);
|
|
1030
|
+
};
|
|
1031
|
+
const tBtn = "h-8 w-8 shrink-0 flex items-center justify-center rounded text-slate-300 hover:bg-slate-700 hover:text-white transition-colors";
|
|
1032
|
+
const tBtnActive = "h-8 w-8 shrink-0 flex items-center justify-center rounded bg-slate-700 text-white";
|
|
1033
|
+
const tBtnSep = "h-5 w-px bg-slate-700 mx-1";
|
|
1034
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full bg-slate-900", children: [
|
|
1035
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5 bg-slate-800 border-b border-slate-700 shrink-0", children: [
|
|
1036
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold tracking-wide text-slate-300 px-2 truncate max-w-xs", title: filename, children: filename }),
|
|
1037
|
+
/* @__PURE__ */ jsx("div", { className: tBtnSep }),
|
|
1038
|
+
/* @__PURE__ */ jsx("button", { onClick: handleFit, className: tBtn, title: "Fit to view", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" }) }) }),
|
|
1039
|
+
/* @__PURE__ */ jsx("div", { className: tBtnSep }),
|
|
1040
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setCameraPreset("iso"), className: tBtn, title: "Isometric view", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold", children: "ISO" }) }),
|
|
1041
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setCameraPreset("top"), className: tBtn, title: "Top view", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold", children: "TOP" }) }),
|
|
1042
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setCameraPreset("front"), className: tBtn, title: "Front view", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold", children: "FRT" }) }),
|
|
1043
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setCameraPreset("side"), className: tBtn, title: "Side view", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold", children: "SDE" }) }),
|
|
1044
|
+
/* @__PURE__ */ jsx("div", { className: tBtnSep }),
|
|
1045
|
+
/* @__PURE__ */ jsx("button", { onClick: handleSnapshot, className: tBtn, title: "Save snapshot as PNG", children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [
|
|
1046
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z" }),
|
|
1047
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z" })
|
|
1048
|
+
] }) }),
|
|
1049
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setShowHint((s) => !s), className: tBtn, title: "How to navigate", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", 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" }) }) }),
|
|
1050
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
1051
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setShowMeshes((s) => !s), className: showMeshes ? tBtnActive : tBtn, title: "Toggle meshes panel", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" }) }) }),
|
|
1052
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setShowSettings((s) => !s), className: showSettings ? tBtnActive : tBtn, title: "Toggle display panel", children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: [
|
|
1053
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" }),
|
|
1054
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" })
|
|
1055
|
+
] }) }),
|
|
1056
|
+
/* @__PURE__ */ jsx("button", { onClick: onDownload ?? handleDefaultDownload, className: tBtn, title: "Download original file", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", 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" }) }) }),
|
|
1057
|
+
onEmail && /* @__PURE__ */ jsx("button", { onClick: onEmail, className: tBtn, title: "Email", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" }) }) })
|
|
675
1058
|
] }),
|
|
676
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
|
|
679
|
-
/* @__PURE__ */ jsx("
|
|
680
|
-
/* @__PURE__ */ jsx("
|
|
681
|
-
/* @__PURE__ */ jsx("span", { children: "Right-click drag to pan" }),
|
|
682
|
-
/* @__PURE__ */ jsx("span", { className: "text-white/40", children: "\u2022" }),
|
|
683
|
-
/* @__PURE__ */ jsx("span", { children: "Scroll to zoom" })
|
|
1059
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex min-h-0", children: [
|
|
1060
|
+
showMeshes && /* @__PURE__ */ jsxs("div", { className: "w-60 shrink-0 bg-slate-800 border-r border-slate-700 flex flex-col", children: [
|
|
1061
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-slate-400 border-b border-slate-700", children: "Meshes" }),
|
|
1062
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto py-1", children: tree ? renderTreeNode(tree) : /* @__PURE__ */ jsx("div", { className: "px-3 py-3 text-[11px] text-slate-500 italic", children: loading ? "Reading model\u2026" : "No structure available" }) }),
|
|
1063
|
+
tree && /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 text-[10px] text-slate-500 border-t border-slate-700", children: hidden.size === 0 ? "All visible" : `${hidden.size} hidden` })
|
|
684
1064
|
] }),
|
|
685
|
-
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
|
|
688
|
-
/* @__PURE__ */ jsx("
|
|
1065
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-w-0", style: { background: bgColor }, children: [
|
|
1066
|
+
/* @__PURE__ */ jsx("div", { ref: containerRef, style: { width: "100%", height: "100%" } }),
|
|
1067
|
+
showHint && !loading && !error && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2 bg-gray-900/85 text-white text-[11px] px-3 py-1.5 rounded-full shadow-lg flex items-center gap-3 z-10 pointer-events-none", children: [
|
|
1068
|
+
/* @__PURE__ */ jsx("span", { children: "Drag to rotate" }),
|
|
1069
|
+
/* @__PURE__ */ jsx("span", { className: "text-white/40", children: "\u2022" }),
|
|
1070
|
+
/* @__PURE__ */ jsx("span", { children: "Right-click drag to pan" }),
|
|
1071
|
+
/* @__PURE__ */ jsx("span", { className: "text-white/40", children: "\u2022" }),
|
|
1072
|
+
/* @__PURE__ */ jsx("span", { children: "Scroll to zoom" })
|
|
1073
|
+
] }),
|
|
1074
|
+
loading && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-white/85 text-sm text-gray-600 gap-2", children: [
|
|
1075
|
+
/* @__PURE__ */ jsxs("svg", { className: "h-6 w-6 text-blue-500 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
|
|
1076
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.2" }),
|
|
1077
|
+
/* @__PURE__ */ jsx("path", { d: "M22 12a10 10 0 0 1-10 10", strokeLinecap: "round" })
|
|
1078
|
+
] }),
|
|
1079
|
+
/* @__PURE__ */ jsx("span", { children: "Loading 3D model\u2026" }),
|
|
1080
|
+
ext === "STP" || ext === "STEP" ? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400", children: "STEP files load OpenCascade WASM (~5 MB) on first use." }) : null
|
|
689
1081
|
] }),
|
|
690
|
-
/* @__PURE__ */ jsx("
|
|
691
|
-
ext === "STP" || ext === "STEP" ? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400", children: "STEP files load OpenCascade WASM (~5 MB) on first use." }) : null
|
|
1082
|
+
error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-red-600 px-6 text-center bg-white/85", children: error })
|
|
692
1083
|
] }),
|
|
693
|
-
|
|
1084
|
+
showSettings && /* @__PURE__ */ jsxs("div", { className: "w-60 shrink-0 bg-slate-800 border-l border-slate-700 flex flex-col", children: [
|
|
1085
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-slate-400 border-b border-slate-700", children: "Model Display" }),
|
|
1086
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-3 py-3 space-y-3 text-[12px] text-slate-200", children: [
|
|
1087
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1088
|
+
/* @__PURE__ */ jsx("span", { children: "Background Color" }),
|
|
1089
|
+
/* @__PURE__ */ jsx("input", { type: "color", value: bgColor, onChange: (e) => setBgColor(e.target.value), className: "h-6 w-10 rounded border border-slate-600 bg-transparent" })
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1092
|
+
/* @__PURE__ */ jsx("span", { children: "Show Edges" }),
|
|
1093
|
+
/* @__PURE__ */ jsx(
|
|
1094
|
+
"button",
|
|
1095
|
+
{
|
|
1096
|
+
onClick: () => setShowEdges((s) => !s),
|
|
1097
|
+
className: `relative h-5 w-9 rounded-full transition-colors ${showEdges ? "bg-blue-500" : "bg-slate-600"}`,
|
|
1098
|
+
children: /* @__PURE__ */ jsx("span", { className: `absolute top-0.5 h-4 w-4 rounded-full bg-white transition-transform ${showEdges ? "translate-x-4" : "translate-x-0.5"}` })
|
|
1099
|
+
}
|
|
1100
|
+
)
|
|
1101
|
+
] }),
|
|
1102
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1103
|
+
/* @__PURE__ */ jsx("span", { className: showEdges ? "" : "opacity-40", children: "Edge Color" }),
|
|
1104
|
+
/* @__PURE__ */ jsx(
|
|
1105
|
+
"input",
|
|
1106
|
+
{
|
|
1107
|
+
type: "color",
|
|
1108
|
+
value: edgeColor,
|
|
1109
|
+
onChange: (e) => setEdgeColor(e.target.value),
|
|
1110
|
+
disabled: !showEdges,
|
|
1111
|
+
className: "h-6 w-10 rounded border border-slate-600 bg-transparent disabled:opacity-40"
|
|
1112
|
+
}
|
|
1113
|
+
)
|
|
1114
|
+
] }),
|
|
1115
|
+
/* @__PURE__ */ jsxs("div", { className: showEdges ? "" : "opacity-40 pointer-events-none", children: [
|
|
1116
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 mb-1", children: [
|
|
1117
|
+
/* @__PURE__ */ jsx("span", { children: "Edge Threshold" }),
|
|
1118
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-400 tabular-nums", children: [
|
|
1119
|
+
edgeThreshold,
|
|
1120
|
+
"\xB0"
|
|
1121
|
+
] })
|
|
1122
|
+
] }),
|
|
1123
|
+
/* @__PURE__ */ jsx(
|
|
1124
|
+
"input",
|
|
1125
|
+
{
|
|
1126
|
+
type: "range",
|
|
1127
|
+
min: 0,
|
|
1128
|
+
max: 45,
|
|
1129
|
+
step: 1,
|
|
1130
|
+
value: edgeThreshold,
|
|
1131
|
+
onChange: (e) => setEdgeThreshold(Number(e.target.value)),
|
|
1132
|
+
className: "w-full accent-blue-500"
|
|
1133
|
+
}
|
|
1134
|
+
)
|
|
1135
|
+
] }),
|
|
1136
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-slate-700 -mx-3 px-3 pt-3 mt-1", children: [
|
|
1137
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1138
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Section View" }),
|
|
1139
|
+
/* @__PURE__ */ jsx(
|
|
1140
|
+
"button",
|
|
1141
|
+
{
|
|
1142
|
+
onClick: () => setSectionEnabled((s) => !s),
|
|
1143
|
+
className: `relative h-5 w-9 rounded-full transition-colors ${sectionEnabled ? "bg-blue-500" : "bg-slate-600"}`,
|
|
1144
|
+
children: /* @__PURE__ */ jsx("span", { className: `absolute top-0.5 h-4 w-4 rounded-full bg-white transition-transform ${sectionEnabled ? "translate-x-4" : "translate-x-0.5"}` })
|
|
1145
|
+
}
|
|
1146
|
+
)
|
|
1147
|
+
] }),
|
|
1148
|
+
/* @__PURE__ */ jsxs("div", { className: sectionEnabled ? "mt-2 space-y-2" : "mt-2 space-y-2 opacity-40 pointer-events-none", children: [
|
|
1149
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1150
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 mb-1", children: [
|
|
1151
|
+
/* @__PURE__ */ jsx("span", { children: "Axis" }),
|
|
1152
|
+
/* @__PURE__ */ jsx(
|
|
1153
|
+
"button",
|
|
1154
|
+
{
|
|
1155
|
+
onClick: () => setSectionFlip((f) => !f),
|
|
1156
|
+
className: "text-[10px] text-slate-300 hover:text-white px-1.5 py-0.5 rounded bg-slate-700 hover:bg-slate-600",
|
|
1157
|
+
title: "Flip section direction",
|
|
1158
|
+
children: sectionFlip ? "\u2190 flipped" : "flip \u2192"
|
|
1159
|
+
}
|
|
1160
|
+
)
|
|
1161
|
+
] }),
|
|
1162
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-1", children: ["x", "y", "z"].map((ax) => /* @__PURE__ */ jsx(
|
|
1163
|
+
"button",
|
|
1164
|
+
{
|
|
1165
|
+
onClick: () => setSectionAxis(ax),
|
|
1166
|
+
className: `py-1 rounded text-[11px] font-semibold ${sectionAxis === ax ? "bg-blue-500 text-white" : "bg-slate-700 text-slate-300 hover:bg-slate-600"}`,
|
|
1167
|
+
children: ax.toUpperCase()
|
|
1168
|
+
},
|
|
1169
|
+
ax
|
|
1170
|
+
)) })
|
|
1171
|
+
] }),
|
|
1172
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1173
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 mb-1", children: [
|
|
1174
|
+
/* @__PURE__ */ jsx("span", { children: "Position" }),
|
|
1175
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-400 tabular-nums", children: [
|
|
1176
|
+
Math.round(sectionPosition * 100),
|
|
1177
|
+
"%"
|
|
1178
|
+
] })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsx(
|
|
1181
|
+
"input",
|
|
1182
|
+
{
|
|
1183
|
+
type: "range",
|
|
1184
|
+
min: 0,
|
|
1185
|
+
max: 1,
|
|
1186
|
+
step: 0.01,
|
|
1187
|
+
value: sectionPosition,
|
|
1188
|
+
onChange: (e) => setSectionPosition(Number(e.target.value)),
|
|
1189
|
+
className: "w-full accent-blue-500"
|
|
1190
|
+
}
|
|
1191
|
+
)
|
|
1192
|
+
] }),
|
|
1193
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2", children: [
|
|
1194
|
+
/* @__PURE__ */ jsx("span", { children: "Cap Color" }),
|
|
1195
|
+
/* @__PURE__ */ jsx(
|
|
1196
|
+
"input",
|
|
1197
|
+
{
|
|
1198
|
+
type: "color",
|
|
1199
|
+
value: sectionCapColor,
|
|
1200
|
+
onChange: (e) => setSectionCapColor(e.target.value),
|
|
1201
|
+
className: "h-6 w-10 rounded border border-slate-600 bg-transparent"
|
|
1202
|
+
}
|
|
1203
|
+
)
|
|
1204
|
+
] })
|
|
1205
|
+
] })
|
|
1206
|
+
] })
|
|
1207
|
+
] }),
|
|
1208
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-t border-slate-700", children: /* @__PURE__ */ jsx(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
onClick: handleResetDisplay,
|
|
1212
|
+
className: "w-full text-[11px] text-slate-300 bg-slate-700 hover:bg-slate-600 rounded py-1.5 transition-colors",
|
|
1213
|
+
children: "Reset to Default"
|
|
1214
|
+
}
|
|
1215
|
+
) })
|
|
1216
|
+
] })
|
|
694
1217
|
] })
|
|
695
1218
|
] });
|
|
696
1219
|
}
|
|
@@ -744,5 +1267,5 @@ function ImagePanel({ url, filename, onDownload, onEmail }) {
|
|
|
744
1267
|
}
|
|
745
1268
|
|
|
746
1269
|
export { Preview, setPdfPreview };
|
|
747
|
-
//# sourceMappingURL=chunk-
|
|
748
|
-
//# sourceMappingURL=chunk-
|
|
1270
|
+
//# sourceMappingURL=chunk-CZKAKVP6.js.map
|
|
1271
|
+
//# sourceMappingURL=chunk-CZKAKVP6.js.map
|