pixifact-cli 0.12.0 → 0.14.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.
- package/README.md +1 -1
- package/editor/assets/browserAll-CG16DIkk.js +1 -0
- package/editor/assets/capture-C6ZrcuNN.js +1 -0
- package/editor/assets/{captureCompilerScenePreview-CeYQNPPP.js → captureCompilerScenePreview-C0gP4JB7.js} +83 -83
- package/editor/assets/editor-BQk2SioZ.css +1 -0
- package/editor/assets/editor-DwuLOGgr.js +5 -0
- package/editor/assets/init-Byk-bAJW.js +1 -0
- package/editor/assets/webworkerAll-BP1wUWno.js +1 -0
- package/editor/capture.html +2 -2
- package/editor/index.html +3 -3
- package/package.json +2 -2
- package/src/editorServer.ts +31 -6
- package/src/editorSession.ts +26 -6
- package/editor/assets/browserAll-D1Wipsos.js +0 -1
- package/editor/assets/capture-DkKec9UG.js +0 -1
- package/editor/assets/editor-B_YR_yGH.css +0 -1
- package/editor/assets/editor-CCOqWQMU.js +0 -5
- package/editor/assets/init-BTbgjve4.js +0 -1
- package/editor/assets/webworkerAll-Ch415gWo.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as e,l as t,u as n,y as r}from"./captureCompilerScenePreview-C0gP4JB7.js";r.add(e,n),r.add(t);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./captureCompilerScenePreview-C0gP4JB7.js";import"./init-Byk-bAJW.js";
|
package/editor/capture.html
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<title>Pixifact Scene Capture</title>
|
|
6
|
-
<script type="module" crossorigin src="/assets/capture-
|
|
7
|
-
<link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-
|
|
6
|
+
<script type="module" crossorigin src="/assets/capture-C6ZrcuNN.js"></script>
|
|
7
|
+
<link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-C0gP4JB7.js">
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
</body>
|
package/editor/index.html
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Pixifact Editor</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/editor-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/editor-
|
|
7
|
+
<script type="module" crossorigin src="/assets/editor-DwuLOGgr.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-C0gP4JB7.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/editor-BQk2SioZ.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixifact-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Pixifact Scene 自动化 CLI。",
|
|
5
5
|
"packageManager": "bun@1.3.13",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"start": "bun src/pixifact-cli.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"pixifact": "^0.
|
|
35
|
+
"pixifact": "^0.14.0",
|
|
36
36
|
"playwright": "1.58.2",
|
|
37
37
|
"vite": "^8.0.10"
|
|
38
38
|
}
|
package/src/editorServer.ts
CHANGED
|
@@ -38,6 +38,10 @@ interface EditorProjectFile {
|
|
|
38
38
|
|
|
39
39
|
interface EditorUiState {
|
|
40
40
|
assetTreeExpandedDirectories?: string[];
|
|
41
|
+
autoSave: boolean;
|
|
42
|
+
theme: 'system' | 'light' | 'dark';
|
|
43
|
+
openScenePaths?: string[];
|
|
44
|
+
activeScenePath?: string;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
interface BunServer {
|
|
@@ -176,16 +180,37 @@ function parseEditorUiState(value: unknown): EditorUiState {
|
|
|
176
180
|
throw new Error('Editor UI state must be a JSON object.');
|
|
177
181
|
}
|
|
178
182
|
const directories = (value as Record<string, unknown>).assetTreeExpandedDirectories;
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
const autoSave = (value as Record<string, unknown>).autoSave;
|
|
184
|
+
const theme = (value as Record<string, unknown>).theme;
|
|
185
|
+
const openScenePaths = (value as Record<string, unknown>).openScenePaths;
|
|
186
|
+
const activeScenePath = (value as Record<string, unknown>).activeScenePath;
|
|
187
|
+
if (directories !== undefined && (!Array.isArray(directories) || directories.some((directory) => typeof directory !== 'string'))) {
|
|
181
188
|
throw new Error('Editor UI state assetTreeExpandedDirectories must be a string array.');
|
|
182
189
|
}
|
|
183
|
-
|
|
190
|
+
if (autoSave !== undefined && typeof autoSave !== 'boolean') {
|
|
191
|
+
throw new Error('Editor UI state autoSave must be a boolean.');
|
|
192
|
+
}
|
|
193
|
+
if (theme !== undefined && theme !== 'system' && theme !== 'light' && theme !== 'dark') {
|
|
194
|
+
throw new EditorRequestError(400, 'Editor UI state theme must be system, light, or dark.');
|
|
195
|
+
}
|
|
196
|
+
if (openScenePaths !== undefined && (!Array.isArray(openScenePaths) || openScenePaths.some((scenePath) => typeof scenePath !== 'string'))) {
|
|
197
|
+
throw new Error('Editor UI state openScenePaths must be a string array.');
|
|
198
|
+
}
|
|
199
|
+
if (activeScenePath !== undefined && typeof activeScenePath !== 'string') {
|
|
200
|
+
throw new Error('Editor UI state activeScenePath must be a string.');
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
...(directories === undefined ? {} : { assetTreeExpandedDirectories: directories }),
|
|
204
|
+
autoSave: autoSave ?? false,
|
|
205
|
+
theme: theme ?? 'dark',
|
|
206
|
+
...(openScenePaths === undefined ? {} : { openScenePaths }),
|
|
207
|
+
...(activeScenePath === undefined ? {} : { activeScenePath }),
|
|
208
|
+
};
|
|
184
209
|
}
|
|
185
210
|
|
|
186
211
|
function readEditorUiState(projectRoot: string): EditorUiState {
|
|
187
212
|
const statePath = editorUiStatePath(projectRoot);
|
|
188
|
-
if (!fs.existsSync(statePath)) return {};
|
|
213
|
+
if (!fs.existsSync(statePath)) return { autoSave: false, theme: 'dark' };
|
|
189
214
|
return parseEditorUiState(JSON.parse(fs.readFileSync(statePath, 'utf8')));
|
|
190
215
|
}
|
|
191
216
|
|
|
@@ -244,8 +269,8 @@ export function createEditorProjectService(options: EditorProjectServiceOptions)
|
|
|
244
269
|
if (url.pathname === '/api/editor-ui-state' && request.method === 'PUT') {
|
|
245
270
|
const body = await readJsonBody(request);
|
|
246
271
|
const state = parseEditorUiState(body);
|
|
247
|
-
if (!state.assetTreeExpandedDirectories) {
|
|
248
|
-
throw new EditorRequestError(400, 'Editor UI state requires assetTreeExpandedDirectories.');
|
|
272
|
+
if (!state.assetTreeExpandedDirectories || typeof body.autoSave !== 'boolean' || body.theme === undefined) {
|
|
273
|
+
throw new EditorRequestError(400, 'Editor UI state requires assetTreeExpandedDirectories, autoSave, and theme.');
|
|
249
274
|
}
|
|
250
275
|
return jsonResponse(writeEditorUiState(projectRoot, state));
|
|
251
276
|
}
|
package/src/editorSession.ts
CHANGED
|
@@ -4,9 +4,9 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { parseSceneTemplate } from 'pixifact/compiler';
|
|
6
6
|
|
|
7
|
-
export const editorSessionProtocolVersion =
|
|
7
|
+
export const editorSessionProtocolVersion = 4;
|
|
8
8
|
|
|
9
|
-
export type EditorContextSyncState = 'synced' | 'saving' | 'conflict' | 'error';
|
|
9
|
+
export type EditorContextSyncState = 'synced' | 'unsaved' | 'saving' | 'conflict' | 'error';
|
|
10
10
|
export type EditorPreviewState = 'loading' | 'ready' | 'error';
|
|
11
11
|
|
|
12
12
|
export type EditorNodeContext =
|
|
@@ -47,6 +47,7 @@ export interface EditorBrowserContext {
|
|
|
47
47
|
syncState: EditorContextSyncState;
|
|
48
48
|
};
|
|
49
49
|
selection: EditorSelectionContext;
|
|
50
|
+
openScenes: Array<{ path: string; syncState: EditorContextSyncState }>;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export interface EditorSessionResumeState {
|
|
@@ -152,18 +153,23 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
function isEditorBrowserContext(value: unknown): value is EditorBrowserContext {
|
|
155
|
-
if (!isRecord(value) || !isRecord(value.scene) || !isRecord(value.selection)) {
|
|
156
|
+
if (!isRecord(value) || !isRecord(value.scene) || !isRecord(value.selection) || !Array.isArray(value.openScenes)) {
|
|
156
157
|
return false;
|
|
157
158
|
}
|
|
158
159
|
const syncState = value.scene.syncState;
|
|
159
160
|
if (
|
|
160
161
|
typeof value.scene.path !== 'string'
|
|
161
162
|
|| typeof value.scene.revision !== 'string'
|
|
162
|
-
|| !['synced', 'saving', 'conflict', 'error'].includes(String(syncState))
|
|
163
|
+
|| !['synced', 'unsaved', 'saving', 'conflict', 'error'].includes(String(syncState))
|
|
163
164
|
|| !['loading', 'ready', 'error'].includes(String(value.scene.previewState))
|
|
164
165
|
) {
|
|
165
166
|
return false;
|
|
166
167
|
}
|
|
168
|
+
if (value.openScenes.some((openScene) => !isRecord(openScene)
|
|
169
|
+
|| typeof openScene.path !== 'string'
|
|
170
|
+
|| !['synced', 'unsaved', 'saving', 'conflict', 'error'].includes(String(openScene.syncState)))) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
167
173
|
if (value.selection.kind === 'scene') {
|
|
168
174
|
return true;
|
|
169
175
|
}
|
|
@@ -179,6 +185,7 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
179
185
|
const browsers = new Set<EditorBrowserSocket>();
|
|
180
186
|
const pendingScreenshots = new Map<string, PendingEditorScreenshot>();
|
|
181
187
|
let activeBrowser: EditorBrowserSocket | undefined;
|
|
188
|
+
let activeHasNoScene = false;
|
|
182
189
|
let context: EditorContext | undefined;
|
|
183
190
|
let resume: EditorSessionResumeState | undefined;
|
|
184
191
|
let screenshotSequence = 0;
|
|
@@ -225,6 +232,7 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
225
232
|
return false;
|
|
226
233
|
}
|
|
227
234
|
activeBrowser = socket;
|
|
235
|
+
activeHasNoScene = false;
|
|
228
236
|
sendState(socket, 'editorSessionActive');
|
|
229
237
|
return true;
|
|
230
238
|
}
|
|
@@ -234,6 +242,7 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
234
242
|
if (socket === activeBrowser) {
|
|
235
243
|
rejectBrowserScreenshots(socket, 'Active Editor browser disconnected during screenshot capture.');
|
|
236
244
|
activeBrowser = undefined;
|
|
245
|
+
activeHasNoScene = false;
|
|
237
246
|
context = undefined;
|
|
238
247
|
}
|
|
239
248
|
}
|
|
@@ -245,7 +254,8 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
245
254
|
}
|
|
246
255
|
if (parsed.type === 'editorSessionTakeoverRequested') {
|
|
247
256
|
if (!browsers.has(socket) || socket === activeBrowser) return;
|
|
248
|
-
if (activeBrowser && context?.scene.syncState !== 'synced'
|
|
257
|
+
if (activeBrowser && !activeHasNoScene && (context?.scene.syncState !== 'synced'
|
|
258
|
+
|| context.openScenes.some((openScene) => openScene.syncState !== 'synced'))) {
|
|
249
259
|
sendState(socket, 'editorSessionStandby', {
|
|
250
260
|
error: '当前 Editor 尚未同步,暂时无法接管。',
|
|
251
261
|
});
|
|
@@ -256,6 +266,7 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
256
266
|
rejectBrowserScreenshots(previous, 'Active Editor browser changed during screenshot capture.');
|
|
257
267
|
}
|
|
258
268
|
activeBrowser = socket;
|
|
269
|
+
activeHasNoScene = false;
|
|
259
270
|
context = undefined;
|
|
260
271
|
for (const browser of browsers) {
|
|
261
272
|
if (browser === socket) {
|
|
@@ -319,7 +330,15 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
319
330
|
}));
|
|
320
331
|
return;
|
|
321
332
|
}
|
|
322
|
-
if (socket !== activeBrowser
|
|
333
|
+
if (socket !== activeBrowser) return;
|
|
334
|
+
if (parsed.type === 'editorContextCleared') {
|
|
335
|
+
context = undefined;
|
|
336
|
+
resume = undefined;
|
|
337
|
+
activeHasNoScene = true;
|
|
338
|
+
notifyStandbyBrowsers();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (parsed.type !== 'editorContextChanged') return;
|
|
323
342
|
if (!isEditorBrowserContext(parsed.context)) {
|
|
324
343
|
throw new Error('Editor context message is invalid.');
|
|
325
344
|
}
|
|
@@ -332,6 +351,7 @@ export function createEditorHostSession(options: EditorHostSessionOptions) {
|
|
|
332
351
|
},
|
|
333
352
|
...structuredClone(parsed.context),
|
|
334
353
|
};
|
|
354
|
+
activeHasNoScene = false;
|
|
335
355
|
resume = {
|
|
336
356
|
scenePath: context.scene.path,
|
|
337
357
|
...(context.selection.kind === 'node'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e,E as t,T as n,b as r,w as i,x as a,y as o}from"./captureCompilerScenePreview-CeYQNPPP.js";import"./init-BTbgjve4.js";t.add(i),t.mixin(n,e),t.add(a),t.add(r),t.mixin(n,o);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{O as e,S as t,a as n,c as r,n as i,r as a,s as o,t as s}from"./captureCompilerScenePreview-CeYQNPPP.js";window.capturePixifactScene=async c=>{let[l,u]=await Promise.all([o(),r(c)]),d=await i({document:{template:e(u.source),sceneInterfaces:{}},projectTree:n(l),scenePath:c}),f=new t,p=!1;try{return await f.init({width:d.width,height:d.height,backgroundAlpha:0,antialias:!0,autoStart:!1,resolution:1}),p=!0,f.stage.addChild(d.root),{scenePath:u.path,revision:u.version,width:d.width,height:d.height,dataUrl:await s(f,d)}}finally{p&&(f.stage.removeChild(d.root),f.destroy({removeView:!0},{children:!1})),a(d)}};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{color:#d9dee7;font-synthesis:none;text-rendering:optimizelegibility;--bg:#111318;--panel:#171a20;--panel-raised:#1d2129;--canvas:#0d0f13;--line:#2a2f39;--line-strong:#39404d;--text:#d9dee7;--muted:#818997;--faint:#596171;--focus:#4c8dff;--focus-soft:#213a63;--success:#51b785;--warning:#d4a64f;--danger:#e36f6f;--control:#11141a;background:#111318;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}html,body,#app{width:100%;height:100%;margin:0;overflow:hidden}button,input,select{color:inherit;font:inherit;letter-spacing:0}button{border:0}.editor-shell{background:var(--bg);grid-template-rows:42px minmax(0,1fr);width:100%;height:100%;display:grid}.editor-session-standby{background:var(--bg);place-items:center;width:100%;height:100%;display:grid}.editor-session-standby>section{gap:18px;width:min(420px,100vw - 40px);display:grid}.standby-product{color:var(--faint);font-size:10px;font-weight:750}.editor-session-standby h1{color:var(--text);letter-spacing:0;margin:0;font-size:18px}.standby-context{border-top:1px solid var(--line);border-bottom:1px solid var(--line);grid-template-columns:76px minmax(0,1fr);gap:8px 12px;padding:14px 0;display:grid}.standby-context span{color:var(--muted);font-size:12px}.standby-context strong{min-width:0;color:var(--text);text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500;overflow:hidden}.takeover-button{background:var(--focus);color:#fff;cursor:pointer;border:1px solid #659cff;border-radius:4px;justify-self:start;min-width:104px;height:32px;padding:0 14px;font-size:12px;font-weight:650}.takeover-button:hover:not(:disabled){background:#5a96ff}.standby-note,.standby-error{margin:-6px 0 0;font-size:11px;line-height:1.6}.standby-note{color:var(--muted)}.standby-error{color:var(--danger)}.topbar{border-bottom:1px solid var(--line);background:#14171c;grid-template-columns:232px minmax(0,1fr) auto 112px;align-items:center;min-width:0;display:grid}.brand{border-right:1px solid var(--line);color:#f1f4f8;align-items:center;height:100%;padding:0 14px;font-size:11px;font-weight:750;display:flex}.scene-identity{align-items:baseline;gap:10px;min-width:0;padding:0 14px;display:flex}.scene-identity strong{flex:none;font-size:13px}.scene-identity span{min-width:0;color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.history-tools{align-items:center;gap:2px;padding-right:10px;display:flex}.history-tools button,.reset-button{width:28px;height:28px;color:var(--muted);cursor:pointer;background:0 0;border-radius:4px;place-items:center;padding:0;display:inline-grid}.history-tools button:hover:not(:disabled),.reset-button:hover:not(:disabled){background:var(--panel-raised);color:var(--text)}button:disabled{opacity:.35;cursor:default}.sync-state{border-left:1px solid var(--line);height:100%;color:var(--muted);align-items:center;gap:7px;padding:0 13px;font-size:11px;display:flex}.sync-state span{background:var(--success);border-radius:50%;width:7px;height:7px}.sync-state[data-state=saving] span{background:var(--warning)}.sync-state[data-state=conflict] span,.sync-state[data-state=error] span{background:var(--danger)}.workspace{grid-template-columns:minmax(190px,232px) minmax(360px,1fr) minmax(270px,296px);min-width:0;min-height:0;display:grid}.left-panel,.right-panel,.canvas-panel{min-width:0;min-height:0}.left-panel,.right-panel{background:var(--panel)}.left-panel{border-right:1px solid var(--line)}.right-panel{border-left:1px solid var(--line);overflow:auto}.left-tabs{grid-template-rows:34px minmax(0,1fr);height:100%;display:grid}.tab-list{border-bottom:1px solid var(--line);grid-template-columns:1fr 1fr;display:grid}.tab-trigger{color:var(--muted);cursor:pointer;background:0 0;font-size:11px;position:relative}.tab-trigger[data-state=active]{color:var(--text)}.tab-trigger[data-state=active]:after{background:var(--focus);content:"";height:2px;position:absolute;bottom:-1px;left:12px;right:12px}.tab-content{outline:none;min-height:0;overflow:auto}.panel-title,.inspector-section-title,.asset-group-label{color:var(--muted);font-size:10px;font-weight:700}.panel-title{border-bottom:1px solid var(--line);align-items:center;height:34px;padding:0 12px;display:flex}.tree-list{margin:0;padding:0;list-style:none}.hierarchy-panel{min-height:100%;padding:0 0 6px}.hierarchy-toolbar{z-index:2;border-bottom:1px solid var(--line);background:var(--panel);grid-template-columns:minmax(0,1fr) 27px 27px 27px;align-items:center;gap:2px;height:35px;padding:4px 6px;display:grid;position:sticky;top:0}.hierarchy-toolbar select{border:1px solid var(--line-strong);background:var(--control);width:100%;min-width:0;height:25px;color:var(--text);border-radius:3px;outline:none;padding:0 5px;font-size:10px}.hierarchy-toolbar select:focus{border-color:var(--focus)}.hierarchy-toolbar button{width:27px;height:27px;color:var(--muted);cursor:pointer;background:0 0;border-radius:3px;place-items:center;padding:0;display:grid}.hierarchy-toolbar button:hover:not(:disabled){background:var(--panel-raised);color:var(--text)}.tree-row{width:100%;height:27px;color:var(--text);text-align:left;cursor:default;background:0 0;grid-template-columns:15px 16px minmax(0,1fr) auto;align-items:center;gap:4px;padding-top:0;padding-bottom:0;padding-right:9px;display:grid;position:relative}.hierarchy-panel.is-dragging .tree-row{cursor:grabbing}.hierarchy-panel.is-asset-dragging .tree-row{cursor:copy}.tree-row.drop-inside{box-shadow:inset 0 0 0 1px var(--focus);background:#26364d}.tree-row.drop-before:before,.tree-row.drop-after:after{z-index:1;background:var(--focus);content:"";pointer-events:none;height:2px;position:absolute;left:5px;right:5px}.tree-row.drop-before:before{top:-1px}.tree-row.drop-after:after{bottom:-1px}.tree-row:hover{background:#20242c}.tree-row.selected{background:var(--focus-soft);color:#eef5ff}.tree-disclosure{width:15px;height:20px;color:var(--muted);place-items:center;display:grid}.tree-disclosure.empty{visibility:hidden}.tree-icon{color:#8da4c7}.tree-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:12px;overflow:hidden}.tree-row small{color:var(--faint);font-size:9px}.scene-root{cursor:default;padding-left:8px;font-weight:650}.scene-mark{color:#a9c8f8;border:1px solid #637a9d;border-radius:3px;place-items:center;width:14px;height:14px;font-size:8px;display:grid}.asset-list{padding:7px 0 14px}.asset-row{width:100%;height:29px;color:var(--text);text-align:left;background:0 0;grid-template-columns:14px 18px minmax(0,1fr);align-items:center;gap:5px;padding:0 12px;display:grid}button.asset-row{cursor:pointer}.asset-directory{color:var(--muted)}.asset-directory svg:nth-of-type(2){color:#a9c8f8}.asset-row[data-asset-draggable=true]{cursor:grab;-webkit-user-select:none;user-select:none}.asset-row:hover,.asset-row.selected{background:#20242c}.asset-row.selected{color:#a9ccff}.asset-row.focused{box-shadow:inset 2px 0 var(--focus);background:#2a3a52}.asset-row span{text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.asset-row .asset-row-spacer{width:14px}.asset-thumbnail{object-fit:contain;pointer-events:none;border-radius:2px;width:18px;height:18px}.asset-row.asset-draggable{color:var(--muted)}.asset-empty,.panel-empty{color:var(--faint);padding:14px 12px;font-size:12px}.panel-empty.compact{padding-top:18px}.canvas-panel{background:var(--canvas);position:relative}.scene-canvas-host{-webkit-user-select:none;user-select:none;position:absolute;inset:0;overflow:hidden}.scene-canvas-host.is-asset-drop-target:after{z-index:2;border:1px solid var(--focus);content:"";pointer-events:none;position:absolute;inset:3px}.canvas-grid{pointer-events:none;background-image:linear-gradient(#20242b 1px,#0000 1px),linear-gradient(90deg,#20242b 1px,#0000 1px);background-size:24px 24px;position:absolute;inset:0}.scene-canvas{display:block;position:absolute;inset:0}.scene-canvas-host.is-pan-ready .scene-canvas{cursor:grab!important}.scene-canvas-host.is-panning .scene-canvas{cursor:grabbing!important}.canvas-tools{z-index:3;position:absolute;top:10px;right:10px}.canvas-tools button{border:1px solid var(--line);background:var(--panel);width:28px;height:28px;color:var(--muted);cursor:pointer;border-radius:4px;place-items:center;padding:0;display:inline-grid}.canvas-tools button:hover:not(:disabled){border-color:var(--line-strong);background:var(--panel-raised);color:var(--text)}.canvas-status{border:1px solid var(--line);max-width:min(520px,100% - 48px);color:var(--muted);background:#15181e;border-radius:4px;padding:7px 10px;font-size:12px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.inspector-panel{min-height:0}.inspector-heading{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;min-height:54px;padding:8px 12px;display:flex}.inspector-heading div{gap:3px;min-width:0;display:grid}.inspector-heading strong{text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.inspector-heading small{color:var(--muted);font-size:10px}.inspector-section-title{border-bottom:1px solid var(--line);height:28px;padding:9px 12px 0}.property-row{border-bottom:1px solid #21252d;grid-template-columns:minmax(0,1fr);min-height:34px;padding:3px 8px 3px 12px;display:grid}.property-row.is-paired{grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.property-field{grid-template-columns:minmax(70px,.8fr) minmax(120px,1.4fr);align-items:center;min-width:0;display:grid}.property-row.is-paired .property-field{grid-template-columns:max-content minmax(0,1fr);align-items:center;gap:4px}.property-row.is-paired .property-field>span{font-size:10px}.property-row.is-paired .property-control{grid-template-columns:minmax(0,1fr) 20px;gap:2px}.property-row.is-paired .reset-button{width:20px}.property-field>span{color:var(--text);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.property-field>span.inherited{color:var(--muted)}.property-field.is-image-drop-candidate{box-shadow:inset 2px 0 var(--success);cursor:copy;background:#162b24}.property-field.is-image-drop-candidate:hover{background:#1b372d}.property-field.is-image-drop-candidate .property-control input[type=text]{border-color:var(--success)}.property-control{grid-template-columns:minmax(0,1fr) 24px;align-items:start;gap:3px;display:grid}.node-id-control{grid-template-columns:minmax(0,1fr)}.property-value{gap:3px;min-width:0;display:grid}.property-control input[type=text],.property-control input[type=number],.property-control select{border:1px solid var(--line-strong);background:var(--control);width:100%;min-width:0;height:25px;color:var(--text);border-radius:3px;outline:none;padding:0 7px;font-size:11px}.property-control input.resource-reference{cursor:alias}.property-control input:focus,.property-control select:focus{border-color:var(--focus)}.property-control input[type=text].is-invalid,.property-control input[type=text].is-invalid:focus{border-color:var(--danger)}.property-control input:disabled,.property-control select:disabled{color:#9ca6b5;cursor:default;opacity:1;background:#1b1f26;border-color:#303640}.property-control input[type=checkbox]{width:15px;height:15px;accent-color:var(--focus)}.property-control input[type=color]{border:1px solid var(--line-strong);background:var(--control);border-radius:3px;width:34px;height:24px;padding:2px}.reset-button{width:24px;height:24px}.binding-source{color:#8eb8e8;text-overflow:ellipsis;white-space:nowrap;align-items:center;gap:4px;min-width:0;font-size:10px;display:flex;overflow:hidden}.inline-error,.global-error{color:#ffd1d1;margin:0;font-size:11px}.inline-error{background:#2d1b20;border-bottom:1px solid #4f292f;padding:9px 12px}.global-error{background:#321e23;border:1px solid #66343c;border-radius:4px;max-width:min(520px,100vw - 24px);padding:9px 11px;position:fixed;bottom:12px;right:12px}@media (width<=860px){.workspace{grid-template-columns:190px minmax(320px,1fr) 270px}.topbar{grid-template-columns:190px minmax(0,1fr) auto 100px}}
|