@volter/editor-blender 0.1.78 → 0.1.79
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.
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
blenderExecute,
|
|
62
62
|
blenderViewShading,
|
|
63
63
|
openModelDocumentBlend,
|
|
64
|
+
modelDocumentMayOpen,
|
|
64
65
|
} from '../host/blender-runtime-host';
|
|
65
66
|
import { BlenderObjectModeHeader } from './blender-header-menus';
|
|
66
67
|
import { blenderOutlinerAuthoringFor, createBlenderOutlinerAuthoring } from './blender-outliner-authoring';
|
|
@@ -215,7 +216,16 @@ export default function BlenderModelDocument(props: ToolContributionProps) {
|
|
|
215
216
|
const unbind = bindModelDocument(binding);
|
|
216
217
|
const publish = () => { unpublish = callbacks.current.publishContext?.(view); };
|
|
217
218
|
setOpened(null);
|
|
218
|
-
|
|
219
|
+
// Native focus can arrive after the contributed pane mounts. A declined
|
|
220
|
+
// open is not an in-flight load: retry when the host activates this model,
|
|
221
|
+
// rather than leaving "Opening model…" latched forever. Utility focus
|
|
222
|
+
// after a successful open must not tear down the model's published view.
|
|
223
|
+
const documents = editorHost().documents;
|
|
224
|
+
let starting = false;
|
|
225
|
+
let finished = false;
|
|
226
|
+
const open = async () => {
|
|
227
|
+
if (cancelled || starting || finished || (binding && !modelDocumentMayOpen(binding))) return;
|
|
228
|
+
starting = true;
|
|
219
229
|
try {
|
|
220
230
|
if (binding) {
|
|
221
231
|
if (!await openModelDocumentBlend(binding, publish)) return;
|
|
@@ -223,18 +233,27 @@ export default function BlenderModelDocument(props: ToolContributionProps) {
|
|
|
223
233
|
// The standing Model document is the explicit blender-start target.
|
|
224
234
|
publish();
|
|
225
235
|
}
|
|
226
|
-
if (!cancelled)
|
|
236
|
+
if (!cancelled) {
|
|
237
|
+
finished = true;
|
|
238
|
+
setOpened({ key, error: null });
|
|
239
|
+
}
|
|
227
240
|
} catch (error) {
|
|
228
241
|
if (cancelled) return;
|
|
242
|
+
finished = true;
|
|
229
243
|
unpublish?.();
|
|
230
244
|
unpublish = undefined;
|
|
231
245
|
const detail = error instanceof Error ? error.message : String(error);
|
|
232
246
|
setOpened({ key, error: detail });
|
|
233
247
|
callbacks.current.notify?.({ tone: 'error', title: `Blender could not open ${blend ?? 'the model'}`, detail });
|
|
248
|
+
} finally {
|
|
249
|
+
starting = false;
|
|
234
250
|
}
|
|
235
|
-
}
|
|
251
|
+
};
|
|
252
|
+
const unsubscribe = documents.subscribe(() => { void open(); });
|
|
253
|
+
void open();
|
|
236
254
|
return () => {
|
|
237
255
|
cancelled = true;
|
|
256
|
+
unsubscribe();
|
|
238
257
|
unpublish?.();
|
|
239
258
|
unbind();
|
|
240
259
|
};
|
|
@@ -315,6 +315,15 @@ export function blenderPresentationDocumentId(): string {
|
|
|
315
315
|
return presentationDocumentId();
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/** The selected pane may load while a utility area (Timeline, for example)
|
|
319
|
+
* has keyboard focus. Another model remains an ownership barrier. */
|
|
320
|
+
export function modelDocumentMayOpen(binding: ModelDocumentBinding): boolean {
|
|
321
|
+
if (boundModel !== binding) return false;
|
|
322
|
+
const active = editorHost().documents.activeId();
|
|
323
|
+
const anotherModel = active?.startsWith('document:model:') || active === BLENDER_RUNTIME_DOCUMENT_ID;
|
|
324
|
+
return !anotherModel || active === binding.documentId;
|
|
325
|
+
}
|
|
326
|
+
|
|
318
327
|
/**
|
|
319
328
|
* OPEN A `.blend` IN THE ENGINE — the Model document's own call, the WS-F save
|
|
320
329
|
* path run backwards. `session.py` opens the named file at start and saves
|
|
@@ -333,7 +342,7 @@ export async function openModelDocumentBlend(
|
|
|
333
342
|
if (!host.session.open()) throw new Error('Opening a model requires an open project session.');
|
|
334
343
|
const project = host.projectLocalState.projectRootPath();
|
|
335
344
|
if (project === null) throw new Error('Opening a model requires a project path.');
|
|
336
|
-
if (
|
|
345
|
+
if (!modelDocumentMayOpen(binding)) return false;
|
|
337
346
|
const session = blenderRuntime();
|
|
338
347
|
// start claims its resource synchronously; its first frame may arrive before
|
|
339
348
|
// the returned promise resolves. The getter above rejects a conflicting file.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@volter/editor-blender",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "AGPL-3.0-only AND GPL-3.0-or-later",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.79",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
@@ -70,14 +70,14 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
73
|
-
"@volter/blender-engine": "0.1.
|
|
74
|
-
"@volter/editor-threejs": "0.5.
|
|
73
|
+
"@volter/blender-engine": "0.1.77",
|
|
74
|
+
"@volter/editor-threejs": "0.5.139",
|
|
75
75
|
"zod": "^4.3.6"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/editor-live": "0.5.
|
|
79
|
-
"@volter/editor-project": "0.5.
|
|
80
|
-
"@volter/editor-sdk": "0.5.
|
|
78
|
+
"@volter/editor-live": "0.5.139",
|
|
79
|
+
"@volter/editor-project": "0.5.139",
|
|
80
|
+
"@volter/editor-sdk": "0.5.139",
|
|
81
81
|
"react": "^19.0.0",
|
|
82
82
|
"react-dom": "^19.0.0",
|
|
83
83
|
"three": "^0.180.0",
|