printbench 0.6.5 → 0.8.0

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.
@@ -0,0 +1,294 @@
1
+ import { Vector3 as Y, LoadingManager as N, Color as O } from "three";
2
+ import { DRACOLoader as _ } from "three/examples/jsm/loaders/DRACOLoader.js";
3
+ import { GLTFLoader as z } from "three/examples/jsm/loaders/GLTFLoader.js";
4
+ import { MeshoptDecoder as B } from "three/examples/jsm/libs/meshopt_decoder.module.js";
5
+ import { STLLoader as W } from "three/examples/jsm/loaders/STLLoader.js";
6
+ import { OBJLoader as $ } from "three/examples/jsm/loaders/OBJLoader.js";
7
+ import { ThreeMFLoader as j } from "three/examples/jsm/loaders/3MFLoader.js";
8
+ import A from "jszip";
9
+ import { w as D, u as F } from "./index-Ci-Js4Ma.js";
10
+ import { A as he } from "./index-Ci-Js4Ma.js";
11
+ import { parse3mfMeshObjects as S } from "./3mf-BC6RlyAs.js";
12
+ const I = new URL("../assets/draco_wasm_wrapper.js", import.meta.url).href, X = new URL("../assets/draco_decoder.wasm", import.meta.url).href;
13
+ let b = null;
14
+ function G(e) {
15
+ return e.split("?")[0]?.split("/").pop() ?? e;
16
+ }
17
+ function J() {
18
+ const e = new N();
19
+ e.setURLModifier((a) => {
20
+ const m = G(a);
21
+ return m === "draco_wasm_wrapper.js" ? I : m === "draco_decoder.wasm" ? X : a;
22
+ });
23
+ const s = new _(e);
24
+ return s.setDecoderConfig({ type: "wasm" }), s.setDecoderPath(""), s;
25
+ }
26
+ function R() {
27
+ const e = new z();
28
+ return b || (b = J()), e.setDRACOLoader(b), e.setMeshoptDecoder(B), e;
29
+ }
30
+ function Z(e) {
31
+ return e ? Array.isArray(e) ? e : [e] : [];
32
+ }
33
+ function x(e) {
34
+ const s = "color" in e ? e.color : void 0;
35
+ return s && s instanceof O ? [s.r, s.g, s.b] : [1, 1, 1];
36
+ }
37
+ function P(e) {
38
+ return ("map" in e ? e.map : void 0) ?? void 0;
39
+ }
40
+ function L(e) {
41
+ return e[0] > 0.97 && e[1] > 0.97 && e[2] > 0.97;
42
+ }
43
+ function k(e) {
44
+ if (!e?.image) return;
45
+ const s = e.image, a = Number(s.width), m = Number(s.height);
46
+ if (!(!a || !m))
47
+ try {
48
+ const r = document.createElement("canvas");
49
+ r.width = a, r.height = m;
50
+ const n = r.getContext("2d");
51
+ return n ? (n.drawImage(e.image, 0, 0, a, m), { canvas: r, flipY: e.flipY }) : void 0;
52
+ } catch {
53
+ return;
54
+ }
55
+ }
56
+ function q(e, s, a, m) {
57
+ const r = (s % 1 + 1) % 1, n = (a % 1 + 1) % 1, u = m ? 1 - n : n, h = Math.min(e.width - 1, Math.max(0, Math.floor(r * e.width))), c = (Math.min(e.height - 1, Math.max(0, Math.floor(u * e.height))) * e.width + h) * 4;
58
+ return [e.data[c] / 255, e.data[c + 1] / 255, e.data[c + 2] / 255];
59
+ }
60
+ function V(e, s, a, m, r, n) {
61
+ const u = new Float32Array(e * 3);
62
+ u.fill(1);
63
+ const h = n.map((c) => {
64
+ if (!c) return;
65
+ const l = c.canvas.getContext("2d");
66
+ if (l)
67
+ return { data: l.getImageData(0, 0, c.canvas.width, c.canvas.height), flipY: c.flipY };
68
+ }), o = (c) => {
69
+ for (const l of m)
70
+ if (c >= l.start && c < l.start + l.count) return l.materialIndex ?? 0;
71
+ return 0;
72
+ };
73
+ for (let c = 0; c < s.length; c += 3) {
74
+ const l = r.length ? Math.max(0, Math.min(r.length - 1, o(c))) : 0, i = h[l], p = r[l] ? x(r[l]) : [1, 1, 1];
75
+ for (let f = 0; f < 3; f++) {
76
+ const d = s[c + f];
77
+ let g = p[0], v = p[1], y = p[2];
78
+ if (i && a && d * 2 + 1 < a.length) {
79
+ const w = q(i.data, a[d * 2], a[d * 2 + 1], i.flipY);
80
+ g *= w[0], v *= w[1], y *= w[2];
81
+ }
82
+ u[d * 3] = g, u[d * 3 + 1] = v, u[d * 3 + 2] = y;
83
+ }
84
+ }
85
+ return u;
86
+ }
87
+ function U(e, s, a, m) {
88
+ const r = e.getAttribute("position");
89
+ if (!r) throw new Error("网格缺少 position 属性");
90
+ const n = e.getIndex(), u = new Float32Array(r.count * 3), h = new Y();
91
+ for (let t = 0; t < r.count; t++)
92
+ h.set(r.getX(t), r.getY(t), r.getZ(t)), a && h.applyMatrix4(a), u[t * 3] = h.x, u[t * 3 + 1] = h.y, u[t * 3 + 2] = h.z;
93
+ let o;
94
+ if (n) {
95
+ o = new Uint32Array(n.count);
96
+ for (let t = 0; t < n.count; t++) o[t] = n.getX(t);
97
+ } else {
98
+ o = new Uint32Array(r.count);
99
+ for (let t = 0; t < r.count; t++) o[t] = t;
100
+ }
101
+ const c = e.getAttribute("color");
102
+ let l;
103
+ if (c) {
104
+ l = new Float32Array(r.count * 3);
105
+ for (let t = 0; t < r.count; t++)
106
+ l[t * 3] = c.getX(t), l[t * 3 + 1] = c.getY(t), l[t * 3 + 2] = c.getZ(t);
107
+ }
108
+ const i = e.getAttribute("uv");
109
+ let p;
110
+ if (i) {
111
+ p = new Float32Array(r.count * 2);
112
+ for (let t = 0; t < r.count; t++)
113
+ p[t * 2] = i.getX(t), p[t * 2 + 1] = i.getY(t);
114
+ }
115
+ const f = Z(m), d = f.map((t) => k(P(t)));
116
+ let g, v, y;
117
+ if (f.length <= 1) {
118
+ if (g = d[0]?.canvas, v = d[0]?.flipY, f[0]) {
119
+ const t = x(f[0]);
120
+ (!L(t) || g) && (y = t);
121
+ }
122
+ } else {
123
+ const t = d.map((M) => M?.canvas).filter((M) => !!M);
124
+ if (new Set(t).size === 1 && t.length === f.length) {
125
+ g = t[0], v = d.find((T) => T)?.flipY;
126
+ const M = x(f[0]);
127
+ (!L(M) || g) && (y = M);
128
+ } else
129
+ l = V(r.count, o, p, e.groups, f, d);
130
+ }
131
+ const w = D(u, o, 1e-5, l, p), E = (() => {
132
+ const t = {};
133
+ if (w.uvs && (t.uvs = w.uvs), w.colors && (t.colors = w.colors), y && (t.baseColor = y), g && (t.texture = g, t.flipY = v), !(!t.texture && !t.colors && !t.baseColor))
134
+ return t;
135
+ })();
136
+ return {
137
+ id: F("mesh"),
138
+ name: s,
139
+ visible: !0,
140
+ buffers: { positions: w.positions, indices: w.indices, colors: w.colors },
141
+ appearance: E
142
+ };
143
+ }
144
+ function H(e, s, a, m) {
145
+ const r = D(s, a, 1e-5);
146
+ return {
147
+ id: F("mesh"),
148
+ name: e,
149
+ visible: !0,
150
+ printColor: m ? [m[0], m[1], m[2]] : void 0,
151
+ buffers: { positions: r.positions, indices: r.indices, colors: r.colors }
152
+ };
153
+ }
154
+ function C(e, s) {
155
+ const a = [];
156
+ let m = 0, r = !1;
157
+ return e.updateMatrixWorld(!0), e.traverse((n) => {
158
+ let u = 0, h = n;
159
+ for (; h; )
160
+ u++, h = h.parent;
161
+ if (m = Math.max(m, u), n.isMesh) {
162
+ const o = n;
163
+ (Array.isArray(o.geometry) ? o.geometry : [o.geometry]).forEach((l, i) => {
164
+ if (!l) return;
165
+ const f = (typeof o.name == "string" ? o.name.trim() : "") || `${s}_${a.length || i}`;
166
+ a.push(U(l, f, o.matrixWorld, o.material));
167
+ }), o.material && (r = !0);
168
+ }
169
+ }), { meshes: a, depth: m, hasMat: r };
170
+ }
171
+ async function K(e) {
172
+ const s = e.name.split(".").pop()?.toLowerCase() ?? "", a = await e.arrayBuffer();
173
+ if (s === "zip") return Q(e, a);
174
+ if (s === "glb" || s === "gltf") {
175
+ const r = await R().parseAsync(a, ""), { meshes: n, depth: u, hasMat: h } = C(r.scene, e.name.replace(/\.[^.]+$/, ""));
176
+ if (!n.length) throw new Error("该文件不包含网格,可能是纯灯光/相机场景");
177
+ const o = n.reduce((l, i) => l + i.buffers.indices.length / 3, 0), c = n.reduce((l, i) => l + i.buffers.positions.length / 3, 0);
178
+ return {
179
+ meshes: n,
180
+ summary: {
181
+ fileName: e.name,
182
+ format: s,
183
+ triangleCount: o,
184
+ vertexCount: c,
185
+ nodeDepth: u,
186
+ hasMaterials: h,
187
+ hasAnimations: (r.animations?.length ?? 0) > 0
188
+ }
189
+ };
190
+ }
191
+ if (s === "stl") {
192
+ const r = new W().parse(a), n = U(r, e.name.replace(/\.[^.]+$/, ""));
193
+ return {
194
+ meshes: [n],
195
+ summary: {
196
+ fileName: e.name,
197
+ format: "stl",
198
+ triangleCount: n.buffers.indices.length / 3,
199
+ vertexCount: n.buffers.positions.length / 3,
200
+ nodeDepth: 1,
201
+ hasMaterials: !1,
202
+ hasAnimations: !1
203
+ }
204
+ };
205
+ }
206
+ if (s === "obj") {
207
+ const m = new TextDecoder().decode(a), n = new $().parse(m), { meshes: u, depth: h, hasMat: o } = C(n, e.name.replace(/\.[^.]+$/, ""));
208
+ if (!u.length) throw new Error("OBJ 中没有可用网格");
209
+ const c = u.reduce((i, p) => i + p.buffers.indices.length / 3, 0), l = u.reduce((i, p) => i + p.buffers.positions.length / 3, 0);
210
+ return {
211
+ meshes: u,
212
+ summary: {
213
+ fileName: e.name,
214
+ format: "obj",
215
+ triangleCount: c,
216
+ vertexCount: l,
217
+ nodeDepth: h,
218
+ hasMaterials: o,
219
+ hasAnimations: !1
220
+ }
221
+ };
222
+ }
223
+ if (s === "3mf") {
224
+ const m = await ee(a), n = (await A.loadAsync(a)).file(/3dmodel\.model$/i)[0], u = n ? await n.async("string") : "", h = u ? S(u) : [];
225
+ let o = [], c = 1, l = !1;
226
+ try {
227
+ const d = new j().parse(a), g = C(d, e.name.replace(/\.[^.]+$/, ""));
228
+ o = g.meshes, c = g.depth, l = g.hasMat;
229
+ } catch {
230
+ o = [];
231
+ }
232
+ if (h.length > o.length ? (o = h.map((f) => H(f.name, f.positions, f.indices, f.printColor)), l = !1, c = 1) : h.length && h.forEach((f, d) => {
233
+ o[d] && (f.name?.trim() && (o[d].name = f.name.trim()), f.printColor && (o[d].printColor = [f.printColor[0], f.printColor[1], f.printColor[2]]));
234
+ }), !o.length) throw new Error("3MF 中没有可用网格");
235
+ const i = o.reduce((f, d) => f + d.buffers.indices.length / 3, 0), p = o.reduce((f, d) => f + d.buffers.positions.length / 3, 0);
236
+ return {
237
+ meshes: o,
238
+ declaredUnit: m,
239
+ summary: {
240
+ fileName: e.name,
241
+ format: "3mf",
242
+ triangleCount: i,
243
+ vertexCount: p,
244
+ nodeDepth: c,
245
+ hasMaterials: l,
246
+ hasAnimations: !1,
247
+ declaredUnit: m
248
+ }
249
+ };
250
+ }
251
+ throw new Error("仅支持 GLB / GLTF / STL / OBJ / 3MF / ZIP");
252
+ }
253
+ async function Q(e, s) {
254
+ const a = await A.loadAsync(s), m = Object.values(a.files).filter(
255
+ (i) => !i.dir && !i.name.startsWith("__MACOSX") && !/(^|\/)\./.test(i.name) && /\.(glb|gltf|stl|obj|3mf)$/i.test(i.name)
256
+ ).sort((i, p) => i.name.localeCompare(p.name, "en"));
257
+ if (!m.length) throw new Error("压缩包中没有 STL / 3MF / OBJ / GLB 模型");
258
+ const r = [];
259
+ let n = !1, u = !1, h = 1, o;
260
+ for (const i of m) {
261
+ const p = await i.async("arraybuffer"), f = i.name.split(/[/\\]/).pop() || i.name, d = await K(new File([p], f));
262
+ r.push(...d.meshes), n = n || d.summary.hasMaterials, u = u || d.summary.hasAnimations, h = Math.max(h, d.summary.nodeDepth), d.declaredUnit && !o && (o = d.declaredUnit);
263
+ }
264
+ if (!r.length) throw new Error("压缩包中没有可用网格");
265
+ const c = r.reduce((i, p) => i + p.buffers.indices.length / 3, 0), l = r.reduce((i, p) => i + p.buffers.positions.length / 3, 0);
266
+ return {
267
+ meshes: r,
268
+ declaredUnit: o,
269
+ summary: {
270
+ fileName: e.name,
271
+ format: "zip",
272
+ triangleCount: c,
273
+ vertexCount: l,
274
+ nodeDepth: h,
275
+ hasMaterials: n,
276
+ hasAnimations: u,
277
+ declaredUnit: o
278
+ }
279
+ };
280
+ }
281
+ async function ee(e) {
282
+ try {
283
+ const a = (await A.loadAsync(e)).file(/3dmodel\.model$/i)[0];
284
+ if (!a) return "mm";
285
+ const n = ((await a.async("string")).match(/unit\s*=\s*"([^"]+)"/i)?.[1] ?? "millimeter").toLowerCase();
286
+ return n.startsWith("micro") ? "mm" : n.startsWith("centi") ? "cm" : n.startsWith("meter") && !n.includes("milli") ? "m" : "mm";
287
+ } catch {
288
+ return "mm";
289
+ }
290
+ }
291
+ export {
292
+ he as ACCEPT_EXT,
293
+ K as loadModelFile
294
+ };
@@ -0,0 +1,49 @@
1
+ import { GLTFLoader as w } from "three/examples/jsm/loaders/GLTFLoader.js";
2
+ import { w as y } from "./index-Ci-Js4Ma.js";
3
+ const A = new URL("../assets/qiu_ao.glb", import.meta.url).href, x = new URL("../assets/qiu_tu_di.glb", import.meta.url).href;
4
+ let l = null, i = null;
5
+ function L(n) {
6
+ n.updateMatrixWorld(!0);
7
+ const r = [], o = [];
8
+ let a = 0;
9
+ if (n.traverse((m) => {
10
+ if (!m.isMesh) return;
11
+ const p = m, u = p.geometry;
12
+ if (!u?.getAttribute("position")) return;
13
+ const s = u.getAttribute("position"), c = u.getIndex(), e = p.matrixWorld.elements;
14
+ for (let t = 0; t < s.count; t++) {
15
+ const f = s.getX(t), d = s.getY(t), g = s.getZ(t);
16
+ r.push(
17
+ e[0] * f + e[4] * d + e[8] * g + e[12],
18
+ e[1] * f + e[5] * d + e[9] * g + e[13],
19
+ e[2] * f + e[6] * d + e[10] * g + e[14]
20
+ );
21
+ }
22
+ if (c)
23
+ for (let t = 0; t < c.count; t++) o.push(a + c.getX(t));
24
+ else
25
+ for (let t = 0; t < s.count; t++) o.push(a + t);
26
+ a += s.count;
27
+ }), !r.length || !o.length) throw new Error("卡扣 GLB 中没有网格");
28
+ return y(new Float32Array(r), new Uint32Array(o));
29
+ }
30
+ async function h(n) {
31
+ const o = await new w().loadAsync(n);
32
+ return L(o.scene);
33
+ }
34
+ async function q() {
35
+ if (l) return l;
36
+ if (i) return i;
37
+ i = (async () => {
38
+ const [n, r] = await Promise.all([h(A), h(x)]);
39
+ return l = { ao: n, tu: r }, l;
40
+ })();
41
+ try {
42
+ return await i;
43
+ } finally {
44
+ i = null;
45
+ }
46
+ }
47
+ export {
48
+ q as loadQiuSnapMeshes
49
+ };
@@ -0,0 +1,44 @@
1
+ import p from "jszip";
2
+ import { Group as u, Mesh as l } from "three";
3
+ import { STLExporter as w } from "three/examples/jsm/exporters/STLExporter.js";
4
+ import { b as m } from "./index-Ci-Js4Ma.js";
5
+ function y(t, o) {
6
+ const e = /* @__PURE__ */ new Set();
7
+ return t.map((n, a) => {
8
+ const f = (n.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/\.+$/g, "").trim() || `part_${a + 1}`).slice(0, 80);
9
+ let r = `${f}.${o}`, i = 2;
10
+ for (; e.has(r.toLowerCase()); )
11
+ r = `${f}_${i}.${o}`, i++;
12
+ return e.add(r.toLowerCase()), r;
13
+ });
14
+ }
15
+ function s(t) {
16
+ const o = new w(), e = new u();
17
+ t.forEach((f) => {
18
+ const r = m(f), i = new l(r);
19
+ e.add(i);
20
+ });
21
+ const n = o.parse(e, { binary: !0 }), a = new Uint8Array(n.byteLength);
22
+ return a.set(new Uint8Array(n.buffer, n.byteOffset, n.byteLength)), a.buffer;
23
+ }
24
+ async function L(t, o, e) {
25
+ if (t.length <= 1)
26
+ return { buf: s(t), name: `${o}.stl`, mime: "model/stl" };
27
+ const n = new p();
28
+ return y(e.length === t.length ? e : t.map((r, i) => `part_${i + 1}`), "stl").forEach((r, i) => {
29
+ const c = s([t[i]]);
30
+ if (!b(c)) throw new Error("导出文件头校验失败");
31
+ n.file(r, c);
32
+ }), { buf: await n.generateAsync({ type: "arraybuffer", compression: "DEFLATE" }), name: `${o}.zip`, mime: "application/zip" };
33
+ }
34
+ function b(t) {
35
+ if (t.byteLength < 84) return !1;
36
+ const e = new DataView(t).getUint32(80, !0);
37
+ return t.byteLength === 84 + e * 50;
38
+ }
39
+ export {
40
+ s as exportBinaryStl,
41
+ L as exportStlPackage,
42
+ y as uniqueExportFileNames,
43
+ b as validateStlHeader
44
+ };
@@ -1 +1 @@
1
- .pb-select{position:relative;display:block;min-width:0;width:100%}.pb-select-trigger{display:flex;align-items:center;gap:6px;width:100%;min-width:0;margin:0;padding:5px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;font:inherit;font-size:12px;line-height:1.3;text-align:left;cursor:pointer;color-scheme:dark}.pb-select-trigger:hover:not(:disabled){border-color:var(--accent)}.pb-select-trigger:disabled{opacity:.45;cursor:not-allowed}.pb-select-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-select-chevron{flex:none;width:12px;height:12px;opacity:.75}.pb-select.open .pb-select-chevron{transform:rotate(180deg)}.pb-select-menu{position:fixed;z-index:80;margin:0;padding:4px;list-style:none;overflow:auto;background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadow);color-scheme:dark;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif;font-size:12px}.pb-select-option{padding:6px 8px;border-radius:5px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-select-option:hover,.pb-select-option.active{background:#5b8def2e}.pb-select-option.selected{color:var(--accent)}.unit-row>span[data-v-d70135e8]{flex:none;white-space:nowrap}.unit-row[data-v-d70135e8] .pb-select{flex:1;min-width:0}.settings-host[data-v-04a6df4b] .dialog{width:min(420px,calc(100% - 24px),92vw)}.row[data-v-04a6df4b]{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:8px 0;font-size:13px}.row .label[data-v-04a6df4b]{flex:1;min-width:0;color:var(--text)}.row[data-v-04a6df4b] .pb-select,.row>input[type=number][data-v-04a6df4b]{flex:0 0 auto;width:140px;max-width:46%}.row>input[type=number][data-v-04a6df4b]{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px;color:var(--text);color-scheme:dark}.section[data-v-04a6df4b]{margin:12px 0;padding-top:10px;border-top:1px solid var(--border)}.section-title[data-v-04a6df4b]{font-size:12px;font-weight:600;color:var(--muted, #9aa3b2);margin-bottom:8px}.section-title .unit[data-v-04a6df4b]{font-weight:400;opacity:.85}.checks[data-v-04a6df4b]{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px;margin-bottom:8px;font-size:13px}.checks label[data-v-04a6df4b]{display:flex;align-items:center;gap:6px;min-width:0}.bed-xyz[data-v-04a6df4b]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.axis[data-v-04a6df4b]{display:flex;flex-direction:column;gap:4px;min-width:0;font-size:11px;color:var(--muted, #9aa3b2)}.axis input[data-v-04a6df4b]{width:100%;box-sizing:border-box;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 6px;color:var(--text);color-scheme:dark}.bed-presets[data-v-04a6df4b]{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.preset[data-v-04a6df4b]{font-size:12px;padding:4px 8px;white-space:nowrap}.warn[data-v-b085aeeb]{color:#e07040;font-weight:600}:root{--bg: #0e1116;--panel: #171b22;--panel-2: #1e242e;--border: #2c3440;--text: #e8edf4;--muted: #8b95a5;--accent: #5b8def;--danger: #ff5c5c;--warn: #f0b429;--ok: #3dcc7a;--shadow: 0 8px 32px rgba(0, 0, 0, .35);font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root,.printbench-root *{box-sizing:border-box}.printbench-root{height:100%;min-height:0;background:var(--bg);color:var(--text);color-scheme:dark;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root button,.printbench-root input,.printbench-root textarea{font:inherit;color:inherit;color-scheme:dark}.workbench{position:relative;display:grid;grid-template-rows:1fr 28px;grid-template-columns:292px 1fr;height:100%}.workbench.no-left{grid-template-columns:36px 1fr}.workbench.left-float{grid-template-columns:1fr}.workbench.left-float>.panel.parts{position:absolute;left:12px;top:12px;width:280px;max-height:calc(100% - 52px);z-index:12;grid-column:1;grid-row:1;border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);background:color-mix(in srgb,var(--panel) 94%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.workbench.left-float>.panel.parts.collapsed{width:36px;max-height:none;bottom:40px}.workbench.left-float>.canvas-wrap{grid-column:1;grid-row:1;min-width:0}.workbench.preview-shell{grid-template-rows:1fr;grid-template-columns:1fr}.item input.swatch{flex:none;width:22px;height:18px;padding:0;border:1px solid var(--border);border-radius:4px;background:transparent;cursor:pointer}.statusbar{grid-column:1 / -1}.panel{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.workbench>.panel.right{position:absolute;right:12px;top:12px;width:280px;max-height:calc(100% - 52px);z-index:var(--printbench-diagnosis-z, 100);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);background:color-mix(in srgb,var(--panel) 94%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-right:1px solid var(--border);border-left:1px solid var(--border)}.workbench>.panel.right.collapsed{width:36px;max-height:none}.panel-hd{display:flex;align-items:center;gap:6px;padding:10px 12px;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);border-bottom:1px solid var(--border)}.panel-hd .ttl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.panel-toggle{flex:none;width:24px;height:24px;padding:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;color:var(--text);cursor:pointer;line-height:1;font-size:16px;display:inline-flex;align-items:center;justify-content:center}.panel-toggle:hover{border-color:var(--accent);color:var(--accent)}.panel-toggle .ico{width:14px;height:14px;display:block}.panel.collapsed .panel-hd{flex-direction:column;height:100%;padding:8px 4px;border-bottom:none;justify-content:flex-start}.panel.collapsed .panel-toggle{order:-1}.panel.collapsed .ttl{writing-mode:vertical-rl;flex:none;letter-spacing:.2em}.panel-bd{padding:10px;overflow:auto;flex:1;display:flex;flex-direction:column;gap:8px}.btn{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 10px;cursor:pointer}.btn:hover{border-color:var(--accent)}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.danger{border-color:var(--danger);color:var(--danger)}.btn:disabled{opacity:.45;cursor:not-allowed}.canvas-wrap{position:relative;min-width:0;min-height:0;background:#12151c}.canvas-wrap canvas{width:100%;height:100%;display:block}.empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;color:var(--muted);text-align:center}.empty h2{color:var(--text);margin:0 0 8px;font-weight:600}.steps{display:flex;gap:18px;margin-top:16px;font-size:13px}.steps b{color:var(--accent)}.tree{display:flex;flex-direction:column;gap:2px;background:var(--panel-2);border:1px solid var(--border);border-radius:8px;padding:4px;min-height:120px;overflow-y:auto}.tree-empty{margin:0;padding:14px 8px;text-align:center;font-size:12px;color:var(--muted)}.item{position:relative;display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer}.item:hover{background:#5b8def14}.item.active{background:#263044}.item .idx{flex:none;width:18px;font-size:11px;color:var(--muted);text-align:right}.item .grow{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.item .rename-btn{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:auto;padding:2px 6px;font-size:11px;line-height:1.3;color:var(--text);opacity:0;pointer-events:none}.item:hover .rename-btn,.item:focus-within .rename-btn{opacity:1;pointer-events:auto}.item:hover .grow,.item:focus-within .grow{padding-right:40px}.item input[type=checkbox]{accent-color:var(--accent);margin:0}.hint{margin:0;font-size:11px;line-height:1.45;color:var(--muted)}.sec{margin:4px 0 0;padding:10px 0 2px;border-top:1px solid var(--border);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}.btn-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.btn-grid .btn,.btn-row .btn{width:100%;font-size:12px;padding:6px 8px}.btn-grid.core-actions.compact,.btn-grid.tool-mode.compact{gap:4px}.btn-grid.core-actions.icon-text{grid-template-columns:1fr 1fr}.btn-grid.core-actions.icons-only{grid-template-columns:repeat(5,1fr)}.btn-grid.tool-mode{grid-template-columns:repeat(3,1fr)}.btn-grid.core-actions .btn.icon-btn,.btn-grid.tool-mode .btn.icon-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:4px 6px;font-size:11px;min-height:28px}.btn-grid.core-actions.icons-only .btn.icon-btn,.btn-grid.tool-mode.icons-only .btn.icon-btn{padding:4px}.btn-grid.core-actions .btn.icon-btn .ico,.btn-grid.tool-mode .btn.icon-btn .ico{width:14px;height:14px;flex:none;display:block}.btn-grid.core-actions .btn.icon-btn span,.btn-grid.tool-mode .btn.icon-btn span{line-height:1.2;white-space:nowrap}.btn-grid .pb-select{width:100%;min-width:0}.btn-row{display:flex;gap:6px}.split-list{display:flex;flex-direction:column;gap:6px}.split-list .btn-row .btn:first-child{flex:1;width:auto}.split-list .btn-row .btn.danger{flex:none;width:auto}.xyz{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;align-items:center;font-size:12px}.xyz>span{color:var(--muted)}.xyz input,.xyz .pb-select .pb-select-trigger{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 5px;color:var(--text);color-scheme:dark}.xyz input{text-align:center}.xyz .pb-select{grid-column:2 / -1;width:100%;min-width:0}.scale-row{display:flex;align-items:center;gap:6px;font-size:12px}.scale-row>span{color:var(--muted);flex:none}.scale-row input,.scale-row .pb-select .pb-select-trigger{min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 5px;color:var(--text);color-scheme:dark}.scale-row input{width:4.5rem;flex:none;text-align:center}.scale-row .pb-select{flex:1;min-width:0}.xyz-hd{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;font-size:10px;color:var(--muted);text-align:center;margin-bottom:-2px}.stack{display:flex;flex-direction:column;gap:6px}.stack .pb-select,.stack input[type=number]{width:100%;min-width:0}.stack input[type=number]{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}.diag{border-left:3px solid var(--border);padding:8px 10px;margin-bottom:8px;background:var(--panel-2);border-radius:0 6px 6px 0;cursor:pointer}.diag.red{border-color:var(--danger)}.diag.yellow{border-color:var(--warn)}.diag.green{border-color:var(--ok)}.diag h4{margin:0 0 4px;font-size:13px}.diag p{margin:0;font-size:12px;color:var(--muted)}.statusbar{display:flex;align-items:center;gap:16px;padding:0 12px;font-size:12px;color:var(--muted);background:var(--panel);border-top:1px solid var(--border)}.overlay{position:absolute;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:40}.workbench>.overlay{grid-column:1 / -1;grid-row:1 / -1}.dialog{width:min(480px,calc(100% - 24px),92vw);max-width:100%;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:18px}.dialog h3{margin:0 0 10px}.dialog .row{margin:10px 0;display:flex;gap:8px;align-items:center}.dialog .pb-select{min-width:0}.dialog .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:16px}.dialog .actions>.btn{flex:0 0 auto;white-space:nowrap}.dialog.help{width:min(640px,94vw);max-height:min(82vh,720px);overflow:auto}.dialog.help p{font-size:13px;line-height:1.55}.dialog .url-input{width:100%;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:8px 10px}.dialog .snapshot-preview{display:block;width:100%;max-height:min(52vh,420px);object-fit:contain;background:#12151c;border:1px solid var(--border);border-radius:8px}.toasts{position:fixed;top:16px;left:50%;transform:translate(-50%);display:flex;flex-direction:column;align-items:center;gap:8px;z-index:50;pointer-events:none}.toast{pointer-events:auto;padding:10px 14px;border-radius:8px;background:#1e242e;border:1px solid var(--border);color:var(--text);box-shadow:var(--shadow)}.toast.ok{background:#15261c;border-color:var(--ok)}.toast.fail{background:#2a1518;border-color:var(--danger)}.toast.info{background:#1e242e}.busy{position:absolute;inset:0;background:#0e11168c;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;cursor:wait;-webkit-user-select:none;user-select:none}.bar{width:180px;height:4px;background:#2a303c;border-radius:4px;overflow:hidden}.bar i{display:block;height:100%;width:40%;background:var(--accent);animation:slide 1s infinite}@keyframes slide{0%{transform:translate(-100%)}to{transform:translate(280%)}}.field{display:flex;align-items:center;gap:8px;margin:6px 0;font-size:13px}.field input,.field .pb-select{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 6px;width:90px}.field .pb-select{padding:0;background:transparent;border:none}.field .pb-select .pb-select-trigger{padding:4px 6px;border-radius:4px}.tour{position:absolute;left:50%;top:72px;transform:translate(-50%);background:var(--panel);border:1px solid var(--accent);border-radius:10px;padding:14px 16px;z-index:8;width:360px}.preview-lighting{position:absolute;top:10px;left:10px;z-index:6;display:flex;align-items:center;gap:6px;padding:6px 8px;border-radius:8px;background:color-mix(in srgb,var(--panel) 92%,transparent);border:1px solid var(--border);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:12px;color:var(--text);pointer-events:auto}.preview-lighting label{display:flex;align-items:center;gap:6px;margin:0;white-space:nowrap}.preview-lighting .pb-select{max-width:9.5em}.preview-lighting .pb-select .pb-select-trigger{padding:4px 8px}.preview-tag{position:absolute;top:10px;left:10px;background:#1d3a24;color:#9dffb0;padding:4px 8px;border-radius:4px;font-size:12px}.preview-tag.below-lighting{top:52px}.marquee{position:absolute;border:1px dashed var(--accent);background:#5b8def1f;pointer-events:none}.ctx{position:fixed;z-index:60;background:var(--panel);border:1px solid var(--border);border-radius:6px;min-width:140px;padding:4px}.ctx button{display:block;width:100%;text-align:left;background:none;border:none;padding:6px 8px;cursor:pointer}.ctx button:hover{background:var(--panel-2)}.clip-slider{position:absolute;top:12px;left:50%;transform:translate(-50%);background:#141820e0;padding:8px 12px;border-radius:8px;font-size:12px;display:flex;align-items:center;gap:8px;z-index:4}.clip-slider input{width:180px}.diag.active{outline:1px solid var(--accent)}.live-read{position:absolute;left:12px;bottom:12px;background:#141820d9;padding:6px 10px;border-radius:6px;font-size:13px;color:#ffe08a}
1
+ .pb-select{position:relative;display:block;min-width:0;width:100%}.pb-select-trigger{display:flex;align-items:center;gap:6px;width:100%;min-width:0;margin:0;padding:5px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;font:inherit;font-size:12px;line-height:1.3;text-align:left;cursor:pointer;color-scheme:dark}.pb-select-trigger:hover:not(:disabled){border-color:var(--accent)}.pb-select-trigger:disabled{opacity:.45;cursor:not-allowed}.pb-select-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pb-select-chevron{flex:none;width:12px;height:12px;opacity:.75}.pb-select.open .pb-select-chevron{transform:rotate(180deg)}.pb-select-menu{position:fixed;z-index:9999;margin:0;padding:4px;list-style:none;overflow:auto;background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadow);color-scheme:dark;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif;font-size:12px}.pb-select-option{padding:6px 8px;border-radius:5px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pb-select-option:hover,.pb-select-option.active{background:#5b8def2e}.pb-select-option.selected{color:var(--accent)}.unit-row>span[data-v-d70135e8]{flex:none;white-space:nowrap}.unit-row[data-v-d70135e8] .pb-select{flex:1;min-width:0}.settings-host[data-v-04a6df4b] .dialog{width:min(420px,calc(100% - 24px),92vw)}.row[data-v-04a6df4b]{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:8px 0;font-size:13px}.row .label[data-v-04a6df4b]{flex:1;min-width:0;color:var(--text)}.row[data-v-04a6df4b] .pb-select,.row>input[type=number][data-v-04a6df4b]{flex:0 0 auto;width:140px;max-width:46%}.row>input[type=number][data-v-04a6df4b]{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px;color:var(--text);color-scheme:dark}.section[data-v-04a6df4b]{margin:12px 0;padding-top:10px;border-top:1px solid var(--border)}.section-title[data-v-04a6df4b]{font-size:12px;font-weight:600;color:var(--muted, #9aa3b2);margin-bottom:8px}.section-title .unit[data-v-04a6df4b]{font-weight:400;opacity:.85}.checks[data-v-04a6df4b]{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px;margin-bottom:8px;font-size:13px}.checks label[data-v-04a6df4b]{display:flex;align-items:center;gap:6px;min-width:0}.bed-xyz[data-v-04a6df4b]{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.axis[data-v-04a6df4b]{display:flex;flex-direction:column;gap:4px;min-width:0;font-size:11px;color:var(--muted, #9aa3b2)}.axis input[data-v-04a6df4b]{width:100%;box-sizing:border-box;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 6px;color:var(--text);color-scheme:dark}.bed-presets[data-v-04a6df4b]{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}.preset[data-v-04a6df4b]{font-size:12px;padding:4px 8px;white-space:nowrap}.anno{display:flex;flex-direction:column;gap:8px}.anno-toolbar{display:flex;align-items:center;gap:8px}.anno-count{font-size:12px;color:var(--muted)}.anno-hint{margin:0;font-size:12px;color:var(--muted);line-height:1.5}.anno-item{border-left:3px solid var(--border);padding:8px 10px;background:var(--panel-2);border-radius:0 6px 6px 0;cursor:pointer}.anno-item.red{border-color:var(--danger)}.anno-item.yellow{border-color:var(--warn)}.anno-item.green{border-color:var(--ok)}.anno-item.info{border-color:var(--accent)}.anno-item.active{outline:1px solid var(--accent)}.anno-item-hd{display:flex;align-items:center;gap:6px;font-size:13px}.anno-item-hd b{font-weight:600;color:var(--muted)}.anno-dot{width:8px;height:8px;flex:none;border-radius:50%;background:var(--accent)}.anno-item.red .anno-dot{background:var(--danger)}.anno-item.yellow .anno-dot{background:var(--warn)}.anno-item.green .anno-dot{background:var(--ok)}.anno-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.anno-badge{flex:none;font-size:10px;padding:1px 5px;border-radius:4px;border:1px solid var(--border);color:var(--muted)}.anno-badge.ok{color:var(--ok);border-color:var(--ok)}.anno-badge.warn{color:var(--warn);border-color:var(--warn)}.anno-body{margin:4px 0 0;font-size:12px;color:var(--text);line-height:1.45;white-space:pre-wrap;word-break:break-word}.anno-meta{margin:4px 0 0;font-size:11px;color:var(--muted)}.anno-editor{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--border);border-radius:6px;background:var(--panel-2)}.anno-editor label{display:flex;flex-direction:column;gap:3px;font-size:11px;color:var(--muted)}.anno-editor input,.anno-editor textarea,.anno-editor select{width:100%;box-sizing:border-box;padding:5px 7px;font:inherit;font-size:12px;color:var(--text);background:var(--panel);border:1px solid var(--border);border-radius:5px;color-scheme:dark;resize:vertical}.anno-editor-actions{display:flex;gap:6px;flex-wrap:wrap}.diag-tabs{display:flex;gap:4px;margin-bottom:8px}.diag-tab{flex:1;padding:5px 8px;font:inherit;font-size:12px;color:var(--muted);background:var(--panel-2);border:1px solid var(--border);border-radius:6px;cursor:pointer}.diag-tab:hover{border-color:var(--accent)}.diag-tab.active{color:var(--text);border-color:var(--accent);background:color-mix(in srgb,var(--accent) 18%,var(--panel-2))}.diag-tab-count{margin-left:4px;font-size:10px;padding:0 4px;border-radius:8px;background:var(--accent);color:#0d1117}.warn[data-v-b085aeeb]{color:#e07040;font-weight:600}:root{--bg: #0e1116;--panel: #171b22;--panel-2: #1e242e;--border: #2c3440;--text: #e8edf4;--muted: #8b95a5;--accent: #5b8def;--danger: #ff5c5c;--warn: #f0b429;--ok: #3dcc7a;--shadow: 0 8px 32px rgba(0, 0, 0, .35);font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root,.printbench-root *{box-sizing:border-box}.printbench-root{height:100%;min-height:0;background:var(--bg);color:var(--text);color-scheme:dark;font-family:Segoe UI,PingFang SC,Microsoft YaHei,sans-serif}.printbench-root button,.printbench-root input,.printbench-root textarea{font:inherit;color:inherit;color-scheme:dark}.workbench{position:relative;display:grid;grid-template-rows:1fr 28px;grid-template-columns:292px 1fr;height:100%}.workbench.no-left{grid-template-columns:36px 1fr}.workbench.left-float{grid-template-columns:1fr}.workbench.left-float>.panel.parts{position:absolute;left:12px;top:12px;width:280px;max-height:calc(100% - 52px);z-index:12;grid-column:1;grid-row:1;border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);background:color-mix(in srgb,var(--panel) 94%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.workbench.left-float>.panel.parts.collapsed{width:36px;max-height:none;bottom:40px}.workbench.left-float>.canvas-wrap{grid-column:1;grid-row:1;min-width:0}.workbench.preview-shell{grid-template-rows:1fr;grid-template-columns:1fr}.item input.swatch{flex:none;width:22px;height:18px;padding:0;border:1px solid var(--border);border-radius:4px;background:transparent;cursor:pointer}.statusbar{grid-column:1 / -1}.panel{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.workbench>.panel.right{position:absolute;right:12px;top:12px;width:280px;max-height:calc(100% - 52px);z-index:var(--printbench-diagnosis-z, 100);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);background:color-mix(in srgb,var(--panel) 94%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border-right:1px solid var(--border);border-left:1px solid var(--border)}.workbench>.panel.right.collapsed{width:36px;max-height:none}.panel-hd{display:flex;align-items:center;gap:6px;padding:10px 12px;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);border-bottom:1px solid var(--border)}.panel-hd .ttl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.panel-toggle{flex:none;width:24px;height:24px;padding:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;color:var(--text);cursor:pointer;line-height:1;font-size:16px;display:inline-flex;align-items:center;justify-content:center}.panel-toggle:hover{border-color:var(--accent);color:var(--accent)}.panel-toggle .ico{width:14px;height:14px;display:block}.panel.collapsed .panel-hd{flex-direction:column;height:100%;padding:8px 4px;border-bottom:none;justify-content:flex-start}.panel.collapsed .panel-toggle{order:-1}.panel.collapsed .ttl{writing-mode:vertical-rl;flex:none;letter-spacing:.2em}.panel-bd{padding:10px;overflow:auto;flex:1;display:flex;flex-direction:column;gap:8px}.btn{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 10px;cursor:pointer}.btn:hover{border-color:var(--accent)}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.danger{border-color:var(--danger);color:var(--danger)}.btn:disabled{opacity:.45;cursor:not-allowed}.canvas-wrap{position:relative;min-width:0;min-height:0;background:#12151c}.canvas-wrap canvas{width:100%;height:100%;display:block}.empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;color:var(--muted);text-align:center}.empty h2{color:var(--text);margin:0 0 8px;font-weight:600}.steps{display:flex;gap:18px;margin-top:16px;font-size:13px}.steps b{color:var(--accent)}.tree{display:flex;flex-direction:column;gap:2px;background:var(--panel-2);border:1px solid var(--border);border-radius:8px;padding:4px;min-height:120px;overflow-y:auto}.tree-empty{margin:0;padding:14px 8px;text-align:center;font-size:12px;color:var(--muted)}.item{position:relative;display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:6px;cursor:pointer}.item:hover{background:#5b8def14}.item.active{background:#263044}.item .idx{flex:none;width:18px;font-size:11px;color:var(--muted);text-align:right}.item .grow{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.item .rename-btn{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:auto;padding:2px 6px;font-size:11px;line-height:1.3;color:var(--text);opacity:0;pointer-events:none}.item:hover .rename-btn,.item:focus-within .rename-btn{opacity:1;pointer-events:auto}.item:hover .grow,.item:focus-within .grow{padding-right:40px}.item input[type=checkbox]{accent-color:var(--accent);margin:0}.hint{margin:0;font-size:11px;line-height:1.45;color:var(--muted)}.sec{margin:4px 0 0;padding:10px 0 2px;border-top:1px solid var(--border);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}.btn-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}.btn-grid .btn,.btn-row .btn{width:100%;font-size:12px;padding:6px 8px}.btn-grid.core-actions.compact,.btn-grid.tool-mode.compact{gap:4px}.btn-grid.core-actions.icon-text{grid-template-columns:1fr 1fr}.btn-grid.core-actions.icons-only{grid-template-columns:repeat(5,1fr)}.btn-grid.tool-mode{grid-template-columns:repeat(3,1fr)}.btn-grid.core-actions .btn.icon-btn,.btn-grid.tool-mode .btn.icon-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:4px 6px;font-size:11px;min-height:28px}.btn-grid.core-actions.icons-only .btn.icon-btn,.btn-grid.tool-mode.icons-only .btn.icon-btn{padding:4px}.btn-grid.core-actions .btn.icon-btn .ico,.btn-grid.tool-mode .btn.icon-btn .ico{width:14px;height:14px;flex:none;display:block}.btn-grid.core-actions .btn.icon-btn span,.btn-grid.tool-mode .btn.icon-btn span{line-height:1.2;white-space:nowrap}.btn-grid .pb-select{width:100%;min-width:0}.btn-row{display:flex;gap:6px}.split-list{display:flex;flex-direction:column;gap:6px}.split-list .btn-row .btn:first-child{flex:1;width:auto}.split-list .btn-row .btn.danger{flex:none;width:auto}.xyz{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;align-items:center;font-size:12px}.xyz>span{color:var(--muted)}.xyz input,.xyz .pb-select .pb-select-trigger{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 5px;color:var(--text);color-scheme:dark}.xyz input{text-align:center}.xyz .pb-select{grid-column:2 / -1;width:100%;min-width:0}.scale-row{display:flex;align-items:center;gap:6px;font-size:12px}.scale-row>span{color:var(--muted);flex:none}.scale-row input,.scale-row .pb-select .pb-select-trigger{min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 5px;color:var(--text);color-scheme:dark}.scale-row input{width:4.5rem;flex:none;text-align:center}.scale-row .pb-select{flex:1;min-width:0}.xyz-hd{display:grid;grid-template-columns:48px 1fr 1fr 1fr;gap:4px;font-size:10px;color:var(--muted);text-align:center;margin-bottom:-2px}.stack{display:flex;flex-direction:column;gap:6px}.stack .pb-select,.stack input[type=number]{width:100%;min-width:0}.stack input[type=number]{background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.check{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}.diag{border-left:3px solid var(--border);padding:8px 10px;margin-bottom:8px;background:var(--panel-2);border-radius:0 6px 6px 0;cursor:pointer}.diag.red{border-color:var(--danger)}.diag.yellow{border-color:var(--warn)}.diag.green{border-color:var(--ok)}.diag h4{margin:0 0 4px;font-size:13px}.diag p{margin:0;font-size:12px;color:var(--muted)}.statusbar{display:flex;align-items:center;gap:16px;padding:0 12px;font-size:12px;color:var(--muted);background:var(--panel);border-top:1px solid var(--border)}.overlay{position:absolute;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:40}.workbench>.overlay{grid-column:1 / -1;grid-row:1 / -1}.dialog{width:min(480px,calc(100% - 24px),92vw);max-width:100%;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:18px}.dialog h3{margin:0 0 10px}.dialog .row{margin:10px 0;display:flex;gap:8px;align-items:center}.dialog .pb-select{min-width:0}.dialog .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:16px}.dialog .actions>.btn{flex:0 0 auto;white-space:nowrap}.dialog.help{width:min(640px,94vw);max-height:min(82vh,720px);overflow:auto}.dialog.help p{font-size:13px;line-height:1.55}.dialog .url-input{width:100%;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:8px 10px}.dialog .snapshot-preview{display:block;width:100%;max-height:min(52vh,420px);object-fit:contain;background:#12151c;border:1px solid var(--border);border-radius:8px}.toasts{position:fixed;top:16px;left:50%;transform:translate(-50%);display:flex;flex-direction:column;align-items:center;gap:8px;z-index:50;pointer-events:none}.toast{pointer-events:auto;padding:10px 14px;border-radius:8px;background:#1e242e;border:1px solid var(--border);color:var(--text);box-shadow:var(--shadow)}.toast.ok{background:#15261c;border-color:var(--ok)}.toast.fail{background:#2a1518;border-color:var(--danger)}.toast.info{background:#1e242e}.busy{position:absolute;inset:0;background:#0e11168c;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;cursor:wait;-webkit-user-select:none;user-select:none}.bar{width:180px;height:4px;background:#2a303c;border-radius:4px;overflow:hidden}.bar i{display:block;height:100%;width:40%;background:var(--accent);animation:slide 1s infinite}@keyframes slide{0%{transform:translate(-100%)}to{transform:translate(280%)}}.field{display:flex;align-items:center;gap:8px;margin:6px 0;font-size:13px}.field input,.field .pb-select{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 6px;width:90px}.field .pb-select{padding:0;background:transparent;border:none}.field .pb-select .pb-select-trigger{padding:4px 6px;border-radius:4px}.tour{position:absolute;left:50%;top:72px;transform:translate(-50%);background:var(--panel);border:1px solid var(--accent);border-radius:10px;padding:14px 16px;z-index:8;width:360px}.preview-lighting{position:absolute;top:10px;left:10px;z-index:6;display:flex;align-items:center;gap:6px;padding:6px 8px;border-radius:8px;background:color-mix(in srgb,var(--panel) 92%,transparent);border:1px solid var(--border);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:12px;color:var(--text);pointer-events:auto}.preview-lighting label{display:flex;align-items:center;gap:6px;margin:0;white-space:nowrap}.preview-lighting .pb-select{max-width:9.5em}.preview-lighting .pb-select .pb-select-trigger{padding:4px 8px}.preview-tag{position:absolute;top:10px;left:10px;background:#1d3a24;color:#9dffb0;padding:4px 8px;border-radius:4px;font-size:12px}.preview-tag.below-lighting{top:52px}.marquee{position:absolute;border:1px dashed var(--accent);background:#5b8def1f;pointer-events:none}.ctx{position:fixed;z-index:60;background:var(--panel);border:1px solid var(--border);border-radius:6px;min-width:140px;padding:4px}.ctx button{display:block;width:100%;text-align:left;background:none;border:none;padding:6px 8px;cursor:pointer}.ctx button:hover{background:var(--panel-2)}.clip-slider{position:absolute;top:12px;left:50%;transform:translate(-50%);background:#141820e0;padding:8px 12px;border-radius:8px;font-size:12px;display:flex;align-items:center;gap:8px;z-index:4}.clip-slider input{width:180px}.diag.active{outline:1px solid var(--accent)}.live-read{position:absolute;left:12px;bottom:12px;background:#141820d9;padding:6px 10px;border-radius:6px;font-size:13px;color:#ffe08a}