@volter/editor-game 0.5.66 → 0.5.68

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 (239) hide show
  1. package/contributions/audio-unlock.service.ts +2 -2
  2. package/contributions/autoplay.service.ts +1 -1
  3. package/contributions/bridge.command.ts +3 -3
  4. package/contributions/build-player.document.tsx +95 -0
  5. package/contributions/canvas/component-board.service.ts +16 -0
  6. package/contributions/canvas/design-time-mount.service.ts +45 -0
  7. package/contributions/canvas-story-capture.service.ts +12 -0
  8. package/contributions/edit-mode-audio.service.ts +2 -2
  9. package/contributions/edit-mode-networking.service.ts +1 -1
  10. package/contributions/gameplay.command.ts +4 -4
  11. package/contributions/generation.service.ts +1 -1
  12. package/contributions/godot.style.ts +26 -6
  13. package/contributions/godot.view.ts +6 -0
  14. package/contributions/ingest.service.ts +2 -2
  15. package/contributions/instances.command.ts +1 -1
  16. package/contributions/navmesh.menu.ts +1 -1
  17. package/contributions/network-observer.service.ts +14 -0
  18. package/contributions/play.command.ts +10 -4
  19. package/contributions/react/component-board.service.ts +1 -1
  20. package/contributions/react/design-time-mount.service.ts +1 -1
  21. package/contributions/scene-document.service.ts +3 -3
  22. package/contributions/state-watch.menu.ts +1 -1
  23. package/contributions/state-watch.utility.tsx +1 -1
  24. package/contributions/team-playtest.service.ts +4 -4
  25. package/contributions/three/component-board.service.ts +1 -1
  26. package/contributions/three/component-verbs.command.ts +6 -6
  27. package/contributions/three/three-authoring.service.ts +8 -5
  28. package/contributions/three-story-capture.service.ts +12 -0
  29. package/contributions/unity.style.ts +17 -7
  30. package/contributions/unity.view.ts +6 -0
  31. package/contributions/unreal.style.ts +10 -2
  32. package/contributions/unreal.view.ts +5 -0
  33. package/package.json +21 -10
  34. package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
  35. package/src/asset-budget/asset-budget-model.ts +1 -1
  36. package/src/audio/AudioDebuggerPanel.tsx +1 -1
  37. package/src/bridge/dispatch.ts +14 -14
  38. package/src/bridge/live-frames.ts +1 -1
  39. package/src/bridge/screenshot.ts +3 -3
  40. package/src/build/BuildProfilesPanel.tsx +14 -3
  41. package/src/build/build-session.ts +35 -0
  42. package/src/canvas/canvas-board/CanvasBoardDocument.tsx +749 -0
  43. package/src/canvas/canvas-board/canvas-board-model.ts +407 -0
  44. package/src/canvas/canvas-board/canvas-component-board.ts +56 -0
  45. package/src/canvas/canvas-design-mount.ts +524 -0
  46. package/src/canvas/design-time-canvas-mount.ts +79 -0
  47. package/src/coverage/live-authoring-surface.ts +5 -5
  48. package/src/coverage/live-project-verbs.ts +1 -1
  49. package/src/coverage/native-system-coverage.ts +5 -5
  50. package/src/coverage/root-coverage.ts +1 -1
  51. package/src/coverage/session-coverage.ts +3 -3
  52. package/src/design-system-stories/ApplicationChrome.stories.tsx +5 -5
  53. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +7 -7
  54. package/src/edit-mode/edit-mode-audio.ts +4 -4
  55. package/src/edit-mode/edit-mode-networking.ts +2 -2
  56. package/src/game-document/GameCaptureFrameButton.tsx +1 -1
  57. package/src/game-document/GameDocument.tsx +2 -2
  58. package/src/game-document/GamePanel.tsx +4 -4
  59. package/src/game-document/InstanceInspectorPicker.tsx +2 -2
  60. package/src/game-document/crowd-debug.ts +1 -1
  61. package/src/game-document/device-preview.ts +10 -11
  62. package/src/game-document/physics-debug.ts +1 -1
  63. package/src/generation/GenerationActivity.tsx +3 -3
  64. package/src/generation/generation-documents.tsx +5 -5
  65. package/src/generation/generation-jobs.ts +1 -1
  66. package/src/host/adapter-runtime-bindings.ts +96 -5
  67. package/src/host/authoring/babylon-authoring-adapter.ts +6 -6
  68. package/src/host/authoring/gesture-persist.ts +1 -1
  69. package/src/host/authoring/ingest-data-writer.ts +1 -1
  70. package/src/host/authoring/ingest-source-persistence.ts +4 -4
  71. package/src/host/authoring/mounted-authoring.ts +4 -4
  72. package/src/host/authoring/phaser-live-authoring-adapter.ts +4 -4
  73. package/src/host/authoring/pixi-authoring-adapter.ts +178 -31
  74. package/src/host/authoring/pixi-creatable-kinds.ts +62 -0
  75. package/src/host/authoring/pixi-creation-site-write-target.ts +2 -2
  76. package/src/host/authoring/pixi-live-write-target.ts +4 -4
  77. package/src/host/authoring/pixi-source-identity.ts +3 -3
  78. package/src/host/authoring/pixi-source-write-target.ts +1709 -0
  79. package/src/host/authoring/pixi-still-presentation.ts +1 -1
  80. package/src/host/authoring/pixi-structure-history.ts +2 -2
  81. package/src/host/authoring/pixi-transform-channels.ts +16 -14
  82. package/src/host/authoring/source-persistence-backend.ts +3 -3
  83. package/src/host/authoring/struct-write-pipe.ts +1 -1
  84. package/src/host/binding-resolver.ts +8 -9
  85. package/src/host/browser-transpile.ts +1 -1
  86. package/src/host/canvas-entry-runtime.ts +58 -47
  87. package/src/host/canvas-preview-frames.ts +482 -0
  88. package/src/host/components/CameraAuthoringOverlay.tsx +1 -1
  89. package/src/host/components/HeaderTelemetry.tsx +4 -4
  90. package/src/host/components/PixiIsolationSceneContent.tsx +11 -11
  91. package/src/host/components/ThreeIsolationSceneContent.tsx +3 -3
  92. package/src/host/components/frame-debugger-model.ts +2 -2
  93. package/src/host/components/header-telemetry-model.ts +2 -2
  94. package/src/host/components/scene-document.tsx +14 -14
  95. package/src/host/components/utility-view-state.ts +1 -1
  96. package/src/host/components/world-root-stage-binding.tsx +12 -12
  97. package/src/host/components/world-root-stage.ts +70 -49
  98. package/src/host/coverage/system-adapter-coverage.ts +3 -4
  99. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +9 -11
  100. package/src/host/document-preview-three.ts +1 -1
  101. package/src/host/entry-adjudication.ts +6 -6
  102. package/src/host/game-css-scope-transform.ts +4 -0
  103. package/src/host/game-realm-page.ts +1 -1
  104. package/src/host/gameplay-export.ts +25 -14
  105. package/src/host/gameplay-recording.ts +5 -5
  106. package/src/host/gated-globals.ts +2 -2
  107. package/src/host/history/json-history-resource.ts +1 -1
  108. package/src/host/projection/pixi.ts +24 -2
  109. package/src/host/r3f-entry-runtime.ts +65 -34
  110. package/src/host/react-mount-runtime.ts +7 -48
  111. package/src/host/realm-services.ts +1 -1
  112. package/src/host/roots/canvas-root.tsx +373 -0
  113. package/src/host/roots/r3f-root.tsx +473 -0
  114. package/src/host/roots/react-root.ts +9 -43
  115. package/src/host/served-bundle-runtime-modules.ts +3 -19
  116. package/src/host/server-log-bridge.ts +2 -2
  117. package/src/host/stories/mounted-story-viewport-source.ts +1 -1
  118. package/src/host/stories/pixi-story-model.ts +30 -0
  119. package/src/host/stories/story-media-captures.ts +46 -0
  120. package/src/host/stories/story-media-presence.ts +3 -3
  121. package/src/host/stories/story-pixi-preview.ts +408 -0
  122. package/src/host/stories/story-three-preview.ts +806 -0
  123. package/src/host/stories/three-story-captures.ts +35 -0
  124. package/src/host/story-three-preview-runtime.ts +56 -0
  125. package/src/host/use-active-performance-source.ts +2 -2
  126. package/src/host/viewport-pose-memory.ts +1 -1
  127. package/src/host/viewport-root-presentation.ts +6 -5
  128. package/src/ingest/active-ingest.ts +1 -1
  129. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
  130. package/src/ingest/authoring/ingest-root-adapter.ts +8 -8
  131. package/src/ingest/deferred-ingest-play.ts +6 -6
  132. package/src/ingest/discovery-public-ingest.ts +2 -2
  133. package/src/ingest/ingest-boot-viewport.ts +2 -2
  134. package/src/ingest/ingest-canvas-scene-document.tsx +9 -9
  135. package/src/ingest/ingest-canvas-scene.ts +3 -3
  136. package/src/ingest/ingest-evidence-hook.ts +1 -1
  137. package/src/ingest/ingest-frame-snapshot.ts +1 -1
  138. package/src/ingest/ingest-play-control.ts +1 -1
  139. package/src/ingest/ingest-render-debug.ts +10 -10
  140. package/src/ingest/ingest-siblings.ts +13 -25
  141. package/src/ingest/module-mode.ts +14 -14
  142. package/src/ingest/mount-canvas-ingest-root.ts +21 -21
  143. package/src/ingest/mount-coverage.ts +2 -2
  144. package/src/ingest/mount-dom-ingest-root.ts +11 -11
  145. package/src/ingest/mount-ingest-root.ts +8 -8
  146. package/src/ingest/mount-three-ingest-root.ts +8 -8
  147. package/src/ingest/resolve-canvas.ts +1 -1
  148. package/src/ingest/served-html-boot.ts +1 -1
  149. package/src/ingest/surface-canvas.ts +1 -1
  150. package/src/ingest/unmount-ingest-root.ts +5 -5
  151. package/src/navmesh/navmesh-handler.ts +24 -16
  152. package/src/network/NetworkInspectorPanel.tsx +939 -37
  153. package/src/network/network-inspector-model.ts +20 -2
  154. package/src/play/play-log-events.ts +1 -1
  155. package/src/play/play-mode.ts +76 -133
  156. package/src/play/play-recording.ts +1 -1
  157. package/src/play/react-play-live-authoring.ts +3 -3
  158. package/src/play/run-selection.ts +93 -0
  159. package/src/play-bar/PlayBar.tsx +20 -39
  160. package/src/profiler/FrameDebuggerPanel.tsx +1 -1
  161. package/src/profiler/PerformancePanel.tsx +3 -3
  162. package/src/react/design-time-react-mount.ts +23 -65
  163. package/src/react/dom-authoring-adapter.ts +9 -9
  164. package/src/react/react-inspector-section.tsx +5 -5
  165. package/src/react/react-world-authoring-adapter.ts +11 -11
  166. package/src/react/story-documents/story-documents.tsx +9 -9
  167. package/src/react/ui-board-document.tsx +7 -7
  168. package/src/react/ui-component-board.ts +2 -2
  169. package/src/runtime/adapter/audio-meter.ts +21 -0
  170. package/src/runtime/adapter/first-party-audio-system.ts +230 -0
  171. package/src/runtime/adapter/ingest/contract-debug-adapter.ts +114 -0
  172. package/src/runtime/adapter/ingest/contract-system-adapters.ts +256 -0
  173. package/src/runtime/adapter/ingest/merge-debug-adapters.ts +197 -0
  174. package/src/runtime/adapter/ingest/observation-debug-adapter.ts +162 -0
  175. package/src/runtime/adapter/ingest/upstream-pin.ts +51 -0
  176. package/src/runtime/adapter/native-debug-module.ts +498 -0
  177. package/src/runtime/audio/bus-mixer.ts +161 -0
  178. package/src/runtime/audio/pose-guard.ts +80 -0
  179. package/src/runtime/core/frame-pacing.ts +126 -0
  180. package/src/runtime/core/game-loop.ts +225 -0
  181. package/src/runtime/core/game-scoped-slot.ts +28 -0
  182. package/src/runtime/core/seeded-random.ts +162 -0
  183. package/src/runtime/core/sim-clock.ts +391 -0
  184. package/src/runtime/core/system-runner.ts +269 -0
  185. package/src/runtime/core/types.ts +104 -0
  186. package/src/runtime/create-runtime.ts +1128 -0
  187. package/src/runtime/debug-bridge.ts +570 -0
  188. package/src/runtime/debug-registry.ts +899 -0
  189. package/src/runtime/dev/chrome-trace.ts +153 -0
  190. package/src/runtime/dev/instruments.ts +403 -0
  191. package/src/runtime/dev/logger.ts +119 -0
  192. package/src/runtime/dev/performance-profiler.ts +367 -0
  193. package/src/runtime/dev/register-render-vitals.ts +276 -0
  194. package/src/runtime/dev/render-census.ts +354 -0
  195. package/src/runtime/dev/render-debug-adapter.ts +218 -0
  196. package/src/runtime/dev/render-memory.ts +226 -0
  197. package/src/runtime/dev/render-vitals.ts +338 -0
  198. package/src/runtime/dev/static-batch-advisor.ts +188 -0
  199. package/src/runtime/dev/webgl-frame-capture.ts +366 -0
  200. package/src/runtime/dev/webgl-gpu-timer.ts +53 -0
  201. package/src/runtime/dev-build.ts +47 -0
  202. package/src/runtime/game.ts +1636 -0
  203. package/src/runtime/gameplay-rng-trap.ts +135 -0
  204. package/src/runtime/host-context.ts +64 -0
  205. package/src/runtime/input-router.ts +182 -0
  206. package/src/runtime/mount-manifest.ts +480 -0
  207. package/src/runtime/pixi/authoring.ts +706 -0
  208. package/src/runtime/pixi/ingest.ts +116 -0
  209. package/src/runtime/pixi/physics-registry.ts +49 -0
  210. package/src/runtime/pixi/render-pass-bracket.ts +117 -0
  211. package/src/runtime/pixi/scene-capture.ts +179 -0
  212. package/src/runtime/pixi/system-adapters.ts +69 -0
  213. package/src/runtime/playtest.ts +22 -0
  214. package/src/runtime/presentation.ts +141 -0
  215. package/src/runtime/render-control.ts +642 -0
  216. package/src/runtime/render-seed.ts +77 -0
  217. package/src/runtime/run-ticks-settled.ts +73 -0
  218. package/src/runtime/setup/setup-audio.ts +72 -0
  219. package/src/services/audio-pose-guard.ts +2 -2
  220. package/src/services/game-audio.ts +152 -0
  221. package/src/services/game-network.ts +767 -0
  222. package/src/services/game-physics.ts +334 -0
  223. package/src/state-watch/StateWatchPanel.tsx +1 -1
  224. package/src/three/authoring/camera-runtime-inspector-section.tsx +3 -2
  225. package/src/three/authoring/constraint-inspector-section.tsx +6 -5
  226. package/src/three/authoring/model-asset-inspector-section.tsx +7 -6
  227. package/src/three/authoring/oid-source-persistence.ts +7 -7
  228. package/src/three/authoring/r3f-design-session.ts +58 -54
  229. package/src/three/authoring/r3f-source-authoring-adapter.ts +95 -91
  230. package/src/three/authoring/reflection-probe-inspector-section.tsx +3 -2
  231. package/src/three/authoring/three-authoring-adapter.ts +29 -29
  232. package/src/three/component-verbs/extract-menu.ts +7 -6
  233. package/src/three/component-verbs/fork-menu.ts +7 -6
  234. package/src/three/component-verbs/internals-menu.ts +2 -2
  235. package/src/three/story-documents/three-story-documents.tsx +13 -13
  236. package/src/three/three-board/ThreeBoardDocument.tsx +13 -12
  237. package/src/three/three-board/board-scene.ts +6 -6
  238. package/src/three/three-board/three-component-board.ts +2 -2
  239. package/src/services/game-audio-unlock.ts +0 -48
@@ -34,7 +34,7 @@ import {
34
34
  AuthoringAdapter2D,
35
35
  type CanvasIdentity,
36
36
  type Transform2DValue,
37
- } from '@volter/game-runtime/pixi/authoring';
37
+ } from '../../runtime/pixi/authoring';
38
38
  import type {
39
39
  AssetDropContext,
40
40
  AssetDropProvider,
@@ -70,9 +70,9 @@ import type {
70
70
  import type { Container, Graphics, Matrix, PointData, Sprite, Text, Texture } from 'pixi.js';
71
71
  import * as shellPixi from 'pixi.js';
72
72
  import type { CanvasPixiNamespace } from '../canvas-entry-runtime';
73
- import { componentStatesProvider } from '@volter/editor-core/component-states-registry';
73
+ import { componentStatesProvider } from '@volter/editor-sdk/kit/component-states-registry';
74
74
  import { editorConsole } from '@volter/editor-sdk/kit/editor-console';
75
- import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
75
+ import type { EditorShellStore } from '@volter/editor-threejs/kit/editor-shell-store';
76
76
  import type { JournalSubject } from '../history/json-history-resource';
77
77
  import { PixiProjector } from '../projection/pixi';
78
78
  import { creationSiteRelated } from './creation-site-related';
@@ -82,8 +82,10 @@ import {
82
82
  readContainerAuthoringLabel,
83
83
  } from './pixi-source-identity';
84
84
  import { CanvasStructureHistory } from './pixi-structure-history';
85
+ import { PIXI_CREATABLE_KINDS } from './pixi-creatable-kinds';
85
86
  import { fromNeutralTransform, toNeutralTransform } from './pixi-transform-channels';
86
87
  import { resolvesLiveOnly, runWritePipe } from '@volter/editor-sdk/kit/write-pipe';
88
+ import { editorHost } from '@volter/editor-sdk/host';
87
89
 
88
90
  /**
89
91
  * What a write target is handed at construction — the live view it edits
@@ -302,12 +304,19 @@ export interface PixiAuthoringOptions {
302
304
  * `Texture.WHITE` and a size; a graphics gets a drawn rect): a node the author
303
305
  * cannot see is indistinguishable from a create that silently failed.
304
306
  */
305
- const CANVAS_CREATABLE_KINDS: readonly { kind: string; label: string }[] = [
306
- { kind: 'container', label: 'Container' },
307
- { kind: 'sprite', label: 'Sprite' },
308
- { kind: 'text', label: 'Text' },
309
- { kind: 'graphics', label: 'Graphics' },
310
- ];
307
+ /** The project-local section holding each canvas world's locked and grouped label paths. */
308
+ const EDIT_LOCKS_SECTION = 'canvasEditLocks';
309
+ type EditLocks = Record<string, { readonly locked: readonly string[]; readonly grouped: readonly string[] }>;
310
+
311
+ function readEditLocks(): EditLocks {
312
+ return editorHost().projectLocalState.read<EditLocks>(EDIT_LOCKS_SECTION) ?? {};
313
+ }
314
+
315
+ function writeEditLocks(world: string, locks: EditLocks[string]): void {
316
+ editorHost().projectLocalState.write(EDIT_LOCKS_SECTION, { ...readEditLocks(), [world]: locks });
317
+ }
318
+
319
+ const CANVAS_CREATABLE_KINDS = PIXI_CREATABLE_KINDS;
311
320
 
312
321
  /** What a canvas world can take from the asset browser: an image becomes a
313
322
  * Sprite. A model or an audio file has no display object to become here. */
@@ -347,7 +356,7 @@ const ORIGIN_UNWRITABLE_REASON =
347
356
  * from the one this prebuilt shell bundles — so a real `Sprite` from the world
348
357
  * fails `instanceof Sprite` here and every selected sprite would silently read
349
358
  * as a pivot node. The engine's own 2D authoring walk already refuses class
350
- * identity for the same reason (`@volter/game-runtime/pixi/authoring`'s `kindOf` keys on the
359
+ * identity for the same reason (`@volter/editor-game/runtime/pixi/authoring`'s `kindOf` keys on the
351
360
  * constructor NAME); asking for the two fields the anchor question is actually
352
361
  * about is the same move without depending on a name a minifier may mangle.
353
362
  *
@@ -417,6 +426,19 @@ function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container
417
426
  return new pixi.Text({ text: 'Text', style: { fill: 0xffffff, fontSize: 24 } });
418
427
  case 'graphics':
419
428
  return new pixi.Graphics().rect(0, 0, 100, 100).fill(0xffffff);
429
+ case 'animatedSprite': {
430
+ const animated = new pixi.AnimatedSprite([pixi.Texture.WHITE]);
431
+ animated.setSize(64, 64);
432
+ return animated;
433
+ }
434
+ case 'tilingSprite':
435
+ return new pixi.TilingSprite({ texture: pixi.Texture.WHITE, width: 100, height: 100 });
436
+ case 'nineSliceSprite':
437
+ return new pixi.NineSliceSprite({ texture: pixi.Texture.WHITE, width: 100, height: 100 });
438
+ case 'bitmapText':
439
+ return new pixi.BitmapText({ text: 'BitmapText', style: { fill: 0xffffff, fontSize: 24 } });
440
+ case 'htmlText':
441
+ return new pixi.HTMLText({ text: 'HTMLText', style: { fill: 0xffffff, fontSize: 24 } });
420
442
  default:
421
443
  return null;
422
444
  }
@@ -490,6 +512,35 @@ interface CanvasBoxEditSession {
490
512
  readonly id: string;
491
513
  readonly transform: Transform;
492
514
  readonly rect: { x: number; y: number; width: number; height: number };
515
+ /** The parent's world matrix's linear part (Pixi's a, b, c, d), when the node has a parent. */
516
+ readonly parentLinear?: { a: number; b: number; c: number; d: number };
517
+ }
518
+
519
+ /**
520
+ * The local turn, in degrees, that turns the node's own x axis by `degrees` on screen. Godot measures
521
+ * a rotate drag in the parent's space; a turn added straight to the local rotation goes backwards
522
+ * under a mirrored parent and by the wrong amount under an unevenly scaled one.
523
+ */
524
+ function localTurn(session: CanvasBoxEditSession, degrees: number): number {
525
+ const m = session.parentLinear;
526
+ if (!m) return degrees;
527
+ const det = m.a * m.d - m.b * m.c;
528
+ if (Math.abs(det) < 1e-9) return degrees;
529
+ const [, , z, w] = session.transform.rotation;
530
+ const r = 2 * Math.atan2(z, w);
531
+ // The node's x axis on screen, turned by the drag, taken back into the parent's space.
532
+ const vx = m.a * Math.cos(r) + m.c * Math.sin(r);
533
+ const vy = m.b * Math.cos(r) + m.d * Math.sin(r);
534
+ const t = (degrees * Math.PI) / 180;
535
+ const wx = vx * Math.cos(t) - vy * Math.sin(t);
536
+ const wy = vx * Math.sin(t) + vy * Math.cos(t);
537
+ const ux = (m.d * wx - m.c * wy) / det;
538
+ const uy = (-m.b * wx + m.a * wy) / det;
539
+ // A long drag passes half a turn: keep the answer beside the turn's own direction in that space.
540
+ const base = det < 0 ? -t : t;
541
+ const raw = Math.atan2(uy, ux) - r - base;
542
+ const wrapped = Math.atan2(Math.sin(raw), Math.cos(raw));
543
+ return ((base + wrapped) * 180) / Math.PI;
493
544
  }
494
545
 
495
546
  function boxPatchChannels(patch: Record<string, number>): TransformChannel[] {
@@ -538,7 +589,7 @@ function transformForBoxPatch(
538
589
  scale: [...session.transform.scale],
539
590
  };
540
591
  const rotate = patch['rotate'];
541
- if (rotate !== undefined) next.rotation = rotateTransformZ(session.transform, rotate);
592
+ if (rotate !== undefined) next.rotation = rotateTransformZ(session.transform, localTurn(session, rotate));
542
593
  const width = patch['width'];
543
594
  if (width !== undefined && session.rect.width > 0) {
544
595
  next.scale[0] = session.transform.scale[0] * Math.max(0.0001, width / session.rect.width);
@@ -669,7 +720,17 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
669
720
  | undefined;
670
721
  private listeners = new Set<() => void>();
671
722
  private boxEditSessions = new Map<string, CanvasBoxEditSession>();
672
- private lockedIds = new Set<string>();
723
+ /**
724
+ * Lock (a click passes through the node) and Godot's Group (a click inside a grouped node selects
725
+ * the group), held by each node's label path in this world so they survive the remount a source
726
+ * write causes and the next session. Kept per checkout, as Unity keeps scene pickability per
727
+ * user; Godot writes them into the scene file instead.
728
+ */
729
+ private lockedPaths = new Set<string>();
730
+ private groupedPaths = new Set<string>();
731
+ private readonly editLockWorld: string;
732
+ /** Each node's label path, cleared whenever the tree is re-projected. */
733
+ private pathKeys = new Map<string, string>();
673
734
 
674
735
  constructor(
675
736
  private readonly root: Container,
@@ -721,13 +782,18 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
721
782
  const surface = opts.surface;
722
783
  if (surface) {
723
784
  this.pickable = {
724
- pick: (clientX, clientY) =>
725
- this.projector.pick(clientX, clientY, (id) => this.isPickLocked(id)),
726
- candidates: (clientX, clientY) =>
727
- this.projector.candidates(clientX, clientY, (id) => this.isPickLocked(id)),
785
+ pick: (clientX, clientY) => {
786
+ const hit = this.projector.pick(clientX, clientY, (id) => this.isPickLocked(id));
787
+ return hit ? this.groupOf(hit) : hit;
788
+ },
789
+ candidates: (clientX, clientY, options) =>
790
+ this.projector.candidates(clientX, clientY, (id) =>
791
+ options?.includeLocked ? false : this.isPickLocked(id),
792
+ ),
728
793
  };
729
794
  this.rects = {
730
795
  rect: (id) => this.projector.rect(id),
796
+ frame: (id) => this.projector.frame(id),
731
797
  contextRects: (id) => this.projector.contextRects(id),
732
798
  };
733
799
  }
@@ -744,7 +810,18 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
744
810
  this.target.provenance.source === 'source-code' && this.target.persistence !== undefined,
745
811
  };
746
812
  this.target.bind({ a2d: this.a2d, store, journal: opts.journal, notify: () => this.notify() });
747
- this.structure = this.target.structure ?? this.liveStructure;
813
+ this.editLockWorld = opts.journal.id;
814
+ const saved = readEditLocks()[this.editLockWorld];
815
+ this.lockedPaths = new Set(saved?.locked ?? []);
816
+ this.groupedPaths = new Set(saved?.grouped ?? []);
817
+ const structure = this.target.structure ?? this.liveStructure;
818
+ // A placed creation reaches the structure in the new node's PARENT's own space: the target
819
+ // writes `x`/`y` as authored, and only this adapter knows the frame `rects` answer in.
820
+ this.structure = {
821
+ ...structure,
822
+ create: (kind, parentId, at) =>
823
+ structure.create(kind, parentId, at ? this.pointInParent(parentId ?? null, at) : undefined),
824
+ };
748
825
  this.assetDrop =
749
826
  this.target.assetDrop ??
750
827
  ({
@@ -767,7 +844,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
767
844
  if (!opts.stories && this.target.componentIdentity) {
768
845
  this.stories = componentStatesProvider(
769
846
  'canvas',
770
- store,
847
+ store.shell,
771
848
  (id) => this.target.componentIdentity?.(id) ?? null,
772
849
  );
773
850
  }
@@ -845,10 +922,11 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
845
922
  queueMicrotask(() => {
846
923
  this.structureNotifyQueued = false;
847
924
  this.projector.reproject();
925
+ this.pathKeys.clear();
848
926
  this.watchStructure(this.root);
849
927
  this.target.onReindex();
850
928
  this.notify();
851
- this.store.notifyIngestEdit();
929
+ this.store.shell.notifyIngestEdit();
852
930
  });
853
931
  };
854
932
 
@@ -914,9 +992,9 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
914
992
  }
915
993
 
916
994
  readonly selection: SelectionProvider = {
917
- get: () => [...this.store.selectedEntityIds],
995
+ get: () => [...this.store.shell.selectedEntityIds],
918
996
  set: (ids) => {
919
- this.store.selectMultiple(ids);
997
+ this.store.shell.selectMultiple(ids);
920
998
  this.notify();
921
999
  },
922
1000
  };
@@ -945,7 +1023,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
945
1023
  // degrade — a half-answer would be a menu item that silently does nothing.
946
1024
 
947
1025
  private readonly liveStructure: StructureProvider = {
948
- create: (kind, parentId) => this.createStructuralNode(kind, parentId ?? null),
1026
+ create: (kind, parentId, at) => this.createStructuralNode(kind, parentId ?? null, at),
949
1027
  remove: (id) => this.removeStructuralNodes([id]),
950
1028
  removeMany: (ids) => this.removeStructuralNodes(ids),
951
1029
  duplicate: (id) => this.duplicateStructuralNode(id),
@@ -1009,10 +1087,11 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1009
1087
  */
1010
1088
  private afterStructuralChange(): void {
1011
1089
  this.projector.reproject();
1090
+ this.pathKeys.clear();
1012
1091
  this.watchStructure(this.root);
1013
1092
  this.target.onReindex();
1014
1093
  this.notify();
1015
- this.store.notifyIngestEdit();
1094
+ this.store.shell.notifyIngestEdit();
1016
1095
  }
1017
1096
 
1018
1097
  private idOf(object: Container): string {
@@ -1025,7 +1104,22 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1025
1104
  * same turn. The live-only report is the other half of the answer and rides
1026
1105
  * back in `ack` (see `StructuralIdWrite`) instead of being fired `void`.
1027
1106
  */
1028
- private createStructuralNode(kind: string, parentId: string | null): StructuralIdWrite {
1107
+ /** A point in the `rects` frame, in the space of the container a new child of `parentId` joins. */
1108
+ private pointInParent(
1109
+ parentId: string | null,
1110
+ at: { readonly x: number; readonly y: number },
1111
+ ): { x: number; y: number } | undefined {
1112
+ const parent = this.containerFor(parentId);
1113
+ if (!parent || typeof parent.updateLocalTransform !== 'function') return undefined;
1114
+ const local = this.localToRootMatrix(parent).clone().invert().apply({ x: at.x, y: at.y });
1115
+ return { x: local.x, y: local.y };
1116
+ }
1117
+
1118
+ private createStructuralNode(
1119
+ kind: string,
1120
+ parentId: string | null,
1121
+ at?: { readonly x: number; readonly y: number },
1122
+ ): StructuralIdWrite {
1029
1123
  const parent = this.containerFor(parentId);
1030
1124
  if (!parent) {
1031
1125
  return {
@@ -1041,6 +1135,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1041
1135
  };
1042
1136
  }
1043
1137
  node.label = defaultLabelFor(kind);
1138
+ if (at) node.position.set(at.x, at.y);
1044
1139
  const label = `Create ${node.label}`;
1045
1140
  this.structureHistory.track(parent);
1046
1141
  this.structureHistory.track(node);
@@ -1274,7 +1369,13 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1274
1369
  this.boxEditSessions.delete(id);
1275
1370
  return;
1276
1371
  }
1277
- this.boxEditSessions.set(id, { id, rect, transform: this.transforms.get(id) });
1372
+ const parent = this.projector.object(id)?.parent?.worldTransform;
1373
+ this.boxEditSessions.set(id, {
1374
+ id,
1375
+ rect,
1376
+ transform: this.transforms.get(id),
1377
+ ...(parent ? { parentLinear: { a: parent.a, b: parent.b, c: parent.c, d: parent.d } } : {}),
1378
+ });
1278
1379
  this.transforms.beginEdit?.(id);
1279
1380
  },
1280
1381
  apply: (id, patch) => this.applyBoxEdit(id, patch),
@@ -1504,12 +1605,23 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1504
1605
  properties: (id) => [
1505
1606
  ...this.target.properties(id),
1506
1607
  { path: 'locked', label: 'Locked', type: 'boolean', group: 'Visibility' },
1608
+ { path: 'grouped', label: 'Grouped', type: 'boolean', group: 'Visibility' },
1507
1609
  ],
1508
- get: (id, path) => (path === 'locked' ? this.lockedIds.has(id) : this.target.get(id, path)),
1610
+ get: (id, path) =>
1611
+ path === 'locked'
1612
+ ? this.lockedPaths.has(this.pathKeyOf(id))
1613
+ : path === 'grouped'
1614
+ ? this.groupedPaths.has(this.pathKeyOf(id))
1615
+ : this.target.get(id, path),
1509
1616
  set: (id, path, value) => {
1510
- if (path === 'locked') {
1511
- if (value === true) this.lockedIds.add(id);
1512
- else this.lockedIds.delete(id);
1617
+ if (path === 'locked' || path === 'grouped') {
1618
+ const set = path === 'locked' ? this.lockedPaths : this.groupedPaths;
1619
+ if (value === true) set.add(this.pathKeyOf(id));
1620
+ else set.delete(this.pathKeyOf(id));
1621
+ writeEditLocks(this.editLockWorld, {
1622
+ locked: [...this.lockedPaths],
1623
+ grouped: [...this.groupedPaths],
1624
+ });
1513
1625
  this.notify();
1514
1626
  return;
1515
1627
  }
@@ -1519,10 +1631,45 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1519
1631
  remove: (id, path) => this.target.remove?.(id, path),
1520
1632
  };
1521
1633
 
1634
+ /** `id`'s label path from its world's root: each step the authored label and its place among
1635
+ * same-labelled siblings, so it holds across line edits and reloads (a rename drops it). */
1636
+ private pathKeyOf(id: string): string {
1637
+ const known = this.pathKeys.get(id);
1638
+ if (known !== undefined) return known;
1639
+ const labelOf = (nodeId: string): string => this.projector.object(nodeId)?.label ?? '';
1640
+ const segments: string[] = [];
1641
+ let current: string | null = id;
1642
+ while (current) {
1643
+ const node = this.projector.node(current);
1644
+ if (!node) break;
1645
+ const siblings = node.parentId
1646
+ ? (this.projector.node(node.parentId)?.childIds ?? [])
1647
+ : this.projector.roots().map((root) => root.id);
1648
+ const label = labelOf(current);
1649
+ const place = siblings.filter((sibling) => labelOf(sibling) === label).indexOf(current);
1650
+ segments.unshift(`${label}#${Math.max(0, place)}`);
1651
+ current = node.parentId ?? null;
1652
+ }
1653
+ const key = segments.join('/');
1654
+ this.pathKeys.set(id, key);
1655
+ return key;
1656
+ }
1657
+
1658
+ /** The outermost grouped node that contains `id`, or `id` itself when none does. */
1659
+ private groupOf(id: string): string {
1660
+ let group = id;
1661
+ let current: string | null = id;
1662
+ while (current) {
1663
+ if (this.groupedPaths.has(this.pathKeyOf(current))) group = current;
1664
+ current = this.projector.node(current)?.parentId ?? null;
1665
+ }
1666
+ return group;
1667
+ }
1668
+
1522
1669
  private isPickLocked(id: string): boolean {
1523
1670
  let current: string | null = id;
1524
1671
  while (current) {
1525
- if (this.lockedIds.has(current)) return true;
1672
+ if (this.lockedPaths.has(this.pathKeyOf(current))) return true;
1526
1673
  current = this.projector.node(current)?.parentId ?? null;
1527
1674
  }
1528
1675
  return false;
@@ -1541,7 +1688,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1541
1688
 
1542
1689
  dispose(): void {
1543
1690
  this.boxEditSessions.clear();
1544
- this.lockedIds.clear();
1691
+ this.pathKeys.clear();
1545
1692
  for (const object of this.watchedForStructure) {
1546
1693
  object.off('childAdded', this.onStructureChanged);
1547
1694
  object.off('childRemoved', this.onStructureChanged);
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The display objects a canvas world creates, as its create dialog lists them: Pixi's own class
3
+ * tree (every one of them is a Container) and a line on what each is, from Pixi's API reference.
4
+ */
5
+
6
+ import type { CreatableKind } from '@volter/editor-project/adapter';
7
+
8
+ export const PIXI_CREATABLE_KINDS: readonly CreatableKind[] = [
9
+ {
10
+ kind: 'container',
11
+ label: 'Container',
12
+ description: 'A general-purpose display object that holds children, and transforms and draws them together.',
13
+ },
14
+ {
15
+ kind: 'sprite',
16
+ label: 'Sprite',
17
+ extends: 'container',
18
+ description: 'Draws one texture: an image, or a frame of a spritesheet.',
19
+ },
20
+ {
21
+ kind: 'animatedSprite',
22
+ label: 'AnimatedSprite',
23
+ extends: 'sprite',
24
+ description: 'Plays a list of textures as frames at an animation speed.',
25
+ },
26
+ {
27
+ kind: 'tilingSprite',
28
+ label: 'TilingSprite',
29
+ extends: 'container',
30
+ description: 'Repeats one texture across its width and height, scrollable by its tile position.',
31
+ },
32
+ {
33
+ kind: 'nineSliceSprite',
34
+ label: 'NineSliceSprite',
35
+ extends: 'container',
36
+ description: 'Stretches a texture without stretching its corners: the nine-slice panel of a UI.',
37
+ },
38
+ {
39
+ kind: 'text',
40
+ label: 'Text',
41
+ extends: 'container',
42
+ description: 'Draws a string with a text style (font, size, fill, wrapping).',
43
+ },
44
+ {
45
+ kind: 'bitmapText',
46
+ label: 'BitmapText',
47
+ extends: 'container',
48
+ description: 'Draws a string from a bitmap font: fast to change every frame.',
49
+ },
50
+ {
51
+ kind: 'htmlText',
52
+ label: 'HTMLText',
53
+ extends: 'container',
54
+ description: 'Draws a string of HTML and CSS markup.',
55
+ },
56
+ {
57
+ kind: 'graphics',
58
+ label: 'Graphics',
59
+ extends: 'container',
60
+ description: 'Draws vector shapes (rectangles, circles, paths) from its draw callback.',
61
+ },
62
+ ];
@@ -27,8 +27,8 @@
27
27
  * place a fallback could quietly pretend to persist.
28
28
  */
29
29
 
30
- import type { PhysicsAdapter2D } from '@volter/game-runtime/pixi/system-adapters';
31
- import type { HistoryService } from '@volter/editor-core/history/history-service';
30
+ import type { PhysicsAdapter2D } from '../../runtime/pixi/system-adapters';
31
+ import type { HistoryService } from '@volter/editor-sdk/kit/history/history-service';
32
32
  import type { IngestSourcePersistence } from './ingest-source-persistence';
33
33
  import type { CanvasWriteTarget } from './pixi-authoring-adapter';
34
34
  import { createLiveCanvasWriteTarget } from './pixi-live-write-target';
@@ -34,8 +34,8 @@ import type {
34
34
  AuthoringAdapter2D,
35
35
  Overlay2D,
36
36
  Transform2DValue,
37
- } from '@volter/game-runtime/pixi/authoring';
38
- import type { PhysicsAdapter2D } from '@volter/game-runtime/pixi/system-adapters';
37
+ } from '../../runtime/pixi/authoring';
38
+ import type { PhysicsAdapter2D } from '../../runtime/pixi/system-adapters';
39
39
  import type {
40
40
  ComponentInstanceApplyResult,
41
41
  ComponentInstanceDescription,
@@ -48,7 +48,7 @@ import type {
48
48
  TransformEditability,
49
49
  } from '@volter/editor-project/adapter';
50
50
  import type { Container } from 'pixi.js';
51
- import type { ChannelValue, CreationSiteLiteralReport } from '@volter/editor-core/creation-site-edit';
51
+ import type { ChannelValue, CreationSiteLiteralReport } from '@volter/editor-sdk/kit/creation-site-edit';
52
52
  import { creationSiteAnchor, instancesAtSite } from '@volter/editor-sdk/kit/creation-site-registry';
53
53
  import { editorConsole } from '@volter/editor-sdk/kit/editor-console';
54
54
  import { JsonHistoryResource } from '../history/json-history-resource';
@@ -750,7 +750,7 @@ export function createLiveCanvasWriteTarget(
750
750
  bind(context: CanvasWriteContext): void {
751
751
  a2d = context.a2d;
752
752
  notify = context.notify;
753
- const history = context.store.projectHistory;
753
+ const history = context.store.shell.projectHistory;
754
754
  historyResource = history
755
755
  ? new JsonHistoryResource({
756
756
  history,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The OID half of the canvas authoring surface's IDENTITY axis
3
- * (`@volter/game-runtime/pixi/authoring`'s {@link CanvasIdentity}) — what a row is called
3
+ * (`@volter/editor-game/runtime/pixi/authoring`'s {@link CanvasIdentity}) — what a row is called
4
4
  * when the display tree was authored as first-party `@pixi/react` TSX, and the
5
5
  * resolution from such an id back to the source tag it addresses.
6
6
  *
@@ -34,9 +34,9 @@
34
34
  * selection survives one.
35
35
  */
36
36
 
37
- import type { CanvasIdentity } from '@volter/game-runtime/pixi/authoring';
37
+ import type { CanvasIdentity } from '../../runtime/pixi/authoring';
38
38
  import type { Container } from 'pixi.js';
39
- import { occurrenceId } from '@volter/editor-core/authoring/component-instance-root';
39
+ import { occurrenceId } from '@volter/editor-threejs/kit/authoring/component-instance-root';
40
40
 
41
41
  const PREFIX = 'pixi';
42
42