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.
@@ -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";
@@ -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-DkKec9UG.js"></script>
7
- <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-CeYQNPPP.js">
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-CCOqWQMU.js"></script>
8
- <link rel="modulepreload" crossorigin href="/assets/captureCompilerScenePreview-CeYQNPPP.js">
9
- <link rel="stylesheet" crossorigin href="/assets/editor-B_YR_yGH.css">
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.12.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.12.0",
35
+ "pixifact": "^0.14.0",
36
36
  "playwright": "1.58.2",
37
37
  "vite": "^8.0.10"
38
38
  }
@@ -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
- if (directories === undefined) return {};
180
- if (!Array.isArray(directories) || directories.some((directory) => typeof directory !== 'string')) {
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
- return { assetTreeExpandedDirectories: directories };
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
  }
@@ -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 = 3;
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 || parsed.type !== 'editorContextChanged') return;
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}}