three-slicer 0.1.1 → 0.1.3

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.
Files changed (39) hide show
  1. package/components/dist/SettingsPanel.js +112 -112
  2. package/components/styles.css +9 -9
  3. package/data/config-schema.json +999 -900
  4. package/data/invalidation-map.json +6 -5
  5. package/data/printers.json +1 -0
  6. package/data/processes.js +2 -0
  7. package/data/toggle-rules.json +44 -44
  8. package/data/ui-tree.json +170 -494
  9. package/engine/examples/headless.mjs +2 -2
  10. package/engine/index.js +2 -2
  11. package/engine/src/data.js +13 -7
  12. package/engine/src/settings.js +120 -35
  13. package/engine/src/slicer.worker.js +23 -21
  14. package/engine/src/slicer_core.js +0 -0
  15. package/engine/src/slicer_core.mt.js +0 -0
  16. package/engine/src/toggle_eval.js +11 -11
  17. package/package.json +4 -2
  18. package/types/components.d.ts +5 -5
  19. package/types/data/config-schema.json.d.ts +6 -6
  20. package/types/data/invalidation-map.json.d.ts +5 -5
  21. package/types/data/printers.json.d.ts +23 -0
  22. package/types/data/processes.js.d.ts +21 -0
  23. package/types/data/toggle-rules.json.d.ts +5 -5
  24. package/types/data/ui-tree.json.d.ts +3 -3
  25. package/types/data.d.ts +10 -7
  26. package/types/gen_settings_types.mjs +10 -10
  27. package/types/settings-keys.d.ts +18 -7
  28. package/types/settings.d.ts +6 -6
  29. package/types/toggle.d.ts +6 -6
  30. package/types/viewer-loaders.d.ts +8 -8
  31. package/types/viewer-toolpath.d.ts +17 -17
  32. package/types/viewer.d.ts +3 -3
  33. package/types/wasm.d.ts +3 -3
  34. package/types/worker.d.ts +2 -2
  35. package/viewer/dist/Viewport.js +1760 -966
  36. package/viewer/dist/make_worker.js +6 -6
  37. package/viewer/dist/model_loaders.js +35 -35
  38. package/viewer/dist/toolpath_gpu.js +146 -147
  39. package/viewer/styles.css +46 -25
@@ -1,179 +1,179 @@
1
- import e, { useRef as R, useState as f, useLayoutEffect as F, useEffect as J, useMemo as z } from "react";
2
- import { createPortal as O } from "react-dom";
3
- import { uiTree as M, schema as C } from "three-slicer/data";
4
- 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";
5
5
  import { disabledKeys as I, makeCfg as B } from "three-slicer/toggle";
6
- function D({ css: t, className: r, children: n }) {
7
- const s = R(null), [l] = f(() => {
8
- const d = document.createElement("div");
9
- return d.style.display = "contents", d;
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 F(() => {
12
- const d = s.current, o = d.shadowRoot || d.attachShadow({ mode: "open" });
13
- let i = o.querySelector("style[data-ts-css]");
14
- i || (i = document.createElement("style"), i.setAttribute("data-ts-css", ""), o.append(i)), i.textContent = t, o.contains(l) || o.append(l);
15
- }, [t, l]), /* @__PURE__ */ e.createElement("div", { ref: s, className: r }, O(n, l));
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 L = ':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}', A = (t, r, n, s) => [[n, s], [t + n, s], [t + n, r + s], [n, r + s]], T = (t) => {
18
- const r = t / 2, n = 32;
19
- return Array.from({ length: n }, (s, l) => {
20
- const d = 2 * Math.PI * l / n;
21
- return [+(r + r * Math.cos(d)).toFixed(2), +(r + r * Math.sin(d)).toFixed(2)];
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
- }, S = (t) => {
24
- const r = t.map((s) => s[0]), n = t.map((s) => s[1]);
25
- return { x0: Math.min(...r), y0: Math.min(...n), w: Math.max(...r) - Math.min(...r), d: Math.max(...n) - Math.min(...n) };
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 $({ value: t, onChange: r, disabled: n }) {
28
- const s = Array.isArray(t) && t.length >= 3 ? t : A(200, 200, 0, 0), l = S(s), [d, o] = f(s.length === 4 ? "rect" : s.length >= 12 ? "circle" : "custom"), [i, m] = f({ w: l.w, d: l.d, ox: l.x0, oy: l.y0 }), [h, u] = f(Math.round(l.w)), [c, x] = f(JSON.stringify(s)), [g, y] = f(!1);
29
- J(() => {
30
- const a = S(s);
31
- m({ w: a.w, d: a.d, ox: a.x0, oy: a.y0 }), u(Math.round(a.w)), x(JSON.stringify(s)), y(!1);
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 v = (a) => {
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 p = JSON.parse(a);
43
- if (!Array.isArray(p) || p.length < 3 || !p.every((b) => Array.isArray(b) && b.length === 2 && b.every(Number.isFinite))) throw 0;
44
- y(!1), r(p);
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
- y(!0);
46
+ N(!0);
47
47
  }
48
- }, E = (a) => {
49
- const p = parseFloat(a);
50
- return Number.isFinite(p) ? p : 0;
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", "사각형"], ["circle", "원형"], ["custom", "커스텀"]].map(([a, p]) => /* @__PURE__ */ e.createElement("button", { key: a, type: "button", className: d === a ? "on" : "", disabled: n, onClick: () => o(a) }, p))), d === "rect" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "폭 ", /* @__PURE__ */ e.createElement(
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: i.w,
58
- disabled: n,
57
+ value: p.w,
58
+ disabled: r,
59
59
  "data-testid": "bed-w",
60
- onChange: (a) => v({ ...i, w: E(a.target.value) })
60
+ onChange: (a) => y({ ...p, w: g(a.target.value) })
61
61
  }
62
- ), " mm"), /* @__PURE__ */ e.createElement("label", null, "깊이 ", /* @__PURE__ */ e.createElement(
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: i.d,
68
- disabled: n,
67
+ value: p.d,
68
+ disabled: r,
69
69
  "data-testid": "bed-d",
70
- onChange: (a) => v({ ...i, d: E(a.target.value) })
70
+ onChange: (a) => y({ ...p, d: g(a.target.value) })
71
71
  }
72
- ), " mm"), /* @__PURE__ */ e.createElement("label", null, "원점 X ", /* @__PURE__ */ e.createElement(
72
+ ), " mm"), /* @__PURE__ */ e.createElement("label", null, "Origin X ", /* @__PURE__ */ e.createElement(
73
73
  "input",
74
74
  {
75
75
  type: "number",
76
- value: i.ox,
77
- disabled: n,
78
- onChange: (a) => v({ ...i, ox: E(a.target.value) })
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, "원점 Y ", /* @__PURE__ */ e.createElement(
80
+ )), /* @__PURE__ */ e.createElement("label", null, "Origin Y ", /* @__PURE__ */ e.createElement(
81
81
  "input",
82
82
  {
83
83
  type: "number",
84
- value: i.oy,
85
- disabled: n,
86
- onChange: (a) => v({ ...i, oy: E(a.target.value) })
84
+ value: p.oy,
85
+ disabled: r,
86
+ onChange: (a) => y({ ...p, oy: g(a.target.value) })
87
87
  }
88
- ))), d === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "지름 ", /* @__PURE__ */ e.createElement(
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: h,
94
- disabled: n,
93
+ value: w,
94
+ disabled: r,
95
95
  "data-testid": "bed-dia",
96
- onChange: (a) => w(E(a.target.value))
96
+ onChange: (a) => k(g(a.target.value))
97
97
  }
98
- ), " mm")), d === "custom" && /* @__PURE__ */ e.createElement(
98
+ ), " mm")), l === "custom" && /* @__PURE__ */ e.createElement(
99
99
  "textarea",
100
100
  {
101
- className: "bse-json" + (g ? " err" : ""),
101
+ className: "bse-json" + (E ? " err" : ""),
102
102
  rows: 3,
103
- value: c,
104
- disabled: n,
103
+ value: d,
104
+ disabled: r,
105
105
  "data-testid": "bed-json",
106
- onChange: (a) => N(a.target.value),
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 U = {
112
- printable_area: $
113
- }, j = Object.keys(M).filter((t) => M[t].length > 0), q = ["all", "simple", "advanced", "expert", "develop"], G = (t) => t.replace("::build", "").replace("Tab", "");
114
- function H(t, r) {
115
- var l;
116
- if (!t || !(r in t)) return !1;
117
- const n = (l = C[r]) == null ? void 0 : l.default, s = Array.isArray(n) ? n[0] : n;
118
- return String(t[r]) !== String(s ?? "");
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, r) {
121
- var s;
122
- if (r === "all" || t.startsWith("<")) return !0;
123
- const n = (s = C[t]) == null ? void 0 : s.mode;
124
- return r === "simple" ? n === "simple" : r === "advanced" ? n === "simple" || n === "advanced" : !0;
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 W(t) {
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 X({ mode: t }) {
129
+ function Y({ mode: t }) {
130
130
  return /* @__PURE__ */ e.createElement("span", { className: `badge mode-${t ?? "none"}` }, t ?? "—");
131
131
  }
132
- function Y({ def: t, optKey: r, settings: n, setSettings: s, disabled: l, customWidgets: d }) {
133
- const o = P(n, r), i = (c) => s((x) => ({ ...x, [r]: c })), m = (d == null ? void 0 : d[r]) ?? U[r];
134
- if (m) return /* @__PURE__ */ e.createElement(m, { def: t, value: o, onChange: i, disabled: l });
135
- const h = W(t), u = Array.isArray(o) ? o[0] : o;
136
- switch (h) {
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: (c) => i(c.target.value), disabled: l }, t.enum_values.map((c, x) => {
139
- var g;
140
- return /* @__PURE__ */ e.createElement("option", { key: c, value: c }, ((g = t.enum_labels) == null ? void 0 : g[x]) ?? c);
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: (c) => i(c.target.checked), disabled: l });
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: (c) => i(c.target.value), disabled: l });
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: (c) => i(c.target.value), disabled: l });
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: (c) => i(c.target.value), disabled: l }), t.sidetext && /* @__PURE__ */ e.createElement("span", { className: "unit" }, t.sidetext));
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 V({ optKey: t, settings: r, setSettings: n, disabled: s, onOptionOpen: l, customWidgets: d }) {
158
- if (t.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙ 커스텀 위젯 ", t));
159
- const o = C[t], i = s ? s[t] : void 0, m = i != null, h = H(r, t), u = () => n((x) => {
160
- const g = { ...x };
161
- return delete g[t], g;
162
- }), c = (o == null ? void 0 : o.label) || (o == null ? void 0 : o.full_label) || t;
163
- return /* @__PURE__ */ e.createElement("div", { className: "row" + (m ? " disabled" : ""), title: m ? `비활성 조건: ${i}` : (o == null ? void 0 : o.tooltip) ?? "", "data-testid": `row-${t}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, h && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "기본값에서 변경됨", "data-testid": `dirty-${t}` }), l ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => l(t), title: "상세" }, c) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, c)), /* @__PURE__ */ e.createElement("code", { className: "key" }, t), /* @__PURE__ */ e.createElement(X, { mode: o == null ? void 0 : o.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(Y, { def: o, optKey: t, settings: r, setSettings: n, disabled: m, customWidgets: d }), h && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: u, title: "기본값으로 리셋", "data-testid": `reset-${t}` }, "↺")));
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 ne({ settings: t, setSettings: r, onOptionOpen: n, embedded: s = !1, customWidgets: l }) {
166
- const [d, o] = f(j[0] ?? ""), [i, m] = f(0), [h, u] = f("all"), [c, x] = f(""), g = M[d] ?? [], y = g[Math.min(i, g.length - 1)], v = z(() => I(B(t)), [t]), w = c.trim().toLowerCase(), N = z(() => w ? Object.entries(C).filter(([a, p]) => {
167
- var b, k;
168
- return a.includes(w) || ((b = p.label) == null ? void 0 : b.toLowerCase().includes(w)) || ((k = p.full_label) == null ? void 0 : k.toLowerCase().includes(w));
169
- }).slice(0, 60).map(([a]) => a) : [], [w]), E = (a, p) => /* @__PURE__ */ e.createElement(V, { key: a + p, optKey: a, settings: t, setSettings: r, disabled: v, onOptionOpen: n, customWidgets: l });
170
- return /* @__PURE__ */ e.createElement(D, { css: L, className: s ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: "옵션 검색 (907개)…", value: c, onChange: (a) => x(a.target.value), "data-testid": "opt-search" }), !w && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("label", { className: "sp-builder" }, /* @__PURE__ */ e.createElement("span", null, "설정 그룹"), /* @__PURE__ */ e.createElement("select", { value: d, onChange: (a) => {
171
- o(a.target.value), m(0);
172
- } }, j.map((a) => /* @__PURE__ */ e.createElement("option", { key: a, value: a }, G(a))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, g.map((a, p) => /* @__PURE__ */ e.createElement("button", { key: a.page + p, className: p === Math.min(i, g.length - 1) ? "on" : "", onClick: () => m(p) }, a.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, q.map((a) => /* @__PURE__ */ e.createElement("button", { key: a, className: h === a ? "on" : "", onClick: () => u(a) }, a))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, w ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "검색: “", c, "” — ", N.length, "건", N.length === 60 ? "+" : ""), N.map(E), !N.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "일치 없음")) : y ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("h2", null, y.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", y.line)), y.groups.map((a) => {
173
- const p = a.options.filter((b) => Q(b, h));
174
- return p.length ? /* @__PURE__ */ e.createElement("section", { key: a.group + a.line }, /* @__PURE__ */ e.createElement("h3", null, a.group || "(무제 그룹)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", a.line)), p.map(E)) : null;
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
- ne as default
178
+ re as default
179
179
  };
@@ -1,10 +1,10 @@
1
- /* @three-slicer/components — SettingsPanel 스타일.
2
- Shadow DOM 격리(shadow_host.jsx): ?inline 으로 번들에 내장돼 패널의 shadow root 에
3
- 주입된다. 호스트 앱 CSS 와 양방향 무간섭 — 클래스명 충돌이 불가능하다. */
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단계 S5: toggle-rules 비활성 + dirty/리셋 */
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
- /* ---- 임베드 변형 (:host 에 sp-embedded — viewer process-card 등 카드 안 전폭 배치) ---- */
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;