@vgai/editor 0.2.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.
- package/LICENSE +661 -0
- package/README.md +35 -0
- package/dist/a/b/c/test-nested.json +1 -0
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
- package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
- package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
- package/dist/assets/browserAll-CEkWYlFk.js +1 -0
- package/dist/assets/collision-world-DniMB0qq.glb +0 -0
- package/dist/assets/create-runtime-DYIWr3va.js +1 -0
- package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
- package/dist/assets/esbuild-BHljloGq.wasm +0 -0
- package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
- package/dist/assets/game-8SDZPPgH.js +1 -0
- package/dist/assets/game-BI111o5f.js +10 -0
- package/dist/assets/game-CevorxyV.js +1 -0
- package/dist/assets/game-Di1H0gFj.js +1 -0
- package/dist/assets/game-Di7XlywD.js +1 -0
- package/dist/assets/game-DjnCaw8R.js +1 -0
- package/dist/assets/game-Q9A0aXSV.js +1 -0
- package/dist/assets/game-pt_1lQLv.js +1 -0
- package/dist/assets/game-source-CvidN7AB.js +1 -0
- package/dist/assets/game-xsOho_wg.js +83 -0
- package/dist/assets/index-Bp5cecD6.js +12 -0
- package/dist/assets/index-CPVV6pjw.js +1 -0
- package/dist/assets/index-CpOnYyeD.js +213 -0
- package/dist/assets/index-Cs8xwv3e.js +1 -0
- package/dist/assets/index-DL7RcQ0n.js +27 -0
- package/dist/assets/index-Dr8H_6Rl.js +1 -0
- package/dist/assets/index-_bCaipUk.js +13650 -0
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
- package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
- package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
- package/dist/assets/scene-query-BivAHhJu.js +1 -0
- package/dist/assets/stats.module--VATS4Kh.js +1 -0
- package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
- package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
- package/dist/index.html +37 -0
- package/dist/ingest/racing-game/UPSTREAM.md +68 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/index.css +1 -0
- package/dist/ingest/racing-game/images/bronze.png +0 -0
- package/dist/ingest/racing-game/images/gold.png +0 -0
- package/dist/ingest/racing-game/images/silver.png +0 -0
- package/dist/ingest/racing-game/index.html +15 -0
- package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
- package/dist/ingest/racing-game/models/track-draco.glb +0 -0
- package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
- package/dist/ingest/racing-game/rg-entry.js +1 -0
- package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
- package/dist/ingest/racing-game/textures/cursor.svg +3 -0
- package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
- package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
- package/dist/ingest/racing-game/textures/mask.svg +11 -0
- package/dist/ingest/racing-game/textures/no_sound.png +0 -0
- package/dist/ingest/racing-game/three-r139.module.js +50617 -0
- package/dist/ingest/racing-game/vendor.js +179 -0
- package/dist/ingest/racing-game/vgai.game.json +20 -0
- package/dist/ingest/simcity/UPSTREAM.md +65 -0
- package/dist/ingest/simcity/favicon.ico +0 -0
- package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
- package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
- package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
- package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
- package/dist/ingest/simcity/icons/bulldozer.png +0 -0
- package/dist/ingest/simcity/icons/calendar.png +0 -0
- package/dist/ingest/simcity/icons/factory-color.png +0 -0
- package/dist/ingest/simcity/icons/factory.png +0 -0
- package/dist/ingest/simcity/icons/house-color.png +0 -0
- package/dist/ingest/simcity/icons/house.png +0 -0
- package/dist/ingest/simcity/icons/job.png +0 -0
- package/dist/ingest/simcity/icons/office.png +0 -0
- package/dist/ingest/simcity/icons/pause-color.png +0 -0
- package/dist/ingest/simcity/icons/pause.png +0 -0
- package/dist/ingest/simcity/icons/person.png +0 -0
- package/dist/ingest/simcity/icons/play-color.png +0 -0
- package/dist/ingest/simcity/icons/play.png +0 -0
- package/dist/ingest/simcity/icons/power-color.png +0 -0
- package/dist/ingest/simcity/icons/power-line-color.png +0 -0
- package/dist/ingest/simcity/icons/power.png +0 -0
- package/dist/ingest/simcity/icons/road-color.png +0 -0
- package/dist/ingest/simcity/icons/road.png +0 -0
- package/dist/ingest/simcity/icons/select-color.png +0 -0
- package/dist/ingest/simcity/icons/select.png +0 -0
- package/dist/ingest/simcity/icons/store-color.png +0 -0
- package/dist/ingest/simcity/index.html +94 -0
- package/dist/ingest/simcity/index.js +58 -0
- package/dist/ingest/simcity/main.css +233 -0
- package/dist/ingest/simcity/models/armored-truck.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
- package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
- package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
- package/dist/ingest/simcity/models/building-cafe.glb +0 -0
- package/dist/ingest/simcity/models/building-casino.glb +0 -0
- package/dist/ingest/simcity/models/building-cinema.glb +0 -0
- package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
- package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
- package/dist/ingest/simcity/models/building-office-big.glb +0 -0
- package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
- package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
- package/dist/ingest/simcity/models/building-office.glb +0 -0
- package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
- package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
- package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
- package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
- package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger.glb +0 -0
- package/dist/ingest/simcity/models/car-police.glb +0 -0
- package/dist/ingest/simcity/models/car-taxi.glb +0 -0
- package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
- package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
- package/dist/ingest/simcity/models/car-veteran.glb +0 -0
- package/dist/ingest/simcity/models/construction-small.glb +0 -0
- package/dist/ingest/simcity/models/data-center.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory.glb +0 -0
- package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
- package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
- package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
- package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
- package/dist/ingest/simcity/models/truck.glb +0 -0
- package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
- package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
- package/dist/ingest/simcity/textures/base.png +0 -0
- package/dist/ingest/simcity/textures/grid.png +0 -0
- package/dist/ingest/simcity/textures/specular.png +0 -0
- package/dist/ingest/simcity/three-r155.module.js +51466 -0
- package/dist/ingest/simcity/vgai.game.json +20 -0
- package/dist/ingest/tanks/UPSTREAM.md +69 -0
- package/dist/ingest/tanks/index.html +214 -0
- package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
- package/dist/ingest/tanks/tanks-entry.js +92 -0
- package/dist/ingest/tanks/three-r170.module.js +54571 -0
- package/dist/ingest/tanks/vgai.game.json +20 -0
- package/dist/inputmaps/default.inputmap.json +41 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/dist/scenes/untitled.vscn.json +29 -0
- package/dist/textures/waternormals.jpg +0 -0
- package/dist-server/packaged.mjs +18155 -0
- package/package.json +36 -0
- package/server/asset-library-routes.ts +833 -0
- package/server/canonical-path.ts +50 -0
- package/server/cloud-asset-catalog.ts +101 -0
- package/server/dev.ts +350 -0
- package/server/editor-server.ts +1537 -0
- package/server/editor-sse.ts +73 -0
- package/server/game-globals-shadow.ts +58 -0
- package/server/local-asset-catalog.ts +163 -0
- package/server/packaged.ts +571 -0
- package/server/prod.ts +75 -0
- package/server/project-optimize-deps-entries.ts +149 -0
- package/server/project-view.ts +65 -0
- package/server/server-utils.ts +430 -0
- package/server/session-registry.ts +93 -0
- package/src/EditorContext.tsx +87 -0
- package/src/achieved-tier.ts +126 -0
- package/src/action-registry.ts +268 -0
- package/src/adapter-resolver.ts +1858 -0
- package/src/asset-events.ts +100 -0
- package/src/authoring/active-adapter.ts +98 -0
- package/src/authoring/active-systems.ts +50 -0
- package/src/authoring/boundary-authoring-adapter.ts +178 -0
- package/src/authoring/composite-authoring-adapter.ts +891 -0
- package/src/authoring/design-time-layers.ts +776 -0
- package/src/authoring/edit-mode-authoring.ts +643 -0
- package/src/authoring/embed-only-adapter.ts +57 -0
- package/src/authoring/ephemeral-persistence.ts +26 -0
- package/src/authoring/eyedropper-session.ts +69 -0
- package/src/authoring/first-party-hierarchy-menu.ts +74 -0
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
- package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
- package/src/authoring/first-party-inspector/register.tsx +108 -0
- package/src/authoring/ingest-authoring-adapter.ts +475 -0
- package/src/authoring/ingest-capture.ts +94 -0
- package/src/authoring/ingest-game-adapter.ts +291 -0
- package/src/authoring/ingest-iframe-adapter.ts +38 -0
- package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
- package/src/authoring/ingest-overlay.ts +193 -0
- package/src/authoring/layered-pick.ts +87 -0
- package/src/authoring/mount-failure-report.ts +112 -0
- package/src/authoring/overlay-report.ts +52 -0
- package/src/authoring/project-scenes.ts +111 -0
- package/src/authoring/react-dom-authoring-adapter.ts +795 -0
- package/src/authoring/react-inspector-section.tsx +1041 -0
- package/src/authoring/react-world-authoring-adapter.ts +2400 -0
- package/src/authoring/root-activation.ts +61 -0
- package/src/authoring/scene-ui-layer.ts +485 -0
- package/src/authoring/story-overlay.ts +48 -0
- package/src/authoring/ui-authoring-adapter.ts +325 -0
- package/src/authoring/ui-hierarchy-menu.ts +40 -0
- package/src/authoring/ui-inspector-sections.tsx +156 -0
- package/src/authoring/undo-timeline.ts +145 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
- package/src/authoring/viewport-pick-context.ts +31 -0
- package/src/authoring/viewport-raycast.ts +80 -0
- package/src/authoring/world-overlay.ts +76 -0
- package/src/authoring/world-pan-state.ts +115 -0
- package/src/authoring/world-session-state.ts +102 -0
- package/src/authoring/world2d-authoring-adapter.ts +313 -0
- package/src/browser-external-watch.ts +98 -0
- package/src/browser-play-entry.ts +112 -0
- package/src/browser-project-scripts.ts +112 -0
- package/src/browser-transpile.ts +246 -0
- package/src/browser-watch-reload.ts +100 -0
- package/src/command-listener.ts +326 -0
- package/src/components/AlignToolbar.tsx +189 -0
- package/src/components/AppRoot.tsx +73 -0
- package/src/components/AssetBrowser.tsx +502 -0
- package/src/components/BottomPanel.tsx +326 -0
- package/src/components/BuildPanel.tsx +455 -0
- package/src/components/CameraInfo.tsx +56 -0
- package/src/components/CommandPalette.tsx +265 -0
- package/src/components/ConsolePanel.tsx +272 -0
- package/src/components/DataPanel.tsx +1268 -0
- package/src/components/DefaultEditorLayout.tsx +509 -0
- package/src/components/FolderTree.tsx +179 -0
- package/src/components/GameHierarchy.tsx +1349 -0
- package/src/components/Inspector.tsx +671 -0
- package/src/components/InspectorToolSection.tsx +74 -0
- package/src/components/MountFailureBanner.tsx +110 -0
- package/src/components/NetworkSection.tsx +109 -0
- package/src/components/OnlineAssetBrowser.tsx +448 -0
- package/src/components/OverlayReportBanner.tsx +122 -0
- package/src/components/PlayBar.tsx +356 -0
- package/src/components/PrefabSaveDialog.tsx +201 -0
- package/src/components/ProjectHeader.tsx +174 -0
- package/src/components/ProjectScreen.tsx +1004 -0
- package/src/components/RightPanel.tsx +166 -0
- package/src/components/RootActivateDialog.tsx +105 -0
- package/src/components/SaveDialog.tsx +197 -0
- package/src/components/SaveStatus.tsx +69 -0
- package/src/components/StatsOverlay.tsx +35 -0
- package/src/components/ToolHost.tsx +80 -0
- package/src/components/Toolbar.tsx +307 -0
- package/src/components/ViewportOverlay.tsx +234 -0
- package/src/components/ViewportPanel.tsx +369 -0
- package/src/components/WorldContextMenu.tsx +257 -0
- package/src/components/WorldSelectionOverlay.tsx +2003 -0
- package/src/components/WorldTextEditor.tsx +86 -0
- package/src/components/asset-viewers/AssetViewer.tsx +38 -0
- package/src/components/asset-viewers/AudioViewer.tsx +149 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
- package/src/components/asset-viewers/ImageViewer.tsx +65 -0
- package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
- package/src/components/asset-viewers/ModelViewer.tsx +180 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
- package/src/components/data-csv.ts +301 -0
- package/src/components/entity-name-commit.ts +18 -0
- package/src/components/inspector-property-grouping.ts +65 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
- package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
- package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
- package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/components/inspector-widgets/FontPicker.tsx +196 -0
- package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
- package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
- package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
- package/src/components/inspector-widgets/color-utils.ts +202 -0
- package/src/components/inspector-widgets/gallery-main.tsx +167 -0
- package/src/components/inspector-widgets/index.ts +22 -0
- package/src/components/inspector-widgets/shared.tsx +135 -0
- package/src/components/inspectors/AnimationSection.tsx +143 -0
- package/src/components/inspectors/AudioSection.tsx +215 -0
- package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
- package/src/components/inspectors/CameraSection.tsx +105 -0
- package/src/components/inspectors/ComponentsSection.tsx +358 -0
- package/src/components/inspectors/JointsSection.tsx +255 -0
- package/src/components/inspectors/LightSection.tsx +116 -0
- package/src/components/inspectors/MaterialSection.tsx +1189 -0
- package/src/components/inspectors/MeshSection.tsx +99 -0
- package/src/components/inspectors/NavigationSection.tsx +58 -0
- package/src/components/inspectors/ParticleSection.tsx +681 -0
- package/src/components/inspectors/PhysicsSection.tsx +326 -0
- package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
- package/src/components/inspectors/RenderingSection.tsx +109 -0
- package/src/components/inspectors/ShadowSection.tsx +84 -0
- package/src/components/inspectors/SplineSection.tsx +191 -0
- package/src/components/inspectors/TransformSection.tsx +191 -0
- package/src/components/inspectors/json-schema-fields.ts +281 -0
- package/src/components/inspectors/multi-edit-utils.ts +49 -0
- package/src/components/inspectors/zod-fields.ts +184 -0
- package/src/components/primitives/ColorInput.tsx +109 -0
- package/src/components/primitives/JsonInput.tsx +98 -0
- package/src/components/primitives/NumberInput.tsx +164 -0
- package/src/components/primitives/Panel.tsx +68 -0
- package/src/components/primitives/SectionHeader.tsx +45 -0
- package/src/components/primitives/Tooltip.tsx +82 -0
- package/src/components/primitives/Vec3Input.tsx +62 -0
- package/src/components/world-overlay-gestures.ts +1438 -0
- package/src/doctor/data-checks.ts +285 -0
- package/src/doctor/report.ts +386 -0
- package/src/editor-api.ts +700 -0
- package/src/editor-console.ts +194 -0
- package/src/editor-hotkeys.ts +441 -0
- package/src/editor-layers.ts +2 -0
- package/src/editor-mode.ts +28 -0
- package/src/editor-store.ts +2225 -0
- package/src/editor-viewport.ts +1924 -0
- package/src/entity-factory.ts +221 -0
- package/src/game-globals-prelude.ts +27 -0
- package/src/gated-globals.ts +131 -0
- package/src/generated/setup-by-example.generated.ts +53 -0
- package/src/gizmo-registry.ts +608 -0
- package/src/handle-system.ts +221 -0
- package/src/hierarchy-menu-registry.ts +70 -0
- package/src/hierarchy-rows.ts +73 -0
- package/src/hosted-example.ts +160 -0
- package/src/hotkeys.ts +142 -0
- package/src/ingest/discovery-react.ts +132 -0
- package/src/ingest/discovery.ts +205 -0
- package/src/ingest/discovery2d.ts +204 -0
- package/src/ingest/esm-entry-resolution.ts +102 -0
- package/src/ingest/games/bloom-composer/game.js +99 -0
- package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
- package/src/ingest/games/embed-bundle/embed.html +10 -0
- package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
- package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
- package/src/ingest/games/games-fps/game.js +479 -0
- package/src/ingest/games/games-fps/vgai.game.json +24 -0
- package/src/ingest/games/iframe-reachable/game-source.js +58 -0
- package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
- package/src/ingest/games/interactive-cubes/game.js +139 -0
- package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
- package/src/ingest/games/mismatched-three/fake-three.js +38 -0
- package/src/ingest/games/mismatched-three/game.js +10 -0
- package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
- package/src/ingest/games/own-three-bundled/game.js +43 -0
- package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
- package/src/ingest/games/pointerlock-maze/game.js +288 -0
- package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
- package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
- package/src/ingest/games-2d/bunnymark/game.ts +88 -0
- package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy/game.ts +148 -0
- package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
- package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
- package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
- package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
- package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
- package/src/ingest/host-namespace-registry.ts +196 -0
- package/src/ingest/types.ts +127 -0
- package/src/ingest-hierarchy-rows.ts +207 -0
- package/src/ingest-mode.ts +2092 -0
- package/src/ingest-siblings.ts +466 -0
- package/src/initial-scene.ts +194 -0
- package/src/inspector-section-registry.ts +117 -0
- package/src/interactive-edit-scope.ts +34 -0
- package/src/main.tsx +23 -0
- package/src/manifest-project.ts +43 -0
- package/src/model-thumbnail.ts +156 -0
- package/src/module-mode.ts +513 -0
- package/src/navmesh-handler.ts +94 -0
- package/src/packaged-runtime.ts +49 -0
- package/src/play-mode.ts +1148 -0
- package/src/playground/main.ts +206 -0
- package/src/project-manager.ts +173 -0
- package/src/scene-diff.ts +112 -0
- package/src/scene-serializer.ts +144 -0
- package/src/scene-sync.ts +587 -0
- package/src/scene-ui-mode/active-scene-ui.ts +58 -0
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
- package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
- package/src/script-loader.ts +143 -0
- package/src/storage/fsa-storage.ts +55 -0
- package/src/storage/handle-storage.ts +150 -0
- package/src/storage/handle-store.ts +116 -0
- package/src/storage/http-storage.ts +137 -0
- package/src/storage/index.ts +147 -0
- package/src/storage/mem-storage.ts +135 -0
- package/src/storage/opfs-storage.ts +25 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/seed.ts +106 -0
- package/src/storage/types.ts +110 -0
- package/src/tool-loader.ts +291 -0
- package/src/ui-editor/inspector.tsx +563 -0
- package/src/ui-editor/node-ops.ts +266 -0
- package/src/ui-editor/overlay.tsx +380 -0
- package/src/ui-editor/palette.tsx +114 -0
- package/src/ui-editor/react-store.ts +78 -0
- package/src/ui-editor/timeline-panel.tsx +443 -0
- package/src/ui-editor/ui-edit-store.ts +552 -0
- package/src/ui-editor/ui-prefab.ts +95 -0
- package/src/ui-source/inspect.ts +544 -0
- package/src/ui-source/oid-transform.ts +133 -0
- package/src/ui-source/source-write-backend.ts +203 -0
- package/src/ui-source/tailwind-map.ts +174 -0
- package/src/ui-source/writer.ts +1175 -0
- package/src/widgets/index.ts +73 -0
- package/template/.claude/skills/editor/SKILL.md +252 -0
- package/template/CLAUDE.md +512 -0
- package/template/LICENSE +30 -0
- package/template/emit-schemas.ts +42 -0
- package/template/index.html +17 -0
- package/template/package.json +53 -0
- package/template/project.json +9 -0
- package/template/public/audio/ambient-arena.wav +0 -0
- package/template/public/audio/ambient-music.wav +0 -0
- package/template/public/audio/ambient-nature.wav +0 -0
- package/template/public/audio/ambient-wind.wav +0 -0
- package/template/public/inputmaps/default.inputmap.json +41 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/template/public/scenes/default.vscn.json +43 -0
- package/template/public/scenes/untitled.vscn.json +29 -0
- package/template/public/textures/waternormals.jpg +0 -0
- package/template/server/colyseus-setup.ts +85 -0
- package/template/server/main.ts +34 -0
- package/template/server/rooms/arena-room.ts +420 -0
- package/template/server/rooms/game-room.ts +162 -0
- package/template/server/rooms.ts +15 -0
- package/template/server/tsconfig.json +19 -0
- package/template/src/data/README.md +40 -0
- package/template/src/data/assets.ts +31 -0
- package/template/src/data/tuning.data.json +5 -0
- package/template/src/data/tuning.schema.json +21 -0
- package/template/src/data/tuning.schema.ts +34 -0
- package/template/src/data/tuning.ts +24 -0
- package/template/src/main.ts +157 -0
- package/template/src/scripts/components/data-spinner.ts +45 -0
- package/template/src/scripts/components/scene-camera.ts +40 -0
- package/template/src/scripts/main.ts +94 -0
- package/template/src/scripts/registry.ts +46 -0
- package/template/src/tools/README.md +14 -0
- package/template/src/tools/example.tool.tsx +91 -0
- package/template/src/tools/spin.tool.tsx +101 -0
- package/template/src/ui/Button.tsx +27 -0
- package/template/src/ui/DefaultHud.tsx +40 -0
- package/template/src/ui/KeyHint.tsx +25 -0
- package/template/src/ui/Panel.tsx +52 -0
- package/template/src/ui/RebindPanel.tsx +129 -0
- package/template/src/ui/Slider.tsx +28 -0
- package/template/src/ui/StatusDot.tsx +22 -0
- package/template/src/ui/Toggle.tsx +19 -0
- package/template/src/ui/game-state.tsx +37 -0
- package/template/src/ui/index.ts +7 -0
- package/template/src/ui/mount-default-hud.tsx +19 -0
- package/template/src/ui/mount-ui.ts +15 -0
- package/template/tsconfig.json +36 -0
- package/template/validate-asset-content.ts +78 -0
- package/template/validate-manifest.ts +55 -0
- package/template/validate-scenes.ts +211 -0
- package/template/vgai.game.json +21 -0
- package/template/vite.config.ts +128 -0
- package/vite-plugin-example-assets.ts +60 -0
- package/vite-plugin-game-globals.ts +46 -0
- package/vite-plugin-game-static.ts +71 -0
- package/vite-plugin-project-game-static.ts +84 -0
- package/vite-plugin-react-game-static.ts +98 -0
- package/vite-plugin-react-world-provider.ts +81 -0
- package/vite-plugin-react-world-runtime.ts +76 -0
- package/vite-plugin-ui-oid.ts +561 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1.a (spec-27 §7 E1 / §9 "UIEditor chrome, palette, … MIGRATE" row) — the
|
|
3
|
+
* UI-node creation palette, surfaced in the MAIN editor (`editor.html`) for
|
|
4
|
+
* whichever world's scene-UI child is currently live. Reuses `ui-editor/
|
|
5
|
+
* palette.tsx`'s `UIPalette` (the harness component) UNCHANGED — its only
|
|
6
|
+
* prop is `store: UIEditStore`, so pointing it at the SAME `UIEditStore`
|
|
7
|
+
* instance `edit-mode-authoring.ts`'s `buildUIChild` constructed for the open
|
|
8
|
+
* scene wires creation through the real undo timeline (`UIEditStore`'s
|
|
9
|
+
* constructor already calls `registerUndoAdapter(this)`) and the real
|
|
10
|
+
* `.vscn` autosave (`onCommit` → `EditorStore.setSceneUI`) for free — no new
|
|
11
|
+
* store, no new undo plumbing.
|
|
12
|
+
*
|
|
13
|
+
* Also carries the E1.c "instantiate a saved prefab" surface (§8 "Scene-UI
|
|
14
|
+
* extras: … prefabs"): a list of `.uiprefab.json` assets under `ui-prefabs/`
|
|
15
|
+
* with a one-click instantiate into the current selection (or the first
|
|
16
|
+
* root) — the SAME "target the selected container, else first root" rule
|
|
17
|
+
* `scene-ui-layer.ts`'s registry component-insert strip (`componentStrip`)
|
|
18
|
+
* uses: a selection that isn't a container falls straight through to the
|
|
19
|
+
* first root. This is NOT the palette's own `add()` (`ui-editor/palette.tsx`),
|
|
20
|
+
* which has an extra middle tier — a selection that isn't a container falls
|
|
21
|
+
* back to that selection's PARENT first, and only THEN to the first root.
|
|
22
|
+
* (The SAVE half of the prefab flow is the hierarchy context-menu item,
|
|
23
|
+
* `ui-prefab-hierarchy-menu.ts` + `ui-prefab-dialog-host.tsx`.)
|
|
24
|
+
*
|
|
25
|
+
* Renders nothing when no scene-UI child is installed (§3.1's install gate —
|
|
26
|
+
* a ui-less scene has nothing to author here yet; see `ui-hierarchy-menu.ts`'s
|
|
27
|
+
* "Add UI Canvas" action for how a scene gets its first one).
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { useEffect, useState } from 'react';
|
|
31
|
+
import type { UIAuthoringAdapter } from '../authoring/ui-authoring-adapter';
|
|
32
|
+
import { Panel } from '../components/primitives/Panel';
|
|
33
|
+
import { findNode } from '../ui-editor/node-ops';
|
|
34
|
+
import { UIPalette } from '../ui-editor/palette';
|
|
35
|
+
import { instantiateUIPrefab } from '../ui-editor/ui-prefab';
|
|
36
|
+
import { useActiveSceneUIAdapter } from './active-scene-ui';
|
|
37
|
+
import {
|
|
38
|
+
fetchUIPrefabFile,
|
|
39
|
+
listUIPrefabAssets,
|
|
40
|
+
UI_PREFAB_SAVED_EVENT,
|
|
41
|
+
type UIPrefabAssetEntry,
|
|
42
|
+
} from './ui-prefab-io';
|
|
43
|
+
|
|
44
|
+
/** Mirrors `ui-editor/palette.tsx`'s own unexported `isContainer` / the
|
|
45
|
+
* identical set in `authoring/scene-ui-layer.ts` — an instantiated prefab
|
|
46
|
+
* nests under the selected container, else the first root. */
|
|
47
|
+
const CONTAINER_KINDS = new Set([
|
|
48
|
+
'canvas',
|
|
49
|
+
'box',
|
|
50
|
+
'grid',
|
|
51
|
+
'stack',
|
|
52
|
+
'scroll',
|
|
53
|
+
'panel',
|
|
54
|
+
'center',
|
|
55
|
+
'wrap',
|
|
56
|
+
'aspect',
|
|
57
|
+
'sizebox',
|
|
58
|
+
'tab',
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export function SceneUIPalettePanel(): React.ReactNode {
|
|
62
|
+
const adapter = useActiveSceneUIAdapter();
|
|
63
|
+
if (!adapter) return null;
|
|
64
|
+
return (
|
|
65
|
+
<Panel
|
|
66
|
+
name="UI Palette"
|
|
67
|
+
data-testid="scene-ui-palette-panel"
|
|
68
|
+
style={{ width: 200, borderRight: '1px solid #333', overflowY: 'auto' }}
|
|
69
|
+
>
|
|
70
|
+
<UIPalette store={adapter.uiStore} />
|
|
71
|
+
<ScenePrefabsSection adapter={adapter} />
|
|
72
|
+
</Panel>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function ScenePrefabsSection({ adapter }: { adapter: UIAuthoringAdapter }): React.ReactNode {
|
|
77
|
+
const [prefabs, setPrefabs] = useState<UIPrefabAssetEntry[]>([]);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
let cancelled = false;
|
|
81
|
+
const refresh = () => {
|
|
82
|
+
void listUIPrefabAssets().then((list) => {
|
|
83
|
+
if (!cancelled) setPrefabs(list);
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
refresh();
|
|
87
|
+
window.addEventListener(UI_PREFAB_SAVED_EVENT, refresh);
|
|
88
|
+
return () => {
|
|
89
|
+
cancelled = true;
|
|
90
|
+
window.removeEventListener(UI_PREFAB_SAVED_EVENT, refresh);
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const store = adapter.uiStore;
|
|
95
|
+
|
|
96
|
+
const instantiate = async (entry: UIPrefabAssetEntry): Promise<void> => {
|
|
97
|
+
const prefab = await fetchUIPrefabFile(entry.path);
|
|
98
|
+
if (!prefab) return;
|
|
99
|
+
const instance = instantiateUIPrefab(prefab);
|
|
100
|
+
const primary = store.primary;
|
|
101
|
+
const primaryLoc = primary ? findNode(store.roots, primary) : null;
|
|
102
|
+
const parentId =
|
|
103
|
+
primaryLoc && CONTAINER_KINDS.has(primaryLoc.node.kind)
|
|
104
|
+
? primary!
|
|
105
|
+
: ((store.roots[0]?.id as string | undefined) ?? null);
|
|
106
|
+
if (parentId) store.addNodeValue(parentId, instance);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div data-testid="scene-ui-prefabs" style={{ padding: 8, fontFamily: 'monospace' }}>
|
|
111
|
+
<div
|
|
112
|
+
style={{
|
|
113
|
+
fontSize: 10,
|
|
114
|
+
textTransform: 'uppercase',
|
|
115
|
+
color: '#888',
|
|
116
|
+
margin: '4px 0',
|
|
117
|
+
letterSpacing: 0.5,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
Prefabs
|
|
121
|
+
</div>
|
|
122
|
+
{prefabs.length === 0 && (
|
|
123
|
+
<div style={{ color: '#666', fontSize: 10 }}>
|
|
124
|
+
none yet — right-click a node → "Save UI Node as Prefab..."
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
|
|
128
|
+
{prefabs.map((entry) => (
|
|
129
|
+
<button
|
|
130
|
+
key={entry.path}
|
|
131
|
+
type="button"
|
|
132
|
+
data-testid={`ui-prefab-instantiate-${entry.name}`}
|
|
133
|
+
title={`instantiate ${entry.name}`}
|
|
134
|
+
onClick={() => void instantiate(entry)}
|
|
135
|
+
style={{
|
|
136
|
+
background: '#262626',
|
|
137
|
+
color: '#ccc',
|
|
138
|
+
border: '1px solid #3a3a3a',
|
|
139
|
+
borderRadius: 3,
|
|
140
|
+
padding: '3px 7px',
|
|
141
|
+
fontSize: 11,
|
|
142
|
+
cursor: 'pointer',
|
|
143
|
+
fontFamily: 'monospace',
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{entry.name}
|
|
147
|
+
</button>
|
|
148
|
+
))}
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1.b (spec-27 §7 E1 / §9 "…, timeline, …" MIGRATE row) — the keyframe/dope-
|
|
3
|
+
* sheet editor (`ui-editor/timeline-panel.tsx`'s `UITimeline`, reused
|
|
4
|
+
* UNCHANGED) mounted as a dockable bottom-panel tab in the unified editor
|
|
5
|
+
* (`components/BottomPanel.tsx`), for the currently active scene-UI child.
|
|
6
|
+
*
|
|
7
|
+
* Shares the SAME `UIEditStore` (so keyframe/track/clip edits are undoable +
|
|
8
|
+
* autosaved through the exact path the palette/inspector use) AND the SAME
|
|
9
|
+
* `UIAnimationController` the design-time stage plays back against
|
|
10
|
+
* (`UIAuthoringAdapter.anim` — see that field's doc comment for why the
|
|
11
|
+
* stage and the timeline can't each own their own controller instance:
|
|
12
|
+
* scrubbing here must move the SAME preview the stage renders).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { UITimeline } from '../ui-editor/timeline-panel';
|
|
16
|
+
import { useActiveSceneUIAdapter } from './active-scene-ui';
|
|
17
|
+
|
|
18
|
+
export function SceneUITimelineTab(): React.ReactNode {
|
|
19
|
+
const adapter = useActiveSceneUIAdapter();
|
|
20
|
+
if (!adapter) {
|
|
21
|
+
return (
|
|
22
|
+
<div style={{ padding: 12, color: '#777', fontSize: 12, fontFamily: 'monospace' }}>
|
|
23
|
+
No scene-UI world is open in this project — right-click a scene root in the hierarchy and
|
|
24
|
+
choose "Add UI Canvas" to author one.
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return <UITimeline store={adapter.uiStore} anim={adapter.anim} />;
|
|
29
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1.c (spec-27 §7 E1) — the "Save UI Node as Prefab..." dialog, mounted once
|
|
3
|
+
* (see `components/DefaultEditorLayout.tsx`) alongside the 3D
|
|
4
|
+
* `PrefabSaveDialog` it shares its component with (parameterized — `extension`/
|
|
5
|
+
* `defaultFolder`/`title`, see that file). Listens for
|
|
6
|
+
* `ui-prefab-hierarchy-menu.ts`'s `UI_SAVE_AS_PREFAB_EVENT`, extracts the
|
|
7
|
+
* chosen node from the ACTIVE scene-UI child's live tree at save time (not at
|
|
8
|
+
* event-dispatch time — the tree may have changed since the context-menu
|
|
9
|
+
* click), and writes a `.uiprefab.json` via `ui-prefab-io.ts`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
13
|
+
import { PrefabSaveDialog } from '../components/PrefabSaveDialog';
|
|
14
|
+
import { findNode } from '../ui-editor/node-ops';
|
|
15
|
+
import { resolveActiveSceneUIAdapter } from './active-scene-ui';
|
|
16
|
+
import { UI_SAVE_AS_PREFAB_EVENT, type UISaveAsPrefabDetail } from './ui-prefab-hierarchy-menu';
|
|
17
|
+
import {
|
|
18
|
+
buildUIPrefabFile,
|
|
19
|
+
saveUIPrefabToServer,
|
|
20
|
+
UI_PREFAB_DIR,
|
|
21
|
+
UI_PREFAB_EXTENSION,
|
|
22
|
+
UI_PREFAB_SAVED_EVENT,
|
|
23
|
+
} from './ui-prefab-io';
|
|
24
|
+
|
|
25
|
+
/** `a-z0-9-` filename hint from a node's display name — purely cosmetic
|
|
26
|
+
* (the folder/filename fields stay freely editable), mirrors the 3D dialog's
|
|
27
|
+
* own `entity.name` default-name usage (`DefaultEditorLayout.tsx`). */
|
|
28
|
+
function slugify(name: string): string {
|
|
29
|
+
const slug = name
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
32
|
+
.replace(/^-+|-+$/g, '');
|
|
33
|
+
return slug || 'ui-prefab';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function UIPrefabDialogHost(): React.ReactNode {
|
|
37
|
+
const [pending, setPending] = useState<UISaveAsPrefabDetail | null>(null);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const handler = (e: Event) => {
|
|
41
|
+
setPending((e as CustomEvent<UISaveAsPrefabDetail>).detail);
|
|
42
|
+
};
|
|
43
|
+
window.addEventListener(UI_SAVE_AS_PREFAB_EVENT, handler);
|
|
44
|
+
return () => window.removeEventListener(UI_SAVE_AS_PREFAB_EVENT, handler);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
const handleSave = useCallback(
|
|
48
|
+
(relPath: string) => {
|
|
49
|
+
const detail = pending;
|
|
50
|
+
setPending(null);
|
|
51
|
+
if (!detail) return;
|
|
52
|
+
const adapter = resolveActiveSceneUIAdapter();
|
|
53
|
+
const loc = adapter ? findNode(adapter.uiStore.roots, detail.nodeId) : null;
|
|
54
|
+
if (!loc) return;
|
|
55
|
+
const prefab = buildUIPrefabFile(detail.name, loc.node);
|
|
56
|
+
void saveUIPrefabToServer(prefab, relPath).then((ok) => {
|
|
57
|
+
if (ok) window.dispatchEvent(new CustomEvent(UI_PREFAB_SAVED_EVENT));
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
[pending],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (!pending) return null;
|
|
64
|
+
return (
|
|
65
|
+
<PrefabSaveDialog
|
|
66
|
+
defaultName={slugify(pending.name)}
|
|
67
|
+
extension={UI_PREFAB_EXTENSION}
|
|
68
|
+
defaultFolder={UI_PREFAB_DIR}
|
|
69
|
+
title="Save UI Node as Prefab"
|
|
70
|
+
onSave={handleSave}
|
|
71
|
+
onCancel={() => setPending(null)}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Save UI Node as Prefab..." hierarchy context-menu item (E1.c, spec-27 §7
|
|
3
|
+
* E1) — the scene-UI counterpart of `authoring/first-party-hierarchy-menu.ts`'s
|
|
4
|
+
* 3D prefab Save-as-Prefab action, registered through the SAME permanent
|
|
5
|
+
* escape hatch (`hierarchy-menu-registry.ts`, C2-blessed): `GameHierarchy`
|
|
6
|
+
* (the contract-only shell) never learns a UI tree exists, it only renders
|
|
7
|
+
* whatever this registration contributes for a matched row.
|
|
8
|
+
*
|
|
9
|
+
* Scoped to a node the ACTIVE scene-UI child's `UIEditStore` actually owns
|
|
10
|
+
* (`resolveActiveSceneUIAdapter` + `findNode`) — never matches a 3D entity or
|
|
11
|
+
* a foreign ingest/boundary node, and never fires when no scene-UI child is
|
|
12
|
+
* installed at all.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { registerHierarchyMenuItems } from '../hierarchy-menu-registry';
|
|
16
|
+
import { findNode } from '../ui-editor/node-ops';
|
|
17
|
+
import { resolveActiveSceneUIAdapter } from './active-scene-ui';
|
|
18
|
+
|
|
19
|
+
/** Dispatched (on `window`) when the user picks "Save UI Node as Prefab..." —
|
|
20
|
+
* `scene-ui-mode/ui-prefab-dialog-host.tsx` is the one listener, mirroring
|
|
21
|
+
* `first-party-hierarchy-menu.ts`'s `editor:save-as-prefab` convention. */
|
|
22
|
+
export const UI_SAVE_AS_PREFAB_EVENT = 'editor:ui-save-as-prefab';
|
|
23
|
+
|
|
24
|
+
export interface UISaveAsPrefabDetail {
|
|
25
|
+
readonly nodeId: string;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
registerHierarchyMenuItems(
|
|
30
|
+
(ctx) => {
|
|
31
|
+
const adapter = resolveActiveSceneUIAdapter();
|
|
32
|
+
return !!adapter && !!findNode(adapter.uiStore.roots, ctx.nodeId);
|
|
33
|
+
},
|
|
34
|
+
(ctx) => {
|
|
35
|
+
const adapter = resolveActiveSceneUIAdapter();
|
|
36
|
+
const loc = adapter ? findNode(adapter.uiStore.roots, ctx.nodeId) : null;
|
|
37
|
+
if (!loc) return [];
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
label: 'Save UI Node as Prefab...',
|
|
41
|
+
color: '#60a5fa',
|
|
42
|
+
action: () => {
|
|
43
|
+
const detail: UISaveAsPrefabDetail = { nodeId: ctx.nodeId, name: loc.node.name };
|
|
44
|
+
window.dispatchEvent(new CustomEvent(UI_SAVE_AS_PREFAB_EVENT, { detail }));
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
},
|
|
49
|
+
);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E1.c (spec-27 §7 E1 / §8 "Scene-UI extras: prefabs" / §9 `ui-prefab.ts`
|
|
3
|
+
* MIGRATE row) — the save/list/fetch I/O the pure `ui-editor/ui-prefab.ts`
|
|
4
|
+
* functions (`instantiateUIPrefab`, unit-tested, no UI surface) never got: the
|
|
5
|
+
* harness exposed `instantiateUIPrefab` on `window.__uiRuntime` for e2e/proof
|
|
6
|
+
* scripts only (`ui-editor/harness.tsx`) — no author-facing save/instantiate
|
|
7
|
+
* flow existed anywhere before this unit. Mirrors the 3D prefab I/O
|
|
8
|
+
* (`components/DefaultEditorLayout.tsx`'s `savePrefabToServer`/
|
|
9
|
+
* `fetchPrefabFile`) but for `.uiprefab.json` (`UIPrefabFileSchema`,
|
|
10
|
+
* `packages/engine/src/scene/schema/ui.ts:590`) under a sibling `ui-prefabs/`
|
|
11
|
+
* folder convention (the 3D convention is `prefabs/`, `PrefabSaveDialog.tsx`'s
|
|
12
|
+
* default folder).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { UINode } from '@engine/scene/schema';
|
|
16
|
+
import { type UIPrefabFile, UIPrefabFileSchema } from '@engine/scene/schema';
|
|
17
|
+
import { listAssets, readProjectTextFile, saveFile } from '../editor-api';
|
|
18
|
+
|
|
19
|
+
/** Default folder a UI prefab saves to / is listed from — sibling of the 3D
|
|
20
|
+
* `prefabs/` convention, never mixed with it (different file schema). */
|
|
21
|
+
export const UI_PREFAB_DIR = 'ui-prefabs';
|
|
22
|
+
export const UI_PREFAB_EXTENSION = 'uiprefab.json';
|
|
23
|
+
|
|
24
|
+
/** Validate a raw parsed JSON object as a `.uiprefab.json` file. Throws a
|
|
25
|
+
* `ZodError` on an invalid shape — callers decide how to surface that. */
|
|
26
|
+
export function parseUIPrefabFile(json: unknown): UIPrefabFile {
|
|
27
|
+
return UIPrefabFileSchema.parse(json);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Build a fresh (version 1) `UIPrefabFile` around a UI node subtree. The
|
|
31
|
+
* node's OWN ids are kept as authored — `instantiateUIPrefab` (`ui-prefab.ts`)
|
|
32
|
+
* is what regenerates fresh ids, at INSTANTIATE time, not save time. */
|
|
33
|
+
export function buildUIPrefabFile(name: string, root: UINode): UIPrefabFile {
|
|
34
|
+
return { version: 1, name, root };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Save a `.uiprefab.json` to disk via the editor server API (same
|
|
38
|
+
* storage-backend seam every other project file write uses). */
|
|
39
|
+
export async function saveUIPrefabToServer(prefab: UIPrefabFile, path: string): Promise<boolean> {
|
|
40
|
+
return saveFile(path, JSON.stringify(prefab, null, 2));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Fetch + validate a `.uiprefab.json` for an instantiate action. `null` on a
|
|
44
|
+
* missing file OR one that fails schema validation (never throws). */
|
|
45
|
+
export async function fetchUIPrefabFile(path: string): Promise<UIPrefabFile | null> {
|
|
46
|
+
const text = await readProjectTextFile(path);
|
|
47
|
+
if (text === null) return null;
|
|
48
|
+
try {
|
|
49
|
+
return parseUIPrefabFile(JSON.parse(text));
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface UIPrefabAssetEntry {
|
|
56
|
+
/** Display name (filename minus the `.uiprefab.json` suffix). */
|
|
57
|
+
name: string;
|
|
58
|
+
/** Project-relative path, ready for `fetchUIPrefabFile`. */
|
|
59
|
+
path: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Every `.uiprefab.json` file in {@link UI_PREFAB_DIR} (empty array if the
|
|
63
|
+
* folder doesn't exist yet — `listAssets` already degrades that way). */
|
|
64
|
+
export async function listUIPrefabAssets(): Promise<UIPrefabAssetEntry[]> {
|
|
65
|
+
const entries = await listAssets('', UI_PREFAB_DIR);
|
|
66
|
+
return entries
|
|
67
|
+
.filter((e) => e.type === 'file' && e.name.endsWith(`.${UI_PREFAB_EXTENSION}`))
|
|
68
|
+
.map((e) => ({
|
|
69
|
+
name: e.name.slice(0, -(UI_PREFAB_EXTENSION.length + 1)),
|
|
70
|
+
path: `${UI_PREFAB_DIR}/${e.name}`,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Dispatched after a UI prefab is successfully saved, so any open "instantiate"
|
|
75
|
+
* picker (the palette's prefabs section) refreshes its list without a reopen. */
|
|
76
|
+
export const UI_PREFAB_SAVED_EVENT = 'editor:ui-prefab-saved';
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script loader — dynamically imports project scripts via Vite.
|
|
3
|
+
*
|
|
4
|
+
* When a project is open, Vite serves the project directory via /@fs/ paths
|
|
5
|
+
* (enabled by server.fs.allow in vite.config.ts). This module imports the
|
|
6
|
+
* project's main script entry point and extracts the setup function.
|
|
7
|
+
*
|
|
8
|
+
* W1 — editor parity (docs/R3F-FIRST-PARTY-DESIGN.md §1.A/§2 W1). The
|
|
9
|
+
* standalone path's `defaultThreeKindFactory`
|
|
10
|
+
* (`packages/engine/src/runtime/mount-game.ts:174-186`) already accepts a
|
|
11
|
+
* first-party entry module that exports EITHER a pre-built `adapter`
|
|
12
|
+
* (`GameAdapter`) OR a `setup` function, and only hard-errors when a module
|
|
13
|
+
* exports neither. This module used to lag that contract — it hard-threw on
|
|
14
|
+
* ANY module lacking `setup`, so an `adapter`-export entry that mounted fine
|
|
15
|
+
* standalone failed here. `ProjectScripts` now carries both as optional
|
|
16
|
+
* fields and `loadProjectScripts`/`reloadProjectScripts` error only when
|
|
17
|
+
* NEITHER is present, with a message mirroring
|
|
18
|
+
* `defaultThreeKindFactory`'s wording so the two paths fail identically.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { GameAdapter } from '@engine/adapter';
|
|
22
|
+
import type { GameComponentClass } from '@engine/ecs/game-component';
|
|
23
|
+
import type { GameSetupFn } from '@engine/runtime/types';
|
|
24
|
+
|
|
25
|
+
export interface ProjectScripts {
|
|
26
|
+
/** Present when the entry module exports a pre-built `GameAdapter` — the
|
|
27
|
+
* same `mod.adapter` shape `defaultThreeKindFactory` prefers
|
|
28
|
+
* (mount-game.ts:176). Optional: a project may instead export `setup`. */
|
|
29
|
+
adapter?: GameAdapter | undefined;
|
|
30
|
+
/** Present when the entry module exports a `setup()` function — the
|
|
31
|
+
* long-standing first-party contract. Optional as of W1: a project may
|
|
32
|
+
* instead export `adapter` (see above). At least one of `adapter`/`setup`
|
|
33
|
+
* is always present on a successfully loaded `ProjectScripts` — callers
|
|
34
|
+
* that only support `setup` (e.g. `resolveDefaultPixiAdapter`, which is
|
|
35
|
+
* OUT of W1's scope — docs/R3F-FIRST-PARTY-DESIGN.md §0.1's kind-parity
|
|
36
|
+
* matrix) must check for `undefined` explicitly rather than assume it. */
|
|
37
|
+
setup?: GameSetupFn | undefined;
|
|
38
|
+
components?: Record<string, GameComponentClass> | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Runtime shape a `GameAdapter` export must satisfy before either load
|
|
42
|
+
* function will accept it in place of `setup` — mirrors `guardModuleShape`'s
|
|
43
|
+
* check in `packages/editor/src/adapter-resolver.ts` (`{ module }` adapters):
|
|
44
|
+
* a non-null object with a non-empty string `id` and a function `mount`.
|
|
45
|
+
* Kept local (rather than imported) because `adapter-resolver.ts` already
|
|
46
|
+
* imports FROM this module (`loadProjectScripts`) — importing back would be
|
|
47
|
+
* circular. */
|
|
48
|
+
function isGameAdapterShaped(candidate: unknown): candidate is GameAdapter {
|
|
49
|
+
if (candidate === null || typeof candidate !== 'object') return false;
|
|
50
|
+
const obj = candidate as Record<string, unknown>;
|
|
51
|
+
return (
|
|
52
|
+
typeof obj['id'] === 'string' && obj['id'].length > 0 && typeof obj['mount'] === 'function'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extract `{ adapter?, setup?, components? }` from a dynamically imported
|
|
58
|
+
* entry module, throwing the shared "neither export" error (worded to match
|
|
59
|
+
* `defaultThreeKindFactory`'s own error — mount-game.ts:179-183) when the
|
|
60
|
+
* module satisfies neither contract. Shared by `loadProjectScripts` and
|
|
61
|
+
* `reloadProjectScripts` so the two functions can never drift apart on this
|
|
62
|
+
* check.
|
|
63
|
+
*/
|
|
64
|
+
function extractProjectScripts(
|
|
65
|
+
// biome-ignore lint/suspicious/noExplicitAny: a dynamically `import()`ed module's shape is unknown until checked
|
|
66
|
+
mod: any,
|
|
67
|
+
scriptEntry: string,
|
|
68
|
+
): ProjectScripts {
|
|
69
|
+
const adapter = isGameAdapterShaped(mod?.adapter) ? (mod.adapter as GameAdapter) : undefined;
|
|
70
|
+
const setup = typeof mod?.setup === 'function' ? (mod.setup as GameSetupFn) : undefined;
|
|
71
|
+
|
|
72
|
+
if (!adapter && !setup) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Project script at ${scriptEntry} exports neither \`adapter\` nor \`setup\` — every ` +
|
|
75
|
+
'first-party entry module exports at least one (see ' +
|
|
76
|
+
'packages/editor/template/src/scripts/main.ts).',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const components = mod.components as Record<string, GameComponentClass> | undefined;
|
|
81
|
+
loadedComponents = components ?? {};
|
|
82
|
+
return { adapter, setup, components };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The most recently loaded project component classes, keyed by class name.
|
|
87
|
+
* Populated by `loadProjectScripts`/`reloadProjectScripts` so non-play surfaces
|
|
88
|
+
* (e.g. the schema-driven inspector) can read a component's `static schema`
|
|
89
|
+
* without re-importing project code. Empty until the first load.
|
|
90
|
+
*/
|
|
91
|
+
let loadedComponents: Record<string, GameComponentClass> = {};
|
|
92
|
+
|
|
93
|
+
/** Get the component classes from the most recent project script load. */
|
|
94
|
+
export function getLoadedComponents(): Record<string, GameComponentClass> {
|
|
95
|
+
return loadedComponents;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Set the loaded component classes directly. Used by the browser build's
|
|
100
|
+
* play-mode path (`browser-project-scripts.ts`), which obtains components from
|
|
101
|
+
* the in-browser transpiled bundle rather than a dev-server import, but still
|
|
102
|
+
* wants inspector surfaces to read their `static schema`s via
|
|
103
|
+
* `getLoadedComponents()`.
|
|
104
|
+
*/
|
|
105
|
+
export function setLoadedComponents(components: Record<string, GameComponentClass>): void {
|
|
106
|
+
loadedComponents = components;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Load project scripts from the given project path.
|
|
111
|
+
*
|
|
112
|
+
* The project's entry point is `src/scripts/main.ts`, which must export
|
|
113
|
+
* EITHER (or both — the EDITOR prefers `setup`, which alone threads
|
|
114
|
+
* `editorPreview` + the firstParty HMR brand, see
|
|
115
|
+
* `resolveDefaultThreeAdapter`; standalone prefers `adapter`,
|
|
116
|
+
* mount-game.ts:176):
|
|
117
|
+
* - adapter: GameAdapter — a pre-built adapter (W1, docs/R3F-FIRST-PARTY-
|
|
118
|
+
* DESIGN.md §1.A — the same `mod.adapter` shape `defaultThreeKindFactory`
|
|
119
|
+
* accepts standalone, mount-game.ts:176)
|
|
120
|
+
* - setup(ctx: GameContext): Promise<GameCleanup>
|
|
121
|
+
*/
|
|
122
|
+
export async function loadProjectScripts(
|
|
123
|
+
projectPath: string,
|
|
124
|
+
scriptEntry = 'src/scripts/main.ts',
|
|
125
|
+
): Promise<ProjectScripts> {
|
|
126
|
+
const modulePath = `/@fs/${projectPath}/${scriptEntry}`;
|
|
127
|
+
const mod = await import(/* @vite-ignore */ modulePath);
|
|
128
|
+
return extractProjectScripts(mod, scriptEntry);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Re-import project scripts with a cache-busting timestamp.
|
|
133
|
+
* Used by the HMR handler to pick up updated code without a full page reload.
|
|
134
|
+
* Same `adapter`-or-`setup` contract as `loadProjectScripts` above.
|
|
135
|
+
*/
|
|
136
|
+
export async function reloadProjectScripts(
|
|
137
|
+
projectPath: string,
|
|
138
|
+
scriptEntry = 'src/scripts/main.ts',
|
|
139
|
+
): Promise<ProjectScripts> {
|
|
140
|
+
const modulePath = `/@fs/${projectPath}/${scriptEntry}?t=${Date.now()}`;
|
|
141
|
+
const mod = await import(/* @vite-ignore */ modulePath);
|
|
142
|
+
return extractProjectScripts(mod, scriptEntry);
|
|
143
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser `StorageBackend` over a real on-disk folder, via the File System
|
|
3
|
+
* Access API (`showDirectoryPicker()` → `FileSystemDirectoryHandle`).
|
|
4
|
+
*
|
|
5
|
+
* Unlike OPFS (sandboxed), this reads and writes the actual directory the user
|
|
6
|
+
* picked — so the in-browser editor can edit a project that also lives on disk /
|
|
7
|
+
* in git. Same handle API as OPFS, so all logic is inherited from
|
|
8
|
+
* `HandleStorage`; this subclass adds the read/write permission gate the FSA
|
|
9
|
+
* spec requires before touching a user-granted handle.
|
|
10
|
+
*
|
|
11
|
+
* Chromium-only today (Firefox/Safari lack `showDirectoryPicker`); callers fall
|
|
12
|
+
* back to `OpfsStorage` where it is unavailable.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { HandleStorage } from './handle-storage';
|
|
16
|
+
|
|
17
|
+
/** Subset of the FSA permission API (not in older TS DOM libs). */
|
|
18
|
+
interface PermissionedHandle {
|
|
19
|
+
queryPermission?(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;
|
|
20
|
+
requestPermission?(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class FsaStorage extends HandleStorage {
|
|
24
|
+
readonly id = 'fsa';
|
|
25
|
+
|
|
26
|
+
private permission: Promise<void> | null = null;
|
|
27
|
+
|
|
28
|
+
constructor(private readonly handle: FileSystemDirectoryHandle) {
|
|
29
|
+
super();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected async rootHandle(): Promise<FileSystemDirectoryHandle> {
|
|
33
|
+
await this.ensurePermission();
|
|
34
|
+
return this.handle;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Ensure read/write permission on the picked handle (prompts at most once). */
|
|
38
|
+
private ensurePermission(): Promise<void> {
|
|
39
|
+
if (!this.permission) {
|
|
40
|
+
this.permission = (async () => {
|
|
41
|
+
const h = this.handle as FileSystemDirectoryHandle & PermissionedHandle;
|
|
42
|
+
// A handle with no permission API is already usable as-is (e.g. OPFS).
|
|
43
|
+
if (!h.queryPermission && !h.requestPermission) return;
|
|
44
|
+
const opts = { mode: 'readwrite' } as const;
|
|
45
|
+
if ((await h.queryPermission?.(opts)) === 'granted') return;
|
|
46
|
+
if ((await h.requestPermission?.(opts)) === 'granted') return;
|
|
47
|
+
throw new Error('FsaStorage: read/write permission to the chosen folder was denied.');
|
|
48
|
+
})().catch((err) => {
|
|
49
|
+
this.permission = null; // allow a retry after a denial
|
|
50
|
+
throw err;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return this.permission;
|
|
54
|
+
}
|
|
55
|
+
}
|