three-slicer 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.
Files changed (44) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +13 -8
  3. package/THIRD-PARTY-NOTICES.md +7 -2
  4. package/components/dist/SettingsPanel.js +12 -198
  5. package/data/config-schema-lean.json +1 -0
  6. package/data/processes.js +1 -1
  7. package/data/ui-tree-keys.json +1 -0
  8. package/engine/src/client.js +9 -0
  9. package/engine/src/data.js +6 -0
  10. package/engine/src/settings.js +12 -627
  11. package/engine/src/sla_core.js +14 -8
  12. package/engine/src/toggle_eval.js +9 -77
  13. package/package.json +24 -17
  14. package/types/client.d.ts +3 -0
  15. package/types/components.d.ts +3 -29
  16. package/types/settings.d.ts +15 -0
  17. package/types/toggle.d.ts +2 -22
  18. package/types/viewer-gcode.d.ts +2 -29
  19. package/types/viewer-loaders.d.ts +1 -33
  20. package/types/viewer-toolpath.d.ts +3 -113
  21. package/types/viewer.d.ts +6 -213
  22. package/viewer/dist/Viewport.js +9 -6571
  23. package/viewer/dist/gcode_reexport.js +1 -0
  24. package/viewer/dist/loaders_reexport.js +1 -0
  25. package/viewer/dist/toolpath_reexport.js +1 -0
  26. package/components/README.md +0 -44
  27. package/components/styles.css +0 -88
  28. package/engine/src/kernel_setting_keys.js +0 -132
  29. package/engine/src/preset_file.js +0 -99
  30. package/types/.omc/state/sessions/02e5ddf5-4378-46e1-8fca-e90412c30804/pre-tool-advisory-throttle.json +0 -10
  31. package/viewer/README.md +0 -423
  32. package/viewer/dist/gcode_parse.js +0 -190
  33. package/viewer/dist/make_worker.js +0 -31
  34. package/viewer/dist/model_loaders.js +0 -184
  35. package/viewer/dist/parse_3mf-C2X_W442.js +0 -300
  36. package/viewer/dist/parse_3mf.worker.js +0 -10
  37. package/viewer/dist/sl1_encode.worker.js +0 -10
  38. package/viewer/dist/sl1_write-CtX8cVL7.js +0 -302
  39. package/viewer/dist/sla_reconstruct-CXkYhux2.js +0 -176
  40. package/viewer/dist/sla_reconstruct.worker.js +0 -24
  41. package/viewer/dist/sla_slice.worker.js +0 -74
  42. package/viewer/dist/toolpath_gpu-4foc4FaU.js +0 -482
  43. package/viewer/dist/toolpath_gpu.js +0 -13
  44. package/viewer/styles.css +0 -394
@@ -0,0 +1 @@
1
+ export * from "three-slicer-viewer/gcode";
@@ -0,0 +1 @@
1
+ export * from "three-slicer-viewer/loaders";
@@ -0,0 +1 @@
1
+ export * from "three-slicer-viewer/toolpath";
@@ -1,44 +0,0 @@
1
- # three-slicer/components
2
-
3
- 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
-
5
- ## `<SettingsPanel/>`
6
-
7
- An OrcaSlicer-style settings form generated from `three-slicer/data` (schema + UI tree + toggle rules): tab/page/group navigation, mode filter (all/simple/advanced/expert/develop), search across 976 options, dirty markers with per-option reset, and enable/disable rules evaluation.
8
-
9
- The tab set follows the printer profile's technology: a settings map whose `printer_technology` says SLA swaps the FFF tabs for the SLA ones (print/material/printer builders carry the technology in their upstream builder name, which is what the filter reads). No prop needed — change the profile in the shared settings map and the panel follows.
10
-
11
- ```bash
12
- npm i three-slicer react react-dom # react is an OPTIONAL peer — npm does not install it for you
13
- ```
14
-
15
- ```jsx
16
- import { useState } from 'react'
17
- import SettingsPanel from 'three-slicer/components'
18
-
19
- function App() {
20
- const [settings, setSettings] = useState({}) // sparse map: edited keys only; missing = schema default
21
- return <SettingsPanel settings={settings} setSettings={setSettings} />
22
- }
23
- ```
24
-
25
- Props:
26
-
27
- | Prop | Type | Description |
28
- |---|---|---|
29
- | `settings` | `{ [schemaKey]: value }` | Sparse settings map — the consumer's state is the single source of truth |
30
- | `setSettings` | `(updater) => void` | React setState-style updater; the only way state leaves the component |
31
- | `onOptionOpen` | `(key) => void` (optional) | Label click hook for deep links/detail views. Omit → plain labels |
32
- | `embedded` | `boolean` (optional) | Compact layout for embedding inside another panel |
33
- | `customWidgets` | `{ [schemaKey]: Component }` (optional) | Replace schema-driven widgets per key (e.g. `{ printable_area: MyBedEditor }`) |
34
- | `only` | `{ builder, page? }` (optional) | Pins the panel to one builder/page and drops the search/page/mode chrome — how `<Viewport/>`'s `motionPanel` slot is meant to be filled |
35
-
36
- The same `settings` object feeds `deriveKernelParams()` in `three-slicer` and the `<Viewport/>` in `three-slicer/viewer` — one state connects all three.
37
-
38
- Ships transpiled ESM (`dist/`); react is an optional peer dependency. The panel arrives fully styled: its stylesheet is inlined into the bundle and injected into a Shadow DOM root, so host CSS neither styles it nor collides with it.
39
-
40
- The 3D viewport lives in its own package: `three-slicer/viewer`.
41
-
42
- ## License
43
-
44
- AGPL-3.0-or-later (derived from OrcaSlicer).
@@ -1,88 +0,0 @@
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
- :host { display: contents; }
5
- * { box-sizing: border-box; }
6
-
7
- /* ---- Option row atoms ---- */
8
- .row { display: grid; grid-template-columns: 260px 220px 70px 1fr; gap: 8px;
9
- align-items: center; padding: 3px 0; border-bottom: 1px dashed #f0f0f0; }
10
- .row .lbl { color: #222; text-decoration: none; }
11
- .row .lbl:hover { color: #0a7d4f; text-decoration: underline; }
12
- .row .key { font-size: 11px; color: #999; overflow: hidden; text-overflow: ellipsis; }
13
- .row input[type="text"], .row input:not([type]), .row select, .row textarea {
14
- width: 100%; max-width: 260px; padding: 2px 6px; }
15
- .inputwrap { display: inline-flex; align-items: center; gap: 4px; }
16
- .unit { color: #888; font-size: 12px; white-space: nowrap; }
17
- .pts { font-size: 11px; color: #666; }
18
-
19
- .badge { font-size: 10px; padding: 1px 6px; border-radius: 8px; color: #fff; text-align: center; }
20
- .mode-simple { background: #2e9e5b; } .mode-advanced { background: #3673c3; }
21
- .mode-expert { background: #d9822b; } .mode-develop { background: #c0392b; }
22
- .mode-none, .mode-undefined { background: #aaa; }
23
- .muted { color: #999; font-weight: 400; font-size: 12px; }
24
- .row.custom { grid-template-columns: 1fr; }
25
-
26
- /* Stage 25 S5: toggle-rules disabling + dirty/reset */
27
- .row.disabled { opacity: 0.42; }
28
- .row.disabled input, .row.disabled select, .row.disabled textarea { cursor: not-allowed; background: #f2f2f2; }
29
- .lbl-cell { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
30
- .lbl-cell .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
31
- .dirty-dot { flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: #e8873a; box-shadow: 0 0 0 2px #fff3e6; }
32
- /* Per-plate override marker: this value is the PLATE's, not the global one; clicking follows global again. */
33
- .override-dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: #f0a542; border: 0; padding: 0;
34
- cursor: pointer; box-shadow: 0 0 0 2px #fdf1dd; }
35
- .override-dot:hover { background: #d98c26; }
36
- .widget-cell { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
37
- .reset-btn { flex: 0 0 auto; border: 1px solid #d8b48a; background: #fbf1e4; color: #b5701f;
38
- border-radius: 5px; cursor: pointer; font-size: 13px; line-height: 1; padding: 1px 5px; }
39
- .reset-btn:hover { background: #f3e2cb; }
40
-
41
- /* ---- Panel shell ---- */
42
- .settings-panel { flex: 0 0 400px; width: 400px; border-left: 1px solid #e2e2e2;
43
- display: flex; flex-direction: column; min-height: 0; background: #fff; color: #222;
44
- font: 14px/1.5 -apple-system, "Segoe UI", sans-serif; }
45
- .settings-panel .sp-top { padding: 10px 12px 8px; border-bottom: 1px solid #eee; }
46
- .sp-builder { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #666; }
47
- .sp-builder select { flex: 1; padding: 4px 6px; }
48
- .sp-pages { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
49
- .sp-pages button { border: 1px solid #e0e0e0; background: #fafafa; border-radius: 6px;
50
- padding: 4px 8px; font-size: 12px; cursor: pointer; }
51
- .sp-pages button.on { background: #e6f5ee; color: #0a7d4f; border-color: #b7e2cd; font-weight: 700; }
52
- .sp-modes { display: flex; gap: 4px; margin-top: 8px; }
53
- .sp-modes button { border: 0; background: none; font-size: 11px; color: #888;
54
- padding: 2px 6px; border-radius: 6px; cursor: pointer; }
55
- .sp-modes button.on { background: #eef2f0; color: #0a7d4f; font-weight: 700; }
56
- .settings-panel .sp-body { flex: 1 1 auto; overflow-y: auto; padding: 8px 14px 24px; }
57
- .settings-panel .sp-body h2 { font-size: 16px; }
58
-
59
- /* The sidebar is narrow, so option rows are re-stacked vertically */
60
- .settings-panel .row { grid-template-columns: 1fr auto; grid-template-areas: "lbl badge" "key widget";
61
- row-gap: 2px; padding: 6px 0; }
62
- .settings-panel .row .lbl { grid-area: lbl; font-weight: 600; }
63
- .settings-panel .row .badge { grid-area: badge; justify-self: end; }
64
- .settings-panel .row .key { grid-area: key; }
65
- .settings-panel .row .inputwrap, .settings-panel .row > select,
66
- .settings-panel .row > input, .settings-panel .row > textarea,
67
- .settings-panel .row > .pts, .settings-panel .row > .muted { grid-area: widget; justify-self: end; }
68
- .settings-panel .row input:not([type="checkbox"]):not([type="color"]),
69
- .settings-panel .row select, .settings-panel .row textarea { max-width: 200px; }
70
- .settings-panel .row.custom { grid-template-columns: 1fr; grid-template-areas: "lbl"; }
71
- .sp-search { width: 100%; box-sizing: border-box; padding: 6px 9px; border: 1px solid #d6d6d6; border-radius: 6px; font-size: 13px; margin-bottom: 8px; }
72
-
73
- /* ---- Embedded variant (sp-embedded on :host — full width inside cards such as the viewer process-card) ---- */
74
- :host(.sp-embedded) .settings-panel { position: static; width: auto; flex: none; border: 0; height: auto; }
75
- :host(.sp-embedded) .sp-body { padding: 4px 2px 8px; max-height: 460px; }
76
-
77
- /* ---- BedShapeEditor (printable_area custom widget) ---- */
78
- .bse { display: flex; flex-direction: column; gap: 6px; min-width: 0; max-width: 220px; }
79
- .bse-modes { display: flex; gap: 4px; }
80
- .bse-modes button { border: 1px solid #e0e0e0; background: #fafafa; border-radius: 6px;
81
- padding: 2px 8px; font-size: 11px; cursor: pointer; }
82
- .bse-modes button.on { background: #e6f5ee; color: #0a7d4f; border-color: #b7e2cd; font-weight: 700; }
83
- .bse-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 8px; }
84
- .bse-grid label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: #666; white-space: nowrap; }
85
- .bse-grid input { width: 56px; padding: 2px 4px; font-size: 12px; }
86
- .bse-json { width: 100%; font: 11px ui-monospace, monospace; padding: 4px 6px;
87
- border: 1px solid #d6d6d6; border-radius: 6px; box-sizing: border-box; }
88
- .bse-json.err { border-color: #d0435a; background: #fdf3f4; }
@@ -1,132 +0,0 @@
1
- // AUTO-GENERATED by types/gen_kernel_params.mjs — DO NOT EDIT.
2
- // The schema keys deriveKernelParams consumes (the distinct "From setting" column of engine/PARAMS.md).
3
- export const KERNEL_SETTING_KEYS = [
4
- "bottom_shell_layers",
5
- "bottom_surface_filament_id",
6
- "bridge_no_support",
7
- "bridge_speed",
8
- "brim_object_gap",
9
- "brim_width",
10
- "close_fan_the_first_x_layers",
11
- "default_acceleration",
12
- "enable_arc_fitting",
13
- "enable_pressure_advance",
14
- "enable_prime_tower",
15
- "enable_support",
16
- "fan_max_speed",
17
- "filament_cost",
18
- "filament_density",
19
- "filament_diameter",
20
- "filament_flow_ratio",
21
- "filament_map",
22
- "filament_retraction_length",
23
- "filament_retraction_minimum_travel",
24
- "filament_retraction_speed",
25
- "filament_settings_id",
26
- "filament_type",
27
- "filament_z_hop",
28
- "flush_into_infill",
29
- "flush_multiplier",
30
- "flush_volumes_matrix",
31
- "full_fan_speed_layer",
32
- "hot_plate_temp",
33
- "independent_support_layer_height",
34
- "infill_direction",
35
- "initial_layer_line_width",
36
- "initial_layer_print_height",
37
- "initial_layer_speed",
38
- "inner_wall_filament_id",
39
- "inner_wall_line_width",
40
- "internal_solid_filament_id",
41
- "internal_solid_infill_line_width",
42
- "ironing_flow",
43
- "ironing_spacing",
44
- "ironing_speed",
45
- "ironing_type",
46
- "layer_height",
47
- "line_width",
48
- "machine_end_gcode",
49
- "machine_max_acceleration_e",
50
- "machine_max_acceleration_retracting",
51
- "machine_max_acceleration_x",
52
- "machine_max_acceleration_z",
53
- "machine_max_jerk_e",
54
- "machine_max_jerk_x",
55
- "machine_max_jerk_z",
56
- "machine_max_speed_e",
57
- "machine_max_speed_x",
58
- "machine_max_speed_z",
59
- "machine_start_gcode",
60
- "max_volumetric_extrusion_rate_slope",
61
- "nozzle_diameter",
62
- "nozzle_temperature",
63
- "outer_wall_filament_id",
64
- "outer_wall_line_width",
65
- "outer_wall_speed",
66
- "pressure_advance",
67
- "prime_tower_width",
68
- "prime_volume",
69
- "printable_area",
70
- "printable_height",
71
- "raft_contact_distance",
72
- "raft_expansion",
73
- "raft_layers",
74
- "reduce_crossing_wall",
75
- "resolution",
76
- "retraction_length",
77
- "retraction_minimum_travel",
78
- "retraction_speed",
79
- "seam_position",
80
- "seam_slope_type",
81
- "skirt_distance",
82
- "skirt_height",
83
- "skirt_loops",
84
- "slow_down_layer_time",
85
- "sparse_infill_density",
86
- "sparse_infill_filament_id",
87
- "sparse_infill_line_width",
88
- "sparse_infill_pattern",
89
- "spiral_mode",
90
- "support_angle",
91
- "support_base_pattern",
92
- "support_base_pattern_spacing",
93
- "support_bottom_z_distance",
94
- "support_expansion",
95
- "support_filament",
96
- "support_interface_bottom_layers",
97
- "support_interface_filament",
98
- "support_interface_pattern",
99
- "support_interface_spacing",
100
- "support_interface_top_layers",
101
- "support_line_width",
102
- "support_object_first_layer_gap",
103
- "support_object_xy_distance",
104
- "support_on_build_plate_only",
105
- "support_remove_small_overhang",
106
- "support_style",
107
- "support_threshold_angle",
108
- "support_threshold_overlap",
109
- "support_top_z_distance",
110
- "support_type",
111
- "top_shell_layers",
112
- "top_surface_filament_id",
113
- "top_surface_line_width",
114
- "travel_acceleration",
115
- "travel_speed",
116
- "tree_support_angle_slow",
117
- "tree_support_branch_angle",
118
- "tree_support_branch_angle_organic",
119
- "tree_support_branch_diameter",
120
- "tree_support_branch_diameter_angle",
121
- "tree_support_branch_diameter_organic",
122
- "tree_support_branch_distance",
123
- "tree_support_branch_distance_organic",
124
- "tree_support_tip_diameter",
125
- "tree_support_top_rate",
126
- "tree_support_wall_count",
127
- "wall_generator",
128
- "wall_loops",
129
- "wipe_tower_x",
130
- "wipe_tower_y",
131
- "z_hop",
132
- ]
@@ -1,99 +0,0 @@
1
- // OrcaSlicer preset files — the `.json` its "Config files (*.json;…)" import accepts, and that its
2
- // "Export preset" writes. One file holds one preset of one type: machine, process or filament.
3
- //
4
- // The value encoding is the same one `project_settings.config` uses, and for the same reason: upstream writes
5
- // every option through ConfigBase::save_to_json, which serializes each one to a string (a bool is "1"/"0", a point
6
- // is "XxY", a vector becomes an array of strings). So normalize/serializeProjectSettings are reused verbatim
7
- // rather than reimplemented — a second copy of that coercion table is exactly how the two would drift.
8
- //
9
- // Zero dependencies, so this lives in the engine and works headless. The zip bundle formats (.orca_printer) need
10
- // a deflate and therefore live in the viewer, which already has one.
11
- import { schema, presetKeys } from './data.js'
12
- import { normalizeProjectSettings, serializeProjectSettings } from './settings.js'
13
-
14
- /** `machine` for printers, `process` for print settings, `filament` for materials — upstream's own `type` field. */
15
- const KEY_LIST = { machine: 'printer', process: 'process', filament: 'filament' }
16
-
17
- // Preset bookkeeping, not settings. Upstream writes six of these; `inherits` is read (it names the parent) and the
18
- // rest are carried only so a round trip does not lose them.
19
- const ENVELOPE = new Set(['type', 'name', 'from', 'version', 'setting_id', 'instantiation', 'inherits',
20
- 'filament_id', 'is_custom_defined', 'filament_settings_id', 'print_settings_id', 'printer_settings_id'])
21
-
22
- /** Every option key that belongs in a preset of this type. */
23
- export function presetOptionKeys(type) {
24
- const list = presetKeys[KEY_LIST[type]]
25
- if (!list) throw new Error(`unknown preset type '${type}' (expected machine, process or filament)`)
26
- return list
27
- }
28
-
29
- /**
30
- * A settings map -> the object an OrcaSlicer preset `.json` holds.
31
- *
32
- * Written **flattened**: no `inherits`, every key present. That is the whole difference between a file another
33
- * program can read on its own and one that only means something next to the vendor database it was derived from —
34
- * upstream's own exports are diffs against a parent (`Preset::save`: "only save difference if it has parent"),
35
- * which is fine for OrcaSlicer reading its own files back and useless for anyone else.
36
- *
37
- * `complete: false` writes only the keys the settings map actually holds, for a diff-shaped file.
38
- */
39
- export function writePresetFile(settings, { type = 'machine', name = 'Custom', complete = true, version = '1.0.0.0' } = {}) {
40
- const keys = presetOptionKeys(type)
41
- const picked = {}
42
- for (const key of keys) {
43
- // `inherits` is in upstream's printer_options list — it is an option to OrcaSlicer's config machinery, but it
44
- // is bookkeeping here, and writing it would put an empty parent name in a file meant to stand alone. The same
45
- // ENVELOPE set the reader strips is what the writer refuses to emit, so the two cannot disagree about it.
46
- if (ENVELOPE.has(key)) continue
47
- if (settings && key in settings && settings[key] != null) picked[key] = settings[key]
48
- // A key with no schema default cannot be filled — there is nothing to fill it with. `complete` means "every
49
- // option that HAS a value", not "every option name", and inventing one would be worse than leaving it out.
50
- else if (complete && schema[key]?.default !== undefined) picked[key] = schema[key].default
51
- }
52
- return {
53
- type, name, from: 'User', version, instantiation: 'true',
54
- ...serializeProjectSettings(picked),
55
- }
56
- }
57
-
58
- /**
59
- * The inverse: a parsed preset `.json` -> a settings map this package accepts.
60
- *
61
- * `inherits` is the trap. Upstream stores a derived preset as the DIFF against its parent, so a vendor file on its
62
- * own carries a fraction of the preset — the Bambu X1C machine json inherits `fdm_bbl_3dp_001_common` and lists
63
- * about twenty keys. Resolution therefore needs a parent lookup, and this package has no full preset database:
64
- * `resolveParent(name)` is the hook, and `printerSettings` covers the machine case for the keys the kernel reads.
65
- *
66
- * When the parent cannot be resolved the file's own values are still applied and `missingParent` names it —
67
- * dropping a whole file because one name is unknown loses more than it protects, and the caller can decide.
68
- */
69
- export function readPresetFile(raw, { resolveParent } = {}) {
70
- if (!raw || typeof raw !== 'object') throw new Error('not a preset object')
71
- const type = raw.type
72
- if (!KEY_LIST[type]) throw new Error(`not an OrcaSlicer preset: type is ${JSON.stringify(type)}`)
73
-
74
- const own = {}
75
- for (const [key, value] of Object.entries(raw)) if (!ENVELOPE.has(key)) own[key] = value
76
- const { settings, applied, skipped } = normalizeProjectSettings(own)
77
-
78
- // The parent goes UNDER the file's own values — the file is the diff, so it wins.
79
- let missingParent = null
80
- let merged = settings
81
- if (raw.inherits) {
82
- const parent = resolveParent?.(raw.inherits, type) ?? null
83
- if (parent) merged = { ...parent, ...settings }
84
- else missingParent = raw.inherits
85
- }
86
-
87
- return {
88
- settings: merged,
89
- type,
90
- name: typeof raw.name === 'string' ? raw.name : '',
91
- inherits: raw.inherits ?? null,
92
- missingParent,
93
- applied,
94
- skipped,
95
- }
96
- }
97
-
98
- /** Convenience: the JSON text an export writes. */
99
- export const presetFileText = (settings, options) => JSON.stringify(writePresetFile(settings, options), null, 1)
@@ -1,10 +0,0 @@
1
- {
2
- "version": 1,
3
- "entries": {
4
- "79a93d4a2f8f50b95f852280616242fee1855dc99a3c75211917f55e72e95fae": {
5
- "last_emitted_at_ms": 1788437194779,
6
- "message": "Use parallel execution for independent tasks. Use run_in_background for long operations (npm install, builds, tests)."
7
- }
8
- },
9
- "updated_at": "2026-09-03T12:06:34.779Z"
10
- }