@vgai/editor 0.2.0 → 0.4.0-canary.20260715.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 (430) hide show
  1. package/README.md +24 -0
  2. package/dist/assets/axe-C7YBtOuM.js +30 -0
  3. package/dist/assets/browserAll-DDtGGE48.js +1 -0
  4. package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
  5. package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
  6. package/dist/assets/game-BdTHR2_X.js +1 -0
  7. package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
  8. package/dist/assets/game-CcHfYxjP.js +1 -0
  9. package/dist/assets/game-Cqzj_lpD.js +1 -0
  10. package/dist/assets/game-DSZdeCHu.js +1 -0
  11. package/dist/assets/game-Dch9sAPc.js +1 -0
  12. package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
  13. package/dist/assets/game-loop-DMIgSMV-.js +1 -0
  14. package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
  15. package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
  16. package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
  17. package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
  18. package/dist/assets/index-CQEfi217.js +15412 -0
  19. package/dist/assets/index-Dvi-V0NE.css +1 -0
  20. package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
  21. package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
  22. package/dist/assets/react-18-C9eHn8x1.js +1 -0
  23. package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
  24. package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
  25. package/dist/index.html +15 -13
  26. package/dist/ingest/racing-game/vgai.game.json +5 -4
  27. package/dist/ingest/simcity/vgai.game.json +5 -4
  28. package/dist/ingest/tanks/vgai.game.json +5 -4
  29. package/dist/project-scripts.bundle.js +3917 -0
  30. package/dist/scenes/untitled.vscn.json +2 -0
  31. package/dist-server/packaged.mjs +7697 -3435
  32. package/package.json +31 -4
  33. package/scripts/probe-reporter.ts +349 -0
  34. package/scripts/run-probe.ts +2260 -0
  35. package/server/asset-catalog-audit.ts +268 -0
  36. package/server/asset-catalog-v2.ts +146 -0
  37. package/server/asset-library-routes.ts +276 -58
  38. package/server/canonical-path.ts +1 -1
  39. package/server/dev.ts +148 -44
  40. package/server/editor-server.ts +888 -61
  41. package/server/editor-sse.ts +115 -4
  42. package/server/harness-chat-service.ts +796 -0
  43. package/server/local-asset-catalog.ts +157 -22
  44. package/server/packaged.ts +51 -21
  45. package/server/process-shutdown.ts +136 -0
  46. package/server/prod.ts +9 -6
  47. package/server/project-hmr-files.ts +41 -0
  48. package/server/project-operations.ts +343 -0
  49. package/server/project-optimize-deps-entries.ts +4 -4
  50. package/server/project-output-writer.ts +156 -0
  51. package/server/project-validation.ts +140 -0
  52. package/server/project-view.ts +8 -50
  53. package/server/server-utils.ts +75 -10
  54. package/server/session-registry.ts +12 -0
  55. package/src/EditorContext.tsx +107 -3
  56. package/src/action-registry.ts +115 -17
  57. package/src/adapter-resolver.ts +213 -478
  58. package/src/asset-events.ts +64 -1
  59. package/src/asset-preview.ts +906 -0
  60. package/src/asset-selection.ts +121 -0
  61. package/src/asset-workflow/asset-capabilities.ts +259 -0
  62. package/src/asset-workflow/asset-editor-errors.ts +46 -0
  63. package/src/asset-workflow/asset-import-pipeline.ts +176 -0
  64. package/src/asset-workflow/asset-library-query.ts +107 -0
  65. package/src/asset-workflow/asset-types.ts +192 -0
  66. package/src/asset-workflow/asset-workflow-quality.ts +65 -0
  67. package/src/asset-workflow/import-contract.ts +113 -0
  68. package/src/asset-workflow/model-conversion.ts +126 -0
  69. package/src/asset-workflow/model-inspection.ts +185 -0
  70. package/src/asset-workflow/project-asset-commands.ts +23 -0
  71. package/src/asset-workflow/project-asset-health.ts +153 -0
  72. package/src/asset-workflow/project-asset-operations.ts +119 -0
  73. package/src/asset-workflow/thumbnail-system.ts +172 -0
  74. package/src/authoring/active-systems.ts +15 -0
  75. package/src/authoring/boundary-authoring-adapter.ts +16 -5
  76. package/src/authoring/composite-authoring-adapter.ts +305 -124
  77. package/src/authoring/design-time-layers.ts +300 -117
  78. package/src/authoring/edit-mode-authoring.ts +278 -293
  79. package/src/authoring/embed-only-adapter.ts +4 -2
  80. package/src/authoring/first-party-hierarchy-menu.ts +7 -2
  81. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
  82. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
  83. package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
  84. package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
  85. package/src/authoring/first-party-inspector/register.tsx +13 -2
  86. package/src/authoring/ingest-authoring-adapter.ts +142 -45
  87. package/src/authoring/mount-failure-report.ts +5 -5
  88. package/src/authoring/provenance.ts +41 -0
  89. package/src/authoring/react-dom-authoring-adapter.ts +92 -92
  90. package/src/authoring/react-inspector-section.tsx +489 -51
  91. package/src/authoring/react-world-authoring-adapter.ts +541 -306
  92. package/src/authoring/story-overlay.ts +1 -1
  93. package/src/authoring/story-types.ts +11 -0
  94. package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
  95. package/src/authoring/viewport-tool-context.ts +78 -0
  96. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  97. package/src/authoring/world-hidden-viewport.ts +186 -0
  98. package/src/authoring/world-pan-state.ts +33 -17
  99. package/src/authoring/world-session-state.ts +1 -1
  100. package/src/authoring/world2d-authoring-adapter.ts +109 -52
  101. package/src/availability-tick.ts +56 -0
  102. package/src/browser-play-entry.ts +5 -5
  103. package/src/browser-project-scripts.ts +1 -0
  104. package/src/build-identity.ts +15 -0
  105. package/src/command-listener.ts +893 -19
  106. package/src/components/AlignToolbar.tsx +74 -63
  107. package/src/components/AppRoot.tsx +193 -26
  108. package/src/components/ApplicationMenus.tsx +272 -0
  109. package/src/components/AssetBrowser.tsx +1450 -293
  110. package/src/components/AssetEditorShell.tsx +141 -0
  111. package/src/components/AssetImportDetails.tsx +218 -0
  112. package/src/components/AssetLibraryPanel.tsx +14 -0
  113. package/src/components/CameraInfo.tsx +11 -20
  114. package/src/components/CenterDocuments.tsx +585 -0
  115. package/src/components/CommandPalette.tsx +75 -30
  116. package/src/components/ConsolePanel.css +55 -0
  117. package/src/components/ConsolePanel.tsx +57 -112
  118. package/src/components/DebugConsolePanel.tsx +461 -0
  119. package/src/components/DebuggerPanel.tsx +165 -0
  120. package/src/components/DefaultEditorLayout.tsx +167 -231
  121. package/src/components/GameHierarchy.tsx +873 -290
  122. package/src/components/GamePerformanceOverlay.tsx +46 -0
  123. package/src/components/HarnessChatPanel.tsx +2352 -0
  124. package/src/components/HistoryPanel.tsx +226 -0
  125. package/src/components/Inspector.tsx +153 -99
  126. package/src/components/InspectorToolSection.tsx +6 -4
  127. package/src/components/MountFailureBanner.tsx +31 -48
  128. package/src/components/NetworkSection.tsx +25 -14
  129. package/src/components/OnlineAssetBrowser.tsx +794 -343
  130. package/src/components/OverlayReportBanner.tsx +38 -64
  131. package/src/components/PerformancePanel.tsx +206 -0
  132. package/src/components/PlayBar.tsx +93 -263
  133. package/src/components/PrefabSaveDialog.tsx +55 -127
  134. package/src/components/ProjectHeader.css +60 -0
  135. package/src/components/ProjectHeader.tsx +170 -105
  136. package/src/components/ProjectScreen.tsx +246 -184
  137. package/src/components/ReactCanvasControls.tsx +185 -0
  138. package/src/components/ResolutionPicker.tsx +85 -0
  139. package/src/components/RootActivateDialog.tsx +39 -68
  140. package/src/components/SaveDialog.tsx +55 -127
  141. package/src/components/SaveStatus.tsx +11 -29
  142. package/src/components/StartupLoadingScreen.tsx +96 -0
  143. package/src/components/StateWatchPanel.tsx +302 -0
  144. package/src/components/StatsOverlay.tsx +12 -5
  145. package/src/components/StatusBar.tsx +64 -0
  146. package/src/components/ThemeManagerDialog.css +89 -0
  147. package/src/components/ThemeManagerDialog.tsx +371 -0
  148. package/src/components/ToolHost.tsx +26 -15
  149. package/src/components/Toolbar.tsx +117 -214
  150. package/src/components/VgaiVoxelLogo.tsx +89 -0
  151. package/src/components/ViewportOverlay.tsx +97 -172
  152. package/src/components/ViewportPanel.tsx +130 -31
  153. package/src/components/WorkspaceDock.tsx +891 -0
  154. package/src/components/WorldSelectionOverlay.tsx +51 -63
  155. package/src/components/WorldTextEditor.tsx +9 -5
  156. package/src/components/asset-documents.tsx +390 -0
  157. package/src/components/asset-editor-persistence.ts +44 -0
  158. package/src/components/asset-selection-section.tsx +338 -0
  159. package/src/components/asset-viewers/AudioViewer.tsx +73 -21
  160. package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
  161. package/src/components/asset-viewers/ImageViewer.tsx +43 -11
  162. package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
  163. package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
  164. package/src/components/asset-viewers/ModelViewer.tsx +805 -121
  165. package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
  166. package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
  167. package/src/components/asset-workflow.css +506 -0
  168. package/src/components/build-documents.tsx +696 -0
  169. package/src/components/core-utilities.tsx +75 -0
  170. package/src/components/data-documents.tsx +364 -0
  171. package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
  172. package/src/components/debugger-panel-state.ts +57 -0
  173. package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
  174. package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
  175. package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
  176. package/src/components/inspector-widgets/FontPicker.tsx +13 -21
  177. package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
  178. package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
  179. package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
  180. package/src/components/inspector-widgets/shared.tsx +90 -49
  181. package/src/components/inspectors/AnimationSection.tsx +39 -40
  182. package/src/components/inspectors/AudioSection.tsx +28 -39
  183. package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
  184. package/src/components/inspectors/CameraSection.tsx +14 -23
  185. package/src/components/inspectors/ComponentsSection.tsx +39 -43
  186. package/src/components/inspectors/JointsSection.tsx +34 -43
  187. package/src/components/inspectors/LightSection.tsx +90 -35
  188. package/src/components/inspectors/MaterialSection.tsx +137 -174
  189. package/src/components/inspectors/MeshSection.tsx +9 -17
  190. package/src/components/inspectors/NavigationSection.tsx +8 -15
  191. package/src/components/inspectors/ParticleSection.tsx +91 -76
  192. package/src/components/inspectors/PhysicsSection.tsx +42 -46
  193. package/src/components/inspectors/PostProcessingSection.tsx +28 -36
  194. package/src/components/inspectors/RenderingSection.tsx +12 -20
  195. package/src/components/inspectors/ShadowSection.tsx +7 -8
  196. package/src/components/inspectors/SplineSection.tsx +31 -42
  197. package/src/components/inspectors/TransformSection.tsx +14 -22
  198. package/src/components/patterns/Dialog.tsx +95 -0
  199. package/src/components/patterns/Fields.tsx +44 -0
  200. package/src/components/patterns/List.tsx +25 -0
  201. package/src/components/patterns/StateSurface.tsx +40 -0
  202. package/src/components/patterns/Surfaces.tsx +115 -0
  203. package/src/components/patterns/Tabs.tsx +80 -0
  204. package/src/components/patterns/Toolbar.tsx +51 -0
  205. package/src/components/patterns/Tree.tsx +72 -0
  206. package/src/components/primitives/Button.tsx +46 -0
  207. package/src/components/primitives/ColorInput.tsx +11 -47
  208. package/src/components/primitives/EditorIcon.tsx +99 -0
  209. package/src/components/primitives/FormControls.tsx +71 -0
  210. package/src/components/primitives/JsonInput.tsx +15 -2
  211. package/src/components/primitives/Layout.tsx +100 -0
  212. package/src/components/primitives/Menu.tsx +105 -0
  213. package/src/components/primitives/NumberInput.tsx +8 -35
  214. package/src/components/primitives/Panel.tsx +49 -44
  215. package/src/components/primitives/SectionHeader.tsx +10 -28
  216. package/src/components/primitives/Text.tsx +46 -0
  217. package/src/components/primitives/Tooltip.tsx +20 -12
  218. package/src/components/primitives/Vec3Input.tsx +3 -0
  219. package/src/components/primitives/banner-tones.ts +33 -0
  220. package/src/components/primitives/editor-icons.ts +102 -0
  221. package/src/components/primitives/panel-header-styles.ts +30 -0
  222. package/src/components/project-operation-documents.tsx +226 -0
  223. package/src/components/status-contributions.tsx +175 -0
  224. package/src/components/story-documents-register.ts +15 -0
  225. package/src/components/story-documents.tsx +1185 -0
  226. package/src/components/tool-documents.tsx +110 -0
  227. package/src/components/workspace-dock.css +293 -0
  228. package/src/components/workspace-history.ts +33 -0
  229. package/src/components/world-documents.tsx +145 -0
  230. package/src/components/xstate-documents.tsx +209 -0
  231. package/src/composite-screenshot.ts +248 -0
  232. package/src/design-system/index.ts +70 -0
  233. package/src/design-system-stories/Foundation.stories.tsx +183 -0
  234. package/src/design-system-stories/Patterns.stories.tsx +251 -0
  235. package/src/design-system-stories/Workspace.stories.tsx +538 -0
  236. package/src/doctor/report.ts +57 -13
  237. package/src/editor-api.ts +295 -31
  238. package/src/editor-compatibility.ts +200 -0
  239. package/src/editor-console.ts +35 -17
  240. package/src/editor-hotkeys.ts +85 -33
  241. package/src/editor-store.ts +538 -233
  242. package/src/editor-viewport.ts +160 -34
  243. package/src/entity-factory.ts +14 -3
  244. package/src/gizmo-registry.ts +21 -5
  245. package/src/harness-chat-editor-context.ts +37 -0
  246. package/src/harness-chat-indicator.ts +68 -0
  247. package/src/harness-chat-layout.ts +53 -0
  248. package/src/harness-chat-lifecycle.ts +170 -0
  249. package/src/harness-chat-reconnect.ts +85 -0
  250. package/src/harness-chat-types.ts +409 -0
  251. package/src/hierarchy-drop.ts +50 -0
  252. package/src/hierarchy-expansion-state.ts +100 -0
  253. package/src/hierarchy-menu-registry.ts +1 -1
  254. package/src/hierarchy-projection.ts +76 -0
  255. package/src/history/editor-session.ts +21 -0
  256. package/src/history/history-commands.ts +147 -0
  257. package/src/history/history-service.ts +904 -0
  258. package/src/history/json-history-resource.ts +165 -0
  259. package/src/history/persistence-coordinator.ts +35 -0
  260. package/src/history/project-file-history.ts +266 -0
  261. package/src/history/project-root-history-backends.ts +53 -0
  262. package/src/history/resource-registry.ts +209 -0
  263. package/src/history/snapshot-store.ts +108 -0
  264. package/src/history/source-history-backend.ts +374 -0
  265. package/src/history/types.ts +100 -0
  266. package/src/hmr-registration-group.ts +67 -0
  267. package/src/hmr-stable-react-context.ts +23 -0
  268. package/src/hosted-example.ts +12 -2
  269. package/src/hotkeys.ts +89 -18
  270. package/src/ingest/discovery-react.ts +9 -10
  271. package/src/ingest/discovery.ts +4 -4
  272. package/src/ingest/discovery2d.ts +5 -5
  273. package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
  274. package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
  275. package/src/ingest/games/games-fps/vgai.game.json +3 -3
  276. package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
  277. package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
  278. package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
  279. package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
  280. package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
  281. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
  282. package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
  283. package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
  284. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
  285. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
  286. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
  287. package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
  288. package/src/ingest/types.ts +1 -1
  289. package/src/ingest-mode.ts +47 -52
  290. package/src/ingest-siblings.ts +23 -33
  291. package/src/initial-scene.ts +1 -2
  292. package/src/main.tsx +16 -8
  293. package/src/manifest-project.ts +10 -12
  294. package/src/model-thumbnail.ts +261 -26
  295. package/src/module-mode.ts +44 -50
  296. package/src/play-log-events.ts +26 -0
  297. package/src/play-mode.ts +495 -401
  298. package/src/play-scene-adoption.ts +23 -0
  299. package/src/playground/main.ts +68 -24
  300. package/src/playwright-shim.ts +473 -0
  301. package/src/project-manager.ts +90 -10
  302. package/src/project-operations.ts +112 -0
  303. package/src/project-story-discovery.ts +25 -0
  304. package/src/project-tool-discovery.ts +43 -0
  305. package/src/scene-serializer.ts +0 -6
  306. package/src/scene-sync.ts +7 -2
  307. package/src/script-loader.ts +3 -3
  308. package/src/skeleton-helper.ts +57 -0
  309. package/src/storage/seed.ts +68 -8
  310. package/src/stories/StoryPreviewMount.tsx +265 -0
  311. package/src/stories/action-stories.fixture.tsx +44 -0
  312. package/src/stories/button-stories.fixture.tsx +50 -0
  313. package/src/stories/compose-project-stories.ts +182 -0
  314. package/src/stories/loader-stories.fixture.tsx +53 -0
  315. package/src/stories/story-discovery.ts +130 -0
  316. package/src/stories/story-registry.ts +146 -0
  317. package/src/theme-css.ts +1049 -0
  318. package/src/theme-library.ts +238 -0
  319. package/src/theme-preference.ts +130 -0
  320. package/src/theme.ts +527 -0
  321. package/src/tool-loader.ts +80 -24
  322. package/src/ui-source/inspect.ts +27 -4
  323. package/src/ui-source/source-write-backend.ts +83 -22
  324. package/src/widgets/index.ts +9 -18
  325. package/src/workspace-aux-commands.ts +11 -0
  326. package/src/workspace-dock-controller.ts +64 -0
  327. package/src/workspace-document-ids.ts +5 -0
  328. package/src/workspace-document-registry.ts +367 -0
  329. package/src/workspace-state-persistence.ts +455 -0
  330. package/src/workspace-status-registry.ts +110 -0
  331. package/src/workspace-utility-commands.ts +49 -0
  332. package/src/workspace-utility-registry.ts +161 -0
  333. package/src/world-document-routing.ts +16 -0
  334. package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
  335. package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
  336. package/src/xstate-inspect/use-live-actor-state.ts +39 -0
  337. package/src/xstate-inspect/xstate-graph.ts +265 -0
  338. package/src/xstate-inspect/xstate-layout.ts +76 -0
  339. package/src/z-index.ts +26 -0
  340. package/template/.claude/skills/editor/SKILL.md +129 -90
  341. package/template/AGENTS.md +1110 -0
  342. package/template/CLAUDE.md +1 -512
  343. package/template/IDIOMS.md +50 -0
  344. package/template/check-idioms.ts +706 -0
  345. package/template/manifest-entry-modules-plugin.ts +53 -0
  346. package/template/package.json +13 -4
  347. package/template/public/scenes/default.scn2d.json +6 -0
  348. package/template/public/scenes/default.vscn.json +7 -1
  349. package/template/public/scenes/untitled.vscn.json +10 -0
  350. package/template/scripts/playtest.ts +40 -0
  351. package/template/server/loopback.ts +75 -0
  352. package/template/server/rooms/arena-room.ts +69 -1
  353. package/template/server/rooms/game-room.ts +68 -1
  354. package/template/src/data/README.md +9 -4
  355. package/template/src/data/data-ref.ts +35 -0
  356. package/template/src/data/tuning.schema.ts +1 -1
  357. package/template/src/main.ts +41 -62
  358. package/template/src/manifest-entry-modules.d.ts +3 -0
  359. package/template/src/operations/README.md +88 -0
  360. package/template/src/runtime/pixi-adapter.ts +24 -0
  361. package/template/src/scripts/components/data-spinner.ts +43 -2
  362. package/template/src/scripts/components/spin-lap-math.ts +27 -0
  363. package/template/src/tools/README.md +5 -3
  364. package/template/src/tools/example.tool.tsx +13 -7
  365. package/template/src/ui/Button.stories.tsx +66 -0
  366. package/template/src/ui/RebindPanel.tsx +2 -6
  367. package/template/src/ui/game.tsx +17 -0
  368. package/template/tests/acceptance/example.spec.ts +64 -0
  369. package/template/tests/logic/example.test.ts +31 -0
  370. package/template/tests/logic/room.test.ts +100 -0
  371. package/template/tests/tsconfig.json +30 -0
  372. package/template/validate-manifest.ts +5 -5
  373. package/template/validate-scenes.ts +50 -18
  374. package/template/vgai.game.json +20 -13
  375. package/template/vite.config.ts +77 -16
  376. package/vite-plugin-ui-oid.ts +261 -17
  377. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
  378. package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
  379. package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
  380. package/dist/assets/browserAll-CEkWYlFk.js +0 -1
  381. package/dist/assets/create-runtime-DYIWr3va.js +0 -1
  382. package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
  383. package/dist/assets/game-8SDZPPgH.js +0 -1
  384. package/dist/assets/game-CevorxyV.js +0 -1
  385. package/dist/assets/game-Di7XlywD.js +0 -1
  386. package/dist/assets/game-DjnCaw8R.js +0 -1
  387. package/dist/assets/game-Q9A0aXSV.js +0 -1
  388. package/dist/assets/index-CPVV6pjw.js +0 -1
  389. package/dist/assets/index-CpOnYyeD.js +0 -213
  390. package/dist/assets/index-Dr8H_6Rl.js +0 -1
  391. package/dist/assets/index-_bCaipUk.js +0 -13650
  392. package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
  393. package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
  394. package/dist/assets/scene-query-BivAHhJu.js +0 -1
  395. package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
  396. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
  397. package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
  398. package/src/authoring/scene-ui-layer.ts +0 -485
  399. package/src/authoring/ui-authoring-adapter.ts +0 -325
  400. package/src/authoring/ui-hierarchy-menu.ts +0 -40
  401. package/src/authoring/ui-inspector-sections.tsx +0 -156
  402. package/src/authoring/undo-timeline.ts +0 -145
  403. package/src/components/BottomPanel.tsx +0 -326
  404. package/src/components/BuildPanel.tsx +0 -455
  405. package/src/components/FolderTree.tsx +0 -179
  406. package/src/components/RightPanel.tsx +0 -166
  407. package/src/components/WorldContextMenu.tsx +0 -257
  408. package/src/components/asset-viewers/AssetViewer.tsx +0 -38
  409. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
  410. package/src/scene-ui-mode/active-scene-ui.ts +0 -58
  411. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
  412. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
  413. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
  414. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
  415. package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
  416. package/src/ui-editor/inspector.tsx +0 -563
  417. package/src/ui-editor/node-ops.ts +0 -266
  418. package/src/ui-editor/overlay.tsx +0 -380
  419. package/src/ui-editor/palette.tsx +0 -114
  420. package/src/ui-editor/react-store.ts +0 -78
  421. package/src/ui-editor/timeline-panel.tsx +0 -443
  422. package/src/ui-editor/ui-edit-store.ts +0 -552
  423. package/src/ui-editor/ui-prefab.ts +0 -95
  424. package/template/project.json +0 -9
  425. package/template/src/scripts/main.ts +0 -94
  426. package/template/src/ui/DefaultHud.tsx +0 -40
  427. package/template/src/ui/game-state.tsx +0 -37
  428. package/template/src/ui/mount-default-hud.tsx +0 -19
  429. package/template/src/ui/mount-ui.ts +0 -15
  430. package/vite-plugin-react-world-provider.ts +0 -81
@@ -0,0 +1,165 @@
1
+ import { HistoryOperationError, type HistoryService } from './history-service';
2
+ import { sha256Hex } from './snapshot-store';
3
+ import type { ResourceDescriptor, ResourceDriver, ResourceKind } from './types';
4
+
5
+ const encoder = new TextEncoder();
6
+ const decoder = new TextDecoder();
7
+
8
+ export interface JsonHistoryResourceOptions<T> {
9
+ readonly history: HistoryService;
10
+ readonly kind: ResourceKind;
11
+ readonly scope: 'project' | 'session';
12
+ readonly location?: string;
13
+ readonly sessionId?: string;
14
+ readonly displayName?: string;
15
+ readonly capture: () => T;
16
+ readonly restore: (value: T) => void | Promise<void>;
17
+ /**
18
+ * The part of the captured state that constitutes this resource's CONFLICT
19
+ * IDENTITY — i.e. "has something else changed this resource since the
20
+ * transaction was recorded?". Defaults to the whole captured value.
21
+ *
22
+ * Override it when `capture()` deliberately includes state that is NOT part
23
+ * of the resource's persisted content and may change on its own. The ingest
24
+ * overlay resource is the motivating case: its snapshot carries a mirror of
25
+ * every live `Object3D`'s pose (needed as the RESTORE payload), but an
26
+ * ingested game is a RUNNING game that animates its own objects every frame.
27
+ * Hashing that live simulation state as identity made every ingest
28
+ * transaction fail preflight with `content-conflict` a frame after it was
29
+ * recorded — so undo silently did nothing (issue #81). The resource's real
30
+ * content is the overlay it serializes to disk, which only editor edits
31
+ * touch.
32
+ */
33
+ readonly conflictIdentity?: (value: T) => unknown;
34
+ }
35
+
36
+ /** Concrete JSON document/session resource backed by the project HistoryService. */
37
+ export class JsonHistoryResource<T> {
38
+ readonly descriptor: ResourceDescriptor;
39
+ private revision = 0;
40
+ private readonly unregister: () => void;
41
+ private disposed = false;
42
+
43
+ constructor(private readonly options: JsonHistoryResourceOptions<T>) {
44
+ if (options.scope === 'project') {
45
+ if (!options.location) throw new Error('A project JSON history resource needs a location.');
46
+ this.descriptor = options.history.registry.registerProject(
47
+ options.kind,
48
+ options.location,
49
+ options.displayName,
50
+ );
51
+ } else {
52
+ if (!options.sessionId)
53
+ throw new Error('A session JSON history resource needs a session id.');
54
+ this.descriptor = options.history.registry.registerSession(
55
+ options.kind,
56
+ options.sessionId,
57
+ options.displayName ?? options.kind,
58
+ );
59
+ }
60
+
61
+ const driver: ResourceDriver = {
62
+ descriptor: this.descriptor,
63
+ capture: async () => {
64
+ const bytes = this.encode(options.capture());
65
+ return {
66
+ revision: this.revision,
67
+ contentType: 'application/json',
68
+ bytes,
69
+ sha256: await sha256Hex(bytes),
70
+ };
71
+ },
72
+ preflight: async (expected) => {
73
+ const bytes = this.encode(options.capture());
74
+ const actualSha256 = await sha256Hex(bytes);
75
+ // Fast path (and the behavior for every resource that does NOT narrow
76
+ // its identity): the full captured bytes still hash to what the
77
+ // transaction expects, so nothing has diverged.
78
+ if (actualSha256 === expected.sha256) return { ok: true };
79
+ // Otherwise the bytes differ — but that only counts as a CONFLICT if the
80
+ // part that identifies the resource differs. Compare the narrowed
81
+ // identity against the expected snapshot's own identity, decoded from
82
+ // the stored blob. (See `conflictIdentity` — an ingested game mutates
83
+ // the live-object mirror in its snapshot every frame on its own.)
84
+ if (options.conflictIdentity) {
85
+ const expectedValue = JSON.parse(
86
+ decoder.decode(options.history.snapshots.read(expected).bytes),
87
+ ) as T;
88
+ const expectedIdentity = JSON.stringify(options.conflictIdentity(expectedValue));
89
+ const actualIdentity = JSON.stringify(options.conflictIdentity(options.capture()));
90
+ if (expectedIdentity === actualIdentity) return { ok: true };
91
+ }
92
+ return {
93
+ ok: false,
94
+ reason: 'content-conflict',
95
+ actualRevision: this.revision,
96
+ actualSha256,
97
+ };
98
+ },
99
+ restore: async (snapshot) => {
100
+ const bytes = options.history.snapshots.read(snapshot).bytes;
101
+ await options.restore(JSON.parse(decoder.decode(bytes)) as T);
102
+ this.revision++;
103
+ },
104
+ estimateBytes: (snapshot) => snapshot.byteLength,
105
+ };
106
+ this.unregister = options.history.registerDriver(driver);
107
+ }
108
+
109
+ record(label: string, before: T, after: T): Promise<boolean> {
110
+ if (this.disposed) return Promise.reject(new Error('JSON history resource is disposed.'));
111
+ this.options.history.assertCanRecordAppliedChange();
112
+ this.revision++;
113
+ return this.options.history
114
+ .recordAppliedTransaction(
115
+ {
116
+ label,
117
+ resources: [this.descriptor.key],
118
+ scope: this.descriptor.scope,
119
+ ...(this.descriptor.sessionId ? { sessionId: this.descriptor.sessionId } : {}),
120
+ },
121
+ [
122
+ {
123
+ resource: this.descriptor.key,
124
+ beforeBytes: this.encode(before),
125
+ afterBytes: this.encode(after),
126
+ contentType: 'application/json',
127
+ },
128
+ ],
129
+ )
130
+ .catch(async (error: unknown) => {
131
+ try {
132
+ await this.options.restore(structuredClone(before));
133
+ this.revision++;
134
+ } catch (compensationError) {
135
+ throw this.options.history.blockForCompensationFailure(label, [this.descriptor.key], {
136
+ operationError: error,
137
+ compensationError,
138
+ });
139
+ }
140
+ if (!(error instanceof HistoryOperationError)) {
141
+ this.options.history.reportAppliedChangeFailure(label, [this.descriptor.key], error);
142
+ }
143
+ throw error;
144
+ });
145
+ }
146
+
147
+ /** Must be called immediately before a live or overlay mutation begins. */
148
+ assertCanMutate(): void {
149
+ if (this.disposed) throw new Error('JSON history resource is disposed.');
150
+ this.options.history.assertCanRecordAppliedChange();
151
+ }
152
+
153
+ dispose(): void {
154
+ if (this.disposed) return;
155
+ this.disposed = true;
156
+ this.unregister();
157
+ if (this.descriptor.sessionId) {
158
+ this.options.history.expireSession(this.descriptor.sessionId);
159
+ }
160
+ }
161
+
162
+ private encode(value: T): Uint8Array {
163
+ return encoder.encode(JSON.stringify(value, null, 2));
164
+ }
165
+ }
@@ -0,0 +1,35 @@
1
+ import type { ResourceKey } from './types';
2
+
3
+ /** Serializes every in-process writer that touches the same authored resource. */
4
+ export class PersistenceCoordinator {
5
+ private readonly tails = new Map<ResourceKey, Promise<void>>();
6
+
7
+ run<T>(resource: ResourceKey, operation: () => Promise<T>): Promise<T> {
8
+ return this.runMany([resource], operation);
9
+ }
10
+
11
+ async runMany<T>(resources: readonly ResourceKey[], operation: () => Promise<T>): Promise<T> {
12
+ const keys = [...new Set(resources)].sort();
13
+ if (keys.length === 0) return operation();
14
+
15
+ const previous = keys.map((key) => this.tails.get(key) ?? Promise.resolve());
16
+ const acquired = Promise.all(previous).then(() => undefined);
17
+ let release!: () => void;
18
+ const held = new Promise<void>((resolve) => {
19
+ release = resolve;
20
+ });
21
+ const reservation = acquired.then(() => held);
22
+ for (const key of keys) this.tails.set(key, reservation);
23
+
24
+ await acquired;
25
+ try {
26
+ return await operation();
27
+ } finally {
28
+ release();
29
+ for (const key of keys) {
30
+ if (this.tails.get(key) !== reservation) continue;
31
+ this.tails.delete(key);
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,266 @@
1
+ import { getStorageBackend } from '../storage';
2
+ import type { HistoryService } from './history-service';
3
+ import { normalizeProjectPath } from './resource-registry';
4
+ import { sha256Hex } from './snapshot-store';
5
+ import type {
6
+ ResourceDescriptor,
7
+ ResourceDriver,
8
+ ResourceKey,
9
+ ResourceKind,
10
+ ResourceSnapshot,
11
+ } from './types';
12
+
13
+ const ABSENT = 0;
14
+ const PRESENT = 1;
15
+
16
+ function encodeFile(exists: boolean, bytes: Uint8Array = new Uint8Array()): Uint8Array {
17
+ const encoded = new Uint8Array(bytes.byteLength + 1);
18
+ encoded[0] = exists ? PRESENT : ABSENT;
19
+ if (exists) encoded.set(bytes, 1);
20
+ return encoded;
21
+ }
22
+
23
+ function decodeFile(encoded: Uint8Array): { exists: boolean; bytes: Uint8Array } {
24
+ const marker = encoded[0];
25
+ if (marker !== ABSENT && marker !== PRESENT) throw new Error('Invalid project-file snapshot.');
26
+ return { exists: marker === PRESENT, bytes: encoded.slice(1) };
27
+ }
28
+
29
+ async function captureFile(
30
+ backend: HistoryFileBackend,
31
+ path: string,
32
+ revision: number,
33
+ contentType: string,
34
+ ): Promise<ResourceSnapshot> {
35
+ const exists = await backend.exists(path);
36
+ const bytes = encodeFile(exists, exists ? await backend.readBytes(path) : undefined);
37
+ return { revision, contentType, bytes, sha256: await sha256Hex(bytes) };
38
+ }
39
+
40
+ interface ManagedFile {
41
+ descriptor: ResourceDescriptor;
42
+ revision: number;
43
+ contentType: string;
44
+ listeners: Set<() => void>;
45
+ }
46
+
47
+ export interface ProjectFileMutation {
48
+ readonly path: string;
49
+ readonly data: string | Uint8Array | null;
50
+ readonly kind?: ResourceKind;
51
+ readonly contentType?: string;
52
+ }
53
+
54
+ /** Exact-byte, existence-aware history for project files reached through StorageBackend. */
55
+ export class ProjectFileHistory {
56
+ private readonly files = new Map<string, ManagedFile>();
57
+ private readonly listeners = new Set<(path: string) => void>();
58
+
59
+ constructor(
60
+ private readonly history: HistoryService,
61
+ private readonly backend: HistoryFileBackend,
62
+ ) {}
63
+
64
+ write(
65
+ path: string,
66
+ data: string | Uint8Array,
67
+ options: { label: string; kind?: ResourceKind; contentType?: string },
68
+ ): Promise<boolean> {
69
+ const normalized = normalizeProjectPath(path);
70
+ const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : Uint8Array.from(data);
71
+ const file = this.file(
72
+ normalized,
73
+ options.kind ?? 'project-file',
74
+ options.contentType ?? (typeof data === 'string' ? 'text/plain' : 'application/octet-stream'),
75
+ );
76
+ return this.history.transaction(
77
+ { label: options.label, resources: [file.descriptor.key], scope: 'project' },
78
+ async (tx) => {
79
+ await tx.write(file.descriptor.key, encodeFile(true, bytes), file.contentType);
80
+ return true;
81
+ },
82
+ );
83
+ }
84
+
85
+ writeWithResources(
86
+ path: string,
87
+ data: string | Uint8Array,
88
+ options: { label: string; kind?: ResourceKind; contentType?: string },
89
+ additional: readonly {
90
+ resource: ResourceKey;
91
+ bytes: Uint8Array;
92
+ contentType: string;
93
+ }[],
94
+ ): Promise<boolean> {
95
+ const normalized = normalizeProjectPath(path);
96
+ const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : Uint8Array.from(data);
97
+ const file = this.file(
98
+ normalized,
99
+ options.kind ?? 'project-file',
100
+ options.contentType ?? (typeof data === 'string' ? 'text/plain' : 'application/octet-stream'),
101
+ );
102
+ return this.history.transaction(
103
+ {
104
+ label: options.label,
105
+ resources: [file.descriptor.key, ...additional.map((change) => change.resource)],
106
+ scope: 'project',
107
+ },
108
+ async (tx) => {
109
+ await tx.write(file.descriptor.key, encodeFile(true, bytes), file.contentType);
110
+ for (const change of additional) {
111
+ await tx.write(change.resource, change.bytes, change.contentType);
112
+ }
113
+ return true;
114
+ },
115
+ );
116
+ }
117
+
118
+ remove(
119
+ path: string,
120
+ options: { label: string; kind?: ResourceKind; contentType?: string },
121
+ ): Promise<boolean> {
122
+ const normalized = normalizeProjectPath(path);
123
+ const file = this.file(
124
+ normalized,
125
+ options.kind ?? 'project-file',
126
+ options.contentType ?? 'application/octet-stream',
127
+ );
128
+ return this.history.transaction(
129
+ { label: options.label, resources: [file.descriptor.key], scope: 'project' },
130
+ async (tx) => {
131
+ await tx.write(file.descriptor.key, encodeFile(false), file.contentType);
132
+ return true;
133
+ },
134
+ );
135
+ }
136
+
137
+ /** Apply a logical multi-file operation as one failure-atomic undo step. */
138
+ mutate(changes: readonly ProjectFileMutation[], options: { label: string }): Promise<boolean> {
139
+ const staged = changes.map((change) => {
140
+ const path = normalizeProjectPath(change.path);
141
+ const contentType = change.contentType ?? 'application/octet-stream';
142
+ return {
143
+ change,
144
+ file: this.file(path, change.kind ?? 'project-file', contentType),
145
+ contentType,
146
+ };
147
+ });
148
+ const uniqueResources = new Set(staged.map(({ file }) => file.descriptor.key));
149
+ if (uniqueResources.size !== staged.length) {
150
+ throw new Error('A project-file mutation cannot write the same path twice.');
151
+ }
152
+ return this.history.transaction(
153
+ { label: options.label, resources: [...uniqueResources], scope: 'project' },
154
+ async (tx) => {
155
+ for (const { change, file, contentType } of staged) {
156
+ const bytes =
157
+ change.data === null
158
+ ? encodeFile(false)
159
+ : encodeFile(
160
+ true,
161
+ typeof change.data === 'string'
162
+ ? new TextEncoder().encode(change.data)
163
+ : Uint8Array.from(change.data),
164
+ );
165
+ await tx.write(file.descriptor.key, bytes, contentType);
166
+ }
167
+ return true;
168
+ },
169
+ );
170
+ }
171
+
172
+ subscribe(path: string, listener: () => void): () => void {
173
+ const normalized = normalizeProjectPath(path);
174
+ const file = this.files.get(normalized);
175
+ if (!file) throw new Error(`Project file "${normalized}" is not registered with history.`);
176
+ file.listeners.add(listener);
177
+ return () => file.listeners.delete(listener);
178
+ }
179
+
180
+ subscribeAll(listener: (path: string) => void): () => void {
181
+ this.listeners.add(listener);
182
+ return () => this.listeners.delete(listener);
183
+ }
184
+
185
+ private file(path: string, kind: ResourceKind, contentType: string): ManagedFile {
186
+ const existing = this.files.get(path);
187
+ if (existing) {
188
+ if (existing.descriptor.kind !== kind) {
189
+ throw new Error(
190
+ `Project file "${path}" is already registered as ${existing.descriptor.kind}.`,
191
+ );
192
+ }
193
+ existing.contentType = contentType;
194
+ return existing;
195
+ }
196
+ const descriptor = this.history.registry.registerProject(kind, path);
197
+ const file: ManagedFile = {
198
+ descriptor,
199
+ revision: 0,
200
+ contentType,
201
+ listeners: new Set(),
202
+ };
203
+ const driver: ResourceDriver = {
204
+ descriptor,
205
+ capture: () => captureFile(this.backend, path, file.revision, file.contentType),
206
+ preflight: async (expected) => {
207
+ const current = await captureFile(this.backend, path, file.revision, file.contentType);
208
+ return current.sha256 === expected.sha256
209
+ ? { ok: true }
210
+ : {
211
+ ok: false,
212
+ reason: 'content-conflict',
213
+ actualRevision: file.revision,
214
+ actualSha256: current.sha256,
215
+ };
216
+ },
217
+ restore: async (snapshot) => {
218
+ const decoded = decodeFile(this.history.snapshots.read(snapshot).bytes);
219
+ if (decoded.exists) await this.backend.write(path, decoded.bytes);
220
+ else await this.backend.remove(path);
221
+ file.revision++;
222
+ for (const listener of file.listeners) listener();
223
+ for (const listener of this.listeners) listener(path);
224
+ },
225
+ estimateBytes: (snapshot) => snapshot.byteLength,
226
+ };
227
+ this.history.registerDriver(driver);
228
+ this.files.set(path, file);
229
+ return file;
230
+ }
231
+ }
232
+
233
+ export interface HistoryFileBackend {
234
+ readonly id: string;
235
+ readBytes(path: string): Promise<Uint8Array>;
236
+ write(path: string, data: string | Uint8Array): Promise<void>;
237
+ exists(path: string): Promise<boolean>;
238
+ remove(path: string): Promise<void>;
239
+ }
240
+
241
+ const managers = new WeakMap<
242
+ HistoryService,
243
+ Map<string, { backend: HistoryFileBackend; manager: ProjectFileHistory }>
244
+ >();
245
+
246
+ export function getProjectFileHistory(
247
+ history: HistoryService,
248
+ backend: HistoryFileBackend = getStorageBackend(),
249
+ namespace = 'public',
250
+ ): ProjectFileHistory {
251
+ let byNamespace = managers.get(history);
252
+ if (!byNamespace) {
253
+ byNamespace = new Map();
254
+ managers.set(history, byNamespace);
255
+ }
256
+ const existing = byNamespace.get(namespace);
257
+ if (existing) {
258
+ if (existing.backend !== backend) {
259
+ throw new Error('The project storage backend changed without creating a new editor session.');
260
+ }
261
+ return existing.manager;
262
+ }
263
+ const manager = new ProjectFileHistory(history, backend);
264
+ byNamespace.set(namespace, { backend, manager });
265
+ return manager;
266
+ }
@@ -0,0 +1,53 @@
1
+ import { getEditorMode } from '../editor-mode';
2
+ import { getStorageBackend } from '../storage';
3
+ import type { HistoryFileBackend } from './project-file-history';
4
+
5
+ class ScopedHttpHistoryBackend implements HistoryFileBackend {
6
+ constructor(
7
+ readonly id: string,
8
+ private readonly endpoint: string,
9
+ ) {}
10
+
11
+ async readBytes(path: string): Promise<Uint8Array> {
12
+ const response = await fetch(`${this.endpoint}?${new URLSearchParams({ path })}`);
13
+ if (!response.ok) throw new Error(`${this.id}.readBytes '${path}': HTTP ${response.status}`);
14
+ return new Uint8Array(await response.arrayBuffer());
15
+ }
16
+
17
+ async write(path: string, data: string | Uint8Array): Promise<void> {
18
+ const content = typeof data === 'string' ? data : new TextDecoder().decode(data);
19
+ const response = await fetch(this.endpoint, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ path, content }),
23
+ });
24
+ if (!response.ok) throw new Error(`${this.id}.write '${path}': HTTP ${response.status}`);
25
+ }
26
+
27
+ async exists(path: string): Promise<boolean> {
28
+ const response = await fetch(`${this.endpoint}?${new URLSearchParams({ path })}`);
29
+ if (response.status === 404) return false;
30
+ if (!response.ok) throw new Error(`${this.id}.exists '${path}': HTTP ${response.status}`);
31
+ return true;
32
+ }
33
+
34
+ async remove(path: string): Promise<void> {
35
+ const response = await fetch(this.endpoint, {
36
+ method: 'DELETE',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify({ path }),
39
+ });
40
+ if (!response.ok) throw new Error(`${this.id}.remove '${path}': HTTP ${response.status}`);
41
+ }
42
+ }
43
+
44
+ const dataHttp = new ScopedHttpHistoryBackend('project-data-http', '/__editor/data-file');
45
+ const manifestHttp = new ScopedHttpHistoryBackend('project-manifest-http', '/__editor/manifest');
46
+
47
+ export function getDataHistoryBackend(): HistoryFileBackend {
48
+ return getEditorMode() === 'browser' ? getStorageBackend() : dataHttp;
49
+ }
50
+
51
+ export function getManifestHistoryBackend(): HistoryFileBackend {
52
+ return getEditorMode() === 'browser' ? getStorageBackend() : manifestHttp;
53
+ }