@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
package/src/node-view-state.ts
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE NODE VIEW'S OWN STATE, and the reason it is a module rather than a
|
|
3
|
-
* `useState` inside the component.
|
|
4
|
-
*
|
|
5
|
-
* `editor.document.*` is scoped to the ACTIVE CENTER DOCUMENT by design — its
|
|
6
|
-
* own header says so: "a selector resolving outside that document's container
|
|
7
|
-
* is refused by a message naming the scope". The node editor is a DRAWER
|
|
8
|
-
* utility, so nothing in the product could read or drive it: the sighted door
|
|
9
|
-
* (`editor.captureEditorChrome`, `cyclotron screenshot editor`) photographs it, and
|
|
10
|
-
* a photograph cannot click.
|
|
11
|
-
*
|
|
12
|
-
* So the view's actions are SESSION VERBS, which is the pattern the keyboard
|
|
13
|
-
* ruling already asks for — a `volter.*` command per action rather than a raw
|
|
14
|
-
* listener — and this module is the one place their state lives. The component
|
|
15
|
-
* subscribes to it; `blender-node-view` (the command) reads and writes it. One
|
|
16
|
-
* store, two readers, no second copy.
|
|
17
|
-
*
|
|
18
|
-
* It imports NOTHING: the host handler and the contribution both reach it, and
|
|
19
|
-
* a store either of them could not import would not be one store.
|
|
20
|
-
*/
|
|
1
|
+
/** Shared node-view store for the component and blender-node-view command. Keep pan, zoom, focus, drawing measurements and refusal in one React-free module. */
|
|
21
2
|
|
|
22
3
|
export interface NodeViewTransform {
|
|
23
4
|
/** The tree-space point at the view's centre. */
|
|
@@ -48,7 +29,7 @@ export interface NodeViewState {
|
|
|
48
29
|
readonly drawn: readonly DrawnNode[];
|
|
49
30
|
}
|
|
50
31
|
|
|
51
|
-
|
|
32
|
+
interface DrawnNode {
|
|
52
33
|
readonly name: string;
|
|
53
34
|
readonly x: number;
|
|
54
35
|
readonly yTop: number;
|
|
@@ -62,10 +43,7 @@ export interface DrawnNode {
|
|
|
62
43
|
readonly firstSocketBelowTop: number | null;
|
|
63
44
|
/** The header's colour, as drawn. */
|
|
64
45
|
readonly header: string;
|
|
65
|
-
/**
|
|
66
|
-
* are collapsed. A node that declares panels and drew them FLAT instead
|
|
67
|
-
* carries the reason in `panelFallback` — the ruling's own condition,
|
|
68
|
-
* readable rather than merely displayed. */
|
|
46
|
+
/** Count drawn socket panels and collapsed panels; panelFallback names a flat-layout limitation. */
|
|
69
47
|
readonly panels: number;
|
|
70
48
|
readonly collapsedPanels: number;
|
|
71
49
|
readonly panelFallback: string | null;
|
package/src/play-mode.ts
CHANGED
|
@@ -1,35 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GAME OR ANIMATION — what a model document's bottom area shows. It is not mutually exclusive:
|
|
3
|
-
* animation is used by games too.
|
|
4
|
-
*
|
|
5
|
-
* - **Animation** is Blender's animation editors: the Timeline, the Dope Sheet's Action Editor and
|
|
6
|
-
* the NLA editor (`../contributions/blender-animation-editors.tsx`). While a game runs they show
|
|
7
|
-
* what the game is animating, read-only.
|
|
8
|
-
* - **Game** is the Game panel (`../contributions/blender-game-panel.tsx`): the ONE Play, and the
|
|
9
|
-
* run's pause, step, speed, restart and clock.
|
|
10
|
-
*
|
|
11
|
-
* They are two views of one bottom area, not two exclusive states: switching to Animation leaves a
|
|
12
|
-
* running game running.
|
|
13
|
-
*
|
|
14
|
-
* The switch is the bottom area's own, at the leading edge of its header row in the Game panel
|
|
15
|
-
* and the Timeline alike (`PlayModeSwitch`, `blender-game-panel.tsx`); this module is the state
|
|
16
|
-
* both sides read. A document opens as a
|
|
17
|
-
* Game when its model has a play script (`*.play.ts` beside the `.blend`, as the Play tool finds
|
|
18
|
-
* it — `DocumentPlayExtension.hasScript`) and in Animation otherwise, and the person's own pick
|
|
19
|
-
* then stands for that document for the browser session, kept the way `area-split.ts` keeps the
|
|
20
|
-
* split, per document id — in session storage rather than local, because the default is what a
|
|
21
|
-
* fresh session should start from.
|
|
22
|
-
*
|
|
23
|
-
* THE BOTTOM AREA SERVES ONE DOCUMENT, the model document on screen. The Timeline was always
|
|
24
|
-
* that (it binds the presented-view singleton, and cannot name a document), but a game panel
|
|
25
|
-
* needs the id to drive a run, and clicking into the panel makes the PANEL the active workspace
|
|
26
|
-
* document — so the model document announces itself here ({@link noteModelDocument}) rather
|
|
27
|
-
* than the panel asking the workspace which document is active.
|
|
28
|
-
*/
|
|
1
|
+
/** A model's bottom area shows Blender's animation editors or the Play tool's Game panel; switching leaves a running game running. Remember the person's mode per model for this browser session; otherwise a model opens in Game where the project has a game (`PlayTool.hasGame`). The model on screen announces itself, so focusing the bottom area does not change which model it serves. */
|
|
29
2
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from '
|
|
3
|
+
playTool,
|
|
4
|
+
subscribePlayTool,
|
|
5
|
+
} from './play-tool';
|
|
33
6
|
|
|
34
7
|
export type ModelPlayMode = 'game' | 'animation';
|
|
35
8
|
|
|
@@ -62,7 +35,7 @@ function stored(documentId: string): ModelPlayMode | null {
|
|
|
62
35
|
}
|
|
63
36
|
|
|
64
37
|
/** The person's own pick for a document, or `null` when the default stands. */
|
|
65
|
-
|
|
38
|
+
function chosenPlayMode(documentId: string): ModelPlayMode | null {
|
|
66
39
|
let value = chosen.get(documentId);
|
|
67
40
|
if (value === undefined) {
|
|
68
41
|
value = stored(documentId);
|
|
@@ -112,17 +85,16 @@ export function modelDocumentSource(documentId: string): string | undefined {
|
|
|
112
85
|
|
|
113
86
|
/**
|
|
114
87
|
* THE MODE A DOCUMENT IS IN: Animation whenever no Play tool is installed (there is no game to
|
|
115
|
-
* switch to), else the person's pick, else Game exactly when the
|
|
116
|
-
*
|
|
88
|
+
* switch to), else the person's pick, else Game exactly when the project has a game. A
|
|
89
|
+
* game not yet looked for counts as none, so a document opens on the Timeline and turns to
|
|
117
90
|
* the Game panel when the answer arrives.
|
|
118
91
|
*/
|
|
119
92
|
export function modelPlayMode(documentId: string): ModelPlayMode {
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
93
|
+
const tool = playTool();
|
|
94
|
+
if (tool === null) return 'animation';
|
|
122
95
|
const pick = chosenPlayMode(documentId);
|
|
123
96
|
if (pick !== null) return pick;
|
|
124
|
-
|
|
125
|
-
return blend !== undefined && extension.hasScript?.(blend) === true ? 'game' : 'animation';
|
|
97
|
+
return sources.has(documentId) && tool.hasGame() === true ? 'game' : 'animation';
|
|
126
98
|
}
|
|
127
99
|
|
|
128
100
|
export function playModeVersion(): number {
|
|
@@ -135,15 +107,14 @@ export function playModeVersion(): number {
|
|
|
135
107
|
* THE PLAY TOOL'S ANSWERS MOVE THE VERSION TOO, because the version is the snapshot every reader
|
|
136
108
|
* takes (`useSyncExternalStore(subscribePlayMode, playModeVersion)`): a script found after the
|
|
137
109
|
* first render, a run starting or stopping, a restart. Handing the tool's publications straight to
|
|
138
|
-
* the reader's listener re-
|
|
139
|
-
*
|
|
140
|
-
* had stopped. One subscription to the tool's registry, held while anything reads this store,
|
|
110
|
+
* the reader's listener would re-run a snapshot that had not changed, so React would keep the old
|
|
111
|
+
* render. One subscription to the tool's registry, held while anything reads this store,
|
|
141
112
|
* bumps the version and then tells the readers.
|
|
142
113
|
*/
|
|
143
114
|
let stopToolWatch: (() => void) | null = null;
|
|
144
115
|
export function subscribePlayMode(listener: () => void): () => void {
|
|
145
116
|
listeners.add(listener);
|
|
146
|
-
stopToolWatch ??=
|
|
117
|
+
stopToolWatch ??= subscribePlayTool(publish);
|
|
147
118
|
return () => {
|
|
148
119
|
listeners.delete(listener);
|
|
149
120
|
if (listeners.size === 0 && stopToolWatch) {
|
package/src/play-tool.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAY, THE PROJECT'S, as editor-blender's bottom area shows it (`registerPlay` in its API, `docs/adr/plugin-apis.md`):
|
|
3
|
+
* a Play tool (play's) owns its run, its controls and its Game view; the bottom area shows the tool's panel under every
|
|
4
|
+
* document but a model in its Animation mode, and asks the tool whether the project has a game, by which a model opens
|
|
5
|
+
* in Game or Animation (`play-mode.ts`).
|
|
6
|
+
*/
|
|
7
|
+
import type { ComponentType } from 'react';
|
|
8
|
+
|
|
9
|
+
export interface PlayTool {
|
|
10
|
+
/** The run's panel: Play, its transport and its log. */
|
|
11
|
+
readonly Panel: ComponentType;
|
|
12
|
+
/** Whether the project has a game to play: `null` while the tool has not looked yet. */
|
|
13
|
+
hasGame(): boolean | null;
|
|
14
|
+
/** Every change of what the tool answers. */
|
|
15
|
+
subscribe(listener: () => void): () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let registered: PlayTool | null = null;
|
|
19
|
+
const listeners = new Set<() => void>();
|
|
20
|
+
function publish(): void { for (const listener of listeners) listener(); }
|
|
21
|
+
|
|
22
|
+
/** The registered Play tool, or `null` while none is. */
|
|
23
|
+
export function playTool(): PlayTool | null {
|
|
24
|
+
return registered;
|
|
25
|
+
}
|
|
26
|
+
/** A registration, and every change the registered tool announces. */
|
|
27
|
+
export function subscribePlayTool(listener: () => void): () => void {
|
|
28
|
+
listeners.add(listener);
|
|
29
|
+
return () => { listeners.delete(listener); };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The API's `registerPlay`: one Play tool at a time. */
|
|
33
|
+
export function registerPlay(tool: PlayTool): () => void {
|
|
34
|
+
if (registered) throw new Error('A Play tool is already registered.');
|
|
35
|
+
registered = tool;
|
|
36
|
+
const stop = tool.subscribe(publish);
|
|
37
|
+
publish();
|
|
38
|
+
return () => {
|
|
39
|
+
stop();
|
|
40
|
+
if (registered !== tool) return;
|
|
41
|
+
registered = null;
|
|
42
|
+
publish();
|
|
43
|
+
};
|
|
44
|
+
}
|
package/src/presentation.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HOW THE BLENDER STAGE BEHAVES, as its starting presentation (`@volter/sdk/
|
|
2
|
+
* HOW THE BLENDER STAGE BEHAVES, as its starting presentation (`@volter/sdk/
|
|
3
3
|
* viewport-presentation`): this package builds the `model` stage, so it states the stage's
|
|
4
4
|
* function there, beneath every person's choice and never in the look (ARCHITECTURE.md rule 7).
|
|
5
5
|
*/
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
DOCUMENT_STUDIO_PRESET,
|
|
8
8
|
registerStartingPresentation,
|
|
9
9
|
type ViewportCamera,
|
|
10
|
-
} from '@volter/sdk/
|
|
10
|
+
} from '@volter/sdk/viewport-presentation';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* A 3D View's field of view for its lens, as Blender computes it (`BKE_camera_params_from_view3d`,
|
|
@@ -22,19 +22,10 @@ export function blenderViewFieldOfView(lens: number): ViewportCamera['fov'] {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const release = registerStartingPresentation('model', {
|
|
25
|
-
//
|
|
26
|
-
// angle on the larger side; and its default user perspective, elevation 26.5° and azimuth
|
|
27
|
-
// −23.8° about the up axis, SOLVED from the reference's two floor axes (projected slopes
|
|
28
|
-
// +1.0093 for X and −0.1969 for Y in `modeling-edit-none.png`), in three's Y-up frame. A file's
|
|
29
|
-
// own saved view and lens are its document's (`blender-runtime.document.tsx`).
|
|
25
|
+
// The factory Blender lens is 50 mm. The default view uses the reference floor-axis orientation in the presenter’s Y-up frame; a saved document supplies its own view and lens.
|
|
30
26
|
camera: { fov: blenderViewFieldOfView(50), opening: [0.8187, 0.4458, 0.3617] },
|
|
31
27
|
interaction: {
|
|
32
|
-
//
|
|
33
|
-
// one of the four transform tools is armed (`space_toolsystem_toolbar.py`:
|
|
34
|
-
// `_defs_view3d_generic.select_box` is the first entry of the Object Mode `_tools_default`
|
|
35
|
-
// tuple). Photographed at the engine's pin: `gizmo-select-box.png` has Select Box lit in the
|
|
36
|
-
// shelf, the default cube selected and outlined, and nothing at its origin but the 3D cursor
|
|
37
|
-
// and the object dot; `gizmo-move.png` has Move lit in the same frame.
|
|
28
|
+
// Open on Select Box, the first Object Mode shelf tool in space_toolsystem_toolbar.py. Show the transform gizmo only when a transform tool is armed.
|
|
38
29
|
bootTool: 'select',
|
|
39
30
|
// BLENDER'S SELECT BOX IS A TOUCH TEST: its object-mode box select reads the object-id buffer
|
|
40
31
|
// under the rectangle, so any drawn part of an object inside it selects that object.
|
|
@@ -47,9 +38,7 @@ const release = registerStartingPresentation('model', {
|
|
|
47
38
|
// SOLID (`clay`, and Wireframe with it) is lit by Blender's four studio lights, the document's
|
|
48
39
|
// view-locked studio, and drawn under STANDARD: Blender gives Solid the display's default view
|
|
49
40
|
// and none of the scene's settings (`draw_color_management.cc`, `ViewTransform`), and the sRGB
|
|
50
|
-
// display's default is Standard (`config.ocio`).
|
|
51
|
-
// the factory cube 142 / 131 / 112 in the default view and 162 on the front face, agreeing with
|
|
52
|
-
// the viewport's own frames (141 / 129 / 111, 161). The scene's AgX is the two modes' below.
|
|
41
|
+
// display's default is Standard (`config.ocio`). Material Preview and Rendered use AgX below.
|
|
53
42
|
all: {
|
|
54
43
|
lighting: {
|
|
55
44
|
source: 'studio',
|
|
@@ -93,8 +82,7 @@ const release = registerStartingPresentation('model', {
|
|
|
93
82
|
},
|
|
94
83
|
},
|
|
95
84
|
overlays: {
|
|
96
|
-
//
|
|
97
|
-
// the default cube's centre in every reference frame (`gizmo-select-box.png`).
|
|
85
|
+
// Draw the selected objects’ origins when the Origins overlay is enabled.
|
|
98
86
|
selection: { origins: true },
|
|
99
87
|
},
|
|
100
88
|
world: {
|
package/src/regions.ts
CHANGED
|
@@ -7,53 +7,13 @@ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
|
|
|
7
7
|
|
|
8
8
|
export const BLENDER_REGIONS: NonNullable<WorkspaceLayoutContribution['regions']> = {
|
|
9
9
|
workspaceTabs: 'shown',
|
|
10
|
-
// Blender has no document tab strip over its areas (the host's own note
|
|
11
|
-
// in `workspace-presets.ts` says so; the bundle contradicted it).
|
|
12
10
|
header: 'shown',
|
|
13
11
|
shelf: 'shown',
|
|
14
12
|
inspector: 'properties',
|
|
15
|
-
//
|
|
16
|
-
// wells (Scene, ViewLayer) and nothing brighter than the 216 its labels
|
|
17
|
-
// ink at. Ours drew a yellow FPS/ms readout, an orange sparkline and a lit
|
|
18
|
-
// audio meter there — the brightest, highest-chroma thing in the frame and
|
|
19
|
-
// the first place the eye lands. The numbers keep their door (the Profiler
|
|
20
|
-
// utility, which the readout's own tooltip names).
|
|
21
|
-
telemetry: 'hidden',
|
|
22
|
-
// Measured on `outliner.png`: Scene Collection, Collection, Camera, Cube
|
|
23
|
-
// and Light each carry their NAME and nothing after it — no type text
|
|
24
|
-
// anywhere in the frame. The type is the GLYPH, which is why the glyph is
|
|
25
|
-
// coloured by category in the first place; a row that also spelled the
|
|
26
|
-
// type would say it twice. Ours printed `Hero Box ·HeroBox` at
|
|
27
|
-
// `content.dim` in a 10px face, one `space-2` past the name.
|
|
13
|
+
// Hide hierarchy type suffixes; glyph categories identify row types.
|
|
28
14
|
hierarchyTypeSuffix: 'hidden',
|
|
29
|
-
//
|
|
30
|
-
// `modeling-object-none.png` (object mode, nothing selected) and
|
|
31
|
-
// `modeling-object-selected.png` (object mode, everything selected):
|
|
32
|
-
// Blender draws NO rule, dotted or solid, under any name in that panel, in
|
|
33
|
-
// any selection state. Ours drew an orange dotted rule under every
|
|
34
|
-
// component-instance name — six of this scaffold's eleven rows — which is a
|
|
35
|
-
// mark with no counterpart in the frame at all. The instance fact keeps its
|
|
36
|
-
// doors (the label's tooltip, Go to Callsite / Open Component Source, the
|
|
37
|
-
// inspector's component sections); only the paint yields.
|
|
15
|
+
// Hide component-instance underline marks; tooltips, source navigation and inspector sections retain the instance information.
|
|
38
16
|
hierarchyInstanceRule: 'hidden',
|
|
39
|
-
//
|
|
40
|
-
// 257.5-270.5, 32 px in from the area's right edge) and a render CAMERA
|
|
41
|
-
// (277.5-290.5, 12 px in) on a 20 px column pitch, and no lock glyph
|
|
42
|
-
// anywhere — Blender's Disable Selection column is off by default. Ours
|
|
43
|
-
// drew a lock 29-41 px in and the eye 6-18 px in, so the LOCK was sitting
|
|
44
|
-
// in Blender's eye column and the eye outboard of its camera column. The
|
|
45
|
-
// lock goes, and the render column is reserved blank so the eye lands on
|
|
46
|
-
// Blender's own; `workspace-regions.ts` carries why it stays blank.
|
|
17
|
+
// Hide the selection-lock column and reserve the render-camera column so the eye occupies Blender’s column.
|
|
47
18
|
hierarchyRestrictions: 'viewport+render',
|
|
48
|
-
// Measured at native 2x on `outliner.png` AND on the Properties area of
|
|
49
|
-
// `modeling-edit-none.png` — device x relative to each area's own left
|
|
50
|
-
// edge, and the two agree to half a pixel: every Blender area begins with
|
|
51
|
-
// an EDITOR-TYPE WELL at x 17..80, i.e. 8.5 CSS in and 32 CSS wide, 20
|
|
52
|
-
// tall, its glyph and a chevron inside a 1 px `#3c3c3c` outline over a
|
|
53
|
-
// `#272727` inner. It is there whether the area holds one editor or a
|
|
54
|
-
// dozen, and its dropdown is how an area changes editor. Ours began with
|
|
55
|
-
// a 203 px strip of WORDS, which is what squeezed the Outliner's search
|
|
56
|
-
// to 64 px against Blender's 115, truncated the third tab to `Libr`, and
|
|
57
|
-
// left the Properties area — a lone panel declaring no header controls —
|
|
58
|
-
// with no header band at all.
|
|
59
19
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** blender-start uses an explicit file first, otherwise the active Model file before the bound pane. During Save As, the active tab can lead pane binding. An unlisted-file follow keeps the session’s new file on the tab it left. */
|
|
2
|
+
export function startDocument(input: {
|
|
3
|
+
/** The file the start names, if any. */
|
|
4
|
+
readonly requested: string | undefined;
|
|
5
|
+
/** The file the editor is showing as the Model: the active tab's, else the bound pane's. */
|
|
6
|
+
readonly shown: string | undefined;
|
|
7
|
+
/** A Save As to an unlisted file, from the tab it left, for this session. */
|
|
8
|
+
readonly follow: { readonly from: string; readonly document: string } | null;
|
|
9
|
+
/** The standing Model's file, for a project with no `.blend` of its own. */
|
|
10
|
+
readonly fallback: string;
|
|
11
|
+
}): string {
|
|
12
|
+
if (input.requested !== undefined) return input.requested;
|
|
13
|
+
const shown = input.shown ?? input.fallback;
|
|
14
|
+
return input.follow !== null && input.follow.from === shown ? input.follow.document : shown;
|
|
15
|
+
}
|
|
@@ -1,32 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE TIMELINE'S OWN VIEW STATE — the pan/zoom of its ruler, the last refusal,
|
|
3
|
-
* the measured box, and what it drew.
|
|
4
|
-
*
|
|
5
|
-
* THE PLAYBACK STATE IS NOT HERE. The frame, the play/pause and the keyframe
|
|
6
|
-
* columns live in the presenter's `BlenderSkinDirector`
|
|
7
|
-
* (`../contributions/blender-runtime-skin.ts`), because that object OWNS the
|
|
8
|
-
* `AnimationMixer` and a second copy of "what frame is it" is exactly the
|
|
9
|
-
* disagreement the whole design exists to avoid. This module holds only what
|
|
10
|
-
* is about the DRAWING.
|
|
11
|
-
*
|
|
12
|
-
* WHAT DRIVES IT IS NOT A SESSION VERB. U8's ruling 1 (2026-09-19): every view
|
|
13
|
-
* publishes `volter.<view>.<verb>` commands through `@volter/sdk/views`, one
|
|
14
|
-
* table behind the frame's command service and standalone `cyclotron edit`'s
|
|
15
|
-
* session verb — so this view adds no `blender-*` verb of its own.
|
|
16
|
-
*
|
|
17
|
-
* It imports NOTHING, for the reason the node and UV stores do: the view and
|
|
18
|
-
* the verb table both reach it, and a store either of them could not import
|
|
19
|
-
* would not be one store.
|
|
20
|
-
*/
|
|
1
|
+
/** React-free Timeline drawing state: ruler pan/zoom, refusal, measured box and drawn geometry. Playback belongs to BlenderSkinDirector. The page registers actions as volter.<view>.<verb> commands. */
|
|
21
2
|
|
|
22
|
-
|
|
3
|
+
interface TimelineTransform {
|
|
23
4
|
/** The FRAME at the view's left edge, and the CSS pixels per frame. Blender's
|
|
24
5
|
* own `View2D.cur` for a time editor is exactly this pair. */
|
|
25
6
|
readonly startFrame: number;
|
|
26
7
|
readonly pixelsPerFrame: number;
|
|
27
8
|
}
|
|
28
9
|
|
|
29
|
-
|
|
10
|
+
interface TimelineDrawn {
|
|
30
11
|
/** The action being played, and whose keys the summary row draws. */
|
|
31
12
|
readonly action: string | null;
|
|
32
13
|
readonly object: string | null;
|
|
@@ -72,23 +53,7 @@ export interface TimelineViewState {
|
|
|
72
53
|
readonly size: { readonly w: number; readonly h: number };
|
|
73
54
|
readonly framedAt: number;
|
|
74
55
|
readonly drawn: TimelineDrawn | null;
|
|
75
|
-
/**
|
|
76
|
-
* BLENDER'S `show_keys_from_selected_only`, and it is VIEW state here for a
|
|
77
|
-
* reason a headless Blender forces: the flag Blender's Timeline reads is
|
|
78
|
-
* `Scene.flag & SCE_KEYS_NO_SELONLY` (`anim_filter.cc:254-270`), and this
|
|
79
|
-
* surface is an INSPECTION surface that does not write the file — so the
|
|
80
|
-
* filter's position is ours to hold and the data it filters on
|
|
81
|
-
* (`Object.select_get()`) stays Blender's.
|
|
82
|
-
*
|
|
83
|
-
* DEFAULTS ON, as Blender's does: `SCE_KEYS_NO_SELONLY` is a NEGATIVE flag
|
|
84
|
-
* and a new scene does not carry it, so a factory Blender's Timeline shows
|
|
85
|
-
* the selected objects' keys only. The reference read recorded exactly that
|
|
86
|
-
* — the probe's own Timeline drew an EMPTY summary row with the rig
|
|
87
|
-
* unselected while its Dope Sheet drew all five keys.
|
|
88
|
-
*
|
|
89
|
-
* IT DECIDES WHAT THE ROW DRAWS, NEVER WHAT PLAYS. Playback moves every
|
|
90
|
-
* object regardless, in Blender and here.
|
|
91
|
-
*/
|
|
56
|
+
/** Only Show Selected is Timeline view state; keyframe subjects still come from Blender’s selection. Default it on, matching Blender’s scene flag, without writing the inspection filter into the file. */
|
|
92
57
|
readonly onlySelected: boolean;
|
|
93
58
|
/** Which of Blender's animation editors the bottom area shows in Animation mode: the
|
|
94
59
|
* Timeline, the Dope Sheet's Action Editor, or the NLA editor (Blender's editor-type menu). */
|
package/src/uv-view-state.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE UV VIEW'S OWN STATE, and the reason it is a module rather than a
|
|
3
|
-
* `useState` inside the component — the same reason `node-view-state.ts`
|
|
4
|
-
* records: `editor.document.*` reaches only the ACTIVE CENTRE DOCUMENT by its
|
|
5
|
-
* own contract, and this view is not one, so a `useState` here would be a
|
|
6
|
-
* surface nothing in the product could read or drive.
|
|
7
|
-
*
|
|
8
|
-
* WHAT DRIVES IT IS NOT A SESSION VERB. U8's ruling 1 (2026-09-19): every view
|
|
9
|
-
* publishes `volter.<view>.<verb>` commands through `@volter/sdk/views`,
|
|
10
|
-
* one table behind the frame's command service and standalone `cyclotron edit`'s
|
|
11
|
-
* session verb — so this view adds NO `blender-*` verb of its own, which is
|
|
12
|
-
* exactly what the ruling asked the remaining I5 views to stop paying for.
|
|
13
|
-
*
|
|
14
|
-
* It imports NOTHING, for the reason the node view's store does: the view and
|
|
15
|
-
* the verb table both reach it, and a store either of them could not import
|
|
16
|
-
* would not be one store.
|
|
17
|
-
*/
|
|
1
|
+
/** React-free UV drawing state shared by the view and page commands. Actions are registered as volter.blender-uv-view.<verb>. */
|
|
18
2
|
|
|
19
|
-
|
|
3
|
+
interface UvViewTransform {
|
|
20
4
|
/** The UV-space point at the view's centre. UV space is Y-UP (v increases
|
|
21
5
|
* upward, as Blender's image space does); the view flips it once, at the
|
|
22
6
|
* SVG transform, so nothing downstream carries a sign. */
|
|
@@ -45,12 +29,12 @@ export interface UvViewState {
|
|
|
45
29
|
readonly drawn: UvViewDrawn | null;
|
|
46
30
|
}
|
|
47
31
|
|
|
48
|
-
|
|
32
|
+
interface UvViewDrawn {
|
|
49
33
|
readonly object: string | null;
|
|
50
34
|
readonly mesh: string | null;
|
|
51
35
|
readonly layer: string | null;
|
|
52
36
|
readonly layers: readonly string[];
|
|
53
|
-
/** Blender
|
|
37
|
+
/** Blender’s mode at the read. */
|
|
54
38
|
readonly mode: string;
|
|
55
39
|
readonly loops: number;
|
|
56
40
|
readonly polygons: number;
|