three-slicer 0.1.2 → 0.1.4

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,1348 +1,1714 @@
1
- import e, { useRef as H, useState as $, useLayoutEffect as Ln, useEffect as Ve, useMemo as Tt } from "react";
2
- import { deriveKernelParams as xt, settingRaw as Je, printersByVendor as On, processPresets as Tn, printerDefaultPreset as Bn, printerKeys as lt, machineLimitKeys as Dn, printerSettings as Zn } from "three-slicer/settings";
3
- import { schema as Bt } from "three-slicer/data";
4
- import { createPortal as In } from "react-dom";
5
- import { SUPPORTED_EXT as bt, fileExt as Vn, loadModel as Fn, splitConnectedComponents as Gn } from "./model_loaders.js";
6
- import * as N from "three";
7
- import { OrbitControls as Un } from "three/examples/jsm/controls/OrbitControls.js";
8
- import { TransformControls as $n } from "three/examples/jsm/controls/TransformControls.js";
9
- import { makeSlicerWorker as Hn } from "./make_worker.js";
10
- import { roleRatios as It, computeColors as Dt, buildSegmentData as qn, makeToolpath as Wn, VIEW_TYPES as Xn, DEFAULT_RANGES_COLORS as Kn } from "./toolpath_gpu.js";
11
- function Yn({ css: n, className: a, children: c }) {
12
- const l = H(null), [r] = $(() => {
13
- const m = document.createElement("div");
14
- return m.style.display = "contents", m;
1
+ import e, { useRef as ne, useState as ae, useLayoutEffect as aa, useEffect as Te, useMemo as _t } from "react";
2
+ import { deriveKernelParams as ln, settingRaw as He, printersByVendor as oa, processPresets as la, printerDefaultPreset as sa, printerKeys as St, machineLimitKeys as ia, printerSettings as ca, filamentPresets as da } from "three-slicer/settings";
3
+ import { uiTree as pa, schema as Un } from "three-slicer/data";
4
+ import { createPortal as ua } from "react-dom";
5
+ import { SUPPORTED_EXT as an, fileExt as ma, loadModel as fa, splitConnectedComponents as ha } from "./model_loaders.js";
6
+ import { parseGcode as ba } from "./gcode_parse.js";
7
+ import * as W from "three";
8
+ import { OrbitControls as ga } from "three/examples/jsm/controls/OrbitControls.js";
9
+ import { TransformControls as ya } from "three/examples/jsm/controls/TransformControls.js";
10
+ import { makeSlicerWorker as xa } from "./make_worker.js";
11
+ import { roleRatios as er, computeColors as Wn, buildSegmentData as va, makeToolpath as wa, VIEW_TYPES as Ea, TOOL_COLOR as Hn, DEFAULT_RANGES_COLORS as ka } from "./toolpath_gpu.js";
12
+ function _a({ css: t, className: r, children: c }) {
13
+ const i = ne(null), [o] = ae(() => {
14
+ const p = document.createElement("div");
15
+ return p.style.display = "contents", p;
15
16
  });
16
- return Ln(() => {
17
- const m = l.current, d = m.shadowRoot || m.attachShadow({ mode: "open" });
18
- let w = d.querySelector("style[data-ts-css]");
19
- w || (w = document.createElement("style"), w.setAttribute("data-ts-css", ""), d.append(w)), w.textContent = n, d.contains(r) || d.append(r);
20
- }, [n, r]), /* @__PURE__ */ e.createElement("div", { ref: l, className: a }, In(c, r));
17
+ return aa(() => {
18
+ const p = i.current, h = p.shadowRoot || p.attachShadow({ mode: "open" });
19
+ let j = h.querySelector("style[data-ts-css]");
20
+ j || (j = document.createElement("style"), j.setAttribute("data-ts-css", ""), h.append(j)), j.textContent = t, h.contains(o) || h.append(o);
21
+ }, [t, o]), /* @__PURE__ */ e.createElement("div", { ref: i, className: r }, ua(c, o));
21
22
  }
22
- const Jn = ':host{display:contents}*{box-sizing:border-box}.app-shell{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;background:#0e1216;color:#cdd6dd;font:14px/1.5 -apple-system,Segoe UI,sans-serif}.app-body{flex:1 1 auto;display:flex;min-height:0}.topbar{flex:0 0 44px;height:44px;display:flex;align-items:center;padding:0 12px;gap:10px;background:#161b21;border-bottom:1px solid #262d35}.topbar .tb-left,.topbar .tb-right{flex:1 1 0;display:flex;align-items:center;gap:8px}.topbar .tb-right{justify-content:flex-end}.tb-logo{font-size:15px;color:#e6edf3;letter-spacing:.01em}.tb-logo b{color:#00ae42}.tb-re{font-size:10px;background:#00ae42;color:#fff;padding:1px 5px;border-radius:4px;margin-left:2px}.tb-btn{display:inline-flex;align-items:center;gap:5px;background:#20272f;border:1px solid #2c353d;color:#cdd6dd;border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px}.tb-btn img{width:15px;height:15px}.tb-btn:hover{background:#28313a}.tb-tabs{display:flex;border:1px solid #2c353d;border-radius:8px;overflow:hidden;flex:0 0 auto}.tb-tabs button{padding:6px 24px;border:0;background:#1a2027;color:#9fb3c0;font-weight:600;font-size:13px;cursor:pointer}.tb-tabs button.on{background:#00ae42;color:#fff}.tb-tabs button:disabled{color:#5a6570;cursor:not-allowed}.tb-icon{width:28px;height:28px;border:1px solid #2c353d;background:#20272f;color:#7d8b96;border-radius:6px;font-size:15px;cursor:not-allowed}.left-rail{flex:0 0 48px;display:flex;flex-direction:column;align-items:center;gap:6px;padding:9px 0;background:#12171c;border-right:1px solid #232a31}.left-rail button{width:36px;height:36px;border:1px solid transparent;border-radius:8px;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}.left-rail button img{width:21px;height:21px;opacity:.72}.left-rail button:hover{background:#1e252c}.left-rail button.on{background:#00ae42}.left-rail button.on img{opacity:1;filter:brightness(1.6)}.rail-sep{width:26px;height:1px;background:#2a323a;margin:3px 0}.viewport-col{position:relative;flex:1 1 auto;min-width:0;background:#161a1e}.vp-top-toolbar{position:absolute;top:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:3px;background:#12171cd9;padding:4px;border-radius:9px;border:1px solid #2a323a;z-index:3;max-width:calc(100% - 24px);overflow-x:auto;scrollbar-width:none}.vp-top-toolbar::-webkit-scrollbar{display:none}.vp-top-toolbar>*{flex:0 0 auto}.vp-top-toolbar button{width:32px;height:32px;border:0;background:transparent;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center}.vp-top-toolbar button img{width:18px;height:18px;opacity:.82}.vp-top-toolbar button:hover:not(:disabled){background:#2a333c}.vp-top-toolbar button:disabled{opacity:.32;cursor:not-allowed}.vtt-sep{width:1px;align-self:stretch;background:#333c45;margin:3px}.plate-bar{position:absolute;right:14px;bottom:30px;display:flex;align-items:center;gap:4px;z-index:3;background:#12171cd9;border:1px solid #2a323a;border-radius:9px;padding:4px}.plate-bar button{min-width:26px;height:26px;border:1px solid #2c353d;background:#1a2027;color:#9fb3c0;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}.plate-tab.on{background:#00ae42;color:#fff;border-color:#00ae42}.plate-add{color:#7ad19a!important}.plate-del{color:#e08a8a!important}.plate-bar button:disabled{opacity:.4;cursor:not-allowed}.slice-dd{position:relative;flex:1.4}.slice-dd .slice-btn{width:100%}.slice-menu{position:absolute;bottom:calc(100% + 6px);left:0;right:0;background:#fff;border:1px solid #d5d8dd;border-radius:8px;box-shadow:0 4px 16px #0000002e;overflow:hidden;z-index:10}.slice-menu button{display:block;width:100%;text-align:left;padding:9px 12px;border:0;background:#fff;color:#24282e;cursor:pointer;font-size:13px}.slice-menu button:hover{background:#eef7f1}.brush-panel{position:absolute;top:58px;left:14px;width:214px;background:#161b21f2;border:1px solid #2c353d;border-radius:10px;padding:10px;z-index:4;box-shadow:0 3px 14px #00000073}.bp-title{font-weight:600;margin-bottom:6px;color:#cdd6dd}.bp-modes{display:flex;gap:4px;flex-wrap:wrap}.bp-modes button{padding:3px 9px;border:1px solid #2c353d;background:#1a2027;color:#cdd6dd;border-radius:5px;cursor:pointer;font-size:12px}.bp-modes button.enf.on{background:#2b6cff;color:#fff;border-color:#2b6cff}.bp-modes button.blk.on{background:#e23b3b;color:#fff;border-color:#e23b3b}.bp-radius{display:block;font-size:12px;color:#aebcc6;margin-top:8px}.bp-radius input{width:100%}.bp-counts{margin-top:6px;font-size:11px}.stats-card{position:absolute;left:14px;bottom:32px;background:#161b21ed;border:1px solid #2c353d;border-radius:10px;padding:10px 14px;font-size:13px;line-height:1.7;z-index:2;color:#cdd6dd}.stats-card b{color:#9fe3bd}.sidebar{flex:0 0 384px;width:384px;display:flex;flex-direction:column;background:#eef0f2;color:#24282e;border-left:1px solid #d5d8dd;min-height:0}.sidebar-scroll{flex:1 1 auto;overflow-y:auto;padding:10px;display:flex;flex-direction:column;gap:10px}.side-card{background:#fff;border:1px solid #e2e4e8;border-radius:10px;padding:10px 12px}.sc-head{font-weight:700;font-size:13px;color:#2a2f36;display:flex;align-items:center;gap:6px;margin-bottom:7px}.sc-count{background:#00ae42;color:#fff;font-size:10px;border-radius:8px;padding:0 6px;font-weight:700}.sc-head-btns{margin-left:auto;display:flex;gap:4px}.sc-head-btns button{width:22px;height:22px;border:1px solid #d3d6db;background:#f6f7f8;border-radius:5px;cursor:pointer;font-size:14px;line-height:1;color:#333}.sc-head-btns button:disabled{opacity:.4;cursor:not-allowed}.sc-info{display:flex;justify-content:space-between;font-size:12px;color:#5a6069;padding:2px 0}.sc-info b{color:#24282e}.side-warn{margin:0 2px;font-size:12px}.filament-row{display:flex;align-items:center;gap:8px;padding:3px 0}.filament-row input[type=color]{width:28px;height:22px;border:1px solid #ccc;border-radius:5px;padding:0;cursor:pointer;background:none}.fil-t{font-weight:700;font-size:12px;width:20px}.fil-swatch{width:15px;height:15px;border-radius:4px;border:1px solid rgba(0,0,0,.15)}.fil-hex{font-size:11px;margin-left:auto}.obj-list2{list-style:none;margin:0 0 6px;padding:0}.obj-list2 li{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:12px}.obj-list2 li.obj-hidden{opacity:.5}.obj-eye{border:0;background:none;cursor:pointer;font-size:13px;padding:0}.obj-list2 .obj-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#24282e}.obj-list2 .obj-ext{border:1px solid #d0d3d8;border-radius:5px;font-size:11px;padding:1px 3px;background:#fff}.obj-del{border:0;background:#fbe9ec;color:#d0435a;border-radius:5px;cursor:pointer;padding:0 6px}.side-card .slice-support{color:#444;font-size:12px;margin:5px 0 0}.process-card{padding:8px}.side-card .view-type-row{color:#444}.side-card .view-type-row select,.side-card .obj-ext{background:#fff;color:#222;border:1px solid #ccd0d5}.side-card .slice-layer label{color:#444;font-size:12px}.side-card .grad-title{color:#333}.side-card .grad-minmax{color:#666}.side-card .role-legend,.side-card .slice-travel{color:#555}.side-card .layer-ctl button{background:#f1f2f4;color:#333;border:1px solid #d3d6db}.side-card .layer-ctl button.on{background:#00ae42;color:#fff;border-color:#00ae42}.side-bottom{flex:0 0 auto;display:flex;gap:8px;padding:10px;border-top:1px solid #dcdfe3;background:#e6e9ec}.side-bottom .slice-btn{flex:1.4;margin:0}.auto-slice{display:flex;align-items:center;gap:5px;font-size:12px;color:#444;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none}.auto-slice input{margin:0}.export-btn{flex:1;text-align:center;padding:9px;border-radius:8px;background:#dfe6ea;color:#1f6feb;border:1px solid #c7d2da;text-decoration:none;font-weight:600;cursor:pointer;font-size:13px}.export-btn:hover{background:#d3dee4}.export-btn:disabled{color:#9aa4ad;background:#e9ecef;cursor:not-allowed}.vp-canvas{position:absolute;top:0;right:0;bottom:0;left:0}.vp-canvas canvas{display:block}.vp-canvas.fail{display:flex;align-items:center;justify-content:center}.vp-fallback{color:#f0c9a8;padding:24px;text-align:center;max-width:560px;line-height:1.5}.vp-status{position:absolute;bottom:8px;left:12px;right:12px;z-index:1;font-size:11.5px;color:#9fe3bd;font-family:ui-monospace,monospace;text-shadow:0 1px 2px #000;pointer-events:none}.slice-warn{color:#f0c060;margin:4px 0}.sc-sub{font-size:10.5px;opacity:.62;margin:1px 0 3px}.sc-fold>summary{display:flex;justify-content:space-between;align-items:center;font-size:12px;color:#5a6069;padding:2px 0;cursor:pointer;list-style:none}.sc-fold>summary::-webkit-details-marker{display:none}.sc-fold-lbl:before{content:"▸";display:inline-block;width:10px;font-size:9px;opacity:.5}.sc-fold[open] .sc-fold-lbl:before{content:"▾"}.sc-fold>summary b{color:#24282e}.sc-fold[open]>summary{border-bottom:1px solid #e3e6ea;margin-bottom:4px}.sc-fold-body{max-height:320px;overflow-y:auto}.sc-model{max-width:62%;font-size:11.5px;padding:1px 2px}.support-tools{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:2px 0 2px 16px;padding-left:6px;border-left:2px solid #e3e6ea}.sup-style{margin-left:16px;padding-left:6px;border-left:2px solid #e3e6ea}.sup-paint{font-size:11px;padding:2px 8px;border:1px solid #c9ced6;border-radius:5px;background:#fff;color:#24282e;cursor:pointer;white-space:nowrap}.sup-paint.on{background:#2b6cff;border-color:#2b6cff;color:#fff}.sup-paint:disabled{opacity:.45;cursor:not-allowed}.slice-btn{width:100%;margin-top:6px;padding:7px;border:0;border-radius:7px;background:#00ae42;color:#fff;font-weight:700;cursor:pointer}.slice-btn:disabled{background:#3a4650;color:#8b98a2;cursor:not-allowed}.slice-err{color:#ff8b8b;margin-top:6px}.slice-stats{margin-top:8px;line-height:1.5}.slice-stats b{color:#9fe3bd}.slice-layer{margin-top:8px;display:flex;flex-direction:column;gap:4px}.slice-layer input[type=range]{width:100%}.slice-travel{color:#aebcc6;display:flex;align-items:center;gap:4px}.view-type-row{display:flex;align-items:center;justify-content:space-between;gap:6px;color:#aebcc6}.view-type-row select{flex:1;background:#10141a;color:#eee;border:1px solid #2c353d;border-radius:4px;padding:2px 4px}.dual-slider{position:relative;height:22px}.dual-slider input[type=range]{position:absolute;left:0;top:0;width:100%;margin:0;pointer-events:none;background:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.dual-slider input[type=range]::-webkit-slider-thumb{pointer-events:auto;-webkit-appearance:none;width:13px;height:13px;border-radius:50%;background:#00ae42;border:2px solid #0c1013;cursor:pointer}.dual-slider input[type=range]::-moz-range-thumb{pointer-events:auto;width:13px;height:13px;border-radius:50%;background:#00ae42;border:2px solid #0c1013;cursor:pointer}.dual-slider input[type=range]::-webkit-slider-runnable-track{height:4px;background:#2c353d;border-radius:2px}.dual-slider input[type=range]:first-child{z-index:2}.layer-ctl{display:flex;align-items:center;gap:8px}.layer-ctl button{padding:2px 8px;border:1px solid #2c353d;border-radius:5px;background:#1a2027;color:#cdd6dd;font-size:11px;cursor:pointer}.layer-ctl button.on{background:#00ae42;color:#fff;border-color:#00ae42}.grad-legend{margin-top:6px}.grad-title{font-size:11px;color:#cdd6dd;margin-bottom:3px}.grad-bar{height:10px;border-radius:3px;border:1px solid #2c353d}.grad-minmax{display:flex;justify-content:space-between;font-size:10px;color:#aebcc6;margin-top:2px}.role-legend{display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:6px;font-size:11px;color:#aebcc6}.role-item{display:inline-flex;align-items:center;gap:4px}.role-item i{width:10px;height:6px;border-radius:2px;display:inline-block}.role-item b{color:#cdd6dd}.vp-canvas.drag-over{outline:3px dashed #00ae42;outline-offset:-6px}.empty-hint{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;pointer-events:none;color:#9fb3c0;text-align:center}.empty-hint .eh-icon{font-size:46px;opacity:.8}.empty-hint .eh-title{font-size:16px;font-weight:600;color:#cdd6dd}.empty-hint .eh-sub{font-size:12px;color:#7d8b96;letter-spacing:.04em}.empty-hint .eh-btn{pointer-events:auto;margin-top:8px;padding:7px 18px;border:0;border-radius:7px;background:#00ae42;color:#fff;font-weight:600;cursor:pointer}.empty-hint .eh-btn:hover{background:#06c04d}.drop-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#00ae4224;color:#d7f7e3;font-size:18px;font-weight:700;pointer-events:none;border:3px dashed #00ae42;box-sizing:border-box}.obj-split{border:0;background:transparent;cursor:pointer;padding:0 3px;line-height:0}.obj-split img{width:13px;height:13px;opacity:.6}.obj-split:hover img{opacity:1}.ctx-scrim{position:absolute;top:0;right:0;bottom:0;left:0;z-index:24}.ctx-menu{position:absolute;z-index:25;min-width:168px;padding:4px;background:#1b2027;border:1px solid #2f3944;border-radius:8px;box-shadow:0 8px 24px #00000080}.ctx-menu button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:16px;padding:6px 10px;border:0;border-radius:5px;background:transparent;color:#dfe6ee;font-size:12px;text-align:left;cursor:pointer}.ctx-menu button:hover:not(:disabled){background:#273140}.ctx-menu button:disabled{color:#64707d;cursor:not-allowed}.ctx-menu button.danger:hover:not(:disabled){background:#4a2229;color:#ffb3b3}.ctx-menu hr{margin:4px 6px;border:0;border-top:1px solid #2f3944}.ctx-key{color:#7d8b96;font-family:ui-monospace,monospace;font-size:11px}.help-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#080c10b8;z-index:20}.help-card{background:#1b2027;color:#dfe6ee;border:1px solid #2f3944;border-radius:10px;padding:18px 22px;max-width:720px;box-shadow:0 12px 40px #00000080}.help-card h3{margin:0 0 12px;font-size:15px}.help-card h4{margin:0 0 6px;font-size:12px;color:#00ae42;text-transform:uppercase;letter-spacing:.04em}.help-cols{display:flex;gap:26px;flex-wrap:wrap}.help-card dl{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:0;font-size:12px}.help-card dt{color:#9fb0c2;font-family:ui-monospace,monospace;white-space:nowrap}.help-card dd{margin:0}.slice-support{display:flex;align-items:center;gap:5px;margin:6px 0;color:#cdd6dd;font-size:12px}.slice-support .muted{font-size:10px}.sc-bed{display:inline-flex;align-items:center;gap:4px;font-weight:700;color:#222}.sc-bed input{width:54px;padding:1px 4px;font-size:12px;font-weight:600;text-align:right;border:1px solid #ccd0d5;border-radius:5px;background:#fff;color:#222}', Qn = 40, Ye = (n) => n + Qn, Vt = 9, gt = (n) => {
23
- const a = Math.sqrt(n), c = Math.round(a);
24
- return a > c ? c + 1 : c;
25
- }, er = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h3m2,0h3m-4-4v8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), tr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M33.5,35H5.5A1.5,1.5,0,0,1,4,33.5V5.5A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v28A1.5,1.5,0,0,1,33.5,35ZM5.5,5a.5.5,0,0,0-.5.5v28a.5.5,0,0,0,.5.5h28a.5.5,0,0,0,.5-.5V5.5a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M30.5,32H8.5A1.5,1.5,0,0,1,7,30.5v-6A1.5,1.5,0,0,1,8.5,23h22A1.5,1.5,0,0,1,32,24.5v6A1.5,1.5,0,0,1,30.5,32Zm-22-8a.5.5,0,0,0-.5.5v6a.5.5,0,0,0,.5.5h22a.5.5,0,0,0,.5-.5v-6a.5.5,0,0,0-.5-.5Zm22-2h-6A1.5,1.5,0,0,1,23,20.5V8.5A1.5,1.5,0,0,1,24.5,7h6A1.5,1.5,0,0,1,32,8.5v12A1.5,1.5,0,0,1,30.5,22Zm-6-14a.5.5,0,0,0-.5.5v12a.5.5,0,0,0,.5.5h6a.5.5,0,0,0,.5-.5V8.5a.5.5,0,0,0-.5-.5Zm-6,6h-8a3.5,3.5,0,0,1,0-7h8a3.5,3.5,0,0,1,0,7Zm-8-6a2.5,2.5,0,0,0,0,5h8a2.5,2.5,0,0,0,0-5Zm8,14A3.5,3.5,0,1,1,22,18.5,3.5,3.5,0,0,1,18.5,22Zm0-6A2.5,2.5,0,1,0,21,18.5,2.5,2.5,0,0,0,18.5,16Zm-8,6A3.5,3.5,0,1,1,14,18.5,3.5,3.5,0,0,1,10.5,22Zm0-6A2.5,2.5,0,1,0,13,18.5,2.5,2.5,0,0,0,10.5,16Z" style="fill:#009688"/></svg>'), nr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6.2,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.6,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), rr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3.5,9h2m2,0h2m2,0h2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5l1.8,3h-3.6z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Ft = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), or = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5l6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M11,5l-6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), ar = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,4h5v5H4z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M7,7h5v5H7z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), lr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8,3a5.93,5.93,0,0,0-4.95,2.5A9.1,9.1,0,0,0,1,8a9.1,9.1,0,0,0,2.05,2.5A5.93,5.93,0,0,0,8,13a5.93,5.93,0,0,0,4.95-2.5A9.1,9.1,0,0,0,15,8a9.1,9.1,0,0,0-2.05-2.5A5.93,5.93,0,0,0,8,3m0,8a2,2,0,1,1,2-2A2,2,0,0,1,8,11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), sr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.5,2.5l13,11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3a5.93,5.93,0,0,0-4.95,2.5A9.1,9.1,0,0,0,1,8a9.1,9.1,0,0,0,2.05,2.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3a5.93,5.93,0,0,1,4.95,2.5A9.1,9.1,0,0,1,15,8a9.1,9.1,0,0,1-2.05,2.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><circle cx="8" cy="8" r="2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), ir = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M2.5,8h11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M2.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M13.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), cr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,8a4,4,0,1,0,8,0a4,4,0,1,0,-8,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,4a4,4,0,0,1,0,8z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;fill:#949494;stroke:none"/></svg>'), dr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.854,19.146l-4-4a.5.5,0,0,0-.708.708L33.293,19H28.5a.5.5,0,0,0,0,1h4.793l-3.147,3.146a.5.5,0,0,0,.708.708l4-4A.5.5,0,0,0,34.854,19.146Z" style="fill:#009688"/><path d="M19.854,4.146a.5.5,0,0,0-.708,0l-4,4a.5.5,0,0,0,.708.708L19,5.707V10.5a.5.5,0,0,0,1,0V5.707l3.146,3.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M23.146,30.146,20,33.293V28.5a.5.5,0,0,0-1,0v4.793l-3.146-3.147a.5.5,0,0,0-.708.708l4,4a.5.5,0,0,0,.708,0l4-4a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/><path d="M10.5,19H5.707l3.147-3.146a.5.5,0,0,0-.708-.708l-4,4a.5.5,0,0,0,0,.708l4,4a.5.5,0,0,0,.708-.708L5.707,20H10.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/></svg>'), pr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5h6v6H5z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;stroke-dasharray:2 1.5"/></svg>'), ur = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v5m-2-2 2,2 2-2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4,11.5h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), mr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.923,10.25a.392.392,0,0,0-.033-.05.5.5,0,0,0-.2-.162l0,0-15-6a.5.5,0,0,0-.372,0l-15,6,0,0a.5.5,0,0,0-.2.162.392.392,0,0,0-.033.05A.478.478,0,0,0,4,10.5v18a.464.464,0,0,0,.028.138.429.429,0,0,0,.011.054l.01.015a.51.51,0,0,0,.1.144l.008,0a.5.5,0,0,0,.156.1l0,0,14.989,6,0,0,.006,0a.5.5,0,0,0,.372,0l.006,0,0,0,1.989-.8a.5.5,0,0,0-.372-.928L20,33.762V16.839l14-5.6V21.5a.5.5,0,0,0,1,0v-11A.481.481,0,0,0,34.923,10.25ZM19,33.762l-14-5.6V11.239l14,5.6Zm.5-17.8L5.846,10.5,19.5,5.039,33.153,10.5Z" style="fill:#b6b6b6"/><path d="M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z" style="fill:#009688"/><path d="M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/></svg>'), fr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,22a.5.5,0,0,1-.5-.5V18H18v3.5a.5.5,0,0,1-1,0v-6a.5.5,0,0,1,.084-.277l2-3A.5.5,0,0,1,19.5,12h4a.5.5,0,0,1,.416.223l2,3A.5.5,0,0,1,26,15.5v6A.5.5,0,0,1,25.5,22ZM18,17h7V15.651L23.232,13H19.768L18,15.651Z" style="fill:#009688"/><path d="M11.964,29.314a.5.5,0,0,0-.65-.278L7.152,30.7a17.321,17.321,0,0,1,3.739-6.888.5.5,0,1,0-.782-.624A19.312,19.312,0,0,0,6.359,29.8L4.964,26.314a.5.5,0,1,0-.928.372l2,5c0,.01.016.015.021.025a.475.475,0,0,0,.15.185c.009.007.016.017.025.023a.5.5,0,0,0,.2.076A.367.367,0,0,0,6.5,32h0a.475.475,0,0,0,.181-.036h0l5-2A.5.5,0,0,0,11.964,29.314Z" style="fill:#009688"/><rect x="4" y="34" width="31" height="1" style="fill:#009688"/><path d="M34.854,17.146l-13-13a.5.5,0,0,0-.708,0l-13,13a.5.5,0,0,0,.708.708L21.5,5.207,33.793,17.5,21.146,30.146a.5.5,0,0,0,.708.708l13-13A.5.5,0,0,0,34.854,17.146Z" style="fill:#b6b6b6"/><path d="M21.5,31a.5.5,0,0,1-.354-.146l-13-13a.5.5,0,0,1,.708-.708l13,13A.5.5,0,0,1,21.5,31Z" style="fill:#009688"/></svg>'), hr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M27.878,34.926h-4.4c-.509,0-.845,0-.955-.394a.593.593,0,0,1,.36-.67,2.3,2.3,0,0,0,1.2-2.189,3.253,3.253,0,1,1,3.8,3.206Zm-3.538-1h2.988a2.253,2.253,0,1,0-2.253-2.253A3.445,3.445,0,0,1,24.34,33.926Z" style="fill:#009688"/><path d="M30.5,30.289l-.846-.535,4.364-6.9a.222.222,0,0,0-.36-.258l-5.4,6.644-.776-.631,5.4-6.644a1.222,1.222,0,0,1,1.982,1.423Z" style="fill:#009688"/><path d="M33.5,35h-1a.5.5,0,0,1,0-1h1a.5.5,0,0,0,.5-.5v-5a.5.5,0,0,1,1,0v5A1.5,1.5,0,0,1,33.5,35Z" style="fill:#b6b6b6"/><path d="M7.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,7.5,35Z" style="fill:#009688"/><path d="M10.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,10.5,35Z" style="fill:#009688"/><path d="M13.5,35h-9a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,.5-.5h9a.5.5,0,0,1,.5.5v20A.5.5,0,0,1,13.5,35ZM5,34h8V15H5Z" style="fill:#009688"/><path d="M22.5,29a.5.5,0,0,1-.5-.5V15H4.5a.5.5,0,0,1-.5-.5v-9A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v13a.5.5,0,0,1-1,0V5.5a.5.5,0,0,0-.5-.5H5.5a.5.5,0,0,0-.5.5V14H22.5a.5.5,0,0,1,.5.5v14A.5.5,0,0,1,22.5,29Z" style="fill:#b6b6b6"/></svg>'), gr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M35,19.5A15.517,15.517,0,0,1,19.5,35a15.767,15.767,0,0,1-2.063-.136.5.5,0,0,1-.43-.562.507.507,0,0,1,.562-.43A14.5,14.5,0,1,0,19.5,5,14.43,14.43,0,0,0,7.751,11H11.5a.5.5,0,0,1,0,1h-5a.5.5,0,0,1-.5-.5v-5a.5.5,0,0,1,1,0v3.836A15.5,15.5,0,0,1,35,19.5ZM5.943,24.66a.5.5,0,1,0-.933.356A15.426,15.426,0,0,0,7.354,29.13a.5.5,0,0,0,.783-.621A14.533,14.533,0,0,1,5.943,24.66Zm8.4,8.4a14.523,14.523,0,0,1-3.85-2.194.5.5,0,0,0-.621.784,15.455,15.455,0,0,0,4.115,2.343.487.487,0,0,0,.178.033.5.5,0,0,0,.178-.966ZM5.128,21.431A14.621,14.621,0,0,1,5,19.5a.5.5,0,0,0-1,0,15.729,15.729,0,0,0,.136,2.062.5.5,0,0,0,.992-.131Z" style="fill:#009688"/></svg>'), br = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,5h-2a.5.5,0,0,1,0-1h2a.5.5,0,0,1,0,1ZM21,4.5a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,4.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,4.5Zm10,30a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,26,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,34.5ZM5,15.5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm30-10v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm-9-1v-10A1.5,1.5,0,0,0,24.5,13h-10A1.5,1.5,0,0,0,13,14.5v10A1.5,1.5,0,0,0,14.5,26h10A1.5,1.5,0,0,0,26,24.5ZM24.5,14a.5.5,0,0,1,.5.5v10a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5v-10a.5.5,0,0,1,.5-.5Z" style="fill:#b6b6b6"/><path d="M34.962,4.309A.5.5,0,0,0,34.5,4h-5a.5.5,0,0,0,0,1h3.793l-6.147,6.146a.5.5,0,0,0,.708.708L34,5.707V9.5a.5.5,0,0,0,1,0v-5A.5.5,0,0,0,34.962,4.309Z" style="fill:#009688"/><path d="M5.707,5H9.5a.5.5,0,0,0,0-1h-5a.5.5,0,0,0-.5.5v5a.5.5,0,0,0,1,0V5.707l6.146,6.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M34.5,29a.5.5,0,0,0-.5.5v3.793l-6.146-6.147a.5.5,0,0,0-.708.708L33.293,34H29.5a.5.5,0,0,0,0,1h5a.5.5,0,0,0,.5-.5v-5A.5.5,0,0,0,34.5,29Z" style="fill:#009688"/><path d="M11.146,27.146,5,33.293V29.5a.5.5,0,0,0-1,0v5a.5.5,0,0,0,.5.5h5a.5.5,0,0,0,0-1H5.707l6.147-6.146a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/></svg>'), xr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v2m0,2v2m0,2v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Gt = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v9" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,6.5,4,8l2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,6.5,12,8l-2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), yr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.5,5h7" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,5v6.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), vr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M3,4.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,7h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,9h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,10.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,11.6h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>');
26
- function wr(n) {
23
+ const Na = ':host{display:contents}*{box-sizing:border-box}.app-shell{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;background:#0e1216;color:#cdd6dd;font:14px/1.5 -apple-system,Segoe UI,sans-serif}.app-body{flex:1 1 auto;display:flex;min-height:0}.topbar{flex:0 0 44px;height:44px;display:flex;align-items:center;padding:0 12px;gap:10px;background:#161b21;border-bottom:1px solid #262d35}.topbar .tb-left,.topbar .tb-right{flex:1 1 0;display:flex;align-items:center;gap:8px}.topbar .tb-right{justify-content:flex-end}.tb-logo{font-size:15px;color:#e6edf3;letter-spacing:.01em}.tb-logo b{color:#00ae42}.tb-re{font-size:10px;background:#00ae42;color:#fff;padding:1px 5px;border-radius:4px;margin-left:2px}.tb-btn{display:inline-flex;align-items:center;gap:5px;background:#20272f;border:1px solid #2c353d;color:#cdd6dd;border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px}.tb-btn img{width:15px;height:15px}.tb-btn:hover{background:#28313a}.tb-tabs{display:flex;border:1px solid #2c353d;border-radius:8px;overflow:hidden;flex:0 0 auto}.tb-tabs button{padding:6px 24px;border:0;background:#1a2027;color:#9fb3c0;font-weight:600;font-size:13px;cursor:pointer}.tb-tabs button.on{background:#00ae42;color:#fff}.tb-tabs button:disabled{color:#5a6570;cursor:not-allowed}.tb-icon{width:28px;height:28px;border:1px solid #2c353d;background:#20272f;color:#7d8b96;border-radius:6px;font-size:15px;cursor:not-allowed}.left-rail{flex:0 0 48px;display:flex;flex-direction:column;align-items:center;gap:6px;padding:9px 0;background:#12171c;border-right:1px solid #232a31}.left-rail button{width:36px;height:36px;border:1px solid transparent;border-radius:8px;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}.left-rail button img{width:21px;height:21px;opacity:.72}.left-rail button:hover{background:#1e252c}.left-rail button.on{background:#00ae42}.left-rail button.on img{opacity:1;filter:brightness(1.6)}.rail-sep{width:26px;height:1px;background:#2a323a;margin:3px 0}.viewport-col{position:relative;flex:1 1 auto;min-width:0;background:#161a1e}.vp-top-toolbar{position:absolute;top:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:3px;background:#12171cd9;padding:4px;border-radius:9px;border:1px solid #2a323a;z-index:3;max-width:calc(100% - 24px);overflow-x:auto;scrollbar-width:none}.vp-top-toolbar::-webkit-scrollbar{display:none}.vp-top-toolbar>*{flex:0 0 auto}.vp-top-toolbar button{width:32px;height:32px;border:0;background:transparent;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center}.vp-top-toolbar button img{width:18px;height:18px;opacity:.82}.vp-top-toolbar button:hover:not(:disabled){background:#2a333c}.vp-top-toolbar button:disabled{opacity:.32;cursor:not-allowed}.vtt-sep{width:1px;align-self:stretch;background:#333c45;margin:3px}.plate-bar{position:absolute;right:14px;bottom:30px;display:flex;align-items:center;gap:4px;z-index:3;background:#12171cd9;border:1px solid #2a323a;border-radius:9px;padding:4px}.plate-bar button{min-width:26px;height:26px;border:1px solid #2c353d;background:#1a2027;color:#9fb3c0;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}.plate-tab.on{background:#00ae42;color:#fff;border-color:#00ae42}.plate-add{color:#7ad19a!important}.plate-del{color:#e08a8a!important}.plate-bar button:disabled{opacity:.4;cursor:not-allowed}.slice-dd{position:relative;flex:1.4}.slice-dd .slice-btn{width:100%}.slice-menu{position:absolute;bottom:calc(100% + 6px);left:0;right:0;background:#fff;border:1px solid #d5d8dd;border-radius:8px;box-shadow:0 4px 16px #0000002e;overflow:hidden;z-index:10}.slice-menu button{display:block;width:100%;text-align:left;padding:9px 12px;border:0;background:#fff;color:#24282e;cursor:pointer;font-size:13px}.slice-menu button:hover{background:#eef7f1}.brush-panel{position:absolute;top:58px;left:14px;width:214px;background:#161b21f2;border:1px solid #2c353d;border-radius:10px;padding:10px;z-index:4;box-shadow:0 3px 14px #00000073}.bp-title{font-weight:600;margin-bottom:6px;color:#cdd6dd}.bp-modes{display:flex;gap:4px;flex-wrap:wrap}.bp-modes button{padding:3px 9px;border:1px solid #2c353d;background:#1a2027;color:#cdd6dd;border-radius:5px;cursor:pointer;font-size:12px}.bp-modes button.enf.on{background:#2b6cff;color:#fff;border-color:#2b6cff}.bp-modes button.blk.on{background:#e23b3b;color:#fff;border-color:#e23b3b}.bp-tools{margin-top:6px}.bp-tool.on{background:#2c353d;color:#fff;border-color:#46525c}.bp-radius{display:block;font-size:12px;color:#aebcc6;margin-top:8px}.bp-radius input{width:100%}.bp-counts{margin-top:6px;font-size:11px}.bp-chip{min-width:30px;font-weight:700;border-color:#00000059!important}.bp-chip.on{outline:2px solid #fff;outline-offset:1px}.bp-eraser{background:#1a2027!important;color:#cdd6dd!important;font-weight:400}.bp-actions{margin-top:6px}.bp-note{margin-top:6px;font-size:10.5px;line-height:1.35;color:#8b98a2}.stats-card{position:absolute;left:14px;bottom:32px;background:#161b21ed;border:1px solid #2c353d;border-radius:10px;padding:10px 14px;font-size:13px;line-height:1.7;z-index:2;color:#cdd6dd}.stats-card b{color:#9fe3bd}.stat-tool{display:flex;align-items:center;gap:5px;font-size:12px;margin-left:10px}.stat-swatch{width:9px;height:9px;border-radius:2px;border:1px solid rgba(255,255,255,.28);flex:0 0 auto}.stat-swatch-purge{background:repeating-linear-gradient(45deg,#6d7780 0,#6d7780 2px,#39424a 2px,#39424a 4px)}.sidebar{flex:0 0 384px;width:384px;display:flex;flex-direction:column;background:#eef0f2;color:#24282e;border-left:1px solid #d5d8dd;min-height:0}.sidebar-scroll{flex:1 1 auto;overflow-y:auto;padding:10px;display:flex;flex-direction:column;gap:10px}.side-card{background:#fff;border:1px solid #e2e4e8;border-radius:10px;padding:10px 12px}.sc-head{font-weight:700;font-size:13px;color:#2a2f36;display:flex;align-items:center;gap:6px;margin-bottom:7px}.sc-count{background:#00ae42;color:#fff;font-size:10px;border-radius:8px;padding:0 6px;font-weight:700}.sc-head-btns{margin-left:auto;display:flex;gap:4px}.sc-head-btns button{width:22px;height:22px;border:1px solid #d3d6db;background:#f6f7f8;border-radius:5px;cursor:pointer;font-size:14px;line-height:1;color:#333}.sc-head-btns button:disabled{opacity:.4;cursor:not-allowed}.sc-preset-btns button{width:auto;padding:0 8px;font-size:11px;white-space:nowrap}.sc-dirty{color:#c47f00;font-weight:600}.sc-info{display:flex;justify-content:space-between;font-size:12px;color:#5a6069;padding:2px 0}.sc-info b{color:#24282e}.side-warn{margin:0 2px;font-size:12px}.filament-row{display:flex;align-items:center;gap:8px;padding:3px 0}.filament-row input[type=color]{width:28px;height:22px;border:1px solid #ccc;border-radius:5px;padding:0;cursor:pointer;background:none}.filament-row{cursor:pointer;border-radius:5px;padding-left:4px}.filament-row.fil-active{background:#eef6f0;box-shadow:inset 2px 0 #00ae42}.fil-mat{font-size:11.5px;color:#5a6069;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sc-note{color:#8a9099;font-size:11px;font-style:italic}.fil-t{font-weight:700;font-size:12px;width:20px}.fil-mat{flex:1;min-width:0}.fil-badge{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;color:#5a6069;background:#eceef1;border-radius:7px;padding:1px 5px;white-space:nowrap}.fil-count{font-size:10px;font-weight:700;color:#fff;background:#2b6cff;border-radius:7px;padding:0 5px}.fil-paint{border:1px solid #d3d6db;background:#f6f7f8;border-radius:5px;cursor:pointer;font-size:11px;line-height:1;padding:3px 5px}.fil-paint.on{background:#2b6cff;border-color:#2b6cff}.obj-list2{list-style:none;margin:0 0 6px;padding:0}.obj-list2 li{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:12px}.obj-list2 li.obj-hidden{opacity:.5}.obj-eye{border:0;background:none;cursor:pointer;font-size:13px;padding:0}.obj-list2 .obj-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#24282e}.obj-list2 .obj-ext{border:1px solid #d0d3d8;border-radius:5px;font-size:11px;padding:1px 3px;background:#fff}.obj-del{border:0;background:#fbe9ec;color:#d0435a;border-radius:5px;cursor:pointer;padding:0 6px}.obj-painted{font-size:10px;line-height:1;background:#e6efff;border-radius:6px;padding:2px 4px}.side-card .slice-support{color:#444;font-size:12px;margin:5px 0 0}.process-card{padding:8px}.side-card .view-type-row{color:#444}.side-card .view-type-row select,.side-card .obj-ext{background:#fff;color:#222;border:1px solid #ccd0d5}.side-card .slice-layer label{color:#444;font-size:12px}.side-card .grad-title{color:#333}.side-card .grad-minmax{color:#666}.side-card .role-legend,.side-card .slice-travel{color:#555}.side-card .layer-ctl button{background:#f1f2f4;color:#333;border:1px solid #d3d6db}.side-card .layer-ctl button.on{background:#00ae42;color:#fff;border-color:#00ae42}.side-bottom{flex:0 0 auto;display:flex;gap:8px;padding:10px;border-top:1px solid #dcdfe3;background:#e6e9ec}.side-bottom .slice-btn{flex:1.4;margin:0}.auto-slice{display:flex;align-items:center;gap:5px;font-size:12px;color:#444;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none}.auto-slice input{margin:0}.export-btn{flex:1;text-align:center;padding:9px;border-radius:8px;background:#dfe6ea;color:#1f6feb;border:1px solid #c7d2da;text-decoration:none;font-weight:600;cursor:pointer;font-size:13px}.export-btn:hover{background:#d3dee4}.export-btn:disabled{color:#9aa4ad;background:#e9ecef;cursor:not-allowed}.vp-canvas{position:absolute;top:0;right:0;bottom:0;left:0}.vp-canvas canvas{display:block}.vp-canvas.fail{display:flex;align-items:center;justify-content:center}.vp-fallback{color:#f0c9a8;padding:24px;text-align:center;max-width:560px;line-height:1.5}.vp-status{position:absolute;bottom:8px;left:12px;right:12px;z-index:1;font-size:11.5px;color:#9fe3bd;font-family:ui-monospace,monospace;text-shadow:0 1px 2px #000;pointer-events:none}.slice-warn{color:#f0c060;margin:4px 0}.sc-sub{font-size:10.5px;opacity:.62;margin:1px 0 3px}.sc-fold>summary{display:flex;justify-content:space-between;align-items:center;font-size:12px;color:#5a6069;padding:2px 0;cursor:pointer;list-style:none}.sc-fold>summary::-webkit-details-marker{display:none}.sc-fold-lbl:before{content:"▸";display:inline-block;width:10px;font-size:9px;opacity:.5}.sc-fold[open] .sc-fold-lbl:before{content:"▾"}.sc-fold>summary b{color:#24282e}.sc-fold[open]>summary{border-bottom:1px solid #e3e6ea;margin-bottom:4px}.sc-fold-body{max-height:320px;overflow-y:auto}.sc-model{max-width:62%;font-size:11.5px;padding:1px 2px}.support-tools{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:2px 0 2px 16px;padding-left:6px;border-left:2px solid #e3e6ea}.sup-style{margin-left:16px;padding-left:6px;border-left:2px solid #e3e6ea}.sup-paint{font-size:11px;padding:2px 8px;border:1px solid #c9ced6;border-radius:5px;background:#fff;color:#24282e;cursor:pointer;white-space:nowrap}.sup-paint.on{background:#2b6cff;border-color:#2b6cff;color:#fff}.sup-paint:disabled{opacity:.45;cursor:not-allowed}.sup-filament-warn{margin:2px 0 2px 22px;font-size:11px;line-height:1.35;color:#a06a00}.slice-btn{width:100%;margin-top:6px;padding:7px;border:0;border-radius:7px;background:#00ae42;color:#fff;font-weight:700;cursor:pointer}.slice-btn:disabled{background:#3a4650;color:#8b98a2;cursor:not-allowed}.slice-err{color:#ff8b8b;margin-top:6px}.slice-stats{margin-top:8px;line-height:1.5}.slice-stats b{color:#9fe3bd}.slice-layer{margin-top:8px;display:flex;flex-direction:column;gap:4px}.slice-layer input[type=range]{width:100%}.slice-travel{color:#aebcc6;display:flex;align-items:center;gap:4px}.view-type-row{display:flex;align-items:center;justify-content:space-between;gap:6px;color:#aebcc6}.view-type-row select{flex:1;background:#10141a;color:#eee;border:1px solid #2c353d;border-radius:4px;padding:2px 4px}.dual-slider{position:relative;height:22px}.dual-slider input[type=range]{position:absolute;left:0;top:0;width:100%;margin:0;pointer-events:none;background:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.dual-slider input[type=range]::-webkit-slider-thumb{pointer-events:auto;-webkit-appearance:none;width:13px;height:13px;border-radius:50%;background:#00ae42;border:2px solid #0c1013;cursor:pointer}.dual-slider input[type=range]::-moz-range-thumb{pointer-events:auto;width:13px;height:13px;border-radius:50%;background:#00ae42;border:2px solid #0c1013;cursor:pointer}.dual-slider input[type=range]::-webkit-slider-runnable-track{height:4px;background:#2c353d;border-radius:2px}.dual-slider input[type=range]:first-child{z-index:2}.layer-ctl{display:flex;align-items:center;gap:8px}.layer-ctl button{padding:2px 8px;border:1px solid #2c353d;border-radius:5px;background:#1a2027;color:#cdd6dd;font-size:11px;cursor:pointer}.layer-ctl button.on{background:#00ae42;color:#fff;border-color:#00ae42}.grad-legend{margin-top:6px}.grad-title{font-size:11px;color:#cdd6dd;margin-bottom:3px}.grad-bar{height:10px;border-radius:3px;border:1px solid #2c353d}.grad-minmax{display:flex;justify-content:space-between;font-size:10px;color:#aebcc6;margin-top:2px}.role-legend{display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:6px;font-size:11px;color:#aebcc6}.role-item{display:inline-flex;align-items:center;gap:4px}.role-item i{width:10px;height:6px;border-radius:2px;display:inline-block}.role-item b{color:#cdd6dd}.vp-canvas.drag-over{outline:3px dashed #00ae42;outline-offset:-6px}.empty-hint{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;pointer-events:none;color:#9fb3c0;text-align:center}.empty-hint .eh-icon{font-size:46px;opacity:.8}.empty-hint .eh-title{font-size:16px;font-weight:600;color:#cdd6dd}.empty-hint .eh-sub{font-size:12px;color:#7d8b96;letter-spacing:.04em}.empty-hint .eh-btn{pointer-events:auto;margin-top:8px;padding:7px 18px;border:0;border-radius:7px;background:#00ae42;color:#fff;font-weight:600;cursor:pointer}.empty-hint .eh-btn:hover{background:#06c04d}.drop-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#00ae4224;color:#d7f7e3;font-size:18px;font-weight:700;pointer-events:none;border:3px dashed #00ae42;box-sizing:border-box}.obj-split{border:0;background:transparent;cursor:pointer;padding:0 3px;line-height:0}.obj-split img{width:13px;height:13px;opacity:.6}.obj-split:hover img{opacity:1}.ctx-scrim{position:absolute;top:0;right:0;bottom:0;left:0;z-index:24}.ctx-menu{position:absolute;z-index:25;min-width:168px;padding:4px;background:#1b2027;border:1px solid #2f3944;border-radius:8px;box-shadow:0 8px 24px #00000080}.ctx-menu button{display:flex;width:100%;align-items:center;justify-content:space-between;gap:16px;padding:6px 10px;border:0;border-radius:5px;background:transparent;color:#dfe6ee;font-size:12px;text-align:left;cursor:pointer}.ctx-menu button:hover:not(:disabled){background:#273140}.ctx-menu button:disabled{color:#64707d;cursor:not-allowed}.ctx-menu button.danger:hover:not(:disabled){background:#4a2229;color:#ffb3b3}.ctx-menu hr{margin:4px 6px;border:0;border-top:1px solid #2f3944}.ctx-key{color:#7d8b96;font-family:ui-monospace,monospace;font-size:11px}.help-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#080c10b8;z-index:20}.help-card{background:#1b2027;color:#dfe6ee;border:1px solid #2f3944;border-radius:10px;padding:18px 22px;max-width:720px;box-shadow:0 12px 40px #00000080}.help-card h3{margin:0 0 12px;font-size:15px}.help-card h4{margin:0 0 6px;font-size:12px;color:#00ae42;text-transform:uppercase;letter-spacing:.04em}.help-cols{display:flex;gap:26px;flex-wrap:wrap}.help-card dl{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:0;font-size:12px}.help-card dt{color:#9fb0c2;font-family:ui-monospace,monospace;white-space:nowrap}.help-card dd{margin:0}.slice-support{display:flex;align-items:center;gap:5px;margin:6px 0;color:#cdd6dd;font-size:12px}.slice-support .muted{font-size:10px}.sc-bed{display:inline-flex;align-items:center;gap:4px;font-weight:700;color:#222}.sc-bed input{width:54px;padding:1px 4px;font-size:12px;font-weight:600;text-align:right;border:1px solid #ccd0d5;border-radius:5px;background:#fff;color:#222}.stat-split{color:#8b98a2;font-size:11px}.stat-type{margin-left:4px;padding:0 4px;border-radius:3px;background:#2c353d;color:#aebcc6;font-size:10.5px}.purge-grid{width:100%;border-collapse:collapse;font-size:11px}.purge-grid th{font-weight:600;color:#aebcc6;padding:2px 3px;white-space:nowrap}.purge-grid th .stat-swatch{margin-right:3px;vertical-align:middle}.purge-grid td{padding:1px 2px;text-align:center}.purge-grid input{width:100%;min-width:42px;background:#1a2027;border:1px solid #2c353d;color:#cdd6dd;border-radius:4px;padding:2px 3px;font-size:11px}.sc-num input{width:62px;background:#1a2027;border:1px solid #2c353d;color:#cdd6dd;border-radius:4px;padding:2px 5px;font-size:12px;margin-left:4px}', Ma = 40, rt = (t) => t + Ma, tr = 9, Ft = (t) => {
24
+ const r = Math.sqrt(t), c = Math.round(r);
25
+ return r > c ? c + 1 : c;
26
+ }, Aa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h3m2,0h3m-4-4v8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Ca = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M33.5,35H5.5A1.5,1.5,0,0,1,4,33.5V5.5A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v28A1.5,1.5,0,0,1,33.5,35ZM5.5,5a.5.5,0,0,0-.5.5v28a.5.5,0,0,0,.5.5h28a.5.5,0,0,0,.5-.5V5.5a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M30.5,32H8.5A1.5,1.5,0,0,1,7,30.5v-6A1.5,1.5,0,0,1,8.5,23h22A1.5,1.5,0,0,1,32,24.5v6A1.5,1.5,0,0,1,30.5,32Zm-22-8a.5.5,0,0,0-.5.5v6a.5.5,0,0,0,.5.5h22a.5.5,0,0,0,.5-.5v-6a.5.5,0,0,0-.5-.5Zm22-2h-6A1.5,1.5,0,0,1,23,20.5V8.5A1.5,1.5,0,0,1,24.5,7h6A1.5,1.5,0,0,1,32,8.5v12A1.5,1.5,0,0,1,30.5,22Zm-6-14a.5.5,0,0,0-.5.5v12a.5.5,0,0,0,.5.5h6a.5.5,0,0,0,.5-.5V8.5a.5.5,0,0,0-.5-.5Zm-6,6h-8a3.5,3.5,0,0,1,0-7h8a3.5,3.5,0,0,1,0,7Zm-8-6a2.5,2.5,0,0,0,0,5h8a2.5,2.5,0,0,0,0-5Zm8,14A3.5,3.5,0,1,1,22,18.5,3.5,3.5,0,0,1,18.5,22Zm0-6A2.5,2.5,0,1,0,21,18.5,2.5,2.5,0,0,0,18.5,16Zm-8,6A3.5,3.5,0,1,1,14,18.5,3.5,3.5,0,0,1,10.5,22Zm0-6A2.5,2.5,0,1,0,13,18.5,2.5,2.5,0,0,0,10.5,16Z" style="fill:#009688"/></svg>'), ja = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6.2,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.6,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Pa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3.5,9h2m2,0h2m2,0h2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5l1.8,3h-3.6z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), nr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), za = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5l6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M11,5l-6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Ra = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,4h5v5H4z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M7,7h5v5H7z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Ta = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M8,3a5.93,5.93,0,0,0-4.95,2.5A9.1,9.1,0,0,0,1,8a9.1,9.1,0,0,0,2.05,2.5A5.93,5.93,0,0,0,8,13a5.93,5.93,0,0,0,4.95-2.5A9.1,9.1,0,0,0,15,8a9.1,9.1,0,0,0-2.05-2.5A5.93,5.93,0,0,0,8,3m0,8a2,2,0,1,1,2-2A2,2,0,0,1,8,11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Oa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.5,2.5l13,11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3a5.93,5.93,0,0,0-4.95,2.5A9.1,9.1,0,0,0,1,8a9.1,9.1,0,0,0,2.05,2.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3a5.93,5.93,0,0,1,4.95,2.5A9.1,9.1,0,0,1,15,8a9.1,9.1,0,0,1-2.05,2.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><circle cx="8" cy="8" r="2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), La = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M2.5,8h11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M2.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M13.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Sa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,8a4,4,0,1,0,8,0a4,4,0,1,0,-8,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,4a4,4,0,0,1,0,8z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;fill:#949494;stroke:none"/></svg>'), Fa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.854,19.146l-4-4a.5.5,0,0,0-.708.708L33.293,19H28.5a.5.5,0,0,0,0,1h4.793l-3.147,3.146a.5.5,0,0,0,.708.708l4-4A.5.5,0,0,0,34.854,19.146Z" style="fill:#009688"/><path d="M19.854,4.146a.5.5,0,0,0-.708,0l-4,4a.5.5,0,0,0,.708.708L19,5.707V10.5a.5.5,0,0,0,1,0V5.707l3.146,3.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M23.146,30.146,20,33.293V28.5a.5.5,0,0,0-1,0v4.793l-3.146-3.147a.5.5,0,0,0-.708.708l4,4a.5.5,0,0,0,.708,0l4-4a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/><path d="M10.5,19H5.707l3.147-3.146a.5.5,0,0,0-.708-.708l-4,4a.5.5,0,0,0,0,.708l4,4a.5.5,0,0,0,.708-.708L5.707,20H10.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/></svg>'), Ba = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5h6v6H5z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;stroke-dasharray:2 1.5"/></svg>'), Da = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v5m-2-2 2,2 2-2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4,11.5h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), $a = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.923,10.25a.392.392,0,0,0-.033-.05.5.5,0,0,0-.2-.162l0,0-15-6a.5.5,0,0,0-.372,0l-15,6,0,0a.5.5,0,0,0-.2.162.392.392,0,0,0-.033.05A.478.478,0,0,0,4,10.5v18a.464.464,0,0,0,.028.138.429.429,0,0,0,.011.054l.01.015a.51.51,0,0,0,.1.144l.008,0a.5.5,0,0,0,.156.1l0,0,14.989,6,0,0,.006,0a.5.5,0,0,0,.372,0l.006,0,0,0,1.989-.8a.5.5,0,0,0-.372-.928L20,33.762V16.839l14-5.6V21.5a.5.5,0,0,0,1,0v-11A.481.481,0,0,0,34.923,10.25ZM19,33.762l-14-5.6V11.239l14,5.6Zm.5-17.8L5.846,10.5,19.5,5.039,33.153,10.5Z" style="fill:#b6b6b6"/><path d="M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z" style="fill:#009688"/><path d="M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/></svg>'), Ia = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,22a.5.5,0,0,1-.5-.5V18H18v3.5a.5.5,0,0,1-1,0v-6a.5.5,0,0,1,.084-.277l2-3A.5.5,0,0,1,19.5,12h4a.5.5,0,0,1,.416.223l2,3A.5.5,0,0,1,26,15.5v6A.5.5,0,0,1,25.5,22ZM18,17h7V15.651L23.232,13H19.768L18,15.651Z" style="fill:#009688"/><path d="M11.964,29.314a.5.5,0,0,0-.65-.278L7.152,30.7a17.321,17.321,0,0,1,3.739-6.888.5.5,0,1,0-.782-.624A19.312,19.312,0,0,0,6.359,29.8L4.964,26.314a.5.5,0,1,0-.928.372l2,5c0,.01.016.015.021.025a.475.475,0,0,0,.15.185c.009.007.016.017.025.023a.5.5,0,0,0,.2.076A.367.367,0,0,0,6.5,32h0a.475.475,0,0,0,.181-.036h0l5-2A.5.5,0,0,0,11.964,29.314Z" style="fill:#009688"/><rect x="4" y="34" width="31" height="1" style="fill:#009688"/><path d="M34.854,17.146l-13-13a.5.5,0,0,0-.708,0l-13,13a.5.5,0,0,0,.708.708L21.5,5.207,33.793,17.5,21.146,30.146a.5.5,0,0,0,.708.708l13-13A.5.5,0,0,0,34.854,17.146Z" style="fill:#b6b6b6"/><path d="M21.5,31a.5.5,0,0,1-.354-.146l-13-13a.5.5,0,0,1,.708-.708l13,13A.5.5,0,0,1,21.5,31Z" style="fill:#009688"/></svg>'), Za = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M27.878,34.926h-4.4c-.509,0-.845,0-.955-.394a.593.593,0,0,1,.36-.67,2.3,2.3,0,0,0,1.2-2.189,3.253,3.253,0,1,1,3.8,3.206Zm-3.538-1h2.988a2.253,2.253,0,1,0-2.253-2.253A3.445,3.445,0,0,1,24.34,33.926Z" style="fill:#009688"/><path d="M30.5,30.289l-.846-.535,4.364-6.9a.222.222,0,0,0-.36-.258l-5.4,6.644-.776-.631,5.4-6.644a1.222,1.222,0,0,1,1.982,1.423Z" style="fill:#009688"/><path d="M33.5,35h-1a.5.5,0,0,1,0-1h1a.5.5,0,0,0,.5-.5v-5a.5.5,0,0,1,1,0v5A1.5,1.5,0,0,1,33.5,35Z" style="fill:#b6b6b6"/><path d="M7.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,7.5,35Z" style="fill:#009688"/><path d="M10.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,10.5,35Z" style="fill:#009688"/><path d="M13.5,35h-9a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,.5-.5h9a.5.5,0,0,1,.5.5v20A.5.5,0,0,1,13.5,35ZM5,34h8V15H5Z" style="fill:#009688"/><path d="M22.5,29a.5.5,0,0,1-.5-.5V15H4.5a.5.5,0,0,1-.5-.5v-9A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v13a.5.5,0,0,1-1,0V5.5a.5.5,0,0,0-.5-.5H5.5a.5.5,0,0,0-.5.5V14H22.5a.5.5,0,0,1,.5.5v14A.5.5,0,0,1,22.5,29Z" style="fill:#b6b6b6"/></svg>'), Va = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M35,19.5A15.517,15.517,0,0,1,19.5,35a15.767,15.767,0,0,1-2.063-.136.5.5,0,0,1-.43-.562.507.507,0,0,1,.562-.43A14.5,14.5,0,1,0,19.5,5,14.43,14.43,0,0,0,7.751,11H11.5a.5.5,0,0,1,0,1h-5a.5.5,0,0,1-.5-.5v-5a.5.5,0,0,1,1,0v3.836A15.5,15.5,0,0,1,35,19.5ZM5.943,24.66a.5.5,0,1,0-.933.356A15.426,15.426,0,0,0,7.354,29.13a.5.5,0,0,0,.783-.621A14.533,14.533,0,0,1,5.943,24.66Zm8.4,8.4a14.523,14.523,0,0,1-3.85-2.194.5.5,0,0,0-.621.784,15.455,15.455,0,0,0,4.115,2.343.487.487,0,0,0,.178.033.5.5,0,0,0,.178-.966ZM5.128,21.431A14.621,14.621,0,0,1,5,19.5a.5.5,0,0,0-1,0,15.729,15.729,0,0,0,.136,2.062.5.5,0,0,0,.992-.131Z" style="fill:#009688"/></svg>'), Ga = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,5h-2a.5.5,0,0,1,0-1h2a.5.5,0,0,1,0,1ZM21,4.5a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,4.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,4.5Zm10,30a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,26,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,34.5ZM5,15.5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm30-10v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm-9-1v-10A1.5,1.5,0,0,0,24.5,13h-10A1.5,1.5,0,0,0,13,14.5v10A1.5,1.5,0,0,0,14.5,26h10A1.5,1.5,0,0,0,26,24.5ZM24.5,14a.5.5,0,0,1,.5.5v10a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5v-10a.5.5,0,0,1,.5-.5Z" style="fill:#b6b6b6"/><path d="M34.962,4.309A.5.5,0,0,0,34.5,4h-5a.5.5,0,0,0,0,1h3.793l-6.147,6.146a.5.5,0,0,0,.708.708L34,5.707V9.5a.5.5,0,0,0,1,0v-5A.5.5,0,0,0,34.962,4.309Z" style="fill:#009688"/><path d="M5.707,5H9.5a.5.5,0,0,0,0-1h-5a.5.5,0,0,0-.5.5v5a.5.5,0,0,0,1,0V5.707l6.146,6.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M34.5,29a.5.5,0,0,0-.5.5v3.793l-6.146-6.147a.5.5,0,0,0-.708.708L33.293,34H29.5a.5.5,0,0,0,0,1h5a.5.5,0,0,0,.5-.5v-5A.5.5,0,0,0,34.5,29Z" style="fill:#009688"/><path d="M11.146,27.146,5,33.293V29.5a.5.5,0,0,0-1,0v5a.5.5,0,0,0,.5.5h5a.5.5,0,0,0,0-1H5.707l6.147-6.146a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/></svg>'), Ua = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v2m0,2v2m0,2v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), rr = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v9" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,6.5,4,8l2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,6.5,12,8l-2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Wa = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.5,5h7" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,5v6.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>'), Ha = "data:image/svg+xml;utf8," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M3,4.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,7h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,9h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,10.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,11.6h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>');
27
+ function qa(t) {
27
28
  return [
28
- { id: "add", icon: er, label: "Add", tip: "Add a model file to the current plate (existing objects are kept)", run: n.add },
29
- { id: "delete", icon: Ft, label: "Delete", tip: "Delete the selected object (Del)", run: n.remove },
30
- { id: "delete-all", icon: or, label: "Delete all", tip: "Delete every object on the plate", run: n.removeAll, disabled: () => n.objectCount() === 0 },
29
+ { id: "add", icon: Aa, label: "Add", tip: "Add a model file to the current plate (existing objects are kept)", run: t.add },
30
+ { id: "delete", icon: nr, label: "Delete", tip: "Delete the selected object (Del)", run: t.remove },
31
+ { id: "delete-all", icon: za, label: "Delete all", tip: "Delete every object on the plate", run: t.removeAll, disabled: () => t.objectCount() === 0 },
31
32
  { sep: !0 },
32
- { id: "duplicate", icon: ar, label: "Duplicate", tip: "Duplicate the selected object (Ctrl+K)", run: n.duplicate },
33
- { id: "split", icon: Gt, label: "Split", tip: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)", run: n.split },
34
- { id: "onbed", icon: ur, label: "Place on bed", tip: "Drop every object onto the bed (Z=0) — to re-seat after lifting with the gizmo", run: n.placeOnBed },
33
+ { id: "duplicate", icon: Ra, label: "Duplicate", tip: "Duplicate the selected object (Ctrl+K)", run: t.duplicate },
34
+ { id: "split", icon: rr, label: "Split", tip: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)", run: t.split },
35
+ { id: "onbed", icon: Da, label: "Place on bed", tip: "Drop every object onto the bed (Z=0) — to re-seat after lifting with the gizmo", run: t.placeOnBed },
35
36
  { sep: !0 },
36
- { id: "arrange", icon: tr, label: "Arrange", tip: "Auto arrange — lay objects out on the bed without overlap. Not implemented (needs the libslic3r Arrange port)" },
37
- { id: "orient", icon: fr, label: "Orient", tip: "Auto orient — rotate to the orientation needing the least support. Not implemented (needs the libslic3r Orient port)" },
37
+ { id: "arrange", icon: Ca, label: "Arrange", tip: "Auto arrange — lay objects out on the bed without overlap. Not implemented (needs the libslic3r Arrange port)" },
38
+ { id: "orient", icon: Ia, label: "Orient", tip: "Auto orient — rotate to the orientation needing the least support. Not implemented (needs the libslic3r Orient port)" },
38
39
  { sep: !0 },
39
- { id: "cut", icon: rr, label: "Cut", tip: "Cut — slice the model with a plane into two pieces. Not implemented (needs cut-surface re-stitching)" },
40
- { id: "boolean", icon: nr, label: "Boolean", tip: "Mesh boolean — union/difference/intersection of two objects. Not implemented (needs the CGAL boolean port)" },
41
- { id: "negative", icon: pr, label: "Negative part", tip: "Add a negative/modifier part — put a part with different properties inside one object. Not implemented (no part concept)" },
40
+ { id: "cut", icon: Pa, label: "Cut", tip: "Cut — slice the model with a plane into two pieces. Not implemented (needs cut-surface re-stitching)" },
41
+ { id: "boolean", icon: ja, label: "Boolean", tip: "Mesh boolean — union/difference/intersection of two objects. Not implemented (needs the CGAL boolean port)" },
42
+ { id: "negative", icon: Ba, label: "Negative part", tip: "Add a negative/modifier part — put a part with different properties inside one object. Not implemented (no part concept)" },
42
43
  { sep: !0 },
43
- { id: "seam", icon: xr, label: "Seam painting", tip: "Seam painting — brush where the layer seam goes. Not implemented (needs kernel seam wiring)" },
44
- { id: "mmu", icon: cr, label: "Color painting", tip: "Color painting — assign multi-material colors per facet. Not implemented (needs the MMU paint codec wiring)" },
45
- { id: "text", icon: yr, label: "Text", tip: "Text/SVG emboss — engrave letters or shapes onto the model surface. Not implemented (needs font rasterization)" },
46
- { id: "measure", icon: ir, label: "Measure", tip: "Measure — distance and angle between two points or faces. Not implemented" },
47
- { id: "varlayer", icon: vr, label: "Variable layers", tip: "Variable layer height — different layer heights per band. Not implemented (the kernel has no variable z)" }
44
+ { id: "seam", icon: Ua, label: "Seam painting", tip: "Seam painting — brush where the layer seam goes. Not implemented (needs kernel seam wiring)" },
45
+ { id: "mmu", icon: Sa, label: "Color painting", tip: "Color painting — assign multi-material colors per facet. Not implemented (needs the MMU paint codec wiring)" },
46
+ { id: "text", icon: Wa, label: "Text", tip: "Text/SVG emboss — engrave letters or shapes onto the model surface. Not implemented (needs font rasterization)" },
47
+ { id: "measure", icon: La, label: "Measure", tip: "Measure — distance and angle between two points or faces. Not implemented" },
48
+ { id: "varlayer", icon: Ha, label: "Variable layers", tip: "Variable layer height — different layer heights per band. Not implemented (the kernel has no variable z)" }
48
49
  ];
49
50
  }
50
- function kr(n) {
51
- return (a) => {
52
- var w;
53
- const c = a.composedPath ? a.composedPath()[0] : a.target;
51
+ function Xa(t) {
52
+ return (r) => {
53
+ var j;
54
+ const c = r.composedPath ? r.composedPath()[0] : r.target;
54
55
  if (c && (c.tagName === "INPUT" || c.tagName === "TEXTAREA" || c.tagName === "SELECT" || c.isContentEditable)) return;
55
- const l = a.key, r = typeof l == "string" ? l.toLowerCase() : "", m = a.ctrlKey || a.metaKey, d = () => {
56
- a.preventDefault(), a.stopPropagation();
56
+ const i = r.key, o = typeof i == "string" ? i.toLowerCase() : "", p = r.ctrlKey || r.metaKey, h = () => {
57
+ r.preventDefault(), r.stopPropagation();
57
58
  };
58
- if (m) {
59
- r === "r" ? (d(), n.slicing || n.slice("current")) : r === "c" ? (d(), n.copy()) : r === "v" ? (d(), n.paste()) : r === "x" ? (d(), n.copy(), n.remove()) : (r === "k" || r === "d") && (d(), n.duplicate());
59
+ if (p) {
60
+ o === "r" ? (h(), t.slicing || t.slice("current")) : o === "c" ? (h(), t.copy()) : o === "v" ? (h(), t.paste()) : o === "x" ? (h(), t.copy(), t.remove()) : (o === "k" || o === "d") && (h(), t.duplicate());
60
61
  return;
61
62
  }
62
- if (n.isPreview()) {
63
- const b = a.shiftKey ? 10 : 1;
64
- l === "ArrowUp" ? (d(), n.stepLayer(b)) : l === "ArrowDown" ? (d(), n.stepLayer(-b)) : r === "l" ? (d(), n.toggleSingleLayer()) : r === "t" ? (d(), n.toggleTravel()) : r === "z" ? (d(), n.zoomAll()) : r === "b" ? (d(), n.zoomBed()) : l === "Escape" && (d(), n.leavePreview());
63
+ if (t.isPreview()) {
64
+ const x = r.shiftKey ? 10 : 1;
65
+ i === "ArrowUp" ? (h(), t.stepLayer(x)) : i === "ArrowDown" ? (h(), t.stepLayer(-x)) : o === "l" ? (h(), t.toggleSingleLayer()) : o === "t" ? (h(), t.toggleTravel()) : o === "z" ? (h(), t.zoomAll()) : o === "b" ? (h(), t.zoomBed()) : i === "Escape" && (h(), t.leavePreview());
65
66
  return;
66
67
  }
67
- if (r === "m" || r === "g")
68
- d(), n.setGizmo("translate");
69
- else if (r === "r")
70
- d(), n.setGizmo("rotate");
71
- else if (r === "s")
72
- d(), n.setGizmo("scale");
73
- else if (r === "z")
74
- d(), n.zoomAll();
75
- else if (r === "b")
76
- d(), n.zoomBed();
77
- else if (l === "Delete" || l === "Backspace")
78
- d(), n.remove();
79
- else if (l === "Escape")
80
- d(), n.cancelTool();
81
- else if (l === "PageUp")
82
- d(), n.rotateSelected(Math.PI / 4);
83
- else if (l === "PageDown")
84
- d(), n.rotateSelected(-Math.PI / 4);
85
- else if ((w = l.startsWith) != null && w.call(l, "Arrow")) {
86
- d();
87
- const b = a.shiftKey ? 1 : 10;
88
- n.nudgeSelected(
89
- l === "ArrowLeft" ? -b : l === "ArrowRight" ? b : 0,
90
- l === "ArrowUp" ? -b : l === "ArrowDown" ? b : 0
68
+ if (o === "m" || o === "g")
69
+ h(), t.setGizmo("translate");
70
+ else if (o === "r")
71
+ h(), t.setGizmo("rotate");
72
+ else if (o === "s")
73
+ h(), t.setGizmo("scale");
74
+ else if (o === "z")
75
+ h(), t.zoomAll();
76
+ else if (o === "b")
77
+ h(), t.zoomBed();
78
+ else if (i === "Delete" || i === "Backspace")
79
+ h(), t.remove();
80
+ else if (i === "Escape")
81
+ h(), t.cancelTool();
82
+ else if (i === "PageUp")
83
+ h(), t.rotateSelected(Math.PI / 4);
84
+ else if (i === "PageDown")
85
+ h(), t.rotateSelected(-Math.PI / 4);
86
+ else if ((j = i.startsWith) != null && j.call(i, "Arrow")) {
87
+ h();
88
+ const x = r.shiftKey ? 1 : 10;
89
+ t.nudgeSelected(
90
+ i === "ArrowLeft" ? -x : i === "ArrowRight" ? x : 0,
91
+ i === "ArrowUp" ? -x : i === "ArrowDown" ? x : 0
91
92
  );
92
- } else l === "?" && (d(), n.toggleHelp());
93
+ } else i === "?" && (h(), t.toggleHelp());
93
94
  };
94
95
  }
95
- function Er(n, a) {
96
- const c = n.geometry, l = c == null ? void 0 : c.getAttribute("position");
97
- if (!l) return null;
98
- const r = c.getIndex(), m = (r ? r.count : l.count) / 3, d = Math.cos(Math.max(0, Math.min(90, a)) * Math.PI / 180), w = new N.Matrix3().getNormalMatrix(n.matrixWorld), b = new N.Vector3(), T = new N.Vector3(), P = new N.Vector3(), M = new N.Vector3(), B = new N.Vector3(), ee = new N.Vector3(), L = [];
99
- for (let I = 0; I < m; I++) {
100
- const A = r ? r.getX(I * 3) : I * 3, u = r ? r.getX(I * 3 + 1) : I * 3 + 1, _ = r ? r.getX(I * 3 + 2) : I * 3 + 2;
101
- b.fromBufferAttribute(l, A), T.fromBufferAttribute(l, u), P.fromBufferAttribute(l, _), ee.copy(M.subVectors(T, b)).cross(B.subVectors(P, b)), ee.lengthSq() !== 0 && (ee.applyMatrix3(w).normalize(), !(-ee.y <= d) && L.push(b.x, b.y, b.z, T.x, T.y, T.z, P.x, P.y, P.z));
102
- }
103
- if (!L.length) return null;
104
- const te = new N.BufferGeometry();
105
- return te.setAttribute("position", new N.Float32BufferAttribute(L, 3)), te.computeVertexNormals(), te;
96
+ function Ya(t, r) {
97
+ const c = t.geometry, i = c == null ? void 0 : c.getAttribute("position");
98
+ if (!i) return null;
99
+ const o = c.getIndex(), p = (o ? o.count : i.count) / 3, h = Math.cos(Math.max(0, Math.min(90, r)) * Math.PI / 180), j = new W.Matrix3().getNormalMatrix(t.matrixWorld), x = new W.Vector3(), B = new W.Vector3(), z = new W.Vector3(), m = new W.Vector3(), L = new W.Vector3(), D = new W.Vector3(), R = [];
100
+ for (let J = 0; J < p; J++) {
101
+ const _ = o ? o.getX(J * 3) : J * 3, T = o ? o.getX(J * 3 + 1) : J * 3 + 1, F = o ? o.getX(J * 3 + 2) : J * 3 + 2;
102
+ x.fromBufferAttribute(i, _), B.fromBufferAttribute(i, T), z.fromBufferAttribute(i, F), D.copy(m.subVectors(B, x)).cross(L.subVectors(z, x)), D.lengthSq() !== 0 && (D.applyMatrix3(j).normalize(), !(-D.y <= h) && R.push(x.x, x.y, x.z, B.x, B.y, B.z, z.x, z.y, z.z));
103
+ }
104
+ if (!R.length) return null;
105
+ const K = new W.BufferGeometry();
106
+ return K.setAttribute("position", new W.Float32BufferAttribute(R, 3)), K.computeVertexNormals(), K;
106
107
  }
107
- function Mr(n) {
108
- const a = n.length, c = new Float32Array(a);
109
- for (let M = 0; M < a; M += 3)
110
- c[M] = n[M], c[M + 1] = n[M + 2], c[M + 2] = -n[M + 1];
111
- let l = 1 / 0, r = 1 / 0, m = 1 / 0, d = -1 / 0, w = -1 / 0, b = -1 / 0;
112
- for (let M = 0; M < a; M += 3)
113
- l = Math.min(l, c[M]), d = Math.max(d, c[M]), r = Math.min(r, c[M + 1]), w = Math.max(w, c[M + 1]), m = Math.min(m, c[M + 2]), b = Math.max(b, c[M + 2]);
114
- const T = (l + d) / 2, P = (m + b) / 2;
115
- for (let M = 0; M < a; M += 3)
116
- c[M] -= T, c[M + 1] -= r, c[M + 2] -= P;
117
- return { localPos: c, size: { w: d - l, d: b - m, h: w - r } };
108
+ function Ka(t) {
109
+ const r = t.length, c = new Float32Array(r);
110
+ for (let m = 0; m < r; m += 3)
111
+ c[m] = t[m], c[m + 1] = t[m + 2], c[m + 2] = -t[m + 1];
112
+ let i = 1 / 0, o = 1 / 0, p = 1 / 0, h = -1 / 0, j = -1 / 0, x = -1 / 0;
113
+ for (let m = 0; m < r; m += 3)
114
+ i = Math.min(i, c[m]), h = Math.max(h, c[m]), o = Math.min(o, c[m + 1]), j = Math.max(j, c[m + 1]), p = Math.min(p, c[m + 2]), x = Math.max(x, c[m + 2]);
115
+ const B = (i + h) / 2, z = (p + x) / 2;
116
+ for (let m = 0; m < r; m += 3)
117
+ c[m] -= B, c[m + 1] -= o, c[m + 2] -= z;
118
+ return { localPos: c, size: { w: h - i, d: x - p, h: j - o } };
118
119
  }
119
- function jr(n) {
120
+ function Ja(t) {
120
121
  const {
121
- apiRef: a,
122
+ apiRef: r,
122
123
  objectsRef: c,
123
- keyRef: l,
124
- workerRef: r,
125
- selectedPlateRef: m,
126
- placeXRef: d,
127
- plateCountRef: w,
128
- canvasModeRef: b,
129
- paintModeRef: T,
130
- brushRadiusRef: P,
131
- paintXformRef: M,
132
- extruderColorsRef: B,
133
- setOk: ee,
134
- setStatus: L,
135
- setGmode: te,
136
- setCtxMenu: I,
137
- setBrushRadius: A
138
- } = n, u = H(null), _ = H({}), G = H(!1), K = H(200), h = H(200);
139
- function E(j) {
140
- const f = gt(w.current);
141
- return { x: j % f * Ye(K.current), z: Math.floor(j / f) * Ye(h.current) };
142
- }
143
- function k(j, f) {
144
- const g = w.current, y = gt(g), v = Math.max(0, Math.min(y - 1, Math.round(j / Ye(K.current)))), D = Math.max(0, Math.round(f / Ye(h.current)));
145
- return Math.max(0, Math.min(g - 1, D * y + v));
146
- }
147
- return Ve(() => {
148
- const j = u.current;
149
- if (!j) return;
150
- const f = document.createElement("canvas");
151
- if (!(f.getContext("webgl2") || f.getContext("webgl"))) {
152
- ee(!1), L("Cannot create a WebGL context in this environment (headless / no GPU).");
124
+ keyRef: i,
125
+ workerRef: o,
126
+ selectedPlateRef: p,
127
+ placeXRef: h,
128
+ plateCountRef: j,
129
+ canvasModeRef: x,
130
+ paintModeRef: B,
131
+ brushRadiusRef: z,
132
+ paintToolRef: m,
133
+ paintXformRef: L,
134
+ extruderColorsRef: D,
135
+ setOk: R,
136
+ setStatus: K,
137
+ setGmode: J,
138
+ setCtxMenu: _,
139
+ setBrushRadius: T
140
+ } = t, F = ne(null), ce = ne({}), pe = ne(!1), w = ne(200), S = ne(200);
141
+ function E(y) {
142
+ const v = Ft(j.current);
143
+ return { x: y % v * rt(w.current), z: Math.floor(y / v) * rt(S.current) };
144
+ }
145
+ function M(y, v) {
146
+ const C = j.current, V = Ft(C), f = Math.max(0, Math.min(V - 1, Math.round(y / rt(w.current)))), N = Math.max(0, Math.round(v / rt(S.current)));
147
+ return Math.max(0, Math.min(C - 1, N * V + f));
148
+ }
149
+ return Te(() => {
150
+ const y = F.current;
151
+ if (!y) return;
152
+ const v = document.createElement("canvas");
153
+ if (!(v.getContext("webgl2") || v.getContext("webgl"))) {
154
+ R(!1), K("Cannot create a WebGL context in this environment (headless / no GPU).");
153
155
  return;
154
156
  }
155
- let g = j.clientWidth || 800, y = j.clientHeight || 480;
156
- const v = new N.WebGLRenderer({ antialias: !0 });
157
- v.setPixelRatio(Math.min(window.devicePixelRatio, 2)), v.setSize(g, y), v.setClearColor(1448478, 1), v.domElement.setAttribute("data-webgl", v.getContext() ? "ok" : "fail"), j.appendChild(v.domElement);
158
- const D = new N.Scene(), W = new N.PerspectiveCamera(50, g / y, 1, 3e3);
159
- W.position.set(210, 180, 260), D.add(new N.HemisphereLight(16777215, 2764598, 1));
160
- const oe = new N.DirectionalLight(16777215, 1.1);
161
- oe.position.set(120, 220, 160), D.add(oe);
162
- const ge = new N.Group();
163
- D.add(ge);
164
- const de = new N.Group();
165
- D.add(de);
166
- const Y = new Un(W, v.domElement);
167
- Y.target.set(0, 22, 0), Y.enableDamping = !1, Y.update(), Y.rotateSpeed = 1.6, Y.panSpeed = 1.6;
168
- const C = new $n(W, v.domElement);
169
- C.setMode("translate"), C.setSize(0.8);
170
- const pe = new N.Box3(), je = (t) => {
171
- if (!t) return;
172
- t.updateMatrixWorld(!0), pe.setFromObject(t);
173
- const s = pe.min.y;
174
- Number.isFinite(s) && Math.abs(s) > 1e-4 && (t.position.y -= s, t.updateMatrixWorld(!0));
157
+ let C = y.clientWidth || 800, V = y.clientHeight || 480;
158
+ const f = new W.WebGLRenderer({ antialias: !0 });
159
+ f.setPixelRatio(Math.min(window.devicePixelRatio, 2)), f.setSize(C, V), f.setClearColor(1448478, 1), f.domElement.setAttribute("data-webgl", f.getContext() ? "ok" : "fail"), y.appendChild(f.domElement);
160
+ const N = new W.Scene(), O = new W.PerspectiveCamera(50, C / V, 1, 3e3);
161
+ O.position.set(210, 180, 260), N.add(new W.HemisphereLight(16777215, 2764598, 1));
162
+ const H = new W.DirectionalLight(16777215, 1.1);
163
+ H.position.set(120, 220, 160), N.add(H);
164
+ const oe = new W.Group();
165
+ N.add(oe);
166
+ const ue = new W.Group();
167
+ N.add(ue);
168
+ const $ = new ga(O, f.domElement);
169
+ $.target.set(0, 22, 0), $.enableDamping = !1, $.update(), $.rotateSpeed = 1.6, $.panSpeed = 1.6;
170
+ const P = new ya(O, f.domElement);
171
+ P.setMode("translate"), P.setSize(0.8);
172
+ const ve = new W.Box3(), Me = (a) => {
173
+ if (!a) return;
174
+ a.updateMatrixWorld(!0), ve.setFromObject(a);
175
+ const l = ve.min.y;
176
+ Number.isFinite(l) && Math.abs(l) > 1e-4 && (a.position.y -= l, a.updateMatrixWorld(!0));
177
+ }, ge = new W.Matrix4(), Ee = new W.Matrix4(), b = /* @__PURE__ */ new Map();
178
+ let k = !1;
179
+ P.addEventListener("objectChange", () => {
180
+ if (!(!k || !P.object)) {
181
+ P.object.updateMatrixWorld(!0), Ee.copy(P.object.matrixWorld).multiply(ge);
182
+ for (const [a, l] of b)
183
+ a.matrixAutoUpdate = !1, a.matrix.multiplyMatrices(Ee, l);
184
+ }
185
+ }), P.addEventListener("dragging-changed", (a) => {
186
+ var l, U, Y, re, X, ke, ze;
187
+ if ($.enabled = !a.value, a.value) {
188
+ b.clear();
189
+ const Ce = (l = t.paintOverlayRef) == null ? void 0 : l.current;
190
+ if (k = !!(Ce && Ce.size && P.object && !((U = P.object.userData) != null && U.isPrimeTower)), k) {
191
+ P.object.updateMatrixWorld(!0), ge.copy(P.object.matrixWorld).invert();
192
+ for (const be of Ce.values()) b.set(be, be.matrix.clone());
193
+ }
194
+ } else k = !1;
195
+ if (!a.value)
196
+ if (P.object && ((Y = P.object.userData) != null && Y.isPrimeTower)) {
197
+ P.object.position.y = P.object.userData.halfHeight;
198
+ const Ce = P.object.scale.x / 2;
199
+ (re = t.onTowerMoved) == null || re.call(t, P.object.position.x - Ce, -P.object.position.z - Ce);
200
+ } else
201
+ Me(P.object), (X = t.onTransformCommitted) == null || X.call(t);
202
+ (ze = (ke = ce.current).invalidate) == null || ze.call(ke);
203
+ }), N.add(P), ce.current = { scene: N, camera: O, renderer: f, orbit: $, transform: P, objectsGroup: oe, toolpathGroup: ue, plateBeds: [] }, typeof window < "u" && (window.__vpThree = ce.current, window.__vpApi = () => r.current);
204
+ let G = !0;
205
+ const q = () => {
206
+ G = !0;
175
207
  };
176
- C.addEventListener("dragging-changed", (t) => {
177
- var s, R;
178
- Y.enabled = !t.value, t.value || je(C.object), (R = (s = _.current).invalidate) == null || R.call(s);
179
- }), D.add(C), _.current = { scene: D, camera: W, renderer: v, orbit: Y, transform: C, objectsGroup: ge, toolpathGroup: de, plateBeds: [] }, typeof window < "u" && (window.__vpThree = _.current, window.__vpApi = () => a.current);
180
- let Ce = !0;
208
+ ce.current.invalidate = q, $.addEventListener("change", q), P.addEventListener("change", q), f.domElement.addEventListener("pointermove", q);
209
+ const s = new W.Raycaster(), d = new W.Vector2();
210
+ let A = null, u = null, I = 0, se = null;
181
211
  const ye = () => {
182
- Ce = !0;
183
- };
184
- _.current.invalidate = ye, Y.addEventListener("change", ye), C.addEventListener("change", ye), v.domElement.addEventListener("pointermove", ye);
185
- const Re = new N.Raycaster(), be = new N.Vector2();
186
- let Se = null, i = null, S = 0;
187
- const V = () => c.current.map((t) => t.mesh), p = () => {
188
- for (const t of V()) t.material.emissive.setHex(t === i ? 44610 : t === Se ? 2055220 : 0);
189
- }, O = () => c.current.length ? `${c.current.length} object(s) · selected: ${i ? i.userData.name : "—"} | M/R/S · left-drag to orbit · ? for shortcuts` : `hover: ${Se ? Se.userData.name : "—"} · selected: ${i ? i.userData.name : "—"} | left-drag to orbit · ? for shortcuts`, ue = (t) => {
190
- const s = v.domElement.getBoundingClientRect();
191
- be.x = (t.clientX - s.left) / s.width * 2 - 1, be.y = -((t.clientY - s.top) / s.height) * 2 + 1;
192
- }, ve = () => {
193
- Re.setFromCamera(be, W);
194
- const t = Re.intersectObjects(V(), !1);
195
- return t.length ? t[0].object : null;
196
- }, Ee = () => {
197
- Re.setFromCamera(be, W);
198
- const t = Re.intersectObjects(V(), !1);
199
- return t.length ? t[0] : null;
200
- }, Z = (t) => {
201
- var me;
202
- const s = M.current;
203
- if (!s) return;
204
- ue(t);
205
- const R = Ee();
206
- if (!R || R.faceIndex == null) return;
207
- const F = (ne) => [ne.x - s.cx, -ne.z - s.cy, ne.y - s.minz], re = F(R.point), U = F(W.position);
208
- (me = r.current) == null || me.postMessage({
212
+ const a = c.current.map((l) => l.mesh);
213
+ return se && se.visible && a.push(se), a;
214
+ }, xe = () => {
215
+ var a;
216
+ for (const l of ye())
217
+ l.material.emissive ? l.material.emissive.setHex(l === u ? 44610 : l === A ? 2055220 : 0) : (a = l.userData) != null && a.isPrimeTower && (l.material.opacity = l === u ? 0.6 : l === A ? 0.5 : 0.35);
218
+ }, je = () => c.current.length ? `${c.current.length} object(s) · selected: ${u ? u.userData.name : "—"} | M/R/S · left-drag to orbit · ? for shortcuts` : `hover: ${A ? A.userData.name : "—"} · selected: ${u ? u.userData.name : "—"} | left-drag to orbit · ? for shortcuts`, Q = (a) => {
219
+ const l = f.domElement.getBoundingClientRect();
220
+ d.x = (a.clientX - l.left) / l.width * 2 - 1, d.y = -((a.clientY - l.top) / l.height) * 2 + 1;
221
+ }, Z = () => {
222
+ s.setFromCamera(d, O);
223
+ const a = s.intersectObjects(ye(), !1);
224
+ return a.length ? a[0].object : null;
225
+ }, ee = () => {
226
+ s.setFromCamera(d, O);
227
+ const a = s.intersectObjects(ye(), !1);
228
+ return a.length ? a[0] : null;
229
+ }, he = (a) => {
230
+ var ze;
231
+ const l = L.current;
232
+ if (!l) return;
233
+ Q(a);
234
+ const U = ee();
235
+ if (!U || U.faceIndex == null) return;
236
+ const Y = (Ce) => [Ce.x - l.cx, -Ce.z - l.cy, Ce.y - l.minz], re = Y(U.point), X = Y(O.position), ke = (m == null ? void 0 : m.current) ?? { tool: "brush", cursor: "sphere", angle: 30 };
237
+ (ze = o.current) == null || ze.postMessage({
209
238
  cmd: "paint",
210
- facet: R.faceIndex,
239
+ facet: U.faceIndex,
211
240
  hx: re[0],
212
241
  hy: re[1],
213
242
  hz: re[2],
214
- cx: U[0],
215
- cy: U[1],
216
- cz: U[2],
217
- radius: P.current,
218
- enforcer: T.current === "enforcer"
243
+ cx: X[0],
244
+ cy: X[1],
245
+ cz: X[2],
246
+ radius: z.current,
247
+ enforcer: B.current === "enforcer",
248
+ tool: ke.tool,
249
+ cursor: ke.cursor,
250
+ angle: ke.angle
219
251
  });
220
- }, q = () => {
221
- const t = v.domElement;
222
- t.style.cursor = b.current === "preview" ? "" : T.current !== "off" ? "crosshair" : Se ? "pointer" : "";
223
- }, z = (t) => {
224
- if (b.current === "preview") return;
225
- if (T.current !== "off") {
226
- G.current && Z(t);
252
+ };
253
+ let me = null, fe = 0;
254
+ const le = () => {
255
+ fe = 0;
256
+ const a = me;
257
+ me = null, a && he(a);
258
+ }, Ae = (a) => {
259
+ me = a, fe || (fe = requestAnimationFrame(le));
260
+ }, Pe = () => {
261
+ var l;
262
+ const a = (l = m == null ? void 0 : m.current) == null ? void 0 : l.tool;
263
+ return a === "smart" || a === "bucket" || a === "triangle";
264
+ }, Re = () => {
265
+ const a = f.domElement;
266
+ a.style.cursor = x.current === "preview" ? "" : B.current !== "off" ? "crosshair" : A ? "pointer" : "";
267
+ }, qe = (a) => {
268
+ if (x.current === "preview") return;
269
+ if (B.current !== "off") {
270
+ pe.current && !Pe() && Ae(a);
227
271
  return;
228
272
  }
229
- if (C.dragging || C.axis) return;
230
- ue(t);
231
- const s = ve();
232
- s !== Se && (Se = s, p(), q(), L(O()));
233
- }, ie = (t) => {
234
- if (t.button !== 0 || b.current === "preview") return;
235
- if (T.current !== "off") {
236
- G.current = !0, Y.enabled = !1, Z(t);
273
+ if (P.dragging || P.axis) return;
274
+ Q(a);
275
+ const l = Z();
276
+ l !== A && (A = l, xe(), Re(), K(je()));
277
+ }, dt = (a) => {
278
+ if (a.button !== 0 || x.current === "preview") return;
279
+ if (B.current !== "off") {
280
+ pe.current = !0, $.enabled = !1, he(a);
237
281
  return;
238
282
  }
239
- if (C.dragging || C.axis) return;
240
- ue(t);
241
- const s = ve();
242
- s ? (i = s, C.attach(s)) : (i = null, C.detach()), p(), L(O());
243
- }, we = () => {
244
- G.current && (G.current = !1, Y.enabled = !0);
245
- }, se = (t) => {
246
- t.button !== 0 || b.current === "preview" || T.current !== "off" || (ue(t), ve() ? Fe() : (i = null, C.detach(), p(), L(O())));
247
- }, ae = (t) => {
248
- if (T.current === "off" || b.current === "preview") return;
249
- t.preventDefault(), t.stopPropagation();
250
- const s = Math.min(15, Math.max(1, P.current + (t.deltaY < 0 ? 0.5 : -0.5)));
251
- P.current = s, A(s);
283
+ if (P.dragging || P.axis) return;
284
+ Q(a);
285
+ const l = Z();
286
+ l ? (u = l, P.attach(l)) : (u = null, P.detach()), xe(), K(je());
287
+ }, Xe = () => {
288
+ pe.current && (pe.current = !1, $.enabled = !0, fe && (cancelAnimationFrame(fe), le()));
289
+ }, Je = (a) => {
290
+ a.button !== 0 || x.current === "preview" || B.current !== "off" || (Q(a), Z() ? mt() : (u = null, P.detach(), xe(), K(je())));
291
+ }, Nt = (a) => {
292
+ if (B.current === "off" || x.current === "preview" || Pe()) return;
293
+ a.preventDefault(), a.stopPropagation();
294
+ const l = Math.min(15, Math.max(1, z.current + (a.deltaY < 0 ? 0.5 : -0.5)));
295
+ z.current = l, T(l);
252
296
  };
253
- let J = null;
254
- const ce = (t) => {
255
- t.button === 2 && (J = { x: t.clientX, y: t.clientY });
256
- }, ke = (t) => {
257
- if (t.preventDefault(), b.current === "preview" || T.current !== "off" || J && Math.hypot(t.clientX - J.x, t.clientY - J.y) > 4) return;
258
- ue(t);
259
- const s = ve();
260
- s ? (i = s, C.attach(s)) : (i = null, C.detach()), p(), L(O());
261
- const R = v.domElement.getBoundingClientRect();
262
- I({ x: t.clientX - R.left, y: t.clientY - R.top, onObject: !!s });
297
+ let $e = null;
298
+ const Mt = (a) => {
299
+ a.button === 2 && ($e = { x: a.clientX, y: a.clientY });
300
+ }, At = (a) => {
301
+ if (a.preventDefault(), x.current === "preview" || B.current !== "off" || $e && Math.hypot(a.clientX - $e.x, a.clientY - $e.y) > 4) return;
302
+ Q(a);
303
+ const l = Z();
304
+ l ? (u = l, P.attach(l)) : (u = null, P.detach()), xe(), K(je());
305
+ const U = f.domElement.getBoundingClientRect();
306
+ _({ x: a.clientX - U.left, y: a.clientY - U.top, onObject: !!l });
263
307
  };
264
- v.domElement.addEventListener("pointermove", z), v.domElement.addEventListener("pointerdown", ie), v.domElement.addEventListener("pointerdown", ce), v.domElement.addEventListener("contextmenu", ke), v.domElement.addEventListener("dblclick", se), v.domElement.addEventListener("wheel", ae, { passive: !1, capture: !0 }), window.addEventListener("pointerup", we), window.addEventListener("pointercancel", we);
265
- const ze = (t) => {
266
- C.setMode(t), te(t);
267
- }, Fe = () => {
268
- const t = c.current, s = new N.Box3();
269
- t.length ? t.forEach((U) => s.expandByObject(U.mesh)) : s.setFromCenterAndSize(new N.Vector3(0, 25, 0), new N.Vector3(100, 50, 100));
270
- const R = s.getCenter(new N.Vector3()), F = s.getSize(new N.Vector3()), re = Math.max(F.x, F.y, F.z, 20) * 1.9 + 40;
271
- Y.target.copy(R), W.position.set(R.x + re * 0.7, R.y + re * 0.55 + F.y * 0.3, R.z + re), W.updateProjectionMatrix(), Y.update();
272
- }, qe = (t, s, R = null, F = null, re = null) => {
273
- const U = new N.BufferGeometry();
274
- U.setAttribute("position", new N.Float32BufferAttribute(s, 3)), U.computeVertexNormals(), U.computeBoundingBox();
275
- const me = B.current[0] || "#6aa0dc", ne = new N.Mesh(U, new N.MeshStandardMaterial({ color: new N.Color(me), roughness: 0.6, metalness: 0.05, side: N.DoubleSide }));
276
- if (R && ne.rotation.copy(R), F && ne.scale.copy(F), re)
277
- ne.position.copy(re);
308
+ f.domElement.addEventListener("pointermove", qe), f.domElement.addEventListener("pointerdown", dt), f.domElement.addEventListener("pointerdown", Mt), f.domElement.addEventListener("contextmenu", At), f.domElement.addEventListener("dblclick", Je), f.domElement.addEventListener("wheel", Nt, { passive: !1, capture: !0 }), window.addEventListener("pointerup", Xe), window.addEventListener("pointercancel", Xe);
309
+ const Ct = (a) => {
310
+ P.setMode(a), J(a);
311
+ }, mt = () => {
312
+ const a = c.current, l = new W.Box3();
313
+ a.length ? a.forEach((X) => l.expandByObject(X.mesh)) : l.setFromCenterAndSize(new W.Vector3(0, 25, 0), new W.Vector3(100, 50, 100));
314
+ const U = l.getCenter(new W.Vector3()), Y = l.getSize(new W.Vector3()), re = Math.max(Y.x, Y.y, Y.z, 20) * 1.9 + 40;
315
+ $.target.copy(U), O.position.set(U.x + re * 0.7, U.y + re * 0.55 + Y.y * 0.3, U.z + re), O.updateProjectionMatrix(), $.update();
316
+ }, ft = (a, l, U = null, Y = null, re = null) => {
317
+ const X = new W.BufferGeometry();
318
+ X.setAttribute("position", new W.Float32BufferAttribute(l, 3)), X.computeVertexNormals(), X.computeBoundingBox();
319
+ const ke = D.current[0] || "#6aa0dc", ze = new W.Mesh(X, new W.MeshStandardMaterial({ color: new W.Color(ke), roughness: 0.6, metalness: 0.05, side: W.DoubleSide }));
320
+ if (U && ze.rotation.copy(U), Y && ze.scale.copy(Y), re)
321
+ ze.position.copy(re);
278
322
  else {
279
- const Ne = (U.boundingBox.max.x - U.boundingBox.min.x) * (F ? Math.abs(F.x) : 1);
280
- c.current.length === 0 && (d.current = 0);
281
- const Pe = E(m.current);
282
- ne.position.set(Pe.x + d.current + Ne / 2, 0, Pe.z), d.current += Ne + 8;
323
+ const be = (X.boundingBox.max.x - X.boundingBox.min.x) * (Y ? Math.abs(Y.x) : 1);
324
+ c.current.length === 0 && (h.current = 0);
325
+ const Se = E(p.current);
326
+ ze.position.set(Se.x + h.current + be / 2, 0, Se.z), h.current += be + 8;
283
327
  }
284
- ne.userData = { name: t }, ge.add(ne);
285
- const Me = ++S;
286
- return c.current.push({ id: Me, name: t, mesh: ne, localPos: s, extruder: 1, visible: !0 }), ge.visible = !0, Ie != null && We(), L(O()), Fe(), { id: Me, name: t };
328
+ ze.userData = { name: a }, oe.add(ze);
329
+ const Ce = ++I;
330
+ return c.current.push({ id: Ce, name: a, mesh: ze, localPos: l, extruder: 1, visible: !0 }), oe.visible = !0, at != null && ht(), K(je()), mt(), { id: Ce, name: a };
287
331
  };
288
- let Ie = null;
289
- function We() {
290
- for (const t of c.current) {
291
- const s = t.mesh.children.find((re) => {
292
- var U;
293
- return (U = re.userData) == null ? void 0 : U.overhang;
332
+ let at = null;
333
+ function ht() {
334
+ for (const a of c.current) {
335
+ const l = a.mesh.children.find((re) => {
336
+ var X;
337
+ return (X = re.userData) == null ? void 0 : X.overhang;
294
338
  });
295
- if (s && (t.mesh.remove(s), s.geometry.dispose(), s.material.dispose()), Ie == null) continue;
296
- t.mesh.updateWorldMatrix(!0, !1);
297
- const R = Er(t.mesh, Ie);
298
- if (!R) continue;
299
- const F = new N.Mesh(R, new N.MeshBasicMaterial({
339
+ if (l && (a.mesh.remove(l), l.geometry.dispose(), l.material.dispose()), at == null) continue;
340
+ a.mesh.updateWorldMatrix(!0, !1);
341
+ const U = Ya(a.mesh, at);
342
+ if (!U) continue;
343
+ const Y = new W.Mesh(U, new W.MeshBasicMaterial({
300
344
  color: 16729139,
301
345
  transparent: !0,
302
346
  opacity: 0.7,
303
- side: N.DoubleSide,
347
+ side: W.DoubleSide,
304
348
  depthWrite: !1,
305
349
  polygonOffset: !0,
306
350
  polygonOffsetFactor: -1,
307
351
  polygonOffsetUnits: -1
308
352
  }));
309
- F.userData.overhang = !0, t.mesh.add(F);
353
+ Y.userData.overhang = !0, a.mesh.add(Y);
310
354
  }
311
- ye();
355
+ q();
312
356
  }
313
- C.addEventListener("dragging-changed", (t) => {
314
- !t.value && Ie != null && We();
315
- }), a.current = {
316
- setMode: ze,
357
+ P.addEventListener("dragging-changed", (a) => {
358
+ !a.value && at != null && ht();
359
+ }), r.current = {
360
+ setMode: Ct,
317
361
  /** Highlight facets below `thresholdDeg` (the support threshold angle); null turns the shading off. */
318
- setOverhang: (t) => {
319
- Ie = t, We();
362
+ setOverhang: (a) => {
363
+ at = a, ht();
320
364
  },
321
- refreshCursor: () => q(),
365
+ refreshCursor: () => Re(),
322
366
  // keeps the cursor hint in sync when the paint mode changes
323
367
  detachTransform: () => {
324
- i = null, C.detach(), p();
368
+ u = null, P.detach(), xe();
325
369
  },
326
370
  // stage 20: release the gizmo when entering painting
327
- addObject: (t, s) => qe(t, Mr(s).localPos),
371
+ addObject: (a, l) => ft(a, Ka(l).localPos),
328
372
  // ---- Shortcut support (duplicate/nudge/rotate/zoom-to) ----
329
- getSnapshot: (t) => {
330
- const s = c.current.find((R) => R.id === t);
331
- return s ? { name: s.name, localPos: s.localPos, rot: s.mesh.rotation.clone(), scale: s.mesh.scale.clone(), pos: s.mesh.position.clone() } : null;
373
+ getSnapshot: (a) => {
374
+ const l = c.current.find((U) => U.id === a);
375
+ return l ? { name: l.name, localPos: l.localPos, rot: l.mesh.rotation.clone(), scale: l.mesh.scale.clone(), pos: l.mesh.position.clone() } : null;
332
376
  },
333
377
  // keepPos=true keeps the snapshot's original position (split). false (default) places it beside via the placement cursor (duplicate/paste).
334
- spawnSnapshot: (t, s = !1) => t ? qe(t.name, t.localPos, t.rot, t.scale, s && t.pos || null) : null,
335
- nudgeSelected: (t, s) => {
336
- i && (i.position.x += t, i.position.z += s);
378
+ spawnSnapshot: (a, l = !1) => a ? ft(a.name, a.localPos, a.rot, a.scale, l && a.pos || null) : null,
379
+ nudgeSelected: (a, l) => {
380
+ u && (u.position.x += a, u.position.z += l);
337
381
  },
338
- rotateSelectedY: (t) => {
339
- i && (i.rotation.y += t);
382
+ rotateSelectedY: (a) => {
383
+ u && (u.rotation.y += a);
340
384
  },
341
- frame: () => Fe(),
385
+ frame: () => mt(),
342
386
  // Z: zoom to all objects
343
387
  frameBed: () => {
344
- const t = E(m.current), s = Math.max(K.current, h.current) * 1.1 + 40;
345
- Y.target.set(t.x, 0, t.z), W.position.set(t.x + s * 0.55, s * 0.8, t.z + s), Y.update();
388
+ const a = E(p.current), l = Math.max(w.current, S.current) * 1.1 + 40;
389
+ $.target.set(a.x, 0, a.z), O.position.set(a.x + l * 0.55, l * 0.8, a.z + l), $.update();
346
390
  },
347
- removeObject: (t) => {
348
- var re, U;
349
- const s = c.current, R = s.findIndex((me) => me.id === t);
350
- if (R < 0) return;
351
- const F = s[R];
352
- i === F.mesh && (i = null, C.detach()), Se === F.mesh && (Se = null);
353
- for (const me of [...F.mesh.children])
354
- F.mesh.remove(me), (re = me.geometry) == null || re.dispose(), (U = me.material) == null || U.dispose();
355
- ge.remove(F.mesh), F.mesh.geometry.dispose(), F.mesh.material.dispose(), s.splice(R, 1), s.length === 0 && (d.current = 0), p(), L(O());
391
+ removeObject: (a) => {
392
+ var re, X;
393
+ const l = c.current, U = l.findIndex((ke) => ke.id === a);
394
+ if (U < 0) return;
395
+ const Y = l[U];
396
+ u === Y.mesh && (u = null, P.detach()), A === Y.mesh && (A = null);
397
+ for (const ke of [...Y.mesh.children])
398
+ Y.mesh.remove(ke), (re = ke.geometry) == null || re.dispose(), (X = ke.material) == null || X.dispose();
399
+ oe.remove(Y.mesh), Y.mesh.geometry.dispose(), Y.mesh.material.dispose(), l.splice(U, 1), l.length === 0 && (h.current = 0), xe(), K(je());
356
400
  },
357
401
  // MM: merges triangles sorted by ascending extruder -> group0 (ext1) followed by group1 (ext2); returns split.
358
- platePos: (t) => E(t),
402
+ platePos: (a) => E(a),
359
403
  // three (x,z) offset of plate i
360
- plateOfObject: (t) => {
361
- t.mesh.updateMatrixWorld(!0);
362
- const s = new N.Vector3().setFromMatrixPosition(t.mesh.matrixWorld);
363
- return k(s.x, s.z);
404
+ plateOfObject: (a) => {
405
+ a.mesh.updateMatrixWorld(!0);
406
+ const l = new W.Vector3().setFromMatrixPosition(a.mesh.matrixWorld);
407
+ return M(l.x, l.z);
364
408
  },
365
409
  // When plateIdx != null, only objects on that plate are used and coordinates are converted to plate-local (three-x -= PX) (keeps the stage-28 contract).
366
- buildMergedSTL: (t = null) => {
367
- let s = c.current.filter((X) => X.visible !== !1);
368
- if (t != null && (s = s.filter((X) => {
369
- X.mesh.updateMatrixWorld(!0);
370
- const xe = new N.Vector3().setFromMatrixPosition(X.mesh.matrixWorld);
371
- return k(xe.x, xe.z) === t;
372
- })), !s.length) return null;
373
- const R = [...s].sort((X, xe) => (X.extruder || 1) - (xe.extruder || 1)), F = new Set(R.map((X) => X.extruder || 1)), re = new N.Vector3(), U = [];
374
- let me = 0, ne = 0;
375
- for (const X of R) {
376
- (X.extruder || 1) >= 2 && ne === 0 && (ne = me), X.mesh.updateMatrixWorld(!0);
377
- const xe = X.mesh.matrixWorld, Ge = X.localPos;
378
- for (let Ue = 0; Ue < Ge.length; Ue += 3)
379
- re.set(Ge[Ue], Ge[Ue + 1], Ge[Ue + 2]).applyMatrix4(xe), U.push(re.x, -re.z, re.y);
380
- me += Ge.length / 9;
410
+ buildMergedSTL: (a = null) => {
411
+ let l = c.current.filter((de) => de.visible !== !1);
412
+ if (a != null && (l = l.filter((de) => {
413
+ de.mesh.updateMatrixWorld(!0);
414
+ const Fe = new W.Vector3().setFromMatrixPosition(de.mesh.matrixWorld);
415
+ return M(Fe.x, Fe.z) === a;
416
+ })), !l.length) return null;
417
+ const U = [...l].sort((de, Fe) => (de.extruder || 1) - (Fe.extruder || 1)), Y = new Set(U.map((de) => de.extruder || 1)), re = new W.Vector3(), X = [];
418
+ let ke = 0, ze = 0;
419
+ const Ce = [], be = [], Se = [];
420
+ for (const de of U) {
421
+ const Fe = de.extruder || 1;
422
+ Se.push(`${de.id}:${Fe}:${de.localPos.length / 9}`), be.length === 0 ? be.push(Fe - 1) : Fe - 1 !== be[be.length - 1] && (Ce.push(ke), be.push(Fe - 1)), Fe >= 2 && ze === 0 && (ze = ke), de.mesh.updateMatrixWorld(!0);
423
+ const Zt = de.mesh.matrixWorld, st = de.localPos;
424
+ for (let tt = 0; tt < st.length; tt += 3)
425
+ re.set(st[tt], st[tt + 1], st[tt + 2]).applyMatrix4(Zt), X.push(re.x, -re.z, re.y);
426
+ ke += st.length / 9;
381
427
  }
382
- let Me = 1e18, Ne = 1e18, Pe = -1e18, Ae = -1e18;
383
- for (let X = 0; X < U.length; X += 3)
384
- U[X] < Me && (Me = U[X]), U[X] > Pe && (Pe = U[X]), U[X + 1] < Ne && (Ne = U[X + 1]), U[X + 1] > Ae && (Ae = U[X + 1]);
385
- const De = (Me + Pe) / 2, Oe = (Ne + Ae) / 2;
386
- for (let X = 0; X < U.length; X += 3)
387
- U[X] -= De, U[X + 1] -= Oe;
388
- const Ze = De, fe = -Oe, le = new ArrayBuffer(84 + me * 50), Le = new DataView(le);
389
- Le.setUint32(80, me, !0);
390
- let _e = 84, Te = 0;
391
- for (let X = 0; X < me; X++) {
392
- _e += 12;
393
- for (let xe = 0; xe < 3; xe++)
394
- Le.setFloat32(_e, U[Te++], !0), Le.setFloat32(_e + 4, U[Te++], !0), Le.setFloat32(_e + 8, U[Te++], !0), _e += 12;
395
- Le.setUint16(_e, 0, !0), _e += 2;
428
+ const Ve = E(a ?? p.current), ot = Ve.x, Ue = -Ve.z;
429
+ for (let de = 0; de < X.length; de += 3)
430
+ X[de] -= ot, X[de + 1] -= Ue;
431
+ const lt = Ve.x, we = Ve.z, _e = new ArrayBuffer(84 + ke * 50), Le = new DataView(_e);
432
+ Le.setUint32(80, ke, !0);
433
+ let Ie = 84, We = 0;
434
+ for (let de = 0; de < ke; de++) {
435
+ Ie += 12;
436
+ for (let Fe = 0; Fe < 3; Fe++)
437
+ Le.setFloat32(Ie, X[We++], !0), Le.setFloat32(Ie + 4, X[We++], !0), Le.setFloat32(Ie + 8, X[We++], !0), Ie += 12;
438
+ Le.setUint16(Ie, 0, !0), Ie += 2;
396
439
  }
397
- return { buf: le, split: ne, extruders: F.size, offX: Ze, offZ: fe };
440
+ let Qe = 1e18, Ye = 1e18, et = -1e18, vt = -1e18;
441
+ for (let de = 0; de < X.length; de += 3)
442
+ X[de] < Qe && (Qe = X[de]), X[de] > et && (et = X[de]), X[de + 1] < Ye && (Ye = X[de + 1]), X[de + 1] > vt && (vt = X[de + 1]);
443
+ return {
444
+ buf: _e,
445
+ split: ze,
446
+ splits: Ce,
447
+ tools: be,
448
+ extruders: Y.size,
449
+ offX: lt,
450
+ offZ: we,
451
+ topology: Se.join("|"),
452
+ minX: Qe,
453
+ minY: Ye,
454
+ maxX: et,
455
+ maxY: vt
456
+ };
398
457
  },
399
- setObjectExtruder: (t, s) => {
400
- const R = c.current.find((F) => F.id === t);
401
- if (R) {
402
- R.extruder = s;
403
- const F = B.current[s - 1];
404
- F && R.mesh.material.color.set(F);
458
+ setObjectExtruder: (a, l) => {
459
+ const U = c.current.find((Y) => Y.id === a);
460
+ if (U) {
461
+ U.extruder = l;
462
+ const Y = D.current[l - 1];
463
+ Y && U.mesh.material.color.set(Y);
405
464
  }
406
465
  },
407
- setObjectVisible: (t, s) => {
408
- const R = c.current.find((F) => F.id === t);
409
- R && (R.visible = s, R.mesh.visible = s);
466
+ setObjectVisible: (a, l) => {
467
+ const U = c.current.find((Y) => Y.id === a);
468
+ U && (U.visible = l, U.mesh.visible = l);
410
469
  },
411
470
  // stage 27: print toggle (eye icon)
412
471
  recolorObjects: () => {
413
- for (const t of c.current) {
414
- const s = B.current[(t.extruder || 1) - 1];
415
- s && t.mesh.material.color.set(s);
472
+ for (const a of c.current) {
473
+ const l = D.current[(a.extruder || 1) - 1];
474
+ l && a.mesh.material.color.set(l);
416
475
  }
417
476
  },
418
477
  // reflect filament color changes
419
478
  selectedObjectId: () => {
420
- var t;
421
- return i ? ((t = c.current.find((s) => s.mesh === i)) == null ? void 0 : t.id) ?? null : null;
479
+ var a;
480
+ return u ? ((a = c.current.find((l) => l.mesh === u)) == null ? void 0 : a.id) ?? null : null;
422
481
  },
423
482
  // stage 27: viewport toolbar "delete selected"
424
- selectObject: (t) => {
425
- const s = c.current.find((R) => R.id === t);
426
- s && (i = s.mesh, C.attach(s.mesh), p(), L(O()));
483
+ selectObject: (a) => {
484
+ const l = c.current.find((U) => U.id === a);
485
+ l && (u = l.mesh, P.attach(l.mesh), xe(), K(je()));
427
486
  },
428
487
  // Stage 28 P1: seat on the bed — local geometry is baked to minZ->0 by bakeLocal (once on load). Re-seat after a gizmo Z move.
429
488
  // (The upstream ensure_on_bed sinking allowance [allow_negative_z] is out of scope — we only seat by -min_z.)
430
489
  placeOnBed: () => {
431
- var t;
432
- for (const s of c.current) s.mesh.position.y = 0;
433
- i && ((t = C.update) == null || t.call(C));
490
+ var a;
491
+ for (const l of c.current) l.mesh.position.y = 0;
492
+ u && ((a = P.update) == null || a.call(P));
434
493
  },
435
494
  onSliced: () => {
436
- ge.visible = !1, C.detach(), i = null, p();
495
+ oe.visible = !1, P.detach(), u = null, xe();
437
496
  },
438
497
  showObjects: () => {
439
- ge.visible = !0;
498
+ oe.visible = !0;
499
+ },
500
+ // Footprint + height of everything currently on the plate, in the same coordinates the tower box uses.
501
+ modelBounds: (a = null) => {
502
+ let l = c.current.filter((re) => re.visible !== !1);
503
+ a != null && (l = l.filter((re) => {
504
+ re.mesh.updateMatrixWorld(!0);
505
+ const X = new W.Vector3().setFromMatrixPosition(re.mesh.matrixWorld);
506
+ return M(X.x, X.z) === a;
507
+ }));
508
+ const U = l.map((re) => re.mesh);
509
+ if (!U.length) return null;
510
+ const Y = new W.Box3();
511
+ for (const re of U)
512
+ re.updateMatrixWorld(!0), Y.expandByObject(re);
513
+ return Number.isFinite(Y.min.x) ? { minX: Y.min.x, maxX: Y.max.x, minY: -Y.max.z, maxY: -Y.min.z, height: Y.max.y - Y.min.y } : null;
514
+ },
515
+ // Draw (or move) the tower stand-in. size/height in mm, x/y in the same bed-centred coordinates the objects
516
+ // use. Passing null removes it — a single-filament print has no tower to show.
517
+ setPrimeTower: (a) => {
518
+ var ke;
519
+ const l = ce.current;
520
+ if (!a) {
521
+ se && (u === se && (u = null, P.detach()), l.scene.remove(se), se.geometry.dispose(), se.material.dispose(), se = null);
522
+ return;
523
+ }
524
+ const { x: U, y: Y, size: re, height: X } = a;
525
+ if (!se) {
526
+ const ze = new W.BoxGeometry(1, 1, 1), Ce = new W.MeshBasicMaterial({ color: 5231045, transparent: !0, opacity: 0.35, depthWrite: !1 });
527
+ se = new W.Mesh(ze, Ce), se.userData = { name: "Prime tower", isPrimeTower: !0 }, l.scene.add(se);
528
+ }
529
+ se.userData.halfHeight = X / 2, se.scale.set(re, X, re), se.position.set(U, X / 2, -Y), se.visible = !0, (ke = l.invalidate) == null || ke.call(l);
440
530
  },
441
531
  // Stage 29-2: render N plates — each plate = grid + border, offset by PX_i, with the selected plate's border highlighted.
442
- setPlates: (t, s, R, F) => {
443
- const re = _.current;
444
- K.current = s, h.current = R, w.current = t, m.current = F;
445
- for (const fe of re.plateBeds || []) for (const le of [fe.gridThin, fe.gridBold, fe.border])
446
- re.scene.remove(le), le.geometry.dispose(), le.material.dispose();
532
+ setPlates: (a, l, U, Y) => {
533
+ const re = ce.current;
534
+ w.current = l, S.current = U, j.current = a, p.current = Y;
535
+ for (const we of re.plateBeds || []) for (const _e of [we.gridThin, we.gridBold, we.border])
536
+ re.scene.remove(_e), _e.geometry.dispose(), _e.material.dispose();
447
537
  re.plateBeds = [];
448
- const U = gt(t), me = Ye(s), ne = Ye(R), Me = Math.min(s, R), Ne = Me >= 6e3 ? 100 : Me >= 1200 ? 50 : Me >= 600 ? 20 : 10, Pe = [], Ae = [], De = -s / 2, Oe = -R / 2;
449
- for (let fe = 0, le = De; le <= s / 2 + 1e-6; le = De + ++fe * Ne) (fe % 5 ? Pe : Ae).push(le, 0, Oe, le, 0, R / 2);
450
- for (let fe = 0, le = Oe; le <= R / 2 + 1e-6; le = Oe + ++fe * Ne) (fe % 5 ? Pe : Ae).push(De, 0, le, s / 2, 0, le);
451
- const Ze = (fe) => {
452
- const le = new N.BufferGeometry();
453
- return le.setAttribute("position", new N.Float32BufferAttribute(fe, 3)), le;
538
+ const X = Ft(a), ke = rt(l), ze = rt(U), Ce = Math.min(l, U), be = Ce >= 6e3 ? 100 : Ce >= 1200 ? 50 : Ce >= 600 ? 20 : 10, Se = [], Ve = [], ot = -l / 2, Ue = -U / 2;
539
+ for (let we = 0, _e = ot; _e <= l / 2 + 1e-6; _e = ot + ++we * be) (we % 5 ? Se : Ve).push(_e, 0, Ue, _e, 0, U / 2);
540
+ for (let we = 0, _e = Ue; _e <= U / 2 + 1e-6; _e = Ue + ++we * be) (we % 5 ? Se : Ve).push(ot, 0, _e, l / 2, 0, _e);
541
+ const lt = (we) => {
542
+ const _e = new W.BufferGeometry();
543
+ return _e.setAttribute("position", new W.Float32BufferAttribute(we, 3)), _e;
454
544
  };
455
- for (let fe = 0; fe < t; fe++) {
456
- const le = fe % U * me, Le = Math.floor(fe / U) * ne, _e = new N.LineSegments(Ze(Pe), new N.LineBasicMaterial({ color: 2304561 })), Te = new N.LineSegments(Ze(Ae), new N.LineBasicMaterial({ color: 3752781 }));
457
- _e.position.set(le, 0, Le), Te.position.set(le, 0, Le), re.scene.add(_e), re.scene.add(Te);
458
- const X = fe === F, xe = new N.LineSegments(new N.EdgesGeometry(new N.PlaneGeometry(s, R)), new N.LineBasicMaterial({ color: X ? 44610 : 4871520, linewidth: X ? 2 : 1 }));
459
- xe.rotation.x = -Math.PI / 2, xe.position.set(le, 0, Le), re.scene.add(xe), re.plateBeds.push({ gridThin: _e, gridBold: Te, border: xe });
545
+ for (let we = 0; we < a; we++) {
546
+ const _e = we % X * ke, Le = Math.floor(we / X) * ze, Ie = new W.LineSegments(lt(Se), new W.LineBasicMaterial({ color: 2304561 })), We = new W.LineSegments(lt(Ve), new W.LineBasicMaterial({ color: 3752781 }));
547
+ Ie.position.set(_e, 0, Le), We.position.set(_e, 0, Le), re.scene.add(Ie), re.scene.add(We);
548
+ const Qe = we === Y, Ye = new W.LineSegments(new W.EdgesGeometry(new W.PlaneGeometry(l, U)), new W.LineBasicMaterial({ color: Qe ? 44610 : 4871520, linewidth: Qe ? 2 : 1 }));
549
+ Ye.rotation.x = -Math.PI / 2, Ye.position.set(_e, 0, Le), re.scene.add(Ye), re.plateBeds.push({ gridThin: Ie, gridBold: We, border: Ye });
460
550
  }
461
551
  },
462
- setBed: (t, s) => {
463
- var R;
464
- (R = a.current) == null || R.setPlates(w.current, t, s, m.current);
552
+ setBed: (a, l) => {
553
+ var U;
554
+ (U = r.current) == null || U.setPlates(j.current, a, l, p.current);
465
555
  }
466
556
  // backwards compatible
467
557
  };
468
- const et = (t) => {
469
- var s;
470
- (s = l.current) == null || s.call(l, t), ye();
558
+ const bt = (a) => {
559
+ var l;
560
+ (l = i.current) == null || l.call(i, a), q();
471
561
  };
472
- window.addEventListener("keydown", et);
473
- const tt = new ResizeObserver(() => {
474
- g = j.clientWidth || g, y = j.clientHeight || y, W.aspect = g / y, W.updateProjectionMatrix(), v.setSize(g, y), ye();
562
+ window.addEventListener("keydown", bt);
563
+ const gt = new ResizeObserver(() => {
564
+ C = y.clientWidth || C, V = y.clientHeight || V, O.aspect = C / V, O.updateProjectionMatrix(), f.setSize(C, V), q();
475
565
  });
476
- tt.observe(j), L(O());
477
- let nt = 0, Xe = 0;
478
- const rt = (t = 0) => {
479
- nt = requestAnimationFrame(rt), Y.update(), (Ce || t - Xe > 500) && (Ce = !1, Xe = t, v.render(D, W));
566
+ gt.observe(y), K(je());
567
+ let yt = 0, xt = 0;
568
+ const jt = (a = 0) => {
569
+ yt = requestAnimationFrame(jt), $.update(), (G || a - xt > 500) && (G = !1, xt = a, f.render(N, O));
480
570
  };
481
- return rt(), () => {
482
- cancelAnimationFrame(nt), tt.disconnect(), window.removeEventListener("keydown", et), v.domElement.removeEventListener("pointermove", z), v.domElement.removeEventListener("pointerdown", ie), v.domElement.removeEventListener("pointerdown", ce), v.domElement.removeEventListener("contextmenu", ke), v.domElement.removeEventListener("dblclick", se), v.domElement.removeEventListener("wheel", ae, { capture: !0 }), window.removeEventListener("pointerup", we), window.removeEventListener("pointercancel", we), a.current = null, C.detach(), C.dispose(), Y.dispose(), D.traverse((t) => {
483
- t.geometry && t.geometry.dispose(), t.material && t.material.dispose();
484
- }), v.dispose(), v.domElement.parentNode && v.domElement.parentNode.removeChild(v.domElement);
571
+ return jt(), () => {
572
+ cancelAnimationFrame(yt), gt.disconnect(), window.removeEventListener("keydown", bt), f.domElement.removeEventListener("pointermove", qe), f.domElement.removeEventListener("pointerdown", dt), f.domElement.removeEventListener("pointerdown", Mt), f.domElement.removeEventListener("contextmenu", At), f.domElement.removeEventListener("dblclick", Je), f.domElement.removeEventListener("wheel", Nt, { capture: !0 }), window.removeEventListener("pointerup", Xe), window.removeEventListener("pointercancel", Xe), r.current = null, P.detach(), P.dispose(), $.dispose(), N.traverse((a) => {
573
+ a.geometry && a.geometry.dispose(), a.material && a.material.dispose();
574
+ }), f.dispose(), f.domElement.parentNode && f.domElement.parentNode.removeChild(f.domElement);
485
575
  };
486
- }, []), { mountRef: u, three: _ };
576
+ }, []), { mountRef: F, three: ce };
487
577
  }
488
- function Cr(n) {
578
+ function Qa(t) {
489
579
  const {
490
- settings: a,
580
+ settings: r,
491
581
  wipeTowerReal: c,
492
- workerRef: l,
493
- apiRef: r,
494
- layersDataRef: m,
495
- layerLoRef: d,
496
- layerHiRef: w,
497
- rebuildToolpaths: b,
498
- rebuildPaintOverlay: T,
499
- setProgress: P,
500
- setSlicing: M,
501
- setError: B,
502
- setStats: ee,
503
- setOverBed: L,
504
- setLayerCount: te,
505
- setLayerLo: I,
506
- setLayerHi: A,
507
- setGcodeUrl: u,
508
- setCanvasMode: _,
509
- setPaintCounts: G
510
- } = n, K = H(null), h = H(null), E = H(!1), k = H(null), j = H(null), f = H(0), g = () => {
511
- f.current && (clearInterval(f.current), f.current = 0);
512
- }, y = () => {
513
- const i = j.current;
514
- if (console.info("[vp-cancel] cancelSlice, hasView=", !!(i != null && i.cancel)), i != null && i.cancel)
582
+ workerRef: i,
583
+ apiRef: o,
584
+ layersDataRef: p,
585
+ layerLoRef: h,
586
+ layerHiRef: j,
587
+ paintStateCountsRef: x,
588
+ rebuildToolpaths: B,
589
+ rebuildPaintOverlay: z,
590
+ setProgress: m,
591
+ setSlicing: L,
592
+ setError: D,
593
+ setStats: R,
594
+ setOverBed: K,
595
+ setLayerCount: J,
596
+ setLayerLo: _,
597
+ setLayerHi: T,
598
+ setGcodeUrl: F,
599
+ setCanvasMode: ce,
600
+ setPaintCounts: pe,
601
+ setSliceNotice: w
602
+ } = t, S = ne(null), E = ne(null), M = ne(!1), y = ne(null), v = ne(null), C = ne(0), V = () => {
603
+ C.current && (clearInterval(C.current), C.current = 0);
604
+ }, f = () => {
605
+ const s = v.current;
606
+ if (console.info("[vp-cancel] cancelSlice, hasView=", !!(s != null && s.cancel)), s != null && s.cancel)
515
607
  try {
516
- Atomics.store(i.cancel, 0, 1);
608
+ Atomics.store(s.cancel, 0, 1);
517
609
  } catch {
518
- i.cancel[0] = 1;
610
+ s.cancel[0] = 1;
519
611
  }
520
- }, v = (i, S) => {
521
- const V = S > 2 ? (S - 2) / 2 : 0;
522
- return V ? i <= V ? 0.35 * (i / V) : i === V + 1 ? 0.36 : i === V + 2 ? 0.87 : 0.87 + 0.13 * ((i - V - 2) / V) : S ? i / S : 0;
523
- }, D = (i) => {
524
- const S = j.current;
525
- S && (g(), f.current = setInterval(() => {
526
- const V = S.arr[0];
527
- P(0.36 + 0.51 * Math.min(0.95, V / (3.5 * i)));
612
+ }, N = (s, d) => {
613
+ const A = d > 2 ? (d - 2) / 2 : 0;
614
+ return A ? s <= A ? 0.35 * (s / A) : s === A + 1 ? 0.36 : s === A + 2 ? 0.87 : 0.87 + 0.13 * ((s - A - 2) / A) : d ? s / d : 0;
615
+ }, O = (s) => {
616
+ const d = v.current;
617
+ d && (V(), C.current = setInterval(() => {
618
+ const A = d.arr[0];
619
+ m(0.36 + 0.51 * Math.min(0.95, A / (3.5 * s)));
528
620
  }, 150));
529
- }, W = () => {
530
- const i = j.current;
531
- i && (g(), f.current = setInterval(() => {
532
- P(0.35 * Math.min(1, i.arr[0] / 1e3));
621
+ }, H = () => {
622
+ const s = v.current;
623
+ s && (V(), C.current = setInterval(() => {
624
+ m(0.35 * Math.min(1, s.arr[0] / 1e3));
533
625
  }, 150));
534
626
  };
535
627
  function oe() {
536
- if (!l.current) {
537
- const i = Hn();
538
- i.onmessage = (V) => {
539
- var ue, ve, Ee, Z, q;
540
- const p = V.data, O = K.current;
541
- if (p.type === "progress") {
542
- const z = p.total > 2 ? (p.total - 2) / 2 : 0;
543
- z && p.done <= z && g(), z && p.done === z + 1 && D(z), z && p.done >= z + 2 && g(), P(v(p.done, p.total)), (ue = O == null ? void 0 : O.note) == null || ue.call(O, p.done, p.total), (ve = O == null ? void 0 : O.kick) == null || ve.call(O);
544
- } else if (p.type === "supsab")
628
+ if (!i.current) {
629
+ const s = xa();
630
+ s.onmessage = (A) => {
631
+ var se, ye, xe, je, Q;
632
+ const u = A.data, I = S.current;
633
+ if (u.type === "progress") {
634
+ const Z = u.total > 2 ? (u.total - 2) / 2 : 0;
635
+ Z && u.done <= Z && V(), Z && u.done === Z + 1 && O(Z), Z && u.done >= Z + 2 && V(), m(N(u.done, u.total)), (se = I == null ? void 0 : I.note) == null || se.call(I, u.done, u.total), (ye = I == null ? void 0 : I.kick) == null || ye.call(I);
636
+ } else if (u.type === "supsab")
545
637
  try {
546
- j.current = { arr: new Uint32Array(p.buf, p.ptr, 1), cancel: p.cancelPtr ? new Uint32Array(p.buf, p.cancelPtr, 1) : null }, console.info("[vp-cancel] supsab cancelPtr=", p.cancelPtr);
547
- } catch (z) {
548
- console.info("[vp-cancel] supsab view fail", z);
638
+ v.current = { arr: new Uint32Array(u.buf, u.ptr, 1), cancel: u.cancelPtr ? new Uint32Array(u.buf, u.cancelPtr, 1) : null }, console.info("[vp-cancel] supsab cancelPtr=", u.cancelPtr);
639
+ } catch (Z) {
640
+ console.info("[vp-cancel] supsab view fail", Z);
549
641
  }
550
- else if (p.type === "layer") {
551
- (Ee = O == null ? void 0 : O.kick) == null || Ee.call(O);
552
- const z = h.current;
553
- z && (z.layers.push({ z: p.z, paths: p.paths, widths: p.widths }), p.gcode && z.gcode.push(p.gcode));
554
- } else p.type === "done" ? (g(), O ? (K.current = null, (Z = O.stop) == null || Z.call(O), O.resolve(ge(p.result))) : (de(ge(p.result)), M(!1))) : p.type === "error" ? (g(), O ? (K.current = null, (q = O.stop) == null || q.call(O), O.reject(new Error(p.error))) : (B("Slice failed: " + p.error), M(!1))) : p.type === "prepared" || (p.type === "painted" ? (G({ enf: p.enf, blk: p.blk }), i.postMessage({ cmd: "overlay" })) : p.type === "overlay" && T(p.enf, p.blk));
642
+ else if (u.type === "layer") {
643
+ (xe = I == null ? void 0 : I.kick) == null || xe.call(I);
644
+ const Z = E.current;
645
+ Z && (Z.layers.push({ z: u.z, paths: u.paths, widths: u.widths }), u.gcode && Z.gcode.push(u.gcode));
646
+ } else u.type === "done" ? (V(), I ? (S.current = null, (je = I.stop) == null || je.call(I), I.resolve(ue(u.result))) : ($(ue(u.result)), L(!1))) : u.type === "error" ? (V(), I ? (S.current = null, (Q = I.stop) == null || Q.call(I), I.reject(new Error(u.error))) : (D("Slice failed: " + u.error), L(!1))) : u.type === "prepared" || (u.type === "painted" ? pe({ enf: u.enf, blk: u.blk }) : u.type === "overlay" && z(u.enf, u.blk, u.overlays));
555
647
  };
556
- const S = (V) => {
557
- var O;
558
- if (l.current !== i) return;
559
- g(), j.current = null;
560
- const p = K.current;
561
- if (p) {
562
- K.current = null, (O = p.stop) == null || O.call(p);
648
+ const d = (A) => {
649
+ var I;
650
+ if (i.current !== s) return;
651
+ V(), v.current = null;
652
+ const u = S.current;
653
+ if (u) {
654
+ S.current = null, (I = u.stop) == null || I.call(u);
563
655
  try {
564
- i.terminate();
656
+ s.terminate();
565
657
  } catch {
566
658
  }
567
- l.current = null, p.reject(new Error(V));
659
+ i.current = null, u.reject(new Error(A));
568
660
  } else
569
- B(V), M(!1);
661
+ D(A), L(!1);
570
662
  };
571
- i.onerror = (V) => S("Worker terminated (likely out of memory): " + (V.message || "worker error")), i.onmessageerror = () => S("Worker message error (structured clone failed)"), l.current = i, typeof window < "u" && (window.__vpWorker = i);
663
+ s.onerror = (A) => d("Worker terminated (likely out of memory): " + (A.message || "worker error")), s.onmessageerror = () => d("Worker message error (structured clone failed)"), i.current = s, typeof window < "u" && (window.__vpWorker = s);
572
664
  }
573
- return l.current;
665
+ return i.current;
574
666
  }
575
- function ge(i) {
576
- if (i && i.stats && i.stats.streamed) {
577
- const S = h.current || { layers: [], gcode: [] };
578
- return { stats: i.stats, layers: S.layers, gcode: S.gcode.join("") };
667
+ function ue(s) {
668
+ if (s && s.stats && s.stats.streamed) {
669
+ const d = E.current || { layers: [], gcode: [] };
670
+ return { stats: s.stats, layers: d.layers, gcode: d.gcode.join("") };
579
671
  }
580
- return i;
672
+ return s;
581
673
  }
582
- function de(i) {
583
- var V;
584
- if (i.error) {
585
- B(String(i.error));
674
+ function $(s) {
675
+ var A;
676
+ if (s.error) {
677
+ D(String(s.error));
586
678
  return;
587
679
  }
588
- m.current = i.layers;
589
- const S = i.layers.length;
590
- d.current = 0, w.current = S - 1, I(0), A(S - 1), b(), (V = r.current) == null || V.onSliced(), _("preview"), ee({
591
- layers: i.stats.layers,
592
- segments: i.stats.path_segments,
593
- filament: i.stats.filament_mm,
594
- timeSec: i.stats.time_estimate,
595
- engine: i.stats.time_engine,
596
- limits: i.stats.machine_limits
597
- }), L(!!i.stats.over_bed), te(S), u((p) => (p && URL.revokeObjectURL(p), URL.createObjectURL(new Blob([i.gcode], { type: "text/plain" }))));
598
- }
599
- Ve(() => () => {
600
- l.current && (l.current.terminate(), l.current = null);
601
- }, []), Ve(() => {
680
+ p.current = s.layers;
681
+ const d = s.layers.length;
682
+ h.current = 0, j.current = d - 1, _(0), T(d - 1), B(), (A = o.current) == null || A.onSliced(), ce("preview"), R({
683
+ layers: s.stats.layers,
684
+ segments: s.stats.path_segments,
685
+ filament: s.stats.filament_mm,
686
+ timeSec: s.stats.time_estimate,
687
+ engine: s.stats.time_engine,
688
+ limits: s.stats.machine_limits
689
+ }), K(!!s.stats.over_bed), J(d), F((u) => (u && URL.revokeObjectURL(u), URL.createObjectURL(new Blob([s.gcode], { type: "text/plain" }))));
690
+ }
691
+ Te(() => () => {
692
+ i.current && (i.current.terminate(), i.current = null);
693
+ }, []), Te(() => {
602
694
  try {
603
695
  oe().postMessage({ cmd: "warmup" });
604
696
  } catch {
605
697
  }
606
698
  }, []);
607
- const Y = 6e4, C = 3e5;
608
- function pe(i, S) {
609
- return new Promise((V, p) => {
699
+ const P = 6e4, ve = 3e5;
700
+ function Me(s, d) {
701
+ return new Promise((A, u) => {
610
702
  if (typeof window < "u" && window.__vpFail && window.__vpFail(window.__vpSliceN = (window.__vpSliceN || 0) + 1)) {
611
- p(new Error("forced failure (test hook)"));
703
+ u(new Error("forced failure (test hook)"));
612
704
  return;
613
705
  }
614
- const O = typeof window < "u" && window.__vpStallNext ? (window.__vpStallNext = !1, !0) : !1, ue = typeof window < "u" && window.__vpWatchdogOnce ? ((J) => (window.__vpWatchdogOnce = 0, J))(window.__vpWatchdogOnce) : Y;
615
- h.current = { layers: [], gcode: [] };
616
- let ve = 0, Ee = 0, Z = 0;
617
- const q = performance.now();
618
- let z = "";
619
- const ie = (J, ce) => {
620
- Ee = J, Z = ce;
621
- const ke = ce > 2 ? (ce - 2) / 2 : 0, ze = ke ? J < ke ? "PASS1…" : J === ke ? "PASS1-done" : J === ke + 1 ? "surf-done" : J === ke + 2 ? "support-done" : J === ce ? "emit-done" : "emit…" : "";
622
- ze && ze !== z && (console.info(`[vp-prof] ${ze} +${((performance.now() - q) / 1e3).toFixed(1)}s`), z = ze);
623
- }, we = () => {
624
- const J = Z > 2 ? (Z - 2) / 2 : 0;
625
- return J && Ee >= J && Ee < J + 2 ? C : ue;
626
- }, se = () => {
627
- ve && (clearTimeout(ve), ve = 0);
628
- }, ae = () => {
629
- se();
630
- const J = we();
631
- ve = setTimeout(() => {
632
- var ce;
633
- K.current = null, g(), j.current = null;
706
+ const I = typeof window < "u" && window.__vpStallNext ? (window.__vpStallNext = !1, !0) : !1, se = typeof window < "u" && window.__vpWatchdogOnce ? ((le) => (window.__vpWatchdogOnce = 0, le))(window.__vpWatchdogOnce) : P;
707
+ E.current = { layers: [], gcode: [] };
708
+ let ye = 0, xe = 0, je = 0;
709
+ const Q = performance.now();
710
+ let Z = "";
711
+ const ee = (le, Ae) => {
712
+ xe = le, je = Ae;
713
+ const Pe = Ae > 2 ? (Ae - 2) / 2 : 0, Re = Pe ? le < Pe ? "PASS1…" : le === Pe ? "PASS1-done" : le === Pe + 1 ? "surf-done" : le === Pe + 2 ? "support-done" : le === Ae ? "emit-done" : "emit…" : "";
714
+ Re && Re !== Z && (console.info(`[vp-prof] ${Re} +${((performance.now() - Q) / 1e3).toFixed(1)}s`), Z = Re);
715
+ }, he = () => {
716
+ const le = je > 2 ? (je - 2) / 2 : 0;
717
+ return le && xe >= le && xe < le + 2 ? ve : se;
718
+ }, me = () => {
719
+ ye && (clearTimeout(ye), ye = 0);
720
+ }, fe = () => {
721
+ me();
722
+ const le = he();
723
+ ye = setTimeout(() => {
724
+ var Ae;
725
+ S.current = null, V(), v.current = null;
634
726
  try {
635
- (ce = l.current) == null || ce.terminate();
727
+ (Ae = i.current) == null || Ae.terminate();
636
728
  } catch {
637
729
  }
638
- l.current = null, p(new Error(`watchdog: no progress for ${J}ms — assuming memory pressure`));
639
- }, J);
730
+ i.current = null, u(new Error(`watchdog: no progress for ${le}ms — assuming memory pressure`));
731
+ }, le);
640
732
  };
641
- K.current = { resolve: V, reject: p, kick: ae, stop: se, note: ie }, ae(), oe().postMessage({ stl: i, params: S, stall: O }), W();
733
+ S.current = { resolve: A, reject: u, kick: fe, stop: me, note: ee }, fe(), oe().postMessage({ stl: s, params: d, stall: I }), H();
642
734
  });
643
735
  }
644
- function je() {
645
- var i;
736
+ function ge() {
737
+ var s;
646
738
  try {
647
- (i = l.current) == null || i.terminate();
739
+ (s = i.current) == null || s.terminate();
648
740
  } catch {
649
741
  }
650
- l.current = null, oe();
742
+ i.current = null, oe();
651
743
  }
652
- async function Ce(i, S) {
653
- const V = (p) => String((p == null ? void 0 : p.message) || p).includes("canceled");
744
+ async function Ee(s, d) {
745
+ const A = (u) => String((u == null ? void 0 : u.message) || u).includes("canceled");
654
746
  try {
655
- const p = await pe(i, JSON.stringify(S));
656
- return { r: p, economy: !!(p.stats && p.stats.economy) };
657
- } catch (p) {
658
- if (V(p)) throw p;
659
- if (S.wall_generator !== "classic") {
660
- je();
747
+ const u = await Me(s, JSON.stringify(d));
748
+ return { r: u, economy: !!(u.stats && u.stats.economy) };
749
+ } catch (u) {
750
+ if (A(u)) throw u;
751
+ if (d.wall_generator !== "classic") {
752
+ ge();
661
753
  try {
662
- const ue = await pe(i, JSON.stringify({ ...S, wall_generator: "classic", keep_stages: !1, reuse_stages: 0 }));
663
- return { r: ue, economy: !!(ue.stats && ue.stats.economy), classicWalls: !0 };
664
- } catch (ue) {
665
- if (V(ue)) throw ue;
754
+ const se = await Me(s, JSON.stringify({ ...d, wall_generator: "classic", keep_stages: !1, reuse_stages: 0 }));
755
+ return { r: se, economy: !!(se.stats && se.stats.economy), classicWalls: !0 };
756
+ } catch (se) {
757
+ if (A(se)) throw se;
666
758
  }
667
759
  }
668
- return je(), { r: await pe(i, JSON.stringify({ ...S, economy: !0, keep_stages: !1, reuse_stages: 0 })), economy: !0, recovered: !0 };
760
+ return ge(), { r: await Me(s, JSON.stringify({ ...d, economy: !0, keep_stages: !1, reuse_stages: 0 })), economy: !0, recovered: !0 };
669
761
  }
670
762
  }
671
- async function ye(i) {
763
+ async function b(s) {
672
764
  try {
673
- const S = await crypto.subtle.digest("SHA-256", i);
674
- return Array.from(new Uint8Array(S, 0, 8)).map((V) => V.toString(16).padStart(2, "0")).join("");
765
+ const d = await crypto.subtle.digest("SHA-256", s);
766
+ return Array.from(new Uint8Array(d, 0, 8)).map((A) => A.toString(16).padStart(2, "0")).join("");
675
767
  } catch {
676
768
  return null;
677
769
  }
678
770
  }
679
- function Re(i, S) {
680
- i.economy || (i.keep_stages = !0, i.reuse_stages = S && S === k.current ? 2 : 0);
771
+ function k(s, d) {
772
+ s.economy || (s.keep_stages = !0, s.reuse_stages = d && d === y.current ? 2 : 0);
681
773
  }
682
- function be(i) {
683
- const S = xt(a);
684
- return i.extruders >= 2 && i.split > 0 && (S.extruder_count = i.extruders, S.mm_group_split = i.split, S.wipe_tower_real = c), E.current && (S.sparse_infill_pattern = "rectilinear", S.infill_density = Math.min(S.infill_density ?? 0.15, 0.08), S.economy = !0), typeof window < "u" && window.__vpForceTree && (S.enable_support = !0, S.support_style = "tree", S.support_threshold_angle = 40), S;
774
+ function G(s) {
775
+ var Z;
776
+ const d = ln(r);
777
+ s.extruders >= 2 && s.split > 0 && (d.extruder_count = s.extruders, d.mm_group_split = s.split, (Z = s.splits) != null && Z.length && (d.mm_group_splits = s.splits, d.mm_group_tools = s.tools), d.wipe_tower_real = c);
778
+ const A = Object.entries((x == null ? void 0 : x.current) ?? {}).filter(([, ee]) => ee > 0).map(([ee]) => Number(ee)), u = A.length ? Math.max(...A) : 0;
779
+ u >= 2 && (d.extruder_count = Math.max(d.extruder_count ?? 1, u), d.wipe_tower_real = c);
780
+ const I = (ee) => {
781
+ const he = He(r, ee), me = Array.isArray(he) ? he : he == null || he === "" ? [] : [he];
782
+ return me.length ? me : void 0;
783
+ }, se = (ee) => ee && ee.map((he) => {
784
+ const me = parseFloat(he);
785
+ return Number.isFinite(me) ? me : 0;
786
+ }), ye = I("filament_type"), xe = I("filament_settings_id"), je = se(I("filament_density")), Q = se(I("filament_cost"));
787
+ if (ye && (d.filament_type = ye), xe && (d.filament_settings_id = xe), je && (d.filament_density = je), Q && (d.filament_cost = Q), d.gcode_stats_block = !0, d.gcode_config_block = !0, (d.extruder_count ?? 1) >= 2 && Number.isFinite(s.minX) && d.prime_tower_x == null) {
788
+ const ee = d.wipe_tower_real ?? !0 ? d.prime_tower_width ?? 30 : 15, he = 5, me = d.bed_width ?? 200, fe = d.bed_depth ?? 200, le = s.minX + me / 2, Ae = (s.minY + s.maxY) / 2 + fe / 2;
789
+ d.prime_tower_x = Math.min(Math.max(le - he - ee, 1), me - ee - 1), d.prime_tower_y = Math.min(Math.max(Ae - ee / 2, 1), fe - ee - 1);
790
+ }
791
+ return A.length && (d.enable_support && A.includes(2) ? w == null || w("Support is on and T2 is painted. One facet carries one mark, and a support blocker is the same mark as T2 — the painted areas were sliced as T2, not as blocked support.") : u < 2 && (w == null || w("Only T1 was painted. T1 is the default extruder every unpainted facet already uses, so the slice is unchanged — paint T2 or higher to get a second material."))), M.current && (d.sparse_infill_pattern = "rectilinear", d.infill_density = Math.min(d.infill_density ?? 0.15, 0.08), d.economy = !0), typeof window < "u" && window.__vpForceTree && (d.enable_support = !0, d.support_style = "tree", d.support_threshold_angle = 40), typeof window < "u" && (window.__vpParams = d), d;
685
792
  }
686
- async function Se(i) {
687
- const S = be(i), V = await ye(i.buf);
688
- Re(S, V);
793
+ async function q(s) {
794
+ const d = G(s), A = await b(s.buf);
795
+ k(d, A);
689
796
  try {
690
- const p = await Ce(i.buf, S);
691
- return k.current = p.economy || p.classicWalls ? null : V, { ...p, params: S };
692
- } catch (p) {
693
- throw k.current = null, p;
797
+ const u = await Ee(s.buf, d);
798
+ return y.current = u.economy || u.classicWalls ? null : A, { ...u, params: d };
799
+ } catch (u) {
800
+ throw y.current = null, u;
694
801
  }
695
802
  }
696
- return { getWorker: oe, cancelSlice: y, runSlice: Se, pendingSliceRef: K, downgradeRef: E };
803
+ return { getWorker: oe, cancelSlice: f, runSlice: q, pendingSliceRef: S, downgradeRef: M };
697
804
  }
698
- function Sr(n) {
805
+ function eo(t) {
699
806
  const {
700
- three: a,
807
+ three: r,
701
808
  plateTpRef: c,
702
- toolpathRef: l,
703
- segDataRef: r,
704
- layersDataRef: m,
705
- plateResultsRef: d,
706
- plateOffsetsRef: w,
707
- lineWidthRef: b,
708
- showTravelRef: T,
709
- viewTypeRef: P,
710
- layerLoRef: M,
711
- layerHiRef: B,
712
- selectedPlateRef: ee,
713
- settings: L,
714
- setSegCount: te,
715
- setColorRange: I,
716
- setRoleLegend: A
717
- } = n;
718
- function u(f) {
719
- var y;
720
- const g = c.current[f];
721
- g && (g.group.remove(g.ctl.mesh), g.group.remove(g.ctl.travLines), g.ctl.dispose(), (y = a.current.toolpathGroup) == null || y.remove(g.group), delete c.current[f], l.current === g.ctl && (l.current = null, r.current = null));
722
- }
723
- function _() {
724
- for (const f of Object.keys(c.current)) u(Number(f));
725
- l.current = null, r.current = null;
726
- }
727
- function G(f, g) {
728
- const { toolpathGroup: y } = a.current;
729
- if (!y || (u(f), !g || !g.length)) return null;
730
- const v = qn(g, b.current), D = Wn(N, v), W = w.current[f] || { offX: 0, offZ: 0 }, oe = new N.Group();
731
- oe.rotation.x = -Math.PI / 2, oe.position.set(W.offX || 0, 0, W.offZ || 0), oe.add(D.mesh), oe.add(D.travLines), y.add(oe), D.setTravelVisible(T.current), D.setLayerRange(0, Math.max(0, g.length - 1));
732
- const ge = Dt(v, P.current, h());
733
- D.setColors(ge.color);
734
- const de = { group: oe, ctl: D, seg: v, layers: g };
735
- return c.current[f] = de, de;
809
+ toolpathRef: i,
810
+ segDataRef: o,
811
+ layersDataRef: p,
812
+ plateResultsRef: h,
813
+ plateOffsetsRef: j,
814
+ lineWidthRef: x,
815
+ showTravelRef: B,
816
+ viewTypeRef: z,
817
+ layerLoRef: m,
818
+ layerHiRef: L,
819
+ selectedPlateRef: D,
820
+ extruderColorsRef: R,
821
+ settings: K,
822
+ setSegCount: J,
823
+ setColorRange: _,
824
+ setRoleLegend: T
825
+ } = t;
826
+ function F(v) {
827
+ var V;
828
+ const C = c.current[v];
829
+ C && (C.group.remove(C.ctl.mesh), C.group.remove(C.ctl.travLines), C.ctl.dispose(), (V = r.current.toolpathGroup) == null || V.remove(C.group), delete c.current[v], i.current === C.ctl && (i.current = null, o.current = null));
736
830
  }
737
- function K() {
738
- for (const [f, g] of Object.entries(d.current)) {
739
- const y = Number(f);
740
- if (!g || g.error || !g.layers || !g.layers.length) continue;
741
- const v = c.current[y];
742
- (!v || v.layers !== g.layers) && G(y, g.layers);
743
- }
744
- }
745
- function h() {
746
- const f = (y, v) => {
747
- const D = Je(L, y), W = parseFloat(D);
748
- return Number.isFinite(W) ? W : v;
749
- }, g = f("outer_wall_speed", 60);
831
+ function ce() {
832
+ for (const v of Object.keys(c.current)) F(Number(v));
833
+ i.current = null, o.current = null;
834
+ }
835
+ function pe(v, C) {
836
+ const { toolpathGroup: V } = r.current;
837
+ if (!V || (F(v), !C || !C.length)) return null;
838
+ const f = va(C, x.current), N = wa(W, f), O = j.current[v] || { offX: 0, offZ: 0 }, H = new W.Group();
839
+ H.rotation.x = -Math.PI / 2, H.position.set(O.offX || 0, 0, O.offZ || 0), H.add(N.mesh), H.add(N.travLines), V.add(H), N.setTravelVisible(B.current), N.setLayerRange(0, Math.max(0, C.length - 1));
840
+ const oe = Wn(f, z.current, S());
841
+ N.setColors(oe.color);
842
+ const ue = { group: H, ctl: N, seg: f, layers: C };
843
+ return c.current[v] = ue, ue;
844
+ }
845
+ function w() {
846
+ for (const [v, C] of Object.entries(h.current)) {
847
+ const V = Number(v);
848
+ if (!C || C.error || !C.layers || !C.layers.length) continue;
849
+ const f = c.current[V];
850
+ (!f || f.layers !== C.layers) && pe(V, C.layers);
851
+ }
852
+ }
853
+ function S() {
854
+ const v = (V, f) => {
855
+ const N = He(K, V), O = parseFloat(N);
856
+ return Number.isFinite(O) ? O : f;
857
+ }, C = v("outer_wall_speed", 60);
750
858
  return {
859
+ // The filament palette, so the Filament view can paint each tool in its own colour rather than a stand-in.
860
+ toolColors: (R == null ? void 0 : R.current) ?? [],
751
861
  speedByType: {
752
- 1: g,
753
- 2: f("sparse_infill_speed", 40),
754
- 3: f("internal_solid_infill_speed", 45),
755
- 4: g,
756
- 5: f("support_speed", 35),
757
- 6: f("support_speed", 35),
758
- 7: f("gap_infill_speed", 30),
759
- 8: g,
760
- 9: f("bridge_speed", 25),
761
- 10: f("ironing_speed", 20),
762
- 11: g
862
+ 1: C,
863
+ 2: v("sparse_infill_speed", 40),
864
+ 3: v("internal_solid_infill_speed", 45),
865
+ 4: C,
866
+ 5: v("support_speed", 35),
867
+ 6: v("support_speed", 35),
868
+ 7: v("gap_infill_speed", 30),
869
+ 8: C,
870
+ 9: v("bridge_speed", 25),
871
+ 10: v("ironing_speed", 20),
872
+ 11: C
763
873
  },
764
- firstLayerSpeed: f("initial_layer_speed", 30),
765
- closeFanLayers: f("close_fan_the_first_x_layers", 1),
766
- fanNormal: f("fan_max_speed", 100),
767
- tempNormal: f("nozzle_temperature", 210),
768
- tempFirst: f("nozzle_temperature_initial_layer", f("nozzle_temperature", 210))
874
+ firstLayerSpeed: v("initial_layer_speed", 30),
875
+ closeFanLayers: v("close_fan_the_first_x_layers", 1),
876
+ fanNormal: v("fan_max_speed", 100),
877
+ tempNormal: v("nozzle_temperature", 210),
878
+ tempFirst: v("nozzle_temperature_initial_layer", v("nozzle_temperature", 210))
769
879
  };
770
880
  }
771
881
  function E() {
772
- const f = h();
773
- for (const g of Object.values(c.current)) {
774
- const y = Dt(g.seg, P.current, f);
775
- g.ctl.setColors(y.color), g.ctl === l.current && I({ min: y.min, max: y.max, label: y.label, unit: y.unit, cont: y.cont });
882
+ const v = S();
883
+ for (const C of Object.values(c.current)) {
884
+ const V = Wn(C.seg, z.current, v);
885
+ C.ctl.setColors(V.color), C.ctl === i.current && _({ min: V.min, max: V.max, label: V.label, unit: V.unit, cont: V.cont });
776
886
  }
777
887
  }
778
- function k() {
779
- const f = ee.current, g = m.current || [];
780
- if (!g.length) {
781
- u(f), te(0), l.current = null, r.current = null;
888
+ function M() {
889
+ const v = D.current, C = p.current || [];
890
+ if (!C.length) {
891
+ F(v), J(0), i.current = null, o.current = null;
782
892
  return;
783
893
  }
784
- const y = G(f, g);
785
- if (!y) {
786
- te(0);
894
+ const V = pe(v, C);
895
+ if (!V) {
896
+ J(0);
787
897
  return;
788
898
  }
789
- l.current = y.ctl, r.current = y.seg, y.ctl.setLayerRange(M.current, B.current), E(), te(y.seg.nSeg), A(It(y.seg.typeLengths));
899
+ i.current = V.ctl, o.current = V.seg, V.ctl.setLayerRange(m.current, L.current), E(), J(V.seg.nSeg), T(er(V.seg.typeLengths));
790
900
  }
791
- function j() {
792
- var f;
793
- (f = l.current) == null || f.setLayerRange(M.current, B.current);
901
+ function y() {
902
+ var v;
903
+ (v = i.current) == null || v.setLayerRange(m.current, L.current);
794
904
  }
795
- return { disposePlateToolpath: u, clearToolpaths: _, buildPlateToolpath: G, ensurePlateToolpaths: K, viewCtx: h, applyViewColors: E, rebuildToolpaths: k, applyLayerRange: j };
905
+ return { disposePlateToolpath: F, clearToolpaths: ce, buildPlateToolpath: pe, ensurePlateToolpaths: w, viewCtx: S, applyViewColors: E, rebuildToolpaths: M, applyLayerRange: y };
796
906
  }
797
- function Rr(n) {
907
+ function to(t) {
798
908
  const {
799
- apiRef: a,
909
+ apiRef: r,
800
910
  selectedPlateRef: c,
801
- plateCountRef: l,
802
- placeXRef: r,
803
- plateResultsRef: m,
804
- plateOffsetsRef: d,
805
- plateTpRef: w,
806
- layersDataRef: b,
807
- toolpathRef: T,
808
- segDataRef: P,
809
- layerLoRef: M,
810
- layerHiRef: B,
811
- lineWidthRef: ee,
812
- downgradeRef: L,
813
- settings: te,
814
- canvasMode: I,
815
- downgradeOffer: A,
816
- runSlice: u,
817
- ensurePlateToolpaths: _,
818
- buildPlateToolpath: G,
819
- applyViewColors: K,
820
- disposePlateToolpath: h,
821
- setStats: E,
822
- setOverBed: k,
823
- setLayerCount: j,
824
- setSegCount: f,
825
- setColorRange: g,
826
- setRoleLegend: y,
827
- setGcodeUrl: v,
828
- setLayerLo: D,
829
- setLayerHi: W,
830
- setCanvasMode: oe,
831
- setSlicedPlateCount: ge,
832
- setSliceMenu: de,
833
- setError: Y,
834
- setSliceNotice: C,
835
- setDowngradeOffer: pe,
836
- setSlicing: je,
837
- setProgress: Ce,
838
- setPlateCount: ye,
839
- setSelectedPlate: Re
840
- } = n;
841
- function be(Z) {
842
- var ae;
843
- _();
844
- const q = T.current;
845
- q && q.setLayerRange(0, 1e9);
846
- const z = m.current[Z];
847
- if (!z || z.error || !z.layers || !z.layers.length) {
848
- b.current = null, T.current = null, P.current = null, E(null), k(!1), j(0), f(0), g(null), y([]), v((J) => (J && URL.revokeObjectURL(J), ""));
911
+ plateCountRef: i,
912
+ placeXRef: o,
913
+ plateResultsRef: p,
914
+ plateOffsetsRef: h,
915
+ plateTpRef: j,
916
+ layersDataRef: x,
917
+ toolpathRef: B,
918
+ segDataRef: z,
919
+ layerLoRef: m,
920
+ layerHiRef: L,
921
+ lineWidthRef: D,
922
+ downgradeRef: R,
923
+ settings: K,
924
+ canvasMode: J,
925
+ downgradeOffer: _,
926
+ runSlice: T,
927
+ ensurePlateToolpaths: F,
928
+ buildPlateToolpath: ce,
929
+ applyViewColors: pe,
930
+ disposePlateToolpath: w,
931
+ setStats: S,
932
+ setOverBed: E,
933
+ setLayerCount: M,
934
+ setSegCount: y,
935
+ setColorRange: v,
936
+ setRoleLegend: C,
937
+ setGcodeUrl: V,
938
+ setLayerLo: f,
939
+ setLayerHi: N,
940
+ setCanvasMode: O,
941
+ setSlicedPlateCount: H,
942
+ setSliceMenu: oe,
943
+ setError: ue,
944
+ setSliceNotice: $,
945
+ setDowngradeOffer: P,
946
+ setSlicing: ve,
947
+ setProgress: Me,
948
+ setPlateCount: ge,
949
+ setSelectedPlate: Ee,
950
+ syncPaintSelector: b,
951
+ onSlicedRef: k
952
+ } = t, G = (Q, Z) => {
953
+ var ee;
954
+ Z && !Z.error && ((ee = k == null ? void 0 : k.current) == null || ee.call(k, { plate: Q, stats: Z.stats, gcode: Z.gcode }));
955
+ };
956
+ function q(Q) {
957
+ var le;
958
+ F();
959
+ const Z = B.current;
960
+ Z && Z.setLayerRange(0, 1e9);
961
+ const ee = p.current[Q];
962
+ if (!ee || ee.error || !ee.layers || !ee.layers.length) {
963
+ x.current = null, B.current = null, z.current = null, S(null), E(!1), M(0), y(0), v(null), C([]), V((Ae) => (Ae && URL.revokeObjectURL(Ae), ""));
849
964
  return;
850
965
  }
851
- b.current = z.layers;
852
- const ie = z.layers.length;
853
- M.current = 0, B.current = ie - 1, D(0), W(ie - 1);
854
- const we = w.current[Z], se = we && we.layers === z.layers ? we : G(Z, z.layers);
855
- se && (T.current = se.ctl, P.current = se.seg, se.ctl.setLayerRange(0, ie - 1), K(), f(se.seg.nSeg), y(It(se.seg.typeLengths))), (ae = a.current) == null || ae.onSliced(), oe("preview"), E({
856
- layers: z.stats.layers,
857
- segments: z.stats.path_segments,
858
- filament: z.stats.filament_mm,
859
- timeSec: z.stats.time_estimate,
860
- engine: z.stats.time_engine,
861
- limits: z.stats.machine_limits
862
- }), k(!!z.stats.over_bed), j(ie), v((J) => (J && URL.revokeObjectURL(J), URL.createObjectURL(new Blob([z.gcode], { type: "text/plain" }))));
863
- }
864
- function Se(Z, q) {
865
- const z = URL.createObjectURL(new Blob([Z], { type: "text/plain" })), ie = document.createElement("a");
866
- ie.href = z, ie.download = q, ie.style.display = "none", document.body.appendChild(ie), ie.click(), setTimeout(() => {
867
- ie.remove(), URL.revokeObjectURL(z);
966
+ x.current = ee.layers;
967
+ const he = ee.layers.length;
968
+ m.current = 0, L.current = he - 1, f(0), N(he - 1);
969
+ const me = j.current[Q], fe = me && me.layers === ee.layers ? me : ce(Q, ee.layers);
970
+ fe && (B.current = fe.ctl, z.current = fe.seg, fe.ctl.setLayerRange(0, he - 1), pe(), y(fe.seg.nSeg), C(er(fe.seg.typeLengths))), (le = r.current) == null || le.onSliced(), O("preview"), S({
971
+ layers: ee.stats.layers,
972
+ segments: ee.stats.path_segments,
973
+ filament: ee.stats.filament_mm,
974
+ timeSec: ee.stats.time_estimate,
975
+ engine: ee.stats.time_engine,
976
+ limits: ee.stats.machine_limits
977
+ }), E(!!ee.stats.over_bed), M(he), V((Ae) => (Ae && URL.revokeObjectURL(Ae), URL.createObjectURL(new Blob([ee.gcode], { type: "text/plain" }))));
978
+ }
979
+ function s(Q, Z) {
980
+ const ee = URL.createObjectURL(new Blob([Q], { type: "text/plain" })), he = document.createElement("a");
981
+ he.href = ee, he.download = Z, he.style.display = "none", document.body.appendChild(he), he.click(), setTimeout(() => {
982
+ he.remove(), URL.revokeObjectURL(ee);
868
983
  }, 4e3);
869
984
  }
870
- const i = (Z) => new Promise((q) => setTimeout(q, Z));
871
- function S() {
872
- ge(Object.values(m.current).filter((Z) => Z && !Z.error && Z.gcode).length);
985
+ const d = (Q) => new Promise((Z) => setTimeout(Z, Q));
986
+ function A() {
987
+ H(Object.values(p.current).filter((Q) => Q && !Q.error && Q.gcode).length);
873
988
  }
874
- async function V() {
875
- de(!1);
876
- const Z = Object.entries(m.current).filter(([, q]) => q && !q.error && q.gcode).sort((q, z) => Number(q[0]) - Number(z[0]));
877
- if (!Z.length) {
878
- Y("No slice results to export — slice first");
989
+ async function u() {
990
+ oe(!1);
991
+ const Q = Object.entries(p.current).filter(([, Z]) => Z && !Z.error && Z.gcode).sort((Z, ee) => Number(Z[0]) - Number(ee[0]));
992
+ if (!Q.length) {
993
+ ue("No slice results to export — slice first");
879
994
  return;
880
995
  }
881
- for (const [q, z] of Z)
882
- Se(z.gcode, `plate_${Number(q) + 1}.gcode`), await i(350);
883
- C(`Exported G-code for ${Z.length} plate(s)`);
884
- }
885
- async function p(Z = "current") {
886
- var z, ie;
887
- de(!1), Y(""), C(""), pe(null);
888
- const q = c.current;
889
- if (ee.current = xt(te).line_width, Z === "all") {
890
- je(!0), Ce(0);
891
- let we = 0, se = !1, ae = !1;
892
- const J = [];
893
- for (let ce = 0; ce < l.current; ce++) {
894
- const ke = (z = a.current) == null ? void 0 : z.buildMergedSTL(ce);
895
- if (ke) {
896
- d.current[ce] = { offX: ke.offX, offZ: ke.offZ };
996
+ for (const [Z, ee] of Q)
997
+ s(ee.gcode, `plate_${Number(Z) + 1}.gcode`), await d(350);
998
+ $(`Exported G-code for ${Q.length} plate(s)`);
999
+ }
1000
+ async function I(Q = "current") {
1001
+ var ee, he;
1002
+ oe(!1), ue(""), $(""), P(null);
1003
+ const Z = c.current;
1004
+ if (D.current = ln(K).line_width, Q === "all") {
1005
+ ve(!0), Me(0);
1006
+ let me = 0, fe = !1, le = !1;
1007
+ const Ae = [];
1008
+ for (let Pe = 0; Pe < i.current; Pe++) {
1009
+ const Re = (ee = r.current) == null ? void 0 : ee.buildMergedSTL(Pe);
1010
+ if (Re) {
1011
+ Pe === c.current && (b == null || b(Re)), h.current[Pe] = { offX: Re.offX, offZ: Re.offZ };
897
1012
  try {
898
- const { r: ze, economy: Fe, classicWalls: qe } = await u(ke);
899
- m.current[ce] = ze, S(), we++, Fe && (se = !0), qe && (ae = !0);
1013
+ const { r: qe, economy: dt, classicWalls: Xe } = await T(Re);
1014
+ p.current[Pe] = qe, A(), G(Pe, qe), me++, dt && (fe = !0), Xe && (le = !0);
900
1015
  } catch {
901
- J.push(ce + 1);
1016
+ Ae.push(Pe + 1);
902
1017
  }
903
1018
  }
904
1019
  }
905
- if (je(!1), !we) {
906
- pe({ scope: "all" }), Y("All plates failed to slice (economy mode included) — try the simplified retry");
1020
+ if (ve(!1), !me) {
1021
+ P({ scope: "all" }), ue("All plates failed to slice (economy mode included) — try the simplified retry");
907
1022
  return;
908
1023
  }
909
- J.length ? Y(`Plate ${J.join(", ")} failed — the ${we} finished result(s) are kept (inspect/export from the tabs)`) : (Y(""), pe(null)), se ? C("Memory pressure — some plates finished in economy mode (no preview, G-code is fine)") : ae && C("Arachne wall generation failed (degenerate geometry) — finished with classic walls (G-code is fine)"), be(m.current[q] ? q : Object.keys(m.current).map(Number)[0]);
1024
+ Ae.length ? ue(`Plate ${Ae.join(", ")} failed — the ${me} finished result(s) are kept (inspect/export from the tabs)`) : (ue(""), P(null)), fe ? $("Memory pressure — some plates finished in economy mode (no preview, G-code is fine)") : le && $("Arachne wall generation failed (degenerate geometry) — finished with classic walls (G-code is fine)"), q(p.current[Z] ? Z : Object.keys(p.current).map(Number)[0]);
910
1025
  } else {
911
- const we = performance.now(), se = (ie = a.current) == null ? void 0 : ie.buildMergedSTL(q);
912
- if (!se) {
913
- Y(`Plate ${q + 1} has no objects`);
1026
+ const me = performance.now(), fe = (he = r.current) == null ? void 0 : he.buildMergedSTL(Z);
1027
+ if (!fe) {
1028
+ ue(`Plate ${Z + 1} has no objects`);
914
1029
  return;
915
1030
  }
916
- console.info(`[vp-prof] buildMergedSTL ${(performance.now() - we).toFixed(0)}ms (${(se.buf.byteLength / 1048576).toFixed(1)}MB)`), d.current[q] = { offX: se.offX, offZ: se.offZ }, je(!0), Ce(0);
1031
+ console.info(`[vp-prof] buildMergedSTL ${(performance.now() - me).toFixed(0)}ms (${(fe.buf.byteLength / 1048576).toFixed(1)}MB)`), Z === c.current && (b == null || b(fe)), h.current[Z] = { offX: fe.offX, offZ: fe.offZ }, ve(!0), Me(0);
917
1032
  try {
918
- const { r: ae, economy: J, classicWalls: ce, params: ke } = await u(se);
919
- ae != null && ae.stats && console.info(`[vp-prof] kernel stages p1=${(ae.stats.t_pass1_ms / 1e3).toFixed(1)}s surf=${(ae.stats.t_surface_ms / 1e3).toFixed(1)}s sup=${(ae.stats.t_support_ms / 1e3).toFixed(1)}s emit=${(ae.stats.t_emit_ms / 1e3).toFixed(1)}s reuse=${ke.reuse_stages}`), m.current[q] = ae, S(), je(!1), be(q), Y(""), pe(null), J ? C("Memory pressure — finished in economy mode (no preview, G-code can still be downloaded)") : ce && C("Arachne wall generation failed (degenerate geometry) — finished with classic walls (G-code is fine)");
920
- } catch (ae) {
921
- if (je(!1), String((ae == null ? void 0 : ae.message) || ae).includes("canceled")) {
922
- C("Slice canceled");
1033
+ const { r: le, economy: Ae, classicWalls: Pe, params: Re } = await T(fe);
1034
+ le != null && le.stats && console.info(`[vp-prof] kernel stages p1=${(le.stats.t_pass1_ms / 1e3).toFixed(1)}s surf=${(le.stats.t_surface_ms / 1e3).toFixed(1)}s sup=${(le.stats.t_support_ms / 1e3).toFixed(1)}s emit=${(le.stats.t_emit_ms / 1e3).toFixed(1)}s reuse=${Re.reuse_stages}`), p.current[Z] = le, A(), G(Z, le), ve(!1), q(Z), ue(""), P(null), Ae ? $("Memory pressure — finished in economy mode (no preview, G-code can still be downloaded)") : Pe && $("Arachne wall generation failed (degenerate geometry) — finished with classic walls (G-code is fine)");
1035
+ } catch (le) {
1036
+ if (ve(!1), String((le == null ? void 0 : le.message) || le).includes("canceled")) {
1037
+ $("Slice canceled");
923
1038
  return;
924
1039
  }
925
- pe({ scope: "current" }), Y("Slice failed (economy mode failed too): " + ae.message);
1040
+ P({ scope: "current" }), ue("Slice failed (economy mode failed too): " + le.message);
926
1041
  }
927
1042
  }
928
1043
  }
929
- async function O() {
930
- const Z = A;
931
- if (pe(null), !!Z) {
932
- L.current = !0;
1044
+ async function se() {
1045
+ const Q = _;
1046
+ if (P(null), !!Q) {
1047
+ R.current = !0;
933
1048
  try {
934
- await p(Z.scope);
1049
+ await I(Q.scope);
935
1050
  } finally {
936
- L.current = !1;
1051
+ R.current = !1;
937
1052
  }
938
1053
  }
939
1054
  }
940
- function ue() {
941
- ye((Z) => Math.min(Vt, Z + 1));
1055
+ function ye() {
1056
+ ge((Q) => Math.min(tr, Q + 1));
942
1057
  }
943
- function ve() {
944
- ye((Z) => {
945
- if (Z <= 1) return Z;
946
- const q = Z - 1;
947
- return delete m.current[q], h(q), S(), c.current >= q && Ee(q - 1), q;
1058
+ function xe() {
1059
+ ge((Q) => {
1060
+ if (Q <= 1) return Q;
1061
+ const Z = Q - 1;
1062
+ return delete p.current[Z], w(Z), A(), c.current >= Z && je(Z - 1), Z;
948
1063
  });
949
1064
  }
950
- function Ee(Z) {
951
- c.current = Z, Re(Z), r.current = 0, I === "preview" && be(Z);
1065
+ function je(Q) {
1066
+ c.current = Q, Ee(Q), o.current = 0, J === "preview" && q(Q);
952
1067
  }
953
- return { showPlateResult: be, refreshSlicedCount: S, exportAllGcode: V, onSlice: p, retryDowngrade: O, addPlate: ue, deletePlate: ve, selectPlate: Ee };
1068
+ return { showPlateResult: q, refreshSlicedCount: A, exportAllGcode: u, onSlice: I, retryDowngrade: se, addPlate: ye, deletePlate: xe, selectPlate: je };
954
1069
  }
955
- function Nr(n) {
1070
+ function no(t) {
956
1071
  const {
957
- apiRef: a,
1072
+ apiRef: r,
958
1073
  objectsRef: c,
959
- layersDataRef: l,
960
- segDataRef: r,
961
- plateResultsRef: m,
962
- plateOffsetsRef: d,
963
- clearToolpaths: w,
964
- refreshSlicedCount: b,
965
- dragOver: T,
966
- setError: P,
967
- setTriWarn: M,
968
- setProgress: B,
969
- setStats: ee,
970
- setOverBed: L,
971
- setLayerCount: te,
972
- setSegCount: I,
973
- setColorRange: A,
974
- setSliceNotice: u,
975
- setDowngradeOffer: _,
976
- setGcodeUrl: G,
977
- setCanvasMode: K,
978
- setObjects: h,
979
- setDragOver: E
980
- } = n;
981
- async function k(D) {
982
- var de, Y;
983
- const W = Array.from(D || []).filter((C) => bt.includes(Vn(C.name))), oe = Array.from(D || []).length - W.length;
984
- if (!W.length) {
985
- oe && P("Supported formats: STL/OBJ/3MF/AMF/PLY");
1074
+ layersDataRef: i,
1075
+ segDataRef: o,
1076
+ plateResultsRef: p,
1077
+ plateOffsetsRef: h,
1078
+ clearToolpaths: j,
1079
+ refreshSlicedCount: x,
1080
+ dragOver: B,
1081
+ setError: z,
1082
+ setTriWarn: m,
1083
+ setProgress: L,
1084
+ setStats: D,
1085
+ setOverBed: R,
1086
+ setLayerCount: K,
1087
+ setSegCount: J,
1088
+ setColorRange: _,
1089
+ setSliceNotice: T,
1090
+ setDowngradeOffer: F,
1091
+ setGcodeUrl: ce,
1092
+ setCanvasMode: pe,
1093
+ setObjects: w,
1094
+ setDragOver: S
1095
+ } = t;
1096
+ async function E(f) {
1097
+ var oe, ue;
1098
+ const N = Array.from(f || []).filter(($) => an.includes(ma($.name))), O = Array.from(f || []).length - N.length;
1099
+ if (!N.length) {
1100
+ O && z("Supported formats: STL/OBJ/3MF/AMF/PLY");
986
1101
  return;
987
1102
  }
988
- P(""), M(""), B(0), l.current = null, r.current = null, m.current = {}, d.current = {}, w(), b(), ee(null), L(!1), te(0), I(0), A(null), u(""), _(null), G((C) => (C && URL.revokeObjectURL(C), "")), K("prepare"), (de = a.current) == null || de.showObjects();
989
- let ge = 0;
990
- for (const C of W)
1103
+ z(""), m(""), L(0), i.current = null, o.current = null, p.current = {}, h.current = {}, j(), x(), D(null), R(!1), K(0), J(0), _(null), T(""), F(null), ce(($) => ($ && URL.revokeObjectURL($), "")), pe("prepare"), (oe = r.current) == null || oe.showObjects();
1104
+ let H = 0;
1105
+ for (const $ of N)
991
1106
  try {
992
- const pe = performance.now(), je = await C.arrayBuffer(), Ce = performance.now(), ye = await Fn(C.name, je), Re = performance.now();
993
- for (const be of ye)
994
- (Y = a.current) == null || Y.addObject(be.name, be.modelPos), ge += be.modelPos.length / 9;
995
- console.info(`[vp-prof] load ${C.name}: read ${(Ce - pe).toFixed(0)}ms, parse ${(Re - Ce).toFixed(0)}ms, scene ${(performance.now() - Re).toFixed(0)}ms`);
996
- } catch (pe) {
997
- P(`Failed to load ${C.name}: ${pe && pe.message || pe}`);
1107
+ const P = performance.now(), ve = await $.arrayBuffer(), Me = performance.now(), ge = await fa($.name, ve), Ee = performance.now();
1108
+ for (const b of ge)
1109
+ (ue = r.current) == null || ue.addObject(b.name, b.modelPos), H += b.modelPos.length / 9;
1110
+ console.info(`[vp-prof] load ${$.name}: read ${(Me - P).toFixed(0)}ms, parse ${(Ee - Me).toFixed(0)}ms, scene ${(performance.now() - Ee).toFixed(0)}ms`);
1111
+ } catch (P) {
1112
+ z(`Failed to load ${$.name}: ${P && P.message || P}`);
998
1113
  }
999
- h(c.current.map((C) => ({ id: C.id, name: C.name, extruder: C.extruder, visible: C.visible !== !1 }))), ge > 1e5 && M(`${Math.round(ge).toLocaleString()} triangles — slicing may take a while`);
1114
+ w(c.current.map(($) => ({ id: $.id, name: $.name, extruder: $.extruder, visible: $.visible !== !1 }))), H > 1e5 && m(`${Math.round(H).toLocaleString()} triangles — slicing may take a while`);
1000
1115
  }
1001
- function j(D) {
1002
- k(D.target.files), D.target.value = "";
1116
+ function M(f) {
1117
+ E(f.target.files), f.target.value = "";
1003
1118
  }
1004
- function f(D) {
1005
- var W;
1006
- (W = a.current) == null || W.removeObject(D), h(c.current.map((oe) => ({ id: oe.id, name: oe.name, extruder: oe.extruder, visible: oe.visible !== !1 })));
1119
+ function y(f) {
1120
+ var N;
1121
+ (N = r.current) == null || N.removeObject(f), w(c.current.map((O) => ({ id: O.id, name: O.name, extruder: O.extruder, visible: O.visible !== !1 })));
1007
1122
  }
1008
- function g(D) {
1009
- var W;
1010
- D.preventDefault(), E(!1), k((W = D.dataTransfer) == null ? void 0 : W.files);
1123
+ function v(f) {
1124
+ var N;
1125
+ f.preventDefault(), S(!1), E((N = f.dataTransfer) == null ? void 0 : N.files);
1011
1126
  }
1012
- function y(D) {
1013
- D.preventDefault(), D.dataTransfer && (D.dataTransfer.dropEffect = "copy"), T || E(!0);
1127
+ function C(f) {
1128
+ f.preventDefault(), f.dataTransfer && (f.dataTransfer.dropEffect = "copy"), B || S(!0);
1014
1129
  }
1015
- function v(D) {
1016
- D.currentTarget.contains(D.relatedTarget) || E(!1);
1130
+ function V(f) {
1131
+ f.currentTarget.contains(f.relatedTarget) || S(!1);
1017
1132
  }
1018
- return { loadFiles: k, onFiles: j, removeObject: f, onDrop: g, onDragOver: y, onDragLeave: v };
1133
+ return { loadFiles: E, onFiles: M, removeObject: y, onDrop: v, onDragOver: C, onDragLeave: V };
1134
+ }
1135
+ const on = 0, kt = 1, Bt = 2, $t = 16;
1136
+ function ro(t, r) {
1137
+ return t === "enforcer" ? kt : t === "blocker" ? Bt : t !== "material" || !Number.isInteger(r) || r < 0 ? on : Math.min(r + 1, $t);
1019
1138
  }
1020
- function Ar(n) {
1139
+ function ao(t) {
1021
1140
  const {
1022
- three: a,
1141
+ three: r,
1023
1142
  objectsRef: c,
1024
- apiRef: l,
1025
- getWorker: r,
1026
- selectedPlateRef: m,
1027
- paintXformRef: d,
1028
- paintOverlayRef: w,
1029
- paintModeRef: b,
1030
- setError: T,
1031
- setPaintModeState: P,
1032
- setPaintCounts: M
1033
- } = n;
1034
- function B(I, A) {
1035
- var h, E;
1036
- const u = a.current;
1037
- if (!u.objectsGroup) return;
1038
- const _ = d.current || { cx: 0, cy: 0, minz: 0 }, G = w.current;
1039
- if (G)
1040
- for (const k of ["enf", "blk"]) G[k] && (u.objectsGroup.remove(G[k]), G[k].geometry.dispose(), G[k].material.dispose());
1041
- const K = (k, j) => {
1042
- if (!k || k.length < 9) return null;
1043
- const f = new Float32Array(k.length);
1044
- for (let v = 0; v < k.length; v += 3) {
1045
- const D = k[v], W = k[v + 1], oe = k[v + 2];
1046
- f[v] = D + _.cx, f[v + 1] = oe + _.minz, f[v + 2] = -(W + _.cy);
1143
+ apiRef: i,
1144
+ getWorker: o,
1145
+ selectedPlateRef: p,
1146
+ selectorGeomRef: h,
1147
+ paintXformRef: j,
1148
+ paintOverlayRef: x,
1149
+ paintModeRef: B,
1150
+ materialExtruderRef: z,
1151
+ extruderColorsRef: m,
1152
+ setError: L,
1153
+ setPaintModeState: D,
1154
+ setPaintCounts: R,
1155
+ setPaintStateCounts: K,
1156
+ setSliceNotice: J
1157
+ } = t, _ = (f) => {
1158
+ const N = new Uint8Array(f);
1159
+ let O = N.length;
1160
+ const H = Math.max(1, Math.floor(N.length / 4096));
1161
+ for (let oe = 0; oe < N.length; oe += H) O = O * 31 + N[oe] | 0;
1162
+ return `${N.length}:${O}`;
1163
+ }, T = (f) => f.__paintLastCounts ?? (f.__paintLastCounts = {});
1164
+ function F() {
1165
+ const f = Array.isArray(m == null ? void 0 : m.current) ? m.current.length : 0, N = [];
1166
+ for (let O = 0; O < Math.min(f, $t); O++)
1167
+ N.push(O + 1);
1168
+ return N.includes(kt) || N.push(kt), N.includes(Bt) || N.push(Bt), N.sort((O, H) => O - H);
1169
+ }
1170
+ function ce() {
1171
+ const f = o();
1172
+ if (!f || f.__paintStateStamped) return f;
1173
+ const N = f.postMessage.bind(f);
1174
+ return f.postMessage = (...O) => {
1175
+ const H = O[0];
1176
+ if (H && H.cmd === "overlay" && !H.states)
1177
+ return O[0] = { ...H, states: F() }, N(...O);
1178
+ if (H && H.cmd === "paint") {
1179
+ const oe = ro(B.current, z == null ? void 0 : z.current);
1180
+ if (oe === on) {
1181
+ const ue = { ...H, cmd: "erase", states: F() };
1182
+ delete ue.enforcer, O[0] = ue;
1183
+ } else
1184
+ O[0] = { ...H, state: oe, states: F(), enforcer: oe === kt };
1047
1185
  }
1048
- const g = new N.BufferGeometry();
1049
- g.setAttribute("position", new N.Float32BufferAttribute(f, 3)), g.computeVertexNormals();
1050
- const y = new N.Mesh(g, new N.MeshBasicMaterial({ color: j, transparent: !0, opacity: 0.55, side: N.DoubleSide, depthTest: !1 }));
1051
- return y.renderOrder = 999, u.objectsGroup.add(y), y;
1052
- };
1053
- w.current = { enf: K(I, 2845951), blk: K(A, 14826299) }, (E = (h = a.current).invalidate) == null || E.call(h);
1186
+ return N(...O);
1187
+ }, f.addEventListener("message", (O) => {
1188
+ const H = O.data;
1189
+ if (!H || H.type !== "painted" || !H.counts) return;
1190
+ K == null || K(H.counts);
1191
+ const oe = T(f), ue = Object.entries(H.counts).filter(([$, P]) => oe[$] !== P).map(([$]) => Number($));
1192
+ for (const [$, P] of Object.entries(H.counts)) oe[$] = P;
1193
+ ue.length && N({ cmd: "overlay", states: ue });
1194
+ }), f.__paintStateStamped = !0, f;
1054
1195
  }
1055
- function ee() {
1056
- const I = a.current, A = w.current;
1057
- if (A && I.objectsGroup) for (const u of ["enf", "blk"]) A[u] && (I.objectsGroup.remove(A[u]), A[u].geometry.dispose(), A[u].material.dispose());
1058
- w.current = null;
1196
+ const pe = { 1: "#2b6cff", 2: "#e23b3b" };
1197
+ function w(f) {
1198
+ var N;
1199
+ return B.current === "material" ? ((N = m == null ? void 0 : m.current) == null ? void 0 : N[f - 1]) ?? "#9aa4b2" : pe[f] ?? "#9aa4b2";
1059
1200
  }
1060
- function L(I) {
1061
- var A, u, _;
1062
- if (I !== "off" && c.current.length === 0) {
1063
- T("Upload an STL first");
1064
- return;
1201
+ function S(f) {
1202
+ var oe;
1203
+ const N = r.current, O = x.current, H = (oe = O == null ? void 0 : O.get) == null ? void 0 : oe.call(O, f);
1204
+ H && (N.objectsGroup && N.objectsGroup.remove(H), H.geometry.dispose(), H.material.dispose(), O.delete(f));
1205
+ }
1206
+ function E() {
1207
+ const f = x.current;
1208
+ if (f != null && f.keys)
1209
+ for (const N of [...f.keys()]) S(N);
1210
+ }
1211
+ function M(f, N, O) {
1212
+ var P, ve, Me;
1213
+ const H = r.current;
1214
+ if (!H.objectsGroup) return;
1215
+ const oe = j.current || { cx: 0, cy: 0, minz: 0 };
1216
+ (P = x.current) != null && P.set || (x.current = /* @__PURE__ */ new Map());
1217
+ const ue = (ge, Ee) => {
1218
+ if (!ge || ge.length < 9) return null;
1219
+ const b = new Float32Array(ge.length);
1220
+ for (let q = 0; q < ge.length; q += 3) {
1221
+ const s = ge[q], d = ge[q + 1], A = ge[q + 2];
1222
+ b[q] = s + oe.cx, b[q + 1] = A + oe.minz, b[q + 2] = -(d + oe.cy);
1223
+ }
1224
+ const k = new W.BufferGeometry();
1225
+ k.setAttribute("position", new W.Float32BufferAttribute(b, 3));
1226
+ const G = new W.Mesh(k, new W.MeshBasicMaterial({ color: Ee, transparent: !0, opacity: 0.55, side: W.DoubleSide, depthTest: !1 }));
1227
+ return G.renderOrder = 999, H.objectsGroup.add(G), G;
1228
+ }, $ = O ? Object.keys(O).map(Number).sort((ge, Ee) => ge - Ee).map((ge) => [ge, O[ge]]) : [[kt, f], [Bt, N]];
1229
+ for (const [ge, Ee] of $) {
1230
+ S(ge);
1231
+ const b = ue(Ee, w(ge));
1232
+ b && x.current.set(ge, b);
1233
+ }
1234
+ (Me = (ve = r.current).invalidate) == null || Me.call(ve);
1235
+ }
1236
+ function y() {
1237
+ E(), x.current = null;
1238
+ }
1239
+ function v(f = null) {
1240
+ var oe;
1241
+ const N = f ?? ((oe = i.current) == null ? void 0 : oe.buildMergedSTL(p.current));
1242
+ if (!N) return;
1243
+ j.current = { cx: N.offX, cy: -N.offZ, minz: 0 };
1244
+ const O = h.current, H = _(N.buf);
1245
+ if ((O == null ? void 0 : O.identity) !== H) {
1246
+ const ue = T(o()), $ = Object.values(ue).some((Me) => Me > 0), P = O != null && O.topology === N.topology, ve = ce();
1247
+ if (ve.postMessage({ cmd: "prepare", stl: N.buf, keepPaint: P }), h.current = { identity: H, topology: N.topology }, P)
1248
+ $ && ve.postMessage({ cmd: "overlay" });
1249
+ else {
1250
+ K == null || K({}), R == null || R({ enf: 0, blk: 0 }), y();
1251
+ for (const Me of Object.keys(ue)) delete ue[Me];
1252
+ $ && (J == null || J("The model changed, so the painted regions were reset — the paint is tied to the facets it was brushed onto."));
1253
+ }
1065
1254
  }
1066
- if (I !== "off") {
1067
- const G = (A = l.current) == null ? void 0 : A.buildMergedSTL(m.current);
1068
- if (!G) return;
1069
- d.current = { cx: G.offX, cy: -G.offZ, minz: 0 }, (u = l.current) == null || u.detachTransform(), r().postMessage({ cmd: "prepare", stl: G.buf });
1255
+ }
1256
+ function C(f) {
1257
+ var N, O;
1258
+ if (f !== "off" && c.current.length === 0) {
1259
+ L("Upload an STL first");
1260
+ return;
1070
1261
  }
1071
- b.current = I, P(I), (_ = l.current) == null || _.refreshCursor();
1262
+ f !== "off" && ((N = i.current) == null || N.detachTransform(), v()), B.current = f, D(f), (O = i.current) == null || O.refreshCursor();
1072
1263
  }
1073
- function te() {
1074
- r().postMessage({ cmd: "clear" }), ee(), M({ enf: 0, blk: 0 });
1264
+ function V() {
1265
+ o().postMessage({ cmd: "clear" }), y(), R({ enf: 0, blk: 0 }), K == null || K({});
1075
1266
  }
1076
- return { rebuildPaintOverlay: B, clearPaintOverlay: ee, setPaintMode: L, clearPaint: te };
1267
+ return { rebuildPaintOverlay: M, clearPaintOverlay: y, setPaintMode: C, clearPaint: V, registerSelector: v };
1077
1268
  }
1078
- function _r(n) {
1269
+ function oo(t) {
1079
1270
  const {
1080
- apiRef: a,
1271
+ apiRef: r,
1081
1272
  objectsRef: c,
1082
- clipboardRef: l,
1083
- paintModeRef: r,
1084
- setPaintMode: m,
1085
- removeObject: d,
1086
- refreshObjects: w,
1087
- setError: b,
1088
- setSliceNotice: T
1089
- } = n;
1090
- function P() {
1091
- var _, G, K;
1092
- const A = (_ = a.current) == null ? void 0 : _.selectedObjectId();
1093
- if (!A) {
1094
- b("Select an object to duplicate first");
1273
+ clipboardRef: i,
1274
+ paintModeRef: o,
1275
+ setPaintMode: p,
1276
+ removeObject: h,
1277
+ refreshObjects: j,
1278
+ setError: x,
1279
+ setSliceNotice: B
1280
+ } = t;
1281
+ function z() {
1282
+ var F, ce, pe;
1283
+ const _ = (F = r.current) == null ? void 0 : F.selectedObjectId();
1284
+ if (!_) {
1285
+ x("Select an object to duplicate first");
1095
1286
  return;
1096
1287
  }
1097
- const u = (G = a.current) == null ? void 0 : G.getSnapshot(A);
1098
- u && ((K = a.current) == null || K.spawnSnapshot(u), w(), b(""));
1288
+ const T = (ce = r.current) == null ? void 0 : ce.getSnapshot(_);
1289
+ T && ((pe = r.current) == null || pe.spawnSnapshot(T), j(), x(""));
1099
1290
  }
1100
- function M() {
1101
- var u, _;
1102
- const A = (u = a.current) == null ? void 0 : u.selectedObjectId();
1103
- if (!A) {
1104
- b("Select an object to copy first");
1291
+ function m() {
1292
+ var T, F;
1293
+ const _ = (T = r.current) == null ? void 0 : T.selectedObjectId();
1294
+ if (!_) {
1295
+ x("Select an object to copy first");
1105
1296
  return;
1106
1297
  }
1107
- l.current = (_ = a.current) == null ? void 0 : _.getSnapshot(A), b(""), T("Object copied (paste with Ctrl+V)");
1298
+ i.current = (F = r.current) == null ? void 0 : F.getSnapshot(_), x(""), B("Object copied (paste with Ctrl+V)");
1108
1299
  }
1109
- function B() {
1110
- var A;
1111
- l.current && ((A = a.current) == null || A.spawnSnapshot(l.current), w());
1300
+ function L() {
1301
+ var _;
1302
+ i.current && ((_ = r.current) == null || _.spawnSnapshot(i.current), j());
1112
1303
  }
1113
- function ee() {
1114
- var u;
1115
- const A = (u = a.current) == null ? void 0 : u.selectedObjectId();
1116
- if (!A) {
1117
- b("Select an object to delete first");
1304
+ function D() {
1305
+ var T;
1306
+ const _ = (T = r.current) == null ? void 0 : T.selectedObjectId();
1307
+ if (!_) {
1308
+ x("Select an object to delete first");
1118
1309
  return;
1119
1310
  }
1120
- d(A), b("");
1311
+ h(_), x("");
1121
1312
  }
1122
- function L() {
1123
- var u;
1124
- const A = c.current.map((_) => _.id);
1125
- if (A.length) {
1126
- for (const _ of A) (u = a.current) == null || u.removeObject(_);
1127
- w(), T(`Deleted all ${A.length} object(s)`);
1128
- }
1129
- }
1130
- function te() {
1131
- var K, h;
1132
- const A = (K = a.current) == null ? void 0 : K.selectedObjectId();
1133
- if (!A) {
1134
- b("Select an object to split first");
1313
+ function R() {
1314
+ var T;
1315
+ const _ = c.current.map((F) => F.id);
1316
+ if (_.length) {
1317
+ for (const F of _) (T = r.current) == null || T.removeObject(F);
1318
+ j(), B(`Deleted all ${_.length} object(s)`);
1319
+ }
1320
+ }
1321
+ function K() {
1322
+ var pe, w;
1323
+ const _ = (pe = r.current) == null ? void 0 : pe.selectedObjectId();
1324
+ if (!_) {
1325
+ x("Select an object to split first");
1135
1326
  return;
1136
1327
  }
1137
- const u = (h = a.current) == null ? void 0 : h.getSnapshot(A);
1138
- if (!u) return;
1139
- let _;
1328
+ const T = (w = r.current) == null ? void 0 : w.getSnapshot(_);
1329
+ if (!T) return;
1330
+ let F;
1140
1331
  try {
1141
- _ = Gn(u.localPos);
1142
- } catch (E) {
1143
- b("Split failed: " + ((E == null ? void 0 : E.message) || E));
1332
+ F = ha(T.localPos);
1333
+ } catch (S) {
1334
+ x("Split failed: " + ((S == null ? void 0 : S.message) || S));
1144
1335
  return;
1145
1336
  }
1146
- if (!_ || _.length < 2) {
1147
- b("No separate parts to split — this is a single connected mesh");
1337
+ if (!F || F.length < 2) {
1338
+ x("No separate parts to split — this is a single connected mesh");
1148
1339
  return;
1149
1340
  }
1150
- const G = String(u.name || "object").replace(/\.[^.]+$/, "");
1151
- d(A), _.forEach((E, k) => {
1152
- var j;
1153
- return (j = a.current) == null ? void 0 : j.spawnSnapshot(
1154
- { name: `${G}_${k + 1}`, localPos: E, rot: u.rot, scale: u.scale, pos: u.pos },
1341
+ const ce = String(T.name || "object").replace(/\.[^.]+$/, "");
1342
+ h(_), F.forEach((S, E) => {
1343
+ var M;
1344
+ return (M = r.current) == null ? void 0 : M.spawnSnapshot(
1345
+ { name: `${ce}_${E + 1}`, localPos: S, rot: T.rot, scale: T.scale, pos: T.pos },
1155
1346
  !0
1156
1347
  );
1157
- }), w(), b(""), T(`Split into ${_.length} objects`);
1348
+ }), j(), x(""), B(`Split into ${F.length} objects`);
1158
1349
  }
1159
- function I(A) {
1160
- var u;
1161
- r.current !== "off" && m("off"), (u = a.current) == null || u.setMode(A);
1350
+ function J(_) {
1351
+ var T;
1352
+ o.current !== "off" && p("off"), (T = r.current) == null || T.setMode(_);
1162
1353
  }
1163
- return { duplicateSelected: P, copySelected: M, pasteClipboard: B, deleteSelected: ee, deleteAllObjects: L, splitSelected: te, setGizmo: I };
1354
+ return { duplicateSelected: z, copySelected: m, pasteClipboard: L, deleteSelected: D, deleteAllObjects: R, splitSelected: K, setGizmo: J };
1164
1355
  }
1165
- function zr({ showTabs: n, canvasMode: a, onCanvasMode: c, previewEnabled: l, onOpen: r }) {
1166
- return /* @__PURE__ */ e.createElement("header", { className: "topbar" }, /* @__PURE__ */ e.createElement("div", { className: "tb-left" }, /* @__PURE__ */ e.createElement("a", { href: "/", className: "tb-logo" }, /* @__PURE__ */ e.createElement("b", null, "Three"), "Slicer ", /* @__PURE__ */ e.createElement("span", { className: "tb-re" }, "RE")), /* @__PURE__ */ e.createElement("button", { className: "tb-btn", onClick: r, title: "Open a model file — every existing object is cleared", "data-testid": "open-file" }, /* @__PURE__ */ e.createElement("img", { src: mr, alt: "" }), /* @__PURE__ */ e.createElement("span", null, "Open"))), n && /* @__PURE__ */ e.createElement("div", { className: "tb-tabs", role: "tablist", "aria-label": "Canvas mode" }, /* @__PURE__ */ e.createElement("button", { role: "tab", className: a === "prepare" ? "on" : "", onClick: () => c("prepare"), "data-testid": "mode-prepare", title: "Arrange, transform and paint supports" }, "Prepare"), /* @__PURE__ */ e.createElement("button", { role: "tab", className: a === "preview" ? "on" : "", onClick: () => c("preview"), disabled: !l, "data-testid": "mode-preview", title: "Preview the sliced toolpaths (enabled after slicing)" }, "Preview")), /* @__PURE__ */ e.createElement("div", { className: "tb-right" }, /* @__PURE__ */ e.createElement("button", { className: "tb-icon", disabled: !0, title: "Undo — not implemented (needs an action history stack)" }, "↶"), /* @__PURE__ */ e.createElement("button", { className: "tb-icon", disabled: !0, title: "Redo — not implemented (needs an action history stack)" }, "↷")));
1356
+ function lo({ showTabs: t, canvasMode: r, onCanvasMode: c, previewEnabled: i, onOpen: o }) {
1357
+ return /* @__PURE__ */ e.createElement("header", { className: "topbar" }, /* @__PURE__ */ e.createElement("div", { className: "tb-left" }, /* @__PURE__ */ e.createElement("a", { href: "/", className: "tb-logo" }, /* @__PURE__ */ e.createElement("b", null, "Three"), "Slicer ", /* @__PURE__ */ e.createElement("span", { className: "tb-re" }, "RE")), /* @__PURE__ */ e.createElement("button", { className: "tb-btn", onClick: o, title: "Open a model file — every existing object is cleared", "data-testid": "open-file" }, /* @__PURE__ */ e.createElement("img", { src: $a, alt: "" }), /* @__PURE__ */ e.createElement("span", null, "Open"))), t && /* @__PURE__ */ e.createElement("div", { className: "tb-tabs", role: "tablist", "aria-label": "Canvas mode" }, /* @__PURE__ */ e.createElement("button", { role: "tab", className: r === "prepare" ? "on" : "", onClick: () => c("prepare"), "data-testid": "mode-prepare", title: "Arrange, transform and paint supports" }, "Prepare"), /* @__PURE__ */ e.createElement("button", { role: "tab", className: r === "preview" ? "on" : "", onClick: () => c("preview"), disabled: !i, "data-testid": "mode-preview", title: "Preview the sliced toolpaths (enabled after slicing)" }, "Preview")), /* @__PURE__ */ e.createElement("div", { className: "tb-right" }, /* @__PURE__ */ e.createElement("button", { className: "tb-icon", disabled: !0, title: "Undo — not implemented (needs an action history stack)" }, "↶"), /* @__PURE__ */ e.createElement("button", { className: "tb-icon", disabled: !0, title: "Redo — not implemented (needs an action history stack)" }, "↷")));
1167
1358
  }
1168
- function Pr({ gizmoMode: n, paintMode: a, onGizmo: c, onTogglePaint: l }) {
1169
- const r = (m) => n === m && a === "off" ? "on" : "";
1170
- return /* @__PURE__ */ e.createElement("nav", { className: "left-rail", role: "toolbar", "aria-label": "Gizmo tools" }, /* @__PURE__ */ e.createElement("button", { className: r("translate"), onClick: () => c("translate"), title: "Move the object — drag a gizmo axis or use arrow keys for 10mm (Shift 1mm) (M/G)", "data-testid": "gizmo-move" }, /* @__PURE__ */ e.createElement("img", { src: dr, alt: "Move" })), /* @__PURE__ */ e.createElement("button", { className: r("rotate"), onClick: () => c("rotate"), title: "Rotate the object — PageUp/PageDown rotates in 45° steps (R)", "data-testid": "gizmo-rotate" }, /* @__PURE__ */ e.createElement("img", { src: gr, alt: "Rotate" })), /* @__PURE__ */ e.createElement("button", { className: r("scale"), onClick: () => c("scale"), title: "Scale the object (S)", "data-testid": "gizmo-scale" }, /* @__PURE__ */ e.createElement("img", { src: br, alt: "Scale" })), /* @__PURE__ */ e.createElement("div", { className: "rail-sep" }), /* @__PURE__ */ e.createElement("button", { className: a !== "off" ? "on" : "", onClick: l, title: "Brush facets to enforce or block support — the wheel changes the brush size", "data-testid": "gizmo-paint" }, /* @__PURE__ */ e.createElement("img", { src: hr, alt: "Support painting" })));
1359
+ function so({ gizmoMode: t, paintMode: r, onGizmo: c, onTogglePaint: i }) {
1360
+ const o = (p) => t === p && r === "off" ? "on" : "";
1361
+ return /* @__PURE__ */ e.createElement("nav", { className: "left-rail", role: "toolbar", "aria-label": "Gizmo tools" }, /* @__PURE__ */ e.createElement("button", { className: o("translate"), onClick: () => c("translate"), title: "Move the object — drag a gizmo axis or use arrow keys for 10mm (Shift 1mm) (M/G)", "data-testid": "gizmo-move" }, /* @__PURE__ */ e.createElement("img", { src: Fa, alt: "Move" })), /* @__PURE__ */ e.createElement("button", { className: o("rotate"), onClick: () => c("rotate"), title: "Rotate the object — PageUp/PageDown rotates in 45° steps (R)", "data-testid": "gizmo-rotate" }, /* @__PURE__ */ e.createElement("img", { src: Va, alt: "Rotate" })), /* @__PURE__ */ e.createElement("button", { className: o("scale"), onClick: () => c("scale"), title: "Scale the object (S)", "data-testid": "gizmo-scale" }, /* @__PURE__ */ e.createElement("img", { src: Ga, alt: "Scale" })), /* @__PURE__ */ e.createElement("div", { className: "rail-sep" }), /* @__PURE__ */ e.createElement("button", { className: r !== "off" ? "on" : "", onClick: i, title: "Brush facets to enforce or block support — the wheel changes the brush size", "data-testid": "gizmo-paint" }, /* @__PURE__ */ e.createElement("img", { src: Za, alt: "Support painting" })));
1171
1362
  }
1172
- function Lr({ tools: n }) {
1173
- return /* @__PURE__ */ e.createElement("div", { className: "vp-top-toolbar", role: "toolbar", "aria-label": "Object tools" }, n.map((a, c) => {
1174
- var l;
1175
- return a.sep ? /* @__PURE__ */ e.createElement("span", { key: "sep" + c, className: "vtt-sep" }) : /* @__PURE__ */ e.createElement(
1363
+ function io({ tools: t }) {
1364
+ return /* @__PURE__ */ e.createElement("div", { className: "vp-top-toolbar", role: "toolbar", "aria-label": "Object tools" }, t.map((r, c) => {
1365
+ var i;
1366
+ return r.sep ? /* @__PURE__ */ e.createElement("span", { key: "sep" + c, className: "vtt-sep" }) : /* @__PURE__ */ e.createElement(
1176
1367
  "button",
1177
1368
  {
1178
- key: a.id,
1179
- onClick: a.run,
1180
- disabled: ((l = a.disabled) == null ? void 0 : l.call(a)) ?? !a.run,
1181
- title: a.tip,
1182
- "data-testid": `tool-${a.id}`
1369
+ key: r.id,
1370
+ onClick: r.run,
1371
+ disabled: ((i = r.disabled) == null ? void 0 : i.call(r)) ?? !r.run,
1372
+ title: r.tip,
1373
+ "data-testid": `tool-${r.id}`
1183
1374
  },
1184
- /* @__PURE__ */ e.createElement("img", { src: a.icon, alt: a.label })
1375
+ /* @__PURE__ */ e.createElement("img", { src: r.icon, alt: r.label })
1185
1376
  );
1186
1377
  }));
1187
1378
  }
1188
- function Or({ menu: n, onClose: a, canPaste: c, actions: l }) {
1189
- const r = (m) => () => {
1190
- a(), m == null || m();
1379
+ function co({ menu: t, onClose: r, canPaste: c, actions: i }) {
1380
+ const o = (p) => () => {
1381
+ r(), p == null || p();
1191
1382
  };
1192
- return /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "ctx-scrim", onPointerDown: a, onContextMenu: (m) => {
1193
- m.preventDefault(), a();
1194
- } }), /* @__PURE__ */ e.createElement("div", { className: "ctx-menu", style: { left: n.x, top: n.y }, "data-testid": "ctx-menu" }, n.onObject ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("button", { onClick: r(l.duplicate), "data-testid": "ctx-duplicate", title: "Duplicate the selected object at the same scale and rotation, placed beside it" }, "Duplicate ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+K")), /* @__PURE__ */ e.createElement("button", { onClick: r(l.copy), "data-testid": "ctx-copy", title: "Copy the selected object to the buffer (paste to add it)" }, "Copy ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+C")), /* @__PURE__ */ e.createElement("button", { onClick: r(l.split), "data-testid": "ctx-split", title: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)" }, "Split"), /* @__PURE__ */ e.createElement("button", { onClick: r(l.placeOnBed), "data-testid": "ctx-seat", title: "Drop every object onto the bed (Z=0)" }, "Place on bed"), /* @__PURE__ */ e.createElement("hr", null), /* @__PURE__ */ e.createElement("button", { className: "danger", onClick: r(l.remove), "data-testid": "ctx-delete", title: "Remove the selected object from the scene" }, "Delete ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Del"))) : /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("button", { onClick: r(l.openFile), "data-testid": "ctx-open", title: "Open a model file and add it to the current plate" }, "Open model…"), /* @__PURE__ */ e.createElement("button", { disabled: !c, onClick: r(l.paste), "data-testid": "ctx-paste", title: "Add the copied object to the current plate" }, "Paste ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+V")), /* @__PURE__ */ e.createElement("hr", null), /* @__PURE__ */ e.createElement("button", { onClick: r(l.zoomAll), "data-testid": "ctx-zoom-all", title: "Fit the camera so every object is visible" }, "Zoom all ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Z")), /* @__PURE__ */ e.createElement("button", { onClick: r(l.zoomBed), "data-testid": "ctx-zoom-bed", title: "Fit the camera to the whole selected plate" }, "Zoom bed ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "B")))));
1383
+ return /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "ctx-scrim", onPointerDown: r, onContextMenu: (p) => {
1384
+ p.preventDefault(), r();
1385
+ } }), /* @__PURE__ */ e.createElement("div", { className: "ctx-menu", style: { left: t.x, top: t.y }, "data-testid": "ctx-menu" }, t.onObject ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("button", { onClick: o(i.duplicate), "data-testid": "ctx-duplicate", title: "Duplicate the selected object at the same scale and rotation, placed beside it" }, "Duplicate ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+K")), /* @__PURE__ */ e.createElement("button", { onClick: o(i.copy), "data-testid": "ctx-copy", title: "Copy the selected object to the buffer (paste to add it)" }, "Copy ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+C")), /* @__PURE__ */ e.createElement("button", { onClick: o(i.split), "data-testid": "ctx-split", title: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)" }, "Split"), /* @__PURE__ */ e.createElement("button", { onClick: o(i.placeOnBed), "data-testid": "ctx-seat", title: "Drop every object onto the bed (Z=0)" }, "Place on bed"), /* @__PURE__ */ e.createElement("hr", null), /* @__PURE__ */ e.createElement("button", { className: "danger", onClick: o(i.remove), "data-testid": "ctx-delete", title: "Remove the selected object from the scene" }, "Delete ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Del"))) : /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("button", { onClick: o(i.openFile), "data-testid": "ctx-open", title: "Open a model file and add it to the current plate" }, "Open model…"), /* @__PURE__ */ e.createElement("button", { disabled: !c, onClick: o(i.paste), "data-testid": "ctx-paste", title: "Add the copied object to the current plate" }, "Paste ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Ctrl+V")), /* @__PURE__ */ e.createElement("hr", null), /* @__PURE__ */ e.createElement("button", { onClick: o(i.zoomAll), "data-testid": "ctx-zoom-all", title: "Fit the camera so every object is visible" }, "Zoom all ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "Z")), /* @__PURE__ */ e.createElement("button", { onClick: o(i.zoomBed), "data-testid": "ctx-zoom-bed", title: "Fit the camera to the whole selected plate" }, "Zoom bed ", /* @__PURE__ */ e.createElement("span", { className: "ctx-key" }, "B")))));
1386
+ }
1387
+ function po({ onClose: t }) {
1388
+ return /* @__PURE__ */ e.createElement("div", { className: "help-overlay", "data-testid": "help-overlay", onClick: t }, /* @__PURE__ */ e.createElement("div", { className: "help-card", onClick: (r) => r.stopPropagation() }, /* @__PURE__ */ e.createElement("h3", null, "Shortcuts ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "(press ? to close)")), /* @__PURE__ */ e.createElement("div", { className: "help-cols" }, /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Prepare"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "M / G"), /* @__PURE__ */ e.createElement("dd", null, "Move"), /* @__PURE__ */ e.createElement("dt", null, "R"), /* @__PURE__ */ e.createElement("dd", null, "Rotate"), /* @__PURE__ */ e.createElement("dt", null, "S"), /* @__PURE__ */ e.createElement("dd", null, "Scale"), /* @__PURE__ */ e.createElement("dt", null, "Arrow keys"), /* @__PURE__ */ e.createElement("dd", null, "Move 10mm (Shift 1mm)"), /* @__PURE__ */ e.createElement("dt", null, "PageUp/Down"), /* @__PURE__ */ e.createElement("dd", null, "Rotate 45°"), /* @__PURE__ */ e.createElement("dt", null, "Del"), /* @__PURE__ */ e.createElement("dd", null, "Delete selection"), /* @__PURE__ */ e.createElement("dt", null, "Esc"), /* @__PURE__ */ e.createElement("dd", null, "Clear selection / leave paint"))), /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Preview"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "↑ / ↓"), /* @__PURE__ */ e.createElement("dd", null, "Layer (Shift steps of 10)"), /* @__PURE__ */ e.createElement("dt", null, "L"), /* @__PURE__ */ e.createElement("dd", null, "Single layer"), /* @__PURE__ */ e.createElement("dt", null, "T"), /* @__PURE__ */ e.createElement("dd", null, "Travel"), /* @__PURE__ */ e.createElement("dt", null, "Esc"), /* @__PURE__ */ e.createElement("dd", null, "Back to Prepare"))), /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Common"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "Ctrl+R"), /* @__PURE__ */ e.createElement("dd", null, "Slice"), /* @__PURE__ */ e.createElement("dt", null, "Ctrl+K / ⌘D"), /* @__PURE__ */ e.createElement("dd", null, "Duplicate"), /* @__PURE__ */ e.createElement("dt", null, "Ctrl+C/V/X"), /* @__PURE__ */ e.createElement("dd", null, "Copy / paste / cut"), /* @__PURE__ */ e.createElement("dt", null, "Z / B"), /* @__PURE__ */ e.createElement("dd", null, "Zoom all / zoom bed"))))));
1389
+ }
1390
+ function uo(t) {
1391
+ const r = String(t ?? "").trim().replace(/^#/, "");
1392
+ if (r.length !== 3 && r.length !== 6) return "#ffffff";
1393
+ const c = r.length === 3 ? r.split("").map((x) => x + x).join("") : r, i = [0, 2, 4].map((x) => parseInt(c.slice(x, x + 2), 16) / 255);
1394
+ if (i.some(Number.isNaN)) return "#ffffff";
1395
+ const [o, p, h] = i;
1396
+ return 0.2126 * o + 0.7152 * p + 0.0722 * h > 0.55 ? "#10141a" : "#ffffff";
1195
1397
  }
1196
- function Tr({ onClose: n }) {
1197
- return /* @__PURE__ */ e.createElement("div", { className: "help-overlay", "data-testid": "help-overlay", onClick: n }, /* @__PURE__ */ e.createElement("div", { className: "help-card", onClick: (a) => a.stopPropagation() }, /* @__PURE__ */ e.createElement("h3", null, "Shortcuts ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "(press ? to close)")), /* @__PURE__ */ e.createElement("div", { className: "help-cols" }, /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Prepare"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "M / G"), /* @__PURE__ */ e.createElement("dd", null, "Move"), /* @__PURE__ */ e.createElement("dt", null, "R"), /* @__PURE__ */ e.createElement("dd", null, "Rotate"), /* @__PURE__ */ e.createElement("dt", null, "S"), /* @__PURE__ */ e.createElement("dd", null, "Scale"), /* @__PURE__ */ e.createElement("dt", null, "Arrow keys"), /* @__PURE__ */ e.createElement("dd", null, "Move 10mm (Shift 1mm)"), /* @__PURE__ */ e.createElement("dt", null, "PageUp/Down"), /* @__PURE__ */ e.createElement("dd", null, "Rotate 45°"), /* @__PURE__ */ e.createElement("dt", null, "Del"), /* @__PURE__ */ e.createElement("dd", null, "Delete selection"), /* @__PURE__ */ e.createElement("dt", null, "Esc"), /* @__PURE__ */ e.createElement("dd", null, "Clear selection / leave paint"))), /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Preview"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "↑ / ↓"), /* @__PURE__ */ e.createElement("dd", null, "Layer (Shift steps of 10)"), /* @__PURE__ */ e.createElement("dt", null, "L"), /* @__PURE__ */ e.createElement("dd", null, "Single layer"), /* @__PURE__ */ e.createElement("dt", null, "T"), /* @__PURE__ */ e.createElement("dd", null, "Travel"), /* @__PURE__ */ e.createElement("dt", null, "Esc"), /* @__PURE__ */ e.createElement("dd", null, "Back to Prepare"))), /* @__PURE__ */ e.createElement("section", null, /* @__PURE__ */ e.createElement("h4", null, "Common"), /* @__PURE__ */ e.createElement("dl", null, /* @__PURE__ */ e.createElement("dt", null, "Ctrl+R"), /* @__PURE__ */ e.createElement("dd", null, "Slice"), /* @__PURE__ */ e.createElement("dt", null, "Ctrl+K / ⌘D"), /* @__PURE__ */ e.createElement("dd", null, "Duplicate"), /* @__PURE__ */ e.createElement("dt", null, "Ctrl+C/V/X"), /* @__PURE__ */ e.createElement("dd", null, "Copy / paste / cut"), /* @__PURE__ */ e.createElement("dt", null, "Z / B"), /* @__PURE__ */ e.createElement("dd", null, "Zoom all / zoom bed"))))));
1398
+ function Dt(t, r) {
1399
+ if (!t) return 0;
1400
+ const c = (Array.isArray(t), t[r]);
1401
+ return Number.isFinite(c) ? c : 0;
1198
1402
  }
1199
- function Br({ paintMode: n, onPaintMode: a, onClear: c, brushRadius: l, onBrushRadius: r, paintCounts: m }) {
1200
- return /* @__PURE__ */ e.createElement("div", { className: "brush-panel", "data-testid": "paint-tools" }, /* @__PURE__ */ e.createElement("div", { className: "bp-title" }, "Support painting"), /* @__PURE__ */ e.createElement("div", { className: "bp-modes" }, /* @__PURE__ */ e.createElement("button", { className: n === "enforcer" ? "on enf" : "enf", onClick: () => a("enforcer"), title: "Force support under the painted facets", "data-testid": "paint-enforcer" }, "enforcer"), /* @__PURE__ */ e.createElement("button", { className: n === "blocker" ? "on blk" : "blk", onClick: () => a("blocker"), title: "Block support under the painted facets", "data-testid": "paint-blocker" }, "blocker"), /* @__PURE__ */ e.createElement("button", { onClick: c, "data-testid": "paint-clear", title: "Clear every painted enforcer/blocker area" }, "Clear"), /* @__PURE__ */ e.createElement("button", { onClick: () => a("off"), "data-testid": "paint-off", title: "Leave painting mode (Esc)" }, "Close")), /* @__PURE__ */ e.createElement("label", { className: "bp-radius" }, "Brush radius ", l, "mm", /* @__PURE__ */ e.createElement(
1403
+ const mo = [
1404
+ ["brush", "brush", "Drag a radius cursor over the model"],
1405
+ ["smart", "smart", "Click once to flood the smooth feature under the cursor (Smart fill)"],
1406
+ ["bucket", "bucket", "Click once to flood everything sharing the clicked spot's current mark"],
1407
+ ["triangle", "triangle", "Click to mark exactly one facet"]
1408
+ ], It = (t) => t === "smart" || t === "bucket" || t === "triangle";
1409
+ function ar({ tool: t = "brush", onTool: r, cursor: c = "sphere", onCursor: i, fillAngle: o = 30, onFillAngle: p }) {
1410
+ return /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "bp-modes bp-tools" }, mo.map(([h, j, x]) => /* @__PURE__ */ e.createElement(
1411
+ "button",
1412
+ {
1413
+ key: h,
1414
+ className: t === h ? "bp-tool on" : "bp-tool",
1415
+ onClick: () => r == null ? void 0 : r(h),
1416
+ title: x,
1417
+ "data-testid": `paint-tool-${h}`
1418
+ },
1419
+ j
1420
+ ))), t === "brush" && /* @__PURE__ */ e.createElement("div", { className: "bp-modes bp-tools" }, /* @__PURE__ */ e.createElement(
1421
+ "button",
1422
+ {
1423
+ className: c === "sphere" ? "bp-tool on" : "bp-tool",
1424
+ onClick: () => i == null ? void 0 : i("sphere"),
1425
+ title: "Sphere — a ball around the hit, so the far side of a thin wall is painted too",
1426
+ "data-testid": "paint-cursor-sphere"
1427
+ },
1428
+ "sphere"
1429
+ ), /* @__PURE__ */ e.createElement(
1430
+ "button",
1431
+ {
1432
+ className: c === "circle" ? "bp-tool on" : "bp-tool",
1433
+ onClick: () => i == null ? void 0 : i("circle"),
1434
+ title: "Circle — a disc facing the camera, so only the surface you are looking at",
1435
+ "data-testid": "paint-cursor-circle"
1436
+ },
1437
+ "circle"
1438
+ )), (t === "smart" || t === "bucket") && /* @__PURE__ */ e.createElement("label", { className: "bp-radius" }, "Fill angle ", o, "°", /* @__PURE__ */ e.createElement(
1439
+ "input",
1440
+ {
1441
+ type: "range",
1442
+ min: "1",
1443
+ max: "90",
1444
+ step: "1",
1445
+ value: o,
1446
+ onChange: (h) => p == null ? void 0 : p(parseFloat(h.target.value)),
1447
+ "data-testid": "paint-fill-angle"
1448
+ }
1449
+ )));
1450
+ }
1451
+ function fo({
1452
+ colors: t,
1453
+ activeExtruder: r,
1454
+ onSelectExtruder: c,
1455
+ onClear: i,
1456
+ onClose: o,
1457
+ brushRadius: p,
1458
+ onBrushRadius: h,
1459
+ paintCounts: j,
1460
+ paintTool: x,
1461
+ onPaintTool: B,
1462
+ brushCursor: z,
1463
+ onBrushCursor: m,
1464
+ fillAngle: L,
1465
+ onFillAngle: D
1466
+ }) {
1467
+ const R = Array.isArray(t) ? t : [], K = Number.isFinite(p) ? p : 5, J = r == null;
1468
+ return /* @__PURE__ */ e.createElement("div", { className: "brush-panel", "data-testid": "material-paint-tools" }, /* @__PURE__ */ e.createElement("div", { className: "bp-title" }, "Material painting"), /* @__PURE__ */ e.createElement("div", { className: "bp-modes" }, R.map((_, T) => /* @__PURE__ */ e.createElement(
1469
+ "button",
1470
+ {
1471
+ key: T,
1472
+ className: r === T ? "bp-chip on" : "bp-chip",
1473
+ style: { background: _, color: uo(_) },
1474
+ onClick: () => c == null ? void 0 : c(T),
1475
+ title: T === 0 ? "T1 is the default extruder — painting it takes a region back from another tool (same result as the eraser)" : `Paint the brushed facets to print with T${T + 1}`,
1476
+ "data-testid": `material-chip-${T}`
1477
+ },
1478
+ "T",
1479
+ T + 1
1480
+ )), /* @__PURE__ */ e.createElement(
1481
+ "button",
1482
+ {
1483
+ className: J ? "bp-chip bp-eraser on" : "bp-chip bp-eraser",
1484
+ onClick: () => c == null ? void 0 : c(null),
1485
+ title: "Erase — return the brushed facets to the default extruder",
1486
+ "data-testid": "material-eraser"
1487
+ },
1488
+ "erase"
1489
+ )), /* @__PURE__ */ e.createElement("div", { className: "bp-modes bp-actions" }, /* @__PURE__ */ e.createElement(
1490
+ "button",
1491
+ {
1492
+ onClick: () => i == null ? void 0 : i(),
1493
+ "data-testid": "material-paint-clear",
1494
+ title: "Clear every material-painted region on this object"
1495
+ },
1496
+ "Clear"
1497
+ ), /* @__PURE__ */ e.createElement(
1498
+ "button",
1499
+ {
1500
+ onClick: () => o == null ? void 0 : o(),
1501
+ "data-testid": "material-paint-close",
1502
+ title: "Leave painting mode (Esc)"
1503
+ },
1504
+ "Close"
1505
+ )), /* @__PURE__ */ e.createElement(
1506
+ ar,
1507
+ {
1508
+ tool: x,
1509
+ onTool: B,
1510
+ cursor: z,
1511
+ onCursor: m,
1512
+ fillAngle: L,
1513
+ onFillAngle: D
1514
+ }
1515
+ ), !It(x) && /* @__PURE__ */ e.createElement("label", { className: "bp-radius" }, "Brush radius ", K, "mm", /* @__PURE__ */ e.createElement(
1516
+ "input",
1517
+ {
1518
+ type: "range",
1519
+ min: "1",
1520
+ max: "15",
1521
+ step: "0.5",
1522
+ value: K,
1523
+ onChange: (_) => h == null ? void 0 : h(parseFloat(_.target.value)),
1524
+ "data-testid": "material-brush-radius"
1525
+ }
1526
+ )), /* @__PURE__ */ e.createElement("div", { className: "muted bp-counts", "data-testid": "material-paint-counts" }, R.map((_, T) => `T${T + 1} ${Dt(j, T)}`).join(" · "), R.length > 0 && " · ", It(x) ? "click the model to fill" : "drag over the model to paint"), /* @__PURE__ */ e.createElement("div", { className: "bp-note", "data-testid": "material-paint-note" }, "Painted regions follow the paint outline per layer; a painted flat face fills that surface's shell."));
1527
+ }
1528
+ function ho({
1529
+ paintMode: t,
1530
+ onPaintMode: r,
1531
+ onClear: c,
1532
+ brushRadius: i,
1533
+ onBrushRadius: o,
1534
+ paintCounts: p,
1535
+ paintTool: h,
1536
+ onPaintTool: j,
1537
+ brushCursor: x,
1538
+ onBrushCursor: B,
1539
+ fillAngle: z,
1540
+ onFillAngle: m
1541
+ }) {
1542
+ return /* @__PURE__ */ e.createElement("div", { className: "brush-panel", "data-testid": "paint-tools" }, /* @__PURE__ */ e.createElement("div", { className: "bp-title" }, "Support painting"), /* @__PURE__ */ e.createElement("div", { className: "bp-modes" }, /* @__PURE__ */ e.createElement("button", { className: t === "enforcer" ? "on enf" : "enf", onClick: () => r("enforcer"), title: "Force support under the painted facets", "data-testid": "paint-enforcer" }, "enforcer"), /* @__PURE__ */ e.createElement("button", { className: t === "blocker" ? "on blk" : "blk", onClick: () => r("blocker"), title: "Block support under the painted facets", "data-testid": "paint-blocker" }, "blocker"), /* @__PURE__ */ e.createElement("button", { onClick: c, "data-testid": "paint-clear", title: "Clear every painted enforcer/blocker area" }, "Clear"), /* @__PURE__ */ e.createElement("button", { onClick: () => r("off"), "data-testid": "paint-off", title: "Leave painting mode (Esc)" }, "Close")), /* @__PURE__ */ e.createElement(
1543
+ ar,
1544
+ {
1545
+ tool: h,
1546
+ onTool: j,
1547
+ cursor: x,
1548
+ onCursor: B,
1549
+ fillAngle: z,
1550
+ onFillAngle: m
1551
+ }
1552
+ ), !It(h) && /* @__PURE__ */ e.createElement("label", { className: "bp-radius" }, "Brush radius ", i, "mm", /* @__PURE__ */ e.createElement(
1201
1553
  "input",
1202
1554
  {
1203
1555
  type: "range",
1204
1556
  min: "1",
1205
1557
  max: "15",
1206
1558
  step: "0.5",
1207
- value: l,
1208
- onChange: (d) => r(parseFloat(d.target.value)),
1559
+ value: i,
1560
+ onChange: (L) => o(parseFloat(L.target.value)),
1209
1561
  "data-testid": "brush-radius"
1210
1562
  }
1211
- )), /* @__PURE__ */ e.createElement("div", { className: "muted bp-counts", "data-testid": "paint-counts" }, "enforcer ", m.enf, " · blocker ", m.blk, " · drag over the model to paint"));
1563
+ )), /* @__PURE__ */ e.createElement("div", { className: "muted bp-counts", "data-testid": "paint-counts" }, "enforcer ", p.enf, " · blocker ", p.blk, " · ", It(h) ? "click the model to fill" : "drag over the model to paint"));
1212
1564
  }
1213
- function Dr({ plateCount: n, selectedPlate: a, maxPlates: c, onSelect: l, onAdd: r, onDelete: m }) {
1214
- return /* @__PURE__ */ e.createElement("div", { className: "plate-bar", "data-testid": "plate-bar", role: "tablist", "aria-label": "Plates" }, Array.from({ length: n }, (d, w) => /* @__PURE__ */ e.createElement("button", { key: w, role: "tab", className: "plate-tab" + (w === a ? " on" : ""), onClick: () => l(w), title: `Select plate ${w + 1} — in Preview this switches to that plate's result`, "data-testid": `plate-${w}` }, w + 1)), /* @__PURE__ */ e.createElement("button", { className: "plate-add", onClick: r, disabled: n >= c, title: "Add an empty plate (max 9) — each plate slices and exports separately", "data-testid": "plate-add" }, "+"), n > 1 && /* @__PURE__ */ e.createElement("button", { className: "plate-del", onClick: m, title: "Delete the last plate and its slice result", "data-testid": "plate-del" }, "−"));
1565
+ function bo({ plateCount: t, selectedPlate: r, maxPlates: c, onSelect: i, onAdd: o, onDelete: p }) {
1566
+ return /* @__PURE__ */ e.createElement("div", { className: "plate-bar", "data-testid": "plate-bar", role: "tablist", "aria-label": "Plates" }, Array.from({ length: t }, (h, j) => /* @__PURE__ */ e.createElement("button", { key: j, role: "tab", className: "plate-tab" + (j === r ? " on" : ""), onClick: () => i(j), title: `Select plate ${j + 1} — in Preview this switches to that plate's result`, "data-testid": `plate-${j}` }, j + 1)), /* @__PURE__ */ e.createElement("button", { className: "plate-add", onClick: o, disabled: t >= c, title: "Add an empty plate (max 9) — each plate slices and exports separately", "data-testid": "plate-add" }, "+"), t > 1 && /* @__PURE__ */ e.createElement("button", { className: "plate-del", onClick: p, title: "Delete the last plate and its slice result", "data-testid": "plate-del" }, "−"));
1215
1567
  }
1216
- const Zt = (n) => `rgb(${Math.round(n[0] * 255)},${Math.round(n[1] * 255)},${Math.round(n[2] * 255)})`;
1217
- function Zr({
1218
- viewType: n,
1219
- onViewType: a,
1568
+ const rn = (t) => `rgb(${Math.round(t[0] * 255)},${Math.round(t[1] * 255)},${Math.round(t[2] * 255)})`;
1569
+ function go({
1570
+ viewType: t,
1571
+ onViewType: r,
1220
1572
  layerCount: c,
1221
- layerLo: l,
1222
- layerHi: r,
1223
- segCount: m,
1224
- singleLayer: d,
1225
- onLayerLo: w,
1226
- onLayerHi: b,
1227
- onToggleSingle: T,
1228
- showTravel: P,
1229
- onToggleTravel: M,
1230
- colorRange: B,
1231
- roleLegend: ee
1573
+ layerLo: i,
1574
+ layerHi: o,
1575
+ segCount: p,
1576
+ singleLayer: h,
1577
+ onLayerLo: j,
1578
+ onLayerHi: x,
1579
+ onToggleSingle: B,
1580
+ showTravel: z,
1581
+ onToggleTravel: m,
1582
+ colorRange: L,
1583
+ roleLegend: D,
1584
+ extruderColors: R
1232
1585
  }) {
1233
- return /* @__PURE__ */ e.createElement("div", { className: "slice-layer", "data-testid": "preview-controls" }, /* @__PURE__ */ e.createElement("label", { className: "view-type-row" }, "View type", /* @__PURE__ */ e.createElement("select", { value: n, onChange: a, "data-testid": "view-type-select", title: "What the toolpath colors represent — feature type, speed, layer height, width, fan, temperature" }, Xn.map((L) => /* @__PURE__ */ e.createElement("option", { key: L.key, value: L.key }, L.label)))), /* @__PURE__ */ e.createElement("label", null, "Layer ", /* @__PURE__ */ e.createElement("b", { "data-testid": "layer-range" }, d ? r + 1 : `${l + 1}..${r + 1}`), " / ", c, /* @__PURE__ */ e.createElement("span", { className: "muted" }, " (", m.toLocaleString(), " segments)")), /* @__PURE__ */ e.createElement("div", { className: "dual-slider" }, /* @__PURE__ */ e.createElement("input", { type: "range", min: "0", max: Math.max(0, c - 1), value: l, onChange: w, "data-testid": "layer-lo", title: "Lowest layer to show (also adjustable with ↓/↑)" }), /* @__PURE__ */ e.createElement("input", { type: "range", min: "0", max: Math.max(0, c - 1), value: r, onChange: b, "data-testid": "layer-hi", title: "Highest layer to show (↓/↑, hold Shift for steps of 10)" })), /* @__PURE__ */ e.createElement("div", { className: "layer-ctl" }, /* @__PURE__ */ e.createElement("button", { className: d ? "on" : "", onClick: T, "data-testid": "single-layer-btn", title: "Show only the selected layer (L)" }, "Single layer"), /* @__PURE__ */ e.createElement("label", { className: "slice-travel" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: P, onChange: M, "data-testid": "travel-toggle", title: "Show non-extruding moves as gray lines (T)" }), " Travel")), B && B.cont ? /* @__PURE__ */ e.createElement("div", { className: "grad-legend", "data-testid": "view-legend" }, /* @__PURE__ */ e.createElement("div", { className: "grad-title" }, B.label, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, B.unit)), /* @__PURE__ */ e.createElement("div", { className: "grad-bar", "data-testid": "gradient-bar", style: { background: `linear-gradient(to right, ${Kn.map(Zt).join(",")})` } }), /* @__PURE__ */ e.createElement("div", { className: "grad-minmax" }, /* @__PURE__ */ e.createElement("span", { "data-testid": "grad-min" }, B.min.toFixed(B.unit === "mm" ? 2 : 0)), /* @__PURE__ */ e.createElement("span", { "data-testid": "grad-max" }, B.max.toFixed(B.unit === "mm" ? 2 : 0)))) : /* @__PURE__ */ e.createElement("div", { className: "role-legend", "data-testid": "view-legend" }, ee.map((L) => /* @__PURE__ */ e.createElement("span", { key: L.type, className: "role-item" }, /* @__PURE__ */ e.createElement("i", { style: { background: Zt(L.color) } }), L.label, " ", /* @__PURE__ */ e.createElement("b", null, L.pct.toFixed(0), "%")))));
1586
+ const K = t === "filament", J = Math.max(1, Array.isArray(R) ? R.length : 0);
1587
+ return /* @__PURE__ */ e.createElement("div", { className: "slice-layer", "data-testid": "preview-controls" }, /* @__PURE__ */ e.createElement("label", { className: "view-type-row" }, "View type", /* @__PURE__ */ e.createElement("select", { value: t, onChange: r, "data-testid": "view-type-select", title: "What the toolpath colors represent — feature type, speed, layer height, width, fan, temperature, printing filament" }, Ea.map((_) => /* @__PURE__ */ e.createElement("option", { key: _.key, value: _.key }, _.label)))), /* @__PURE__ */ e.createElement("label", null, "Layer ", /* @__PURE__ */ e.createElement("b", { "data-testid": "layer-range" }, h ? o + 1 : `${i + 1}..${o + 1}`), " / ", c, /* @__PURE__ */ e.createElement("span", { className: "muted" }, " (", p.toLocaleString(), " segments)")), /* @__PURE__ */ e.createElement("div", { className: "dual-slider" }, /* @__PURE__ */ e.createElement("input", { type: "range", min: "0", max: Math.max(0, c - 1), value: i, onChange: j, "data-testid": "layer-lo", title: "Lowest layer to show (also adjustable with ↓/↑)" }), /* @__PURE__ */ e.createElement("input", { type: "range", min: "0", max: Math.max(0, c - 1), value: o, onChange: x, "data-testid": "layer-hi", title: "Highest layer to show (↓/↑, hold Shift for steps of 10)" })), /* @__PURE__ */ e.createElement("div", { className: "layer-ctl" }, /* @__PURE__ */ e.createElement("button", { className: h ? "on" : "", onClick: B, "data-testid": "single-layer-btn", title: "Show only the selected layer (L)" }, "Single layer"), /* @__PURE__ */ e.createElement("label", { className: "slice-travel" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: z, onChange: m, "data-testid": "travel-toggle", title: "Show non-extruding moves as gray lines (T)" }), " Travel")), K ? (
1588
+ // Swatches only, no share percentage: buildSegmentData accumulates extruded length per feature type
1589
+ // (typeLengths) and never per tool, so a "% per extruder" here would be made up. The millimetres per tool
1590
+ // are the stats card's job. The colors come from TOOL_COLOR because that is exactly what computeColors
1591
+ // paints the segments with — an extruderColors swatch would show the user's picker color, not the toolpath.
1592
+ /* @__PURE__ */ e.createElement("div", { className: "role-legend", "data-testid": "view-legend" }, Array.from({ length: J }, (_, T) => /* @__PURE__ */ e.createElement("span", { key: T, className: "role-item" }, /* @__PURE__ */ e.createElement("i", { style: { background: rn(Hn[T % Hn.length]) } }), "T", T + 1)))
1593
+ ) : L && L.cont ? /* @__PURE__ */ e.createElement("div", { className: "grad-legend", "data-testid": "view-legend" }, /* @__PURE__ */ e.createElement("div", { className: "grad-title" }, L.label, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, L.unit)), /* @__PURE__ */ e.createElement("div", { className: "grad-bar", "data-testid": "gradient-bar", style: { background: `linear-gradient(to right, ${ka.map(rn).join(",")})` } }), /* @__PURE__ */ e.createElement("div", { className: "grad-minmax" }, /* @__PURE__ */ e.createElement("span", { "data-testid": "grad-min" }, L.min.toFixed(L.unit === "mm" ? 2 : 0)), /* @__PURE__ */ e.createElement("span", { "data-testid": "grad-max" }, L.max.toFixed(L.unit === "mm" ? 2 : 0)))) : /* @__PURE__ */ e.createElement("div", { className: "role-legend", "data-testid": "view-legend" }, D.map((_) => /* @__PURE__ */ e.createElement("span", { key: _.type, className: "role-item" }, /* @__PURE__ */ e.createElement("i", { style: { background: rn(_.color) } }), _.label, " ", /* @__PURE__ */ e.createElement("b", null, _.pct.toFixed(0), "%")))));
1234
1594
  }
1235
- const Ir = (n) => {
1236
- const a = Math.round(n);
1237
- return `${Math.floor(a / 3600)}h ${String(Math.floor(a % 3600 / 60)).padStart(2, "0")}m ${String(a % 60).padStart(2, "0")}s`;
1238
- }, Vr = { full: "GCodeProcessor", transcribed: "transcribed planner", fallback: "fallback" };
1239
- function Fr({ stats: n, overBed: a }) {
1240
- return n ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", null, /* @__PURE__ */ e.createElement("b", null, n.layers), " layers · ", /* @__PURE__ */ e.createElement("b", null, n.segments), " segments"), /* @__PURE__ */ e.createElement("div", null, "Filament ", /* @__PURE__ */ e.createElement("b", null, n.filament.toFixed(1)), " mm"), typeof n.timeSec == "number" && n.timeSec > 0 && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { "data-testid": "print-time" }, "⏱ Estimated print ", /* @__PURE__ */ e.createElement("b", null, Ir(n.timeSec))), n.limits && // What the estimator actually ran with, echoed back by the kernel — not re-derived from the settings,
1595
+ const yo = (t) => {
1596
+ const r = Math.round(t);
1597
+ return `${Math.floor(r / 3600)}h ${String(Math.floor(r % 3600 / 60)).padStart(2, "0")}m ${String(r % 60).padStart(2, "0")}s`;
1598
+ }, xo = { full: "GCodeProcessor", transcribed: "transcribed planner", fallback: "fallback" };
1599
+ function qn(t, r, c) {
1600
+ const i = ((t == null ? void 0 : t[c]) ?? "").trim(), o = ((r == null ? void 0 : r[c]) ?? "").trim();
1601
+ return i || (o ? o.replace(/\s*@.*$/, "") : "");
1602
+ }
1603
+ function vo({ stats: t, overBed: r, extruderColors: c, filamentTypes: i, filamentIds: o }) {
1604
+ if (!t) return null;
1605
+ const p = Array.isArray(t.filamentPerTool) ? t.filamentPerTool : null, j = (p ? p.filter((L) => Number.isFinite(L) && L > 0).length : 0) > 1, x = Number.isFinite(t.filamentPurge) ? t.filamentPurge : 0, B = Array.isArray(t.filamentPurgePerTool) ? t.filamentPurgePerTool : null, z = (L) => Number.isFinite(B == null ? void 0 : B[L]) ? B[L] : 0, m = Array.isArray(c) ? c : [];
1606
+ return /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", null, /* @__PURE__ */ e.createElement("b", null, t.layers), " layers · ", /* @__PURE__ */ e.createElement("b", null, t.segments), " segments"), /* @__PURE__ */ e.createElement("div", null, "Filament ", /* @__PURE__ */ e.createElement("b", null, t.filament.toFixed(1)), " mm"), j && p.map((L, D) => Number.isFinite(L) && L > 0 ? /* @__PURE__ */ e.createElement("div", { className: "stat-tool", key: D, "data-testid": `filament-tool-${D}` }, /* @__PURE__ */ e.createElement("span", { className: "stat-swatch", style: { background: m[D] ?? "#8a9099" } }), "T", D + 1, qn(i, o, D) && /* @__PURE__ */ e.createElement("span", { className: "stat-type", "data-testid": `filament-type-${D}` }, qn(i, o, D)), " ", /* @__PURE__ */ e.createElement("b", null, L.toFixed(1)), " mm", B && z(D) > 0 && /* @__PURE__ */ e.createElement("span", { className: "stat-split", "data-testid": `filament-tool-split-${D}` }, " ", "(model ", (L - z(D)).toFixed(1), " · tower ", z(D).toFixed(1), ")")) : null), j && x > 0 && /* @__PURE__ */ e.createElement("div", { className: "stat-tool", "data-testid": "filament-purge" }, /* @__PURE__ */ e.createElement("span", { className: "stat-swatch stat-swatch-purge" }), "Prime tower / purge ", /* @__PURE__ */ e.createElement("b", null, x.toFixed(1)), " mm"), typeof t.timeSec == "number" && t.timeSec > 0 && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { "data-testid": "print-time" }, "⏱ Estimated print ", /* @__PURE__ */ e.createElement("b", null, yo(t.timeSec))), t.limits && // What the estimator actually ran with, echoed back by the kernel — not re-derived from the settings,
1241
1607
  // so an unwired limit shows up as a mismatch here instead of being silently reported as applied.
1242
1608
  /* @__PURE__ */ e.createElement(
1243
1609
  "div",
1244
1610
  {
1245
1611
  className: "sc-sub",
1246
1612
  "data-testid": "print-time-basis",
1247
- title: Object.entries(n.limits).map(([c, l]) => `${c}: ${l}`).join(`
1613
+ title: Object.entries(t.limits).map(([L, D]) => `${L}: ${D}`).join(`
1248
1614
  `)
1249
1615
  },
1250
- Vr[n.engine] ?? n.engine,
1616
+ xo[t.engine] ?? t.engine,
1251
1617
  " · ",
1252
- n.limits.max_speed_xy,
1618
+ t.limits.max_speed_xy,
1253
1619
  " mm/s · ",
1254
- n.limits.accel_print,
1620
+ t.limits.accel_print,
1255
1621
  " mm/s² · jerk ",
1256
- n.limits.jerk_xy
1257
- )), a && /* @__PURE__ */ e.createElement("div", { className: "slice-warn", "data-testid": "over-bed" }, "⚠ Model extends beyond the bed")) : null;
1622
+ t.limits.jerk_xy
1623
+ )), r && /* @__PURE__ */ e.createElement("div", { className: "slice-warn", "data-testid": "over-bed" }, "⚠ Model extends beyond the bed"));
1258
1624
  }
1259
- function Gr({ bedWidth: n, bedDepth: a, nozzleDia: c, onBedSize: l, settings: r, setSettings: m, motionPanel: d }) {
1260
- const w = Tt(() => Object.entries(On).sort(([h], [E]) => h.localeCompare(E)), []), b = (r == null ? void 0 : r.printer_settings_id) ?? "", T = Tt(() => {
1261
- const h = /* @__PURE__ */ new Map();
1262
- for (const [E, k] of w)
1263
- for (const [j, [f, , g]] of Object.entries(k)) {
1264
- const y = `${E}\0${g}`;
1265
- h.has(y) || h.set(y, { vendor: E, model: g, variants: [] }), h.get(y).variants.push({ name: j, nozzle: f });
1625
+ function wo({ bedWidth: t, bedDepth: r, nozzleDia: c, onBedSize: i, settings: o, setSettings: p, motionPanel: h }) {
1626
+ const j = _t(() => Object.entries(oa).sort(([w], [S]) => w.localeCompare(S)), []), x = (o == null ? void 0 : o.printer_settings_id) ?? "", B = _t(() => {
1627
+ const w = /* @__PURE__ */ new Map();
1628
+ for (const [S, E] of j)
1629
+ for (const [M, [y, , v]] of Object.entries(E)) {
1630
+ const C = `${S}\0${v}`;
1631
+ w.has(C) || w.set(C, { vendor: S, model: v, variants: [] }), w.get(C).variants.push({ name: M, nozzle: y });
1266
1632
  }
1267
- for (const E of h.values()) E.variants.sort((k, j) => parseFloat(k.nozzle) - parseFloat(j.nozzle));
1268
- return h;
1269
- }, [w]), P = [...T.entries()].find(([, h]) => h.variants.some((E) => E.name === b)), M = (P == null ? void 0 : P[1].variants) ?? [], [B, ee] = $(null), L = (h) => m == null ? void 0 : m((E) => {
1270
- const k = { ...E };
1271
- for (const f of lt) delete k[f];
1272
- for (const f of (B == null ? void 0 : B.keys) ?? []) delete k[f];
1273
- delete k.printer_settings_id, delete k.print_settings_id;
1274
- const j = h ? Zn(h) : null;
1275
- return j ? { ...k, ...j, printer_settings_id: h } : k;
1633
+ for (const S of w.values()) S.variants.sort((E, M) => parseFloat(E.nozzle) - parseFloat(M.nozzle));
1634
+ return w;
1635
+ }, [j]), z = [...B.entries()].find(([, w]) => w.variants.some((S) => S.name === x)), m = (z == null ? void 0 : z[1].variants) ?? [], [L, D] = ae(null), R = (w) => p == null ? void 0 : p((S) => {
1636
+ const E = { ...S };
1637
+ for (const y of St) delete E[y];
1638
+ for (const y of (L == null ? void 0 : L.keys) ?? []) delete E[y];
1639
+ delete E.printer_settings_id, delete E.print_settings_id;
1640
+ const M = w ? ca(w) : null;
1641
+ return M ? { ...E, ...M, printer_settings_id: w } : E;
1276
1642
  });
1277
- Ve(() => {
1278
- if (!b) return;
1279
- let h = !0;
1280
- return Tn().then((E) => {
1281
- if (!h) return;
1282
- ee(E);
1283
- const k = Bn(b);
1284
- m == null || m((j) => {
1285
- if (j.printer_settings_id !== b || j.print_settings_id) return j;
1286
- const f = k && E.listFor(b).includes(k) ? E.settingsFor(k) : null;
1287
- if (!f) return j;
1288
- for (const g of lt) delete f[g];
1289
- return { ...j, ...f, print_settings_id: k };
1643
+ Te(() => {
1644
+ if (!x) return;
1645
+ let w = !0;
1646
+ return la().then((S) => {
1647
+ if (!w) return;
1648
+ D(S);
1649
+ const E = sa(x);
1650
+ p == null || p((M) => {
1651
+ if (M.printer_settings_id !== x || M.print_settings_id) return M;
1652
+ const y = E && S.listFor(x).includes(E) ? S.settingsFor(E) : null;
1653
+ if (!y) return M;
1654
+ for (const v of St) delete y[v];
1655
+ return { ...M, ...y, print_settings_id: E };
1290
1656
  });
1291
1657
  }), () => {
1292
- h = !1;
1658
+ w = !1;
1293
1659
  };
1294
- }, [b]);
1295
- const te = B && b ? B.listFor(b) : [], I = (h) => m == null ? void 0 : m((E) => {
1296
- const k = { ...E };
1297
- for (const f of B.keys)
1298
- lt.includes(f) || delete k[f];
1299
- delete k.print_settings_id;
1300
- const j = h ? B.settingsFor(h) : null;
1301
- if (!j) return k;
1302
- for (const f of lt) delete j[f];
1303
- return { ...k, ...j, print_settings_id: h };
1304
- }), A = (h) => {
1305
- const E = T.get(h);
1306
- if (!E) return L("");
1307
- const k = E.variants.find((j) => {
1308
- var f;
1309
- return j.nozzle === ((f = M.find((g) => g.name === b)) == null ? void 0 : f.nozzle);
1660
+ }, [x]);
1661
+ const K = L && x ? L.listFor(x) : [], J = (w) => p == null ? void 0 : p((S) => {
1662
+ const E = { ...S };
1663
+ for (const y of L.keys)
1664
+ St.includes(y) || delete E[y];
1665
+ delete E.print_settings_id;
1666
+ const M = w ? L.settingsFor(w) : null;
1667
+ if (!M) return E;
1668
+ for (const y of St) delete M[y];
1669
+ return { ...E, ...M, print_settings_id: w };
1670
+ }), _ = (w) => {
1671
+ const S = B.get(w);
1672
+ if (!S) return R("");
1673
+ const E = S.variants.find((M) => {
1674
+ var y;
1675
+ return M.nozzle === ((y = m.find((v) => v.name === x)) == null ? void 0 : y.nozzle);
1310
1676
  });
1311
- L((k ?? E.variants[Math.min(1, E.variants.length - 1)]).name);
1312
- }, u = Dn.filter((h) => r && h in r), _ = Math.round(n), G = Math.round(a), K = (h) => {
1313
- h.key === "Enter" && h.target.blur();
1677
+ R((E ?? S.variants[Math.min(1, S.variants.length - 1)]).name);
1678
+ }, T = ia.filter((w) => o && w in o), F = Math.round(t), ce = Math.round(r), pe = (w) => {
1679
+ w.key === "Enter" && w.target.blur();
1314
1680
  };
1315
- return /* @__PURE__ */ e.createElement("section", { className: "side-card" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🖨 Printer"), m && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Model"), /* @__PURE__ */ e.createElement(
1681
+ return /* @__PURE__ */ e.createElement("section", { className: "side-card" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🖨 Printer"), p && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Model"), /* @__PURE__ */ e.createElement(
1316
1682
  "select",
1317
1683
  {
1318
1684
  className: "sc-model",
1319
- value: (P == null ? void 0 : P[0]) ?? "",
1320
- onChange: (h) => A(h.target.value),
1685
+ value: (z == null ? void 0 : z[0]) ?? "",
1686
+ onChange: (w) => _(w.target.value),
1321
1687
  "data-testid": "printer-model",
1322
1688
  title: "Loads this printer's profile (motion limits, bed, nozzle) from the upstream vendor data"
1323
1689
  },
1324
1690
  /* @__PURE__ */ e.createElement("option", { value: "" }, "Custom (defaults)"),
1325
- w.map(([h]) => /* @__PURE__ */ e.createElement("optgroup", { key: h, label: h }, [...T.entries()].filter(([, E]) => E.vendor === h).map(([E, k]) => /* @__PURE__ */ e.createElement("option", { key: E, value: E }, k.model))))
1326
- )), M.length > 1 && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Nozzle"), /* @__PURE__ */ e.createElement("select", { className: "sc-model", value: b, onChange: (h) => L(h.target.value), "data-testid": "printer-nozzle" }, M.map((h) => /* @__PURE__ */ e.createElement("option", { key: h.name, value: h.name }, h.nozzle, " mm")))), te.length > 0 && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Quality"), /* @__PURE__ */ e.createElement(
1691
+ j.map(([w]) => /* @__PURE__ */ e.createElement("optgroup", { key: w, label: w }, [...B.entries()].filter(([, S]) => S.vendor === w).map(([S, E]) => /* @__PURE__ */ e.createElement("option", { key: S, value: S }, E.model))))
1692
+ )), m.length > 1 && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Nozzle"), /* @__PURE__ */ e.createElement("select", { className: "sc-model", value: x, onChange: (w) => R(w.target.value), "data-testid": "printer-nozzle" }, m.map((w) => /* @__PURE__ */ e.createElement("option", { key: w.name, value: w.name }, w.nozzle, " mm")))), K.length > 0 && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Quality"), /* @__PURE__ */ e.createElement(
1327
1693
  "select",
1328
1694
  {
1329
1695
  className: "sc-model",
1330
- value: (r == null ? void 0 : r.print_settings_id) ?? "",
1331
- onChange: (h) => I(h.target.value),
1696
+ value: (o == null ? void 0 : o.print_settings_id) ?? "",
1697
+ onChange: (w) => J(w.target.value),
1332
1698
  "data-testid": "process-preset",
1333
1699
  title: "Print preset from the vendor profile — the speeds and accelerations the printer is actually driven at"
1334
1700
  },
1335
1701
  /* @__PURE__ */ e.createElement("option", { value: "" }, "Custom (defaults)"),
1336
- te.map((h) => /* @__PURE__ */ e.createElement("option", { key: h, value: h }, h.replace(/\s*@.*$/, "")))
1702
+ K.map((w) => /* @__PURE__ */ e.createElement("option", { key: w, value: w }, w.replace(/\s*@.*$/, "")))
1337
1703
  ))), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Bed"), /* @__PURE__ */ e.createElement("span", { className: "sc-bed", title: "Plate size — applied on Enter or blur. Circular/custom shapes come from the printable_area option" }, /* @__PURE__ */ e.createElement(
1338
1704
  "input",
1339
1705
  {
1340
1706
  type: "number",
1341
1707
  min: "1",
1342
- key: `w${_}`,
1343
- defaultValue: _,
1344
- onBlur: (h) => l(+h.target.value, a),
1345
- onKeyDown: K,
1708
+ key: `w${F}`,
1709
+ defaultValue: F,
1710
+ onBlur: (w) => i(+w.target.value, r),
1711
+ onKeyDown: pe,
1346
1712
  "data-testid": "bed-w-card"
1347
1713
  }
1348
1714
  ), "×", /* @__PURE__ */ e.createElement(
@@ -1350,610 +1716,1207 @@ function Gr({ bedWidth: n, bedDepth: a, nozzleDia: c, onBedSize: l, settings: r,
1350
1716
  {
1351
1717
  type: "number",
1352
1718
  min: "1",
1353
- key: `d${G}`,
1354
- defaultValue: G,
1355
- onBlur: (h) => l(n, +h.target.value),
1356
- onKeyDown: K,
1719
+ key: `d${ce}`,
1720
+ defaultValue: ce,
1721
+ onBlur: (w) => i(t, +w.target.value),
1722
+ onKeyDown: pe,
1357
1723
  "data-testid": "bed-d-card"
1358
1724
  }
1359
- ), "mm")), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Nozzle Ø"), /* @__PURE__ */ e.createElement("b", null, c, " mm")), d ? /* @__PURE__ */ e.createElement("details", { className: "sc-fold", "data-testid": "motion-fold" }, /* @__PURE__ */ e.createElement("summary", null, /* @__PURE__ */ e.createElement("span", { className: "sc-fold-lbl" }, "Motion"), /* @__PURE__ */ e.createElement("b", { "data-testid": "motion-source" }, u.length ? `Custom (${u.length})` : "Default")), /* @__PURE__ */ e.createElement("div", { className: "sc-fold-body" }, d)) : /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Motion"), /* @__PURE__ */ e.createElement(
1725
+ ), "mm")), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Nozzle Ø"), /* @__PURE__ */ e.createElement("b", null, c, " mm")), h ? /* @__PURE__ */ e.createElement("details", { className: "sc-fold", "data-testid": "motion-fold" }, /* @__PURE__ */ e.createElement("summary", null, /* @__PURE__ */ e.createElement("span", { className: "sc-fold-lbl" }, "Motion"), /* @__PURE__ */ e.createElement("b", { "data-testid": "motion-source" }, T.length ? `Custom (${T.length})` : "Default")), /* @__PURE__ */ e.createElement("div", { className: "sc-fold-body" }, h)) : /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Motion"), /* @__PURE__ */ e.createElement(
1360
1726
  "b",
1361
1727
  {
1362
1728
  "data-testid": "motion-source",
1363
- title: u.length ? `Edited: ${u.join(", ")}` : "Machine limits are at their defaults"
1729
+ title: T.length ? `Edited: ${T.join(", ")}` : "Machine limits are at their defaults"
1364
1730
  },
1365
- u.length ? `Custom (${u.length})` : "Default"
1731
+ T.length ? `Custom (${T.length})` : "Default"
1366
1732
  )));
1367
1733
  }
1368
- function Ur({ colors: n, onColor: a, onAdd: c, onRemove: l }) {
1369
- return /* @__PURE__ */ e.createElement("section", { className: "side-card", "data-testid": "filament-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🧵 Filament ", /* @__PURE__ */ e.createElement("span", { className: "sc-count" }, n.length), /* @__PURE__ */ e.createElement("span", { className: "sc-head-btns" }, /* @__PURE__ */ e.createElement("button", { onClick: c, disabled: n.length >= 4, title: "Add a filament (extruder) — up to 4, assignable per object", "data-testid": "filament-add" }, "+"), /* @__PURE__ */ e.createElement("button", { onClick: l, disabled: n.length <= 1, title: "Remove the last filament — objects using it are reassigned to a remaining number", "data-testid": "filament-del" }, "−"))), n.map((r, m) => /* @__PURE__ */ e.createElement("div", { className: "filament-row", key: m }, /* @__PURE__ */ e.createElement("input", { type: "color", value: r, onChange: (d) => a(m, d.target.value), title: `T${m + 1} filament color`, "data-testid": `filament-color-${m}` }), /* @__PURE__ */ e.createElement("span", { className: "fil-t" }, "T", m + 1), /* @__PURE__ */ e.createElement("span", { className: "fil-swatch", style: { background: r } }), /* @__PURE__ */ e.createElement("span", { className: "fil-hex muted" }, r))));
1734
+ const Xn = ["TabFilament::build", "TabFilament::add_filament_overrides_page"].flatMap((t) => pa[t] ?? []).flatMap((t) => t.groups.flatMap((r) => r.options)).filter((t) => !t.startsWith("<")), or = "three-slicer.filaments";
1735
+ function Yn() {
1736
+ try {
1737
+ return JSON.parse(localStorage.getItem(or)) ?? {};
1738
+ } catch {
1739
+ return {};
1740
+ }
1370
1741
  }
1371
- function $r({
1372
- objects: n,
1373
- extruderColors: a,
1742
+ function Kn(t) {
1743
+ try {
1744
+ localStorage.setItem(or, JSON.stringify(t));
1745
+ } catch {
1746
+ }
1747
+ return t;
1748
+ }
1749
+ const Et = (t) => Array.isArray(t) ? t[0] : t;
1750
+ function Eo({
1751
+ colors: t,
1752
+ onColor: r,
1753
+ onAdd: c,
1754
+ onRemove: i,
1755
+ settings: o,
1756
+ setSettings: p,
1757
+ filamentPanel: h,
1758
+ paintMode: j,
1759
+ onPaintExtruder: x,
1760
+ paintCounts: B
1761
+ }) {
1762
+ const z = (o == null ? void 0 : o.printer_settings_id) ?? "", [m, L] = ae(null), [D, R] = ae(Yn), [K, J] = ae(null), [_, T] = ae(0), F = t.length;
1763
+ Te(() => {
1764
+ _ >= F && T(F - 1);
1765
+ }, [F, _]);
1766
+ const ce = _t(() => [.../* @__PURE__ */ new Set([...(m == null ? void 0 : m.keys) ?? [], ...Xn])], [m]), pe = _t(() => {
1767
+ const b = o == null ? void 0 : o.filament_settings_id, k = Array.isArray(b) ? b : b ? [b] : [];
1768
+ return Array.from({ length: F }, (G, q) => k[q] ?? "");
1769
+ }, [o == null ? void 0 : o.filament_settings_id, F]), w = pe[_] ?? "", S = (b, k, G) => {
1770
+ var s;
1771
+ if (!G) return null;
1772
+ if (k != null && k[G]) return "Saved";
1773
+ const q = (s = z ? b == null ? void 0 : b.listFor(z) : b == null ? void 0 : b.all()) == null ? void 0 : s.find((d) => d.name === G);
1774
+ return (q == null ? void 0 : q.type) || null;
1775
+ }, E = (b, k, G, q) => {
1776
+ const s = [.../* @__PURE__ */ new Set([...(b == null ? void 0 : b.keys) ?? [], ...Xn])], d = G.map((I) => I ? k[I] ?? (b == null ? void 0 : b.settingsFor(I)) ?? null : null), A = { ...q };
1777
+ for (const I of s) delete A[I];
1778
+ for (const I of s) {
1779
+ const se = d.map((ye) => ye && I in ye ? Et(ye[I]) : null);
1780
+ se.some((ye) => ye != null) && (A[I] = se);
1781
+ }
1782
+ if (G.every((I) => !I))
1783
+ return delete A.filament_settings_id, delete A.filament_type, A;
1784
+ A.filament_settings_id = G;
1785
+ const u = G.map((I) => S(b, k, I));
1786
+ return u.some((I) => I) && (A.filament_type = u.map((I) => I ?? "")), A;
1787
+ }, M = (b, k, G, q = D) => E(m, q, pe.map((s, d) => d === b ? k : s), G), y = () => {
1788
+ F <= 1 || (i == null || i(_), p == null || p((b) => E(m, D, pe.filter((k, G) => G !== _), b)), T((b) => Math.max(0, Math.min(b, F - 2))));
1789
+ }, v = m && w ? D[w] ?? m.settingsFor(w) : null, C = !!m && ce.some((b) => {
1790
+ if (!(b in (o ?? {}))) return !1;
1791
+ const k = Array.isArray(o[b]) ? o[b][_] : Et(o[b]);
1792
+ return JSON.stringify(k ?? null) !== JSON.stringify(Et(v == null ? void 0 : v[b]) ?? null);
1793
+ }), V = () => {
1794
+ var s;
1795
+ const b = (w || "My material").replace(/\s*@.*$/, "") + (D[w] ? "" : " (custom)"), k = (s = window.prompt(`Save T${_ + 1}'s filament settings as:`, b)) == null ? void 0 : s.trim();
1796
+ if (!k) return;
1797
+ const G = {};
1798
+ for (const d of ce) {
1799
+ if (!(d in (o ?? {}))) continue;
1800
+ const A = Array.isArray(o[d]) ? o[d][_] : Et(o[d]);
1801
+ A != null && (G[d] = [A]);
1802
+ }
1803
+ const q = Kn({ ...D, [k]: G });
1804
+ R(q), J("Saved"), p == null || p((d) => M(_, k, d, q));
1805
+ }, f = () => {
1806
+ if (!D[w] || !window.confirm(`Delete the saved material "${w}"?`)) return;
1807
+ const { [w]: b, ...k } = D;
1808
+ R(Kn(k)), J(null), p == null || p((G) => M(_, "", G, k));
1809
+ };
1810
+ Te(() => {
1811
+ let b = !0;
1812
+ return da().then((k) => {
1813
+ b && (L(k), z && (p == null || p((G) => {
1814
+ if (G.printer_settings_id !== z) return G;
1815
+ const q = k.listFor(z), s = Yn(), d = k.recommendedFor(z)[0] ?? q[0], A = G.filament_settings_id, u = Array.isArray(A) ? A : A ? [A] : [], I = (ye) => ye && (ye in s || q.some((xe) => xe.name === ye)), se = Array.from({ length: F }, (ye, xe) => I(u[xe]) ? u[xe] : (d == null ? void 0 : d.name) ?? "");
1816
+ return se.every((ye, xe) => ye === (u[xe] ?? "")) ? G : E(k, s, se, G);
1817
+ })));
1818
+ }), () => {
1819
+ b = !1;
1820
+ };
1821
+ }, [z, F]);
1822
+ const N = _t(() => {
1823
+ const b = /* @__PURE__ */ new Map();
1824
+ if (!m) return { types: [], byType: b };
1825
+ const k = (s, d) => {
1826
+ b.has(s) || b.set(s, []), b.get(s).push(d);
1827
+ };
1828
+ for (const s of Object.keys(D).sort((d, A) => d.localeCompare(A))) k("Saved", { name: s });
1829
+ if (z) for (const s of m.recommendedFor(z)) k("★ Recommended", s);
1830
+ for (const s of z ? m.listFor(z) : m.all()) k(s.type || "Other", s);
1831
+ const G = ["Saved", "★ Recommended"].filter((s) => b.has(s)), q = [...b.keys()].filter((s) => !G.includes(s)).sort((s, d) => s.localeCompare(d));
1832
+ return { types: [...G, ...q], byType: b };
1833
+ }, [m, z, D]);
1834
+ Te(() => {
1835
+ J(null);
1836
+ }, [z, _]);
1837
+ const H = K ?? ((b) => {
1838
+ if (!b || !m) return null;
1839
+ if (D[b]) return "Saved";
1840
+ const k = (z ? m.listFor(z) : m.all()).find((G) => G.name === b);
1841
+ return k ? k.type || "Other" : null;
1842
+ })(w) ?? N.types[0] ?? "", oe = N.byType.get(H) ?? [], ue = (b) => {
1843
+ J(b);
1844
+ const k = N.byType.get(b) ?? [], G = new Set((N.byType.get("★ Recommended") ?? []).map((s) => s.name)), q = k.find((s) => G.has(s.name)) ?? k[0];
1845
+ q && (p == null || p((s) => M(_, q.name, s)));
1846
+ }, $ = (b) => {
1847
+ if (F < 2) return b;
1848
+ const k = { ...b };
1849
+ for (const G of ce) Array.isArray(k[G]) && (k[G] = k[G][_]);
1850
+ return k;
1851
+ }, P = (b) => p == null ? void 0 : p((k) => {
1852
+ const G = $(k), q = typeof b == "function" ? b(G) : b;
1853
+ if (F < 2) return q;
1854
+ const s = { ...k };
1855
+ for (const d of Object.keys(q)) {
1856
+ if (JSON.stringify(q[d]) === JSON.stringify(G[d])) continue;
1857
+ if (!ce.includes(d)) {
1858
+ s[d] = q[d];
1859
+ continue;
1860
+ }
1861
+ const A = Array.isArray(k[d]) ? [...k[d]] : Array.from({ length: F }, () => null);
1862
+ A[_] = Et(q[d]), s[d] = A;
1863
+ }
1864
+ for (const d of Object.keys(G)) {
1865
+ if (d in q || !ce.includes(d)) continue;
1866
+ const A = Array.isArray(k[d]) ? [...k[d]] : Array.from({ length: F }, () => null);
1867
+ A[_] = null, A.every((u) => u == null) ? delete s[d] : s[d] = A;
1868
+ }
1869
+ return s;
1870
+ }), ve = typeof h == "function" ? h($(o ?? {}), P) : h, Me = (b) => b ? b.replace(/\s*@.*$/, "") : "—", ge = j === "material", Ee = (b) => {
1871
+ T(b), x == null || x(b);
1872
+ };
1873
+ return /* @__PURE__ */ e.createElement("section", { className: "side-card", "data-testid": "filament-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🧵 Filament ", /* @__PURE__ */ e.createElement("span", { className: "sc-count" }, F), /* @__PURE__ */ e.createElement("span", { className: "sc-head-btns" }, /* @__PURE__ */ e.createElement("button", { onClick: c, disabled: F >= 16, title: "Add a filament (extruder) — up to 16, assignable per object or by painting", "data-testid": "filament-add" }, "+"), /* @__PURE__ */ e.createElement("button", { onClick: y, disabled: F <= 1, title: "Remove the selected filament — objects using it move to T1, later tools shift down", "data-testid": "filament-del" }, "−"))), t.map((b, k) => /* @__PURE__ */ e.createElement(
1874
+ "div",
1875
+ {
1876
+ className: `filament-row${k === _ ? " fil-active" : ""}`,
1877
+ key: k,
1878
+ onClick: () => T(k),
1879
+ "data-testid": `filament-row-${k}`,
1880
+ title: pe[k] ? `T${k + 1}: ${pe[k]}` : `T${k + 1}: no material picked`
1881
+ },
1882
+ /* @__PURE__ */ e.createElement(
1883
+ "input",
1884
+ {
1885
+ type: "color",
1886
+ value: b,
1887
+ onClick: (G) => G.stopPropagation(),
1888
+ onChange: (G) => r(k, G.target.value),
1889
+ title: `T${k + 1} filament color`,
1890
+ "data-testid": `filament-color-${k}`
1891
+ }
1892
+ ),
1893
+ /* @__PURE__ */ e.createElement("span", { className: "fil-t" }, "T", k + 1),
1894
+ /* @__PURE__ */ e.createElement("span", { className: "fil-mat", "data-testid": `filament-material-${k}` }, Me(pe[k])),
1895
+ k === 0 && /* @__PURE__ */ e.createElement(
1896
+ "span",
1897
+ {
1898
+ className: "fil-badge",
1899
+ "data-testid": "filament-default-badge",
1900
+ title: "Default extruder — everything not painted with another tool prints with this one"
1901
+ },
1902
+ "default"
1903
+ ),
1904
+ Dt(B, k) > 0 && /* @__PURE__ */ e.createElement(
1905
+ "span",
1906
+ {
1907
+ className: "fil-count",
1908
+ "data-testid": `filament-paint-count-${k}`,
1909
+ title: `${Dt(B, k)} facets painted for T${k + 1}`
1910
+ },
1911
+ Dt(B, k)
1912
+ ),
1913
+ x && /* @__PURE__ */ e.createElement(
1914
+ "button",
1915
+ {
1916
+ className: ge && k === _ ? "fil-paint on" : "fil-paint",
1917
+ onClick: (G) => {
1918
+ G.stopPropagation(), Ee(k);
1919
+ },
1920
+ "data-testid": `filament-paint-${k}`,
1921
+ title: `Brush parts of a model to print with T${k + 1}`
1922
+ },
1923
+ "🖌"
1924
+ )
1925
+ )), N.types.length > 0 && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, F > 1 ? `T${_ + 1} material` : "Material"), /* @__PURE__ */ e.createElement(
1926
+ "select",
1927
+ {
1928
+ className: "sc-model",
1929
+ value: H,
1930
+ onChange: (b) => ue(b.target.value),
1931
+ "data-testid": "filament-type",
1932
+ title: "Material type — narrows the presets below to the ones made of it"
1933
+ },
1934
+ N.types.map((b) => /* @__PURE__ */ e.createElement("option", { key: b, value: b }, b, " (", N.byType.get(b).length, ")"))
1935
+ )), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Preset"), /* @__PURE__ */ e.createElement(
1936
+ "select",
1937
+ {
1938
+ className: "sc-model",
1939
+ value: w,
1940
+ onChange: (b) => p == null ? void 0 : p((k) => M(_, b.target.value, k)),
1941
+ "data-testid": "filament-preset",
1942
+ title: "Material preset from the upstream vendor profiles — temperatures, flow ratio, cooling and retraction"
1943
+ },
1944
+ /* @__PURE__ */ e.createElement("option", { value: "" }, "Custom (defaults)"),
1945
+ oe.map((b) => /* @__PURE__ */ e.createElement("option", { key: b.name, value: b.name }, Me(b.name)))
1946
+ )), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", { className: "sc-dirty" }, C ? "● Modified" : ""), /* @__PURE__ */ e.createElement("span", { className: "sc-head-btns sc-preset-btns" }, /* @__PURE__ */ e.createElement(
1947
+ "button",
1948
+ {
1949
+ onClick: V,
1950
+ "data-testid": "filament-save",
1951
+ title: "Save this extruder's filament values as your own material — system presets are vendor data and stay read-only"
1952
+ },
1953
+ "Save as…"
1954
+ ), /* @__PURE__ */ e.createElement(
1955
+ "button",
1956
+ {
1957
+ onClick: f,
1958
+ disabled: !D[w],
1959
+ "data-testid": "filament-delete",
1960
+ title: "Delete this saved material"
1961
+ },
1962
+ "Delete"
1963
+ )))), ve && N.types.length > 0 && /* @__PURE__ */ e.createElement("details", { className: "sc-fold", "data-testid": "filament-fold" }, /* @__PURE__ */ e.createElement("summary", null, /* @__PURE__ */ e.createElement("span", { className: "sc-fold-lbl" }, "Settings"), /* @__PURE__ */ e.createElement("b", null, C ? "Modified" : "Preset")), /* @__PURE__ */ e.createElement("div", { className: "sc-fold-body" }, ve)));
1964
+ }
1965
+ function ko({
1966
+ objects: t,
1967
+ extruderColors: r,
1374
1968
  onToggleVisible: c,
1375
- onExtruder: l,
1376
- onSplit: r,
1377
- onRemove: m,
1378
- supportOn: d,
1379
- onToggleSupport: w,
1380
- wipeTowerReal: b,
1381
- onToggleWipeTower: T,
1382
- overhangOn: P,
1383
- onToggleOverhang: M,
1384
- overhangAngle: B,
1385
- paintMode: ee,
1969
+ onExtruder: i,
1970
+ onSplit: o,
1971
+ onRemove: p,
1972
+ supportOn: h,
1973
+ onToggleSupport: j,
1974
+ overhangOn: x,
1975
+ onToggleOverhang: B,
1976
+ overhangAngle: z,
1977
+ paintMode: m,
1386
1978
  onTogglePaint: L,
1387
- supportStyle: te,
1388
- supportStyles: I,
1389
- onSupportStyle: A
1979
+ supportStyle: D,
1980
+ supportStyles: R,
1981
+ onSupportStyle: K,
1982
+ supportFilament: J,
1983
+ onSupportFilament: _,
1984
+ supportInterfaceFilament: T,
1985
+ onSupportInterfaceFilament: F,
1986
+ paintedObjectIds: ce
1390
1987
  }) {
1391
- return /* @__PURE__ */ e.createElement("section", { className: "side-card", "data-testid": "object-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "📦 Objects ", /* @__PURE__ */ e.createElement("span", { className: "sc-count" }, n.length)), /* @__PURE__ */ e.createElement("ul", { className: "obj-list2", "data-testid": "obj-list" }, n.map((u) => /* @__PURE__ */ e.createElement("li", { key: u.id, className: u.visible === !1 ? "obj-hidden" : "" }, /* @__PURE__ */ e.createElement("button", { className: "obj-eye", onClick: () => c(u.id), title: "Include/exclude this object from printing — excluding it keeps it in the scene", "data-testid": `eye-${u.id}` }, /* @__PURE__ */ e.createElement("img", { src: u.visible === !1 ? sr : lr, alt: u.visible === !1 ? "Hidden" : "Visible" })), /* @__PURE__ */ e.createElement("span", { className: "obj-name", title: u.name }, u.name), /* @__PURE__ */ e.createElement("select", { className: "obj-ext", value: u.extruder ?? 1, onChange: (_) => l(u.id, +_.target.value), title: "Which filament (extruder) prints this object", "data-testid": `ext-${u.id}` }, a.map((_, G) => /* @__PURE__ */ e.createElement("option", { key: G, value: G + 1 }, "T", G + 1))), /* @__PURE__ */ e.createElement("button", { className: "obj-split", onClick: () => r(u.id), title: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)", "data-testid": `split-${u.id}` }, /* @__PURE__ */ e.createElement("img", { src: Gt, alt: "Split" })), /* @__PURE__ */ e.createElement("button", { className: "obj-del", onClick: () => m(u.id), title: "Remove this object from the scene" }, /* @__PURE__ */ e.createElement("img", { src: Ft, alt: "Delete" }))))), /* @__PURE__ */ e.createElement("label", { className: "slice-support" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: d, onChange: w, title: "Generate support structures under overhangs (same as enable_support in the settings panel)", "data-testid": "support-toggle" }), " Generate support"), d && /* @__PURE__ */ e.createElement("div", { className: "sc-info sup-style" }, /* @__PURE__ */ e.createElement("span", null, "Style"), /* @__PURE__ */ e.createElement(
1988
+ const pe = Number.isFinite(J) ? J : 0, w = Number.isFinite(T) ? T : 0, S = pe > 0 || w > 0, E = (y) => ce ? typeof ce.has == "function" ? ce.has(y) : Array.isArray(ce) && ce.includes(y) : !1, M = [
1989
+ /* @__PURE__ */ e.createElement("option", { key: "default", value: 0 }, "Default (current tool)"),
1990
+ ...r.map((y, v) => /* @__PURE__ */ e.createElement("option", { key: v, value: v + 1 }, "T", v + 1))
1991
+ ];
1992
+ return /* @__PURE__ */ e.createElement("section", { className: "side-card", "data-testid": "object-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "📦 Objects ", /* @__PURE__ */ e.createElement("span", { className: "sc-count" }, t.length)), /* @__PURE__ */ e.createElement("ul", { className: "obj-list2", "data-testid": "obj-list" }, t.map((y) => /* @__PURE__ */ e.createElement("li", { key: y.id, className: y.visible === !1 ? "obj-hidden" : "" }, /* @__PURE__ */ e.createElement("button", { className: "obj-eye", onClick: () => c(y.id), title: "Include/exclude this object from printing — excluding it keeps it in the scene", "data-testid": `eye-${y.id}` }, /* @__PURE__ */ e.createElement("img", { src: y.visible === !1 ? Oa : Ta, alt: y.visible === !1 ? "Hidden" : "Visible" })), /* @__PURE__ */ e.createElement("span", { className: "obj-name", title: y.name }, y.name), E(y.id) && /* @__PURE__ */ e.createElement(
1993
+ "span",
1994
+ {
1995
+ className: "obj-painted",
1996
+ "data-testid": `obj-painted-${y.id}`,
1997
+ title: "This object has material-painted regions — the extruder below applies only to the rest of it"
1998
+ },
1999
+ "🖌"
2000
+ ), /* @__PURE__ */ e.createElement("select", { className: "obj-ext", value: y.extruder ?? 1, onChange: (v) => i(y.id, +v.target.value), title: "Which filament (extruder) prints this object", "data-testid": `ext-${y.id}` }, r.map((v, C) => /* @__PURE__ */ e.createElement("option", { key: C, value: C + 1 }, "T", C + 1))), /* @__PURE__ */ e.createElement("button", { className: "obj-split", onClick: () => o(y.id), title: "Split to objects — every disconnected part (connected component) becomes its own object. Split to parts is not implemented (no part concept)", "data-testid": `split-${y.id}` }, /* @__PURE__ */ e.createElement("img", { src: rr, alt: "Split" })), /* @__PURE__ */ e.createElement("button", { className: "obj-del", onClick: () => p(y.id), title: "Remove this object from the scene" }, /* @__PURE__ */ e.createElement("img", { src: nr, alt: "Delete" }))))), /* @__PURE__ */ e.createElement("label", { className: "slice-support" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: h, onChange: j, title: "Generate support structures under overhangs (same as enable_support in the settings panel)", "data-testid": "support-toggle" }), " Generate support"), h && /* @__PURE__ */ e.createElement("div", { className: "sc-info sup-style" }, /* @__PURE__ */ e.createElement("span", null, "Style"), /* @__PURE__ */ e.createElement(
1392
2001
  "select",
1393
2002
  {
1394
2003
  className: "sc-model",
1395
- value: te,
1396
- onChange: (u) => A(u.target.value),
2004
+ value: D,
2005
+ onChange: (y) => K(y.target.value),
1397
2006
  "data-testid": "support-style",
1398
2007
  title: "Support style (support_style). The kernel builds the tree/organic family as real tree support; the rest as grid."
1399
2008
  },
1400
- I.map((u) => /* @__PURE__ */ e.createElement("option", { key: u.value, value: u.value }, u.label))
1401
- )), d && /* @__PURE__ */ e.createElement("div", { className: "support-tools" }, /* @__PURE__ */ e.createElement("label", { className: "slice-support" }, /* @__PURE__ */ e.createElement(
2009
+ R.map((y) => /* @__PURE__ */ e.createElement("option", { key: y.value, value: y.value }, y.label))
2010
+ )), h && _ && /* @__PURE__ */ e.createElement("div", { className: "sc-info sup-style" }, /* @__PURE__ */ e.createElement("span", null, "Filament"), /* @__PURE__ */ e.createElement(
2011
+ "select",
2012
+ {
2013
+ className: "sc-model",
2014
+ value: pe,
2015
+ onChange: (y) => _(+y.target.value),
2016
+ "data-testid": "support-filament",
2017
+ title: "Which filament prints the support body (support_filament). Default leaves it on whatever tool the layer is already using."
2018
+ },
2019
+ M
2020
+ )), h && F && /* @__PURE__ */ e.createElement("div", { className: "sc-info sup-style" }, /* @__PURE__ */ e.createElement("span", null, "Interface filament"), /* @__PURE__ */ e.createElement(
2021
+ "select",
2022
+ {
2023
+ className: "sc-model",
2024
+ value: w,
2025
+ onChange: (y) => F(+y.target.value),
2026
+ "data-testid": "support-interface-filament",
2027
+ title: "Which filament prints the support interface — the few layers touching the model (support_interface_filament). This is where a release material pays off."
2028
+ },
2029
+ M
2030
+ )), h && S && // A dedicated support tool is per-layer, not per-object: every layer that has both support and model
2031
+ // changes tool at least twice, so the purge volume multiplies by the layer count rather than the part count.
2032
+ /* @__PURE__ */ e.createElement("div", { className: "sup-filament-warn", "data-testid": "support-filament-warn" }, "⚠ Support on its own filament adds a tool change on nearly every layer — expect a much longer print and far more purge."), h && /* @__PURE__ */ e.createElement("div", { className: "support-tools" }, /* @__PURE__ */ e.createElement("label", { className: "slice-support" }, /* @__PURE__ */ e.createElement(
1402
2033
  "input",
1403
2034
  {
1404
2035
  type: "checkbox",
1405
- checked: !!P,
1406
- onChange: M,
1407
- title: `Shade the facets that will need support (slope below ${B}°, the support threshold angle)`,
2036
+ checked: !!x,
2037
+ onChange: B,
2038
+ title: `Shade the facets that will need support (slope below ${z}°, the support threshold angle)`,
1408
2039
  "data-testid": "overhang-toggle"
1409
2040
  }
1410
- ), " Show overhangs ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "<", B, "°")), /* @__PURE__ */ e.createElement(
2041
+ ), " Show overhangs ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "<", z, "°")), /* @__PURE__ */ e.createElement(
1411
2042
  "button",
1412
2043
  {
1413
- className: ee !== "off" ? "sup-paint on" : "sup-paint",
2044
+ className: m !== "off" ? "sup-paint on" : "sup-paint",
1414
2045
  onClick: L,
1415
- disabled: !n.length,
2046
+ disabled: !t.length,
1416
2047
  "data-testid": "support-paint-btn",
1417
2048
  title: "Brush facets to force or block support — the same tool as the paint icon on the left rail"
1418
2049
  },
1419
- ee !== "off" ? "Painting…" : "Paint support"
1420
- )), /* @__PURE__ */ e.createElement("label", { className: "slice-support" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: b, onChange: T, title: "Use the upstream WipeTower to compute purge volumes on multi-material tool changes (off = a simple square ring)", "data-testid": "wipe-tower-real-toggle" }), " Real wipe tower ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "(MM)")));
2050
+ m !== "off" ? "Painting…" : "Paint support"
2051
+ )));
2052
+ }
2053
+ function Jn(t, r) {
2054
+ const c = Array.isArray(t) ? t.map(Number) : [];
2055
+ return Array.from({ length: r }, (i, o) => Array.from({ length: r }, (p, h) => {
2056
+ const j = c[o * r + h];
2057
+ return Number.isFinite(j) ? j : o === h ? 0 : _o;
2058
+ }));
2059
+ }
2060
+ const _o = 140;
2061
+ function No({
2062
+ settings: t,
2063
+ setSettings: r,
2064
+ extruderColors: c,
2065
+ wipeTowerReal: i,
2066
+ onToggleWipeTower: o,
2067
+ towerStats: p
2068
+ }) {
2069
+ const h = Array.isArray(c) ? c : [], j = h.length, x = t ?? {}, B = (E) => {
2070
+ const M = x[E];
2071
+ return Array.isArray(M) ? M[0] : M;
2072
+ }, z = (E, M) => {
2073
+ const y = Number(B(E));
2074
+ return Number.isFinite(y) ? y : M;
2075
+ }, m = x.enable_prime_tower === !1, L = m ? "off" : i ? "real" : "ring", D = (E) => {
2076
+ r == null || r((M) => {
2077
+ const y = { ...M };
2078
+ return E === "off" ? y.enable_prime_tower = !1 : delete y.enable_prime_tower, y;
2079
+ }), E !== "off" && (o == null || o({ target: { checked: E === "real" } }));
2080
+ }, R = x.flush_into_infill === !0, K = (E) => r == null ? void 0 : r((M) => {
2081
+ const y = { ...M };
2082
+ return E ? y.flush_into_infill = !0 : delete y.flush_into_infill, y;
2083
+ }), J = z("prime_tower_width", 0), _ = B("wipe_tower_x"), T = B("wipe_tower_y"), F = Number.isFinite(Number(_)) && String(_) !== "", ce = (E, M) => r == null ? void 0 : r((y) => {
2084
+ const v = { ...y };
2085
+ return M === null || M === "" || !Number.isFinite(Number(M)) ? delete v[E] : v[E] = Number(M), v;
2086
+ }), pe = (E, M) => r == null ? void 0 : r((y) => {
2087
+ const v = { ...y };
2088
+ return E === null ? (delete v.wipe_tower_x, delete v.wipe_tower_y, v) : (v.wipe_tower_x = Number(E), v.wipe_tower_y = Number(M), v);
2089
+ }), w = Jn(x.flush_volumes_matrix, j), S = (E, M, y) => r == null ? void 0 : r((v) => {
2090
+ const C = { ...v }, V = Jn(v == null ? void 0 : v.flush_volumes_matrix, j);
2091
+ return V[E][M] = Math.max(0, Number(y) || 0), C.flush_volumes_matrix = V.flat(), C;
2092
+ });
2093
+ return /* @__PURE__ */ e.createElement("section", { className: "side-card", "data-testid": "tower-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🗼 Prime tower"), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Mode"), /* @__PURE__ */ e.createElement("select", { className: "sc-model", "data-testid": "tower-mode", value: L, onChange: (E) => D(E.target.value) }, /* @__PURE__ */ e.createElement("option", { value: "ring" }, "Ring — deterministic"), /* @__PURE__ */ e.createElement("option", { value: "real" }, "Real wipe tower — not reproducible yet"), /* @__PURE__ */ e.createElement("option", { value: "off" }, "Off — no tower"))), /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Purge into"), /* @__PURE__ */ e.createElement(
2094
+ "select",
2095
+ {
2096
+ className: "sc-model",
2097
+ "data-testid": "tower-flush",
2098
+ value: R ? "infill" : "tower",
2099
+ onChange: (E) => K(E.target.value === "infill"),
2100
+ title: "Infill hides the purge inside the part; a layer with no sparse infill still needs the tower."
2101
+ },
2102
+ /* @__PURE__ */ e.createElement("option", { value: "tower" }, "The tower"),
2103
+ /* @__PURE__ */ e.createElement("option", { value: "infill" }, "The model's infill")
2104
+ )), m && !R && /* @__PURE__ */ e.createElement("div", { className: "bp-note", "data-testid": "tower-off-warning" }, "With no tower and no flush destination, a tool change carries the previous colour into the model."), L !== "off" && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Width"), /* @__PURE__ */ e.createElement("span", { className: "sc-num" }, /* @__PURE__ */ e.createElement(
2105
+ "input",
2106
+ {
2107
+ type: "number",
2108
+ min: "5",
2109
+ max: "150",
2110
+ step: "1",
2111
+ value: J || "",
2112
+ placeholder: "auto",
2113
+ "data-testid": "tower-width",
2114
+ title: "Footprint of the tower (mm). Empty uses the slicer's own default.",
2115
+ onChange: (E) => ce("prime_tower_width", E.target.value)
2116
+ }
2117
+ ), " mm")), L !== "off" && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "Position"), /* @__PURE__ */ e.createElement(
2118
+ "select",
2119
+ {
2120
+ className: "sc-model",
2121
+ "data-testid": "tower-position-mode",
2122
+ value: F ? "manual" : "auto",
2123
+ onChange: (E) => pe(E.target.value === "auto" ? null : (p == null ? void 0 : p.x) ?? 10, (p == null ? void 0 : p.y) ?? 10),
2124
+ title: "Auto places the tower beside the model; manual pins it to a bed coordinate."
2125
+ },
2126
+ /* @__PURE__ */ e.createElement("option", { value: "auto" }, "Beside the model"),
2127
+ /* @__PURE__ */ e.createElement("option", { value: "manual" }, "Fixed coordinate")
2128
+ )), L !== "off" && F && /* @__PURE__ */ e.createElement("div", { className: "sc-info" }, /* @__PURE__ */ e.createElement("span", null, "X / Y"), /* @__PURE__ */ e.createElement("span", { className: "sc-num" }, /* @__PURE__ */ e.createElement(
2129
+ "input",
2130
+ {
2131
+ type: "number",
2132
+ step: "1",
2133
+ value: Number(_),
2134
+ "data-testid": "tower-x",
2135
+ onChange: (E) => pe(E.target.value, Number(T) || 0)
2136
+ }
2137
+ ), /* @__PURE__ */ e.createElement(
2138
+ "input",
2139
+ {
2140
+ type: "number",
2141
+ step: "1",
2142
+ value: Number(T) || 0,
2143
+ "data-testid": "tower-y",
2144
+ onChange: (E) => pe(Number(_) || 0, E.target.value)
2145
+ }
2146
+ ), " mm")), j > 1 && /* @__PURE__ */ e.createElement("details", { className: "sc-fold", "data-testid": "tower-purge-fold" }, /* @__PURE__ */ e.createElement("summary", null, /* @__PURE__ */ e.createElement("span", { className: "sc-fold-lbl" }, "Purging volumes"), /* @__PURE__ */ e.createElement("b", null, "mm³")), /* @__PURE__ */ e.createElement("div", { className: "sc-fold-body" }, /* @__PURE__ */ e.createElement("table", { className: "purge-grid", "data-testid": "purge-matrix" }, /* @__PURE__ */ e.createElement("thead", null, /* @__PURE__ */ e.createElement("tr", null, /* @__PURE__ */ e.createElement("th", null), h.map((E, M) => /* @__PURE__ */ e.createElement("th", { key: M }, /* @__PURE__ */ e.createElement("span", { className: "stat-swatch", style: { background: E } }), "T", M + 1)))), /* @__PURE__ */ e.createElement("tbody", null, h.map((E, M) => /* @__PURE__ */ e.createElement("tr", { key: M }, /* @__PURE__ */ e.createElement("th", null, /* @__PURE__ */ e.createElement("span", { className: "stat-swatch", style: { background: E } }), "T", M + 1), h.map((y, v) => /* @__PURE__ */ e.createElement("td", { key: v }, M === v ? /* @__PURE__ */ e.createElement("span", { className: "muted" }, "—") : /* @__PURE__ */ e.createElement(
2147
+ "input",
2148
+ {
2149
+ type: "number",
2150
+ min: "0",
2151
+ step: "10",
2152
+ value: w[M][v],
2153
+ "data-testid": `purge-${M}-${v}`,
2154
+ onChange: (C) => S(M, v, C.target.value)
2155
+ }
2156
+ ))))))), /* @__PURE__ */ e.createElement("div", { className: "muted bp-note" }, "Volume pushed out when the print switches from the row's filament to the column's. A dark colour following a light one needs more."))), p && /* @__PURE__ */ e.createElement("div", { className: "muted bp-counts", "data-testid": "tower-stats" }, "last slice: ", p.changes, " tool change", p.changes === 1 ? "" : "s", " ·", " ", p.purge.toFixed(1), " mm purged", Number.isFinite(p.x) && ` · at ${p.x.toFixed(0)}, ${p.y.toFixed(0)}`));
1421
2157
  }
1422
- function Hr({
1423
- autoSlice: n,
1424
- onAutoSlice: a,
2158
+ function Mo({
2159
+ autoSlice: t,
2160
+ onAutoSlice: r,
1425
2161
  slicing: c,
1426
- progress: l,
1427
- plateCount: r,
1428
- selectedPlate: m,
1429
- sliceMenuOpen: d,
1430
- onSliceMenu: w,
1431
- slicedPlateCount: b,
1432
- canSlice: T,
1433
- onSlice: P,
1434
- onCancel: M,
1435
- onExportAll: B,
1436
- gcodeUrl: ee
2162
+ progress: i,
2163
+ plateCount: o,
2164
+ selectedPlate: p,
2165
+ sliceMenuOpen: h,
2166
+ onSliceMenu: j,
2167
+ slicedPlateCount: x,
2168
+ canSlice: B,
2169
+ onSlice: z,
2170
+ onCancel: m,
2171
+ onExportAll: L,
2172
+ gcodeUrl: D
1437
2173
  }) {
1438
- const L = c ? "Click to cancel the slice" : r > 1 ? "Choose what to slice (Ctrl+R = current plate)" : "Slice the current plate (Ctrl+R)";
1439
- return /* @__PURE__ */ e.createElement("div", { className: "side-bottom" }, /* @__PURE__ */ e.createElement("label", { className: "auto-slice", "data-testid": "auto-slice", title: "Re-slice automatically 0.8s after a settings change (the first slice is manual; a running slice is canceled and restarted)" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: n, onChange: (te) => a(te.target.checked) }), " Auto slice"), /* @__PURE__ */ e.createElement("div", { className: "slice-dd" }, /* @__PURE__ */ e.createElement(
2174
+ const R = c ? "Click to cancel the slice" : o > 1 ? "Choose what to slice (Ctrl+R = current plate)" : "Slice the current plate (Ctrl+R)";
2175
+ return /* @__PURE__ */ e.createElement("div", { className: "side-bottom" }, /* @__PURE__ */ e.createElement("label", { className: "auto-slice", "data-testid": "auto-slice", title: "Re-slice automatically 0.8s after a settings change (the first slice is manual; a running slice is canceled and restarted)" }, /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: t, onChange: (K) => r(K.target.checked) }), " Auto slice"), /* @__PURE__ */ e.createElement("div", { className: "slice-dd" }, /* @__PURE__ */ e.createElement(
1440
2176
  "button",
1441
2177
  {
1442
2178
  className: "slice-btn",
1443
- title: L,
1444
- onClick: () => c ? M() : r > 1 ? w() : P("current"),
1445
- disabled: !T,
2179
+ title: R,
2180
+ onClick: () => c ? m() : o > 1 ? j() : z("current"),
2181
+ disabled: !B,
1446
2182
  "data-testid": "slice-btn"
1447
2183
  },
1448
- c ? `Slicing… ${Math.round(l * 100)}%` : r > 1 ? "Slice ▾" : "Slice"
1449
- ), d && r > 1 && /* @__PURE__ */ e.createElement("div", { className: "slice-menu", "data-testid": "slice-menu" }, /* @__PURE__ */ e.createElement("button", { onClick: () => P("current"), "data-testid": "slice-current", title: "Slice only the selected plate (Ctrl+R)" }, "Current plate (P", m + 1, ")"), /* @__PURE__ */ e.createElement("button", { onClick: () => P("all"), "data-testid": "slice-all", title: "Slice every plate in turn — switch tabs to inspect the results" }, "All plates (", r, ")"), b > 0 && /* @__PURE__ */ e.createElement("button", { onClick: B, "data-testid": "export-all", title: "Save the G-code of every sliced plate as its own file" }, "Export all G-code (", b, ")"))), ee ? /* @__PURE__ */ e.createElement("a", { className: "export-btn", href: ee, download: `plate_${m + 1}.gcode`, title: "Save the G-code of the plate you are viewing", "data-testid": "gcode-dl" }, "Export G-code") : /* @__PURE__ */ e.createElement("button", { className: "export-btn", disabled: !0, title: "Export G-code — enabled after slicing" }, "Export G-code"));
2184
+ c ? `Slicing… ${Math.round(i * 100)}%` : o > 1 ? "Slice ▾" : "Slice"
2185
+ ), h && o > 1 && /* @__PURE__ */ e.createElement("div", { className: "slice-menu", "data-testid": "slice-menu" }, /* @__PURE__ */ e.createElement("button", { onClick: () => z("current"), "data-testid": "slice-current", title: "Slice only the selected plate (Ctrl+R)" }, "Current plate (P", p + 1, ")"), /* @__PURE__ */ e.createElement("button", { onClick: () => z("all"), "data-testid": "slice-all", title: "Slice every plate in turn — switch tabs to inspect the results" }, "All plates (", o, ")"), x > 0 && /* @__PURE__ */ e.createElement("button", { onClick: L, "data-testid": "export-all", title: "Save the G-code of every sliced plate as its own file" }, "Export all G-code (", x, ")"))), D ? /* @__PURE__ */ e.createElement("a", { className: "export-btn", href: D, download: `plate_${p + 1}.gcode`, title: "Save the G-code of the plate you are viewing", "data-testid": "gcode-dl" }, "Export G-code") : /* @__PURE__ */ e.createElement("button", { className: "export-btn", disabled: !0, title: "Export G-code — enabled after slicing" }, "Export G-code"));
1450
2186
  }
1451
- function no({ settings: n = {}, setSettings: a = () => {
1452
- }, processPanel: c = null, motionPanel: l = null }) {
1453
- var Pt, Lt, Ot;
1454
- const r = H(null), m = H(null), d = H([]), w = H(null), b = H(null), T = H(null), P = H({}), M = H(null), B = H(null), ee = H(!1), L = H("feature"), te = H(0), I = H(0), A = H("prepare"), u = H(0.42), _ = H("off"), G = H(5), K = H(null), h = H(null), E = H({}), k = H({}), j = H(0), f = H(1), g = H(0), [y, v] = $(!0), [D, W] = $("translate"), [oe, ge] = $("Initializing…"), [de, Y] = $([]), [C, pe] = $(""), [je, Ce] = $(!1), [ye, Re] = $(!1), be = H(0), [Se, i] = $(0), [S, V] = $(""), [p, O] = $(null), [ue, ve] = $(!1), [Ee, Z] = $(0), [q, z] = $(0), [ie, we] = $(1), [se, ae] = $(0), [J, ce] = $(!1), [ke, ze] = $(!1), [Fe, qe] = $(0), [Ie, We] = $(null), [et, tt] = $("feature"), [nt, Xe] = $(null), [rt, t] = $(0), [s, R] = $(0), [F, re] = $(!1), [U, me] = $([]), [ne, Me] = $("prepare"), [Ne, Pe] = $(!1), Ae = H(null), [De, Oe] = $(["#6aa0dc", "#e08a2b"]), Ze = H(["#6aa0dc", "#e08a2b"]), [fe, le] = $(""), [Le, _e] = $(!1), [Te, X] = $(!1), [xe, Ge] = $("off"), [Ue, yt] = $(5), [Ut, vt] = $({ enf: 0, blk: 0 }), [wt, st] = $(""), [kt, Et] = $(null), { mountRef: $t, three: ot } = jr({
1455
- apiRef: r,
1456
- objectsRef: d,
1457
- keyRef: M,
1458
- workerRef: m,
1459
- selectedPlateRef: j,
1460
- placeXRef: g,
1461
- plateCountRef: f,
1462
- canvasModeRef: A,
1463
- paintModeRef: _,
1464
- brushRadiusRef: G,
1465
- paintXformRef: K,
1466
- extruderColorsRef: Ze,
1467
- setOk: v,
1468
- setStatus: ge,
1469
- setGmode: W,
1470
- setCtxMenu: We,
1471
- setBrushRadius: yt
1472
- }), $e = xt(n);
1473
- Ve(() => {
1474
- var o;
1475
- (o = r.current) == null || o.setPlates(ie, $e.bed_width, $e.bed_depth, se);
1476
- }, [$e.bed_width, $e.bed_depth, ie, se]), Ve(() => {
1477
- var Q;
1478
- A.current = ne;
1479
- const o = ot.current;
1480
- if (!o.toolpathGroup) return;
1481
- const x = ne === "preview";
1482
- o.toolpathGroup.visible = x, o.objectsGroup && (o.objectsGroup.visible = !x && d.current.length > 0), x && ((Q = r.current) == null || Q.detachTransform(), _.current !== "off" && Ke("off"));
1483
- }, [ne]);
2187
+ const Qn = [
2188
+ "#6aa0dc",
2189
+ "#e08a2b",
2190
+ "#e0473b",
2191
+ "#3bb0e0",
2192
+ "#7ad14a",
2193
+ "#b06ad1",
2194
+ "#d1c34a",
2195
+ "#4ad1a8",
2196
+ "#d16a9a",
2197
+ "#6a7ad1",
2198
+ "#8fd16a",
2199
+ "#d18f6a",
2200
+ "#6ad1d1",
2201
+ "#c74ad1",
2202
+ "#a8a8a8",
2203
+ "#4a6ad1"
2204
+ ];
2205
+ function Fo({
2206
+ settings: t = {},
2207
+ setSettings: r = () => {
2208
+ },
2209
+ processPanel: c = null,
2210
+ motionPanel: i = null,
2211
+ filamentPanel: o = null,
2212
+ panels: p = null,
2213
+ gcode: h = null,
2214
+ defaultExtruderColors: j = null,
2215
+ defaultAutoSlice: x = !1,
2216
+ onEvent: B = null,
2217
+ onSliced: z = null
2218
+ }) {
2219
+ var zn, Rn, Tn, On, Ln, Sn, Fn;
2220
+ const m = (n) => (p == null ? void 0 : p[n]) !== !1, L = ne(B);
2221
+ L.current = B;
2222
+ const D = ne(z);
2223
+ D.current = z;
2224
+ const R = ne(null), K = ne(null), J = ne([]), _ = ne(null), T = ne(null), F = ne(null), ce = ne({}), pe = ne(null), w = ne(null), S = ne(!1), E = ne("feature"), M = ne(0), y = ne(0), v = ne("prepare"), C = ne(0.42), V = ne("off"), f = ne(0), N = ne(5), O = ne(null), H = ne(null), oe = ne(null), ue = ne(null), $ = ne({}), P = ne({}), ve = ne(0), Me = ne(1), ge = ne(0), [Ee, b] = ae(!0), [k, G] = ae("translate"), [q, s] = ae("Initializing…"), [d, A] = ae([]), [u, I] = ae(""), [se, ye] = ae(!1), [xe, je] = ae(!!x), Q = ne(0), [Z, ee] = ae(0), [he, me] = ae(""), [fe, le] = ae(null), [Ae, Pe] = ae(!1), [Re, qe] = ae(0), [dt, Xe] = ae(0), [Je, Nt] = ae(1), [$e, Mt] = ae(0), [At, Ct] = ae(!1), [mt, ft] = ae(!1), [at, ht] = ae(0), [bt, gt] = ae(null), [yt, xt] = ae("feature"), [jt, a] = ae(null), [l, U] = ae(0), [Y, re] = ae(0), [X, ke] = ae(!1), [ze, Ce] = ae([]), [be, Se] = ae("prepare"), [Ve, ot] = ae(!1), Ue = ne(null), lt = Array.isArray(j) && j.length ? j.slice(0, $t) : Qn.slice(0, 2), [we, _e] = ae(lt), Le = ne(lt), [Ie, We] = ae(""), [Qe, Ye] = ae(!1), [et, vt] = ae(!1), [de, Fe] = ae("off"), [Zt, st] = ae(0), [tt, Vt] = ae(5), [Pt, sn] = ae("brush"), [zt, cn] = ae("sphere"), [Rt, dn] = ae(30), pn = ne({ tool: "brush", cursor: "sphere", angle: 30 });
2225
+ Te(() => {
2226
+ pn.current = { tool: Pt, cursor: zt, angle: Rt };
2227
+ }, [Pt, zt, Rt]);
2228
+ const [Gt, un] = ae({ enf: 0, blk: 0 }), [Ut, lr] = ae({}), mn = ne({});
2229
+ Te(() => {
2230
+ mn.current = Ut;
2231
+ }, [Ut]);
2232
+ const [Wt, pt] = ae(""), [fn, hn] = ae(null), Ze = (n, g) => {
2233
+ const te = ne(!0);
2234
+ Te(() => {
2235
+ var ie;
2236
+ if (te.current) {
2237
+ te.current = !1;
2238
+ return;
2239
+ }
2240
+ (ie = L.current) == null || ie.call(L, { type: n, value: g });
2241
+ }, [g]);
2242
+ };
2243
+ Ze("canvasMode", be), Ze("objects", d), Ze("selectedPlate", $e), Ze("plateCount", Je), Ze("extruderColors", we), Ze("autoSlice", xe), Ze("slicing", se), Ze("progress", Z), Ze("viewType", yt), Ze("paintMode", de), Ze("layerCount", Re), Ze("error", he), Ze("notice", Wt);
2244
+ const bn = ne(!0);
2245
+ Te(() => {
2246
+ var n;
2247
+ if (bn.current) {
2248
+ bn.current = !1;
2249
+ return;
2250
+ }
2251
+ (n = L.current) == null || n.call(L, { type: "layerRange", value: { lo: l, hi: Y } });
2252
+ }, [l, Y]);
2253
+ const { mountRef: sr, three: Tt } = Ja({
2254
+ apiRef: R,
2255
+ objectsRef: J,
2256
+ keyRef: pe,
2257
+ workerRef: K,
2258
+ selectedPlateRef: ve,
2259
+ placeXRef: ge,
2260
+ plateCountRef: Me,
2261
+ canvasModeRef: v,
2262
+ paintModeRef: V,
2263
+ brushRadiusRef: N,
2264
+ paintToolRef: pn,
2265
+ paintXformRef: O,
2266
+ paintOverlayRef: H,
2267
+ extruderColorsRef: Le,
2268
+ setOk: b,
2269
+ setStatus: s,
2270
+ setGmode: G,
2271
+ setCtxMenu: gt,
2272
+ setBrushRadius: Vt,
2273
+ // Dragging the tower box is how a position becomes chosen: it writes the same wipe_tower_x/y the card edits,
2274
+ // so the two controls are one setting seen two ways.
2275
+ // The drop lands in world coordinates and the setting is a bed coordinate, so the plate's origin comes back
2276
+ // off — the same conversion the box's placement makes going the other way.
2277
+ // A move/rotate/scale changes the coordinates the kernel's selector holds, so it has to be handed the new
2278
+ // ones — otherwise the overlay stays where the model was and a slice projects the paint there too. Gated on
2279
+ // the selector EXISTING, not on paint mode being on: in paint mode the gizmo is detached, so every real move
2280
+ // happens with the panel closed — a paint-mode gate would never fire. Through a ref because the paint module
2281
+ // is built further down and this handler is installed once.
2282
+ onTransformCommitted: () => {
2283
+ var n;
2284
+ oe.current && ((n = ue.current) == null || n.call(ue));
2285
+ },
2286
+ onTowerMoved: (n, g) => {
2287
+ var ie, Ne;
2288
+ const te = ((Ne = (ie = R.current) == null ? void 0 : ie.platePos) == null ? void 0 : Ne.call(ie, ve.current)) ?? { x: 0, z: 0 };
2289
+ r((Be) => ({
2290
+ ...Be,
2291
+ wipe_tower_x: Math.round((n - te.x + Ht.current.bedW / 2) * 10) / 10,
2292
+ wipe_tower_y: Math.round((g + te.z + Ht.current.bedD / 2) * 10) / 10
2293
+ }));
2294
+ }
2295
+ }), Oe = ln(t), Ht = ne({ bedW: 200, bedD: 200 });
2296
+ Ht.current = { bedW: Oe.bed_width, bedD: Oe.bed_depth }, Te(() => {
2297
+ var n;
2298
+ (n = R.current) == null || n.setPlates(Je, Oe.bed_width, Oe.bed_depth, $e);
2299
+ }, [Oe.bed_width, Oe.bed_depth, Je, $e]), Te(() => {
2300
+ var In, Zn, Vn, Gn;
2301
+ const n = R.current;
2302
+ if (!n) return;
2303
+ const g = we.length > 1, te = (t == null ? void 0 : t.enable_prime_tower) === !1;
2304
+ if (!g || te || be !== "prepare" || d.length === 0) {
2305
+ (In = n.setPrimeTower) == null || In.call(n, null);
2306
+ return;
2307
+ }
2308
+ const ie = et ? Number(He(t, "prime_tower_width")) || 30 : 15, Ne = (Zn = n.modelBounds) == null ? void 0 : Zn.call(n, $e), Be = Oe.bed_width, De = Oe.bed_depth, Ke = ((Vn = n.platePos) == null ? void 0 : Vn.call(n, $e)) ?? { x: 0, z: 0 }, wt = (nn) => {
2309
+ const ct = t == null ? void 0 : t[nn], Lt = Number(Array.isArray(ct) ? ct[0] : ct);
2310
+ return ct != null && ct !== "" && Number.isFinite(Lt) ? Lt : NaN;
2311
+ }, Bn = wt("wipe_tower_x"), Qr = wt("wipe_tower_y"), Dn = !Number.isFinite(Bn), ea = 5, $n = (nn, ct, Lt) => Math.min(Math.max(nn, ct), Lt), ta = Dn ? Ke.x + $n((Ne ? Ne.minX - Ke.x : 0) - ea - ie / 2, -Be / 2 + ie / 2, Be / 2 - ie / 2) : Ke.x + Bn - Be / 2 + ie / 2, na = Dn ? -Ke.z + $n(Ne ? (Ne.minY + Ne.maxY) / 2 + Ke.z : 0, -De / 2 + ie / 2, De / 2 - ie / 2) : -Ke.z + Qr - De / 2 + ie / 2, ra = Math.max(2, (Ne == null ? void 0 : Ne.height) ?? 20);
2312
+ (Gn = n.setPrimeTower) == null || Gn.call(n, { x: ta, y: na, size: ie, height: ra });
2313
+ }, [we.length, be, d.length, et, t, $e, Oe.bed_width, Oe.bed_depth]), Te(() => {
2314
+ var te;
2315
+ v.current = be;
2316
+ const n = Tt.current;
2317
+ if (!n.toolpathGroup) return;
2318
+ const g = be === "preview";
2319
+ n.toolpathGroup.visible = g, n.objectsGroup && (n.objectsGroup.visible = !g && J.current.length > 0), g && ((te = R.current) == null || te.detachTransform(), V.current !== "off" && nt("off"));
2320
+ }, [be]);
1484
2321
  const {
1485
- disposePlateToolpath: Ht,
1486
- clearToolpaths: qt,
1487
- buildPlateToolpath: Wt,
1488
- ensurePlateToolpaths: Xt,
1489
- applyViewColors: Mt,
1490
- rebuildToolpaths: Kt,
1491
- applyLayerRange: Yt
1492
- } = Sr({
1493
- three: ot,
1494
- plateTpRef: P,
1495
- toolpathRef: b,
1496
- segDataRef: T,
1497
- layersDataRef: w,
1498
- plateResultsRef: E,
1499
- plateOffsetsRef: k,
1500
- lineWidthRef: u,
1501
- showTravelRef: ee,
1502
- viewTypeRef: L,
1503
- layerLoRef: te,
1504
- layerHiRef: I,
1505
- selectedPlateRef: j,
1506
- settings: n,
1507
- setSegCount: z,
1508
- setColorRange: Xe,
1509
- setRoleLegend: me
1510
- }), { getWorker: Jt, cancelSlice: jt, runSlice: Qt, pendingSliceRef: en, downgradeRef: tn } = Cr({
1511
- settings: n,
1512
- wipeTowerReal: Te,
1513
- workerRef: m,
1514
- apiRef: r,
1515
- layersDataRef: w,
1516
- layerLoRef: te,
1517
- layerHiRef: I,
1518
- rebuildToolpaths: Kt,
1519
- rebuildPaintOverlay: (o, x) => nn(o, x),
1520
- setProgress: i,
1521
- setSlicing: Ce,
1522
- setError: V,
1523
- setStats: O,
1524
- setOverBed: ve,
1525
- setLayerCount: Z,
1526
- setLayerLo: t,
1527
- setLayerHi: R,
1528
- setGcodeUrl: le,
1529
- setCanvasMode: Me,
1530
- setPaintCounts: vt
1531
- }), { rebuildPaintOverlay: nn, setPaintMode: Ke, clearPaint: rn } = Ar({
1532
- three: ot,
1533
- objectsRef: d,
1534
- apiRef: r,
1535
- getWorker: Jt,
1536
- selectedPlateRef: j,
1537
- paintXformRef: K,
1538
- paintOverlayRef: h,
1539
- paintModeRef: _,
1540
- setError: V,
1541
- setPaintModeState: Ge,
1542
- setPaintCounts: vt
1543
- }), {
1544
- refreshSlicedCount: on,
1545
- exportAllGcode: an,
1546
- onSlice: it,
1547
- retryDowngrade: ln,
1548
- addPlate: sn,
1549
- deletePlate: cn,
1550
- selectPlate: dn
1551
- } = Rr({
1552
- apiRef: r,
1553
- selectedPlateRef: j,
1554
- plateCountRef: f,
1555
- placeXRef: g,
1556
- plateResultsRef: E,
1557
- plateOffsetsRef: k,
1558
- plateTpRef: P,
1559
- layersDataRef: w,
1560
- toolpathRef: b,
1561
- segDataRef: T,
1562
- layerLoRef: te,
1563
- layerHiRef: I,
1564
- lineWidthRef: u,
1565
- downgradeRef: tn,
1566
- settings: n,
1567
- canvasMode: ne,
1568
- downgradeOffer: kt,
1569
- runSlice: Qt,
1570
- ensurePlateToolpaths: Xt,
1571
- buildPlateToolpath: Wt,
1572
- applyViewColors: Mt,
1573
- disposePlateToolpath: Ht,
1574
- setStats: O,
1575
- setOverBed: ve,
1576
- setLayerCount: Z,
1577
- setSegCount: z,
1578
- setColorRange: Xe,
1579
- setRoleLegend: me,
1580
- setGcodeUrl: le,
1581
- setLayerLo: t,
1582
- setLayerHi: R,
1583
- setCanvasMode: Me,
1584
- setSlicedPlateCount: qe,
1585
- setSliceMenu: ce,
1586
- setError: V,
1587
- setSliceNotice: st,
1588
- setDowngradeOffer: Et,
1589
- setSlicing: Ce,
1590
- setProgress: i,
1591
- setPlateCount: we,
1592
- setSelectedPlate: ae
1593
- }), { onFiles: pn, removeObject: Ct, onDrop: un, onDragOver: mn, onDragLeave: fn } = Nr({
1594
- apiRef: r,
1595
- objectsRef: d,
1596
- layersDataRef: w,
1597
- segDataRef: T,
1598
- plateResultsRef: E,
1599
- plateOffsetsRef: k,
1600
- clearToolpaths: qt,
1601
- refreshSlicedCount: on,
1602
- dragOver: Ne,
1603
- setError: V,
1604
- setTriWarn: pe,
1605
- setProgress: i,
1606
- setStats: O,
1607
- setOverBed: ve,
1608
- setLayerCount: Z,
1609
- setSegCount: z,
1610
- setColorRange: Xe,
1611
- setSliceNotice: st,
1612
- setDowngradeOffer: Et,
1613
- setGcodeUrl: le,
1614
- setCanvasMode: Me,
1615
- setObjects: Y,
1616
- setDragOver: Pe
2322
+ disposePlateToolpath: ir,
2323
+ clearToolpaths: cr,
2324
+ buildPlateToolpath: dr,
2325
+ ensurePlateToolpaths: pr,
2326
+ applyViewColors: Ot,
2327
+ rebuildToolpaths: ur,
2328
+ applyLayerRange: mr
2329
+ } = eo({
2330
+ three: Tt,
2331
+ plateTpRef: ce,
2332
+ toolpathRef: T,
2333
+ segDataRef: F,
2334
+ layersDataRef: _,
2335
+ plateResultsRef: $,
2336
+ plateOffsetsRef: P,
2337
+ lineWidthRef: C,
2338
+ showTravelRef: S,
2339
+ viewTypeRef: E,
2340
+ layerLoRef: M,
2341
+ layerHiRef: y,
2342
+ selectedPlateRef: ve,
2343
+ extruderColorsRef: Le,
2344
+ settings: t,
2345
+ setSegCount: Xe,
2346
+ setColorRange: a,
2347
+ setRoleLegend: Ce
2348
+ }), { getWorker: fr, cancelSlice: gn, runSlice: hr, pendingSliceRef: br, downgradeRef: gr } = Qa({
2349
+ settings: t,
2350
+ wipeTowerReal: et,
2351
+ workerRef: K,
2352
+ apiRef: R,
2353
+ layersDataRef: _,
2354
+ layerLoRef: M,
2355
+ layerHiRef: y,
2356
+ paintStateCountsRef: mn,
2357
+ rebuildToolpaths: ur,
2358
+ rebuildPaintOverlay: (n, g, te) => yr(n, g, te),
2359
+ setProgress: ee,
2360
+ setSlicing: ye,
2361
+ setError: me,
2362
+ setStats: le,
2363
+ setOverBed: Pe,
2364
+ setLayerCount: qe,
2365
+ setLayerLo: U,
2366
+ setLayerHi: re,
2367
+ setGcodeUrl: We,
2368
+ setCanvasMode: Se,
2369
+ setPaintCounts: un,
2370
+ setSliceNotice: pt
2371
+ }), { rebuildPaintOverlay: yr, setPaintMode: nt, clearPaint: yn, registerSelector: xr } = ao({
2372
+ three: Tt,
2373
+ objectsRef: J,
2374
+ apiRef: R,
2375
+ getWorker: fr,
2376
+ selectedPlateRef: ve,
2377
+ selectorGeomRef: oe,
2378
+ paintXformRef: O,
2379
+ paintOverlayRef: H,
2380
+ paintModeRef: V,
2381
+ materialExtruderRef: f,
2382
+ extruderColorsRef: Le,
2383
+ setError: me,
2384
+ setPaintModeState: Fe,
2385
+ setPaintCounts: un,
2386
+ setPaintStateCounts: lr,
2387
+ setSliceNotice: pt
2388
+ });
2389
+ ue.current = xr;
2390
+ const {
2391
+ showPlateResult: vr,
2392
+ refreshSlicedCount: xn,
2393
+ exportAllGcode: wr,
2394
+ onSlice: qt,
2395
+ retryDowngrade: Er,
2396
+ addPlate: kr,
2397
+ deletePlate: _r,
2398
+ selectPlate: Nr
2399
+ } = to({
2400
+ onSlicedRef: D,
2401
+ apiRef: R,
2402
+ selectedPlateRef: ve,
2403
+ plateCountRef: Me,
2404
+ placeXRef: ge,
2405
+ plateResultsRef: $,
2406
+ plateOffsetsRef: P,
2407
+ plateTpRef: ce,
2408
+ layersDataRef: _,
2409
+ toolpathRef: T,
2410
+ segDataRef: F,
2411
+ layerLoRef: M,
2412
+ layerHiRef: y,
2413
+ lineWidthRef: C,
2414
+ downgradeRef: gr,
2415
+ settings: t,
2416
+ canvasMode: be,
2417
+ downgradeOffer: fn,
2418
+ runSlice: hr,
2419
+ ensurePlateToolpaths: pr,
2420
+ buildPlateToolpath: dr,
2421
+ applyViewColors: Ot,
2422
+ disposePlateToolpath: ir,
2423
+ setStats: le,
2424
+ setOverBed: Pe,
2425
+ setLayerCount: qe,
2426
+ setSegCount: Xe,
2427
+ setColorRange: a,
2428
+ setRoleLegend: Ce,
2429
+ setGcodeUrl: We,
2430
+ setLayerLo: U,
2431
+ setLayerHi: re,
2432
+ setCanvasMode: Se,
2433
+ setSlicedPlateCount: ht,
2434
+ setSliceMenu: Ct,
2435
+ setError: me,
2436
+ setSliceNotice: pt,
2437
+ setDowngradeOffer: hn,
2438
+ setSlicing: ye,
2439
+ setProgress: ee,
2440
+ setPlateCount: Nt,
2441
+ setSelectedPlate: Mt,
2442
+ // Belt to the commit hook's braces: a move can reach a slice without a gizmo commit (keyboard nudge, plate
2443
+ // re-arrange), so the slice itself hands the selector the mesh it is about to cut. Only for the selected
2444
+ // plate — that is the mesh the brush painted — and only when a selector exists at all.
2445
+ syncPaintSelector: (n) => {
2446
+ var g;
2447
+ oe.current && ((g = ue.current) == null || g.call(ue, n));
2448
+ }
2449
+ }), { onFiles: Mr, removeObject: vn, onDrop: Ar, onDragOver: Cr, onDragLeave: jr } = no({
2450
+ apiRef: R,
2451
+ objectsRef: J,
2452
+ layersDataRef: _,
2453
+ segDataRef: F,
2454
+ plateResultsRef: $,
2455
+ plateOffsetsRef: P,
2456
+ clearToolpaths: cr,
2457
+ refreshSlicedCount: xn,
2458
+ dragOver: Ve,
2459
+ setError: me,
2460
+ setTriWarn: I,
2461
+ setProgress: ee,
2462
+ setStats: le,
2463
+ setOverBed: Pe,
2464
+ setLayerCount: qe,
2465
+ setSegCount: Xe,
2466
+ setColorRange: a,
2467
+ setSliceNotice: pt,
2468
+ setDowngradeOffer: hn,
2469
+ setGcodeUrl: We,
2470
+ setCanvasMode: Se,
2471
+ setObjects: A,
2472
+ setDragOver: ot
1617
2473
  });
1618
- Ve(() => {
1619
- if (!ye || !de.length || !E.current[j.current]) return;
1620
- clearTimeout(be.current);
1621
- const o = () => {
1622
- if (en.current) {
1623
- jt(), be.current = setTimeout(o, 300);
2474
+ Te(() => {
2475
+ if (!xe || !d.length || h != null) return;
2476
+ clearTimeout(Q.current);
2477
+ const n = () => {
2478
+ if (br.current) {
2479
+ gn(), Q.current = setTimeout(n, 300);
1624
2480
  return;
1625
2481
  }
1626
- it("current");
2482
+ qt("current");
1627
2483
  };
1628
- return be.current = setTimeout(o, 800), () => clearTimeout(be.current);
1629
- }, [n, ye]);
1630
- function hn(o, x) {
1631
- if (!(o > 0) || !(x > 0)) return;
1632
- const Q = Je(n, "printable_area"), he = Array.isArray(Q) && Q.length >= 3, Be = he ? Math.min(...Q.map((Qe) => Qe[0])) : 0, at = he ? Math.min(...Q.map((Qe) => Qe[1])) : 0;
1633
- a((Qe) => ({ ...Qe, printable_area: [[Be, at], [Be + o, at], [Be + o, at + x], [Be, at + x]] }));
1634
- }
1635
- function gn(o, x) {
1636
- var Q;
1637
- (Q = r.current) == null || Q.setObjectExtruder(o, x), Y(d.current.map((he) => ({ id: he.id, name: he.name, extruder: he.extruder, visible: he.visible !== !1 })));
1638
- }
1639
- function He(o, x) {
1640
- const Q = Math.max(0, Ee - 1);
1641
- if (o = Math.max(0, Math.min(Q, o)), x = Math.max(0, Math.min(Q, x)), o > x) {
1642
- const he = o;
1643
- o = x, x = he;
1644
- }
1645
- te.current = o, I.current = x, t(o), R(x), Yt();
1646
- }
1647
- function bn(o) {
1648
- const x = parseInt(o.target.value, 10);
1649
- F ? He(x, x) : He(x, I.current);
1650
- }
1651
- function xn(o) {
1652
- const x = parseInt(o.target.value, 10);
1653
- He(F ? x : te.current, x);
1654
- }
1655
- function St() {
1656
- const o = !F;
1657
- re(o), o && He(I.current, I.current);
1658
- }
1659
- function yn(o) {
1660
- const x = o.target.value;
1661
- tt(x), L.current = x, Mt();
1662
- }
1663
- function Rt(o) {
1664
- const x = o.target.checked;
1665
- _e(x), ee.current = x;
1666
- for (const Q of Object.values(P.current)) Q.ctl.setTravelVisible(x);
1667
- }
1668
- function vn(o) {
1669
- const x = o.target.checked;
1670
- a((Q) => ({ ...Q, enable_support: x }));
1671
- }
1672
- const wn = !!Je(n, "enable_support"), kn = (((Pt = Bt.support_style) == null ? void 0 : Pt.enum_values) ?? []).map((o, x) => {
1673
- var Q, he;
1674
- return { value: o, label: ((he = (Q = Bt.support_style) == null ? void 0 : Q.enum_labels) == null ? void 0 : he[x]) ?? o };
1675
- }), En = String(Je(n, "support_style") ?? "default"), [ct, Mn] = $(!1), dt = Number(Je(n, "support_threshold_angle")) || 30;
1676
- Ve(() => {
1677
- var o;
1678
- (o = r.current) == null || o.setOverhang(ct && ne === "prepare" ? dt : null);
1679
- }, [ct, dt, ne, de.length]);
1680
- function pt() {
1681
- Y(d.current.map((o) => ({ id: o.id, name: o.name, extruder: o.extruder, visible: o.visible !== !1 })));
1682
- }
1683
- function jn(o, x) {
1684
- Oe((Q) => {
1685
- var Be;
1686
- const he = [...Q];
1687
- return he[o] = x, Ze.current = he, (Be = r.current) == null || Be.recolorObjects(), he;
2484
+ return Q.current = setTimeout(n, 800), () => clearTimeout(Q.current);
2485
+ }, [t, xe, d.length]), Te(() => {
2486
+ if (h == null) return;
2487
+ const n = ve.current;
2488
+ try {
2489
+ const g = ba(String(h), { filamentDiameter: Number(Oe.filament_diameter) || 1.75 });
2490
+ if (!g.layers.length) {
2491
+ me("No printable moves found in the G-code");
2492
+ return;
2493
+ }
2494
+ const te = Ft(Me.current), ie = Oe.bed_width, Ne = Oe.bed_depth;
2495
+ P.current[n] = {
2496
+ offX: n % te * rt(ie) - ie / 2,
2497
+ offZ: Math.floor(n / te) * rt(Ne) + Ne / 2
2498
+ }, C.current = Oe.line_width || 0.42, $.current[n] = { stats: g.stats, layers: g.layers, gcode: String(h) }, xn(), me(""), pt(""), vr(n);
2499
+ } catch (g) {
2500
+ me("G-code parse failed: " + ((g == null ? void 0 : g.message) || g));
2501
+ }
2502
+ }, [h]);
2503
+ function Pr(n, g) {
2504
+ if (!(n > 0) || !(g > 0)) return;
2505
+ const te = He(t, "printable_area"), ie = Array.isArray(te) && te.length >= 3, Ne = ie ? Math.min(...te.map((De) => De[0])) : 0, Be = ie ? Math.min(...te.map((De) => De[1])) : 0;
2506
+ r((De) => ({ ...De, printable_area: [[Ne, Be], [Ne + n, Be], [Ne + n, Be + g], [Ne, Be + g]] }));
2507
+ }
2508
+ function zr(n, g) {
2509
+ var te;
2510
+ (te = R.current) == null || te.setObjectExtruder(n, g), A(J.current.map((ie) => ({ id: ie.id, name: ie.name, extruder: ie.extruder, visible: ie.visible !== !1 })));
2511
+ }
2512
+ function it(n, g) {
2513
+ const te = Math.max(0, Re - 1);
2514
+ if (n = Math.max(0, Math.min(te, n)), g = Math.max(0, Math.min(te, g)), n > g) {
2515
+ const ie = n;
2516
+ n = g, g = ie;
2517
+ }
2518
+ M.current = n, y.current = g, U(n), re(g), mr();
2519
+ }
2520
+ function Rr(n) {
2521
+ const g = parseInt(n.target.value, 10);
2522
+ X ? it(g, g) : it(g, y.current);
2523
+ }
2524
+ function Tr(n) {
2525
+ const g = parseInt(n.target.value, 10);
2526
+ it(X ? g : M.current, g);
2527
+ }
2528
+ function wn() {
2529
+ const n = !X;
2530
+ ke(n), n && it(y.current, y.current);
2531
+ }
2532
+ function Or(n) {
2533
+ const g = n.target.value;
2534
+ xt(g), E.current = g, Ot();
2535
+ }
2536
+ Te(() => {
2537
+ var te, ie;
2538
+ if (!fe) return;
2539
+ const n = (ie = (te = $.current[ve.current]) == null ? void 0 : te.stats) == null ? void 0 : ie.filament_mm_by_tool;
2540
+ (Array.isArray(n) ? n.filter((Ne) => Ne > 0).length : 0) > 1 && E.current === "feature" && (xt("filament"), E.current = "filament", Ot());
2541
+ }, [fe]);
2542
+ function En(n) {
2543
+ const g = n.target.checked;
2544
+ Ye(g), S.current = g;
2545
+ for (const te of Object.values(ce.current)) te.ctl.setTravelVisible(g);
2546
+ }
2547
+ function Lr(n) {
2548
+ const g = n.target.checked;
2549
+ r((te) => ({ ...te, enable_support: g }));
2550
+ }
2551
+ const Sr = !!He(t, "enable_support"), Fr = (((zn = Un.support_style) == null ? void 0 : zn.enum_values) ?? []).map((n, g) => {
2552
+ var te, ie;
2553
+ return { value: n, label: ((ie = (te = Un.support_style) == null ? void 0 : te.enum_labels) == null ? void 0 : ie[g]) ?? n };
2554
+ }), Br = String(He(t, "support_style") ?? "default"), Dr = Number(He(t, "support_filament")) || 0, $r = Number(He(t, "support_interface_filament")) || 0, [Xt, Ir] = ae(!1), Yt = Number(He(t, "support_threshold_angle")) || 30;
2555
+ Te(() => {
2556
+ var n;
2557
+ (n = R.current) == null || n.setOverhang(Xt && be === "prepare" ? Yt : null);
2558
+ }, [Xt, Yt, be, d.length]);
2559
+ function Kt() {
2560
+ A(J.current.map((n) => ({ id: n.id, name: n.name, extruder: n.extruder, visible: n.visible !== !1 })));
2561
+ }
2562
+ function Zr(n, g) {
2563
+ _e((te) => {
2564
+ var Ne;
2565
+ const ie = [...te];
2566
+ return ie[n] = g, Le.current = ie, (Ne = R.current) == null || Ne.recolorObjects(), Ot(), ie;
1688
2567
  });
1689
2568
  }
1690
- function Cn() {
1691
- Oe((o) => {
1692
- if (o.length >= 4) return o;
1693
- const x = ["#e0473b", "#3bb0e0", "#7ad14a"], Q = [...o, x[o.length - 1] || "#888888"];
1694
- return Ze.current = Q, Q;
2569
+ function Vr() {
2570
+ _e((n) => {
2571
+ if (n.length >= $t) return n;
2572
+ const g = [...n, Qn[n.length] || "#888888"];
2573
+ return Le.current = g, g;
1695
2574
  });
1696
2575
  }
1697
- function Sn() {
1698
- Oe((o) => {
1699
- var Q;
1700
- if (o.length <= 1) return o;
1701
- const x = o.slice(0, -1);
1702
- return Ze.current = x, d.current.forEach((he) => {
1703
- var Be;
1704
- (he.extruder || 1) > x.length && ((Be = r.current) == null || Be.setObjectExtruder(he.id, x.length));
1705
- }), (Q = r.current) == null || Q.recolorObjects(), pt(), x;
2576
+ function Gr(n) {
2577
+ _e((g) => {
2578
+ var Ne;
2579
+ if (g.length <= 1) return g;
2580
+ const te = Number.isInteger(n) ? Math.min(Math.max(n, 0), g.length - 1) : g.length - 1, ie = g.filter((Be, De) => De !== te);
2581
+ return Le.current = ie, J.current.forEach((Be) => {
2582
+ var Ke, wt;
2583
+ const De = Be.extruder || 1;
2584
+ De === te + 1 ? (Ke = R.current) == null || Ke.setObjectExtruder(Be.id, 1) : De > te + 1 && ((wt = R.current) == null || wt.setObjectExtruder(Be.id, De - 1));
2585
+ }), (Ne = R.current) == null || Ne.recolorObjects(), Kt(), ie;
1706
2586
  });
1707
2587
  }
1708
- function Rn(o) {
1709
- var Q;
1710
- const x = d.current.find((he) => he.id === o);
1711
- (Q = r.current) == null || Q.setObjectVisible(o, (x == null ? void 0 : x.visible) === !1), pt();
2588
+ function Ur(n) {
2589
+ var te;
2590
+ const g = J.current.find((ie) => ie.id === n);
2591
+ (te = R.current) == null || te.setObjectVisible(n, (g == null ? void 0 : g.visible) === !1), Kt();
1712
2592
  }
1713
- function Nt() {
1714
- Ke(xe === "off" ? "enforcer" : "off");
2593
+ const kn = de === "enforcer" || de === "blocker";
2594
+ function _n() {
2595
+ nt(kn ? "off" : "enforcer");
1715
2596
  }
1716
- const {
1717
- duplicateSelected: ut,
1718
- copySelected: At,
1719
- pasteClipboard: _t,
1720
- deleteSelected: mt,
1721
- deleteAllObjects: Nn,
1722
- splitSelected: ft,
1723
- setGizmo: An
1724
- } = _r({
1725
- apiRef: r,
1726
- objectsRef: d,
1727
- clipboardRef: B,
1728
- paintModeRef: _,
1729
- setPaintMode: Ke,
1730
- removeObject: Ct,
1731
- refreshObjects: pt,
1732
- setError: V,
1733
- setSliceNotice: st
1734
- }), _n = wr({
2597
+ function Nn(n) {
2598
+ const g = Number.isInteger(n) ? n : null;
2599
+ f.current = g, st(g), V.current !== "material" && nt("material");
2600
+ }
2601
+ const Mn = we.map((n, g) => {
2602
+ const te = Ut[g + 1];
2603
+ return Number.isFinite(te) ? te : g === 0 ? Gt.enf : g === 1 ? Gt.blk : 0;
2604
+ }), {
2605
+ duplicateSelected: Jt,
2606
+ copySelected: An,
2607
+ pasteClipboard: Cn,
2608
+ deleteSelected: Qt,
2609
+ deleteAllObjects: Wr,
2610
+ splitSelected: en,
2611
+ setGizmo: Hr
2612
+ } = oo({
2613
+ apiRef: R,
2614
+ objectsRef: J,
2615
+ clipboardRef: w,
2616
+ paintModeRef: V,
2617
+ setPaintMode: nt,
2618
+ removeObject: vn,
2619
+ refreshObjects: Kt,
2620
+ setError: me,
2621
+ setSliceNotice: pt
2622
+ }), qr = qa({
1735
2623
  add: () => {
1736
- var o;
1737
- return (o = Ae.current) == null ? void 0 : o.click();
2624
+ var n;
2625
+ return (n = Ue.current) == null ? void 0 : n.click();
1738
2626
  },
1739
- remove: mt,
1740
- removeAll: Nn,
1741
- duplicate: ut,
1742
- split: ft,
2627
+ remove: Qt,
2628
+ removeAll: Wr,
2629
+ duplicate: Jt,
2630
+ split: en,
1743
2631
  placeOnBed: () => {
1744
- var o;
1745
- return (o = r.current) == null ? void 0 : o.placeOnBed();
2632
+ var n;
2633
+ return (n = R.current) == null ? void 0 : n.placeOnBed();
1746
2634
  },
1747
- objectCount: () => de.length
2635
+ objectCount: () => d.length
1748
2636
  });
1749
- M.current = kr({
1750
- slicing: je,
1751
- isPreview: () => A.current === "preview",
1752
- slice: it,
1753
- copy: At,
1754
- paste: _t,
1755
- remove: mt,
1756
- duplicate: ut,
1757
- stepLayer: (o) => {
1758
- const x = I.current + o;
1759
- He(F ? x : te.current, x);
2637
+ pe.current = Xa({
2638
+ slicing: se,
2639
+ isPreview: () => v.current === "preview",
2640
+ slice: qt,
2641
+ copy: An,
2642
+ paste: Cn,
2643
+ remove: Qt,
2644
+ duplicate: Jt,
2645
+ stepLayer: (n) => {
2646
+ const g = y.current + n;
2647
+ it(X ? g : M.current, g);
1760
2648
  },
1761
- toggleSingleLayer: St,
1762
- toggleTravel: () => Rt({ target: { checked: !ee.current } }),
2649
+ toggleSingleLayer: wn,
2650
+ toggleTravel: () => En({ target: { checked: !S.current } }),
1763
2651
  zoomAll: () => {
1764
- var o;
1765
- return (o = r.current) == null ? void 0 : o.frame();
2652
+ var n;
2653
+ return (n = R.current) == null ? void 0 : n.frame();
1766
2654
  },
1767
2655
  zoomBed: () => {
1768
- var o;
1769
- return (o = r.current) == null ? void 0 : o.frameBed();
2656
+ var n;
2657
+ return (n = R.current) == null ? void 0 : n.frameBed();
1770
2658
  },
1771
- leavePreview: () => Me("prepare"),
1772
- setGizmo: An,
2659
+ leavePreview: () => Se("prepare"),
2660
+ setGizmo: Hr,
1773
2661
  cancelTool: () => {
1774
- var o;
1775
- _.current !== "off" && Ke("off"), (o = r.current) == null || o.detachTransform();
2662
+ var n;
2663
+ V.current !== "off" && nt("off"), (n = R.current) == null || n.detachTransform();
1776
2664
  },
1777
- rotateSelected: (o) => {
1778
- var x;
1779
- return (x = r.current) == null ? void 0 : x.rotateSelectedY(o);
2665
+ rotateSelected: (n) => {
2666
+ var g;
2667
+ return (g = R.current) == null ? void 0 : g.rotateSelectedY(n);
1780
2668
  },
1781
- nudgeSelected: (o, x) => {
1782
- var Q;
1783
- return (Q = r.current) == null ? void 0 : Q.nudgeSelected(o, x);
2669
+ nudgeSelected: (n, g) => {
2670
+ var te;
2671
+ return (te = R.current) == null ? void 0 : te.nudgeSelected(n, g);
1784
2672
  },
1785
- toggleHelp: () => ze((o) => !o)
2673
+ toggleHelp: () => ft((n) => !n)
1786
2674
  });
1787
- const zn = $e.nozzle_diameter || Je(n, "nozzle_diameter") || "0.4";
1788
- (Ot = (Lt = ot.current).invalidate) == null || Ot.call(Lt);
1789
- const zt = Ee > 0 && /* @__PURE__ */ e.createElement(
1790
- Zr,
2675
+ const Xr = Oe.nozzle_diameter || He(t, "nozzle_diameter") || "0.4";
2676
+ (Tn = (Rn = Tt.current).invalidate) == null || Tn.call(Rn);
2677
+ const jn = Re > 0 && /* @__PURE__ */ e.createElement(
2678
+ go,
2679
+ {
2680
+ viewType: yt,
2681
+ onViewType: Or,
2682
+ layerCount: Re,
2683
+ layerLo: l,
2684
+ layerHi: Y,
2685
+ segCount: dt,
2686
+ singleLayer: X,
2687
+ onLayerLo: Rr,
2688
+ onLayerHi: Tr,
2689
+ onToggleSingle: wn,
2690
+ showTravel: Qe,
2691
+ onToggleTravel: En,
2692
+ colorRange: jt,
2693
+ roleLegend: ze,
2694
+ extruderColors: we
2695
+ }
2696
+ ), Ge = (On = $.current[ve.current]) == null ? void 0 : On.stats, Yr = fe && {
2697
+ ...fe,
2698
+ filamentPerTool: Array.isArray(Ge == null ? void 0 : Ge.filament_mm_by_tool) ? Ge.filament_mm_by_tool : void 0,
2699
+ filamentPurge: Number.isFinite(Ge == null ? void 0 : Ge.filament_mm_purge) ? Ge.filament_mm_purge : void 0,
2700
+ filamentPurgePerTool: Array.isArray(Ge == null ? void 0 : Ge.filament_mm_purge_by_tool) ? Ge.filament_mm_purge_by_tool : void 0
2701
+ }, Pn = (n) => {
2702
+ const g = He(t, n);
2703
+ return Array.isArray(g) ? g : g ? [g] : [];
2704
+ }, ut = $.current[ve.current], Kr = ut != null && ut.stats && Number.isFinite(ut.stats.filament_mm_purge) ? {
2705
+ purge: ut.stats.filament_mm_purge,
2706
+ changes: (((Ln = ut.gcode) == null ? void 0 : Ln.match(/^T\d+$/gm)) ?? []).length,
2707
+ x: (Sn = window.__vpParams) == null ? void 0 : Sn.prime_tower_x,
2708
+ y: (Fn = window.__vpParams) == null ? void 0 : Fn.prime_tower_y
2709
+ } : null, Jr = /* @__PURE__ */ e.createElement(
2710
+ vo,
1791
2711
  {
1792
- viewType: et,
1793
- onViewType: yn,
1794
- layerCount: Ee,
1795
- layerLo: rt,
1796
- layerHi: s,
1797
- segCount: q,
1798
- singleLayer: F,
1799
- onLayerLo: bn,
1800
- onLayerHi: xn,
1801
- onToggleSingle: St,
1802
- showTravel: Le,
1803
- onToggleTravel: Rt,
1804
- colorRange: nt,
1805
- roleLegend: U
1806
- }
1807
- ), Pn = /* @__PURE__ */ e.createElement(Fr, { stats: p, overBed: ue }), ht = bt.map((o) => o.toUpperCase()).join(" · ");
1808
- return /* @__PURE__ */ e.createElement(Yn, { css: Jn }, /* @__PURE__ */ e.createElement("div", { className: "app-shell" }, /* @__PURE__ */ e.createElement("input", { ref: Ae, type: "file", accept: bt.map((o) => "." + o).join(","), multiple: !0, onChange: pn, title: `${ht} (multiple files allowed)`, "data-testid": "stl-input", style: { display: "none" } }), /* @__PURE__ */ e.createElement(
1809
- zr,
2712
+ stats: Yr,
2713
+ overBed: Ae,
2714
+ extruderColors: we,
2715
+ filamentTypes: Pn("filament_type"),
2716
+ filamentIds: Pn("filament_settings_id")
2717
+ }
2718
+ ), tn = an.map((n) => n.toUpperCase()).join(" · ");
2719
+ return /* @__PURE__ */ e.createElement(_a, { css: Na }, /* @__PURE__ */ e.createElement("div", { className: "app-shell" }, /* @__PURE__ */ e.createElement("input", { ref: Ue, type: "file", accept: an.map((n) => "." + n).join(","), multiple: !0, onChange: Mr, title: `${tn} (multiple files allowed)`, "data-testid": "stl-input", style: { display: "none" } }), m("topBar") && /* @__PURE__ */ e.createElement(
2720
+ lo,
1810
2721
  {
1811
- showTabs: y,
1812
- canvasMode: ne,
1813
- onCanvasMode: Me,
1814
- previewEnabled: Ee > 0,
2722
+ showTabs: Ee,
2723
+ canvasMode: be,
2724
+ onCanvasMode: Se,
2725
+ previewEnabled: Re > 0,
1815
2726
  onOpen: () => {
1816
- var o;
1817
- return (o = Ae.current) == null ? void 0 : o.click();
2727
+ var n;
2728
+ return (n = Ue.current) == null ? void 0 : n.click();
1818
2729
  }
1819
2730
  }
1820
- ), /* @__PURE__ */ e.createElement("div", { className: "app-body" }, y && ne === "prepare" && /* @__PURE__ */ e.createElement(
1821
- Pr,
2731
+ ), /* @__PURE__ */ e.createElement("div", { className: "app-body" }, Ee && be === "prepare" && m("gizmoRail") && /* @__PURE__ */ e.createElement(
2732
+ so,
1822
2733
  {
1823
- gizmoMode: D,
1824
- paintMode: xe,
1825
- onGizmo: (o) => {
1826
- var x;
1827
- Ke("off"), (x = r.current) == null || x.setMode(o);
2734
+ gizmoMode: k,
2735
+ paintMode: de,
2736
+ onGizmo: (n) => {
2737
+ var g;
2738
+ nt("off"), (g = R.current) == null || g.setMode(n);
1828
2739
  },
1829
- onTogglePaint: Nt
2740
+ onTogglePaint: _n
1830
2741
  }
1831
2742
  ), /* @__PURE__ */ e.createElement("div", { className: "viewport-col" }, /* @__PURE__ */ e.createElement(
1832
2743
  "div",
1833
2744
  {
1834
- className: (y ? "vp-canvas" : "vp-canvas fail") + (Ne ? " drag-over" : ""),
1835
- ref: $t,
1836
- onDrop: un,
1837
- onDragOver: mn,
1838
- onDragLeave: fn,
2745
+ className: (Ee ? "vp-canvas" : "vp-canvas fail") + (Ve ? " drag-over" : ""),
2746
+ ref: sr,
2747
+ onDrop: Ar,
2748
+ onDragOver: Cr,
2749
+ onDragLeave: jr,
1839
2750
  "data-testid": "drop-zone"
1840
2751
  },
1841
- !y && /* @__PURE__ */ e.createElement("div", { className: "vp-fallback" }, "⚠ ", oe),
1842
- y && de.length === 0 && ne !== "preview" && /* @__PURE__ */ e.createElement("div", { className: "empty-hint", "data-testid": "empty-hint" }, /* @__PURE__ */ e.createElement("div", { className: "eh-icon" }, "📦"), /* @__PURE__ */ e.createElement("div", { className: "eh-title" }, "Drag in a file or pick one"), /* @__PURE__ */ e.createElement("div", { className: "eh-sub" }, ht), /* @__PURE__ */ e.createElement("button", { className: "eh-btn", onClick: () => {
1843
- var o;
1844
- return (o = Ae.current) == null ? void 0 : o.click();
1845
- }, "data-testid": "empty-pick", title: `Pick a ${ht} file (multiple allowed)` }, "Choose file")),
1846
- Ne && /* @__PURE__ */ e.createElement("div", { className: "drop-overlay", "data-testid": "drop-overlay" }, "Drop here (STL/OBJ/3MF/AMF/PLY)"),
1847
- Ie && /* @__PURE__ */ e.createElement(
1848
- Or,
2752
+ !Ee && /* @__PURE__ */ e.createElement("div", { className: "vp-fallback" }, "⚠ ", q),
2753
+ Ee && d.length === 0 && be !== "preview" && m("emptyHint") && /* @__PURE__ */ e.createElement("div", { className: "empty-hint", "data-testid": "empty-hint" }, /* @__PURE__ */ e.createElement("div", { className: "eh-icon" }, "📦"), /* @__PURE__ */ e.createElement("div", { className: "eh-title" }, "Drag in a file or pick one"), /* @__PURE__ */ e.createElement("div", { className: "eh-sub" }, tn), /* @__PURE__ */ e.createElement("button", { className: "eh-btn", onClick: () => {
2754
+ var n;
2755
+ return (n = Ue.current) == null ? void 0 : n.click();
2756
+ }, "data-testid": "empty-pick", title: `Pick a ${tn} file (multiple allowed)` }, "Choose file")),
2757
+ Ve && /* @__PURE__ */ e.createElement("div", { className: "drop-overlay", "data-testid": "drop-overlay" }, "Drop here (STL/OBJ/3MF/AMF/PLY)"),
2758
+ bt && /* @__PURE__ */ e.createElement(
2759
+ co,
1849
2760
  {
1850
- menu: Ie,
1851
- onClose: () => We(null),
1852
- canPaste: !!B.current,
2761
+ menu: bt,
2762
+ onClose: () => gt(null),
2763
+ canPaste: !!w.current,
1853
2764
  actions: {
1854
- duplicate: ut,
1855
- copy: At,
1856
- split: ft,
2765
+ duplicate: Jt,
2766
+ copy: An,
2767
+ split: en,
1857
2768
  placeOnBed: () => {
1858
- var o;
1859
- return (o = r.current) == null ? void 0 : o.placeOnBed();
2769
+ var n;
2770
+ return (n = R.current) == null ? void 0 : n.placeOnBed();
1860
2771
  },
1861
- remove: mt,
2772
+ remove: Qt,
1862
2773
  openFile: () => {
1863
- var o;
1864
- return (o = Ae.current) == null ? void 0 : o.click();
2774
+ var n;
2775
+ return (n = Ue.current) == null ? void 0 : n.click();
1865
2776
  },
1866
- paste: _t,
2777
+ paste: Cn,
1867
2778
  zoomAll: () => {
1868
- var o;
1869
- return (o = r.current) == null ? void 0 : o.frame();
2779
+ var n;
2780
+ return (n = R.current) == null ? void 0 : n.frame();
1870
2781
  },
1871
2782
  zoomBed: () => {
1872
- var o;
1873
- return (o = r.current) == null ? void 0 : o.frameBed();
2783
+ var n;
2784
+ return (n = R.current) == null ? void 0 : n.frameBed();
1874
2785
  }
1875
2786
  }
1876
2787
  }
1877
2788
  ),
1878
- ke && /* @__PURE__ */ e.createElement(Tr, { onClose: () => ze(!1) })
1879
- ), y && ne === "prepare" && /* @__PURE__ */ e.createElement(Lr, { tools: _n }), y && ne === "prepare" && xe !== "off" && /* @__PURE__ */ e.createElement(
1880
- Br,
2789
+ mt && /* @__PURE__ */ e.createElement(po, { onClose: () => ft(!1) })
2790
+ ), Ee && be === "prepare" && m("objectToolbar") && /* @__PURE__ */ e.createElement(io, { tools: qr }), Ee && be === "prepare" && kn && m("paintPanel") && /* @__PURE__ */ e.createElement(
2791
+ ho,
1881
2792
  {
1882
- paintMode: xe,
1883
- onPaintMode: Ke,
1884
- onClear: rn,
1885
- brushRadius: Ue,
1886
- paintCounts: Ut,
1887
- onBrushRadius: (o) => {
1888
- yt(o), G.current = o;
2793
+ paintMode: de,
2794
+ onPaintMode: nt,
2795
+ onClear: yn,
2796
+ brushRadius: tt,
2797
+ paintCounts: Gt,
2798
+ paintTool: Pt,
2799
+ onPaintTool: sn,
2800
+ brushCursor: zt,
2801
+ onBrushCursor: cn,
2802
+ fillAngle: Rt,
2803
+ onFillAngle: dn,
2804
+ onBrushRadius: (n) => {
2805
+ Vt(n), N.current = n;
1889
2806
  }
1890
2807
  }
1891
- ), y && ne === "preview" && p && /* @__PURE__ */ e.createElement("div", { className: "stats-card", "data-testid": "slice-stats" }, Pn), y && /* @__PURE__ */ e.createElement(
1892
- Dr,
2808
+ ), Ee && be === "prepare" && de === "material" && m("paintPanel") && /* @__PURE__ */ e.createElement(
2809
+ fo,
1893
2810
  {
1894
- plateCount: ie,
1895
- selectedPlate: se,
1896
- maxPlates: Vt,
1897
- onSelect: dn,
1898
- onAdd: sn,
1899
- onDelete: cn
1900
- }
1901
- ), y && /* @__PURE__ */ e.createElement("div", { className: "vp-status", "data-testid": "vp-status" }, oe)), y && /* @__PURE__ */ e.createElement("aside", { className: "sidebar" }, /* @__PURE__ */ e.createElement("div", { className: "sidebar-scroll" }, /* @__PURE__ */ e.createElement(
1902
- Gr,
2811
+ colors: we,
2812
+ activeExtruder: Zt,
2813
+ onSelectExtruder: Nn,
2814
+ onClear: yn,
2815
+ onClose: () => nt("off"),
2816
+ brushRadius: tt,
2817
+ paintCounts: Mn,
2818
+ paintTool: Pt,
2819
+ onPaintTool: sn,
2820
+ brushCursor: zt,
2821
+ onBrushCursor: cn,
2822
+ fillAngle: Rt,
2823
+ onFillAngle: dn,
2824
+ onBrushRadius: (n) => {
2825
+ Vt(n), N.current = n;
2826
+ }
2827
+ }
2828
+ ), Ee && be === "preview" && fe && m("statsCard") && /* @__PURE__ */ e.createElement("div", { className: "stats-card", "data-testid": "slice-stats" }, Jr), Ee && m("plateBar") && /* @__PURE__ */ e.createElement(
2829
+ bo,
1903
2830
  {
1904
- bedWidth: $e.bed_width,
1905
- bedDepth: $e.bed_depth,
1906
- nozzleDia: zn,
1907
- onBedSize: hn,
1908
- settings: n,
1909
- setSettings: a,
1910
- motionPanel: l
1911
- }
1912
- ), /* @__PURE__ */ e.createElement(Ur, { colors: De, onColor: jn, onAdd: Cn, onRemove: Sn }), de.length > 0 && /* @__PURE__ */ e.createElement(
1913
- $r,
2831
+ plateCount: Je,
2832
+ selectedPlate: $e,
2833
+ maxPlates: tr,
2834
+ onSelect: Nr,
2835
+ onAdd: kr,
2836
+ onDelete: _r
2837
+ }
2838
+ ), Ee && m("status") && /* @__PURE__ */ e.createElement("div", { className: "vp-status", "data-testid": "vp-status" }, q)), Ee && m("sidebar") && /* @__PURE__ */ e.createElement("aside", { className: "sidebar" }, /* @__PURE__ */ e.createElement("div", { className: "sidebar-scroll" }, m("printerCard") && /* @__PURE__ */ e.createElement(
2839
+ wo,
1914
2840
  {
1915
- objects: de,
1916
- extruderColors: De,
1917
- onToggleVisible: Rn,
1918
- onExtruder: gn,
1919
- onSplit: (o) => {
1920
- var x;
1921
- (x = r.current) == null || x.selectObject(o), ft();
2841
+ bedWidth: Oe.bed_width,
2842
+ bedDepth: Oe.bed_depth,
2843
+ nozzleDia: Xr,
2844
+ onBedSize: Pr,
2845
+ settings: t,
2846
+ setSettings: r,
2847
+ motionPanel: i
2848
+ }
2849
+ ), m("filamentCard") && /* @__PURE__ */ e.createElement(
2850
+ Eo,
2851
+ {
2852
+ colors: we,
2853
+ onColor: Zr,
2854
+ onAdd: Vr,
2855
+ onRemove: Gr,
2856
+ settings: t,
2857
+ setSettings: r,
2858
+ filamentPanel: o,
2859
+ paintMode: de,
2860
+ onPaintExtruder: Nn,
2861
+ paintCounts: Mn
2862
+ }
2863
+ ), d.length > 0 && m("objectList") && /* @__PURE__ */ e.createElement(
2864
+ ko,
2865
+ {
2866
+ objects: d,
2867
+ extruderColors: we,
2868
+ onToggleVisible: Ur,
2869
+ onExtruder: zr,
2870
+ onSplit: (n) => {
2871
+ var g;
2872
+ (g = R.current) == null || g.selectObject(n), en();
1922
2873
  },
1923
- onRemove: Ct,
1924
- supportOn: wn,
1925
- onToggleSupport: vn,
1926
- overhangOn: ct,
1927
- onToggleOverhang: (o) => Mn(o.target.checked),
1928
- overhangAngle: dt,
1929
- paintMode: xe,
1930
- onTogglePaint: Nt,
1931
- supportStyle: En,
1932
- supportStyles: kn,
1933
- onSupportStyle: (o) => a((x) => ({ ...x, support_style: o })),
1934
- wipeTowerReal: Te,
1935
- onToggleWipeTower: (o) => X(o.target.checked)
1936
- }
1937
- ), C && /* @__PURE__ */ e.createElement("div", { className: "slice-warn side-warn" }, "⚠ ", C), wt && /* @__PURE__ */ e.createElement("div", { className: "slice-warn side-warn", "data-testid": "slice-notice" }, "ℹ ", wt), S && /* @__PURE__ */ e.createElement("div", { className: "slice-err side-warn", "data-testid": "slice-err" }, S), kt && /* @__PURE__ */ e.createElement("button", { className: "slice-btn", "data-testid": "downgrade-retry", onClick: ln, title: "Simplify the infill and lower its density to reduce memory pressure, then retry" }, "Simplified retry (simple infill, economy mode)"), ne === "preview" && zt && /* @__PURE__ */ e.createElement("section", { className: "side-card" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🎚 Preview"), zt), /* @__PURE__ */ e.createElement("section", { className: "side-card process-card", "data-testid": "process-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "⚙ Process"), c)), /* @__PURE__ */ e.createElement(
1938
- Hr,
2874
+ onRemove: vn,
2875
+ supportOn: Sr,
2876
+ onToggleSupport: Lr,
2877
+ overhangOn: Xt,
2878
+ onToggleOverhang: (n) => Ir(n.target.checked),
2879
+ overhangAngle: Yt,
2880
+ paintMode: de,
2881
+ onTogglePaint: _n,
2882
+ supportStyle: Br,
2883
+ supportStyles: Fr,
2884
+ onSupportStyle: (n) => r((g) => ({ ...g, support_style: n })),
2885
+ supportFilament: Dr,
2886
+ onSupportFilament: (n) => r((g) => ({ ...g, support_filament: n })),
2887
+ supportInterfaceFilament: $r,
2888
+ onSupportInterfaceFilament: (n) => r((g) => ({ ...g, support_interface_filament: n }))
2889
+ }
2890
+ ), we.length > 1 && m("towerCard") && /* @__PURE__ */ e.createElement(
2891
+ No,
2892
+ {
2893
+ settings: t,
2894
+ setSettings: r,
2895
+ extruderColors: we,
2896
+ wipeTowerReal: et,
2897
+ onToggleWipeTower: (n) => vt(n.target.checked),
2898
+ towerStats: Kr
2899
+ }
2900
+ ), u && /* @__PURE__ */ e.createElement("div", { className: "slice-warn side-warn" }, "⚠ ", u), Wt && /* @__PURE__ */ e.createElement("div", { className: "slice-warn side-warn", "data-testid": "slice-notice" }, "ℹ ", Wt), he && /* @__PURE__ */ e.createElement("div", { className: "slice-err side-warn", "data-testid": "slice-err" }, he), fn && /* @__PURE__ */ e.createElement("button", { className: "slice-btn", "data-testid": "downgrade-retry", onClick: Er, title: "Simplify the infill and lower its density to reduce memory pressure, then retry" }, "Simplified retry (simple infill, economy mode)"), be === "preview" && jn && m("previewControls") && /* @__PURE__ */ e.createElement("section", { className: "side-card" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "🎚 Preview"), jn), m("processCard") && /* @__PURE__ */ e.createElement("section", { className: "side-card process-card", "data-testid": "process-section" }, /* @__PURE__ */ e.createElement("div", { className: "sc-head" }, "⚙ Process"), c)), m("sliceBar") && /* @__PURE__ */ e.createElement(
2901
+ Mo,
1939
2902
  {
1940
- autoSlice: ye,
1941
- onAutoSlice: Re,
1942
- slicing: je,
1943
- progress: Se,
1944
- plateCount: ie,
1945
- selectedPlate: se,
1946
- sliceMenuOpen: J,
1947
- onSliceMenu: () => ce((o) => !o),
1948
- slicedPlateCount: Fe,
1949
- canSlice: de.length > 0,
1950
- onSlice: it,
1951
- onCancel: jt,
1952
- onExportAll: an,
1953
- gcodeUrl: fe
2903
+ autoSlice: xe,
2904
+ onAutoSlice: je,
2905
+ slicing: se,
2906
+ progress: Z,
2907
+ plateCount: Je,
2908
+ selectedPlate: $e,
2909
+ sliceMenuOpen: At,
2910
+ onSliceMenu: () => Ct((n) => !n),
2911
+ slicedPlateCount: at,
2912
+ canSlice: d.length > 0,
2913
+ onSlice: qt,
2914
+ onCancel: gn,
2915
+ onExportAll: wr,
2916
+ gcodeUrl: Ie
1954
2917
  }
1955
2918
  )))));
1956
2919
  }
1957
2920
  export {
1958
- no as default
2921
+ Fo as default
1959
2922
  };