@volter/editor-blender 0.1.11 → 0.1.12

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.
@@ -19,7 +19,7 @@
19
19
  * ## Licence
20
20
  *
21
21
  * Blender's icon sources are GPL-2.0-or-later, like the rest of the Blender
22
- * tree, and a trace of one is a derivative of it. `@vgai/blender` therefore
22
+ * tree, and a trace of one is a derivative of it. `@volter/blender` therefore
23
23
  * carries BOTH licences — its SPDX expression is
24
24
  * `AGPL-3.0-only AND GPL-3.0-or-later` (`packages/blender/LICENSE`): our code
25
25
  * AGPL-3.0-only, Blender's traced artwork conveyed under GPL-3.0-or-later on
@@ -4,7 +4,7 @@
4
4
  * sources and are GPL-2.0-or-later artwork (see "GPL PATHS, DELIBERATELY"
5
5
  * below, `blender-icon-trace.mjs`, and the provenance file
6
6
  * `blender.icons.traced.json`); the other 149 are drawn HERE in Blender's
7
- * IDIOM. That mixture is why `@vgai/blender` is
7
+ * IDIOM. That mixture is why `@volter/blender` is
8
8
  * `AGPL-3.0-only AND GPL-3.0-or-later` (`packages/blender/LICENSE`).
9
9
  * The idiom is what a screenshot conveys: a 16-unit grid, monochrome filled
10
10
  * silhouettes, ~1.3-unit strokes rendered as filled capsules with round ends,
@@ -2464,7 +2464,7 @@ g(
2464
2464
  * and the outliner tints a row's type glyph the same way. Each name below is
2465
2465
  * a category the palette inks (`EditorTheme.color.category`, where every
2466
2466
  * value's source coordinates are recorded); a glyph paints
2467
- * `var(--vgai-category-<tone>, currentColor)`, so under a palette that names
2467
+ * `var(--volter-category-<tone>, currentColor)`, so under a palette that names
2468
2468
  * no category group it paints exactly as it did monochrome.
2469
2469
  *
2470
2470
  * A NAME, NOT A SITE. A tone here tints EVERY site that asks for this icon
@@ -2507,7 +2507,7 @@ g(
2507
2507
  * glyph — and an explicit site tone wins by contract. A tone there would
2508
2508
  * never paint.
2509
2509
  */
2510
- /** The union `IconCategoryTone` declares (`@vgai/editor-sdk/looks`), and the
2510
+ /** The union `IconCategoryTone` declares (`@volter/editor-sdk/looks`), and the
2511
2511
  * reason `blender.style.ts` may narrow this JSON with an assertion: a tone
2512
2512
  * outside it never reaches the artifact. */
2513
2513
  const TONES = ['object', 'modifier', 'material', 'tool', 'operator', 'data', 'scene', 'collection', 'select'];
@@ -2752,7 +2752,7 @@ g(
2752
2752
  dataVertex(10, 20),
2753
2753
  );
2754
2754
 
2755
- // The Outliner's DATABLOCK row (`@vgai/blender`'s datablock hierarchy) —
2755
+ // The Outliner's DATABLOCK row (`@volter/blender`'s datablock hierarchy) —
2756
2756
  // the same MESH_DATA mark the Properties rail's Data tab draws, under its own
2757
2757
  // name because the two sites composite differently: `outliner.png`'s data
2758
2758
  // glyph is #07b189 over the row's #272727 and `properties-object.png`'s tab is
@@ -2783,7 +2783,7 @@ g('outliner-data', glyphs['properties-data'].path);
2783
2783
  * justify one shape.
2784
2784
  *
2785
2785
  * GPL PATHS, DELIBERATELY — AND THE PACKAGE'S SPDX SAYS SO. Blender's icon
2786
- * sources are GPL-2.0-or-later, and `@vgai/blender` is
2786
+ * sources are GPL-2.0-or-later, and `@volter/blender` is
2787
2787
  * `AGPL-3.0-only AND GPL-3.0-or-later` (`packages/blender/LICENSE`, whose
2788
2788
  * notice block names this set, its provenance file and the §13 combination):
2789
2789
  * our code is AGPL, these 197 paths are Blender's artwork conveyed as GPL-3.0,
@@ -31,7 +31,7 @@
31
31
  *
32
32
  * WHY IT IS THE DRAWER AND NOT A SECOND DOCUMENT. Blender's Shading workspace
33
33
  * puts the Shader Editor in the AREA BELOW the 3D viewport, and this host's
34
- * bottom group (`vgai:bottom-center`) is that area: the Model workspace hides
34
+ * bottom group (`volter:bottom-center`) is that area: the Model workspace hides
35
35
  * it for exactly this reason, recorded in `workspace-regions.ts` — "Blender's
36
36
  * modeling workspace has no timeline strip". The Shading workspace shows it
37
37
  * and puts this in it.
@@ -109,8 +109,8 @@ export const title = 'Shader Editor';
109
109
 
110
110
  // THE VIEW'S PRODUCT DOOR (WORK.md §The core is Code-OSS U8, ruling 1). This
111
111
  // view's verbs are published ONCE, here, where the view itself is contributed:
112
- // under the Code-OSS frame each becomes a `vgai.blender-node-view.<verb>`
113
- // command the bridge dispatches into the view, and standalone `vgai edit` —
112
+ // under the Code-OSS frame each becomes a `volter.blender-node-view.<verb>`
113
+ // command the bridge dispatches into the view, and standalone `volter-model-editor edit` —
114
114
  // which has no command service — reaches the SAME table through the session's
115
115
  // `blender-node-view` verb. One table, two doors, which is why the remaining
116
116
  // read-only editors (UV Editing, Animation, Texture Paint) add no session verb
@@ -1171,9 +1171,9 @@ function NodeEditorHeader({
1171
1171
  style={{
1172
1172
  display: 'flex',
1173
1173
  alignItems: 'center',
1174
- gap: 'var(--vgai-space-2)',
1174
+ gap: 'var(--volter-space-2)',
1175
1175
  height: HEADER_HEIGHT,
1176
- padding: '0 var(--vgai-space-2)',
1176
+ padding: '0 var(--volter-space-2)',
1177
1177
  background: NODE_THEME.background,
1178
1178
  color: NODE_THEME.text,
1179
1179
  font: `${UI_TEXT_POINTS}px inherit`,
@@ -1189,7 +1189,7 @@ function NodeEditorHeader({
1189
1189
  <span style={{ opacity: 0.6 }}>{Math.round(zoom * 100)}%</span>
1190
1190
  <button
1191
1191
  type="button"
1192
- className="vgai-btn"
1192
+ className="volter-btn"
1193
1193
  data-variant="ghost"
1194
1194
  data-size="sm"
1195
1195
  onClick={onViewAll}
@@ -1199,7 +1199,7 @@ function NodeEditorHeader({
1199
1199
  </button>
1200
1200
  <button
1201
1201
  type="button"
1202
- className="vgai-btn"
1202
+ className="volter-btn"
1203
1203
  data-variant="ghost"
1204
1204
  data-size="sm"
1205
1205
  data-testid="node-editor-use-nodes"
@@ -1258,7 +1258,7 @@ function StatusLine({
1258
1258
  height: STATUS_HEIGHT,
1259
1259
  display: 'flex',
1260
1260
  alignItems: 'center',
1261
- padding: '0 var(--vgai-space-2)',
1261
+ padding: '0 var(--volter-space-2)',
1262
1262
  background: NODE_THEME.background,
1263
1263
  color: error || refusal ? '#ffa028' : '#888888',
1264
1264
  font: `${UI_TEXT_POINTS}px inherit`,
@@ -1302,14 +1302,14 @@ function NodePanel({
1302
1302
  overflow: 'auto',
1303
1303
  // THE SIDEBAR IS PART OF THE NODE EDITOR'S AREA, so it wears the
1304
1304
  // space's own `TH_BACK` like the header and the canvas do. It used to
1305
- // paint `--vgai-color-surface-panel`, which is the DOCK's grey
1305
+ // paint `--volter-color-surface-panel`, which is the DOCK's grey
1306
1306
  // (#303030 under the Blender look): Blender fills every region of a
1307
1307
  // node editor area with `TH_BACK` and draws panels ON it
1308
1308
  // (`ED_region_panels`), so the grey was this view wearing its
1309
1309
  // container's colour. MEASURED 2026-09-19 (the frame walk, beat 5):
1310
1310
  // at (600,500) — inside the Shader Editor's pane, which a 320px
1311
1311
  // sidebar had squeezed the canvas out of — the read was #303030 where
1312
- // the beat named #1a1a1a. The frame's `vgai.nodeEditor.background`
1312
+ // the beat named #1a1a1a. The frame's `volter.nodeEditor.background`
1313
1313
  // theme colour carries the same traced value for the workbench's own
1314
1314
  // painting; this is the same number, from the same trace, for the
1315
1315
  // pixels we paint ourselves.
@@ -1317,10 +1317,10 @@ function NodePanel({
1317
1317
  color: NODE_THEME.text,
1318
1318
  font: `${UI_TEXT_POINTS}px inherit`,
1319
1319
  borderLeft: '1px solid #161616',
1320
- padding: 'var(--vgai-space-2)',
1320
+ padding: 'var(--volter-space-2)',
1321
1321
  }}
1322
1322
  >
1323
- <div style={{ opacity: 0.6, marginBottom: 'var(--vgai-space-2)' }}>Node</div>
1323
+ <div style={{ opacity: 0.6, marginBottom: 'var(--volter-space-2)' }}>Node</div>
1324
1324
  {!node ? (
1325
1325
  // Blender's panel `poll` is `context.active_node is not None`
1326
1326
  // (`space_node.py:806-808`): with no active node the tab draws nothing.
@@ -1339,7 +1339,7 @@ function NodePanel({
1339
1339
  <div
1340
1340
  style={{
1341
1341
  opacity: 0.6,
1342
- margin: 'var(--vgai-space-3) 0 var(--vgai-space-1)',
1342
+ margin: 'var(--volter-space-3) 0 var(--volter-space-1)',
1343
1343
  }}
1344
1344
  >
1345
1345
  Properties
@@ -1371,7 +1371,7 @@ function Row({
1371
1371
  }) {
1372
1372
  return (
1373
1373
  <div
1374
- style={{ display: 'flex', alignItems: 'center', gap: 'var(--vgai-space-2)', height: 20 }}
1374
+ style={{ display: 'flex', alignItems: 'center', gap: 'var(--volter-space-2)', height: 20 }}
1375
1375
  onPointerDown={() =>
1376
1376
  onRefuse(`"${label}" is read here and written nowhere — inspection parity.`)
1377
1377
  }
@@ -74,7 +74,7 @@
74
74
  * ## Licence
75
75
  *
76
76
  * What lands in the JSON is a table of PANEL NAMES and SOCKET NAMES read out
77
- * of GPL-2.0-or-later sources. `@vgai/blender` is AGPL-3.0-only, which that
77
+ * of GPL-2.0-or-later sources. `@volter/blender` is AGPL-3.0-only, which that
78
78
  * is compatible with (relicense to GPL-3.0, then §13), and the package
79
79
  * already carries Blender itself — the same standing this file's sibling
80
80
  * `blender-icon-trace.mjs` records for the icon paths. Nothing traced here
@@ -41,7 +41,7 @@ interface PresentedView {
41
41
  * `BlenderRuntimeView.blenderSelection`. The engine is the truth and this
42
42
  * is the read; {@link blenderEngineSelection} is the door. */
43
43
  blenderSelection(): { readonly selected: readonly string[]; readonly active: string | null };
44
- /** Where `vgai.stage.mode` comes from — see `BlenderRuntimeView.stageMode`. */
44
+ /** Where `volter.stage.mode` comes from — see `BlenderRuntimeView.stageMode`. */
45
45
  setStageMode(mode: string | null): void;
46
46
  }
47
47
 
@@ -203,7 +203,7 @@ async function read(selected: readonly string[]): Promise<void> {
203
203
  if (seq !== readSeq) return;
204
204
  // THE MODE RIDES WITH THE TREE, because the tree is what needs it (pose
205
205
  // rows exist only in pose mode) and because the read happens anyway.
206
- // `vgai.stage.mode` is then the document's own published context — see
206
+ // `volter.stage.mode` is then the document's own published context — see
207
207
  // `BlenderRuntimeView.stageMode`.
208
208
  blenderPresentedView()?.setStageMode(tree?.mode ?? null);
209
209
  publish({ tree, ...index(tree), loading: false, error: null });
@@ -39,7 +39,7 @@
39
39
  * widget text #e6e6e6 :98 number slider #4772b3 :97
40
40
  * checkbox mark #ffffff :77 (wcol_option.item)
41
41
  *
42
- * The values are literal rather than `var(--vgai-…)` because the palette is
42
+ * The values are literal rather than `var(--volter-…)` because the palette is
43
43
  * the FRAME's grading (U8/U9) and eyedropped: `blender.palette.json` carries
44
44
  * `surface.panel` #2f2f2f where the source says `.space_properties.back` is
45
45
  * #303030, and `content.primary` #e5e5e5 against `.text` #e6e6e6. Where a
@@ -16,7 +16,7 @@
16
16
  * TypeScript geometry module. The bpy scripts that authored a `.blend` are
17
17
  * ordinary project files beside it (`src/models/<name>.py`); this document
18
18
  * does not run them, a person or an agent does, through the session's Blender
19
- * (`vgai blender-mcp`).
19
+ * (`volter blender-mcp`).
20
20
  *
21
21
  * WITHOUT AN ENTRY it is still the document `blender-start` presents into: a
22
22
  * project that lists no `.blend` of its own gets one Model document at the
@@ -117,12 +117,12 @@ const REFUSALS = {
117
117
  * sentence, with ONE author: the `play` verb throws it and the header's
118
118
  * controls wear it as their title while they are disabled.
119
119
  *
120
- * It exists because the two halves disagreed. `vgai.timeline.play` refused by
120
+ * It exists because the two halves disagreed. `volter.timeline.play` refused by
121
121
  * name while the BUTTON called `transport()?.play()` straight through — the
122
122
  * clock went to `playing`, both glyphs flipped to Pause, and the playhead
123
123
  * stayed on frame 1 forever, because `#seek`/the play tick return early with
124
124
  * no mixer to move (`blender-runtime-skin.ts`). Measured on U8's walk 3
125
- * (2026-09-20) over a `--template models` scaffold: `vgai.timeline.state`
125
+ * (2026-09-20) over a `--template models` scaffold: `volter.timeline.state`
126
126
  * answered `action: null` with that warning while the header showed a running
127
127
  * transport. A control whose success can be invisible must say so.
128
128
  */
@@ -240,8 +240,8 @@ function report(): unknown {
240
240
  * no mixer (`blender-runtime-skin.ts`), so the seek was a no-op and
241
241
  * `report()` then answered `frame: 1` — the playhead's honest position and a
242
242
  * complete lie about the gesture. Measured on walk 5 over a fresh
243
- * `model-editor create` scaffold: `vgai.timeline.frame {frame:120}` and
244
- * `vgai.timeline.jump-end` (frame 250) both answered `frame: 1`,
243
+ * `model-editor create` scaffold: `volter.timeline.frame {frame:120}` and
244
+ * `volter.timeline.jump-end` (frame 250) both answered `frame: 1`,
245
245
  * `refusal: null`. This is the half of walk 4's W5 (#7740) that the header
246
246
  * got and the VERBS did not — there the buttons were disabled wearing this
247
247
  * sentence as their title while `play` threw it, and the five verbs that
@@ -822,9 +822,9 @@ function TimelineViewMenu() {
822
822
  style={{
823
823
  display: 'flex',
824
824
  alignItems: 'center',
825
- gap: 'var(--vgai-space-2)',
825
+ gap: 'var(--volter-space-2)',
826
826
  width: '100%',
827
- padding: 'var(--vgai-space-1) var(--vgai-space-3)',
827
+ padding: 'var(--volter-space-1) var(--volter-space-3)',
828
828
  border: 'none',
829
829
  background: 'transparent',
830
830
  color: TIMELINE_CHROME.widgetText,
@@ -851,7 +851,7 @@ function TimelineViewMenu() {
851
851
  background: 'transparent',
852
852
  color: TIMELINE_CHROME.text,
853
853
  font: 'inherit',
854
- padding: '0 var(--vgai-space-2)',
854
+ padding: '0 var(--volter-space-2)',
855
855
  cursor: 'pointer',
856
856
  }}
857
857
  >
@@ -93,8 +93,8 @@ const REFUSALS = {
93
93
  } as const;
94
94
 
95
95
  /** THE VIEW'S VERBS, published once, here — U8's ruling 1 (2026-09-19). Under
96
- * the Code-OSS frame each becomes a `vgai.blender-uv-view.<verb>` command the
97
- * bridge dispatches into the view, and standalone `vgai edit` reaches the
96
+ * the Code-OSS frame each becomes a `volter.blender-uv-view.<verb>` command the
97
+ * bridge dispatches into the view, and standalone `volter-model-editor edit` reaches the
98
98
  * SAME table through the session. **This view adds no `blender-*` session
99
99
  * verb at all**, which is exactly what the ruling asked the remaining I5
100
100
  * views to stop paying for. */
@@ -381,8 +381,8 @@ export default function BlenderUvEditor() {
381
381
  // MEASURED difference between a drawer panel and an EDITOR GROUP. In
382
382
  // the drawer this view's `height: '100%'` worked, because the drawer
383
383
  // group imposes a height on its panel. As a `workspace.document` in
384
- // `vgai:area:uv` it resolved to ZERO against an auto-height content
385
- // box — measured live 2026-09-19, `vgai.blender-uv-view.state`
384
+ // `volter:area:uv` it resolved to ZERO against an auto-height content
385
+ // box — measured live 2026-09-19, `volter.blender-uv-view.state`
386
386
  // answered `size: {w: 782, h: 0}` with a fully READ subject beneath
387
387
  // it (24 corners, 6 faces, the ProbeChecker backdrop found), which is
388
388
  // a view that has everything to draw and nowhere to draw it. The node
@@ -15,7 +15,7 @@
15
15
  *
16
16
  * It is read through the SDK's host door and NOT through `@editor/*`, and
17
17
  * its two inline styles spell the theme tokens directly
18
- * (`var(--vgai-space-2)`, `var(--vgai-font-md)`) rather than importing the
18
+ * (`var(--volter-space-2)`, `var(--volter-font-md)`) rather than importing the
19
19
  * host's `fontSizeVar`/`spaceVar` handles: `@volter/editor-blender` is pinned at ZERO
20
20
  * host imports (`validate-editor-closure.mjs`), the first package to reach
21
21
  * that destination, and a token NAME is the published contract while the
@@ -53,7 +53,7 @@ export default function BlenderVersionStatus() {
53
53
  if (version === null) return null;
54
54
  return (
55
55
  <span
56
- className="vgai-status-copy"
56
+ className="volter-status-copy"
57
57
  data-testid="status-blender-version"
58
58
  data-engine-version={version}
59
59
  // NO COLOUR AND NO `sm`. Measured on the native-2x frames, Blender's
@@ -67,8 +67,8 @@ export default function BlenderVersionStatus() {
67
67
  style={{
68
68
  display: 'inline-flex',
69
69
  alignItems: 'center',
70
- gap: 'var(--vgai-space-2)',
71
- fontSize: 'var(--vgai-font-md)',
70
+ gap: 'var(--volter-space-2)',
71
+ fontSize: 'var(--volter-font-md)',
72
72
  }}
73
73
  title={`This project is built against engine ${version}`}
74
74
  >
@@ -82,8 +82,8 @@ export default function BlenderVersionStatus() {
82
82
  <span
83
83
  aria-hidden="true"
84
84
  style={{
85
- width: 'var(--vgai-stroke-resting)',
86
- height: 'var(--vgai-icon-md)',
85
+ width: 'var(--volter-stroke-resting)',
86
+ height: 'var(--volter-icon-md)',
87
87
  background: 'currentColor',
88
88
  }}
89
89
  />
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * These left `command-listener.ts`'s switch (and their rows left
11
11
  * `command-table.ts`) when Blender's editor half left the host — WORK.md
12
- * §The workbench, item D. `vgai blender-mcp` is transport only: every
12
+ * §The workbench, item D. `volter blender-mcp` is transport only: every
13
13
  * `execute_blender_code`, `get_scene_info`, `get_object_info` and
14
14
  * `get_viewport_screenshot` still arrives as the same `blender-*` command
15
15
  * through the same relay, and is answered here instead of there.
@@ -93,7 +93,7 @@ export const commands: CommandContribution['commands'] = {
93
93
  'blender-rig': verb('none', 60_000),
94
94
  'blender-action-clip': verb('none', 120_000),
95
95
  // THE NODE VIEW'S OWN ACTIONS as a session verb — the keyboard ruling's
96
- // `vgai.*`-command-per-action pattern, and the only way the drawer's view
96
+ // `volter.*`-command-per-action pattern, and the only way the drawer's view
97
97
  // can be read or driven at all: `editor.document.*` is scoped to the active
98
98
  // CENTER document by its own contract, and a utility is not one. Read-only
99
99
  // over the MODEL: `look` and the view transform are inspection state, and a
@@ -38,8 +38,8 @@
38
38
  * a surface ever makes a button mean something else, it publishes that and
39
39
  * this reads it; it does not get hardcoded here.
40
40
  *
41
- * The inline styles spell the theme tokens directly (`var(--vgai-space-5)`,
42
- * `var(--vgai-font-md)`) instead of importing the host's `spaceVar` /
41
+ * The inline styles spell the theme tokens directly (`var(--volter-space-5)`,
42
+ * `var(--volter-font-md)`) instead of importing the host's `spaceVar` /
43
43
  * `fontSizeVar` handles: `@volter/editor-blender` is pinned at ZERO host imports
44
44
  * (`validate-editor-closure.mjs`). See `blender-version.status.tsx`.
45
45
  */
@@ -140,7 +140,7 @@ const RADIUS = 3.6;
140
140
  const BOX = { width: 12, height: 16 } as const;
141
141
 
142
142
  function MouseGlyph({ button }: { button: MouseButton }) {
143
- const id = `vgai-mouse-${button}`;
143
+ const id = `volter-mouse-${button}`;
144
144
  const rightX = BOX.width - BUTTON_W;
145
145
  const fillX = button === 'right' ? rightX : 0;
146
146
  // The stroke's own rect runs along the centre line of a 1-unit stroke, so it
@@ -156,7 +156,7 @@ function MouseGlyph({ button }: { button: MouseButton }) {
156
156
  // 16 CSS px, which is the `lg` icon rung — and Blender's glyph measures
157
157
  // exactly 16 CSS tall in the frame. `width: auto` then resolves to 12
158
158
  // from the viewBox, the measured width, with no second number to drift.
159
- style={{ height: 'var(--vgai-icon-lg)', width: 'auto', display: 'block', flex: '0 0 auto' }}
159
+ style={{ height: 'var(--volter-icon-lg)', width: 'auto', display: 'block', flex: '0 0 auto' }}
160
160
  >
161
161
  <title>{button} mouse button</title>
162
162
  {/* The outer silhouette: what the fill is intersected with, so the lit
@@ -209,11 +209,11 @@ function MouseGlyph({ button }: { button: MouseButton }) {
209
209
  export default function BlenderMouseHintsStatus() {
210
210
  return (
211
211
  <span
212
- className="vgai-status-copy"
212
+ className="volter-status-copy"
213
213
  data-testid="status-blender-mouse-hints"
214
214
  // Blender's hint-to-hint gap is 9.5 CSS (glyph 1 ends at 51.5, glyph 2
215
215
  // starts at 61; the second gap repeats it to a tenth). `space-5` is 10.
216
- style={{ display: 'inline-flex', alignItems: 'center', gap: 'var(--vgai-space-5)' }}
216
+ style={{ display: 'inline-flex', alignItems: 'center', gap: 'var(--volter-space-5)' }}
217
217
  >
218
218
  {HINTS.map((hint) => (
219
219
  <span
@@ -231,8 +231,8 @@ export default function BlenderMouseHintsStatus() {
231
231
  style={{
232
232
  display: 'inline-flex',
233
233
  alignItems: 'center',
234
- gap: 'var(--vgai-space-1)',
235
- fontSize: 'var(--vgai-font-md)',
234
+ gap: 'var(--volter-space-1)',
235
+ fontSize: 'var(--volter-font-md)',
236
236
  }}
237
237
  title={`${hint.label} — the ${hint.button} mouse button in the viewport`}
238
238
  >
@@ -5,14 +5,14 @@
5
5
  * file the engine in the tab opens, edits and saves. The bpy scripts that
6
6
  * authored it are ordinary project files beside it (`src/models/<name>.py`);
7
7
  * this document does not run them, a person or an agent does, through the
8
- * session's Blender (`vgai blender-mcp`).
8
+ * session's Blender (`volter blender-mcp`).
9
9
  *
10
10
  * It replaced `modelsFromMeshModules`, which listed every module exporting
11
11
  * `build(): THREE.BufferGeometry` (owner ruling 2026-09-19, "do the delete":
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 `vgai.adapter.ts`:
15
+ * Select it in `volter.adapter.ts`:
16
16
  *
17
17
  * documents: { find: [{ finder: 'modelsFromBlendFiles', include: ['src/models/**\/*.blend'] }] }
18
18
  *
@@ -28,13 +28,13 @@
28
28
  * is why this arrangement is Model's with one change; and the centre column is
29
29
  * split EXACTLY 50/50, 514 over 514 of the 1031 px between the top bar and the
30
30
  * status bar. On the captured 928-px grid that is 464/464, which is the size
31
- * `vgai:bottom-center` was already carrying at `"visible": false`.
31
+ * `volter:bottom-center` was already carrying at `"visible": false`.
32
32
  *
33
33
  * WHICH AREA THE SHADER EDITOR IS, AND WHAT IT IS MADE OF. Blender's node
34
34
  * editor is the area BELOW the viewport — and an AREA IS AN EDITOR GROUP
35
35
  * (orchestrator ruling 2026-09-19), not the bottom utility drawer. So Shading
36
36
  * declares it as an `areas` entry holding the `blender-node-editor.document`
37
- * contribution, the dock puts it in `vgai:area:shader` below the centre at
37
+ * contribution, the dock puts it in `volter:area:shader` below the centre at
38
38
  * Blender's own 50/50, and the frame's layout host puts the same document in
39
39
  * a second VS Code editor group. The drawer is `hidden` here, as it is in
40
40
  * Model: Blender's Shading screen has no utility strip.
@@ -86,7 +86,7 @@ export const layout: WorkspaceLayoutContribution = {
86
86
  // `x294 y23 w1282 h514`, measured through bpy on the engine — and both the
87
87
  // dock and the Code-OSS frame have exactly that shape in editor groups. So
88
88
  // the Shader Editor is a `workspace.document` this workspace opens into
89
- // `vgai:area:shader`, not a `workspace.utility` in the drawer.
89
+ // `volter:area:shader`, not a `workspace.utility` in the drawer.
90
90
  //
91
91
  // AND THAT IS WHAT RETIRED THE DRAWER RACE BY CONSTRUCTION. Switching to UV
92
92
  // Editing kept opening the Shader Editor — and the cause was NOT the dock
@@ -29,7 +29,7 @@
29
29
  * its width. **RULED 2026-09-19 (orchestrator): a Blender editor AREA is an
30
30
  * editor group; the drawer holds utilities.** The UV editor is not a utility
31
31
  * at all now — it is a `workspace.document` this workspace opens into
32
- * `vgai:area:uv`, LEFT of the model document at Blender's own 786/1571, and
32
+ * `volter:area:uv`, LEFT of the model document at Blender's own 786/1571, and
33
33
  * under the frame the layout host puts the same document in a second VS Code
34
34
  * editor group. Nothing is deviating and nothing is owed to U10 here.
35
35
  *
@@ -58,7 +58,7 @@ var BLENDER_WALI_SUBSTRATE = [
58
58
  { segment: "browser-wali", specifier: "@volter/browser-wali" },
59
59
  { segment: "browser-runtime", specifier: "@volter/browser-runtime" }
60
60
  ];
61
- var BLENDER_WASM_DIR_VARIABLE = "VGAI_BLENDER_WASM_DIR";
61
+ var BLENDER_WASM_DIR_VARIABLE = "VOLTER_BLENDER_WASM_DIR";
62
62
  var SHIPPED_BLENDER_WASM_DIR = (() => {
63
63
  const candidate = fileURLToPath(new URL("../../blender-engine/wasm/", import.meta.url));
64
64
  return existsSync(candidate) ? candidate.replace(/\/$/, "") : null;
@@ -277,7 +277,7 @@ async function readBody(req) {
277
277
  return Buffer.concat(chunks);
278
278
  }
279
279
  function blenderRoutesPlugin(services) {
280
- const transferRoot = join2(tmpdir(), "vgai-blender-transfer");
280
+ const transferRoot = join2(tmpdir(), "volter-blender-transfer");
281
281
  const transferPath = (id) => join2(transferRoot, id);
282
282
  void rm(transferRoot, { recursive: true, force: true });
283
283
  const transferId = (value) => value !== null && /^[0-9a-f-]{36}$/.test(value) ? value : null;
@@ -463,7 +463,7 @@ function blenderRoutesPlugin(services) {
463
463
  match: /^\/__editor\/blender-project-file$/,
464
464
  handle: async (_req, res, url) => {
465
465
  const relPath = url.searchParams.get("path") ?? "";
466
- const scratchDocument = isContainedRelativePath(relPath) && relPath.startsWith(".vgai/tmp/") && relPath.endsWith(".blend");
466
+ const scratchDocument = isContainedRelativePath(relPath) && relPath.startsWith(".volter/tmp/") && relPath.endsWith(".blend");
467
467
  if (!services.isProjectOwnedPath(relPath) && !scratchDocument) {
468
468
  json(res, { error: "Not a project-owned path." }, 400);
469
469
  return;
@@ -601,7 +601,7 @@ function blenderRoutesPlugin(services) {
601
601
  }
602
602
  ];
603
603
  return {
604
- name: "vgai-blender-routes",
604
+ name: "volter-blender-routes",
605
605
  configureServer(server) {
606
606
  server.middlewares.use((req, res, next) => {
607
607
  const url = new URL(req.url ?? "/", "http://local");
@@ -10,7 +10,7 @@
10
10
  * and other packages' exports), which is what lets this lane ship against a
11
11
  * workbench it was not built in.
12
12
  *
13
- * `vgai blender-mcp` is transport only: every `execute_blender_code`,
13
+ * `volter blender-mcp` is transport only: every `execute_blender_code`,
14
14
  * `get_scene_info`, `get_object_info` and `get_viewport_screenshot` arrives
15
15
  * here as a `blender-*` command and is answered by Blender running in this
16
16
  * tab's worker (`@volter/blender-engine/browser`). The model exists in that worker
@@ -296,7 +296,7 @@ export async function openModelDocumentBlend(
296
296
  * (`../contributions/blender-properties-*`) call these three rather than the
297
297
  * `blender-rna*` commands: same session, same `session.py` functions, one
298
298
  * fewer hop, and no `EditorCommandResult` envelope to unwrap in a render.
299
- * The COMMANDS remain the wire's door onto the same calls, so a `vgai eval`
299
+ * The COMMANDS remain the wire's door onto the same calls, so a `volter-model-editor eval`
300
300
  * and the panel read one thing.
301
301
  *
302
302
  * THEY NEVER START THE ENGINE. A panel asking what the engine holds must not
@@ -327,7 +327,7 @@ export function blenderSessionStarted(): boolean {
327
327
  * So the version is minted HERE, at the door every RNA write goes through,
328
328
  * and it is bumped by the write rather than by the picture. Both halves of
329
329
  * each door bump it once: the in-page function is what the wire's
330
- * `blender-rna-set` / `blender-outliner-set` case calls, so a `vgai eval` and
330
+ * `blender-rna-set` / `blender-outliner-set` case calls, so a `volter-model-editor eval` and
331
331
  * a panel click are one path. `blender-execute` bumps it too — arbitrary bpy
332
332
  * can change anything RNA answers, and a view that went stale under a probe's
333
333
  * own script would be the same defect one layer out.
@@ -531,8 +531,8 @@ export async function blenderOutlinerSet(
531
531
 
532
532
  /**
533
533
  * THE NODE EDITOR'S VERBS, published ONCE and reached two ways (U8's ruling 1,
534
- * 2026-09-19). Under the Code-OSS frame each is a `vgai.blender-node-view.<verb>`
535
- * command the bridge dispatches into the view; standalone `vgai edit`, which has
534
+ * 2026-09-19). Under the Code-OSS frame each is a `volter.blender-node-view.<verb>`
535
+ * command the bridge dispatches into the view; standalone `volter-model-editor edit`, which has
536
536
  * no command service, reaches the SAME table through the session's
537
537
  * `blender-node-view` verb below. One table, two doors — the shape
538
538
  * `key-actions.ts`'s action table already has, and the reason the next
@@ -757,7 +757,7 @@ async function runtimeView(): Promise<RuntimeView> {
757
757
  `The Blender Model document is not open, or the open one is not a Model this engine can present ` +
758
758
  `to (it must answer applyFrame, captureSnapshot, recordPresentation and recordPhotograph): nothing ` +
759
759
  `published a presentable view as ${id} within 15 s (bound model: ${boundModel?.documentId ?? 'none'}). ` +
760
- 'Open the Model document first (`vgai blender-mcp` opens it before its first call).',
760
+ 'Open the Model document first (`volter blender-mcp` opens it before its first call).',
761
761
  );
762
762
  }
763
763
 
@@ -1030,7 +1030,7 @@ export function blenderRuntime(): BlenderRuntime {
1030
1030
  // how long it has been stuck — the loop that would send the number is the loop
1031
1031
  // that is stuck — so this side keeps the clock and the host carries it out on
1032
1032
  // the heartbeat, the one channel that still beats through a blocked main
1033
- // thread. `vgai status` prints the block. Published as a READ of the live
1033
+ // thread. `volter-model-editor status` prints the block. Published as a READ of the live
1034
1034
  // meter rather than a snapshot, so the host always asks the running session:
1035
1035
  // an in-flight call's age has to be computed at the moment it is reported.
1036
1036
  // The page's own long-task half is the HOST's, behind this same door.
@@ -1050,7 +1050,7 @@ export function lastBlenderCapture(): CaptureRequest | null {
1050
1050
  * not lose what a script modelled: before this, closing the tab lost it, with
1051
1051
  * no `.blend` anywhere. `models/` rather than `public/` is what says a
1052
1052
  * document is the SOURCE a shipped artifact is exported from, not the artifact
1053
- * — which is also why it carries no provenance record (`vgai blender-mcp`'s
1053
+ * — which is also why it carries no provenance record (`volter blender-mcp`'s
1054
1054
  * Mirror owns that line, for `public/`).
1055
1055
  *
1056
1056
  * It is the fallback only. A project that declares the `model` finder lists
@@ -1193,7 +1193,7 @@ export async function handleBlenderCommand(cmd: {
1193
1193
  // RNA version is minted at the door (ruling 3, 2026-09-19), and
1194
1194
  // ARBITRARY bpy can change anything RNA answers, so a script that
1195
1195
  // reached the engine around it would leave every view drawing the tree
1196
- // it had. One path for a `vgai eval`, an MCP call and a panel's own
1196
+ // it had. One path for a `volter-model-editor eval`, an MCP call and a panel's own
1197
1197
  // operator.
1198
1198
  // THE DOOR'S TEXT, verbatim — `execute_blender_code`'s MCP contract is
1199
1199
  // that one string, so the wire keeps answering it while the in-page
@@ -1240,7 +1240,7 @@ export async function handleBlenderCommand(cmd: {
1240
1240
  }
1241
1241
  // THE TREE DOOR on the wire, beside the RNA one: `blender-outliner`
1242
1242
  // answers Blender's View Layer tree and `blender-outliner-set` writes one
1243
- // restriction column, so a `vgai eval` reads exactly what the hierarchy
1243
+ // restriction column, so a `volter-model-editor eval` reads exactly what the hierarchy
1244
1244
  // panel draws.
1245
1245
  case 'blender-outliner': {
1246
1246
  const selected = Array.isArray(cmd['selected'])
@@ -1251,7 +1251,7 @@ export async function handleBlenderCommand(cmd: {
1251
1251
  return { ok: true, data: { result: await session.outliner(selected) } };
1252
1252
  }
1253
1253
  // THE NODE-TREE DOOR on the wire, beside the other two: one material's
1254
- // shader node tree, whole, so a `vgai eval` reads exactly what the node
1254
+ // shader node tree, whole, so a `volter-model-editor eval` reads exactly what the node
1255
1255
  // view draws.
1256
1256
  case 'blender-node-tree':
1257
1257
  return {
@@ -1264,7 +1264,7 @@ export async function handleBlenderCommand(cmd: {
1264
1264
  },
1265
1265
  };
1266
1266
  // THE UV DOOR on the wire, beside the node one: one mesh's UV layout,
1267
- // so a `vgai eval` reads exactly what the UV view draws.
1267
+ // so a `volter-model-editor eval` reads exactly what the UV view draws.
1268
1268
  case 'blender-uv-layout':
1269
1269
  return {
1270
1270
  ok: true,
@@ -1276,7 +1276,7 @@ export async function handleBlenderCommand(cmd: {
1276
1276
  },
1277
1277
  };
1278
1278
  // THE RIG AND CLIP DOORS on the wire, beside the UV one: the skin
1279
- // binding and the action as three.js tracks, so a `vgai eval` reads
1279
+ // binding and the action as three.js tracks, so a `volter-model-editor eval` reads
1280
1280
  // exactly what the presenter bound and what the Timeline plays.
1281
1281
  case 'blender-rig':
1282
1282
  return {
@@ -1303,7 +1303,7 @@ export async function handleBlenderCommand(cmd: {
1303
1303
  //
1304
1304
  // THE SESSION IS THE STANDALONE DOOR ONTO `NODE_VIEW_VERBS`, not a second
1305
1305
  // implementation (U8's ruling 1). Under the Code-OSS frame each verb is a
1306
- // `vgai.blender-node-view.<verb>` command; standalone `vgai edit` has no
1306
+ // `volter.blender-node-view.<verb>` command; standalone `volter-model-editor edit` has no
1307
1307
  // command service, so this verb routes the SAME table. `invokeViewVerb`
1308
1308
  // throws the view's own refusal, which is the sentence this door already
1309
1309
  // answered with.
@@ -1368,7 +1368,7 @@ export async function handleBlenderCommand(cmd: {
1368
1368
  }
1369
1369
  case 'blender-list-files': {
1370
1370
  // WHO to attribute these files to, answered in the same breath as the
1371
- // listing. The write-back door (`vgai blender-mcp`'s Mirror) records a
1371
+ // listing. The write-back door (`volter blender-mcp`'s Mirror) records a
1372
1372
  // file it lands under `public/` in the project's provenance ledger, and
1373
1373
  // what it can honestly name is this session and the model state it had
1374
1374
  // reached — taken here, at the instant of the listing, rather than
@@ -108,7 +108,7 @@ function projectRelative(project: string, absolute: string): string | null {
108
108
  /**
109
109
  * The worker's filesystem is the only place a script's outputs exist; this
110
110
  * mirrors them to disk. Roots: the project (a script's `public/models/x.glb`
111
- * lands in the project) plus any `VGAI_BLENDER_MIRROR_ROOTS` (colon-separated
111
+ * lands in the project) plus any `VOLTER_BLENDER_MIRROR_ROOTS` (colon-separated
112
112
  * absolute paths — the replay harness names its run directory). Only files
113
113
  * whose size or mtime changed since the last mirror are read back, and only
114
114
  * files the SESSION owns are listed at all — a project file it merely read
@@ -117,7 +117,7 @@ function projectRelative(project: string, absolute: string): string | null {
117
117
  * THIS IS THE DOOR THAT RECORDS, because it is the door a session's bytes
118
118
  * enter the project through. MEASURED 2026-09-18: a script's
119
119
  * `export_scene.gltf` to `<project>/public/models/lantern.glb` landed here and
120
- * `.vgai/provenance.json` was never created — the one state
120
+ * `.volter/provenance.json` was never created — the one state
121
121
  * `scripts/validate-project-provenance.mjs` calls fatal, and the opposite of
122
122
  * the doctrine that a generated artifact enters `public/` through a door that
123
123
  * records it atomically. So a file this lands under `public/` is POSTed to the
@@ -131,7 +131,7 @@ function projectRelative(project: string, absolute: string): string | null {
131
131
  * transport uses that same session boundary as the other product commands;
132
132
  * it never creates a second ledger writer in the MCP process.
133
133
  *
134
- * Everything else the session owns — `.vgai/tmp/*.png`, a `.blend`, a replay
134
+ * Everything else the session owns — `.volter/tmp/*.png`, a `.blend`, a replay
135
135
  * harness's run directory outside the project — is mirrored with a plain write
136
136
  * and deliberately NOT recorded: the ledger is about what the project SHIPS,
137
137
  * and `public/` is what ships.
@@ -232,7 +232,7 @@ class TabSession {
232
232
  #origin: string | null = null;
233
233
  #port: number | null = null;
234
234
  /** A replay starts with a fresh model once; ordinary agent sessions persist. */
235
- #freshSessionPending = Boolean(process.env['VGAI_BLENDER_FRESH_SESSION']);
235
+ #freshSessionPending = Boolean(process.env['VOLTER_BLENDER_FRESH_SESSION']);
236
236
  constructor(
237
237
  private readonly project: string,
238
238
  private readonly ensureEditor: () => Promise<void>,
@@ -308,7 +308,7 @@ export async function serveBlenderMcp(
308
308
  const defaults = blenderDefaults as Record<string, string>;
309
309
  const mirror = new Mirror(project, [
310
310
  project,
311
- ...(process.env['VGAI_BLENDER_MIRROR_ROOTS'] ?? '')
311
+ ...(process.env['VOLTER_BLENDER_MIRROR_ROOTS'] ?? '')
312
312
  .split(':')
313
313
  .filter((root) => root.startsWith('/')),
314
314
  ], `${command} blender-mcp`);
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.11",
5
+ "version": "0.1.12",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -30,7 +30,7 @@
30
30
  "./mcp": "./node/blender-mcp.ts",
31
31
  "./package.json": "./package.json"
32
32
  },
33
- "vgai": {
33
+ "volter": {
34
34
  "contributions": [
35
35
  "./contributions/blender-node-editor.document.tsx",
36
36
  "./contributions/blender-timeline.document.tsx",
@@ -69,14 +69,14 @@
69
69
  },
70
70
  "dependencies": {
71
71
  "@modelcontextprotocol/sdk": "1.30.0",
72
- "@volter/blender-engine": "0.1.9",
73
- "@volter/editor-threejs": "0.5.68",
72
+ "@volter/blender-engine": "0.1.10",
73
+ "@volter/editor-threejs": "0.5.69",
74
74
  "zod": "^4.3.6"
75
75
  },
76
76
  "peerDependencies": {
77
- "@volter/editor-sdk": "0.5.68",
78
- "@volter/editor-live": "0.5.68",
79
- "@volter/editor-project": "0.5.68",
77
+ "@volter/editor-sdk": "0.5.69",
78
+ "@volter/editor-live": "0.5.69",
79
+ "@volter/editor-project": "0.5.69",
80
80
  "react": "^19.0.0",
81
81
  "three": "^0.180.0",
82
82
  "vite": "^6.4.3"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `@volter/editor-blender`'s server half (`package.json#vgai.serving`): the routes the Blender
2
+ * `@volter/editor-blender`'s server half (`package.json#volter.serving`): the routes the Blender
3
3
  * in the tab needs from the session — the engine's WebAssembly build, the file spool its
4
4
  * transports move bytes through, the project's files as Blender reads them, and the two ways
5
5
  * bytes it produced reach the project (a shipped output, the session's `.blend`). They were
@@ -55,7 +55,7 @@ export function blenderRoutesPlugin(services: ProjectServingServices): Plugin {
55
55
  // octet-stream; the requester GETs it once and it is gone. The spool keeps PATH AUTHORITY
56
56
  // WITH THE REQUESTER: the page never names a destination on disk, only an id the caller
57
57
  // minted.
58
- const transferRoot = join(tmpdir(), 'vgai-blender-transfer');
58
+ const transferRoot = join(tmpdir(), 'volter-blender-transfer');
59
59
  const transferPath = (id: string): string => join(transferRoot, id);
60
60
  // Every id is a uuid minted by a caller in a PREVIOUS process, so nothing live is here yet:
61
61
  // clearing once at startup bounds what a failed transfer can leave.
@@ -261,7 +261,7 @@ export function blenderRoutesPlugin(services: ProjectServingServices): Plugin {
261
261
  handle: async (_req, res, url) => {
262
262
  const relPath = url.searchParams.get('path') ?? '';
263
263
  const scratchDocument =
264
- isContainedRelativePath(relPath) && relPath.startsWith('.vgai/tmp/') && relPath.endsWith('.blend');
264
+ isContainedRelativePath(relPath) && relPath.startsWith('.volter/tmp/') && relPath.endsWith('.blend');
265
265
  if (!services.isProjectOwnedPath(relPath) && !scratchDocument) {
266
266
  json(res, { error: 'Not a project-owned path.' }, 400);
267
267
  return;
@@ -410,7 +410,7 @@ export function blenderRoutesPlugin(services: ProjectServingServices): Plugin {
410
410
  ];
411
411
 
412
412
  return {
413
- name: 'vgai-blender-routes',
413
+ name: 'volter-blender-routes',
414
414
  configureServer(server) {
415
415
  server.middlewares.use((req, res, next) => {
416
416
  const url = new URL(req.url ?? '/', 'http://local');
@@ -23,7 +23,7 @@
23
23
  * setting and no flag, because nothing a user does may depend on the skew —
24
24
  * the ruling's "a skew may refuse a capability by name, never answer the same
25
25
  * question differently" leaves nothing for a chooser to choose.
26
- * `VGAI_BLENDER_WASM_DIR` is the existing door for pointing the editor at a
26
+ * `VOLTER_BLENDER_WASM_DIR` is the existing door for pointing the editor at a
27
27
  * different build, and pointing it at a WALI pack is how the substrate skew is
28
28
  * reached in development.
29
29
  *
@@ -110,14 +110,14 @@ export const BLENDER_WALI_SUBSTRATE = [
110
110
  { segment: 'browser-runtime', specifier: '@volter/browser-runtime' },
111
111
  ] as const;
112
112
 
113
- export const BLENDER_WASM_DIR_VARIABLE = 'VGAI_BLENDER_WASM_DIR';
113
+ export const BLENDER_WASM_DIR_VARIABLE = 'VOLTER_BLENDER_WASM_DIR';
114
114
 
115
115
  /**
116
116
  * The bundle `@volter/blender-engine` ships — Blender itself, so its own GPL
117
117
  * package (ARCHITECTURE-CORE §Licensing, "Blender's licence stops at the
118
118
  * wire"). This resolution is a PATH, not an import: the editor serves those
119
119
  * bytes and never imports the engine. `import.meta.url` is THIS FILE's esbuild bundle
120
- * (`<pkg>/dist-node/serving.mjs`, the `vgai.serving` module); from it, two
120
+ * (`<pkg>/dist-node/serving.mjs`, the `volter.serving` module); from it, two
121
121
  * directories up and across is the sibling package -- `packages/blender-engine/wasm`
122
122
  * in a checkout, `node_modules/@volter/blender-engine/wasm` installed. Existence is
123
123
  * checked once so a realm that lacks it gets a named absence, not a path.
package/serving/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `@volter/editor-blender`'s server half (`package.json#vgai.serving`, the project-serving door in
2
+ * `@volter/editor-blender`'s server half (`package.json#volter.serving`, the project-serving door in
3
3
  * `@volter/editor-sdk/session/project-serving`): the routes the Blender in the tab reads and
4
4
  * writes through.
5
5
  */
package/src/layouts.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The BLENDER layout — the composition a models project's adapter declares
3
- * (`vgai.adapter.ts`: `editor: { Layout: ModelLayout }`, the door
3
+ * (`volter.adapter.ts`: `editor: { Layout: ModelLayout }`, the door
4
4
  * `@volter/editor-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
package/src/looks.ts CHANGED
@@ -1,12 +1,12 @@
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 `vgai.adapter.ts` declares them from
3
+ * the public surface a project's `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 }
7
7
  *
8
8
  * The same two objects the package's `workspace.style` / `workspace.keymap`
9
- * contributions register (`package.json#vgai.contributions`). Registration
9
+ * contributions register (`package.json#volter.contributions`). Registration
10
10
  * publishes them to every project that declares this package; the adapter's
11
11
  * declaration is what CHOOSES them for one.
12
12
  */
@@ -6,11 +6,11 @@
6
6
  * own header says so: "a selector resolving outside that document's container
7
7
  * is refused by a message naming the scope". The node editor is a DRAWER
8
8
  * utility, so nothing in the product could read or drive it: the sighted door
9
- * (`editor.captureEditorChrome`, `vgai screenshot editor`) photographs it, and
9
+ * (`editor.captureEditorChrome`, `volter-model-editor screenshot editor`) photographs it, and
10
10
  * a photograph cannot click.
11
11
  *
12
12
  * So the view's actions are SESSION VERBS, which is the pattern the keyboard
13
- * ruling already asks for — a `vgai.*` command per action rather than a raw
13
+ * ruling already asks for — a `volter.*` command per action rather than a raw
14
14
  * listener — and this module is the one place their state lives. The component
15
15
  * subscribes to it; `blender-node-view` (the command) reads and writes it. One
16
16
  * store, two readers, no second copy.
@@ -10,8 +10,8 @@
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 `vgai.<view>.<verb>` commands through `@volter/editor-sdk/views`, one
14
- * table behind the frame's command service and standalone `vgai edit`'s
13
+ * publishes `volter.<view>.<verb>` commands through `@volter/editor-sdk/views`, one
14
+ * table behind the frame's command service and standalone `volter-model-editor edit`'s
15
15
  * session verb — so this view adds no `blender-*` verb of its own.
16
16
  *
17
17
  * It imports NOTHING, for the reason the node and UV stores do: the view and
@@ -6,8 +6,8 @@
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 `vgai.<view>.<verb>` commands through `@volter/editor-sdk/views`,
10
- * one table behind the frame's command service and standalone `vgai edit`'s
9
+ * publishes `volter.<view>.<verb>` commands through `@volter/editor-sdk/views`,
10
+ * one table behind the frame's command service and standalone `volter-model-editor 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.
13
13
  *