@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,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonicalize a project root path (T6.2 slice 3 finding — see
|
|
3
|
+
* docs/REACT-WORLD-DESIGN.md; reported in the slice-3 "Report back").
|
|
4
|
+
*
|
|
5
|
+
* Vite's own module resolver calls `fs.realpathSync` when resolving a
|
|
6
|
+
* STATIC relative import — e.g. a react world's entry module's own
|
|
7
|
+
* `import { useGameState } from './src/ui/game-state'` — which
|
|
8
|
+
* collapses a symlinked path segment (macOS's `/var` -> `/private/var`,
|
|
9
|
+
* hit by every project scaffolded under `os.tmpdir()`, as every e2e spec
|
|
10
|
+
* that calls `scaffoldProject()` does) to its real form.
|
|
11
|
+
* `adapter-resolver.ts`'s hand-built `/@fs/${projectRoot}/...` dynamic-import
|
|
12
|
+
* strings (which must bypass Vite's static analysis via `@vite-ignore` — the
|
|
13
|
+
* path is only known at runtime) do NOT go through that same resolution. So
|
|
14
|
+
* if `projectRoot` itself isn't already canonical, two DIFFERENT `/@fs/`
|
|
15
|
+
* URLs get produced for the exact same file: Vite's realpath'd rewrite of
|
|
16
|
+
* the entry module's own relative import vs. our raw, non-realpath'd string
|
|
17
|
+
* used to import `GameProvider` for the `<GameProvider>` wrap. Browser ES
|
|
18
|
+
* module identity is per-URL, so the SAME `game-state.tsx` loads as TWO
|
|
19
|
+
* separate module instances with TWO separate top-level `createContext()`
|
|
20
|
+
* calls — `useGameState`'s `useGame()` then throws "no Game in context"
|
|
21
|
+
* even though `<GameProvider>` genuinely wraps the entry (this is exactly
|
|
22
|
+
* what the T6.2 slice-3 AC e2e's first real-browser run of a react world
|
|
23
|
+
* caught).
|
|
24
|
+
*
|
|
25
|
+
* The fix is upstream of any one `/@fs/` string: canonicalize the project
|
|
26
|
+
* root ONCE, at the points a project path enters server state (dev/prod
|
|
27
|
+
* boot from `VGAI_PROJECT`, and the `/__editor/open-project` route) — every
|
|
28
|
+
* `/@fs/${projectRoot}` string built anywhere afterwards (browser-side,
|
|
29
|
+
* repo-wide, not just the react-world resolver) then already matches
|
|
30
|
+
* whatever Vite's own resolver would independently arrive at for that same
|
|
31
|
+
* project.
|
|
32
|
+
*/
|
|
33
|
+
import { realpathSync } from 'node:fs';
|
|
34
|
+
import { resolve } from 'node:path';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve `rawPath` to an absolute, symlink-canonicalized path. Falls back to
|
|
38
|
+
* a plain `path.resolve` if the path doesn't exist yet or `realpathSync`
|
|
39
|
+
* otherwise fails (e.g. a permissions error) — never throws, so a bad/odd
|
|
40
|
+
* path degrades to today's pre-canonicalization behavior rather than
|
|
41
|
+
* blocking project open.
|
|
42
|
+
*/
|
|
43
|
+
export function canonicalProjectRoot(rawPath: string): string {
|
|
44
|
+
const absolute = resolve(rawPath);
|
|
45
|
+
try {
|
|
46
|
+
return realpathSync(absolute);
|
|
47
|
+
} catch {
|
|
48
|
+
return absolute;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export const DEFAULT_CLOUD_ASSET_BASE_URL = 'https://vgai-asset-library.aaron-0ed.workers.dev';
|
|
2
|
+
|
|
3
|
+
export interface CloudAssetObject {
|
|
4
|
+
key: string;
|
|
5
|
+
sha256: string;
|
|
6
|
+
sizeBytes: number;
|
|
7
|
+
contentType: string;
|
|
8
|
+
relativePath: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CloudAssetFile {
|
|
12
|
+
label: string;
|
|
13
|
+
format: string;
|
|
14
|
+
main: CloudAssetObject;
|
|
15
|
+
dependencies: CloudAssetObject[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CloudAssetRecord {
|
|
19
|
+
id: string;
|
|
20
|
+
status: 'validated';
|
|
21
|
+
source: string;
|
|
22
|
+
name: string;
|
|
23
|
+
type: string;
|
|
24
|
+
license: string;
|
|
25
|
+
author: string;
|
|
26
|
+
sourceUrl: string;
|
|
27
|
+
attribution: string;
|
|
28
|
+
files: CloudAssetFile[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface CloudAssetManifest {
|
|
32
|
+
version: 1;
|
|
33
|
+
generatedAt: string;
|
|
34
|
+
assets: CloudAssetRecord[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let cachedManifest: { value: CloudAssetManifest; url: string; loadedAt: number } | undefined;
|
|
38
|
+
const CACHE_MS = 5 * 60 * 1000;
|
|
39
|
+
|
|
40
|
+
export function getCloudAssetBaseUrl(): string {
|
|
41
|
+
return (process.env['VGAI_CLOUD_ASSET_URL'] ?? DEFAULT_CLOUD_ASSET_BASE_URL).replace(/\/+$/, '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function loadCloudAssetManifest(): Promise<CloudAssetManifest | null> {
|
|
45
|
+
const baseUrl = getCloudAssetBaseUrl();
|
|
46
|
+
if (
|
|
47
|
+
cachedManifest &&
|
|
48
|
+
cachedManifest.url === baseUrl &&
|
|
49
|
+
Date.now() - cachedManifest.loadedAt < CACHE_MS
|
|
50
|
+
) {
|
|
51
|
+
return cachedManifest.value;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`${baseUrl}/v1/manifest`, { signal: AbortSignal.timeout(5_000) });
|
|
55
|
+
if (!response.ok) return null;
|
|
56
|
+
const value = (await response.json()) as CloudAssetManifest;
|
|
57
|
+
if (value.version !== 1 || !Array.isArray(value.assets)) return null;
|
|
58
|
+
cachedManifest = { value, url: baseUrl, loadedAt: Date.now() };
|
|
59
|
+
return value;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function findCloudAsset(id: string): Promise<CloudAssetRecord | null> {
|
|
66
|
+
const manifest = await loadCloudAssetManifest();
|
|
67
|
+
return manifest?.assets.find((asset) => asset.id === id) ?? null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function cloudAssetObjectUrl(
|
|
71
|
+
assetId: string,
|
|
72
|
+
fileIndex: number,
|
|
73
|
+
dependencyIndex?: number,
|
|
74
|
+
): string {
|
|
75
|
+
const base = `${getCloudAssetBaseUrl()}/v1/assets/${encodeURIComponent(assetId)}/files/${fileIndex}`;
|
|
76
|
+
return dependencyIndex === undefined ? base : `${base}/dependencies/${dependencyIndex}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function parseCloudAssetObjectUrl(
|
|
80
|
+
value: string,
|
|
81
|
+
expectedAssetId: string,
|
|
82
|
+
): { fileIndex: number; dependencyIndex?: number } | null {
|
|
83
|
+
let url: URL;
|
|
84
|
+
try {
|
|
85
|
+
url = new URL(value);
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const expectedBase = new URL(getCloudAssetBaseUrl());
|
|
90
|
+
if (url.protocol !== 'https:' || url.origin !== expectedBase.origin || url.search || url.hash)
|
|
91
|
+
return null;
|
|
92
|
+
const prefix = expectedBase.pathname.replace(/\/$/, '');
|
|
93
|
+
const relativePath = url.pathname.slice(prefix.length);
|
|
94
|
+
const match = /^\/v1\/assets\/([a-f0-9]{24})\/files\/(\d+)(?:\/dependencies\/(\d+))?$/.exec(
|
|
95
|
+
relativePath,
|
|
96
|
+
);
|
|
97
|
+
if (!match || match[1] !== expectedAssetId) return null;
|
|
98
|
+
const parsed: { fileIndex: number; dependencyIndex?: number } = { fileIndex: Number(match[2]) };
|
|
99
|
+
if (match[3] !== undefined) parsed.dependencyIndex = Number(match[3]);
|
|
100
|
+
return parsed;
|
|
101
|
+
}
|
package/server/dev.ts
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor dev server — Express + Vite middleware mode on a single port.
|
|
3
|
+
*
|
|
4
|
+
* Run with:
|
|
5
|
+
* npx tsx packages/editor/server/dev.ts
|
|
6
|
+
*
|
|
7
|
+
* Supports VGAI_PROJECT env var for initial project path.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { type Dirent, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import express from 'express';
|
|
14
|
+
import { createServer as createViteServer } from 'vite';
|
|
15
|
+
import { projectGameStaticPlugin } from '../vite-plugin-project-game-static';
|
|
16
|
+
import { canonicalProjectRoot } from './canonical-path';
|
|
17
|
+
import { createEditorServer } from './editor-server';
|
|
18
|
+
import { gameGlobalsShadowPlugin } from '../vite-plugin-game-globals';
|
|
19
|
+
import { ingestGameShadowRoots } from './game-globals-shadow';
|
|
20
|
+
import { friendlyListenError, resolveBindHost } from './server-utils';
|
|
21
|
+
import { registerSession, unregisterSession } from './session-registry';
|
|
22
|
+
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
// Port is env-overridable so multiple editor servers (e.g. parallel e2e/capture
|
|
25
|
+
// runs across git worktrees) don't collide on 5173. Defaults preserve behavior.
|
|
26
|
+
const PORT = Number(process.env['VGAI_EDITOR_PORT']) || 5173;
|
|
27
|
+
const HMR_PORT = Number(process.env['VGAI_HMR_PORT']) || (PORT === 5173 ? 24678 : PORT + 100);
|
|
28
|
+
// S3: bind to loopback by default; opt-in to a wider interface via env.
|
|
29
|
+
const HOST = resolveBindHost();
|
|
30
|
+
// Engine root is three levels up from packages/editor/server/
|
|
31
|
+
const engineRoot = path.resolve(__dirname, '../../..');
|
|
32
|
+
// Worktree / hoisted-monorepo support: `node_modules` is very often a SYMLINK
|
|
33
|
+
// (git worktrees share ONE install; npm workspaces hoist to a parent), and its
|
|
34
|
+
// realpath then lands OUTSIDE `engineRoot`. Vite's `server.fs.allow` matches on
|
|
35
|
+
// the symlink-RESOLVED path, so without allowing the resolved node_modules a
|
|
36
|
+
// game's own dependency — e.g. a vendored react game importing a font package —
|
|
37
|
+
// 403s ("outside of Vite serving allow list") in a worktree checkout even
|
|
38
|
+
// though it serves fine in a normal one. Allowing the project's own resolved
|
|
39
|
+
// deps is safe, and this is a no-op when node_modules is a real directory
|
|
40
|
+
// already under engineRoot.
|
|
41
|
+
const nodeModulesRealPath = (() => {
|
|
42
|
+
try {
|
|
43
|
+
const real = realpathSync(path.join(engineRoot, 'node_modules'));
|
|
44
|
+
return real.startsWith(engineRoot) ? null : real;
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
// Canonicalized (symlink-resolved), not just `path.resolve` — see
|
|
50
|
+
// canonical-path.ts's doc comment (T6.2 slice 3 finding: a react world's
|
|
51
|
+
// `GameProvider` colocation breaks if this path and Vite's own resolver
|
|
52
|
+
// disagree on a symlinked segment, e.g. macOS's `/var` -> `/private/var`).
|
|
53
|
+
const projectPath = process.env['VGAI_PROJECT']
|
|
54
|
+
? canonicalProjectRoot(process.env['VGAI_PROJECT'])
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
// D-X2 (docs/EXTERNAL-FOLDER-INGEST-DESIGN.md): the project-static route
|
|
58
|
+
// (below) must follow the CURRENTLY open project, not a boot-time snapshot —
|
|
59
|
+
// `open-project` can switch it live (`onProjectOpened` below). A mutable box
|
|
60
|
+
// read by the plugin's thunk, updated in the SAME callback that already
|
|
61
|
+
// extends Vite's fs.allow / watcher / game-globals shadow for a newly opened
|
|
62
|
+
// project.
|
|
63
|
+
let currentProjectRoot: string | undefined = projectPath;
|
|
64
|
+
|
|
65
|
+
// Roots whose `/src/` modules are treated as game code and get the gated-globals
|
|
66
|
+
// prelude (see below). Seeded from the env project (extended when the editor
|
|
67
|
+
// opens/switches a project at runtime, onProjectOpened below) PLUS the fixed
|
|
68
|
+
// in-tree ingest-fixture roots (R2c — see game-globals-shadow.ts's doc comment:
|
|
69
|
+
// those same-realm `shared`/`deduped` fixtures are part of THIS package's own
|
|
70
|
+
// `/src/` tree, not a project a user opens, so they need their own entry here).
|
|
71
|
+
const projectRoots = new Set<string>(ingestGameShadowRoots(path.resolve(__dirname, '../src')));
|
|
72
|
+
if (projectPath) projectRoots.add(projectPath);
|
|
73
|
+
|
|
74
|
+
async function main(): Promise<void> {
|
|
75
|
+
const app = express();
|
|
76
|
+
|
|
77
|
+
// 1. Create Vite in middleware mode (before editor routes so we can pass the callback)
|
|
78
|
+
const vite = await createViteServer({
|
|
79
|
+
configFile: path.resolve(engineRoot, 'vite.config.ts'),
|
|
80
|
+
plugins: [
|
|
81
|
+
// Pipe gated `window`/`document` into game code: prepend a lexical shadow
|
|
82
|
+
// to every project `/src/` module so its globals resolve to the editor's
|
|
83
|
+
// gated proxies. Scoped to `projectRoots` — an opened project's root PLUS
|
|
84
|
+
// the fixed in-tree ingest-fixture roots (R2c) — so engine/editor code is
|
|
85
|
+
// untouched (it keeps the real window; its input is gated separately via
|
|
86
|
+
// InputManager.setEnabled). The repo-root config registers this same
|
|
87
|
+
// plugin over the ingest roots for the hosted build; the shared factory
|
|
88
|
+
// no-ops on already-shadowed code, so the overlap in dev is harmless.
|
|
89
|
+
gameGlobalsShadowPlugin(() => projectRoots),
|
|
90
|
+
// Project-rooted verbatim static serving (D-X2) — the external-folder
|
|
91
|
+
// sibling of `gameStaticPlugin`'s vendored-dist route (`vite.config.ts`,
|
|
92
|
+
// repo-root config). Always registered (not gated on `projectPath`
|
|
93
|
+
// being set at boot): a project can open LATER via `onProjectOpened`,
|
|
94
|
+
// and the route must still come alive then — the thunk reads
|
|
95
|
+
// `currentProjectRoot` live on every request.
|
|
96
|
+
projectGameStaticPlugin(() => currentProjectRoot),
|
|
97
|
+
// Intercept project script changes and send a custom HMR event
|
|
98
|
+
// instead of triggering a full page reload.
|
|
99
|
+
...(projectPath
|
|
100
|
+
? [
|
|
101
|
+
{
|
|
102
|
+
name: 'vgai-script-hmr',
|
|
103
|
+
configureServer(server: { watcher: { add: (p: string) => void } }) {
|
|
104
|
+
// Watch the project directory so changes outside the Vite root are detected
|
|
105
|
+
server.watcher.add(projectPath);
|
|
106
|
+
},
|
|
107
|
+
handleHotUpdate({
|
|
108
|
+
file,
|
|
109
|
+
server,
|
|
110
|
+
}: {
|
|
111
|
+
file: string;
|
|
112
|
+
server: { ws: { send: (msg: unknown) => void } };
|
|
113
|
+
}) {
|
|
114
|
+
// Vite's `file` hook argument is ALWAYS forward-slash normalized
|
|
115
|
+
// (its cross-platform module-id convention), but `projectPath`
|
|
116
|
+
// comes from `canonicalProjectRoot` -> `realpathSync`, which on
|
|
117
|
+
// Windows returns a native backslash path. A naive
|
|
118
|
+
// `file.startsWith(projectPath)` therefore NEVER matches on
|
|
119
|
+
// Windows ("C:/Users/...".startsWith("C:\\Users\\...") is
|
|
120
|
+
// false) — the custom `vgai:script-update` event silently never
|
|
121
|
+
// fires and every project edit falls through to Vite's default
|
|
122
|
+
// full-module-reload HMR instead. Compare forward-slash forms
|
|
123
|
+
// on both sides so this works identically on every OS.
|
|
124
|
+
const projectPathPosix = projectPath!.split(path.sep).join('/');
|
|
125
|
+
if (file.startsWith(projectPathPosix)) {
|
|
126
|
+
// Data assets (docs/DATA-TOOLS-DESIGN.md §2.3) ride Vite's
|
|
127
|
+
// STOCK HMR: fall through so the imported JSON module
|
|
128
|
+
// updates and the owning handle module's
|
|
129
|
+
// `import.meta.hot.accept('./x.data.json', …)` fires in the
|
|
130
|
+
// running game (live tuning). Safe from the full-page-reload
|
|
131
|
+
// hazard the swallow below exists for: the template's
|
|
132
|
+
// handle module self-accepts the dep, and an unimported
|
|
133
|
+
// .data.json maps to zero modules (no-op). Scene/asset
|
|
134
|
+
// files stay swallowed — the editor owns those via SSE.
|
|
135
|
+
if (/\.data\.json$/.test(file)) return;
|
|
136
|
+
if (/\.(ts|tsx|js|jsx)$/.test(file)) {
|
|
137
|
+
server.ws.send({ type: 'custom', event: 'vgai:script-update', data: { file } });
|
|
138
|
+
}
|
|
139
|
+
// Swallow all project file changes — the editor handles
|
|
140
|
+
// scene/asset updates via its own SSE channel.
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
]
|
|
146
|
+
: []),
|
|
147
|
+
],
|
|
148
|
+
server: {
|
|
149
|
+
middlewareMode: true,
|
|
150
|
+
hmr: { port: HMR_PORT },
|
|
151
|
+
fs: {
|
|
152
|
+
allow: [
|
|
153
|
+
engineRoot,
|
|
154
|
+
...(nodeModulesRealPath ? [nodeModulesRealPath] : []),
|
|
155
|
+
...(projectPath ? [projectPath] : []),
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
// Pass project path to editor client (server-first mode is implied by the
|
|
160
|
+
// dev server's Vite mode 'development' — see editor-mode.ts).
|
|
161
|
+
define: {
|
|
162
|
+
__VGAI_PROJECT_PATH__: JSON.stringify(projectPath ?? ''),
|
|
163
|
+
// Track N, N2 (docs/REACT-INGEST-LANDING-DESIGN.md D-N2): the absolute
|
|
164
|
+
// repo root, so `ingest/discovery-react.ts` can build the `projectRoot`
|
|
165
|
+
// an `ingest-react` fixture's `resolveWorldAdapter` call needs (its
|
|
166
|
+
// `entry`'s `/@fs/` dynamic import + `loadProjectGameProvider` lookup)
|
|
167
|
+
// WITHOUT a "current project" — these in-tree fixtures live in THIS
|
|
168
|
+
// package's own source tree, not a project a user opens (mirrors
|
|
169
|
+
// `ingestGameShadowRoots`'s identical reasoning for the game-globals
|
|
170
|
+
// shadow). Undefined in the `web` (browser-mode, no dev server) build —
|
|
171
|
+
// `ingestGameReactProjectRoot` degrades to `undefined` there, and every
|
|
172
|
+
// caller already gates on `getEditorMode() !== 'browser'` first.
|
|
173
|
+
__VGAI_ENGINE_ROOT__: JSON.stringify(engineRoot),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// 2. Mount editor API routes (/__editor/*) with Vite fs.allow update callback
|
|
178
|
+
const editorRouter = createEditorServer({
|
|
179
|
+
engineRoot,
|
|
180
|
+
projectPath,
|
|
181
|
+
onProjectOpened(newProjectPath: string) {
|
|
182
|
+
// Dynamically allow Vite to serve /@fs/ paths from the new project directory
|
|
183
|
+
const allowed = vite.config.server.fs.allow;
|
|
184
|
+
if (!allowed.includes(newProjectPath)) {
|
|
185
|
+
allowed.push(newProjectPath);
|
|
186
|
+
}
|
|
187
|
+
// Also start watching the project for HMR
|
|
188
|
+
vite.watcher.add(newProjectPath);
|
|
189
|
+
// And gate its modules with the game-globals prelude (see vgai-game-globals above).
|
|
190
|
+
projectRoots.add(newProjectPath);
|
|
191
|
+
// And point the project-static route (D-X2) at the newly opened project.
|
|
192
|
+
currentProjectRoot = newProjectPath;
|
|
193
|
+
// Keep the session registry (D12) pointing at the CURRENT project so
|
|
194
|
+
// `vgai edit` reuse/targeting decisions see switches, not boot state.
|
|
195
|
+
registerSession({
|
|
196
|
+
project: newProjectPath,
|
|
197
|
+
port: PORT,
|
|
198
|
+
pid: process.pid,
|
|
199
|
+
startedAt: sessionStartedAt,
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
app.use(editorRouter);
|
|
204
|
+
app.use(vite.middlewares);
|
|
205
|
+
|
|
206
|
+
// Opt-in PROJECT-SCOPED poll watching (VGAI_WATCH_POLL=1, or =<ms> for the
|
|
207
|
+
// interval). Load-bearing on WSL when the project lives on a Windows drive
|
|
208
|
+
// (/mnt/c, drvfs): inotify never fires there, so without polling the editor
|
|
209
|
+
// silently misses every external file edit — most visibly the data-asset
|
|
210
|
+
// live-tuning loop (docs/DATA-TOOLS-DESIGN.md §2.3: edit
|
|
211
|
+
// `src/data/*.data.json` in VS Code/agent while playing → HMR into the
|
|
212
|
+
// running game). The project's own vite.config.ts can NOT provide this —
|
|
213
|
+
// the editor boots Vite from the ENGINE's config and only `watcher.add()`s
|
|
214
|
+
// the project — so this env var is the single knob.
|
|
215
|
+
//
|
|
216
|
+
// Deliberately NOT Vite's `server.watch.usePolling`: that polls the whole
|
|
217
|
+
// watched set (the entire engine tree), and over drvfs the stat flood
|
|
218
|
+
// starves the event loop badly enough that play-mode's game mount never
|
|
219
|
+
// completes (field-tested 2026-07-10: editor shell loads, ▶ mounts nothing
|
|
220
|
+
// even after 45s; scoped polling mounts fine). Instead: a tiny mtime poller
|
|
221
|
+
// over the CURRENT project only, forwarding synthetic add/change/unlink
|
|
222
|
+
// events into Vite's watcher, which runs the normal invalidate+HMR pipeline.
|
|
223
|
+
if (process.env['VGAI_WATCH_POLL']) {
|
|
224
|
+
startProjectPollWatcher(vite.watcher, () => currentProjectRoot);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 3. Start Express
|
|
228
|
+
const sessionStartedAt = new Date().toISOString();
|
|
229
|
+
const server = app.listen(PORT, HOST, () => {
|
|
230
|
+
console.log(
|
|
231
|
+
`\n \x1b[32mEditor server\x1b[0m running at \x1b[36mhttp://${HOST}:${PORT}\x1b[0m`,
|
|
232
|
+
);
|
|
233
|
+
if (projectPath) {
|
|
234
|
+
console.log(` \x1b[33mProject\x1b[0m: ${projectPath}`);
|
|
235
|
+
}
|
|
236
|
+
console.log(
|
|
237
|
+
` \x1b[33mEditor\x1b[0m: \x1b[36mhttp://${HOST}:${PORT}/\x1b[0m\n`,
|
|
238
|
+
);
|
|
239
|
+
// D12: announce this session so `vgai edit` can reuse instead of hijack.
|
|
240
|
+
registerSession({
|
|
241
|
+
project: projectPath ?? null,
|
|
242
|
+
port: PORT,
|
|
243
|
+
pid: process.pid,
|
|
244
|
+
startedAt: sessionStartedAt,
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// SC5: friendly message instead of an opaque crash on port conflict.
|
|
249
|
+
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
250
|
+
console.error(`\n \x1b[31m${friendlyListenError(err, PORT, HOST)}\x1b[0m\n`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Cleanup on shutdown
|
|
255
|
+
const shutdown = () => {
|
|
256
|
+
unregisterSession(process.pid);
|
|
257
|
+
vite.close();
|
|
258
|
+
server.close();
|
|
259
|
+
};
|
|
260
|
+
process.once('SIGINT', shutdown);
|
|
261
|
+
process.once('SIGTERM', shutdown);
|
|
262
|
+
process.once('exit', () => unregisterSession(process.pid));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* VGAI_WATCH_POLL's engine: rescan the current project's tree every
|
|
267
|
+
* `interval` ms (default 1000; `VGAI_WATCH_POLL=<ms>` overrides), diff mtimes
|
|
268
|
+
* against the previous scan, and forward add/change/unlink as synthetic
|
|
269
|
+
* events on Vite's own chokidar watcher — from there the stock
|
|
270
|
+
* invalidate + handleHotUpdate pipeline (including the vgai-script-hmr
|
|
271
|
+
* plugin above) runs exactly as if inotify had fired. See the call site's
|
|
272
|
+
* comment for why this exists and why it is scoped to the project.
|
|
273
|
+
*
|
|
274
|
+
* Cost model: a project tree is small (tens to a few hundred files once
|
|
275
|
+
* node_modules/.git/dist are skipped), so even drvfs-priced stats are cheap.
|
|
276
|
+
* Switching projects re-baselines silently (no spurious event storm).
|
|
277
|
+
*/
|
|
278
|
+
function startProjectPollWatcher(
|
|
279
|
+
watcher: { emit: (event: string, file: string) => void },
|
|
280
|
+
getRoot: () => string | undefined,
|
|
281
|
+
): void {
|
|
282
|
+
const raw = Number(process.env['VGAI_WATCH_POLL']);
|
|
283
|
+
const interval = raw > 1 ? raw : 1000;
|
|
284
|
+
const SKIP = new Set(['node_modules', '.git', 'dist', '.vgai', 'logs']);
|
|
285
|
+
let known: Map<string, number> | undefined;
|
|
286
|
+
let lastRoot: string | undefined;
|
|
287
|
+
|
|
288
|
+
const listDir = (dir: string): Dirent[] => {
|
|
289
|
+
try {
|
|
290
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
291
|
+
} catch {
|
|
292
|
+
return []; // directory vanished mid-scan
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const statMtime = (file: string): number | undefined => {
|
|
297
|
+
try {
|
|
298
|
+
return statSync(file).mtimeMs;
|
|
299
|
+
} catch {
|
|
300
|
+
return undefined; // file vanished between readdir and stat
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const scan = (dir: string, out: Map<string, number>): void => {
|
|
305
|
+
for (const entry of listDir(dir)) {
|
|
306
|
+
if (SKIP.has(entry.name)) continue;
|
|
307
|
+
const full = path.join(dir, entry.name);
|
|
308
|
+
if (entry.isDirectory()) {
|
|
309
|
+
scan(full, out);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!entry.isFile()) continue;
|
|
313
|
+
const mtime = statMtime(full);
|
|
314
|
+
if (mtime !== undefined) out.set(full, mtime);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const diffAndEmit = (prev: Map<string, number>, next: Map<string, number>): void => {
|
|
319
|
+
for (const [file, mtime] of next) {
|
|
320
|
+
const was = prev.get(file);
|
|
321
|
+
if (was === undefined) watcher.emit('add', file);
|
|
322
|
+
else if (was !== mtime) watcher.emit('change', file);
|
|
323
|
+
}
|
|
324
|
+
for (const file of prev.keys()) {
|
|
325
|
+
if (!next.has(file)) watcher.emit('unlink', file);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const tick = (): void => {
|
|
330
|
+
const root = getRoot();
|
|
331
|
+
if (root !== lastRoot) {
|
|
332
|
+
lastRoot = root;
|
|
333
|
+
known = undefined; // new/first project: baseline scan, emit nothing
|
|
334
|
+
}
|
|
335
|
+
if (root) {
|
|
336
|
+
const next = new Map<string, number>();
|
|
337
|
+
scan(root, next);
|
|
338
|
+
if (known) diffAndEmit(known, next);
|
|
339
|
+
known = next;
|
|
340
|
+
}
|
|
341
|
+
setTimeout(tick, interval);
|
|
342
|
+
};
|
|
343
|
+
setTimeout(tick, interval);
|
|
344
|
+
console.log(` Project poll-watcher on (VGAI_WATCH_POLL, every ${interval}ms)`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
main().catch((err) => {
|
|
348
|
+
console.error('Failed to start dev server:', err);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
});
|