@volter/editor-blender 0.1.141 → 0.1.142

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.
Files changed (51) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +1 -1
  3. package/contributions/blender-animation-editors.tsx +387 -0
  4. package/contributions/blender-game-panel.tsx +25 -24
  5. package/contributions/blender-header-menus.tsx +2 -2
  6. package/contributions/blender-icons.source.mjs +1 -1
  7. package/contributions/blender-model-opening.tsx +3 -3
  8. package/contributions/blender-node-editor.document.tsx +1 -1
  9. package/contributions/blender-outliner-authoring.ts +4 -4
  10. package/contributions/blender-outliner-model.ts +1 -1
  11. package/contributions/blender-play-skin.ts +48 -6
  12. package/contributions/blender-pose.ts +7 -0
  13. package/contributions/blender-properties-context.ts +1 -1
  14. package/contributions/blender-properties-model.ts +2 -2
  15. package/contributions/blender-properties-tab.tsx +2 -2
  16. package/contributions/blender-properties-view.tsx +1 -1
  17. package/contributions/blender-runtime-skin.ts +35 -1
  18. package/contributions/blender-runtime.document.tsx +17 -13
  19. package/contributions/blender-screenshot.menu.ts +3 -3
  20. package/contributions/blender-timeline-geometry.ts +2 -2
  21. package/contributions/blender-timeline.document.tsx +41 -10
  22. package/contributions/blender-uv-editor.document.tsx +1 -1
  23. package/contributions/blender-version.status.tsx +2 -2
  24. package/contributions/blender.command.ts +3 -3
  25. package/contributions/blender.environment.ts +2 -2
  26. package/contributions/blender.keymap.ts +2 -2
  27. package/contributions/blender.status.tsx +2 -2
  28. package/contributions/blender.style.ts +3 -3
  29. package/contributions/model.layout.ts +2 -2
  30. package/contributions/models.finder.ts +1 -1
  31. package/contributions/properties-data.inspector.tsx +1 -1
  32. package/contributions/properties-scene.inspector.tsx +1 -1
  33. package/contributions/sculpt.layout.ts +1 -1
  34. package/contributions/shading.layout.ts +1 -1
  35. package/contributions/texture.layout.ts +1 -1
  36. package/contributions/uv-editing.layout.ts +1 -1
  37. package/dist-node/serving.mjs +2 -2
  38. package/host/blender-runtime-host.ts +4 -4
  39. package/node/blender-mcp.ts +3 -3
  40. package/package.json +6 -6
  41. package/serving/blender-routes.ts +4 -4
  42. package/serving/index.ts +2 -2
  43. package/src/layouts.tsx +3 -3
  44. package/src/looks.ts +1 -1
  45. package/src/play-live.ts +27 -0
  46. package/src/play-mode.ts +23 -15
  47. package/src/presentation.ts +2 -2
  48. package/src/regions.ts +1 -1
  49. package/src/timeline-view-state.ts +7 -1
  50. package/src/uv-view-state.ts +1 -1
  51. package/view/blender-view-routes.ts +2 -2
@@ -2,14 +2,14 @@
2
2
  * The BLENDER style bundle — Blender's greys and widget blue as a palette
3
3
  * document (`blender.palette.json`, the same v3 shape a person imports), its
4
4
  * own opaque material, and Blender's chrome regions
5
- * (`@volter/editor-sdk/looks`, a `workspace.style` contribution).
5
+ * (`@volter/sdk/looks`, a `workspace.style` contribution).
6
6
  *
7
7
  * The material transcribes Blender's default theme: widgets round at 0.2 of
8
8
  * their height (4px on a 20px widget), areas and panels are flat with no
9
9
  * shadow, and only menus and popovers cast one (`menu_shadow_fac` 0.5 over
10
10
  * `menu_shadow_width` 4). The `large` radius is our reading — Blender rounds
11
11
  * its popovers the same as its menus. The density is measured from Blender
12
- * 5.2's own frames at 1x (`/Volumes/PeakSSD/volter-work/blender-reference`):
12
+ * 5.2's own frames at 1x:
13
13
  * 26px top bar and area headers, 24px status bar, and — re-measured against
14
14
  * the frames widget by widget — EVERY widget 20px tall, button/field/toggle
15
15
  * alike, so all three control sizes are 20 rather than a 18/20/24 ramp
@@ -37,7 +37,7 @@
37
37
  * A glyph is EDITED in `blender-icons.source.mjs` and the JSON re-emitted by
38
38
  * running it; a path `d` is not a thing a person edits by hand.
39
39
  */
40
- import type { IconSetContribution, StyleContribution } from '@volter/editor-sdk/looks';
40
+ import type { IconSetContribution, StyleContribution } from '@volter/sdk/looks';
41
41
  import iconsJson from './blender.icons.json';
42
42
  import palette from './blender.palette.json';
43
43
 
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * The MODEL workspace — Blender-shaped modeling: a dominant document, outliner
3
- * above properties (`@volter/editor-sdk/looks`, a `workspace.layout`
3
+ * above properties (`@volter/sdk/looks`, a `workspace.layout`
4
4
  * contribution). Its arrangement carries Blender 5.2's own MODELING-workspace
5
5
  * proportions; `../src/layouts.tsx` states the measurement and the host's
6
6
  * fixed-pane clamp that bounds it.
7
7
  */
8
8
 
9
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
9
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
10
10
  import { BLENDER_REGIONS } from '../src/regions';
11
11
 
12
12
  export const point = 'workspace.layout';
@@ -12,7 +12,7 @@
12
12
  * the TypeScript mesh kit was the modeling engine while Blender could not run
13
13
  * in the tab; it now does, and a model is Blender data).
14
14
  *
15
- * Select it in `volter.adapter.ts`:
15
+ * Select it in `editor/volter.adapter.ts`:
16
16
  *
17
17
  * documents: { find: [{ finder: 'modelsFromBlendFiles', include: ['src/models/**\/*.blend'] }] }
18
18
  *
@@ -31,7 +31,7 @@
31
31
  * It stands when: `buttons_context_path_data` — the object HAS data (an Empty
32
32
  * usually has none).
33
33
  */
34
- import type { ToolContributionNode } from '@volter/editor-sdk/contributions';
34
+ import type { ToolContributionNode } from '@volter/sdk/contributions';
35
35
  import { blenderPropertiesState, resolveBlenderSubject } from './blender-properties-model';
36
36
  import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
37
37
  import type { BlenderCuratedPanel } from './blender-properties-view';
@@ -36,7 +36,7 @@
36
36
  import { blenderPropertiesTabMatch, blenderPropertiesTabSection } from './blender-properties-tab';
37
37
  import type { BlenderCuratedPanel } from './blender-properties-view';
38
38
  import { resolveBlenderSubject } from './blender-properties-model';
39
- import type { ToolContributionNode } from '@volter/editor-sdk/contributions';
39
+ import type { ToolContributionNode } from '@volter/sdk/contributions';
40
40
 
41
41
  /** `properties_scene.py`, panel by panel. Line numbers are that file's. */
42
42
  const CURATED: readonly BlenderCuratedPanel[] = [
@@ -5,7 +5,7 @@
5
5
  * chrome); it keeps its OWN persisted layout the moment a person touches it.
6
6
  */
7
7
 
8
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
8
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
9
9
  import { BLENDER_REGIONS } from '../src/regions';
10
10
 
11
11
  export const point = 'workspace.layout';
@@ -52,7 +52,7 @@
52
52
  * contribution exists because the node view does.
53
53
  */
54
54
 
55
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
55
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
56
56
  import { BLENDER_REGIONS } from '../src/regions';
57
57
 
58
58
  export const point = 'workspace.layout';
@@ -4,7 +4,7 @@
4
4
  * layer stack (see `sculpt.layout.ts` for why).
5
5
  */
6
6
 
7
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
7
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
8
8
 
9
9
  export const point = 'workspace.layout';
10
10
  export const layout: WorkspaceLayoutContribution = {
@@ -45,7 +45,7 @@
45
45
  * this contribution exists because the UV view does.
46
46
  */
47
47
 
48
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
48
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
49
49
  import { BLENDER_REGIONS } from '../src/regions';
50
50
 
51
51
  export const point = 'workspace.layout';
@@ -6,7 +6,7 @@ import { tmpdir as tmpdir2 } from "node:os";
6
6
  import { dirname as dirname2, join as join2 } from "node:path";
7
7
  import { createBrotliDecompress } from "node:zlib";
8
8
 
9
- // ../editor-sdk/src/project/output-roots.ts
9
+ // ../sdk/src/project/output-roots.ts
10
10
  var PROJECT_OUTPUT_ROOTS = ["public", "references"];
11
11
  function normalizeSlashes(path) {
12
12
  const slashed = path.replaceAll("\\", "/");
@@ -24,7 +24,7 @@ function projectOutputRootOf(path) {
24
24
  );
25
25
  }
26
26
 
27
- // ../editor-sdk/src/session/relative-path-guard.ts
27
+ // ../sdk/src/session/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";
@@ -4,7 +4,7 @@
4
4
  * the Code-OSS frame, item 6). Its thirteen verbs are
5
5
  * `../contributions/blender.command.ts`, a `workspace.command` contribution;
6
6
  * what it needs of the running editor it asks THROUGH THE DOOR,
7
- * `@volter/editor-sdk/host` — the document context it
7
+ * `@volter/sdk/host` — the document context it
8
8
  * presents into, the session it lives and dies with. It imports no host module
9
9
  * (WORKBENCH.md §The invariants, "Direction": a package imports only the SDK
10
10
  * and other packages' exports), which is what lets this lane ship against a
@@ -43,9 +43,9 @@ import type {
43
43
  } from '@volter/blender-engine/browser/rna';
44
44
  import { createBlenderDisplayTransform, blenderDisplaySettingsForRender } from '@volter/blender-engine/browser/three/blender-display-transform';
45
45
  import { BlenderRuntimeView } from '@volter/blender-engine/browser/three/blender-runtime-view';
46
- import type { EditorCommandResult } from '@volter/editor-sdk/commands';
47
- import { editorHost } from '@volter/editor-sdk/host';
48
- import { invokeViewVerb, type ViewVerbContribution } from '@volter/editor-sdk/views';
46
+ import type { EditorCommandResult } from '@volter/sdk/commands';
47
+ import { editorHost } from '@volter/sdk/host';
48
+ import { invokeViewVerb, type ViewVerbContribution } from '@volter/sdk/views';
49
49
  import {
50
50
  captureSceneImage,
51
51
  captureSceneLinear,
@@ -22,9 +22,9 @@ import {
22
22
  ListPromptsRequestSchema,
23
23
  ListToolsRequestSchema,
24
24
  } from '@modelcontextprotocol/sdk/types.js';
25
- import { EditorClient, type EditorView } from '@volter/editor-sdk';
25
+ import { EditorClient, type EditorView } from '@volter/sdk';
26
26
  import blenderBundle from '@volter/blender-engine/wasm/BUNDLE.json';
27
- import { projectOutputRootOf } from '@volter/editor-sdk/project/output-roots';
27
+ import { projectOutputRootOf } from '@volter/sdk/project/output-roots';
28
28
  import blenderDefaults from './blender-mcp-defaults.json';
29
29
  import blenderTools from './blender-mcp-tools.json';
30
30
 
@@ -266,7 +266,7 @@ class TabSession {
266
266
  if (!this.#connecting) {
267
267
  this.#connecting = (async () => {
268
268
  await this.ensureEditor();
269
- const { resolveSession } = await import('@volter/editor-live');
269
+ const { resolveSession } = await import('@volter/live');
270
270
  const session = await resolveSession(this.project);
271
271
  this.#port = session.port;
272
272
  this.#origin = `http://127.0.0.1:${session.port}`;
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.141",
5
+ "version": "0.1.142",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -72,15 +72,15 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@modelcontextprotocol/sdk": "1.32.1",
75
- "@volter/blender-engine": "0.1.139",
76
- "@volter/editor-threejs": "0.5.202",
75
+ "@volter/blender-engine": "0.1.140",
76
+ "@volter/editor-threejs": "0.5.203",
77
77
  "zod": "^4.3.6",
78
78
  "@fortawesome/free-solid-svg-icons": "^7.2.0"
79
79
  },
80
80
  "peerDependencies": {
81
- "@volter/editor-live": "0.5.202",
82
- "@volter/editor-project": "0.5.202",
83
- "@volter/editor-sdk": "0.5.202",
81
+ "@volter/live": "0.5.203",
82
+ "@volter/project": "0.5.203",
83
+ "@volter/sdk": "0.5.203",
84
84
  "react": "^19.0.0",
85
85
  "react-dom": "^19.0.0",
86
86
  "three": "^0.180.0",
@@ -5,7 +5,7 @@
5
5
  * bytes it produced reach the project (a shipped output, the session's `.blend`). They were
6
6
  * the kit's own routes; the kit now names no Blender, and reaches these only because the
7
7
  * product that composes this package declares its serving module. Writes go through the kit's
8
- * services (`@volter/editor-sdk/session/project-serving`), so they are attributed and recorded
8
+ * services (`@volter/sdk/session/project-serving`), so they are attributed and recorded
9
9
  * like every other project write.
10
10
  */
11
11
 
@@ -16,9 +16,9 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
16
16
  import { tmpdir } from 'node:os';
17
17
  import { dirname, join } from 'node:path';
18
18
  import { createBrotliDecompress } from 'node:zlib';
19
- import { projectOutputRootOf } from '@volter/editor-sdk/project/output-roots';
20
- import { isContainedRelativePath } from '@volter/editor-sdk/session/relative-path-guard';
21
- import type { ProjectServingServices } from '@volter/editor-sdk/session/project-serving';
19
+ import { projectOutputRootOf } from '@volter/sdk/project/output-roots';
20
+ import { isContainedRelativePath } from '@volter/sdk/session/relative-path-guard';
21
+ import type { ProjectServingServices } from '@volter/sdk/session/project-serving';
22
22
  import type { Plugin } from 'vite';
23
23
  import {
24
24
  BLENDER_WALI_ARTIFACT,
package/serving/index.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * `@volter/editor-blender`'s server half (`package.json#volter.serving`, the project-serving door in
3
- * `@volter/editor-sdk/session/project-serving`): the routes the Blender in the tab reads and
3
+ * `@volter/sdk/session/project-serving`): the routes the Blender in the tab reads and
4
4
  * writes through.
5
5
  */
6
6
 
7
- import type { ProjectServingModule } from '@volter/editor-sdk/session/project-serving';
7
+ import type { ProjectServingModule } from '@volter/sdk/session/project-serving';
8
8
  import { blenderRoutesPlugin } from './blender-routes';
9
9
  import { BLENDER_WALI_ARTIFACT, BLENDER_WASM_FILES, blenderWasmStatus } from './blender-wasm-artifact';
10
10
 
package/src/layouts.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The BLENDER layout — the composition a models project's adapter declares
3
- * (`volter.adapter.ts`: `editor: { Layout: ModelLayout }`, the door
4
- * `@volter/editor-sdk/layouts` documents). `ModelArrangement` names the Model
3
+ * (`editor/volter.adapter.ts`: `editor: { Layout: ModelLayout }`, the door
4
+ * `@volter/sdk/layouts` documents). `ModelArrangement` names the Model
5
5
  * workspace and the chrome regions it shows; Sculpt and Texture open on it too
6
6
  * — their reserved panels (the brush rail, the layer stack) ship WITH their
7
7
  * programs, never as empty chrome, and until then nothing about a sculpt
@@ -21,7 +21,7 @@ import {
21
21
  EditorHeader,
22
22
  Workspace,
23
23
  type WorkspaceArrangement,
24
- } from '@volter/editor-sdk/layouts';
24
+ } from '@volter/sdk/layouts';
25
25
  import { BLENDER_REGIONS } from './regions';
26
26
 
27
27
  export const ModelArrangement: WorkspaceArrangement = {
package/src/looks.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The Blender skew's LOOK and BINDINGS as importable values — `@volter/editor-blender/looks`,
3
- * the public surface a project's `volter.adapter.ts` declares them from
3
+ * the public surface a project's `editor/volter.adapter.ts` declares them from
4
4
  * (ARCHITECTURE-CORE §Adapters and contributions are code, not configs):
5
5
  *
6
6
  * editor: { Layout: ModelLayout, style: blenderStyle, keymap: blenderKeymap }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * THE RUNNING GAME'S ANIMATION, for the animation editors. A game poses its copy of the model
3
+ * (`../contributions/blender-play-skin.ts`); while it runs, the Action Editor and the NLA editor
4
+ * show what it is playing, read-only, the way a game engine's animator view highlights the live
5
+ * state. The run sets the slot when its animation starts and clears it when it ends.
6
+ */
7
+ import type { PlayAnimation } from '../contributions/blender-play-skin';
8
+
9
+ let current: PlayAnimation | null = null;
10
+ const listeners = new Set<() => void>();
11
+
12
+ export function setLiveAnimation(animation: PlayAnimation | null): void {
13
+ if (current === animation) return;
14
+ current = animation;
15
+ for (const listener of [...listeners]) listener();
16
+ }
17
+
18
+ /** The running game's animation, or null when no game runs. */
19
+ export function liveAnimation(): PlayAnimation | null {
20
+ return current;
21
+ }
22
+
23
+ /** Fires when a game's animation starts or ends (not on each frame: readers poll while it runs). */
24
+ export function subscribeLiveAnimation(listener: () => void): () => void {
25
+ listeners.add(listener);
26
+ return () => listeners.delete(listener);
27
+ }
package/src/play-mode.ts CHANGED
@@ -1,18 +1,21 @@
1
1
  /**
2
- * GAME OR MOVIE — what a model document's bottom area and transport are for (owner, 2026-10-06:
3
- * "it's very confusing that the timeline at the bottom has a play button AND there's a play
4
- * button at the top … allow the user to flip between game and movie mode").
2
+ * GAME OR ANIMATION — what a model document's bottom area shows. It is not mutually exclusive:
3
+ * animation is used by games too.
5
4
  *
6
- * - **Movie** is Blender's own Layout screen: the Timeline at the bottom, its transport playing
7
- * the file's animation, and no game Play anywhere.
8
- * - **Game** replaces the Timeline with the Game panel (`../contributions/blender-game-panel.tsx`):
9
- * the ONE Play, and the run's pause, step, speed, restart and clock.
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.
10
13
  *
11
14
  * The switch is the bottom area's own, at the leading edge of its header row in the Game panel
12
15
  * and the Timeline alike (`PlayModeSwitch`, `blender-game-panel.tsx`); this module is the state
13
16
  * both sides read. A document opens as a
14
17
  * Game when its model has a play script (`*.play.ts` beside the `.blend`, as the Play tool finds
15
- * it — `DocumentPlayExtension.hasScript`) and as a Movie otherwise, and the person's own pick
18
+ * it — `DocumentPlayExtension.hasScript`) and in Animation otherwise, and the person's own pick
16
19
  * then stands for that document for the browser session, kept the way `area-split.ts` keeps the
17
20
  * split, per document id — in session storage rather than local, because the default is what a
18
21
  * fresh session should start from.
@@ -26,9 +29,14 @@
26
29
  import {
27
30
  documentPlayExtension,
28
31
  subscribeDocumentPlayExtensions,
29
- } from '@volter/editor-sdk/kit/document-play-extension';
32
+ } from '@volter/sdk/kit/document-play-extension';
33
+
34
+ export type ModelPlayMode = 'game' | 'animation';
30
35
 
31
- export type ModelPlayMode = 'game' | 'movie';
36
+ /** A mode as asked for, or null for anything else. */
37
+ export function playModeOf(value: unknown): ModelPlayMode | null {
38
+ return value === 'game' || value === 'animation' ? value : null;
39
+ }
32
40
 
33
41
  const STORAGE_PREFIX = 'volter.blender.play-mode:';
34
42
  const listeners = new Set<() => void>();
@@ -47,7 +55,7 @@ function publish(): void {
47
55
  function stored(documentId: string): ModelPlayMode | null {
48
56
  try {
49
57
  const value = globalThis.sessionStorage?.getItem(STORAGE_PREFIX + documentId);
50
- return value === 'game' || value === 'movie' ? value : null;
58
+ return playModeOf(value);
51
59
  } catch {
52
60
  return null;
53
61
  }
@@ -103,18 +111,18 @@ export function modelDocumentSource(documentId: string): string | undefined {
103
111
  }
104
112
 
105
113
  /**
106
- * THE MODE A DOCUMENT IS IN: Movie whenever no Play tool is installed (there is no game to
114
+ * THE MODE A DOCUMENT IS IN: Animation whenever no Play tool is installed (there is no game to
107
115
  * switch to), else the person's pick, else Game exactly when the model has a play script. A
108
116
  * script not yet looked for counts as none, so a document opens on the Timeline and turns to
109
117
  * the Game panel when the answer arrives.
110
118
  */
111
119
  export function modelPlayMode(documentId: string): ModelPlayMode {
112
120
  const extension = documentPlayExtension('model');
113
- if (extension === null) return 'movie';
121
+ if (extension === null) return 'animation';
114
122
  const pick = chosenPlayMode(documentId);
115
123
  if (pick !== null) return pick;
116
124
  const blend = sources.get(documentId);
117
- return blend !== undefined && extension.hasScript?.(blend) === true ? 'game' : 'movie';
125
+ return blend !== undefined && extension.hasScript?.(blend) === true ? 'game' : 'animation';
118
126
  }
119
127
 
120
128
  export function playModeVersion(): number {
@@ -128,7 +136,7 @@ export function playModeVersion(): number {
128
136
  * takes (`useSyncExternalStore(subscribePlayMode, playModeVersion)`): a script found after the
129
137
  * first render, a run starting or stopping, a restart. Handing the tool's publications straight to
130
138
  * the reader's listener re-ran a snapshot that had not changed, so React kept the old render — a
131
- * document with a play script stayed in Movie, and the Game panel kept showing Stop after the game
139
+ * document with a play script stayed in Animation, and the Game panel kept showing Stop after the game
132
140
  * had stopped. One subscription to the tool's registry, held while anything reads this store,
133
141
  * bumps the version and then tells the readers.
134
142
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HOW THE BLENDER STAGE BEHAVES, as its starting presentation (`@volter/editor-sdk/kit/
2
+ * HOW THE BLENDER STAGE BEHAVES, as its starting presentation (`@volter/sdk/kit/
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/editor-sdk/kit/viewport-presentation';
10
+ } from '@volter/sdk/kit/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`,
package/src/regions.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * which regions exist and what a hierarchy row spells are function, the workspace's, never the
4
4
  * look's). Each of this package's layouts spreads these beneath its own.
5
5
  */
6
- import type { WorkspaceLayoutContribution } from '@volter/editor-sdk/looks';
6
+ import type { WorkspaceLayoutContribution } from '@volter/sdk/looks';
7
7
 
8
8
  export const BLENDER_REGIONS: NonNullable<WorkspaceLayoutContribution['regions']> = {
9
9
  workspaceTabs: 'shown',
@@ -10,7 +10,7 @@
10
10
  * is about the DRAWING.
11
11
  *
12
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/editor-sdk/views`, one
13
+ * publishes `volter.<view>.<verb>` commands through `@volter/sdk/views`, one
14
14
  * table behind the frame's command service and standalone `cyclotron edit`'s
15
15
  * session verb — so this view adds no `blender-*` verb of its own.
16
16
  *
@@ -90,8 +90,13 @@ export interface TimelineViewState {
90
90
  * object regardless, in Blender and here.
91
91
  */
92
92
  readonly onlySelected: boolean;
93
+ /** Which of Blender's animation editors the bottom area shows in Animation mode: the
94
+ * Timeline, the Dope Sheet's Action Editor, or the NLA editor (Blender's editor-type menu). */
95
+ readonly editor: AnimationEditor;
93
96
  }
94
97
 
98
+ export type AnimationEditor = 'timeline' | 'action' | 'nla';
99
+
95
100
  const INITIAL: TimelineViewState = {
96
101
  transform: { startFrame: -5, pixelsPerFrame: 8 },
97
102
  refusal: null,
@@ -99,6 +104,7 @@ const INITIAL: TimelineViewState = {
99
104
  framedAt: 0,
100
105
  drawn: null,
101
106
  onlySelected: true,
107
+ editor: 'timeline',
102
108
  };
103
109
 
104
110
  let state: TimelineViewState = INITIAL;
@@ -6,7 +6,7 @@
6
6
  * surface nothing in the product could read or drive.
7
7
  *
8
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/editor-sdk/views`,
9
+ * publishes `volter.<view>.<verb>` commands through `@volter/sdk/views`,
10
10
  * one table behind the frame's command service and standalone `cyclotron edit`'s
11
11
  * session verb — so this view adds NO `blender-*` verb of its own, which is
12
12
  * exactly what the ruling asked the remaining I5 views to stop paying for.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * `@volter/editor-blender`'s routes in a LIMITED VIEW (`package.json#volter.viewServing`,
3
- * `@volter/editor-sdk/session/limited-view`): the same requests the Blender in the tab makes of
3
+ * `@volter/sdk/session/limited-view`): the same requests the Blender in the tab makes of
4
4
  * its session (`serving/blender-routes.ts`), answered in the page against the project's files in
5
5
  * memory. The engine's own bytes are not here: they are fixed for a build, so the serving module
6
6
  * declares them (`viewSnapshotRoutes`) and the view ships what the session answered.
@@ -10,7 +10,7 @@
10
10
  * memory, readable by the editor and the workbench like any other file, gone on reload.
11
11
  */
12
12
 
13
- import type { ViewRoute, ViewServingModule, ViewServingServices } from '@volter/editor-sdk/session/limited-view';
13
+ import type { ViewRoute, ViewServingModule, ViewServingServices } from '@volter/sdk/session/limited-view';
14
14
 
15
15
  /** The largest chunk a document save sends (`document-chunks.mts`'s cut bound). */
16
16
  const DOCUMENT_CHUNK_MAX = 4 * 1024 * 1024;