three-slicer 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/dist/SettingsPanel.js +112 -112
- package/components/styles.css +9 -9
- package/data/invalidation-map.json +1 -1
- package/engine/examples/headless.mjs +2 -2
- package/engine/index.js +2 -2
- package/engine/src/data.js +7 -7
- package/engine/src/settings.js +34 -34
- package/engine/src/slicer.worker.js +23 -21
- package/engine/src/toggle_eval.js +11 -11
- package/package.json +1 -1
- package/types/components.d.ts +5 -5
- package/types/data/config-schema.json.d.ts +6 -6
- package/types/data/invalidation-map.json.d.ts +5 -5
- package/types/data/toggle-rules.json.d.ts +5 -5
- package/types/data/ui-tree.json.d.ts +3 -3
- package/types/data.d.ts +6 -6
- package/types/gen_settings_types.mjs +10 -10
- package/types/settings-keys.d.ts +4 -4
- package/types/settings.d.ts +6 -6
- package/types/toggle.d.ts +6 -6
- package/types/viewer-loaders.d.ts +8 -8
- package/types/viewer-toolpath.d.ts +17 -17
- package/types/viewer.d.ts +3 -3
- package/types/wasm.d.ts +3 -3
- package/types/worker.d.ts +2 -2
- package/viewer/dist/Viewport.js +1760 -966
- package/viewer/dist/make_worker.js +6 -6
- package/viewer/dist/model_loaders.js +35 -35
- package/viewer/dist/toolpath_gpu.js +146 -147
- package/viewer/styles.css +25 -25
|
@@ -1,179 +1,179 @@
|
|
|
1
|
-
import e, { useRef as
|
|
2
|
-
import { createPortal as
|
|
3
|
-
import { uiTree as
|
|
4
|
-
import { settingRaw as
|
|
1
|
+
import e, { useRef as D, useState as h, useLayoutEffect as O, useEffect as P, useMemo as A } from "react";
|
|
2
|
+
import { createPortal as F } from "react-dom";
|
|
3
|
+
import { uiTree as S, schema as C } from "three-slicer/data";
|
|
4
|
+
import { settingRaw as J } from "three-slicer/settings";
|
|
5
5
|
import { disabledKeys as I, makeCfg as B } from "three-slicer/toggle";
|
|
6
|
-
function
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
return
|
|
6
|
+
function L({ css: t, className: s, children: r }) {
|
|
7
|
+
const i = D(null), [c] = h(() => {
|
|
8
|
+
const l = document.createElement("div");
|
|
9
|
+
return l.style.display = "contents", l;
|
|
10
10
|
});
|
|
11
|
-
return
|
|
12
|
-
const
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
}, [t,
|
|
11
|
+
return O(() => {
|
|
12
|
+
const l = i.current, o = l.shadowRoot || l.attachShadow({ mode: "open" });
|
|
13
|
+
let p = o.querySelector("style[data-ts-css]");
|
|
14
|
+
p || (p = document.createElement("style"), p.setAttribute("data-ts-css", ""), o.append(p)), p.textContent = t, o.contains(c) || o.append(c);
|
|
15
|
+
}, [t, c]), /* @__PURE__ */ e.createElement("div", { ref: i, className: s }, F(r, c));
|
|
16
16
|
}
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
return Array.from({ length:
|
|
20
|
-
const
|
|
21
|
-
return [+(
|
|
17
|
+
const T = ':host{display:contents}*{box-sizing:border-box}.row{display:grid;grid-template-columns:260px 220px 70px 1fr;gap:8px;align-items:center;padding:3px 0;border-bottom:1px dashed #f0f0f0}.row .lbl{color:#222;text-decoration:none}.row .lbl:hover{color:#0a7d4f;text-decoration:underline}.row .key{font-size:11px;color:#999;overflow:hidden;text-overflow:ellipsis}.row input[type=text],.row input:not([type]),.row select,.row textarea{width:100%;max-width:260px;padding:2px 6px}.inputwrap{display:inline-flex;align-items:center;gap:4px}.unit{color:#888;font-size:12px;white-space:nowrap}.pts{font-size:11px;color:#666}.badge{font-size:10px;padding:1px 6px;border-radius:8px;color:#fff;text-align:center}.mode-simple{background:#2e9e5b}.mode-advanced{background:#3673c3}.mode-expert{background:#d9822b}.mode-develop{background:#c0392b}.mode-none,.mode-undefined{background:#aaa}.muted{color:#999;font-weight:400;font-size:12px}.row.custom{grid-template-columns:1fr}.row.disabled{opacity:.42}.row.disabled input,.row.disabled select,.row.disabled textarea{cursor:not-allowed;background:#f2f2f2}.lbl-cell{display:inline-flex;align-items:center;gap:5px;min-width:0}.lbl-cell .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dirty-dot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;background:#e8873a;box-shadow:0 0 0 2px #fff3e6}.widget-cell{display:inline-flex;align-items:center;gap:6px;min-width:0}.reset-btn{flex:0 0 auto;border:1px solid #d8b48a;background:#fbf1e4;color:#b5701f;border-radius:5px;cursor:pointer;font-size:13px;line-height:1;padding:1px 5px}.reset-btn:hover{background:#f3e2cb}.settings-panel{flex:0 0 400px;width:400px;border-left:1px solid #e2e2e2;display:flex;flex-direction:column;min-height:0;background:#fff;color:#222;font:14px/1.5 -apple-system,Segoe UI,sans-serif}.settings-panel .sp-top{padding:10px 12px 8px;border-bottom:1px solid #eee}.sp-builder{display:flex;align-items:center;gap:8px;font-size:12px;color:#666}.sp-builder select{flex:1;padding:4px 6px}.sp-pages{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.sp-pages button{border:1px solid #e0e0e0;background:#fafafa;border-radius:6px;padding:4px 8px;font-size:12px;cursor:pointer}.sp-pages button.on{background:#e6f5ee;color:#0a7d4f;border-color:#b7e2cd;font-weight:700}.sp-modes{display:flex;gap:4px;margin-top:8px}.sp-modes button{border:0;background:none;font-size:11px;color:#888;padding:2px 6px;border-radius:6px;cursor:pointer}.sp-modes button.on{background:#eef2f0;color:#0a7d4f;font-weight:700}.settings-panel .sp-body{flex:1 1 auto;overflow-y:auto;padding:8px 14px 24px}.settings-panel .sp-body h2{font-size:16px}.settings-panel .row{grid-template-columns:1fr auto;grid-template-areas:"lbl badge" "key widget";row-gap:2px;padding:6px 0}.settings-panel .row .lbl{grid-area:lbl;font-weight:600}.settings-panel .row .badge{grid-area:badge;justify-self:end}.settings-panel .row .key{grid-area:key}.settings-panel .row .inputwrap,.settings-panel .row>select,.settings-panel .row>input,.settings-panel .row>textarea,.settings-panel .row>.pts,.settings-panel .row>.muted{grid-area:widget;justify-self:end}.settings-panel .row input:not([type=checkbox]):not([type=color]),.settings-panel .row select,.settings-panel .row textarea{max-width:200px}.settings-panel .row.custom{grid-template-columns:1fr;grid-template-areas:"lbl"}.sp-search{width:100%;box-sizing:border-box;padding:6px 9px;border:1px solid #d6d6d6;border-radius:6px;font-size:13px;margin-bottom:8px}:host(.sp-embedded) .settings-panel{position:static;width:auto;flex:none;border:0;height:auto}:host(.sp-embedded) .sp-body{padding:4px 2px 8px;max-height:460px}.bse{display:flex;flex-direction:column;gap:6px;min-width:0;max-width:220px}.bse-modes{display:flex;gap:4px}.bse-modes button{border:1px solid #e0e0e0;background:#fafafa;border-radius:6px;padding:2px 8px;font-size:11px;cursor:pointer}.bse-modes button.on{background:#e6f5ee;color:#0a7d4f;border-color:#b7e2cd;font-weight:700}.bse-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 8px}.bse-grid label{display:flex;align-items:center;gap:4px;font-size:11px;color:#666;white-space:nowrap}.bse-grid input{width:56px;padding:2px 4px;font-size:12px}.bse-json{width:100%;font:11px ui-monospace,monospace;padding:4px 6px;border:1px solid #d6d6d6;border-radius:6px;box-sizing:border-box}.bse-json.err{border-color:#d0435a;background:#fdf3f4}', j = (t, s, r, i) => [[r, i], [t + r, i], [t + r, s + i], [r, s + i]], $ = (t) => {
|
|
18
|
+
const s = t / 2, r = 32;
|
|
19
|
+
return Array.from({ length: r }, (i, c) => {
|
|
20
|
+
const l = 2 * Math.PI * c / r;
|
|
21
|
+
return [+(s + s * Math.cos(l)).toFixed(2), +(s + s * Math.sin(l)).toFixed(2)];
|
|
22
22
|
});
|
|
23
|
-
},
|
|
24
|
-
const
|
|
25
|
-
return { x0: Math.min(...
|
|
23
|
+
}, R = (t) => {
|
|
24
|
+
const s = t.map((i) => i[0]), r = t.map((i) => i[1]);
|
|
25
|
+
return { x0: Math.min(...s), y0: Math.min(...r), w: Math.max(...s) - Math.min(...s), d: Math.max(...r) - Math.min(...r) };
|
|
26
26
|
};
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
const a =
|
|
31
|
-
|
|
27
|
+
function U({ value: t, onChange: s, disabled: r }) {
|
|
28
|
+
const i = Array.isArray(t) && t.length >= 3 ? t : j(200, 200, 0, 0), c = R(i), [l, o] = h(i.length === 4 ? "rect" : i.length >= 12 ? "circle" : "custom"), [p, x] = h({ w: c.w, d: c.d, ox: c.x0, oy: c.y0 }), [w, u] = h(Math.round(c.w)), [d, f] = h(JSON.stringify(i)), [E, N] = h(!1);
|
|
29
|
+
P(() => {
|
|
30
|
+
const a = R(i);
|
|
31
|
+
x({ w: a.w, d: a.d, ox: a.x0, oy: a.y0 }), u(Math.round(a.w)), f(JSON.stringify(i)), N(!1);
|
|
32
32
|
}, [JSON.stringify(t)]);
|
|
33
|
-
const
|
|
34
|
-
m(a);
|
|
35
|
-
const { w: p, d: b, ox: k, oy: _ } = a;
|
|
36
|
-
p > 0 && b > 0 && r(A(p, b, k, _));
|
|
37
|
-
}, w = (a) => {
|
|
38
|
-
u(a), a > 0 && r(T(a));
|
|
39
|
-
}, N = (a) => {
|
|
33
|
+
const y = (a) => {
|
|
40
34
|
x(a);
|
|
35
|
+
const { w: m, d: n, ox: b, oy: v } = a;
|
|
36
|
+
m > 0 && n > 0 && s(j(m, n, b, v));
|
|
37
|
+
}, k = (a) => {
|
|
38
|
+
u(a), a > 0 && s($(a));
|
|
39
|
+
}, M = (a) => {
|
|
40
|
+
f(a);
|
|
41
41
|
try {
|
|
42
|
-
const
|
|
43
|
-
if (!Array.isArray(
|
|
44
|
-
|
|
42
|
+
const m = JSON.parse(a);
|
|
43
|
+
if (!Array.isArray(m) || m.length < 3 || !m.every((n) => Array.isArray(n) && n.length === 2 && n.every(Number.isFinite))) throw 0;
|
|
44
|
+
N(!1), s(m);
|
|
45
45
|
} catch {
|
|
46
|
-
|
|
46
|
+
N(!0);
|
|
47
47
|
}
|
|
48
|
-
},
|
|
49
|
-
const
|
|
50
|
-
return Number.isFinite(
|
|
48
|
+
}, g = (a) => {
|
|
49
|
+
const m = parseFloat(a);
|
|
50
|
+
return Number.isFinite(m) ? m : 0;
|
|
51
51
|
};
|
|
52
|
-
return /* @__PURE__ */ e.createElement("div", { className: "bse", "data-testid": "bed-shape-editor" }, /* @__PURE__ */ e.createElement("div", { className: "bse-modes" }, [["rect", "
|
|
52
|
+
return /* @__PURE__ */ e.createElement("div", { className: "bse", "data-testid": "bed-shape-editor" }, /* @__PURE__ */ e.createElement("div", { className: "bse-modes" }, [["rect", "Rectangular"], ["circle", "Circular"], ["custom", "Custom"]].map(([a, m]) => /* @__PURE__ */ e.createElement("button", { key: a, type: "button", className: l === a ? "on" : "", disabled: r, onClick: () => o(a) }, m))), l === "rect" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Width ", /* @__PURE__ */ e.createElement(
|
|
53
53
|
"input",
|
|
54
54
|
{
|
|
55
55
|
type: "number",
|
|
56
56
|
min: "1",
|
|
57
|
-
value:
|
|
58
|
-
disabled:
|
|
57
|
+
value: p.w,
|
|
58
|
+
disabled: r,
|
|
59
59
|
"data-testid": "bed-w",
|
|
60
|
-
onChange: (a) =>
|
|
60
|
+
onChange: (a) => y({ ...p, w: g(a.target.value) })
|
|
61
61
|
}
|
|
62
|
-
), " mm"), /* @__PURE__ */ e.createElement("label", null, "
|
|
62
|
+
), " mm"), /* @__PURE__ */ e.createElement("label", null, "Depth ", /* @__PURE__ */ e.createElement(
|
|
63
63
|
"input",
|
|
64
64
|
{
|
|
65
65
|
type: "number",
|
|
66
66
|
min: "1",
|
|
67
|
-
value:
|
|
68
|
-
disabled:
|
|
67
|
+
value: p.d,
|
|
68
|
+
disabled: r,
|
|
69
69
|
"data-testid": "bed-d",
|
|
70
|
-
onChange: (a) =>
|
|
70
|
+
onChange: (a) => y({ ...p, d: g(a.target.value) })
|
|
71
71
|
}
|
|
72
|
-
), " mm"), /* @__PURE__ */ e.createElement("label", null, "
|
|
72
|
+
), " mm"), /* @__PURE__ */ e.createElement("label", null, "Origin X ", /* @__PURE__ */ e.createElement(
|
|
73
73
|
"input",
|
|
74
74
|
{
|
|
75
75
|
type: "number",
|
|
76
|
-
value:
|
|
77
|
-
disabled:
|
|
78
|
-
onChange: (a) =>
|
|
76
|
+
value: p.ox,
|
|
77
|
+
disabled: r,
|
|
78
|
+
onChange: (a) => y({ ...p, ox: g(a.target.value) })
|
|
79
79
|
}
|
|
80
|
-
)), /* @__PURE__ */ e.createElement("label", null, "
|
|
80
|
+
)), /* @__PURE__ */ e.createElement("label", null, "Origin Y ", /* @__PURE__ */ e.createElement(
|
|
81
81
|
"input",
|
|
82
82
|
{
|
|
83
83
|
type: "number",
|
|
84
|
-
value:
|
|
85
|
-
disabled:
|
|
86
|
-
onChange: (a) =>
|
|
84
|
+
value: p.oy,
|
|
85
|
+
disabled: r,
|
|
86
|
+
onChange: (a) => y({ ...p, oy: g(a.target.value) })
|
|
87
87
|
}
|
|
88
|
-
))),
|
|
88
|
+
))), l === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Diameter ", /* @__PURE__ */ e.createElement(
|
|
89
89
|
"input",
|
|
90
90
|
{
|
|
91
91
|
type: "number",
|
|
92
92
|
min: "1",
|
|
93
|
-
value:
|
|
94
|
-
disabled:
|
|
93
|
+
value: w,
|
|
94
|
+
disabled: r,
|
|
95
95
|
"data-testid": "bed-dia",
|
|
96
|
-
onChange: (a) =>
|
|
96
|
+
onChange: (a) => k(g(a.target.value))
|
|
97
97
|
}
|
|
98
|
-
), " mm")),
|
|
98
|
+
), " mm")), l === "custom" && /* @__PURE__ */ e.createElement(
|
|
99
99
|
"textarea",
|
|
100
100
|
{
|
|
101
|
-
className: "bse-json" + (
|
|
101
|
+
className: "bse-json" + (E ? " err" : ""),
|
|
102
102
|
rows: 3,
|
|
103
|
-
value:
|
|
104
|
-
disabled:
|
|
103
|
+
value: d,
|
|
104
|
+
disabled: r,
|
|
105
105
|
"data-testid": "bed-json",
|
|
106
|
-
onChange: (a) =>
|
|
106
|
+
onChange: (a) => M(a.target.value),
|
|
107
107
|
placeholder: "[[0,0],[200,0],[200,200],[0,200]]"
|
|
108
108
|
}
|
|
109
109
|
));
|
|
110
110
|
}
|
|
111
|
-
const
|
|
112
|
-
printable_area:
|
|
113
|
-
},
|
|
114
|
-
function H(t,
|
|
115
|
-
var
|
|
116
|
-
if (!t || !(
|
|
117
|
-
const
|
|
118
|
-
return String(t[
|
|
111
|
+
const W = {
|
|
112
|
+
printable_area: U
|
|
113
|
+
}, _ = Object.keys(S).filter((t) => S[t].length > 0), q = ["all", "simple", "advanced", "expert", "develop"], G = (t) => t.replace("::build", "").replace("Tab", "");
|
|
114
|
+
function H(t, s) {
|
|
115
|
+
var c;
|
|
116
|
+
if (!t || !(s in t)) return !1;
|
|
117
|
+
const r = (c = C[s]) == null ? void 0 : c.default, i = Array.isArray(r) ? r[0] : r;
|
|
118
|
+
return String(t[s]) !== String(i ?? "");
|
|
119
119
|
}
|
|
120
|
-
function Q(t,
|
|
121
|
-
var
|
|
122
|
-
if (
|
|
123
|
-
const
|
|
124
|
-
return
|
|
120
|
+
function Q(t, s) {
|
|
121
|
+
var i;
|
|
122
|
+
if (s === "all" || t.startsWith("<")) return !0;
|
|
123
|
+
const r = (i = C[t]) == null ? void 0 : i.mode;
|
|
124
|
+
return s === "simple" ? r === "simple" : s === "advanced" ? r === "simple" || r === "advanced" : !0;
|
|
125
125
|
}
|
|
126
|
-
function
|
|
126
|
+
function X(t) {
|
|
127
127
|
return t ? t.gui_type === "color" ? "color" : t.enum_values ? "select" : t.type === "coBool" || t.type === "coBools" ? "checkbox" : t.multiline || t.is_code ? "textarea" : t.type === "coPoints" || t.type === "coPoint" ? "points" : "input" : "unknown";
|
|
128
128
|
}
|
|
129
|
-
function
|
|
129
|
+
function Y({ mode: t }) {
|
|
130
130
|
return /* @__PURE__ */ e.createElement("span", { className: `badge mode-${t ?? "none"}` }, t ?? "—");
|
|
131
131
|
}
|
|
132
|
-
function
|
|
133
|
-
const o =
|
|
134
|
-
if (
|
|
135
|
-
const
|
|
136
|
-
switch (
|
|
132
|
+
function V({ def: t, optKey: s, settings: r, setSettings: i, disabled: c, customWidgets: l }) {
|
|
133
|
+
const o = J(r, s), p = (d) => i((f) => ({ ...f, [s]: d })), x = (l == null ? void 0 : l[s]) ?? W[s];
|
|
134
|
+
if (x) return /* @__PURE__ */ e.createElement(x, { def: t, value: o, onChange: p, disabled: c });
|
|
135
|
+
const w = X(t), u = Array.isArray(o) ? o[0] : o;
|
|
136
|
+
switch (w) {
|
|
137
137
|
case "select":
|
|
138
|
-
return /* @__PURE__ */ e.createElement("select", { value: u ?? "", onChange: (
|
|
139
|
-
var
|
|
140
|
-
return /* @__PURE__ */ e.createElement("option", { key:
|
|
138
|
+
return /* @__PURE__ */ e.createElement("select", { value: u ?? "", onChange: (d) => p(d.target.value), disabled: c }, t.enum_values.map((d, f) => {
|
|
139
|
+
var E;
|
|
140
|
+
return /* @__PURE__ */ e.createElement("option", { key: d, value: d }, ((E = t.enum_labels) == null ? void 0 : E[f]) ?? d);
|
|
141
141
|
}));
|
|
142
142
|
case "checkbox":
|
|
143
|
-
return /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: Array.isArray(o) ? !!o[0] : !!o, onChange: (
|
|
143
|
+
return /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: Array.isArray(o) ? !!o[0] : !!o, onChange: (d) => p(d.target.checked), disabled: c });
|
|
144
144
|
case "color":
|
|
145
|
-
return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof u == "string" && u.startsWith("#") ? u : "#00ae42", onChange: (
|
|
145
|
+
return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof u == "string" && u.startsWith("#") ? u : "#00ae42", onChange: (d) => p(d.target.value), disabled: c });
|
|
146
146
|
case "textarea":
|
|
147
147
|
return /* @__PURE__ */ e.createElement("textarea", { rows: 2, value: Array.isArray(o) ? o.join(`
|
|
148
|
-
`) : o ?? "", onChange: (
|
|
148
|
+
`) : o ?? "", onChange: (d) => p(d.target.value), disabled: c });
|
|
149
149
|
case "points":
|
|
150
150
|
return /* @__PURE__ */ e.createElement("code", { className: "pts" }, JSON.stringify(o ?? []));
|
|
151
151
|
case "unknown":
|
|
152
|
-
return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "
|
|
152
|
+
return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "No schema");
|
|
153
153
|
default:
|
|
154
|
-
return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value: u ?? "", onChange: (
|
|
154
|
+
return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value: u ?? "", onChange: (d) => p(d.target.value), disabled: c }), t.sidetext && /* @__PURE__ */ e.createElement("span", { className: "unit" }, t.sidetext));
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
-
function
|
|
158
|
-
if (t.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙
|
|
159
|
-
const o = C[t],
|
|
160
|
-
const
|
|
161
|
-
return delete
|
|
162
|
-
}),
|
|
163
|
-
return /* @__PURE__ */ e.createElement("div", { className: "row" + (
|
|
157
|
+
function Z({ optKey: t, settings: s, setSettings: r, disabled: i, onOptionOpen: c, customWidgets: l }) {
|
|
158
|
+
if (t.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙ Custom widget ", t));
|
|
159
|
+
const o = C[t], p = i ? i[t] : void 0, x = p != null, w = H(s, t), u = () => r((f) => {
|
|
160
|
+
const E = { ...f };
|
|
161
|
+
return delete E[t], E;
|
|
162
|
+
}), d = (o == null ? void 0 : o.label) || (o == null ? void 0 : o.full_label) || t;
|
|
163
|
+
return /* @__PURE__ */ e.createElement("div", { className: "row" + (x ? " disabled" : ""), title: x ? `Disabled when: ${p}` : (o == null ? void 0 : o.tooltip) ?? "", "data-testid": `row-${t}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, w && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "Changed from default", "data-testid": `dirty-${t}` }), c ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => c(t), title: "Details" }, d) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, d)), /* @__PURE__ */ e.createElement("code", { className: "key" }, t), /* @__PURE__ */ e.createElement(Y, { mode: o == null ? void 0 : o.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(V, { def: o, optKey: t, settings: s, setSettings: r, disabled: x, customWidgets: l }), w && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: u, title: "Reset to default", "data-testid": `reset-${t}` }, "↺")));
|
|
164
164
|
}
|
|
165
|
-
function
|
|
166
|
-
const [
|
|
167
|
-
var
|
|
168
|
-
return
|
|
169
|
-
}).slice(0, 60).map(([
|
|
170
|
-
return /* @__PURE__ */ e.createElement(
|
|
171
|
-
|
|
172
|
-
} },
|
|
173
|
-
const
|
|
174
|
-
return
|
|
175
|
-
})) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "
|
|
165
|
+
function re({ settings: t, setSettings: s, onOptionOpen: r, embedded: i = !1, customWidgets: c, only: l }) {
|
|
166
|
+
const [o, p] = h((l == null ? void 0 : l.builder) ?? _[0] ?? ""), [x, w] = h(0), [u, d] = h("all"), [f, E] = h(""), N = S[(l == null ? void 0 : l.builder) ?? o] ?? [], y = l != null && l.page ? N.filter((n) => n.page === l.page) : N, k = y[Math.min(x, y.length - 1)], M = A(() => I(B(t)), [t]), g = f.trim().toLowerCase(), a = A(() => g ? Object.entries(C).filter(([n, b]) => {
|
|
167
|
+
var v, z;
|
|
168
|
+
return n.includes(g) || ((v = b.label) == null ? void 0 : v.toLowerCase().includes(g)) || ((z = b.full_label) == null ? void 0 : z.toLowerCase().includes(g));
|
|
169
|
+
}).slice(0, 60).map(([n]) => n) : [], [g]), m = (n, b) => /* @__PURE__ */ e.createElement(Z, { key: n + b, optKey: n, settings: t, setSettings: s, disabled: M, onOptionOpen: r, customWidgets: c });
|
|
170
|
+
return /* @__PURE__ */ e.createElement(L, { css: T, className: i ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !l && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: "Search options (907)…", value: f, onChange: (n) => E(n.target.value), "data-testid": "opt-search" }), !g && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("label", { className: "sp-builder" }, /* @__PURE__ */ e.createElement("span", null, "Settings group"), /* @__PURE__ */ e.createElement("select", { value: o, onChange: (n) => {
|
|
171
|
+
p(n.target.value), w(0);
|
|
172
|
+
} }, _.map((n) => /* @__PURE__ */ e.createElement("option", { key: n, value: n }, G(n))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, y.map((n, b) => /* @__PURE__ */ e.createElement("button", { key: n.page + b, className: b === Math.min(x, y.length - 1) ? "on" : "", onClick: () => w(b) }, n.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, q.map((n) => /* @__PURE__ */ e.createElement("button", { key: n, className: u === n ? "on" : "", onClick: () => d(n) }, n))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, g ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "Search: “", f, "” — ", a.length, " hit", a.length === 1 ? "" : "s", a.length === 60 ? "+" : ""), a.map(m), !a.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No matches")) : k ? /* @__PURE__ */ e.createElement(e.Fragment, null, !l && /* @__PURE__ */ e.createElement("h2", null, k.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", k.line)), k.groups.map((n) => {
|
|
173
|
+
const b = n.options.filter((v) => Q(v, u) && !(l && v.startsWith("<")));
|
|
174
|
+
return b.length ? /* @__PURE__ */ e.createElement("section", { key: n.group + n.line }, /* @__PURE__ */ e.createElement("h3", null, n.group || "(untitled group)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", n.line)), b.map(m)) : null;
|
|
175
|
+
})) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No builder"))));
|
|
176
176
|
}
|
|
177
177
|
export {
|
|
178
|
-
|
|
178
|
+
re as default
|
|
179
179
|
};
|
package/components/styles.css
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/* @three-slicer/components — SettingsPanel
|
|
2
|
-
Shadow DOM
|
|
3
|
-
|
|
1
|
+
/* @three-slicer/components — SettingsPanel styles.
|
|
2
|
+
Shadow DOM isolation (shadow_host.jsx): inlined into the bundle via ?inline and injected into the
|
|
3
|
+
panel's shadow root. Fully isolated from host app CSS in both directions — class names cannot collide. */
|
|
4
4
|
:host { display: contents; }
|
|
5
5
|
* { box-sizing: border-box; }
|
|
6
6
|
|
|
7
|
-
/* ----
|
|
7
|
+
/* ---- Option row atoms ---- */
|
|
8
8
|
.row { display: grid; grid-template-columns: 260px 220px 70px 1fr; gap: 8px;
|
|
9
9
|
align-items: center; padding: 3px 0; border-bottom: 1px dashed #f0f0f0; }
|
|
10
10
|
.row .lbl { color: #222; text-decoration: none; }
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
.muted { color: #999; font-weight: 400; font-size: 12px; }
|
|
24
24
|
.row.custom { grid-template-columns: 1fr; }
|
|
25
25
|
|
|
26
|
-
/* 25
|
|
26
|
+
/* Stage 25 S5: toggle-rules disabling + dirty/reset */
|
|
27
27
|
.row.disabled { opacity: 0.42; }
|
|
28
28
|
.row.disabled input, .row.disabled select, .row.disabled textarea { cursor: not-allowed; background: #f2f2f2; }
|
|
29
29
|
.lbl-cell { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
border-radius: 5px; cursor: pointer; font-size: 13px; line-height: 1; padding: 1px 5px; }
|
|
35
35
|
.reset-btn:hover { background: #f3e2cb; }
|
|
36
36
|
|
|
37
|
-
/* ----
|
|
37
|
+
/* ---- Panel shell ---- */
|
|
38
38
|
.settings-panel { flex: 0 0 400px; width: 400px; border-left: 1px solid #e2e2e2;
|
|
39
39
|
display: flex; flex-direction: column; min-height: 0; background: #fff; color: #222;
|
|
40
40
|
font: 14px/1.5 -apple-system, "Segoe UI", sans-serif; }
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
.settings-panel .sp-body { flex: 1 1 auto; overflow-y: auto; padding: 8px 14px 24px; }
|
|
53
53
|
.settings-panel .sp-body h2 { font-size: 16px; }
|
|
54
54
|
|
|
55
|
-
/*
|
|
55
|
+
/* The sidebar is narrow, so option rows are re-stacked vertically */
|
|
56
56
|
.settings-panel .row { grid-template-columns: 1fr auto; grid-template-areas: "lbl badge" "key widget";
|
|
57
57
|
row-gap: 2px; padding: 6px 0; }
|
|
58
58
|
.settings-panel .row .lbl { grid-area: lbl; font-weight: 600; }
|
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
.settings-panel .row.custom { grid-template-columns: 1fr; grid-template-areas: "lbl"; }
|
|
67
67
|
.sp-search { width: 100%; box-sizing: border-box; padding: 6px 9px; border: 1px solid #d6d6d6; border-radius: 6px; font-size: 13px; margin-bottom: 8px; }
|
|
68
68
|
|
|
69
|
-
/* ----
|
|
69
|
+
/* ---- Embedded variant (sp-embedded on :host — full width inside cards such as the viewer process-card) ---- */
|
|
70
70
|
:host(.sp-embedded) .settings-panel { position: static; width: auto; flex: none; border: 0; height: auto; }
|
|
71
71
|
:host(.sp-embedded) .sp-body { padding: 4px 2px 8px; max-height: 460px; }
|
|
72
72
|
|
|
73
|
-
/* ---- BedShapeEditor (printable_area
|
|
73
|
+
/* ---- BedShapeEditor (printable_area custom widget) ---- */
|
|
74
74
|
.bse { display: flex; flex-direction: column; gap: 6px; min-width: 0; max-width: 220px; }
|
|
75
75
|
.bse-modes { display: flex; gap: 4px; }
|
|
76
76
|
.bse-modes button { border: 1px solid #e0e0e0; background: #fafafa; border-radius: 6px;
|
|
@@ -627,5 +627,5 @@
|
|
|
627
627
|
"line": 1533
|
|
628
628
|
}
|
|
629
629
|
],
|
|
630
|
-
"note": "
|
|
630
|
+
"note": "An opt_key absent from the conditions (the default branch) invalidates everything. steps are the step tokens referenced in the branch block."
|
|
631
631
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// three-slicer headless SDK example + independence proof: drive the engine from Node using ONLY the
|
|
2
2
|
// public API (no UI, no viewer). Generates a 20mm cube STL, slices it, prints stats + G-code length.
|
|
3
3
|
// run: node packages/engine/examples/headless.mjs
|
|
4
|
-
import { createSlicer } from 'three-slicer' //
|
|
4
|
+
import { createSlicer } from 'three-slicer' // resolved via the workspace symlink — same path an external consumer takes
|
|
5
5
|
|
|
6
6
|
// minimal binary-STL writer for a box (public API takes a binary-STL ArrayBuffer)
|
|
7
7
|
function boxTris(ox, oy, oz, sx, sy, sz) {
|
|
@@ -27,7 +27,7 @@ const slicer = await createSlicer()
|
|
|
27
27
|
const r = slicer.slice(cube, params)
|
|
28
28
|
console.log(`batch : layers=${r.stats.layers} segments=${r.stats.path_segments} filament=${r.stats.filament_mm.toFixed(1)}mm gcode=${r.gcode.length} chars`)
|
|
29
29
|
|
|
30
|
-
// (b) streaming slice via onLayer (30
|
|
30
|
+
// (b) streaming slice via onLayer (stage 30) — assemble G-code from per-layer chunks
|
|
31
31
|
let chunks = 0, gbytes = 0
|
|
32
32
|
const rs = slicer.slice(cube, params, { onLayer: ({ gcode }) => { chunks++; gbytes += gcode.length } })
|
|
33
33
|
console.log(`stream: layers=${chunks} streamed=${rs.stats.streamed} assembled-gcode=${gbytes} chars`)
|
package/engine/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import createModule from './src/slicer_core.js'
|
|
|
7
7
|
const u8 = (b) => (b instanceof Uint8Array ? b : new Uint8Array(b))
|
|
8
8
|
|
|
9
9
|
// Off-main-thread worker URL for the browser: `new Worker(engineWorkerURL(), { type: 'module' })`.
|
|
10
|
-
// The worker speaks the 30
|
|
10
|
+
// The worker speaks the stage-30 streaming protocol ({type:'layer'|'done'|'error'|'progress'}).
|
|
11
11
|
export const engineWorkerURL = () => new URL('./src/slicer.worker.js', import.meta.url)
|
|
12
12
|
|
|
13
13
|
// createSlicer(): load the WASM kernel and return a handle. Works in Node and the browser main thread.
|
|
@@ -15,7 +15,7 @@ export const engineWorkerURL = () => new URL('./src/slicer.worker.js', import.me
|
|
|
15
15
|
// - stl: ArrayBuffer|Uint8Array (binary STL)
|
|
16
16
|
// - params: kernel-params object OR JSON string (see web/GUIDE.md; use deriveKernelParams to build from UI settings)
|
|
17
17
|
// - onProgress(done,total): per-layer progress
|
|
18
|
-
// - onLayer({z,idx,gcode,paths,widths}): 30
|
|
18
|
+
// - onLayer({z,idx,gcode,paths,widths}): stage-30 streaming — emits + frees each layer; when set, the
|
|
19
19
|
// returned result carries stats only (assemble gcode/layers from the callbacks). When omitted, the
|
|
20
20
|
// result carries the whole { gcode, stats, layers }.
|
|
21
21
|
// paintPrepare/paint/paintClear/overlay: manual support enforcer/blocker painting (TriangleSelector).
|
package/engine/src/data.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
//
|
|
1
|
+
// The single import site for the 4 extracted JSON files.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// Vite/esbuild
|
|
5
|
-
//
|
|
6
|
-
// "
|
|
7
|
-
// "inconsistent import attributes"
|
|
8
|
-
//
|
|
3
|
+
// Why funnel them through one module: JSON requires `with { type: 'json' }` in Node 22+ ESM, but
|
|
4
|
+
// Vite/esbuild strip that attribute from bundle output (neither build.target nor importAttributesKey
|
|
5
|
+
// prevents it — measured). So if the same JSON is pointed at by both "the original with the attribute
|
|
6
|
+
// (engine/src/*.js)" and "the bundle without it (components/dist)", the consumer's bundler warns with
|
|
7
|
+
// "inconsistent import attributes". Only this file holds the JSON; everything else imports this module
|
|
8
|
+
// as external -> no JSON import survives in the bundle, so the mismatch cannot arise.
|
|
9
9
|
export { default as schema } from 'three-slicer/data/config-schema.json' with { type: 'json' }
|
|
10
10
|
export { default as uiTree } from 'three-slicer/data/ui-tree.json' with { type: 'json' }
|
|
11
11
|
export { default as toggleRules } from 'three-slicer/data/toggle-rules.json' with { type: 'json' }
|