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.
@@ -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;
@@ -627,5 +627,5 @@
627
627
  "line": 1533
628
628
  }
629
629
  ],
630
- "note": "조건에 없는 opt_key(기본 분기)는 전체 무효화. steps는 분기 블록에서 참조된 단계 토큰."
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단계) — assemble G-code from per-layer chunks
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단계 streaming protocol ({type:'layer'|'done'|'error'|'progress'}).
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단계 streaming — emits + frees each layer; when set, the
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).
@@ -1,11 +1,11 @@
1
- // 추출 JSON 4종의 유일한 import 지점.
1
+ // The single import site for the 4 extracted JSON files.
2
2
  //
3
- // 왜 한 군데로 모으는가: JSON 은 Node 22+ ESM 에서 `with { type: 'json' }` 이 필수인데,
4
- // Vite/esbuild 는 번들 출력에서 이 attribute 를 떼어낸다(build.target·importAttributesKey 로
5
- // 막을 수 없음 — 실측). 그래서 같은 JSON 을 "attribute 있는 원본(engine/src/*.js)" 과
6
- // "attribute 없는 번들(components/dist)" 이 동시에 가리키면 소비자 번들러가
7
- // "inconsistent import attributes" 로 경고한다. 여기 한 곳만 JSON 을 잡고 나머지는
8
- // 이 모듈을 external 로 import → 번들에 JSON import 자체가 남지 않아 불일치가 성립하지 않는다.
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' }