three-slicer-viewer 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/COMPONENTS.md CHANGED
@@ -1,4 +1,7 @@
1
- # three-slicer/components
1
+ # three-slicer-viewer/components — the settings form
2
+
3
+ (Published as `three-slicer-viewer/components`, MIT. `three-slicer/components` is the same component with upstream's
4
+ schema, tab tree and toggle rules plugged in; everything below applies to both.)
2
5
 
3
6
  Reusable React components for the browser slicer. Props-driven — no global state, no React context, no router coupling (proven standalone by the tarball consumer gate `packages/pack_check.sh`).
4
7
 
package/README.md CHANGED
@@ -9,6 +9,10 @@ exports; it does not slice. Slicing is plugged in through two props — `slicer`
9
9
  `catalog` (vendor presets) — which `three-slicer/viewer` supplies. Install `three-slicer` for that, and note that
10
10
  doing so puts your application under AGPL.
11
11
 
12
+ Developed in the [`Three_Slicer`](https://github.com/kimgh06/Three_Slicer) monorepo (`viewer-package/`), where it
13
+ is built and tested beside the kernel; this repository is that folder's history, pushed on every release. Issues and
14
+ pull requests are welcome here — a change lands upstream and comes back with the next sync.
15
+
12
16
  ## The viewer
13
17
 
14
18
  ```jsx
@@ -66,12 +70,12 @@ handle.setColors(computeColors(data, 'speed', ctx).color) // recolour without a
66
70
  ## Licensing
67
71
 
68
72
  MIT. It depends on nothing AGPL, which a check in this repository enforces rather than documents
69
- (`packages/viewer/test_license_boundary.mjs`).
73
+ (`packages/viewer/tests/test_license_boundary.mjs`).
70
74
 
71
75
  The implementation was written from [`TOOLPATH_SPEC.md`](./TOOLPATH_SPEC.md), a functional contract, rather
72
76
  than derived from any upstream slicer. The reasoning and evidence are in
73
- [`../packages/PROVENANCE.md`](../packages/PROVENANCE.md); the plan it came from is
74
- [`../packages/RELICENSE.md`](../packages/RELICENSE.md).
77
+ [`PROVENANCE.md`](https://github.com/kimgh06/Three_Slicer/blob/main/packages/PROVENANCE.md); the plan it came from is
78
+ [`RELICENSE.md`](https://github.com/kimgh06/Three_Slicer/blob/main/packages/RELICENSE.md).
75
79
 
76
80
  Published as a locked pair with `three-slicer`: both carry the same version, and `three-slicer` pins this
77
81
  package exactly, so a combination that was never built together cannot be resolved.
package/TOOLPATH_SPEC.md CHANGED
@@ -163,10 +163,10 @@ Constraints:
163
163
 
164
164
  ## 8. How to know it works
165
165
 
166
- 1. `node packages-mit/test_toolpath_contract.mjs` — the contract, plus a printed snapshot to diff against
166
+ 1. `node viewer-package/tests/test_toolpath_contract.mjs` — the contract, plus a printed snapshot to diff against
167
167
  the recorded run. Exact float equality is **not** expected; counts, strides, encodings, ranges and length
168
168
  accounting are.
169
- 2. `node packages-mit/test_move_scrub.mjs` — the scrub queries.
169
+ 2. `node viewer-package/tests/test_move_scrub.mjs` — the scrub queries.
170
170
  3. `npm run test:viewer` — everything, including the license boundary check.
171
171
  4. **Look at it.** Build the three demos that consume this API
172
172
  (`examples/{instant-quote,cad-embed,farm-dashboard}`) and open them. A static check cannot tell you a
package/VIEWER.md CHANGED
@@ -1,4 +1,7 @@
1
- # three-slicer/viewer
1
+ # three-slicer-viewer — the viewer
2
+
3
+ (Published as `three-slicer-viewer`, MIT. `three-slicer/viewer` is the same component with the WASM kernel and the
4
+ vendor catalog plugged in; everything below applies to both.)
2
5
 
3
6
  3D slicer viewer as a React component: three.js viewport (orbit/transform gizmos), model import (STL/OBJ/3MF/AMF/PLY + pluggable formats such as STEP, multi-object, drag & drop), Web Worker slicing via `three-slicer`, support and material painting, per-extruder filament presets, multi-plate, and a GPU-instanced volumetric toolpath preview ported from OrcaSlicer's libvgcode (millions of segments in a single draw path, per-feature colors, layer range slider, G-code export).
4
7
 
@@ -391,6 +394,70 @@ for 143MB) against a 4GB pool budget, which gives 2 for that model and does not
391
394
  manual count is capped the same way. What the browser has NOT measured yet is
392
395
  the arachne + tree-support preset, where the harness gains were largest.
393
396
 
397
+ ## Theming
398
+
399
+ Both shadow roots (the viewer and `<SettingsPanel/>`) paint their chrome from one palette, `UI_TOKENS` in
400
+ `src/core/theme.js`. Each entry is a custom property a host can set on any ancestor of the component — custom
401
+ properties are the one thing that inherits across a shadow boundary, so this is the only styling hook the isolation
402
+ leaves open:
403
+
404
+ ```css
405
+ .my-slicer-page { --vp-accent: #0a84ff; --vp-light-bg: #f4f4f6; }
406
+ ```
407
+
408
+ An unset property falls back to the default below. Four of them are also used as translucent fill, and CSS cannot
409
+ take the alpha off a hex — those carry an `-rgb` twin (`--vp-accent-rgb: 0, 174, 66`) that a host overriding the
410
+ colour must set as well. The 3D scene (the selection tint, plate borders, grid, SLA preview
411
+ colours) is drawn by three.js from `THEME` in the same file and does not read these properties.
412
+
413
+ | Property | Default |
414
+ |---|---|
415
+ | `--vp-accent` | `#00ae42` |
416
+ | `--vp-accent-hover` | `#06c04d` |
417
+ | `--vp-accent-deep` | `#23402c` |
418
+ | `--vp-accent-ink` | `#167a3a` |
419
+ | `--vp-accent-on-dark` | `#9fe3bd` |
420
+ | `--vp-accent-soft` | `#e3f6ea` |
421
+ | `--vp-accent-soft-border` | `#9adfb6` |
422
+ | `--vp-on-accent` | `#ffffff` |
423
+ | `--vp-dark-base` | `#0e1216` |
424
+ | `--vp-dark-surface` | `#161b21` |
425
+ | `--vp-dark-control` | `#1a2027` |
426
+ | `--vp-dark-control-hover` | `#28313a` |
427
+ | `--vp-dark-border` | `#2c353d` |
428
+ | `--vp-dark-border-strong` | `#3a4550` |
429
+ | `--vp-dark-text` | `#cdd6dd` |
430
+ | `--vp-dark-text-muted` | `#aebcc6` |
431
+ | `--vp-dark-text-faint` | `#8b98a2` |
432
+ | `--vp-dark-text-disabled` | `#5a6570` |
433
+ | `--vp-light-bg` | `#eef0f2` |
434
+ | `--vp-light-surface` | `#ffffff` |
435
+ | `--vp-light-subtle` | `#f6f7f8` |
436
+ | `--vp-light-control` | `#dfe6ea` |
437
+ | `--vp-light-control-hover` | `#d3dee4` |
438
+ | `--vp-light-border` | `#d5d8dd` |
439
+ | `--vp-light-divider` | `#e3e6ea` |
440
+ | `--vp-light-text` | `#24282e` |
441
+ | `--vp-light-text-muted` | `#5a6069` |
442
+ | `--vp-light-text-faint` | `#8a9099` |
443
+ | `--vp-info` | `#2b6cff` |
444
+ | `--vp-info-ink` | `#2563eb` |
445
+ | `--vp-info-soft` | `#e6efff` |
446
+ | `--vp-tech-badge` | `#6d7ce0` |
447
+ | `--vp-danger` | `#e23b3b` |
448
+ | `--vp-danger-ink` | `#a33b37` |
449
+ | `--vp-danger-soft` | `#fbeaea` |
450
+ | `--vp-danger-on-dark` | `#ff8b8b` |
451
+ | `--vp-danger-deep` | `#4a2229` |
452
+ | `--vp-warn` | `#f0a542` |
453
+ | `--vp-warn-ink` | `#a86a12` |
454
+ | `--vp-warn-soft` | `#fdf4e3` |
455
+ | `--vp-warn-soft-hover` | `#f3e2cb` |
456
+ | `--vp-warn-border` | `#d8b48a` |
457
+ | `--vp-warn-on-dark` | `#f0c060` |
458
+ | `--vp-warn-deep` | `#3a2a10` |
459
+ | `--vp-warn-deep-border` | `#7a5a1e` |
460
+
394
461
  ## Bundler notes
395
462
 
396
463
  - **Vite:** two config lines (worker code-splitting + top-level await in the mt glue; Chrome 89+/Safari 15+):
@@ -1,125 +1,116 @@
1
- import e, { useRef as J, useState as w, useLayoutEffect as T, useEffect as I, useMemo as j } from "react";
2
- import { createPortal as W } from "react-dom";
3
- import { uiTreeKeys as $, leanSchema as U } from "three-slicer-viewer/data";
4
- import { printerTechnology as G, settingRaw as q } from "three-slicer-viewer/settings";
5
- import { disabledKeys as H, makeCfg as Q } from "three-slicer-viewer/toggle";
6
- function X({ css: t, className: a, children: n }) {
7
- const r = J(null), [l] = w(() => {
8
- const p = document.createElement("div");
9
- return p.style.display = "contents", p;
1
+ import e, { useState as y, useEffect as P, useMemo as j } from "react";
2
+ import { leanSchema as R, uiTreeKeys as U } from "three-slicer-viewer/data";
3
+ import { printerTechnology as W, settingRaw as I } from "three-slicer-viewer/settings";
4
+ import { disabledKeys as $, makeCfg as G } from "three-slicer-viewer/toggle";
5
+ import { U as H } from "./viewer_defaults-B7UUZ582.js";
6
+ import { a as Q } from "./theme-2oyfMuvf.js";
7
+ import { S as X } from "./shadow_host-CQ3LirzD.js";
8
+ const Y = ':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 var(--_light-divider)}.row .lbl{color:var(--_light-text);text-decoration:none}.row .lbl:hover{color:var(--_accent-ink);text-decoration:underline}.row .key{font-size:11px;color:var(--_light-text-faint);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:var(--_light-text-faint);font-size:12px;white-space:nowrap}.pts{font-size:11px;color:var(--_light-text-muted)}.badge{font-size:10px;padding:1px 6px;border-radius:8px;color:var(--_on-accent);text-align:center}.mode-simple{background:var(--_accent-ink)}.mode-advanced{background:var(--_info-ink)}.mode-expert{background:var(--_warn)}.mode-develop{background:var(--_danger)}.mode-none,.mode-undefined{background:var(--_light-text-faint)}.muted{color:var(--_light-text-faint);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:var(--_light-subtle)}.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:var(--_warn);box-shadow:0 0 0 2px var(--_warn-soft)}.override-dot{flex:0 0 auto;width:9px;height:9px;border-radius:50%;background:var(--_warn);border:0;padding:0;cursor:pointer;box-shadow:0 0 0 2px var(--_warn-soft)}.override-dot:hover{background:var(--_warn-ink)}.widget-cell{display:inline-flex;align-items:center;gap:6px;min-width:0}.reset-btn{flex:0 0 auto;border:1px solid var(--_warn-border);background:var(--_warn-soft);color:var(--_warn-ink);border-radius:5px;cursor:pointer;font-size:13px;line-height:1;padding:1px 5px}.reset-btn:hover{background:var(--_warn-soft-hover)}.settings-panel{flex:0 0 400px;width:400px;border-left:1px solid var(--_light-divider);display:flex;flex-direction:column;min-height:0;background:var(--_light-surface);color:var(--_light-text);font:14px/1.5 -apple-system,Segoe UI,sans-serif}.settings-panel .sp-top{padding:10px 12px 8px;border-bottom:1px solid var(--_light-divider)}.sp-builder{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--_light-text-muted)}.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 var(--_light-border);background:var(--_light-subtle);border-radius:6px;padding:4px 8px;font-size:12px;cursor:pointer}.sp-pages button.on{background:var(--_accent-soft);color:var(--_accent-ink);border-color:var(--_accent-soft-border);font-weight:700}.sp-modes{display:flex;gap:4px;margin-top:8px}.sp-modes button{border:0;background:none;font-size:11px;color:var(--_light-text-faint);padding:2px 6px;border-radius:6px;cursor:pointer}.sp-modes button.on{background:var(--_accent-soft);color:var(--_accent-ink);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 var(--_light-border);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 var(--_light-border);background:var(--_light-subtle);border-radius:6px;padding:2px 8px;font-size:11px;cursor:pointer}.bse-modes button.on{background:var(--_accent-soft);color:var(--_accent-ink);border-color:var(--_accent-soft-border);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:var(--_light-text-muted);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 var(--_light-border);border-radius:6px;box-sizing:border-box}.bse-json.err{border-color:var(--_danger);background:var(--_danger-soft)}', q = (t, a, r, o) => [[r, o], [t + r, o], [t + r, a + o], [r, a + o]], L = R.printable_area.default, V = (t) => {
9
+ const a = t / 2, r = 32;
10
+ return Array.from({ length: r }, (o, i) => {
11
+ const u = 2 * Math.PI * i / r;
12
+ return [+(a + a * Math.cos(u)).toFixed(2), +(a + a * Math.sin(u)).toFixed(2)];
10
13
  });
11
- return T(() => {
12
- const p = r.current, d = p.shadowRoot || p.attachShadow({ mode: "open" });
13
- let i = d.querySelector("style[data-ts-css]");
14
- i || (i = document.createElement("style"), i.setAttribute("data-ts-css", ""), d.append(i)), i.textContent = t, d.contains(l) || d.append(l);
15
- }, [t, l]), /* @__PURE__ */ e.createElement("div", { ref: r, className: a }, W(n, l));
16
- }
17
- const Y = ':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}.override-dot{flex:0 0 auto;width:9px;height:9px;border-radius:50%;background:#f0a542;border:0;padding:0;cursor:pointer;box-shadow:0 0 0 2px #fdf1dd}.override-dot:hover{background:#d98c26}.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}', R = (t, a, n, r) => [[n, r], [t + n, r], [t + n, a + r], [n, a + r]], V = (t) => {
18
- const a = t / 2, n = 32;
19
- return Array.from({ length: n }, (r, l) => {
20
- const p = 2 * Math.PI * l / n;
21
- return [+(a + a * Math.cos(p)).toFixed(2), +(a + a * Math.sin(p)).toFixed(2)];
22
- });
23
- }, L = (t) => {
24
- const a = t.map((r) => r[0]), n = t.map((r) => r[1]);
25
- return { x0: Math.min(...a), y0: Math.min(...n), w: Math.max(...a) - Math.min(...a), d: Math.max(...n) - Math.min(...n) };
14
+ }, T = (t) => {
15
+ const a = t.map((o) => o[0]), r = t.map((o) => o[1]);
16
+ return { x0: Math.min(...a), y0: Math.min(...r), w: Math.max(...a) - Math.min(...a), d: Math.max(...r) - Math.min(...r) };
26
17
  };
27
- function Z({ value: t, onChange: a, disabled: n }) {
28
- const r = Array.isArray(t) && t.length >= 3 ? t : R(200, 200, 0, 0), l = L(r), [p, d] = w(r.length === 4 ? "rect" : r.length >= 12 ? "circle" : "custom"), [i, u] = w({ w: l.w, d: l.d, ox: l.x0, oy: l.y0 }), [x, g] = w(Math.round(l.w)), [c, b] = w(JSON.stringify(r)), [C, E] = w(!1);
29
- I(() => {
30
- const o = L(r);
31
- u({ w: o.w, d: o.d, ox: o.x0, oy: o.y0 }), g(Math.round(o.w)), b(JSON.stringify(r)), E(!1);
18
+ function Z({ value: t, onChange: a, disabled: r }) {
19
+ const o = Array.isArray(t) && t.length >= 3 ? t : L, i = T(o), [u, d] = y(o.length === 4 ? "rect" : o.length >= 12 ? "circle" : "custom"), [c, p] = y({ w: i.w, d: i.d, ox: i.x0, oy: i.y0 }), [x, g] = y(Math.round(i.w)), [l, b] = y(JSON.stringify(o)), [k, w] = y(!1);
20
+ P(() => {
21
+ const n = T(o);
22
+ p({ w: n.w, d: n.d, ox: n.x0, oy: n.y0 }), g(Math.round(n.w)), b(JSON.stringify(o)), w(!1);
32
23
  }, [JSON.stringify(t)]);
33
- const y = (o) => {
34
- u(o);
35
- const { w: m, d: h, ox: A, oy: M } = o;
36
- m > 0 && h > 0 && a(R(m, h, A, M));
37
- }, N = (o) => {
38
- g(o), o > 0 && a(V(o));
39
- }, _ = (o) => {
40
- b(o);
24
+ const v = (n) => {
25
+ p(n);
26
+ const { w: m, d: f, ox: S, oy: M } = n;
27
+ m > 0 && f > 0 && a(q(m, f, S, M));
28
+ }, _ = (n) => {
29
+ g(n), n > 0 && a(V(n));
30
+ }, z = (n) => {
31
+ b(n);
41
32
  try {
42
- const m = JSON.parse(o);
43
- if (!Array.isArray(m) || m.length < 3 || !m.every((h) => Array.isArray(h) && h.length === 2 && h.every(Number.isFinite))) throw 0;
44
- E(!1), a(m);
33
+ const m = JSON.parse(n);
34
+ if (!Array.isArray(m) || m.length < 3 || !m.every((f) => Array.isArray(f) && f.length === 2 && f.every(Number.isFinite))) throw 0;
35
+ w(!1), a(m);
45
36
  } catch {
46
- E(!0);
37
+ w(!0);
47
38
  }
48
- }, v = (o) => {
49
- const m = parseFloat(o);
39
+ }, E = (n) => {
40
+ const m = parseFloat(n);
50
41
  return Number.isFinite(m) ? m : 0;
51
42
  };
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(([o, m]) => /* @__PURE__ */ e.createElement("button", { key: o, type: "button", className: p === o ? "on" : "", disabled: n, onClick: () => d(o) }, m))), p === "rect" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Width ", /* @__PURE__ */ e.createElement(
43
+ 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(([n, m]) => /* @__PURE__ */ e.createElement("button", { key: n, type: "button", className: u === n ? "on" : "", disabled: r, onClick: () => d(n) }, m))), u === "rect" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Width ", /* @__PURE__ */ e.createElement(
53
44
  "input",
54
45
  {
55
46
  type: "number",
56
47
  min: "1",
57
- value: i.w,
58
- disabled: n,
48
+ value: c.w,
49
+ disabled: r,
59
50
  "data-testid": "bed-w",
60
- onChange: (o) => y({ ...i, w: v(o.target.value) })
51
+ onChange: (n) => v({ ...c, w: E(n.target.value) })
61
52
  }
62
53
  ), " mm"), /* @__PURE__ */ e.createElement("label", null, "Depth ", /* @__PURE__ */ e.createElement(
63
54
  "input",
64
55
  {
65
56
  type: "number",
66
57
  min: "1",
67
- value: i.d,
68
- disabled: n,
58
+ value: c.d,
59
+ disabled: r,
69
60
  "data-testid": "bed-d",
70
- onChange: (o) => y({ ...i, d: v(o.target.value) })
61
+ onChange: (n) => v({ ...c, d: E(n.target.value) })
71
62
  }
72
63
  ), " mm"), /* @__PURE__ */ e.createElement("label", null, "Origin X ", /* @__PURE__ */ e.createElement(
73
64
  "input",
74
65
  {
75
66
  type: "number",
76
- value: i.ox,
77
- disabled: n,
78
- onChange: (o) => y({ ...i, ox: v(o.target.value) })
67
+ value: c.ox,
68
+ disabled: r,
69
+ onChange: (n) => v({ ...c, ox: E(n.target.value) })
79
70
  }
80
71
  )), /* @__PURE__ */ e.createElement("label", null, "Origin Y ", /* @__PURE__ */ e.createElement(
81
72
  "input",
82
73
  {
83
74
  type: "number",
84
- value: i.oy,
85
- disabled: n,
86
- onChange: (o) => y({ ...i, oy: v(o.target.value) })
75
+ value: c.oy,
76
+ disabled: r,
77
+ onChange: (n) => v({ ...c, oy: E(n.target.value) })
87
78
  }
88
- ))), p === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Diameter ", /* @__PURE__ */ e.createElement(
79
+ ))), u === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Diameter ", /* @__PURE__ */ e.createElement(
89
80
  "input",
90
81
  {
91
82
  type: "number",
92
83
  min: "1",
93
84
  value: x,
94
- disabled: n,
85
+ disabled: r,
95
86
  "data-testid": "bed-dia",
96
- onChange: (o) => N(v(o.target.value))
87
+ onChange: (n) => _(E(n.target.value))
97
88
  }
98
- ), " mm")), p === "custom" && /* @__PURE__ */ e.createElement(
89
+ ), " mm")), u === "custom" && /* @__PURE__ */ e.createElement(
99
90
  "textarea",
100
91
  {
101
- className: "bse-json" + (C ? " err" : ""),
92
+ className: "bse-json" + (k ? " err" : ""),
102
93
  rows: 3,
103
- value: c,
104
- disabled: n,
94
+ value: l,
95
+ disabled: r,
105
96
  "data-testid": "bed-json",
106
- onChange: (o) => _(o.target.value),
107
- placeholder: "[[0,0],[200,0],[200,200],[0,200]]"
97
+ onChange: (n) => z(n.target.value),
98
+ placeholder: JSON.stringify(L)
108
99
  }
109
100
  ));
110
101
  }
111
102
  const K = {
112
103
  printable_area: Z
113
- }, ee = ["all", "simple", "advanced", "expert", "develop"], B = (t) => t.replace("::build", "").replace("Tab", "").replace("_sla", " (SLA)"), te = (t) => /SLA|build_sla\b/.test(t) ? "SLA" : "FFF", ae = Object.fromEntries(Object.entries($).map(([t, a]) => [t, a.length ? [{ page: "Settings", groups: [{ group: B(t), options: a }] }] : []])), ne = { makeCfg: Q, disabledKeys: H }, re = (t) => Object.keys(t).filter((a) => t[a].length > 0);
114
- function oe(t, a, n) {
115
- if (!a || !(n in a)) return !1;
116
- const r = t[n]?.default, l = Array.isArray(r) ? r[0] : r;
117
- return String(a[n]) !== String(l ?? "");
104
+ }, ee = ["all", "simple", "advanced", "expert", "develop"], B = (t) => t.replace("::build", "").replace("Tab", "").replace("_sla", " (SLA)"), te = (t) => /SLA|build_sla\b/.test(t) ? "SLA" : "FFF", ae = Object.fromEntries(Object.entries(U).map(([t, a]) => [t, a.length ? [{ page: "Settings", groups: [{ group: B(t), options: a }] }] : []])), re = { makeCfg: G, disabledKeys: $ }, ne = (t) => Object.keys(t).filter((a) => t[a].length > 0);
105
+ function oe(t, a, r) {
106
+ if (!a || !(r in a)) return !1;
107
+ const o = t[r]?.default, i = Array.isArray(o) ? o[0] : o;
108
+ return String(a[r]) !== String(i ?? "");
118
109
  }
119
- function se(t, a, n) {
120
- if (n === "all" || a.startsWith("<")) return !0;
121
- const r = t[a]?.mode;
122
- return n === "simple" ? r === "simple" : n === "advanced" ? r === "simple" || r === "advanced" : !0;
110
+ function se(t, a, r) {
111
+ if (r === "all" || a.startsWith("<")) return !0;
112
+ const o = t[a]?.mode;
113
+ return r === "simple" ? o === "simple" : r === "advanced" ? o === "simple" || o === "advanced" : !0;
123
114
  }
124
115
  function le(t) {
125
116
  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";
@@ -127,80 +118,80 @@ function le(t) {
127
118
  function ie({ mode: t }) {
128
119
  return /* @__PURE__ */ e.createElement("span", { className: `badge mode-${t ?? "none"}` }, t ?? "—");
129
120
  }
130
- function ce({ def: t, optKey: a, settings: n, setSettings: r, disabled: l, customWidgets: p }) {
131
- const d = q(n, a), i = (c) => r((b) => ({ ...b, [a]: c })), u = p?.[a] ?? K[a];
132
- if (u) return /* @__PURE__ */ e.createElement(u, { def: t, value: d, onChange: i, disabled: l });
121
+ function ce({ def: t, optKey: a, settings: r, setSettings: o, disabled: i, customWidgets: u }) {
122
+ const d = I(r, a), c = (l) => o((b) => ({ ...b, [a]: l })), p = u?.[a] ?? K[a];
123
+ if (p) return /* @__PURE__ */ e.createElement(p, { def: t, value: d, onChange: c, disabled: i });
133
124
  const x = le(t), g = Array.isArray(d) ? d[0] : d;
134
125
  switch (x) {
135
126
  case "select":
136
- return /* @__PURE__ */ e.createElement("select", { value: g ?? "", onChange: (c) => i(c.target.value), disabled: l }, t.enum_values.map((c, b) => /* @__PURE__ */ e.createElement("option", { key: c, value: c }, t.enum_labels?.[b] ?? c)));
127
+ return /* @__PURE__ */ e.createElement("select", { value: g ?? "", onChange: (l) => c(l.target.value), disabled: i }, t.enum_values.map((l, b) => /* @__PURE__ */ e.createElement("option", { key: l, value: l }, t.enum_labels?.[b] ?? l)));
137
128
  case "checkbox":
138
- return /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: Array.isArray(d) ? !!d[0] : !!d, onChange: (c) => i(c.target.checked), disabled: l });
129
+ return /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: Array.isArray(d) ? !!d[0] : !!d, onChange: (l) => c(l.target.checked), disabled: i });
139
130
  case "color":
140
- return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof g == "string" && g.startsWith("#") ? g : "#00ae42", onChange: (c) => i(c.target.value), disabled: l });
131
+ return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof g == "string" && g.startsWith("#") ? g : H, onChange: (l) => c(l.target.value), disabled: i });
141
132
  case "textarea":
142
133
  return /* @__PURE__ */ e.createElement("textarea", { rows: 2, value: Array.isArray(d) ? d.join(`
143
- `) : d ?? "", onChange: (c) => i(c.target.value), disabled: l });
134
+ `) : d ?? "", onChange: (l) => c(l.target.value), disabled: i });
144
135
  case "points":
145
136
  return /* @__PURE__ */ e.createElement("code", { className: "pts" }, JSON.stringify(d ?? []));
146
137
  case "unknown":
147
138
  return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "No schema");
148
139
  default:
149
- return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value: g ?? "", onChange: (c) => i(c.target.value), disabled: l }), t.sidetext && /* @__PURE__ */ e.createElement("span", { className: "unit" }, t.sidetext));
140
+ return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value: g ?? "", onChange: (l) => c(l.target.value), disabled: i }), t.sidetext && /* @__PURE__ */ e.createElement("span", { className: "unit" }, t.sidetext));
150
141
  }
151
142
  }
152
- function de({ schema: t, optKey: a, settings: n, setSettings: r, disabled: l, onOptionOpen: p, customWidgets: d, overridden: i, onRevertKey: u }) {
143
+ function de({ schema: t, optKey: a, settings: r, setSettings: o, disabled: i, onOptionOpen: u, customWidgets: d, overridden: c, onRevertKey: p }) {
153
144
  if (a.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙ Custom widget ", a));
154
- const x = t[a], g = l ? l[a] : void 0, c = g != null, b = oe(t, n, a), C = () => r((y) => {
155
- const N = { ...y };
156
- return delete N[a], N;
157
- }), E = x?.label || x?.full_label || a;
158
- return /* @__PURE__ */ e.createElement("div", { className: "row" + (c ? " disabled" : ""), title: c ? `Disabled when: ${g}` : x?.tooltip ?? "", "data-testid": `row-${a}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, i && /* @__PURE__ */ e.createElement(
145
+ const x = t[a], g = i ? i[a] : void 0, l = g != null, b = oe(t, r, a), k = () => o((v) => {
146
+ const _ = { ...v };
147
+ return delete _[a], _;
148
+ }), w = x?.label || x?.full_label || a;
149
+ return /* @__PURE__ */ e.createElement("div", { className: "row" + (l ? " disabled" : ""), title: l ? `Disabled when: ${g}` : x?.tooltip ?? "", "data-testid": `row-${a}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, c && /* @__PURE__ */ e.createElement(
159
150
  "button",
160
151
  {
161
152
  type: "button",
162
153
  className: "override-dot",
163
- onClick: () => u?.(a),
154
+ onClick: () => p?.(a),
164
155
  title: "This plate's override — click to follow the global value again",
165
156
  "data-testid": `override-${a}`
166
157
  }
167
- ), b && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "Changed from default", "data-testid": `dirty-${a}` }), p ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => p(a), title: "Details" }, E) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, E)), /* @__PURE__ */ e.createElement("code", { className: "key" }, a), /* @__PURE__ */ e.createElement(ie, { mode: x?.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(ce, { def: x, optKey: a, settings: n, setSettings: r, disabled: c, customWidgets: d }), b && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: C, title: "Reset to default", "data-testid": `reset-${a}` }, "↺")));
158
+ ), b && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "Changed from default", "data-testid": `dirty-${a}` }), u ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => u(a), title: "Details" }, w) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, w)), /* @__PURE__ */ e.createElement("code", { className: "key" }, a), /* @__PURE__ */ e.createElement(ie, { mode: x?.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(ce, { def: x, optKey: a, settings: r, setSettings: o, disabled: l, customWidgets: d }), b && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: k, title: "Reset to default", "data-testid": `reset-${a}` }, "↺")));
168
159
  }
169
- function xe({
170
- schema: t = U,
160
+ function fe({
161
+ schema: t = R,
171
162
  uiTree: a = ae,
172
- toggle: n = ne,
173
- settings: r,
174
- setSettings: l,
175
- onOptionOpen: p,
163
+ toggle: r = re,
164
+ settings: o,
165
+ setSettings: i,
166
+ onOptionOpen: u,
176
167
  embedded: d = !1,
177
- customWidgets: i,
178
- only: u,
168
+ customWidgets: c,
169
+ only: p,
179
170
  overriddenKeys: x,
180
171
  onRevertKey: g
181
172
  }) {
182
- const c = j(() => re(a), [a]), [b, C] = w(u?.builder ?? c[0] ?? ""), [E, y] = w(0), [N, _] = w("all"), [v, o] = w(""), m = G(r), h = c.filter((s) => te(s) === m), A = u?.builder ?? (h.includes(b) ? b : h[0] ?? ""), M = a[u?.builder ?? A] ?? [], z = u?.page ? M.filter((s) => s.page === u.page) : M, O = z[Math.min(E, z.length - 1)], P = j(() => n.disabledKeys(n.makeCfg(r)), [r, n]), k = v.trim().toLowerCase(), S = j(() => k ? Object.entries(t).filter(([s, f]) => s.includes(k) || f.label?.toLowerCase().includes(k) || f.full_label?.toLowerCase().includes(k)).slice(0, 60).map(([s]) => s) : [], [k]), D = (s, f) => /* @__PURE__ */ e.createElement(
173
+ const l = j(() => ne(a), [a]), [b, k] = y(p?.builder ?? l[0] ?? ""), [w, v] = y(0), [_, z] = y("all"), [E, n] = y(""), m = W(o), f = l.filter((s) => te(s) === m), S = p?.builder ?? (f.includes(b) ? b : f[0] ?? ""), M = a[p?.builder ?? S] ?? [], A = p?.page ? M.filter((s) => s.page === p.page) : M, O = A[Math.min(w, A.length - 1)], J = j(() => r.disabledKeys(r.makeCfg(o)), [o, r]), N = E.trim().toLowerCase(), C = j(() => N ? Object.entries(t).filter(([s, h]) => s.includes(N) || h.label?.toLowerCase().includes(N) || h.full_label?.toLowerCase().includes(N)).slice(0, 60).map(([s]) => s) : [], [N]), D = (s, h) => /* @__PURE__ */ e.createElement(
183
174
  de,
184
175
  {
185
176
  schema: t,
186
- key: s + f,
177
+ key: s + h,
187
178
  optKey: s,
188
- settings: r,
189
- setSettings: l,
190
- disabled: P,
191
- onOptionOpen: p,
192
- customWidgets: i,
179
+ settings: o,
180
+ setSettings: i,
181
+ disabled: J,
182
+ onOptionOpen: u,
183
+ customWidgets: c,
193
184
  overridden: x?.includes(s),
194
185
  onRevertKey: g
195
186
  }
196
187
  );
197
- return /* @__PURE__ */ e.createElement(X, { css: Y, className: d ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !u && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: `Search options (${Object.keys(t).length})…`, value: v, onChange: (s) => o(s.target.value), "data-testid": "opt-search" }), !k && /* @__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: A, onChange: (s) => {
198
- C(s.target.value), y(0);
199
- } }, h.map((s) => /* @__PURE__ */ e.createElement("option", { key: s, value: s }, B(s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, z.map((s, f) => /* @__PURE__ */ e.createElement("button", { key: s.page + f, className: f === Math.min(E, z.length - 1) ? "on" : "", onClick: () => y(f) }, s.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, ee.map((s) => /* @__PURE__ */ e.createElement("button", { key: s, className: N === s ? "on" : "", onClick: () => _(s) }, s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, k ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "Search: “", v, "” — ", S.length, " hit", S.length === 1 ? "" : "s", S.length === 60 ? "+" : ""), S.map(D), !S.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No matches")) : O ? /* @__PURE__ */ e.createElement(e.Fragment, null, !u && /* @__PURE__ */ e.createElement("h2", null, O.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", O.line)), O.groups.map((s) => {
200
- const f = s.options.filter((F) => se(t, F, N) && !(u && F.startsWith("<")));
201
- return f.length ? /* @__PURE__ */ e.createElement("section", { key: s.group + s.line }, /* @__PURE__ */ e.createElement("h3", null, s.group || "(untitled group)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", s.line)), f.map(D)) : null;
188
+ return /* @__PURE__ */ e.createElement(X, { css: Q + Y, className: d ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !p && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: `Search options (${Object.keys(t).length})…`, value: E, onChange: (s) => n(s.target.value), "data-testid": "opt-search" }), !N && /* @__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: S, onChange: (s) => {
189
+ k(s.target.value), v(0);
190
+ } }, f.map((s) => /* @__PURE__ */ e.createElement("option", { key: s, value: s }, B(s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, A.map((s, h) => /* @__PURE__ */ e.createElement("button", { key: s.page + h, className: h === Math.min(w, A.length - 1) ? "on" : "", onClick: () => v(h) }, s.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, ee.map((s) => /* @__PURE__ */ e.createElement("button", { key: s, className: _ === s ? "on" : "", onClick: () => z(s) }, s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, N ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "Search: “", E, "” — ", C.length, " hit", C.length === 1 ? "" : "s", C.length === 60 ? "+" : ""), C.map(D), !C.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No matches")) : O ? /* @__PURE__ */ e.createElement(e.Fragment, null, !p && /* @__PURE__ */ e.createElement("h2", null, O.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", O.line)), O.groups.map((s) => {
191
+ const h = s.options.filter((F) => se(t, F, _) && !(p && F.startsWith("<")));
192
+ return h.length ? /* @__PURE__ */ e.createElement("section", { key: s.group + s.line }, /* @__PURE__ */ e.createElement("h3", null, s.group || "(untitled group)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", s.line)), h.map(D)) : null;
202
193
  })) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No builder"))));
203
194
  }
204
195
  export {
205
- xe as default
196
+ fe as default
206
197
  };