@volter/editor-blender 0.1.149 → 0.1.151
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 +3 -7
- package/contributions/blender-node-editor.document.tsx +28 -180
- 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 +100 -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 +15 -90
- package/contributions/blender-properties-tab.tsx +3 -50
- package/contributions/blender-properties-view.tsx +15 -147
- package/contributions/blender-remedy.ts +14 -0
- package/contributions/blender-runtime-skin.ts +103 -99
- package/contributions/blender-runtime.document.tsx +217 -339
- 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 +142 -332
- package/contributions/blender-uv-editor.document.tsx +48 -112
- package/contributions/blender-uv-geometry.ts +14 -179
- package/contributions/blender-version.status.tsx +43 -23
- 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 +63 -0
- package/contributions/blender.status.tsx +9 -145
- 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 +12 -32
- 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 -30
- package/host/blender-runtime-host.ts +541 -372
- package/host/scene-sound.ts +143 -0
- package/node/blender-mcp.ts +49 -93
- package/package.json +1209 -46
- package/producticons/blender-icons.otf +0 -0
- package/producticons/blender-product-icon-theme.json +129 -0
- package/serving/blender-routes.ts +9 -43
- 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/model-play-extension.ts +173 -0
- package/src/node-view-state.ts +3 -25
- package/src/play-mode.ts +64 -38
- 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,10 @@
|
|
|
1
|
-
|
|
2
|
-
* THE NODE EDITOR — Blender's Shader Editor, READ-ONLY, over the active
|
|
3
|
-
* material's RNA node tree (WORK.md §Blender in the tab is Blender,
|
|
4
|
-
* "Inspection parity", I5; ARCHITECTURE-CORE §Blender north star).
|
|
5
|
-
*
|
|
6
|
-
* OUR PANEL, BLENDER'S DRAWING. Blender's own UI layer — `bl_ui`, the node
|
|
7
|
-
* editor's C++ drawing code — is never run, ported as a UI system, or
|
|
8
|
-
* recorded here. It is READ as the specification of what to draw, and every
|
|
9
|
-
* number it specifies lives in `./blender-node-geometry.ts` with its file and
|
|
10
|
-
* line beside it. This module is an ordinary React component over that data.
|
|
11
|
-
*
|
|
12
|
-
* INSPECTION PARITY, NOT EDITING PARITY. Nothing here writes. A gesture that
|
|
13
|
-
* WOULD edit — dragging a node, dragging a link, typing into a socket's value
|
|
14
|
-
* — is refused BY NAME in the frame's status line rather than quietly doing
|
|
15
|
-
* nothing, which is the ruling's own "never a silent degrade". The only state
|
|
16
|
-
* this view owns is INSPECTION state: which node is looked at, and where the
|
|
17
|
-
* view is panned. Selecting a node here does not write `Node.select` in the
|
|
18
|
-
* engine (that would be a mutation the document saves); the ENGINE's own
|
|
19
|
-
* selection is still drawn, in Blender's own selected outline.
|
|
20
|
-
*
|
|
21
|
-
* WHY SVG AND NOT CANVAS 2D. The tree is a few dozen rounded rects, circles
|
|
22
|
-
* and cubics, and SVG gives three things a 2D context would have to
|
|
23
|
-
* reimplement: the browser's own `C` path segment for `node_link_bezier`'s
|
|
24
|
-
* cubic, real text layout and measurement for the header labels, and hit
|
|
25
|
-
* testing for free on every node and socket. The measured tree this unit was
|
|
26
|
-
* built against is 2 nodes and 37 sockets; a pathological 200-node tree is
|
|
27
|
-
* ~1,200 elements, which is inside what a browser lays out in one frame. The
|
|
28
|
-
* moment a tree is measured slow, the answer is a canvas with the same
|
|
29
|
-
* geometry module behind it — which is why the geometry lives in its own
|
|
30
|
-
* module and this file holds no constants.
|
|
31
|
-
*
|
|
32
|
-
* WHY IT IS THE DRAWER AND NOT A SECOND DOCUMENT. Blender's Shading workspace
|
|
33
|
-
* puts the Shader Editor in the AREA BELOW the 3D viewport, and this host's
|
|
34
|
-
* bottom group (`volter:bottom-center`) is that area: the Model workspace hides
|
|
35
|
-
* it for exactly this reason, recorded in `workspace-regions.ts` — "Blender's
|
|
36
|
-
* modeling workspace has no timeline strip". The Shading workspace shows it
|
|
37
|
-
* and puts this in it.
|
|
38
|
-
*/
|
|
1
|
+
|
|
39
2
|
|
|
40
3
|
import type { BlenderNode, BlenderNodeTree } from '@volter/blender-engine/browser/rna';
|
|
41
|
-
import { registerViewVerbs } from '@volter/sdk/views';
|
|
42
4
|
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
43
5
|
import {
|
|
44
6
|
blenderNodeTree,
|
|
45
7
|
blenderRnaVersion,
|
|
46
|
-
NODE_VIEW_VERBS,
|
|
47
8
|
subscribeBlenderRna,
|
|
48
9
|
} from '../host/blender-runtime-host';
|
|
49
10
|
import type { NodeViewTransform } from '../src/node-view-state';
|
|
@@ -107,26 +68,7 @@ export const point = 'workspace.document';
|
|
|
107
68
|
* the editor-type menu): "Shader Editor" for a `ShaderNodeTree`. */
|
|
108
69
|
export const title = 'Shader Editor';
|
|
109
70
|
|
|
110
|
-
|
|
111
|
-
// view's verbs are published ONCE, here, where the view itself is contributed:
|
|
112
|
-
// under the Code-OSS frame each becomes a `volter.blender-node-view.<verb>`
|
|
113
|
-
// command the bridge dispatches into the view, and standalone `cyclotron edit` —
|
|
114
|
-
// which has no command service — reaches the SAME table through the session's
|
|
115
|
-
// `blender-node-view` verb. One table, two doors, which is why the remaining
|
|
116
|
-
// read-only editors (UV Editing, Animation, Texture Paint) add no session verb
|
|
117
|
-
// of their own: they register here instead.
|
|
118
|
-
// THIS LINE RUNS ONCE PER EVALUATION, and a contribution is evaluated more
|
|
119
|
-
// than once per session by design — the tool loader imports it as
|
|
120
|
-
// `?t=<version>`, so a save re-evaluates it against a registry that already
|
|
121
|
-
// holds the view. `registerViewVerbs` recognises the re-evaluation and the
|
|
122
|
-
// newer table wins; measured live 2026-09-19, before it did, the first reload
|
|
123
|
-
// threw and the loader reported "editor app failed to start", taking the whole
|
|
124
|
-
// editor down with the view.
|
|
125
|
-
registerViewVerbs(NODE_VIEW_VERBS);
|
|
126
|
-
|
|
127
|
-
/** The N-panel's width. `bl_ui` sizes a sidebar in `UI_UNIT_X`; the node
|
|
128
|
-
* editor's sidebar opens at the region default, measured in Blender 5.2 at
|
|
129
|
-
* ~16 units. */
|
|
71
|
+
/** The node sidebar opens at about 16 UI_UNIT_X units in Blender 5.2. */
|
|
130
72
|
const SIDEBAR_WIDTH = 16 * 20;
|
|
131
73
|
|
|
132
74
|
/** One unit of tree space per CSS px is Blender's zoom 1 — the node editor
|
|
@@ -137,23 +79,13 @@ const ZOOM_MAX = 4;
|
|
|
137
79
|
type View = NodeViewTransform;
|
|
138
80
|
|
|
139
81
|
export default function BlenderNodeEditor() {
|
|
140
|
-
//
|
|
141
|
-
// 2026-09-19). This used to read the properties model's version, which moves
|
|
142
|
-
// on a presented frame — and collapsing a socket panel writes
|
|
143
|
-
// `Node.panel_states[n].is_collapsed`, which presents NOTHING, so the view
|
|
144
|
-
// kept drawing the old tree until the document was reopened (measured by the
|
|
145
|
-
// socket-panel step that shipped the panels). `blenderRnaVersion` is the RNA
|
|
146
|
-
// door's own, bumped by the write, and a frame bumps it too.
|
|
82
|
+
// Use the RNA door’s version: every write and presented frame bumps it, including panel-state writes that change no rendered geometry.
|
|
147
83
|
const version = useSyncExternalStore(subscribeBlenderRna, blenderRnaVersion, blenderRnaVersion);
|
|
148
84
|
const [tree, setTree] = useState<BlenderNodeTree | null>(null);
|
|
149
85
|
const [error, setError] = useState<string | null>(null);
|
|
150
86
|
const canvas = useRef<HTMLDivElement | null>(null);
|
|
151
87
|
const framed = useRef(false);
|
|
152
|
-
//
|
|
153
|
-
// component's, because the session verb `blender-node-view` has to reach it:
|
|
154
|
-
// `editor.document.*` is scoped to the active CENTER document and a drawer
|
|
155
|
-
// utility is not one, so a `useState` here would be a surface nothing in the
|
|
156
|
-
// product could read or drive.
|
|
88
|
+
// Share view state with the blender-node-view command through node-view-state.ts.
|
|
157
89
|
useSyncExternalStore(subscribeNodeView, nodeViewVersion, nodeViewVersion);
|
|
158
90
|
const { transform: view, looked, refusal, size } = nodeViewState();
|
|
159
91
|
const setView = useCallback((next: View | ((current: View) => View)): void => {
|
|
@@ -272,7 +204,7 @@ export default function BlenderNodeEditor() {
|
|
|
272
204
|
// `VIEW2D_OT_pan`, bound to `MIDDLEMOUSE` in the default keymap).
|
|
273
205
|
if (event.button !== 1) return;
|
|
274
206
|
event.preventDefault();
|
|
275
|
-
(event.target as Element).setPointerCapture
|
|
207
|
+
(event.target as Element).setPointerCapture(event.pointerId);
|
|
276
208
|
panning.current = { x: event.clientX, y: event.clientY, cx: view.cx, cy: view.cy };
|
|
277
209
|
};
|
|
278
210
|
const onPointerMove = (event: React.PointerEvent<SVGSVGElement>) => {
|
|
@@ -310,11 +242,7 @@ export default function BlenderNodeEditor() {
|
|
|
310
242
|
>
|
|
311
243
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: 0, minHeight: 0 }}>
|
|
312
244
|
<NodeEditorHeader tree={tree} zoom={view.zoom} onViewAll={viewAll} onRefuse={refuse} />
|
|
313
|
-
{/*
|
|
314
|
-
content: a `height: 100%` SVG inside an auto-height parent makes
|
|
315
|
-
the parent's height a function of the SVG's, which is a function
|
|
316
|
-
of the parent's — measured live 2026-09-19, that loop settled at
|
|
317
|
-
~30 px and drew the whole tree in a sliver at 31 % zoom. */}
|
|
245
|
+
{/* Flex supplies the canvas height; a percentage-height SVG inside an auto-height parent would size the parent from its own content. */}
|
|
318
246
|
<div ref={canvas} style={{ position: 'relative', flex: 1, minHeight: 0 }}>
|
|
319
247
|
<svg
|
|
320
248
|
role="presentation"
|
|
@@ -383,8 +311,6 @@ interface LaidLink {
|
|
|
383
311
|
readonly key: string;
|
|
384
312
|
readonly from: readonly [number, number];
|
|
385
313
|
readonly to: readonly [number, number];
|
|
386
|
-
readonly fromColor: string;
|
|
387
|
-
readonly toColor: string;
|
|
388
314
|
readonly muted: boolean;
|
|
389
315
|
readonly valid: boolean;
|
|
390
316
|
}
|
|
@@ -419,7 +345,6 @@ function layoutTree(tree: BlenderNodeTree | null): {
|
|
|
419
345
|
const placed = new Map<string, LaidOutNode>();
|
|
420
346
|
const entries = tree.nodes.map((node): LaidEntry => {
|
|
421
347
|
const flat = {
|
|
422
|
-
name: node.name,
|
|
423
348
|
location: node.location,
|
|
424
349
|
width: node.width,
|
|
425
350
|
collapsed: node.collapsed,
|
|
@@ -469,9 +394,8 @@ function layoutTree(tree: BlenderNodeTree | null): {
|
|
|
469
394
|
return { node, laid, panels: [], fallback: null, approximate: false };
|
|
470
395
|
});
|
|
471
396
|
const links: LaidLink[] = tree.links.flatMap((link, index) => {
|
|
472
|
-
const from = placed.get(link.fromNode)
|
|
473
|
-
const to = placed.get(link.toNode)
|
|
474
|
-
if (!from || !to) return [];
|
|
397
|
+
const from = placed.get(link.fromNode)!;
|
|
398
|
+
const to = placed.get(link.toNode)!;
|
|
475
399
|
const fromSocket = from.sockets.find((s) => s.output && s.identifier === link.fromSocket);
|
|
476
400
|
const toSocket = to.sockets.find((s) => !s.output && s.identifier === link.toSocket);
|
|
477
401
|
if (!fromSocket || !toSocket) return [];
|
|
@@ -480,8 +404,6 @@ function layoutTree(tree: BlenderNodeTree | null): {
|
|
|
480
404
|
key: `${link.fromNode}.${link.fromSocket}→${link.toNode}.${link.toSocket}#${index}`,
|
|
481
405
|
from: [fromSocket.x, fromSocket.y] as const,
|
|
482
406
|
to: [toSocket.x, toSocket.y] as const,
|
|
483
|
-
fromColor: socketColor(fromSocket.type),
|
|
484
|
-
toColor: socketColor(toSocket.type),
|
|
485
407
|
muted: link.muted,
|
|
486
408
|
valid: link.valid,
|
|
487
409
|
},
|
|
@@ -837,11 +759,7 @@ function PanelHeader({
|
|
|
837
759
|
);
|
|
838
760
|
}
|
|
839
761
|
|
|
840
|
-
/** Blender's
|
|
841
|
-
* TYPE's colour with a 1 px `TH_WIRE` outline, shaped by `display_shape`
|
|
842
|
-
* (`node_draw.cc:1824-1857`; the shapes themselves are SDFs in
|
|
843
|
-
* `gpu_shader_2D_node_socket*.glsl`, which is outside the sparse checkout —
|
|
844
|
-
* the silhouettes below are drawn from the enum's own names). */
|
|
762
|
+
/** Socket marks use Blender's type colours, TH_WIRE outlines and display_shape silhouettes. */
|
|
845
763
|
function Socket({
|
|
846
764
|
socket,
|
|
847
765
|
node,
|
|
@@ -925,12 +843,8 @@ function Socket({
|
|
|
925
843
|
* `:529`), which is why an output's name is right-aligned against the node's
|
|
926
844
|
* edge and an input's widget fills the row.
|
|
927
845
|
*
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
* `foreignObject` per socket and would not scale with the view transform, so
|
|
931
|
-
* it would stop being the same drawing at any zoom but 1. The Properties
|
|
932
|
-
* rail's widgets are reused where they belong — the N-panel, which is
|
|
933
|
-
* ordinary unzoomed DOM.
|
|
846
|
+
* SVG widgets use the node canvas's tree-space transform. The N-panel uses
|
|
847
|
+
* ordinary unzoomed DOM controls.
|
|
934
848
|
*/
|
|
935
849
|
function SocketRow({
|
|
936
850
|
socket,
|
|
@@ -965,6 +879,7 @@ function SocketRow({
|
|
|
965
879
|
}
|
|
966
880
|
|
|
967
881
|
const isColour = Array.isArray(socket.value) && socket.value.length >= 3;
|
|
882
|
+
const fraction = sliderFraction(socket);
|
|
968
883
|
return (
|
|
969
884
|
<g
|
|
970
885
|
onPointerDown={(event) => {
|
|
@@ -989,15 +904,8 @@ function SocketRow({
|
|
|
989
904
|
fill="#545454"
|
|
990
905
|
transform="scale(1 -1)"
|
|
991
906
|
/>
|
|
992
|
-
{/*
|
|
993
|
-
|
|
994
|
-
value's proportion of that range (`widget_numslider`,
|
|
995
|
-
`interface_widgets.cc`) — which is why Roughness at 0.5 reads as
|
|
996
|
-
half-filled at a glance and ours read as a text box (sighted read,
|
|
997
|
-
2026-09-20). No range means no fill, which is also what Blender does
|
|
998
|
-
for an unbounded number. The fill is clipped to the field's own
|
|
999
|
-
rounded rect so a full-value slider does not square off its corners. */}
|
|
1000
|
-
{sliderFraction(socket) !== null && (
|
|
907
|
+
{/* A bounded scalar socket fills its rounded field to its fraction of the soft range (widget_numslider). Unbounded values have no fill. */}
|
|
908
|
+
{fraction !== null && (
|
|
1001
909
|
<>
|
|
1002
910
|
<clipPath id={`sl-${socket.identifier}`}>
|
|
1003
911
|
<rect
|
|
@@ -1011,7 +919,7 @@ function SocketRow({
|
|
|
1011
919
|
<rect
|
|
1012
920
|
x={left}
|
|
1013
921
|
y={centre - NODE_DY / 2 + 1}
|
|
1014
|
-
width={(right - left) *
|
|
922
|
+
width={(right - left) * fraction}
|
|
1015
923
|
height={NODE_DY - 2}
|
|
1016
924
|
fill={NODE_THEME.sliderFill}
|
|
1017
925
|
clipPath={`url(#sl-${socket.identifier})`}
|
|
@@ -1037,7 +945,7 @@ function SocketRow({
|
|
|
1037
945
|
width={30}
|
|
1038
946
|
height={NODE_DY - 6}
|
|
1039
947
|
rx={2}
|
|
1040
|
-
fill={rgbFloatsToHex(socket.value as readonly number[])}
|
|
948
|
+
fill={rgbFloatsToHex(socket.value as unknown as readonly [number, number, number, ...number[]])}
|
|
1041
949
|
transform="scale(1 -1)"
|
|
1042
950
|
/>
|
|
1043
951
|
) : (
|
|
@@ -1059,15 +967,14 @@ function SocketRow({
|
|
|
1059
967
|
}
|
|
1060
968
|
|
|
1061
969
|
/**
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
*
|
|
1065
|
-
* pair only for a bounded scalar, so the presence of the pair IS the test.
|
|
970
|
+
* A scalar factor socket's fraction of its soft range, or null without a
|
|
971
|
+
* numeric value and range. The engine reports only finite, increasing soft
|
|
972
|
+
* ranges for factor scalars.
|
|
1066
973
|
*/
|
|
1067
974
|
function sliderFraction(socket: LaidOutSocket): number | null {
|
|
1068
975
|
const { softMin, softMax, value } = socket;
|
|
1069
976
|
if (softMin === undefined || softMax === undefined) return null;
|
|
1070
|
-
if (typeof value !== 'number'
|
|
977
|
+
if (typeof value !== 'number') return null;
|
|
1071
978
|
return Math.min(1, Math.max(0, (value - softMin) / (softMax - softMin)));
|
|
1072
979
|
}
|
|
1073
980
|
|
|
@@ -1104,10 +1011,8 @@ function Noodle({ link }: { readonly link: LaidLink }) {
|
|
|
1104
1011
|
);
|
|
1105
1012
|
}
|
|
1106
1013
|
|
|
1107
|
-
/** A frame
|
|
1108
|
-
*
|
|
1109
|
-
* and a centred label `0.5 * margin_top + 0.35 * label_size` below the top
|
|
1110
|
-
* (`node_draw.cc:3474-3484, 3699-3703`). */
|
|
1014
|
+
/** A frame with its own colour at TH_NODE_FRAME's alpha, BASIS_RAD corners and
|
|
1015
|
+
* a centred 20-pixel label 25 tree units below the top. */
|
|
1111
1016
|
function FrameNode({ node, laid }: { readonly node: BlenderNode; readonly laid: LaidOutNode }) {
|
|
1112
1017
|
const height = Math.max(laid.yTop - laid.yBottom, FRAME_MARGIN * 2);
|
|
1113
1018
|
return (
|
|
@@ -1140,21 +1045,7 @@ function FrameNode({ node, laid }: { readonly node: BlenderNode; readonly laid:
|
|
|
1140
1045
|
|
|
1141
1046
|
/* -------------------------------------------------------------------------- */
|
|
1142
1047
|
|
|
1143
|
-
/**
|
|
1144
|
-
* THE HEADER, per `bl_ui/space_node.py:41-260` — read as a specification and
|
|
1145
|
-
* drawn READ-ONLY, which is where it diverges from Blender by design: a
|
|
1146
|
-
* control that would WRITE is drawn as what it reads and refuses the click by
|
|
1147
|
-
* name. What the shader path's header carries, in order: the tree-type well
|
|
1148
|
-
* (`template_header`, `:55`), the shader-type selector (`snode.shader_type`,
|
|
1149
|
-
* `:62`), the material slot popover and the material ID (`:79-93`), the pin
|
|
1150
|
-
* (`:222-224`), the breadcrumb parent (`:228-230`), snapping
|
|
1151
|
-
* (`tool_settings.use_snap_node`, `:249-252`) and the overlays popover
|
|
1152
|
-
* (`:254-260`).
|
|
1153
|
-
*
|
|
1154
|
-
* There is deliberately NO "Use Nodes" checkbox: measured in the source, 5.2
|
|
1155
|
-
* draws it only for Line Style (`:119`) and Texture (`:133`) trees, never for
|
|
1156
|
-
* a material's.
|
|
1157
|
-
*/
|
|
1048
|
+
/** Read-only tree/material labels, zoom and View All; Use Nodes reports its write refusal. */
|
|
1158
1049
|
function NodeEditorHeader({
|
|
1159
1050
|
tree,
|
|
1160
1051
|
zoom,
|
|
@@ -1213,20 +1104,7 @@ function NodeEditorHeader({
|
|
|
1213
1104
|
);
|
|
1214
1105
|
}
|
|
1215
1106
|
|
|
1216
|
-
/**
|
|
1217
|
-
* THE STATUS LINE — where a refusal lands, and where anything this view
|
|
1218
|
-
* cannot draw is named rather than silently skipped.
|
|
1219
|
-
*
|
|
1220
|
-
* THE PANEL GAP IS CLOSED, and what is left of it is stated here rather than
|
|
1221
|
-
* hidden. A node's socket-panel MEMBERSHIP is traced from Blender's own
|
|
1222
|
-
* `declare()` bodies (`blender.node-panels.json`) and its COLLAPSED STATE is
|
|
1223
|
-
* read live from `Node.panel_states`, so a Principled BSDF draws the eight
|
|
1224
|
-
* collapsed panel rows Blender draws. Two things still fall back, and both
|
|
1225
|
-
* say so by name: a node whose LIVE socket list disagrees with the traced
|
|
1226
|
-
* declaration (the ruling's own condition), and a node the trace does not
|
|
1227
|
-
* cover at all — a node GROUP, whose panels are its tree interface's runtime
|
|
1228
|
-
* data rather than a declaration.
|
|
1229
|
-
*/
|
|
1107
|
+
/** The status line names refusals and node-layout limitations. Socket panels use traced declarations and live panel_states; mismatched socket lists and runtime node groups draw flat with a named warning. */
|
|
1230
1108
|
function StatusLine({
|
|
1231
1109
|
entries,
|
|
1232
1110
|
error,
|
|
@@ -1272,20 +1150,7 @@ function StatusLine({
|
|
|
1272
1150
|
);
|
|
1273
1151
|
}
|
|
1274
1152
|
|
|
1275
|
-
/**
|
|
1276
|
-
* THE N-PANEL'S NODE TAB — `NODE_PT_active_node_generic`
|
|
1277
|
-
* (`bl_ui/space_node.py:800-841`), in its own order: `name` (`:819`), `label`
|
|
1278
|
-
* (`:820`), the Color heading with `use_custom_color` and `color`
|
|
1279
|
-
* (`:822-834`), then `show_options` (`:837`) and `mute` (`:838`). Blender
|
|
1280
|
-
* 5.2 has no separate `NODE_PT_active_node_color`; the colour controls are
|
|
1281
|
-
* that panel's own sub-column.
|
|
1282
|
-
*
|
|
1283
|
-
* `NODE_PT_active_node_properties` (`:844-857`) is ONE call —
|
|
1284
|
-
* `layout.template_node_inputs(node)` — and that template is C: it draws the
|
|
1285
|
-
* same per-socket widgets the node body draws. The node's own inputs are
|
|
1286
|
-
* listed here with their values for exactly that reason, which is the same
|
|
1287
|
-
* fact through the same door.
|
|
1288
|
-
*/
|
|
1153
|
+
/** Read-only looked-at node name, label, colour, mute and visible socket values. */
|
|
1289
1154
|
function NodePanel({
|
|
1290
1155
|
node,
|
|
1291
1156
|
onRefuse,
|
|
@@ -1301,15 +1166,9 @@ function NodePanel({
|
|
|
1301
1166
|
flex: '0 0 auto',
|
|
1302
1167
|
overflow: 'auto',
|
|
1303
1168
|
// THE SIDEBAR IS PART OF THE NODE EDITOR'S AREA, so it wears the
|
|
1304
|
-
// space's own `TH_BACK` like the header and the canvas do
|
|
1305
|
-
//
|
|
1306
|
-
//
|
|
1307
|
-
// node editor area with `TH_BACK` and draws panels ON it
|
|
1308
|
-
// (`ED_region_panels`), so the grey was this view wearing its
|
|
1309
|
-
// container's colour. MEASURED 2026-09-19 (the frame walk, beat 5):
|
|
1310
|
-
// at (600,500) — inside the Shader Editor's pane, which a 320px
|
|
1311
|
-
// sidebar had squeezed the canvas out of — the read was #303030 where
|
|
1312
|
-
// the beat named #1a1a1a. The frame's `volter.nodeEditor.background`
|
|
1169
|
+
// space's own `TH_BACK` like the header and the canvas do, not the
|
|
1170
|
+
// DOCK's grey: Blender fills every region of a node editor area with
|
|
1171
|
+
// `TH_BACK` and draws panels ON it (`ED_region_panels`). The frame's `volter.nodeEditor.background`
|
|
1313
1172
|
// theme colour carries the same traced value for the workbench's own
|
|
1314
1173
|
// painting; this is the same number, from the same trace, for the
|
|
1315
1174
|
// pixels we paint ourselves.
|
|
@@ -1322,8 +1181,6 @@ function NodePanel({
|
|
|
1322
1181
|
>
|
|
1323
1182
|
<div style={{ opacity: 0.6, marginBottom: 'var(--volter-space-2)' }}>Node</div>
|
|
1324
1183
|
{!node ? (
|
|
1325
|
-
// Blender's panel `poll` is `context.active_node is not None`
|
|
1326
|
-
// (`space_node.py:806-808`): with no active node the tab draws nothing.
|
|
1327
1184
|
<div style={{ opacity: 0.5 }}>No node looked at.</div>
|
|
1328
1185
|
) : (
|
|
1329
1186
|
<>
|
|
@@ -1391,12 +1248,3 @@ function Row({
|
|
|
1391
1248
|
</div>
|
|
1392
1249
|
);
|
|
1393
1250
|
}
|
|
1394
|
-
|
|
1395
|
-
// THERE IS DELIBERATELY NO `available` GATE. A `workspace.utility` may gate
|
|
1396
|
-
// its tab on session state, and the Network utility does because a game may or
|
|
1397
|
-
// may not expose a networking adapter. This tab's condition is that the
|
|
1398
|
-
// BLENDER BUILD is loaded, and a contribution of this package cannot be
|
|
1399
|
-
// registered unless it is — the same reason `model.layout.ts` gates on the
|
|
1400
|
-
// project declaring a `model` document and nothing else. With no material the
|
|
1401
|
-
// view draws what Blender draws with no tree: the bare background, and the
|
|
1402
|
-
// header saying so.
|