printbench 0.1.2 → 0.2.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/README.md +78 -5
- package/dist/printbench.css +1 -1
- package/dist/printbench.js +2855 -2564
- package/package.json +54 -53
package/README.md
CHANGED
|
@@ -78,6 +78,59 @@ function onExported(payload: PrintBenchExportedPayload) {
|
|
|
78
78
|
| `modelUrl` | `string` | — | 挂载后按 URL 自动导入模型(需 CORS) |
|
|
79
79
|
| `uploadUrl` | `string` | — | 「一键上传」的 POST 地址;未设则无法上传 |
|
|
80
80
|
| `customExport` | `boolean` | `false` | 为 `true` 时,下载 / 一键上传只生成文件并触发 `exported`(用 `action` 区分),不本地下载、不 POST |
|
|
81
|
+
| `mode` | `'edit' \| 'preview'` | `'edit'` | `preview` 为嵌入几何只读:隐藏工作台壳,画布可旋转与点选零件,不能改网格 |
|
|
82
|
+
| `selectedPartIds` | `string[]` | — | 受控选中。传入后画布点击只触发 `select-part`,由宿主回写 |
|
|
83
|
+
| `partColors` | `Record<string, string>` | — | 受控零件色,`#RRGGBB`,key 为零件 `id` |
|
|
84
|
+
|
|
85
|
+
嵌入预览示例(宿主列表 + 色板 + 自定义导出):
|
|
86
|
+
|
|
87
|
+
```vue
|
|
88
|
+
<script setup lang="ts">
|
|
89
|
+
import { ref } from 'vue'
|
|
90
|
+
import { PrintBench } from 'printbench'
|
|
91
|
+
import type { PrintBenchExpose } from 'printbench'
|
|
92
|
+
|
|
93
|
+
const bench = ref<PrintBenchExpose | null>(null)
|
|
94
|
+
|
|
95
|
+
async function download() {
|
|
96
|
+
const file = await bench.value?.exportFile({ action: 'download', format: '3mf' })
|
|
97
|
+
if (!file) return
|
|
98
|
+
const a = document.createElement('a')
|
|
99
|
+
a.href = URL.createObjectURL(file.blob)
|
|
100
|
+
a.download = file.fileName
|
|
101
|
+
a.click()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function upload() {
|
|
105
|
+
const file = await bench.value?.exportFile({ action: 'upload', format: '3mf' })
|
|
106
|
+
if (!file) return
|
|
107
|
+
// 宿主自己的上传,例如 FormData POST,不必传 uploadUrl
|
|
108
|
+
await fetch('/api/your-export', { method: 'POST', body: file.blob })
|
|
109
|
+
}
|
|
110
|
+
</script>
|
|
111
|
+
|
|
112
|
+
<template>
|
|
113
|
+
<button @click="download">下载</button>
|
|
114
|
+
<button @click="upload">上传</button>
|
|
115
|
+
<PrintBench
|
|
116
|
+
ref="bench"
|
|
117
|
+
mode="preview"
|
|
118
|
+
custom-export
|
|
119
|
+
:model-url="modelUrl"
|
|
120
|
+
:selected-part-ids="selectedIds"
|
|
121
|
+
:part-colors="colorMap"
|
|
122
|
+
@parts="parts = $event"
|
|
123
|
+
@select-part="onSelectPart"
|
|
124
|
+
@exported="onExported"
|
|
125
|
+
/>
|
|
126
|
+
</template>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`mode="preview"` 时插件不显示导出按钮。宿主用 `ref.exportFile()` 只要文件:`customExport` 为 true 时不本地下载、不 POST,返回 `blob` 后由宿主保存或上传。工作台顶栏导出仍可用 `uploadUrl` 走插件上传。
|
|
130
|
+
|
|
131
|
+
`@parts` 在导入/求值后给出 `{ id, index, name }[]`。`id` 为 meshId 或基本体/布尔节点 id,不要用下标当主键。
|
|
132
|
+
|
|
133
|
+
**填色优先级**:有 `partColors` / 打印色时用纯色(盖过导入贴图);否则用导入外观;再否则默认蓝。
|
|
81
134
|
|
|
82
135
|
### Events
|
|
83
136
|
|
|
@@ -87,11 +140,20 @@ function onExported(payload: PrintBenchExportedPayload) {
|
|
|
87
140
|
| `error` | `string` | 可展示错误 |
|
|
88
141
|
| `exported` | `{ action, format, fileName, blob, unit, axis? }` | 导出完成。`action` 为 `'download'`(下载)或 `'upload'`(一键上传) |
|
|
89
142
|
| `report` | 审核报告对象 | 下载 JSON / HTML 审核报告时触发 |
|
|
143
|
+
| `parts` | `{ id, index, name }[]` | 当前零件列表 |
|
|
144
|
+
| `select-part` | `{ id, index, additive }` | 画布或零件树点选 |
|
|
145
|
+
| `part-colors-change` | `Record<string, string>` | 工作台内改色时抛出(嵌入预览以 `partColors` 为准) |
|
|
90
146
|
|
|
91
|
-
`exported` 的 `format` 为 `'stl' | '3mf'`;多零件 STL 时 `fileName` 为 zip,`blob` 为打包后的文件。
|
|
147
|
+
`exported` 的 `format` 为 `'stl' | '3mf' | 'glb'`;多零件 STL 时 `fileName` 为 zip,`blob` 为打包后的文件。GLB 为单文件,零件色写入材质。
|
|
92
148
|
|
|
93
149
|
「一键上传」以 `multipart/form-data` POST,字段为 `file` 与 `meta`(JSON:fileName、format、unit 等)。
|
|
94
150
|
|
|
151
|
+
### 实例方法(`ref`)
|
|
152
|
+
|
|
153
|
+
| 方法 | 说明 |
|
|
154
|
+
|------|------|
|
|
155
|
+
| `exportFile(options?)` | 不打开插件对话框。`action`: `'download'` \| `'upload'`;`format`: `'stl'` \| `'3mf'` \| `'glb'`;`unit` 默认 mm;`silent: true` 跳过黄级确认。红级阻断返回 `null`。成功返回与 `exported` 相同的 payload。 |
|
|
156
|
+
|
|
95
157
|
## 工作台能力
|
|
96
158
|
|
|
97
159
|
推荐流程:导入模型并确认单位 → 看右侧诊断,必要时一键修复 → 导出 STL / 3MF 给切片软件(坐标按毫米或导出时所选单位解释)。
|
|
@@ -108,16 +170,17 @@ function onExported(payload: PrintBenchExportedPayload) {
|
|
|
108
170
|
|
|
109
171
|
| 区域 | 作用 |
|
|
110
172
|
|------|------|
|
|
111
|
-
| 顶栏 |
|
|
112
|
-
| 左栏 |
|
|
173
|
+
| 顶栏 | 导入、撤销/重做、预览/编辑、修复、导出、草稿、设置、帮助 |
|
|
174
|
+
| 左栏 | 零件树、上色、编辑(布尔、拆件等),可收起 |
|
|
113
175
|
| 画布 | 轨道旋转缩放;双击或 **F** 自适应;**复位视角** 回默认相机 |
|
|
114
176
|
| 右栏 | 诊断与测量,可收起 |
|
|
115
177
|
| 底栏 | 包围盒、内部单位 mm、体积、文件名 |
|
|
116
178
|
|
|
117
179
|
### 导出
|
|
118
180
|
|
|
119
|
-
- **STL**:二进制;无单位字段,切片软件需与导出单位一致(推荐毫米)。单零件 `.stl`,多零件 `.zip`(每件一个 STL
|
|
120
|
-
- **3MF**:自带毫米单位;每零件单独 object
|
|
181
|
+
- **STL**:二进制;无单位字段,切片软件需与导出单位一致(推荐毫米)。单零件 `.stl`,多零件 `.zip`(每件一个 STL)。不含颜色。
|
|
182
|
+
- **3MF**:自带毫米单位;每零件单独 object,并写入 `displaycolor`(零件打印色)
|
|
183
|
+
- **GLB**:单文件多网格;零件打印色写入材质。无打印单位字段,坐标按导出单位解释。
|
|
121
184
|
|
|
122
185
|
### 快捷键
|
|
123
186
|
|
|
@@ -126,6 +189,7 @@ function onExported(payload: PrintBenchExportedPayload) {
|
|
|
126
189
|
| Ctrl+Z / Ctrl+Y | 撤销 / 重做 |
|
|
127
190
|
| Ctrl+S | 保存自动草稿(IndexedDB) |
|
|
128
191
|
| 1 / 2 / 3 | 移动 / 旋转 / 缩放 |
|
|
192
|
+
| P | 切换工作台观察预览 |
|
|
129
193
|
| Esc | 取消手柄、预览、测量 |
|
|
130
194
|
| F | 按模型自适应视角 |
|
|
131
195
|
| Shift | 变换吸附 |
|
|
@@ -143,4 +207,13 @@ npm run build:lib # npm 库产物(写入 dist/,与 SPA 互斥覆盖)
|
|
|
143
207
|
npm test
|
|
144
208
|
```
|
|
145
209
|
|
|
210
|
+
开发站路由(仅本仓库 SPA,不进入 npm 包):
|
|
211
|
+
|
|
212
|
+
| 路径 | 说明 |
|
|
213
|
+
|------|------|
|
|
214
|
+
| `/print-bench/` | 完整工作台 |
|
|
215
|
+
| `/print-bench/preview` | 嵌入只读壳 + 左侧宿主色板,用来测 `selectedPartIds` / `partColors` |
|
|
216
|
+
|
|
217
|
+
预览页可在左侧选择本地文件或 `?model=` 直链;下载/上传按钮走宿主逻辑(`custom-export` + `exportFile` 只拿 blob)。工作台内观察态仍用顶栏「预览」或快捷键 `P`。
|
|
218
|
+
|
|
146
219
|
发布前请使用 `npm run build:lib`,不要用 SPA 的 `build` 覆盖 `dist/`。
|
package/dist/printbench.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
: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 select,.printbench-root textarea{font:inherit;color:inherit;color-scheme:dark}.printbench-root select option{background:var(--panel-2);color:var(--text)}.workbench{display:grid;grid-template-rows:48px 1fr 28px;grid-template-columns:292px 1fr 300px;height:100%}.workbench.no-left{grid-template-columns:36px 1fr 300px}.workbench.no-right{grid-template-columns:292px 1fr 36px}.workbench.no-left.no-right{grid-template-columns:36px 1fr 36px}.topbar,.statusbar{grid-column:1 / -1}.panel{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.panel.right{border-right:none;border-left:1px solid var(--border)}.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}.panel-toggle:hover{border-color:var(--accent);color:var(--accent)}.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}.topbar{display:flex;align-items:center;gap:8px;padding:0 10px;background:var(--panel);border-bottom:1px solid var(--border)}.brand{font-weight:700;margin-right:12px}.brand span{color:var(--accent)}.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{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 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 select{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:6px 8px;font-size:12px}.btn-row{display:flex;gap:6px}.rename{display:grid;grid-template-columns:1fr auto;gap:6px}.rename input{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.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 select{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 select{grid-column:2 / -1}.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 select,.stack input[type=number]{width:100%;min-width:0;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:fixed;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:40}.dialog{width:min(480px,92vw);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 select{background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:5px 8px;color-scheme:dark}.dialog select option{background:var(--panel-2);color:var(--text)}.dialog .actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.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}.toasts{position:fixed;right:16px;bottom:40px;display:flex;flex-direction:column;gap:8px;z-index:50}.toast{padding:10px 14px;border-radius:8px;background:var(--panel-2);border:1px solid var(--border)}.toast.ok{border-color:var(--ok)}.toast.fail{border-color:var(--danger)}.busy{position:absolute;inset:0;background:#0e11168c;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:5}.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 select{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 6px;width:90px}.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-tag{position:absolute;top:10px;left:10px;background:#1d3a24;color:#9dffb0;padding:4px 8px;border-radius:4px;font-size:12px}.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
|
+
: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 select,.printbench-root textarea{font:inherit;color:inherit;color-scheme:dark}.printbench-root select option{background:var(--panel-2);color:var(--text)}.workbench{display:grid;grid-template-rows:48px 1fr 28px;grid-template-columns:292px 1fr 300px;height:100%}.workbench.no-left{grid-template-columns:36px 1fr 300px}.workbench.no-right{grid-template-columns:292px 1fr 36px}.workbench.no-left.no-right{grid-template-columns:36px 1fr 36px}.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}.topbar,.statusbar{grid-column:1 / -1}.panel{background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0}.panel.right{border-right:none;border-left:1px solid var(--border)}.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}.panel-toggle:hover{border-color:var(--accent);color:var(--accent)}.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}.topbar{display:flex;align-items:center;gap:8px;padding:0 10px;background:var(--panel);border-bottom:1px solid var(--border)}.brand{font-weight:700;margin-right:12px}.brand span{color:var(--accent)}.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{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 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 select{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:6px 8px;font-size:12px}.btn-row{display:flex;gap:6px}.rename{display:grid;grid-template-columns:1fr auto;gap:6px}.rename input{width:100%;min-width:0;background:var(--panel-2);border:1px solid var(--border);border-radius:6px;padding:5px 8px}.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 select{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 select{grid-column:2 / -1}.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 select,.stack input[type=number]{width:100%;min-width:0;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:fixed;inset:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:40}.dialog{width:min(480px,92vw);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 select{background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:5px 8px;color-scheme:dark}.dialog select option{background:var(--panel-2);color:var(--text)}.dialog .actions{display:flex;justify-content:flex-end;gap:8px;margin-top:16px}.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}.toasts{position:fixed;right:16px;bottom:40px;display:flex;flex-direction:column;gap:8px;z-index:50}.toast{padding:10px 14px;border-radius:8px;background:var(--panel-2);border:1px solid var(--border)}.toast.ok{border-color:var(--ok)}.toast.fail{border-color:var(--danger)}.busy{position:absolute;inset:0;background:#0e11168c;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:5}.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 select{background:var(--panel-2);border:1px solid var(--border);border-radius:4px;padding:4px 6px;width:90px}.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-tag{position:absolute;top:10px;left:10px;background:#1d3a24;color:#9dffb0;padding:4px 8px;border-radius:4px;font-size:12px}.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}
|