three-slicer 0.1.0 → 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/README.md +342 -24
- package/components/README.md +1 -1
- package/components/dist/SettingsPanel.js +111 -112
- package/components/styles.css +9 -9
- package/data/invalidation-map.json +1 -1
- package/engine/examples/headless.mjs +2 -2
- package/engine/index.d.ts +3 -1
- package/engine/index.js +2 -2
- package/engine/src/data.js +12 -0
- package/engine/src/settings.js +35 -35
- package/engine/src/slicer.worker.js +25 -22
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -0
- package/engine/src/toggle_eval.js +12 -13
- package/package.json +54 -26
- package/types/components.d.ts +19 -0
- package/types/data/config-schema.json.d.ts +55 -0
- package/types/data/invalidation-map.json.d.ts +15 -0
- package/types/data/toggle-rules.json.d.ts +20 -0
- package/types/data/ui-tree.json.d.ts +19 -0
- package/types/data.d.ts +17 -0
- package/types/gen_settings_types.mjs +70 -0
- package/types/settings-keys.d.ts +1704 -0
- package/types/settings.d.ts +19 -0
- package/types/toggle.d.ts +22 -0
- package/types/viewer-loaders.d.ts +33 -0
- package/types/viewer-toolpath.d.ts +105 -0
- package/types/viewer.d.ts +15 -0
- package/types/wasm.d.ts +5 -0
- package/types/worker.d.ts +3 -0
- package/viewer/README.md +1 -1
- package/viewer/dist/Viewport.js +1774 -923
- package/viewer/dist/make_worker.js +6 -6
- package/viewer/dist/model_loaders.js +221 -109
- package/viewer/dist/toolpath_gpu.js +146 -147
- package/viewer/styles.css +27 -25
- package/data/config-schema-builddump.json +0 -12921
- package/viewer/src/Viewport.jsx +0 -1353
- package/viewer/src/assets/add.svg +0 -1
- package/viewer/src/assets/arrange.svg +0 -1
- package/viewer/src/assets/boolean.svg +0 -1
- package/viewer/src/assets/cut.svg +0 -1
- package/viewer/src/assets/delete.svg +0 -1
- package/viewer/src/assets/deleteall.svg +0 -1
- package/viewer/src/assets/duplicate.svg +0 -1
- package/viewer/src/assets/measure.svg +0 -1
- package/viewer/src/assets/mmu.svg +0 -1
- package/viewer/src/assets/move.svg +0 -1
- package/viewer/src/assets/negative.svg +0 -1
- package/viewer/src/assets/onbed.svg +0 -1
- package/viewer/src/assets/open.svg +0 -1
- package/viewer/src/assets/orient.svg +0 -1
- package/viewer/src/assets/paint.svg +0 -1
- package/viewer/src/assets/rotate.svg +0 -1
- package/viewer/src/assets/scale.svg +0 -1
- package/viewer/src/assets/seam.svg +0 -1
- package/viewer/src/assets/split.svg +0 -1
- package/viewer/src/assets/textmark.svg +0 -1
- package/viewer/src/assets/varlayer.svg +0 -1
- package/viewer/src/icons.js +0 -23
- package/viewer/src/make_worker.js +0 -9
- package/viewer/src/model_loaders.js +0 -178
- package/viewer/src/shadow_host.jsx +0 -30
- package/viewer/src/toolpath_gpu.js +0 -430
|
@@ -1,179 +1,178 @@
|
|
|
1
|
-
import e, { useRef as
|
|
2
|
-
import { createPortal as
|
|
3
|
-
import C from "three-slicer/data
|
|
4
|
-
import
|
|
5
|
-
import { settingRaw as P } from "three-slicer/settings";
|
|
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";
|
|
6
5
|
import { disabledKeys as I, makeCfg as B } from "three-slicer/toggle";
|
|
7
|
-
function
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
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;
|
|
11
10
|
});
|
|
12
|
-
return
|
|
13
|
-
const
|
|
14
|
-
let
|
|
15
|
-
|
|
16
|
-
}, [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));
|
|
17
16
|
}
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
return Array.from({ length:
|
|
21
|
-
const
|
|
22
|
-
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)];
|
|
23
22
|
});
|
|
24
|
-
},
|
|
25
|
-
const
|
|
26
|
-
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) };
|
|
27
26
|
};
|
|
28
|
-
function
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const a =
|
|
32
|
-
|
|
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);
|
|
33
32
|
}, [JSON.stringify(t)]);
|
|
34
|
-
const
|
|
35
|
-
m(a);
|
|
36
|
-
const { w: p, d: b, ox: k, oy: _ } = a;
|
|
37
|
-
p > 0 && b > 0 && r(A(p, b, k, _));
|
|
38
|
-
}, w = (a) => {
|
|
39
|
-
u(a), a > 0 && r(T(a));
|
|
40
|
-
}, N = (a) => {
|
|
33
|
+
const y = (a) => {
|
|
41
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);
|
|
42
41
|
try {
|
|
43
|
-
const
|
|
44
|
-
if (!Array.isArray(
|
|
45
|
-
|
|
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);
|
|
46
45
|
} catch {
|
|
47
|
-
|
|
46
|
+
N(!0);
|
|
48
47
|
}
|
|
49
|
-
},
|
|
50
|
-
const
|
|
51
|
-
return Number.isFinite(
|
|
48
|
+
}, g = (a) => {
|
|
49
|
+
const m = parseFloat(a);
|
|
50
|
+
return Number.isFinite(m) ? m : 0;
|
|
52
51
|
};
|
|
53
|
-
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(
|
|
54
53
|
"input",
|
|
55
54
|
{
|
|
56
55
|
type: "number",
|
|
57
56
|
min: "1",
|
|
58
|
-
value:
|
|
59
|
-
disabled:
|
|
57
|
+
value: p.w,
|
|
58
|
+
disabled: r,
|
|
60
59
|
"data-testid": "bed-w",
|
|
61
|
-
onChange: (a) =>
|
|
60
|
+
onChange: (a) => y({ ...p, w: g(a.target.value) })
|
|
62
61
|
}
|
|
63
|
-
), " mm"), /* @__PURE__ */ e.createElement("label", null, "
|
|
62
|
+
), " mm"), /* @__PURE__ */ e.createElement("label", null, "Depth ", /* @__PURE__ */ e.createElement(
|
|
64
63
|
"input",
|
|
65
64
|
{
|
|
66
65
|
type: "number",
|
|
67
66
|
min: "1",
|
|
68
|
-
value:
|
|
69
|
-
disabled:
|
|
67
|
+
value: p.d,
|
|
68
|
+
disabled: r,
|
|
70
69
|
"data-testid": "bed-d",
|
|
71
|
-
onChange: (a) =>
|
|
70
|
+
onChange: (a) => y({ ...p, d: g(a.target.value) })
|
|
72
71
|
}
|
|
73
|
-
), " mm"), /* @__PURE__ */ e.createElement("label", null, "
|
|
72
|
+
), " mm"), /* @__PURE__ */ e.createElement("label", null, "Origin X ", /* @__PURE__ */ e.createElement(
|
|
74
73
|
"input",
|
|
75
74
|
{
|
|
76
75
|
type: "number",
|
|
77
|
-
value:
|
|
78
|
-
disabled:
|
|
79
|
-
onChange: (a) =>
|
|
76
|
+
value: p.ox,
|
|
77
|
+
disabled: r,
|
|
78
|
+
onChange: (a) => y({ ...p, ox: g(a.target.value) })
|
|
80
79
|
}
|
|
81
|
-
)), /* @__PURE__ */ e.createElement("label", null, "
|
|
80
|
+
)), /* @__PURE__ */ e.createElement("label", null, "Origin Y ", /* @__PURE__ */ e.createElement(
|
|
82
81
|
"input",
|
|
83
82
|
{
|
|
84
83
|
type: "number",
|
|
85
|
-
value:
|
|
86
|
-
disabled:
|
|
87
|
-
onChange: (a) =>
|
|
84
|
+
value: p.oy,
|
|
85
|
+
disabled: r,
|
|
86
|
+
onChange: (a) => y({ ...p, oy: g(a.target.value) })
|
|
88
87
|
}
|
|
89
|
-
))),
|
|
88
|
+
))), l === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Diameter ", /* @__PURE__ */ e.createElement(
|
|
90
89
|
"input",
|
|
91
90
|
{
|
|
92
91
|
type: "number",
|
|
93
92
|
min: "1",
|
|
94
|
-
value:
|
|
95
|
-
disabled:
|
|
93
|
+
value: w,
|
|
94
|
+
disabled: r,
|
|
96
95
|
"data-testid": "bed-dia",
|
|
97
|
-
onChange: (a) =>
|
|
96
|
+
onChange: (a) => k(g(a.target.value))
|
|
98
97
|
}
|
|
99
|
-
), " mm")),
|
|
98
|
+
), " mm")), l === "custom" && /* @__PURE__ */ e.createElement(
|
|
100
99
|
"textarea",
|
|
101
100
|
{
|
|
102
|
-
className: "bse-json" + (
|
|
101
|
+
className: "bse-json" + (E ? " err" : ""),
|
|
103
102
|
rows: 3,
|
|
104
|
-
value:
|
|
105
|
-
disabled:
|
|
103
|
+
value: d,
|
|
104
|
+
disabled: r,
|
|
106
105
|
"data-testid": "bed-json",
|
|
107
|
-
onChange: (a) =>
|
|
106
|
+
onChange: (a) => M(a.target.value),
|
|
108
107
|
placeholder: "[[0,0],[200,0],[200,200],[0,200]]"
|
|
109
108
|
}
|
|
110
109
|
));
|
|
111
110
|
}
|
|
112
|
-
const
|
|
113
|
-
printable_area:
|
|
114
|
-
},
|
|
115
|
-
function H(t,
|
|
116
|
-
var
|
|
117
|
-
if (!t || !(
|
|
118
|
-
const
|
|
119
|
-
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 ?? "");
|
|
120
119
|
}
|
|
121
|
-
function Q(t,
|
|
122
|
-
var
|
|
123
|
-
if (
|
|
124
|
-
const
|
|
125
|
-
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;
|
|
126
125
|
}
|
|
127
|
-
function
|
|
126
|
+
function X(t) {
|
|
128
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";
|
|
129
128
|
}
|
|
130
|
-
function
|
|
129
|
+
function Y({ mode: t }) {
|
|
131
130
|
return /* @__PURE__ */ e.createElement("span", { className: `badge mode-${t ?? "none"}` }, t ?? "—");
|
|
132
131
|
}
|
|
133
|
-
function
|
|
134
|
-
const o =
|
|
135
|
-
if (
|
|
136
|
-
const
|
|
137
|
-
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) {
|
|
138
137
|
case "select":
|
|
139
|
-
return /* @__PURE__ */ e.createElement("select", { value: u ?? "", onChange: (
|
|
140
|
-
var
|
|
141
|
-
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);
|
|
142
141
|
}));
|
|
143
142
|
case "checkbox":
|
|
144
|
-
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 });
|
|
145
144
|
case "color":
|
|
146
|
-
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 });
|
|
147
146
|
case "textarea":
|
|
148
147
|
return /* @__PURE__ */ e.createElement("textarea", { rows: 2, value: Array.isArray(o) ? o.join(`
|
|
149
|
-
`) : o ?? "", onChange: (
|
|
148
|
+
`) : o ?? "", onChange: (d) => p(d.target.value), disabled: c });
|
|
150
149
|
case "points":
|
|
151
150
|
return /* @__PURE__ */ e.createElement("code", { className: "pts" }, JSON.stringify(o ?? []));
|
|
152
151
|
case "unknown":
|
|
153
|
-
return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "
|
|
152
|
+
return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "No schema");
|
|
154
153
|
default:
|
|
155
|
-
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));
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
|
-
function
|
|
159
|
-
if (t.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙
|
|
160
|
-
const o = C[t],
|
|
161
|
-
const
|
|
162
|
-
return delete
|
|
163
|
-
}),
|
|
164
|
-
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}` }, "↺")));
|
|
165
164
|
}
|
|
166
|
-
function re({ settings: t, setSettings:
|
|
167
|
-
const [
|
|
168
|
-
var
|
|
169
|
-
return
|
|
170
|
-
}).slice(0, 60).map(([
|
|
171
|
-
return /* @__PURE__ */ e.createElement(
|
|
172
|
-
|
|
173
|
-
} },
|
|
174
|
-
const
|
|
175
|
-
return
|
|
176
|
-
})) : /* @__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"))));
|
|
177
176
|
}
|
|
178
177
|
export {
|
|
179
178
|
re as default
|
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.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Type definitions for
|
|
1
|
+
// Type definitions for three-slicer (engine entry point)
|
|
2
|
+
export type { SlicerSettings, SettingKey, Point } from '../types/settings-keys.d.ts'
|
|
3
|
+
|
|
2
4
|
export interface SliceLayer { z: number; idx: number; gcode: string; paths: Float32Array; widths: Float32Array }
|
|
3
5
|
export interface SliceCallbacks {
|
|
4
6
|
onProgress?: (done: number, total: number) => void
|
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).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The single import site for the 4 extracted JSON files.
|
|
2
|
+
//
|
|
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
|
+
export { default as schema } from 'three-slicer/data/config-schema.json' with { type: 'json' }
|
|
10
|
+
export { default as uiTree } from 'three-slicer/data/ui-tree.json' with { type: 'json' }
|
|
11
|
+
export { default as toggleRules } from 'three-slicer/data/toggle-rules.json' with { type: 'json' }
|
|
12
|
+
export { default as invalidationMap } from 'three-slicer/data/invalidation-map.json' with { type: 'json' }
|