@volter/editor-game 0.5.65

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 (302) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +23 -0
  3. package/contributions/asset-budget-asset.menu.ts +26 -0
  4. package/contributions/asset-budget.action.ts +18 -0
  5. package/contributions/asset-budget.document.tsx +21 -0
  6. package/contributions/asset-budget.menu.ts +22 -0
  7. package/contributions/audio-unlock.service.ts +17 -0
  8. package/contributions/audio.utility.tsx +15 -0
  9. package/contributions/autoplay.service.ts +48 -0
  10. package/contributions/bridge.command.ts +172 -0
  11. package/contributions/build-profiles.document.tsx +21 -0
  12. package/contributions/build-progress.status.tsx +45 -0
  13. package/contributions/build.action.ts +26 -0
  14. package/contributions/build.command.ts +27 -0
  15. package/contributions/build.header.tsx +37 -0
  16. package/contributions/build.menu.ts +31 -0
  17. package/contributions/build.service.ts +31 -0
  18. package/contributions/connection.status.tsx +43 -0
  19. package/contributions/coverage.service.ts +118 -0
  20. package/contributions/edit-mode-audio.service.ts +23 -0
  21. package/contributions/edit-mode-networking.service.ts +30 -0
  22. package/contributions/game-document.service.ts +25 -0
  23. package/contributions/game-eval.command.ts +210 -0
  24. package/contributions/game.layout.ts +19 -0
  25. package/contributions/gameplay.command.ts +255 -0
  26. package/contributions/generation.service.ts +96 -0
  27. package/contributions/generations.status.tsx +53 -0
  28. package/contributions/ingest.service.ts +80 -0
  29. package/contributions/instances.command.ts +75 -0
  30. package/contributions/navmesh.menu.ts +39 -0
  31. package/contributions/navmesh.service.ts +19 -0
  32. package/contributions/network.utility.tsx +17 -0
  33. package/contributions/play.command.ts +355 -0
  34. package/contributions/profiler.action.ts +16 -0
  35. package/contributions/profiler.menu.ts +22 -0
  36. package/contributions/profiler.utility.tsx +17 -0
  37. package/contributions/react/component-board.service.ts +21 -0
  38. package/contributions/react/design-time-mount.service.ts +60 -0
  39. package/contributions/react/pasteboard.action.ts +41 -0
  40. package/contributions/react/react-inspector.service.ts +85 -0
  41. package/contributions/react/story-documents.service.ts +44 -0
  42. package/contributions/scene-document.service.ts +32 -0
  43. package/contributions/state-watch.action.ts +17 -0
  44. package/contributions/state-watch.menu.ts +23 -0
  45. package/contributions/state-watch.utility.tsx +20 -0
  46. package/contributions/team-playtest.service.ts +124 -0
  47. package/contributions/three/camera-runtime.inspector.tsx +34 -0
  48. package/contributions/three/component-board.service.ts +24 -0
  49. package/contributions/three/component-verbs.command.ts +110 -0
  50. package/contributions/three/component-verbs.service.ts +92 -0
  51. package/contributions/three/constraints.inspector.tsx +34 -0
  52. package/contributions/three/model-asset-sections.service.ts +26 -0
  53. package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
  54. package/contributions/three/story-documents.service.ts +31 -0
  55. package/contributions/three/three-authoring.service.ts +66 -0
  56. package/contributions/transport.header.tsx +19 -0
  57. package/contributions/xstate-behavior.action.ts +42 -0
  58. package/contributions/xstate-behavior.document.tsx +73 -0
  59. package/contributions/xstate-behavior.inspector.tsx +28 -0
  60. package/contributions/xstate-behavior.menu.ts +23 -0
  61. package/package.json +144 -0
  62. package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
  63. package/src/asset-budget/asset-budget-model.ts +799 -0
  64. package/src/asset-budget/basis-encoder.ts +165 -0
  65. package/src/asset-budget/gltf-io.ts +154 -0
  66. package/src/asset-budget/gltf-optimize.ts +384 -0
  67. package/src/asset-budget/image-dims.ts +78 -0
  68. package/src/asset-budget/optimize-apply.ts +221 -0
  69. package/src/audio/AudioDebuggerPanel.tsx +457 -0
  70. package/src/audio/audio-debugger-model.ts +87 -0
  71. package/src/bridge/call.ts +60 -0
  72. package/src/bridge/dispatch.ts +459 -0
  73. package/src/bridge/live-frames.ts +25 -0
  74. package/src/bridge/screenshot.ts +316 -0
  75. package/src/build/BuildProfilesPanel.tsx +476 -0
  76. package/src/build/build-session.ts +247 -0
  77. package/src/build/format-bytes.ts +14 -0
  78. package/src/command-results.ts +36 -0
  79. package/src/coverage/live-authoring-surface.ts +30 -0
  80. package/src/coverage/live-project-verbs.ts +162 -0
  81. package/src/coverage/native-system-coverage.ts +143 -0
  82. package/src/coverage/root-coverage.ts +79 -0
  83. package/src/coverage/session-coverage.ts +193 -0
  84. package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
  85. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
  86. package/src/edit-mode/edit-mode-audio.ts +56 -0
  87. package/src/edit-mode/edit-mode-networking.ts +112 -0
  88. package/src/game-document/DevicePresetPicker.tsx +84 -0
  89. package/src/game-document/GameCaptureFrameButton.tsx +45 -0
  90. package/src/game-document/GameDocument.tsx +215 -0
  91. package/src/game-document/GamePanel.tsx +662 -0
  92. package/src/game-document/InstanceInspectorPicker.tsx +183 -0
  93. package/src/game-document/crowd-debug.ts +183 -0
  94. package/src/game-document/device-preview.ts +336 -0
  95. package/src/game-document/game-view-store.ts +107 -0
  96. package/src/game-document/physics-debug.ts +187 -0
  97. package/src/generation/GenerationActivity.tsx +421 -0
  98. package/src/generation/GenerationGallery.css +231 -0
  99. package/src/generation/generation-documents.tsx +338 -0
  100. package/src/generation/generation-jobs.ts +128 -0
  101. package/src/generation/generation-presentation.ts +257 -0
  102. package/src/host/adapter-reach.ts +445 -0
  103. package/src/host/adapter-runtime-bindings.ts +303 -0
  104. package/src/host/after-paint.ts +112 -0
  105. package/src/host/api/configurations.ts +66 -0
  106. package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
  107. package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
  108. package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
  109. package/src/host/authoring/contract-scenes-stories.ts +204 -0
  110. package/src/host/authoring/creation-site-related.ts +55 -0
  111. package/src/host/authoring/ephemeral-persistence.ts +29 -0
  112. package/src/host/authoring/gesture-persist.ts +84 -0
  113. package/src/host/authoring/ingest-data-writer.ts +232 -0
  114. package/src/host/authoring/ingest-source-persistence.ts +826 -0
  115. package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
  116. package/src/host/authoring/mounted-authoring.ts +41 -0
  117. package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
  118. package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
  119. package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
  120. package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
  121. package/src/host/authoring/pixi-isolation-assets.ts +25 -0
  122. package/src/host/authoring/pixi-live-write-target.ts +979 -0
  123. package/src/host/authoring/pixi-source-identity.ts +141 -0
  124. package/src/host/authoring/pixi-still-presentation.ts +71 -0
  125. package/src/host/authoring/pixi-structure-history.ts +237 -0
  126. package/src/host/authoring/pixi-transform-channels.ts +205 -0
  127. package/src/host/authoring/selection-remount-handoff.ts +23 -0
  128. package/src/host/authoring/source-persistence-backend.ts +373 -0
  129. package/src/host/authoring/source-refresh-revisions.ts +81 -0
  130. package/src/host/authoring/struct-write-pipe.ts +143 -0
  131. package/src/host/auto-frame-window.ts +89 -0
  132. package/src/host/binding-resolver.ts +393 -0
  133. package/src/host/browser-transpile.ts +631 -0
  134. package/src/host/canvas-entry-runtime.ts +95 -0
  135. package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
  136. package/src/host/components/HeaderTelemetry.tsx +341 -0
  137. package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
  138. package/src/host/components/ResolutionPicker.tsx +89 -0
  139. package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
  140. package/src/host/components/frame-debugger-model.ts +579 -0
  141. package/src/host/components/header-telemetry-model.ts +74 -0
  142. package/src/host/components/scene-document.tsx +447 -0
  143. package/src/host/components/utility-view-state.ts +87 -0
  144. package/src/host/components/world-root-stage-binding.tsx +133 -0
  145. package/src/host/components/world-root-stage.ts +1046 -0
  146. package/src/host/coverage/authoring-read-probe.ts +583 -0
  147. package/src/host/coverage/capability-coverage.ts +1587 -0
  148. package/src/host/coverage/coverage-accounting.ts +261 -0
  149. package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
  150. package/src/host/coverage/project-verb-coverage.ts +148 -0
  151. package/src/host/coverage/system-adapter-coverage.ts +373 -0
  152. package/src/host/design-system-stories/StoryLayout.tsx +104 -0
  153. package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
  154. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
  155. package/src/host/document-preview-three.ts +110 -0
  156. package/src/host/entry-adjudication.ts +89 -0
  157. package/src/host/game-location-guard.ts +150 -0
  158. package/src/host/game-module-access.ts +196 -0
  159. package/src/host/game-realm-page.ts +358 -0
  160. package/src/host/game-realm-reclaim.ts +55 -0
  161. package/src/host/game-realm-storage.ts +104 -0
  162. package/src/host/gameplay-export.ts +288 -0
  163. package/src/host/gameplay-recording.ts +717 -0
  164. package/src/host/gated-globals.ts +1511 -0
  165. package/src/host/history/json-history-resource.ts +254 -0
  166. package/src/host/ingest/registry.ts +261 -0
  167. package/src/host/instance-extract-actions.ts +120 -0
  168. package/src/host/instance-fork-actions.ts +120 -0
  169. package/src/host/play-control-hook.ts +26 -0
  170. package/src/host/playwright-shim.ts +479 -0
  171. package/src/host/projection/dom.ts +295 -0
  172. package/src/host/projection/pixi.ts +288 -0
  173. package/src/host/r3f-entry-runtime.ts +77 -0
  174. package/src/host/react-mount-runtime.ts +162 -0
  175. package/src/host/realm-services.ts +148 -0
  176. package/src/host/recording-preview.ts +106 -0
  177. package/src/host/roots/module-root.ts +203 -0
  178. package/src/host/roots/react-root.ts +181 -0
  179. package/src/host/same-realm-loop-gate.ts +544 -0
  180. package/src/host/scene-view-drawability.ts +69 -0
  181. package/src/host/sdk/tools.ts +31 -0
  182. package/src/host/served-bundle-runtime-modules.ts +331 -0
  183. package/src/host/server-log-bridge.ts +60 -0
  184. package/src/host/staged-projects.ts +24 -0
  185. package/src/host/stories/mounted-story-viewport-source.ts +64 -0
  186. package/src/host/stories/story-arg-descriptors.ts +50 -0
  187. package/src/host/stories/story-media-presence.ts +152 -0
  188. package/src/host/surface-content.ts +87 -0
  189. package/src/host/take-named-export.ts +23 -0
  190. package/src/host/three-ingest-runtime.ts +76 -0
  191. package/src/host/types-fastnoise-lite.d.ts +7 -0
  192. package/src/host/types-mikktspace.d.ts +20 -0
  193. package/src/host/types-troika-three-text.d.ts +7 -0
  194. package/src/host/use-active-performance-source.ts +53 -0
  195. package/src/host/viewport-pose-memory.ts +48 -0
  196. package/src/host/viewport-root-presentation.ts +40 -0
  197. package/src/ingest/active-ingest.ts +251 -0
  198. package/src/ingest/active-scene-navigation.ts +43 -0
  199. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
  200. package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
  201. package/src/ingest/capture-wait-report.ts +122 -0
  202. package/src/ingest/deferred-ingest-play.ts +216 -0
  203. package/src/ingest/deferred-ingest-session.ts +23 -0
  204. package/src/ingest/discovery-public-ingest.ts +242 -0
  205. package/src/ingest/dom-stub-mark.ts +7 -0
  206. package/src/ingest/entry-load.ts +56 -0
  207. package/src/ingest/game-contract-realm.ts +34 -0
  208. package/src/ingest/game-pointer-lock.ts +89 -0
  209. package/src/ingest/held-scene-repaint.ts +73 -0
  210. package/src/ingest/host-surface-box.ts +174 -0
  211. package/src/ingest/ingest-boot-viewport.ts +54 -0
  212. package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
  213. package/src/ingest/ingest-canvas-scene.ts +50 -0
  214. package/src/ingest/ingest-evidence-hook.ts +131 -0
  215. package/src/ingest/ingest-frame-snapshot.ts +183 -0
  216. package/src/ingest/ingest-play-commands.ts +134 -0
  217. package/src/ingest/ingest-play-control.ts +299 -0
  218. package/src/ingest/ingest-render-debug.ts +320 -0
  219. package/src/ingest/ingest-siblings.ts +487 -0
  220. package/src/ingest/ingest-status.ts +62 -0
  221. package/src/ingest/live-ingest-facet.ts +46 -0
  222. package/src/ingest/module-mode.ts +229 -0
  223. package/src/ingest/mount-canvas-ingest-root.ts +899 -0
  224. package/src/ingest/mount-coverage.ts +296 -0
  225. package/src/ingest/mount-dom-ingest-root.ts +282 -0
  226. package/src/ingest/mount-ingest-root.ts +744 -0
  227. package/src/ingest/mount-three-ingest-root.ts +366 -0
  228. package/src/ingest/resolve-canvas.ts +47 -0
  229. package/src/ingest/resolve-three.ts +123 -0
  230. package/src/ingest/served-bundle.ts +109 -0
  231. package/src/ingest/served-html-boot.ts +292 -0
  232. package/src/ingest/surface-canvas.ts +84 -0
  233. package/src/ingest/surface-dom.ts +84 -0
  234. package/src/ingest/surface-three.ts +128 -0
  235. package/src/ingest/types.ts +76 -0
  236. package/src/ingest/unmount-ingest-root.ts +224 -0
  237. package/src/navmesh/navmesh-actions.ts +27 -0
  238. package/src/navmesh/navmesh-handler.ts +237 -0
  239. package/src/navmesh/navmesh-workflow-store.ts +78 -0
  240. package/src/network/NetworkInspectorPanel.tsx +644 -0
  241. package/src/network/network-inspector-model.ts +225 -0
  242. package/src/play/play-boot-stall.ts +118 -0
  243. package/src/play/play-log-events.ts +26 -0
  244. package/src/play/play-mode.ts +2553 -0
  245. package/src/play/play-recording.ts +335 -0
  246. package/src/play/react-play-live-authoring.ts +165 -0
  247. package/src/play-bar/PlayBar.tsx +488 -0
  248. package/src/play-bar/PlayerCountPicker.tsx +100 -0
  249. package/src/profiler/FrameDebuggerPanel.tsx +458 -0
  250. package/src/profiler/PerformancePanel.tsx +1068 -0
  251. package/src/profiler/ProfilerPanel.tsx +53 -0
  252. package/src/profiler/frame-debugger-store.ts +97 -0
  253. package/src/profiler/main-thread-busy.ts +90 -0
  254. package/src/react/design-time-react-mount.ts +782 -0
  255. package/src/react/dom-authoring-adapter.ts +764 -0
  256. package/src/react/pasteboard-materialize.ts +154 -0
  257. package/src/react/react-inspector-section.tsx +2496 -0
  258. package/src/react/react-world-authoring-adapter.ts +3795 -0
  259. package/src/react/story-documents/story-args-section.ts +19 -0
  260. package/src/react/story-documents/story-documents.tsx +1380 -0
  261. package/src/react/story-paint-bounds.ts +94 -0
  262. package/src/react/ui-board-document.tsx +101 -0
  263. package/src/react/ui-board-title.ts +9 -0
  264. package/src/react/ui-component-board.ts +72 -0
  265. package/src/services/audio-pose-guard.ts +81 -0
  266. package/src/services/game-audio-unlock.ts +48 -0
  267. package/src/state-watch/StateWatchPanel.tsx +535 -0
  268. package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
  269. package/src/three/authoring/constraint-inspector-section.tsx +189 -0
  270. package/src/three/authoring/design-time-renderer.ts +189 -0
  271. package/src/three/authoring/model-asset-inspector-section.css +41 -0
  272. package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
  273. package/src/three/authoring/oid-source-persistence.ts +557 -0
  274. package/src/three/authoring/r3f-design-session.ts +1174 -0
  275. package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
  276. package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
  277. package/src/three/authoring/spatial-audio-handles.ts +215 -0
  278. package/src/three/authoring/spatial-collider-handles.ts +229 -0
  279. package/src/three/authoring/spatial-joint-handles.ts +114 -0
  280. package/src/three/authoring/spatial-light-handles.ts +178 -0
  281. package/src/three/authoring/spatial-lod-handles.ts +93 -0
  282. package/src/three/authoring/spatial-particle-handles.ts +368 -0
  283. package/src/three/authoring/three-authoring-adapter.ts +1953 -0
  284. package/src/three/authoring/three-scene-identity.ts +19 -0
  285. package/src/three/authoring/three-spatial-handles.ts +161 -0
  286. package/src/three/authoring/typed-three-inspector.ts +528 -0
  287. package/src/three/component-verbs/extract-menu.ts +71 -0
  288. package/src/three/component-verbs/fork-menu.ts +78 -0
  289. package/src/three/component-verbs/internals-menu.ts +91 -0
  290. package/src/three/story-documents/three-story-documents.tsx +607 -0
  291. package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
  292. package/src/three/three-board/board-framing.ts +412 -0
  293. package/src/three/three-board/board-layout.ts +401 -0
  294. package/src/three/three-board/board-scene.ts +901 -0
  295. package/src/three/three-board/three-component-board.ts +62 -0
  296. package/src/xstate/XStateBehaviorSection.tsx +130 -0
  297. package/src/xstate/XStateMachineInspector.tsx +566 -0
  298. package/src/xstate/character-animation-machine.fixture.ts +74 -0
  299. package/src/xstate/live-behaviors.ts +106 -0
  300. package/src/xstate/use-live-actor-state.ts +44 -0
  301. package/src/xstate/xstate-graph.ts +235 -0
  302. package/src/xstate/xstate-layout.ts +76 -0
@@ -0,0 +1,189 @@
1
+ import { instanceStampOf } from '@volter/editor-core/authoring/component-instance-root';
2
+ import { setAuthoringSelection } from '@volter/editor-core/authoring/consumer-actions';
3
+ import { useEditorStore } from '@volter/editor-core/editor-runtime';
4
+ import type { InspectorSectionProps } from '@volter/editor-core/inspector-section-registry';
5
+ import type { AuthoringAdapter, EditorNode } from '@volter/editor-project/adapter';
6
+ import {
7
+ type ConstraintMark,
8
+ type ConstraintSnapshot,
9
+ constraintsOf,
10
+ } from '@volter/threejs-runtime/adapter/constraint';
11
+ import {
12
+ Button,
13
+ EditorBanner,
14
+ FieldGroup,
15
+ FieldRow,
16
+ Text,
17
+ themeVars,
18
+ } from '@volter/editor-sdk/widgets';
19
+ import type * as THREE from 'three';
20
+
21
+ function objectFor(adapter: AuthoringAdapter, nodeId: string | null) {
22
+ return nodeId ? (adapter.hierarchy.object3D?.(nodeId) ?? null) : null;
23
+ }
24
+
25
+ /** Marks owned by this semantic component, including same-instance host nodes
26
+ * the component-aware hierarchy deliberately folds beneath its one row. */
27
+ function constraintMarksFor(object: THREE.Object3D | null): ConstraintMark[] {
28
+ if (!object) return [];
29
+ const instance = instanceStampOf(object);
30
+ const marks = new Set<ConstraintMark>();
31
+ const visit = (current: THREE.Object3D): void => {
32
+ const currentInstance = instanceStampOf(current);
33
+ if (
34
+ current !== object &&
35
+ instance !== undefined &&
36
+ currentInstance !== undefined &&
37
+ currentInstance !== instance
38
+ ) {
39
+ return;
40
+ }
41
+ for (const mark of constraintsOf(current)) marks.add(mark);
42
+ for (const child of current.children) visit(child);
43
+ };
44
+ visit(object);
45
+ return [...marks];
46
+ }
47
+
48
+ export function matches(node: EditorNode | null, adapter: AuthoringAdapter): boolean {
49
+ return constraintMarksFor(objectFor(adapter, node?.id ?? null)).length > 0;
50
+ }
51
+
52
+ const STATUS_LABEL: Record<ConstraintSnapshot['status'], string> = {
53
+ ready: 'Ready',
54
+ disabled: 'Disabled',
55
+ unresolved: 'Unresolved',
56
+ error: 'Error',
57
+ };
58
+
59
+ function typeLabel(type: ConstraintMark['config']['type']): string {
60
+ if (type === 'two-bone-ik') return 'Two Bone IK';
61
+ if (type === 'ccd-ik') return 'CCD IK';
62
+ if (type === 'aim') return 'Aim';
63
+ if (type === 'rotation') return 'Rotation';
64
+ return type;
65
+ }
66
+
67
+ export function ConstraintStackSection({ adapter, nodeId }: InspectorSectionProps) {
68
+ const store = useEditorStore();
69
+ const object = nodeId
70
+ ? (store.objectMap.get(nodeId) ?? objectFor(adapter, nodeId) ?? null)
71
+ : null;
72
+ const marks = constraintMarksFor(object).sort(
73
+ (left, right) => left.config.order - right.config.order,
74
+ );
75
+
76
+ const selectObject = (object: THREE.Object3D | null): void => {
77
+ if (!object) return;
78
+ const id = adapter.hierarchy.idForObject3D?.(object);
79
+ if (!id) return;
80
+ if (!setAuthoringSelection(adapter, [id], { intent: 'exact' })) store.select(id);
81
+ };
82
+
83
+ return (
84
+ <div style={{ display: 'grid', gap: 10 }}>
85
+ {/* biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one compact card projects one constraint snapshot. */}
86
+ {marks.map((mark, index) => {
87
+ const config = mark.config;
88
+ const snapshot = mark.getSnapshot();
89
+ const isIk = config.type === 'two-bone-ik' || config.type === 'ccd-ik';
90
+ return (
91
+ <div
92
+ key={config.id}
93
+ style={{
94
+ border: `1px solid ${themeVars.boundary.default}`,
95
+ borderRadius: 6,
96
+ overflow: 'hidden',
97
+ }}
98
+ >
99
+ <div
100
+ style={{
101
+ alignItems: 'center',
102
+ background: themeVars.surface.raised,
103
+ display: 'flex',
104
+ gap: 8,
105
+ justifyContent: 'space-between',
106
+ padding: '6px 8px',
107
+ }}
108
+ >
109
+ <Text>{`${index + 1}. ${config.label}`}</Text>
110
+ <Text tone="muted">{typeLabel(config.type)}</Text>
111
+ </div>
112
+ {snapshot.message &&
113
+ (snapshot.status === 'error' || snapshot.status === 'unresolved') ? (
114
+ <EditorBanner tone="error">{snapshot.message}</EditorBanner>
115
+ ) : null}
116
+ <FieldGroup>
117
+ <FieldRow label="Status">
118
+ <Text
119
+ tone={
120
+ snapshot.status === 'error' || snapshot.status === 'unresolved'
121
+ ? 'danger'
122
+ : snapshot.status === 'ready'
123
+ ? 'success'
124
+ : 'muted'
125
+ }
126
+ >
127
+ {STATUS_LABEL[snapshot.status]}
128
+ </Text>
129
+ </FieldRow>
130
+ <FieldRow label="Weight">
131
+ <Text tone="muted">{`${Math.round(config.weight * 100)}%`}</Text>
132
+ </FieldRow>
133
+ {isIk ? (
134
+ <FieldRow label="Chain">
135
+ <Text tone="muted">
136
+ {snapshot.chain.map((bone) => bone.name || bone.type).join(' → ') ||
137
+ 'Unresolved'}
138
+ </Text>
139
+ </FieldRow>
140
+ ) : (
141
+ <FieldRow label="Constrained">
142
+ {snapshot.constrained &&
143
+ adapter.hierarchy.idForObject3D?.(snapshot.constrained) ? (
144
+ <Button size="compact" onClick={() => selectObject(snapshot.constrained)}>
145
+ {snapshot.constrained.name || 'Select object'}
146
+ </Button>
147
+ ) : (
148
+ <Text tone="muted">{snapshot.constrained?.name || 'Unresolved'}</Text>
149
+ )}
150
+ </FieldRow>
151
+ )}
152
+ {snapshot.error !== null ? (
153
+ <FieldRow
154
+ label={snapshot.error.unit === 'degrees' ? 'Angular error' : 'Solve error'}
155
+ >
156
+ <Text tone="muted">
157
+ {snapshot.error.unit === 'degrees'
158
+ ? `${snapshot.error.value.toFixed(2)}°`
159
+ : `${snapshot.error.value.toFixed(3)} m`}
160
+ </Text>
161
+ </FieldRow>
162
+ ) : null}
163
+ <FieldRow label="Target">
164
+ {snapshot.target && adapter.hierarchy.idForObject3D?.(snapshot.target) ? (
165
+ <Button size="compact" onClick={() => selectObject(snapshot.target)}>
166
+ {snapshot.target.name || 'Select target'}
167
+ </Button>
168
+ ) : (
169
+ <Text tone="muted">{snapshot.target?.name || 'Dynamic target'}</Text>
170
+ )}
171
+ </FieldRow>
172
+ {isIk ? (
173
+ <FieldRow label="Pole">
174
+ {snapshot.pole && adapter.hierarchy.idForObject3D?.(snapshot.pole) ? (
175
+ <Button size="compact" onClick={() => selectObject(snapshot.pole)}>
176
+ {snapshot.pole.name || 'Select pole'}
177
+ </Button>
178
+ ) : (
179
+ <Text tone="muted">{snapshot.pole?.name || 'None'}</Text>
180
+ )}
181
+ </FieldRow>
182
+ ) : null}
183
+ </FieldGroup>
184
+ </div>
185
+ );
186
+ })}
187
+ </div>
188
+ );
189
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * A design root borrows the viewport's GPU for offscreen work (PMREM, cube
3
+ * cameras, render textures). The editor owns the canvas, loop and context.
4
+ * Fiber configuration is local to the borrower and applied only while it
5
+ * submits work, so overlapping HMR mounts cannot restore each other's state.
6
+ */
7
+ import {
8
+ Color,
9
+ type ColorRepresentation,
10
+ Vector4,
11
+ type WebGLRenderer,
12
+ type WebGLRenderTarget,
13
+ } from 'three';
14
+
15
+ export function createDesignTimeRenderer(
16
+ canvas: HTMLCanvasElement,
17
+ renderer: WebGLRenderer,
18
+ ): WebGLRenderer {
19
+ const noop = (): void => {};
20
+ const owned = new Set<PropertyKey>([
21
+ 'dispose',
22
+ 'forceContextLoss',
23
+ 'forceContextRestore',
24
+ 'setAnimationLoop',
25
+ 'setSize',
26
+ 'setPixelRatio',
27
+ 'setDrawingBufferSize',
28
+ ]);
29
+ const writes = new Map<PropertyKey, unknown>();
30
+ const shadowMap = { ...renderer.shadowMap };
31
+ let xrEnabled = renderer.xr.enabled;
32
+ const clearColor = renderer.getClearColor(new Color());
33
+ let clearAlpha = renderer.getClearAlpha();
34
+ let renderTarget: WebGLRenderTarget | null = null;
35
+ let cubeFace = 0;
36
+ let mipLevel = 0;
37
+ const viewport = renderer.getViewport(new Vector4());
38
+ const scissor = renderer.getScissor(new Vector4());
39
+ let scissorTest = renderer.getScissorTest();
40
+ let viewportAfterTarget = false;
41
+ let scissorAfterTarget = false;
42
+ let scissorTestAfterTarget = false;
43
+ const copyRect = (out: Vector4, x: Vector4 | number, y?: number, w?: number, h?: number) => {
44
+ if (typeof x === 'number') out.set(x, y!, w!, h!);
45
+ else out.copy(x);
46
+ };
47
+ const xr = new Proxy(renderer.xr, {
48
+ get(target, key) {
49
+ if (key === 'enabled') return xrEnabled;
50
+ if (key === 'setAnimationLoop' || key === 'addEventListener' || key === 'removeEventListener')
51
+ return noop;
52
+ const value = Reflect.get(target, key, target);
53
+ return typeof value === 'function' ? value.bind(target) : value;
54
+ },
55
+ set(target, key, value) {
56
+ if (key === 'enabled') {
57
+ xrEnabled = value;
58
+ return true;
59
+ }
60
+ return Reflect.set(target, key, value, target);
61
+ },
62
+ });
63
+ const renderLists = new Proxy(renderer.renderLists, {
64
+ get(target, key) {
65
+ if (key === 'dispose') return noop;
66
+ const value = Reflect.get(target, key, target);
67
+ return typeof value === 'function' ? value.bind(target) : value;
68
+ },
69
+ });
70
+
71
+ function submit(method: (...args: unknown[]) => unknown, args: unknown[]): unknown {
72
+ const previous = new Map<PropertyKey, unknown>();
73
+ const previousShadows = { ...renderer.shadowMap };
74
+ const previousXr = renderer.xr.enabled;
75
+ const previousColor = renderer.getClearColor(new Color());
76
+ const previousAlpha = renderer.getClearAlpha();
77
+ const previousTarget = renderer.getRenderTarget();
78
+ const previousFace = renderer.getActiveCubeFace();
79
+ const previousMip = renderer.getActiveMipmapLevel();
80
+ const previousViewport = renderer.getViewport(new Vector4());
81
+ const previousScissor = renderer.getScissor(new Vector4());
82
+ const previousScissorTest = renderer.getScissorTest();
83
+ try {
84
+ for (const [key, value] of writes) {
85
+ previous.set(key, Reflect.get(renderer, key));
86
+ Reflect.set(renderer, key, value);
87
+ }
88
+ Object.assign(renderer.shadowMap, shadowMap);
89
+ renderer.xr.enabled = xrEnabled;
90
+ renderer.setClearColor(clearColor, clearAlpha);
91
+ renderer.setViewport(viewport);
92
+ renderer.setScissor(scissor);
93
+ renderer.setScissorTest(scissorTest);
94
+ renderer.setRenderTarget(renderTarget, cubeFace, mipLevel);
95
+ if (viewportAfterTarget) renderer.setViewport(viewport);
96
+ if (scissorAfterTarget) renderer.setScissor(scissor);
97
+ if (scissorTestAfterTarget) renderer.setScissorTest(scissorTest);
98
+ return method.apply(renderer, args);
99
+ } finally {
100
+ for (const [key, value] of previous) Reflect.set(renderer, key, value);
101
+ Object.assign(renderer.shadowMap, previousShadows);
102
+ renderer.xr.enabled = previousXr;
103
+ renderer.setClearColor(previousColor, previousAlpha);
104
+ renderer.setViewport(previousViewport);
105
+ renderer.setScissor(previousScissor);
106
+ renderer.setScissorTest(previousScissorTest);
107
+ renderer.setRenderTarget(previousTarget, previousFace, previousMip);
108
+ }
109
+ }
110
+
111
+ const methods = new Map<PropertyKey, unknown>();
112
+ return new Proxy(renderer, {
113
+ get(target, key) {
114
+ if (key === 'domElement') return canvas;
115
+ if (key === 'shadowMap') return shadowMap;
116
+ if (key === 'xr') return xr;
117
+ if (key === 'renderLists') return renderLists;
118
+ if (owned.has(key)) return noop;
119
+ if (key === 'getRenderTarget') return () => renderTarget;
120
+ if (key === 'getActiveCubeFace') return () => cubeFace;
121
+ if (key === 'getActiveMipmapLevel') return () => mipLevel;
122
+ if (key === 'setRenderTarget')
123
+ return (value: WebGLRenderTarget | null, face = 0, mip = 0) => {
124
+ renderTarget = value;
125
+ cubeFace = face;
126
+ mipLevel = mip;
127
+ viewportAfterTarget = scissorAfterTarget = scissorTestAfterTarget = false;
128
+ };
129
+ if (key === 'getViewport') return (out: Vector4) => out.copy(viewport);
130
+ if (key === 'getCurrentViewport')
131
+ return (out: Vector4) => {
132
+ if (renderTarget && !viewportAfterTarget) return out.copy(renderTarget.viewport);
133
+ out.copy(viewport).multiplyScalar(renderer.getPixelRatio());
134
+ return viewportAfterTarget ? out.round() : out.floor();
135
+ };
136
+ if (key === 'getScissor') return (out: Vector4) => out.copy(scissor);
137
+ if (key === 'getScissorTest') return () => scissorTest;
138
+ if (key === 'setViewport')
139
+ return (x: Vector4 | number, y?: number, w?: number, h?: number) => {
140
+ copyRect(viewport, x, y, w, h);
141
+ viewportAfterTarget = true;
142
+ };
143
+ if (key === 'setScissor')
144
+ return (x: Vector4 | number, y?: number, w?: number, h?: number) => {
145
+ copyRect(scissor, x, y, w, h);
146
+ scissorAfterTarget = true;
147
+ };
148
+ if (key === 'setScissorTest')
149
+ return (value: boolean) => {
150
+ scissorTest = value;
151
+ scissorTestAfterTarget = true;
152
+ };
153
+ if (key === 'getClearColor') return (out: Color) => out.copy(clearColor);
154
+ if (key === 'getClearAlpha') return () => clearAlpha;
155
+ if (key === 'setClearColor')
156
+ return (color: ColorRepresentation, alpha = clearAlpha) => {
157
+ clearColor.set(color);
158
+ clearAlpha = alpha;
159
+ };
160
+ if (key === 'setClearAlpha')
161
+ return (alpha: number) => {
162
+ clearAlpha = alpha;
163
+ };
164
+ if (writes.has(key)) return writes.get(key);
165
+ const value = Reflect.get(target, key, target);
166
+ if (typeof value !== 'function') return value;
167
+ if (!methods.has(key)) {
168
+ const gpu = /^(render|compile|clear|copy|init|readRenderTarget)/.test(String(key));
169
+ methods.set(
170
+ key,
171
+ gpu
172
+ ? (...args: unknown[]) => {
173
+ // Fiber's final screen draw is the viewport's job. Offscreen draws
174
+ // and clears must execute: their textures are inputs to that image.
175
+ if ((key === 'render' || String(key).startsWith('clear')) && !renderTarget)
176
+ return undefined;
177
+ return submit(value, args);
178
+ }
179
+ : value.bind(target),
180
+ );
181
+ }
182
+ return methods.get(key);
183
+ },
184
+ set(_target, key, value) {
185
+ if (key !== 'domElement') writes.set(key, value);
186
+ return true;
187
+ },
188
+ });
189
+ }
@@ -0,0 +1,41 @@
1
+ /* A section's own verb (Open Editor, Open Source). Section headers carry no
2
+ action slot — every projection heads a section the same way — so a block's
3
+ verb sits at the top of its BODY, where it reads identically in the card
4
+ and the column. */
5
+ .vgai-model-inspector-actions {
6
+ display: flex;
7
+ justify-content: flex-end;
8
+ padding: var(--vgai-space-2) var(--vgai-space-3) 0;
9
+ }
10
+
11
+ .vgai-model-inspector-row > div {
12
+ display: grid;
13
+ min-width: 0;
14
+ }
15
+
16
+ .vgai-model-inspector-list {
17
+ display: grid;
18
+ max-height: 360px;
19
+ gap: var(--vgai-space-2);
20
+ overflow: auto;
21
+ }
22
+
23
+ .vgai-model-inspector-row {
24
+ display: grid;
25
+ grid-template-columns: minmax(0, 1fr) auto;
26
+ align-items: center;
27
+ gap: var(--vgai-space-3);
28
+ padding: var(--vgai-space-3);
29
+ }
30
+
31
+ .vgai-model-inspector-row:has(.vgai-model-material-swatch) {
32
+ grid-template-columns: auto minmax(0, 1fr) auto;
33
+ }
34
+
35
+ .vgai-model-material-swatch {
36
+ width: var(--vgai-control-default-height);
37
+ height: var(--vgai-control-default-height);
38
+ border: var(--vgai-stroke-resting) solid var(--vgai-boundary-strong);
39
+ border-radius: var(--vgai-radius-sm);
40
+ background-color: var(--vgai-surface-inset);
41
+ }