@volter/editor-blender 0.1.0

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 (58) hide show
  1. package/LICENSE +1409 -0
  2. package/README.md +17 -0
  3. package/contributions/blender-header-menus.tsx +483 -0
  4. package/contributions/blender-icon-trace.mjs +403 -0
  5. package/contributions/blender-icons.source.mjs +2925 -0
  6. package/contributions/blender-node-editor.document.tsx +1402 -0
  7. package/contributions/blender-node-geometry.ts +1138 -0
  8. package/contributions/blender-node-panels.source.mjs +485 -0
  9. package/contributions/blender-outliner-authoring.ts +1729 -0
  10. package/contributions/blender-outliner-model.ts +389 -0
  11. package/contributions/blender-palette.source.mjs +319 -0
  12. package/contributions/blender-properties-model.ts +351 -0
  13. package/contributions/blender-properties-tab.tsx +100 -0
  14. package/contributions/blender-properties-view.tsx +1191 -0
  15. package/contributions/blender-runtime-skin.ts +619 -0
  16. package/contributions/blender-runtime.document.tsx +232 -0
  17. package/contributions/blender-timeline-geometry.ts +323 -0
  18. package/contributions/blender-timeline.document.tsx +1056 -0
  19. package/contributions/blender-uv-editor.document.tsx +483 -0
  20. package/contributions/blender-uv-geometry.ts +305 -0
  21. package/contributions/blender-version.status.tsx +93 -0
  22. package/contributions/blender.command.ts +102 -0
  23. package/contributions/blender.icons.json +1247 -0
  24. package/contributions/blender.icons.traced.json +1561 -0
  25. package/contributions/blender.keymap.ts +39 -0
  26. package/contributions/blender.node-panels.json +2436 -0
  27. package/contributions/blender.palette.json +93 -0
  28. package/contributions/blender.status.tsx +263 -0
  29. package/contributions/blender.style.ts +271 -0
  30. package/contributions/model.layout.ts +53 -0
  31. package/contributions/models.finder.ts +59 -0
  32. package/contributions/properties-bone-constraints.inspector.tsx +50 -0
  33. package/contributions/properties-bone.inspector.tsx +184 -0
  34. package/contributions/properties-collection.inspector.tsx +96 -0
  35. package/contributions/properties-constraints.inspector.tsx +69 -0
  36. package/contributions/properties-data.inspector.tsx +229 -0
  37. package/contributions/properties-material.inspector.tsx +121 -0
  38. package/contributions/properties-modifiers.inspector.tsx +74 -0
  39. package/contributions/properties-object.inspector.tsx +215 -0
  40. package/contributions/properties-output.inspector.tsx +210 -0
  41. package/contributions/properties-particles.inspector.tsx +494 -0
  42. package/contributions/properties-physics.inspector.tsx +614 -0
  43. package/contributions/properties-render.inspector.tsx +446 -0
  44. package/contributions/properties-scene.inspector.tsx +174 -0
  45. package/contributions/properties-texture.inspector.tsx +300 -0
  46. package/contributions/properties-view-layer.inspector.tsx +145 -0
  47. package/contributions/properties-world.inspector.tsx +130 -0
  48. package/contributions/sculpt.layout.ts +25 -0
  49. package/contributions/shading.layout.ts +99 -0
  50. package/contributions/texture.layout.ts +16 -0
  51. package/contributions/uv-editing.layout.ts +93 -0
  52. package/host/blender-runtime-host.ts +1256 -0
  53. package/package.json +77 -0
  54. package/src/layouts.tsx +48 -0
  55. package/src/looks.ts +14 -0
  56. package/src/node-view-state.ts +125 -0
  57. package/src/timeline-view-state.ts +154 -0
  58. package/src/uv-view-state.ts +125 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * ONE PROPERTIES TAB IS ONE `selection.inspector` CONTRIBUTION — the factory
3
+ * every `properties-*.inspector.tsx` in this package is built from (WORK.md
4
+ * §Blender in the tab is Blender, "Inspection parity", I1, item 3).
5
+ *
6
+ * WHY A TAB IS A SECTION, and not a rail of our own: under the `properties`
7
+ * presentation the inspector's sections ARE the vertical icon rail, 1:1
8
+ * (`packages/editor/src/components/InspectionProjection.tsx`,
9
+ * `PropertiesColumn`). So the way to make Blender's tab rail appear is to
10
+ * contribute Blender's tabs as sections — which is also exactly the shape I2
11
+ * needs, because each of these modules then grows a CURATED body over the same
12
+ * door while the generic view stays beneath it.
13
+ *
14
+ * WHICH TABS STAND is `buttons_context.cc`'s answer, computed in the engine
15
+ * (`session.py::rna_context`) and read here. Two kinds of tab:
16
+ *
17
+ * - `standing: 'any' | 'object'` — a tab `buttons_context.cc` builds a path
18
+ * to UNCONDITIONALLY given a scene (`buttons_context_path_scene`,
19
+ * `_view_layer`, `_world`) or given an object at all
20
+ * (`buttons_context_path_object`, of which only the OBJECT tab is standing
21
+ * here — Physics and Constraints share that path but are context-gated,
22
+ * because Blender's Properties editor follows the ACTIVE OBJECT rather
23
+ * than the row that was clicked, and only the door knows there is one).
24
+ * These match from the SELECTION alone, synchronously.
25
+ * - everything else — Modifiers (an object type set), Particles (a mesh),
26
+ * Object Data (the object has data), Bone (an active bone), Material,
27
+ * Collection, Texture — which only the engine can answer, so they match
28
+ * against the context the door last returned.
29
+ *
30
+ * THE STANDING TABS EXIST SO THE RAIL IS NEVER EMPTY: `match()` is
31
+ * synchronous and the first composition has read nothing, so if every tab
32
+ * waited on the door there would be no rail at all. The READ itself is driven
33
+ * from `match` (see the note there), and when its answer lands it calls
34
+ * `documents.contextChanged`, which re-derives the whole inspection and brings
35
+ * the rest of the rail in.
36
+ */
37
+
38
+ import type {
39
+ ToolContributionNode,
40
+ ToolInspectorContributionProps,
41
+ } from '@volter/editor-sdk/contributions';
42
+ import {
43
+ blenderPropertiesState,
44
+ resolveBlenderSubject,
45
+ showBlenderSubject,
46
+ } from './blender-properties-model';
47
+ import { type BlenderCuratedPanel, BlenderPropertiesSection } from './blender-properties-view';
48
+
49
+ export interface BlenderPropertiesTab {
50
+ /** `BCONTEXT_*` in lower case, as the door spells it. */
51
+ readonly id: string;
52
+ /** Whether this tab needs the engine's answer to exist, or stands on the
53
+ * selection alone — see the header. */
54
+ readonly standing?: 'any' | 'object';
55
+ /** BLENDER'S OWN PANELS for this tab, in Blender's order, transcribed from
56
+ * the matching `scripts/startup/bl_ui/properties_*.py`
57
+ * ({@link BlenderCuratedPanel}). The generic RNA view stays beneath them
58
+ * under "All properties"; a tab that declares none is the generic view
59
+ * alone, which is what I1 shipped. */
60
+ readonly curated?: readonly BlenderCuratedPanel[];
61
+ }
62
+
63
+ export function blenderPropertiesTabMatch(tab: BlenderPropertiesTab) {
64
+ return (node: ToolContributionNode | null, adapter: unknown): boolean => {
65
+ const subject = resolveBlenderSubject(node, adapter);
66
+ if (subject === null) return false;
67
+ // THE READ IS DRIVEN FROM `match`, NOT FROM A BODY, and that is a
68
+ // correction the live walk forced: under the `properties` presentation
69
+ // the host mounts ONLY THE ACTIVE TAB's body, so a read driven from a
70
+ // body never happened at all unless the person had already clicked a
71
+ // Blender tab — the rail showed the standing tabs and nothing else, for
72
+ // good (measured 2026-09-19). A matcher runs on every composition,
73
+ // active tab or not, so this is the one place that always sees the
74
+ // subject. It is idempotent by construction: `showBlenderSubject`
75
+ // returns immediately unless the subject moved.
76
+ showBlenderSubject(subject);
77
+ if (tab.standing === 'any') return true;
78
+ if (tab.standing === 'object') return subject.kind === 'object';
79
+ const state = blenderPropertiesState();
80
+ // The context must be the one read for THIS subject: a stale answer from
81
+ // the previously selected object would show its tabs over the new one.
82
+ // BOTH halves of the subject, because a COLLECTION row and the Scene
83
+ // Collection row both read with no object and would otherwise pass each
84
+ // other's context.
85
+ const object = subject.kind === 'object' ? subject.name : null;
86
+ const collection = subject.kind === 'collection' ? subject.path : null;
87
+ if (state.object !== object || state.collection !== collection) return false;
88
+ return state.context?.tabs.some((entry) => entry.id === tab.id) ?? false;
89
+ };
90
+ }
91
+
92
+ export function blenderPropertiesTabSection(tab: BlenderPropertiesTab) {
93
+ return function BlenderPropertiesTabSection({ node, adapter }: ToolInspectorContributionProps) {
94
+ const subject = resolveBlenderSubject(node, adapter);
95
+ if (subject === null) return null;
96
+ return (
97
+ <BlenderPropertiesSection tabId={tab.id} subject={subject} curated={tab.curated ?? []} />
98
+ );
99
+ };
100
+ }