@volter/editor-blender 0.1.150 → 0.1.152
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/LICENSE +18 -9
- package/README.md +5 -5
- package/contributions/blender-animation-editors.tsx +17 -69
- package/contributions/blender-display-settings.ts +36 -0
- package/contributions/blender-export-gltf.ts +228 -0
- package/contributions/blender-export.verbs.ts +39 -0
- package/contributions/blender-gltf-export.verbs.ts +312 -0
- package/contributions/blender-header-menus.tsx +37 -133
- package/contributions/blender-icon-trace.mjs +8 -98
- package/contributions/blender-icons.source.mjs +96 -860
- package/contributions/blender-mixer-pose.ts +369 -0
- package/contributions/blender-model-opening.tsx +26 -56
- package/contributions/blender-node-editor.document.tsx +30 -181
- package/contributions/blender-node-geometry.ts +44 -230
- package/contributions/blender-node-panels.source.mjs +7 -121
- package/contributions/blender-outliner-authoring.ts +61 -415
- package/contributions/blender-outliner-model.ts +15 -112
- package/contributions/blender-play-area.tsx +99 -0
- package/contributions/blender-playback-movie.ts +409 -0
- package/contributions/blender-pose.ts +18 -368
- package/contributions/blender-properties-context.ts +1 -1
- package/contributions/blender-properties-model.ts +59 -91
- package/contributions/blender-properties-tab.tsx +3 -50
- package/contributions/blender-properties-view.tsx +39 -181
- package/contributions/blender-remedy.ts +14 -0
- package/contributions/blender-runtime-skin.ts +103 -99
- package/contributions/blender-runtime.document.tsx +170 -618
- package/contributions/blender-scene-mixer.ts +51 -0
- package/contributions/blender-screenshot.service.ts +28 -0
- package/contributions/blender-timeline-geometry.ts +16 -136
- package/contributions/blender-timeline.document.tsx +145 -333
- package/contributions/blender-uv-editor.document.tsx +48 -112
- package/contributions/blender-uv-geometry.ts +14 -179
- package/contributions/blender-version.status.tsx +45 -31
- package/contributions/blender.environment.ts +2 -2
- package/contributions/blender.icons.json +0 -9
- package/contributions/blender.keymap.ts +9 -51
- package/contributions/blender.node-panels.json +4 -13
- package/contributions/blender.page.ts +64 -0
- package/contributions/blender.status.tsx +8 -150
- package/contributions/blender.style.ts +25 -152
- package/contributions/blender.verbs.ts +52 -0
- package/contributions/model.layout.ts +2 -34
- package/contributions/models.finder.ts +3 -30
- package/contributions/properties-bone-constraints.inspector.tsx +1 -16
- package/contributions/properties-bone.inspector.tsx +2 -31
- package/contributions/properties-collection.inspector.tsx +1 -34
- package/contributions/properties-constraints.inspector.tsx +1 -21
- package/contributions/properties-data.inspector.tsx +2 -39
- package/contributions/properties-material.inspector.tsx +2 -33
- package/contributions/properties-modifiers.inspector.tsx +2 -30
- package/contributions/properties-object.inspector.tsx +2 -44
- package/contributions/properties-output.inspector.tsx +1 -27
- package/contributions/properties-particles.inspector.tsx +2 -46
- package/contributions/properties-physics.inspector.tsx +2 -41
- package/contributions/properties-render.inspector.tsx +1 -45
- package/contributions/properties-scene.inspector.tsx +1 -35
- package/contributions/properties-texture.inspector.tsx +1 -49
- package/contributions/properties-view-layer.inspector.tsx +1 -29
- package/contributions/properties-world.inspector.tsx +2 -39
- package/contributions/shading.layout.ts +3 -78
- package/contributions/uv-editing.layout.ts +3 -70
- package/dist-node/serving.mjs +13 -32
- package/host/blender-runtime-host.ts +795 -412
- package/host/scene-sound.ts +143 -0
- package/node/blender-mcp.ts +52 -95
- package/package.json +1221 -46
- package/producticons/blender-icons.otf +0 -0
- package/producticons/blender-product-icon-theme.json +129 -0
- package/serving/blender-routes.ts +9 -48
- package/serving/blender-wasm-artifact.ts +29 -166
- package/serving/index.ts +1 -1
- package/src/api.ts +9 -0
- package/src/layouts.tsx +1 -17
- package/src/looks.ts +1 -12
- package/src/model-document-saving.ts +25 -0
- package/src/node-view-state.ts +3 -25
- package/src/play-mode.ts +13 -42
- package/src/play-tool.ts +44 -0
- package/src/presentation.ts +6 -18
- package/src/regions.ts +3 -43
- package/src/start-document.ts +15 -0
- package/src/timeline-view-state.ts +4 -39
- package/src/uv-view-state.ts +4 -20
- package/themes/blender.json +299 -0
- package/view/blender-view-routes.ts +1 -1
- package/contributions/blender-game-panel.tsx +0 -809
- package/contributions/blender-palette.source.mjs +0 -350
- package/contributions/blender-play-skin.ts +0 -508
- package/contributions/blender-screenshot.menu.ts +0 -32
- package/contributions/blender.command.ts +0 -109
- package/contributions/blender.palette.json +0 -102
- package/src/play-live.ts +0 -27
|
@@ -1,49 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The UV EDITING workspace — Blender's own: the UV editor BESIDE the 3D
|
|
3
|
-
* viewport, with the Outliner over the Properties editor on the right.
|
|
4
|
-
*
|
|
5
|
-
* THE PROPORTIONS ARE BLENDER'S, MEASURED THROUGH bpy on the engine in the tab
|
|
6
|
-
* (2026-09-19), not eyeballed from a screenshot:
|
|
7
|
-
* `bpy.data.workspaces['UV Editing'].screens[0].areas` answers, in a 1920-wide
|
|
8
|
-
* window, PROPERTIES `x1579 y23 w339 h839`, OUTLINER `x1579 y865 w339 h189`,
|
|
9
|
-
* IMAGE_EDITOR `x2 y23 w786 h1031` with `SpaceImage.mode == 'UV'`, and VIEW_3D
|
|
10
|
-
* `x791 y23 w785 h1031`.
|
|
11
|
-
*
|
|
12
|
-
* Three readings out of that:
|
|
13
|
-
* - the right column is the SAME column Modeling and Shading have — 339/1920
|
|
14
|
-
* = 0.177 of the window, the Outliner taking 189/1028 = 0.184 of it — which
|
|
15
|
-
* is why this arrangement is Model's with one change;
|
|
16
|
-
* - the centre is split SIDE BY SIDE, not top-and-bottom: 786 and 785 of the
|
|
17
|
-
* 1571 px left of the right column, an exact 50/50 to within the 1-px
|
|
18
|
-
* splitter. This is the FIRST I5 workspace whose editor is not Blender's
|
|
19
|
-
* bottom area — Shading's node editor is (`workspace-regions.ts` says so
|
|
20
|
-
* where it explains why Model hides the bottom strip), and the UV editor is
|
|
21
|
-
* a left half of the centre;
|
|
22
|
-
* - the UV editor opens in UV mode, which is what makes its title "UV Editor"
|
|
23
|
-
* rather than "Image Editor".
|
|
24
|
-
*
|
|
25
|
-
* WHICH REGION THE UV EDITOR IS — and the deviation this file used to record
|
|
26
|
-
* is GONE, because the ruling removed its cause. It said: this host has ONE
|
|
27
|
-
* utility region, the bottom area, so a utility could not claim a leaf beside
|
|
28
|
-
* the centre and the UV editor took the bottom at the height Blender gives
|
|
29
|
-
* its width. **RULED 2026-09-19 (orchestrator): a Blender editor AREA is an
|
|
30
|
-
* editor group; the drawer holds utilities.** The UV editor is not a utility
|
|
31
|
-
* at all now — it is a `workspace.document` this workspace opens into
|
|
32
|
-
* `volter:area:uv`, LEFT of the model document at Blender's own 786/1571, and
|
|
33
|
-
* under the frame the layout host puts the same document in a second VS Code
|
|
34
|
-
* editor group. Nothing is deviating and nothing is owed to U10 here.
|
|
35
|
-
*
|
|
36
|
-
* WHAT IS NOT REPRODUCED, also named: Blender's UV editor shows the IMAGE
|
|
37
|
-
* behind the tile. Measured on both probe files — `arena-weapons.blend` has
|
|
38
|
-
* four materials, all `use_nodes`, and NOT ONE image texture node; the whole
|
|
39
|
-
* file has zero images, as does `arena-vanguard.blend` — so there is nothing
|
|
40
|
-
* for a backdrop to show and the view says so in its own warning rather than
|
|
41
|
-
* implying an empty tile is a drawn one.
|
|
42
|
-
*
|
|
43
|
-
* THE STRIP GROWS ONLY AS A VIEW LANDS (§Editor chrome; WORK.md I5). UV
|
|
44
|
-
* Editing appears in the workspace strip because this contribution exists, and
|
|
45
|
-
* this contribution exists because the UV view does.
|
|
46
|
-
*/
|
|
1
|
+
/** UV Editing places the UV Editor left of the 3D view, with Outliner over Properties at right. The UV view opens in UV mode and names a missing image backdrop in its warning. */
|
|
47
2
|
|
|
48
3
|
import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
|
|
49
4
|
import { BLENDER_REGIONS } from '../src/regions';
|
|
@@ -68,29 +23,7 @@ export const layout: WorkspaceLayoutContribution = {
|
|
|
68
23
|
// is a utility strip. The UV editor is an AREA — an editor GROUP — below.
|
|
69
24
|
drawer: 'hidden',
|
|
70
25
|
},
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// RULED 2026-09-19 (orchestrator): "A Blender editor AREA is an editor
|
|
74
|
-
// group; the drawer holds utilities." Blender's UV Editing screen splits the
|
|
75
|
-
// centre SIDE BY SIDE, with the image editor on the LEFT: IMAGE_EDITOR
|
|
76
|
-
// `x2 y23 w786 h1031` (mode `UV`) and VIEW_3D `x791 y23 w785 h1031`,
|
|
77
|
-
// measured through bpy on the engine in a 1920-wide window.
|
|
78
|
-
//
|
|
79
|
-
// THE RATIO IS 786 of the 1571 px left of the 339-px right column — 0.5003,
|
|
80
|
-
// an exact half to within the 1-px splitter. It is stated to four places
|
|
81
|
-
// because it is a measurement, not a preference; the dock uses it once, to
|
|
82
|
-
// size the group the first time this workspace stands up, and a capture of
|
|
83
|
-
// the settled grid wins afterwards.
|
|
26
|
+
// Open the UV Editor in the left editor group at 786/1571 ≈ 0.5003 of the centre width.
|
|
84
27
|
areas: [{ id: 'uv', document: 'blender-uv-editor.document', place: 'left', ratio: 0.5003 }],
|
|
85
|
-
//
|
|
86
|
-
// ruling. The one it replaced was Shading's capture with the node view's
|
|
87
|
-
// utility id substituted by hand — and that substitution MISSED TWO of the
|
|
88
|
-
// seven occurrences: the panel record's `params.utilityId` and its `title`
|
|
89
|
-
// both still said `blender-node-editor.utility` / "Shader Editor", so the
|
|
90
|
-
// leaf keyed by the UV panel's id rendered the NODE editor. That is the
|
|
91
|
-
// whole of the "switching to UV Editing opens the Shader Editor" defect the
|
|
92
|
-
// previous landing recorded as a dock ordering race; it was a bad derived
|
|
93
|
-
// blob, which is exactly what `workspace-preset-layouts.ts` forbids
|
|
94
|
-
// ("shipped defaults are authored by arranging live and snapshotting
|
|
95
|
-
// `toJSON` — never hand-written JSON"). This one is captured.
|
|
28
|
+
// The arrangement is a captured dock layout for these UV editor documents.
|
|
96
29
|
};
|
package/dist-node/serving.mjs
CHANGED
|
@@ -24,7 +24,7 @@ function projectOutputRootOf(path) {
|
|
|
24
24
|
);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// ../sdk/src/
|
|
27
|
+
// ../sdk/src/relative-path-guard.ts
|
|
28
28
|
function relativePathRejection(path) {
|
|
29
29
|
if (!path || path.trim() === "") return "empty";
|
|
30
30
|
if (path.includes("\0")) return "nul";
|
|
@@ -65,8 +65,8 @@ var SHIPPED_BLENDER_WASM_DIR = (() => {
|
|
|
65
65
|
const candidate = fileURLToPath(new URL("../../blender-engine/wasm/", import.meta.url));
|
|
66
66
|
return existsSync(candidate) ? candidate.replace(/\/$/, "") : null;
|
|
67
67
|
})();
|
|
68
|
-
function blenderWasmDir(
|
|
69
|
-
const named = env[BLENDER_WASM_DIR_VARIABLE];
|
|
68
|
+
function blenderWasmDir() {
|
|
69
|
+
const named = process.env[BLENDER_WASM_DIR_VARIABLE];
|
|
70
70
|
if (typeof named === "string" && named.trim() !== "") {
|
|
71
71
|
const dir = named.trim();
|
|
72
72
|
return isAbsolute(dir) ? dir : null;
|
|
@@ -92,8 +92,8 @@ async function skewOf(dir) {
|
|
|
92
92
|
if (existsSync(join(dir, BLENDER_WALI_ARTIFACT))) return "wali";
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
|
-
async function blenderWasmStatus(
|
|
96
|
-
const dir = blenderWasmDir(
|
|
95
|
+
async function blenderWasmStatus() {
|
|
96
|
+
const dir = blenderWasmDir();
|
|
97
97
|
if (dir === null)
|
|
98
98
|
return {
|
|
99
99
|
available: false,
|
|
@@ -117,13 +117,13 @@ async function blenderWasmStatus(env = process.env) {
|
|
|
117
117
|
`${dir} holds neither ${BLENDER_WASM_FILES.join(", ")} (the standalone skew) nor ${BLENDER_WALI_ARTIFACT} (the substrate skew)`
|
|
118
118
|
]
|
|
119
119
|
};
|
|
120
|
-
return skew === "wali" ? waliStatus(dir) : emscriptenStatus(dir
|
|
120
|
+
return skew === "wali" ? waliStatus(dir) : emscriptenStatus(dir);
|
|
121
121
|
}
|
|
122
122
|
var BROWSER_SUBSTRATE_VARIABLE = "BROWSER_SUBSTRATE_URL";
|
|
123
|
-
function servedFromTab(
|
|
124
|
-
return (env[BROWSER_SUBSTRATE_VARIABLE] ?? "").trim() !== "";
|
|
123
|
+
function servedFromTab() {
|
|
124
|
+
return (process.env[BROWSER_SUBSTRATE_VARIABLE] ?? "").trim() !== "";
|
|
125
125
|
}
|
|
126
|
-
async function emscriptenStatus(dir
|
|
126
|
+
async function emscriptenStatus(dir) {
|
|
127
127
|
const sizes = {};
|
|
128
128
|
const encoded = {};
|
|
129
129
|
const missing = [];
|
|
@@ -136,7 +136,7 @@ async function emscriptenStatus(dir, env) {
|
|
|
136
136
|
sizes[file] = found.size;
|
|
137
137
|
if (found.encoding) encoded[file] = found.encoding;
|
|
138
138
|
}
|
|
139
|
-
return { available: missing.length === 0, skew: "emscripten", dir, sizes, encoded, missing, digests: await emscriptenDigests(dir), inTab: servedFromTab(
|
|
139
|
+
return { available: missing.length === 0, skew: "emscripten", dir, sizes, encoded, missing, digests: await emscriptenDigests(dir), inTab: servedFromTab() };
|
|
140
140
|
}
|
|
141
141
|
async function emscriptenDigests(dir) {
|
|
142
142
|
const digests = {};
|
|
@@ -217,7 +217,6 @@ async function runtimeIndex(dir) {
|
|
|
217
217
|
if (cachedRuntimeIndex?.dir === dir) return cachedRuntimeIndex.index;
|
|
218
218
|
const root = join(dir, BLENDER_WALI_RUNTIME);
|
|
219
219
|
const files = [];
|
|
220
|
-
let newest = 0;
|
|
221
220
|
const walk = async (where) => {
|
|
222
221
|
for (const entry of (await readdir(where, { withFileTypes: true })).sort(
|
|
223
222
|
(left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0
|
|
@@ -235,12 +234,11 @@ async function runtimeIndex(dir) {
|
|
|
235
234
|
mode: info.mode & 4095,
|
|
236
235
|
sha256: createHash("sha256").update(await readFile(path)).digest("hex")
|
|
237
236
|
});
|
|
238
|
-
newest = Math.max(newest, info.mtimeMs);
|
|
239
237
|
}
|
|
240
238
|
};
|
|
241
239
|
await walk(root);
|
|
242
240
|
if (files.length === 0) throw new Error(`${root} is empty`);
|
|
243
|
-
const index = { files
|
|
241
|
+
const index = { files };
|
|
244
242
|
cachedRuntimeIndex = { dir, index };
|
|
245
243
|
return index;
|
|
246
244
|
}
|
|
@@ -354,13 +352,10 @@ function blenderRoutesPlugin(services) {
|
|
|
354
352
|
method: "GET",
|
|
355
353
|
match: /^\/__editor\/blender-wasm\/status$/,
|
|
356
354
|
handle: async (_req, res) => {
|
|
357
|
-
services.allowCrossOriginFrameEmbedding(res);
|
|
358
355
|
json(res, await blenderWasmStatus());
|
|
359
356
|
}
|
|
360
357
|
},
|
|
361
|
-
//
|
|
362
|
-
// one index and one blob (a request per file would be ~2,300 round trips), and the
|
|
363
|
-
// substrate's browser ESM with its bare specifiers rewritten.
|
|
358
|
+
// Serve the substrate runtime tree as one index and blob, plus browser ESM with rewritten bare specifiers.
|
|
364
359
|
{
|
|
365
360
|
method: "GET",
|
|
366
361
|
match: /^\/__editor\/blender-wasm\/runtime\.idx$/,
|
|
@@ -430,7 +425,6 @@ function blenderRoutesPlugin(services) {
|
|
|
430
425
|
match: /^\/__editor\/blender-wasm\/([^/]+)$/,
|
|
431
426
|
handle: async (req, res, url) => {
|
|
432
427
|
const file = decodeURIComponent(url.pathname.slice("/__editor/blender-wasm/".length));
|
|
433
|
-
services.allowCrossOriginFrameEmbedding(res);
|
|
434
428
|
const bundled = BLENDER_WASM_FILES.includes(file);
|
|
435
429
|
if (!bundled && file !== BLENDER_WALI_ARTIFACT) {
|
|
436
430
|
json(res, { error: `The Blender build has no ${file}.` }, 404);
|
|
@@ -657,20 +651,7 @@ function blenderRoutesPlugin(services) {
|
|
|
657
651
|
}
|
|
658
652
|
}
|
|
659
653
|
},
|
|
660
|
-
//
|
|
661
|
-
// `.blend`; its `save_as_mainfile` writes it into the engine's filesystem, and this is how
|
|
662
|
-
// those bytes reach the disk the project lives on. Unlike the spool, the destination shape is
|
|
663
|
-
// pinned BY THIS SERVER, and the write is the project's one attributed, conflict-checked
|
|
664
|
-
// transaction: a `.blend` is the project's file like any other.
|
|
665
|
-
//
|
|
666
|
-
// IT ARRIVES AS A DELTA. The page cuts each save into content-defined chunks
|
|
667
|
-
// (`blender-engine/browser/document-chunks.mts`) and posts their manifest; this answers with
|
|
668
|
-
// the chunks it does not already hold, the page posts those, and the manifest again commits.
|
|
669
|
-
// MEASURED 2026-09-29, native Blender 5.2 on the Stoneguard bridge: a 497 MB save, one object
|
|
670
|
-
// moved, changed 1 of 475 one-MiB chunks, so an edit carries about a megabyte instead of the
|
|
671
|
-
// whole file. Chunks this server reuses come from the document as it lies on disk, and each
|
|
672
|
-
// is hashed again against the manifest before the commit: a file changed underneath (another
|
|
673
|
-
// writer, a restore) is not drift but a missing chunk, re-sent.
|
|
654
|
+
// Save the session .blend through the attributed, conflict-checked project transaction. A chunk manifest requests only missing bytes. Rehash reused disk chunks at commit so an external file change requests replacement chunks.
|
|
674
655
|
{
|
|
675
656
|
method: "POST",
|
|
676
657
|
match: /^\/__editor\/blender-document-chunk$/,
|