@volter/editor-game 0.5.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
package/NOTICE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright 2026 Volter AI, Inc.
|
|
2
|
+
|
|
3
|
+
The files in this package were imported from volter-ai/vgai-engine at commit
|
|
4
|
+
09c2749ce. LICENSE contains the AGPL-3.0-only license they retain.
|
|
5
|
+
|
|
6
|
+
packages/game/src, packages/game/contributions (@vgai/game)
|
|
7
|
+
-> src, contributions
|
|
8
|
+
packages/dom/src, packages/dom/contributions (@vgai/dom)
|
|
9
|
+
-> src/react, contributions/react
|
|
10
|
+
packages/threejs/src except viewport/ and capture/, packages/threejs/contributions
|
|
11
|
+
(@vgai/threejs) -> src/three, contributions/three
|
|
12
|
+
host modules of packages/editor/src (@vgai/editor) -> src/host, including
|
|
13
|
+
gameplay-export.ts and document-preview-three.ts, and the world-root stage
|
|
14
|
+
(components/world-root-stage.ts and the modules it draws with)
|
|
15
|
+
src/host/components/world-root-stage-binding.tsx is transcribed from the
|
|
16
|
+
world-root parts of packages/editor/src/components/stage-overlay-set.tsx;
|
|
17
|
+
src/host/components/scene-document.tsx and
|
|
18
|
+
contributions/scene-document.service.ts from the Scene document half of
|
|
19
|
+
packages/editor/src/components/CenterDocuments.tsx
|
|
20
|
+
|
|
21
|
+
src/host/sdk/tools.ts was imported from packages/vgai-sdk/src/tools.ts, part of
|
|
22
|
+
the Apache-2.0 @vgai/sdk package, and retains that license. It re-exports the
|
|
23
|
+
Apache-2.0 tool contract of @volter/editor-sdk.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Open in Asset Budget` on ONE asset — the Asset Browser's `asset` context
|
|
3
|
+
* menu (`@volter/editor-sdk/chrome`), whose items are invoked with the asset's
|
|
4
|
+
* project path. It was a built-in item in `AssetBrowser.tsx`; the budget is
|
|
5
|
+
* this package's document, so the item is this package's too, and a models
|
|
6
|
+
* project's context menu simply does not carry it.
|
|
7
|
+
*/
|
|
8
|
+
import type { MenuContribution } from '@volter/editor-sdk/chrome';
|
|
9
|
+
import { openAssetBudgetDocument } from '../src/asset-budget/AssetBudgetPanel';
|
|
10
|
+
|
|
11
|
+
export const point = 'workspace.menu';
|
|
12
|
+
|
|
13
|
+
export const menu: MenuContribution = {
|
|
14
|
+
menu: 'asset',
|
|
15
|
+
items: [
|
|
16
|
+
{
|
|
17
|
+
id: 'open-budget',
|
|
18
|
+
label: 'Open in Asset Budget',
|
|
19
|
+
testId: 'asset-context-open-budget',
|
|
20
|
+
execute: ({ assetPath }) => {
|
|
21
|
+
if (assetPath === undefined) return;
|
|
22
|
+
openAssetBudgetDocument(assetPath);
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Open Asset Budget` in the command palette — the same document Window ▸
|
|
3
|
+
* Asset Budget opens, through the action point.
|
|
4
|
+
*/
|
|
5
|
+
import type { ActionContribution } from '@volter/editor-sdk/chrome';
|
|
6
|
+
import { openAssetBudgetDocument } from '../src/asset-budget/AssetBudgetPanel';
|
|
7
|
+
|
|
8
|
+
export const point = 'workspace.action';
|
|
9
|
+
|
|
10
|
+
export const actions: ActionContribution['actions'] = [
|
|
11
|
+
{
|
|
12
|
+
id: 'asset-budget',
|
|
13
|
+
label: 'Open Asset Budget',
|
|
14
|
+
execute: () => {
|
|
15
|
+
openAssetBudgetDocument();
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE ASSET BUDGET (`workspace.document`) — what a project's shipped assets
|
|
3
|
+
* cost: per-asset size, VRAM estimate, triangle count, who references it, and
|
|
4
|
+
* the glTF/KTX2 optimizers that rewrite it. A singleton center document,
|
|
5
|
+
* opened from Window ▸ Asset Budget, the palette, or an asset's own context
|
|
6
|
+
* menu (`asset-budget-asset.menu.ts`, which hands it the asset's path).
|
|
7
|
+
*
|
|
8
|
+
* It ships with `@volter/editor-game` because a budget is a GAME's question — what the
|
|
9
|
+
* player downloads and what the GPU holds. A folder of models has no shipped
|
|
10
|
+
* asset set and no Asset Budget tab.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ToolContributionProps } from '@volter/editor-sdk/contributions';
|
|
14
|
+
import { AssetBudgetPanel } from '../src/asset-budget/AssetBudgetPanel';
|
|
15
|
+
|
|
16
|
+
export const point = 'workspace.document';
|
|
17
|
+
export const title = 'Asset Budget';
|
|
18
|
+
|
|
19
|
+
export default function AssetBudgetDocument(_props: ToolContributionProps) {
|
|
20
|
+
return <AssetBudgetPanel />;
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window ▸ Asset Budget — the document in the application menu, beside the
|
|
3
|
+
* editor's own windows.
|
|
4
|
+
*/
|
|
5
|
+
import type { MenuContribution } from '@volter/editor-sdk/chrome';
|
|
6
|
+
import { openAssetBudgetDocument } from '../src/asset-budget/AssetBudgetPanel';
|
|
7
|
+
|
|
8
|
+
export const point = 'workspace.menu';
|
|
9
|
+
|
|
10
|
+
export const menu: MenuContribution = {
|
|
11
|
+
menu: 'window',
|
|
12
|
+
items: [
|
|
13
|
+
{
|
|
14
|
+
id: 'asset-budget',
|
|
15
|
+
label: 'Asset Budget',
|
|
16
|
+
testId: 'menu-asset-budget',
|
|
17
|
+
execute: () => {
|
|
18
|
+
openAssetBudgetDocument();
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page-level audio services of a RUNNING game (`@volter/editor-sdk/services`,
|
|
3
|
+
* a `workspace.service` contribution): the host-level AudioContext unlock —
|
|
4
|
+
* a game's own unlock listeners go through the realm's gated re-dispatch,
|
|
5
|
+
* which is not the trusted gesture stack browsers require — and the Web
|
|
6
|
+
* Audio pose guard's editor diagnostic. Both are page-wide and idempotent;
|
|
7
|
+
* neither matters to a project that plays nothing.
|
|
8
|
+
*/
|
|
9
|
+
import { installAudioPoseGuard } from '../src/services/audio-pose-guard';
|
|
10
|
+
import { installGameAudioUnlock } from '../src/services/game-audio-unlock';
|
|
11
|
+
|
|
12
|
+
export const point = 'workspace.service';
|
|
13
|
+
|
|
14
|
+
export function start(): void {
|
|
15
|
+
installGameAudioUnlock();
|
|
16
|
+
installAudioPoseGuard();
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Audio debugger as a drawer tab (W3c): node graph, transport strip,
|
|
3
|
+
* per-bus meters and the event log over the inspected session's
|
|
4
|
+
* `AudioAdapter`. Always on offer: the editor project registers the same
|
|
5
|
+
* first-party graph in edit mode, and Play supplies the running game's.
|
|
6
|
+
*/
|
|
7
|
+
import { AudioDebuggerPanel } from '../src/audio/AudioDebuggerPanel';
|
|
8
|
+
|
|
9
|
+
export const point = 'workspace.utility';
|
|
10
|
+
export const title = 'Audio';
|
|
11
|
+
export const order = 26;
|
|
12
|
+
|
|
13
|
+
export default function AudioUtility() {
|
|
14
|
+
return <AudioDebuggerPanel />;
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE PLAY ENTRY THAT IS NOT A CLICK (`@volter/editor-sdk/services`, a
|
|
3
|
+
* `workspace.service` contribution): `?play=1`, the URL asking this session to
|
|
4
|
+
* open playing.
|
|
5
|
+
*
|
|
6
|
+
* It was an effect in `components/DefaultEditorLayout.tsx`; the shell's layout
|
|
7
|
+
* must not name Play to honour a query param. The boot gate that makes the
|
|
8
|
+
* timing safe is armed before any descendant effect (see
|
|
9
|
+
* `initial-project.ts`'s header), and a service starts after the contribution
|
|
10
|
+
* pass — which can be before a project session's store exists — so the entry
|
|
11
|
+
* waits for the store's ARRIVAL (`@editor/shell-store-door`, `onShellStore`,
|
|
12
|
+
* which fires at once when one is already registered) rather than for a React
|
|
13
|
+
* mount.
|
|
14
|
+
*
|
|
15
|
+
* It reports its failure to the editor console — `vgai console` is what reads
|
|
16
|
+
* it — rather than leaving a stopped surface with no reason on it.
|
|
17
|
+
*/
|
|
18
|
+
import { onShellStore } from '@volter/editor-core/shell-store-door';
|
|
19
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
20
|
+
import { enterPlayMode } from '../src/play/play-mode';
|
|
21
|
+
|
|
22
|
+
export const point = 'workspace.service';
|
|
23
|
+
|
|
24
|
+
function enter(reason: string): void {
|
|
25
|
+
void enterPlayMode().catch((error) => {
|
|
26
|
+
editorHost().console.error(
|
|
27
|
+
`${reason}: ${error instanceof Error ? error.message : String(error)}`,
|
|
28
|
+
'play',
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function start(): () => void {
|
|
34
|
+
let timer: number | undefined;
|
|
35
|
+
let urlPlayStarted = false;
|
|
36
|
+
const stopStore = onShellStore(() => {
|
|
37
|
+
if (urlPlayStarted) return;
|
|
38
|
+
if (new URLSearchParams(window.location.search).get('play') !== '1') return;
|
|
39
|
+
urlPlayStarted = true;
|
|
40
|
+
// Next task, not this one: the store has only just been registered, and
|
|
41
|
+
// the boot gate the entry rides is armed on the same turn.
|
|
42
|
+
timer = window.setTimeout(() => enter('Could not enter requested Play mode'), 0);
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
if (timer !== undefined) window.clearTimeout(timer);
|
|
46
|
+
stopStore();
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PAGE-BRIDGE verbs of the session wire (`@volter/editor-sdk/commands`, a
|
|
3
|
+
* `workspace.command` contribution): the session-generic `bridge-call`, the
|
|
4
|
+
* game-stack still, and the gameplay recorder's start/stop/timeline.
|
|
5
|
+
*
|
|
6
|
+
* These hand control to the GAME's own code (or read its pixels), so their
|
|
7
|
+
* budgets are the game's, not the editor's — carried here from the host's own
|
|
8
|
+
* table row for row.
|
|
9
|
+
*
|
|
10
|
+
* `bridge-recording-export` deliberately did NOT come with them: it reads and
|
|
11
|
+
* subscribes to the EditorShellStore's `playState` (pause is its
|
|
12
|
+
* precondition, and a play-state change is what cancels it), and a
|
|
13
|
+
* contributed handler is handed the command only. It stays in
|
|
14
|
+
* `command-listener.ts` with the host gate that refuses every command while
|
|
15
|
+
* an export owns the paused run, until Play itself leaves the host.
|
|
16
|
+
*
|
|
17
|
+
* Play mode, the recorders and the composite capture are the game skew's own
|
|
18
|
+
* modules, still housed in the editor until Play leaves the host (WORK.md
|
|
19
|
+
* §The workbench, G); they are reached through the `@editor/*` alias the
|
|
20
|
+
* editor's Vite serves to every contribution, and become this package's own
|
|
21
|
+
* imports when they move. Nothing here is host API.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
|
|
25
|
+
import {
|
|
26
|
+
activeGameplayRecordingTimeline,
|
|
27
|
+
startGameplayRecording,
|
|
28
|
+
stopGameplayRecording,
|
|
29
|
+
} from '../src/host/gameplay-recording';
|
|
30
|
+
import { captureGameplayReplay, exportGameplayReplay } from '@volter/editor-core/gameplay-replay';
|
|
31
|
+
import type { CommandContribution } from '@volter/editor-sdk/commands';
|
|
32
|
+
import { handleBridgeCall } from '../src/bridge/call';
|
|
33
|
+
import { hasLiveDebugPlane } from '../src/bridge/dispatch';
|
|
34
|
+
import { captureLiveCanvasFrame, refreshRecordingFrame } from '../src/bridge/live-frames';
|
|
35
|
+
import { handleBridgeScreenshot } from '../src/bridge/screenshot';
|
|
36
|
+
import { notPlayingResult, structuredErrorResult } from '../src/command-results';
|
|
37
|
+
import { isIngestActive } from '../src/ingest/active-ingest';
|
|
38
|
+
import { getInstanceContainer, isPlayModeActive } from '../src/play/play-mode';
|
|
39
|
+
|
|
40
|
+
export const point = 'workspace.command';
|
|
41
|
+
|
|
42
|
+
export const commands: CommandContribution['commands'] = {
|
|
43
|
+
// #140 — the session-generic debug-seam relay: see `dispatchBridgeMethod`'s
|
|
44
|
+
// doc comment for why this is a distinct primitive from the NAMED
|
|
45
|
+
// debug-seam verbs (`list-gameplay-state` etc., `gameplay.command.ts`)
|
|
46
|
+
// rather than a duplicate of them.
|
|
47
|
+
'bridge-call': { timeoutMs: 60_000, derivedRefresh: 'always', handle: handleBridgeCall },
|
|
48
|
+
'bridge-screenshot': {
|
|
49
|
+
timeoutMs: 15_000,
|
|
50
|
+
derivedRefresh: 'if-content-changed',
|
|
51
|
+
handle: handleBridgeScreenshot,
|
|
52
|
+
},
|
|
53
|
+
'bridge-recording-start': {
|
|
54
|
+
timeoutMs: 15_000,
|
|
55
|
+
derivedRefresh: 'none',
|
|
56
|
+
handle: async (cmd) => {
|
|
57
|
+
if (!isPlayModeActive() && !hasLiveDebugPlane() && !isIngestActive()) {
|
|
58
|
+
return notPlayingResult();
|
|
59
|
+
}
|
|
60
|
+
const container = getInstanceContainer();
|
|
61
|
+
if (!container) {
|
|
62
|
+
return { ok: false, error: 'no play-mode game surface is mounted yet' };
|
|
63
|
+
}
|
|
64
|
+
// Recording belongs to the RUNNING game, never the edit-time Asset Lab
|
|
65
|
+
// audio fallback. A game with no audio adapter must produce an honest
|
|
66
|
+
// video-only WebM instead of attaching a suspended preview track.
|
|
67
|
+
const audio = getActiveSystems().audio?.acquireRecordingStream?.() ?? null;
|
|
68
|
+
try {
|
|
69
|
+
const started = await startGameplayRecording(container, {
|
|
70
|
+
fps: typeof cmd['fps'] === 'number' ? cmd['fps'] : undefined,
|
|
71
|
+
format:
|
|
72
|
+
cmd['format'] === 'canvas-dom' || cmd['format'] === 'composite-webm'
|
|
73
|
+
? cmd['format']
|
|
74
|
+
: undefined,
|
|
75
|
+
name: typeof cmd['name'] === 'string' || cmd['name'] === null ? cmd['name'] : undefined,
|
|
76
|
+
canvasFrame: captureLiveCanvasFrame,
|
|
77
|
+
refreshFrame: refreshRecordingFrame,
|
|
78
|
+
audio,
|
|
79
|
+
});
|
|
80
|
+
return { ok: true, data: { ...started } };
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return structuredErrorResult(error);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
'bridge-recording-stop': {
|
|
87
|
+
timeoutMs: 60_000,
|
|
88
|
+
derivedRefresh: 'none',
|
|
89
|
+
handle: async () => {
|
|
90
|
+
try {
|
|
91
|
+
const capture = await stopGameplayRecording();
|
|
92
|
+
return { ok: true, data: { ...capture } };
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return structuredErrorResult(error);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
'bridge-recording-timeline': {
|
|
99
|
+
derivedRefresh: 'none',
|
|
100
|
+
handle: () => {
|
|
101
|
+
const timeline = activeGameplayRecordingTimeline();
|
|
102
|
+
return timeline === null
|
|
103
|
+
? { ok: false, error: 'no gameplay recording is active' }
|
|
104
|
+
: { ok: true, data: { ...timeline } };
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
'bridge-recording-replay-capture': {
|
|
108
|
+
timeoutMs: 60_000,
|
|
109
|
+
derivedRefresh: 'none',
|
|
110
|
+
handle: async (cmd) => {
|
|
111
|
+
try {
|
|
112
|
+
if (typeof cmd['replayPath'] !== 'string') {
|
|
113
|
+
return { ok: false, error: 'replayPath is required' };
|
|
114
|
+
}
|
|
115
|
+
if (typeof cmd['positionMs'] !== 'number' || !Number.isFinite(cmd['positionMs'])) {
|
|
116
|
+
return { ok: false, error: 'finite positionMs is required' };
|
|
117
|
+
}
|
|
118
|
+
const capture = await captureGameplayReplay(cmd['replayPath'], cmd['positionMs']);
|
|
119
|
+
return { ok: true, data: { ...capture } };
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return structuredErrorResult(error);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
'bridge-recording-replay-export': {
|
|
126
|
+
timeoutMs: 600_000,
|
|
127
|
+
derivedRefresh: 'none',
|
|
128
|
+
handle: async (cmd) => {
|
|
129
|
+
try {
|
|
130
|
+
if (typeof cmd['replayPath'] !== 'string')
|
|
131
|
+
return { ok: false, error: 'replayPath is required' };
|
|
132
|
+
const { blob, ...result } = await exportGameplayReplay({
|
|
133
|
+
replayPath: cmd['replayPath'],
|
|
134
|
+
...(typeof cmd['fps'] === 'number' ? { fps: cmd['fps'] } : {}),
|
|
135
|
+
...(typeof cmd['startMs'] === 'number' ? { startMs: cmd['startMs'] } : {}),
|
|
136
|
+
...(typeof cmd['endMs'] === 'number' ? { endMs: cmd['endMs'] } : {}),
|
|
137
|
+
});
|
|
138
|
+
const {
|
|
139
|
+
beginGameplayRecordingSink,
|
|
140
|
+
appendGameplayRecordingChunk,
|
|
141
|
+
finishGameplayRecordingSink,
|
|
142
|
+
abortGameplayRecordingSink,
|
|
143
|
+
} = await import('@volter/editor-core/editor-api');
|
|
144
|
+
const sink = await beginGameplayRecordingSink({
|
|
145
|
+
startedAt: new Date().toISOString(),
|
|
146
|
+
mimeType: 'video/webm',
|
|
147
|
+
purpose: 'export',
|
|
148
|
+
name: typeof cmd['name'] === 'string' ? cmd['name'] : `replay-export-${Date.now()}`,
|
|
149
|
+
});
|
|
150
|
+
try {
|
|
151
|
+
for (
|
|
152
|
+
let offset = 0, sequence = 0;
|
|
153
|
+
offset < blob.size;
|
|
154
|
+
offset += 4 * 1024 * 1024, sequence++
|
|
155
|
+
)
|
|
156
|
+
await appendGameplayRecordingChunk(
|
|
157
|
+
sink,
|
|
158
|
+
sequence,
|
|
159
|
+
blob.slice(offset, offset + 4 * 1024 * 1024),
|
|
160
|
+
);
|
|
161
|
+
await finishGameplayRecordingSink(sink, 'post-processed canvas and DOM recording');
|
|
162
|
+
} catch (error) {
|
|
163
|
+
await abortGameplayRecordingSink(sink);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
return { ok: true, data: { ...result, path: sink.path } };
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return structuredErrorResult(error);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BUILD PROFILES (`workspace.document`) — the project's build configuration
|
|
3
|
+
* as a center document: the build-role targets, the manifest fields a build
|
|
4
|
+
* reads, and the Build trigger. A document rather than a dialog because the
|
|
5
|
+
* configuration is a persistent authored surface (it writes
|
|
6
|
+
* `vgai.project.json` through the history service) someone tunes and re-runs,
|
|
7
|
+
* and because a modal would block the viewport while a build streams.
|
|
8
|
+
*
|
|
9
|
+
* It ships with `@volter/editor-game`, so a folder of models has no Build Profiles tab
|
|
10
|
+
* and no Build button at all.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ToolContributionProps } from '@volter/editor-sdk/contributions';
|
|
14
|
+
import { BuildProfilesPanel } from '../src/build/BuildProfilesPanel';
|
|
15
|
+
|
|
16
|
+
export const point = 'workspace.document';
|
|
17
|
+
export const title = 'Build Profiles';
|
|
18
|
+
|
|
19
|
+
export default function BuildProfilesDocument(_props: ToolContributionProps) {
|
|
20
|
+
return <BuildProfilesPanel />;
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BUILD PROGRESS (`workspace.status`) — the compact status-bar item for the
|
|
3
|
+
* running build: `Building <target>…`, then complete or failed. Nothing at
|
|
4
|
+
* all while idle, so the strip carries no row for a build nobody started.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useSyncExternalStore } from 'react';
|
|
8
|
+
import {
|
|
9
|
+
buildSessionVersion,
|
|
10
|
+
getBuildSession,
|
|
11
|
+
subscribeBuildSession,
|
|
12
|
+
} from '../src/build/build-session';
|
|
13
|
+
|
|
14
|
+
export const point = 'workspace.status';
|
|
15
|
+
export const title = 'Build Progress';
|
|
16
|
+
export const align = 'left';
|
|
17
|
+
export const order = 30;
|
|
18
|
+
|
|
19
|
+
export default function BuildProgressStatus() {
|
|
20
|
+
useSyncExternalStore(subscribeBuildSession, buildSessionVersion, buildSessionVersion);
|
|
21
|
+
const session = getBuildSession();
|
|
22
|
+
if (session.phase === 'idle') return null;
|
|
23
|
+
if (session.phase === 'building') {
|
|
24
|
+
return (
|
|
25
|
+
<span
|
|
26
|
+
className="vgai-status-copy"
|
|
27
|
+
data-testid="status-build-progress"
|
|
28
|
+
data-build-phase="building"
|
|
29
|
+
data-status-tone="accent"
|
|
30
|
+
>
|
|
31
|
+
Building {session.target}…
|
|
32
|
+
</span>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return (
|
|
36
|
+
<span
|
|
37
|
+
className="vgai-status-copy"
|
|
38
|
+
data-testid="status-build-progress"
|
|
39
|
+
data-build-phase={session.result?.ok ? 'complete' : 'failed'}
|
|
40
|
+
data-status-tone={session.result?.ok ? 'success' : 'danger'}
|
|
41
|
+
>
|
|
42
|
+
{session.result?.ok ? 'Build complete' : 'Build failed'}
|
|
43
|
+
</span>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The palette's BUILD actions — `Build for Web (itch.io)` and
|
|
3
|
+
* `Build for Desktop (Steam)`, both opening the Build Profiles document where
|
|
4
|
+
* the target is chosen and the build is run.
|
|
5
|
+
*/
|
|
6
|
+
import type { ActionContribution } from '@volter/editor-sdk/chrome';
|
|
7
|
+
import { openBuildProfilesDocument } from '../src/build/build-session';
|
|
8
|
+
|
|
9
|
+
export const point = 'workspace.action';
|
|
10
|
+
|
|
11
|
+
export const actions: ActionContribution['actions'] = [
|
|
12
|
+
{
|
|
13
|
+
id: 'build.web',
|
|
14
|
+
label: 'Build for Web (itch.io)',
|
|
15
|
+
execute: () => {
|
|
16
|
+
openBuildProfilesDocument();
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'build.steam',
|
|
21
|
+
label: 'Build for Desktop (Steam)',
|
|
22
|
+
execute: () => {
|
|
23
|
+
openBuildProfilesDocument();
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `show-build` (`@volter/editor-sdk/commands`) — the session verb that opens the
|
|
3
|
+
* Build Profiles document. It sat in `command-listener.ts` because opening a
|
|
4
|
+
* workspace document needed the live shell store; the host door's
|
|
5
|
+
* `workspace.openContributedDocument` is that reach, so the verb travels with
|
|
6
|
+
* the document it opens. Its relay budget is the host table's row it replaces
|
|
7
|
+
* (`alwaysRefresh()`: opening the document changes what a caller reads next).
|
|
8
|
+
*/
|
|
9
|
+
import type { CommandContribution } from '@volter/editor-sdk/commands';
|
|
10
|
+
import { openBuildProfilesDocument } from '../src/build/build-session';
|
|
11
|
+
|
|
12
|
+
export const point = 'workspace.command';
|
|
13
|
+
|
|
14
|
+
export const commands: CommandContribution['commands'] = {
|
|
15
|
+
'show-build': {
|
|
16
|
+
derivedRefresh: 'always',
|
|
17
|
+
handle: () => {
|
|
18
|
+
if (!openBuildProfilesDocument()) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: 'The Build Profiles document is not registered in this session.',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return { ok: true };
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE EXPORT BUTTON (`workspace.header`) — the door into Build
|
|
3
|
+
* Profiles, in the project header's contributed cluster beside the Play
|
|
4
|
+
* transport. Disabled while Play runs: a build reads the project from disk,
|
|
5
|
+
* and a running game holds ephemeral edits that are not on disk.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { editorHost } from '@volter/editor-sdk/host';
|
|
9
|
+
import { Button, EditorIcon, editorIcons } from '@volter/editor-sdk/widgets';
|
|
10
|
+
import { useSyncExternalStore } from 'react';
|
|
11
|
+
import { openBuildProfilesDocument } from '../src/build/build-session';
|
|
12
|
+
|
|
13
|
+
export const point = 'workspace.header';
|
|
14
|
+
export const order = 10;
|
|
15
|
+
|
|
16
|
+
export default function BuildHeader() {
|
|
17
|
+
const { session } = editorHost();
|
|
18
|
+
const playState = useSyncExternalStore(session.subscribe, session.playState, session.playState);
|
|
19
|
+
const playing = playState !== 'stopped';
|
|
20
|
+
return (
|
|
21
|
+
<Button
|
|
22
|
+
variant="ghost"
|
|
23
|
+
size="compact"
|
|
24
|
+
data-testid="project-export"
|
|
25
|
+
aria-label="Export project"
|
|
26
|
+
disabled={playing}
|
|
27
|
+
title={playing ? 'Stop Play mode before exporting the project' : 'Export project'}
|
|
28
|
+
className="vgai-chrome-island vgai-glass-island"
|
|
29
|
+
onClick={() => {
|
|
30
|
+
openBuildProfilesDocument();
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<EditorIcon icon={editorIcons.action.export} />
|
|
34
|
+
Export
|
|
35
|
+
</Button>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window ▸ Build Profiles / Build Output — the two build surfaces in the
|
|
3
|
+
* application menu. A build spawns `npm run <script>` on the session's own
|
|
4
|
+
* server.
|
|
5
|
+
*/
|
|
6
|
+
import type { MenuContribution } from '@volter/editor-sdk/chrome';
|
|
7
|
+
import { openBuildProfilesDocument, showBuildOutput } from '../src/build/build-session';
|
|
8
|
+
|
|
9
|
+
export const point = 'workspace.menu';
|
|
10
|
+
|
|
11
|
+
export const menu: MenuContribution = {
|
|
12
|
+
menu: 'window',
|
|
13
|
+
items: [
|
|
14
|
+
{
|
|
15
|
+
id: 'build-profiles',
|
|
16
|
+
label: 'Build Profiles',
|
|
17
|
+
testId: 'menu-build-profiles',
|
|
18
|
+
execute: () => {
|
|
19
|
+
openBuildProfilesDocument();
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'build-output',
|
|
24
|
+
label: 'Build Output',
|
|
25
|
+
testId: 'menu-build-output',
|
|
26
|
+
execute: () => {
|
|
27
|
+
showBuildOutput();
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE BUILD FAILURE REVEAL (`workspace.service`) — when a build finishes
|
|
3
|
+
* FAILED, show the Build output channel so the log that explains it is on screen.
|
|
4
|
+
* Only actionable failures open a utility automatically, and a build the user
|
|
5
|
+
* started and that did not produce an artifact is the clearest of them; it
|
|
6
|
+
* fires on the completing EDGE, never repeatedly.
|
|
7
|
+
*
|
|
8
|
+
* It was one of three watchers in the host's `installUtilityAutoOpen`. The
|
|
9
|
+
* other two (a world that did not mount, a source write that failed) watch
|
|
10
|
+
* the host's own state and stay there; this one watches THIS package's
|
|
11
|
+
* session, so it belongs here.
|
|
12
|
+
*/
|
|
13
|
+
import type { ServiceContribution } from '@volter/editor-sdk/services';
|
|
14
|
+
import {
|
|
15
|
+
getBuildSession,
|
|
16
|
+
showBuildOutput,
|
|
17
|
+
subscribeBuildSession,
|
|
18
|
+
} from '../src/build/build-session';
|
|
19
|
+
|
|
20
|
+
export const point = 'workspace.service';
|
|
21
|
+
|
|
22
|
+
export const start: ServiceContribution['start'] = () => {
|
|
23
|
+
let previousPhase = getBuildSession().phase;
|
|
24
|
+
return subscribeBuildSession(() => {
|
|
25
|
+
const session = getBuildSession();
|
|
26
|
+
if (session.phase === 'done' && previousPhase !== 'done' && session.result?.ok === false) {
|
|
27
|
+
showBuildOutput();
|
|
28
|
+
}
|
|
29
|
+
previousPhase = session.phase;
|
|
30
|
+
});
|
|
31
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connection pill in the status bar: `net: <state>` from the inspected
|
|
3
|
+
* networking adapter, opening the Network inspector on click. Renders
|
|
4
|
+
* nothing when there is no adapter or no connection-state reader — a pill
|
|
5
|
+
* claiming "disconnected" for an unmeasured link would be worse than none.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { editorHost, useHostAvailabilitySelector } from '@volter/editor-sdk/host';
|
|
9
|
+
import { Button } from '@volter/editor-sdk/widgets';
|
|
10
|
+
import type { ConnectionState } from '@volter/editor-project/adapter';
|
|
11
|
+
|
|
12
|
+
export const point = 'workspace.status';
|
|
13
|
+
export const title = 'Connection';
|
|
14
|
+
export const align = 'left';
|
|
15
|
+
export const order = 20;
|
|
16
|
+
|
|
17
|
+
/** The Network inspector's registered utility id (`network.utility.tsx`, under
|
|
18
|
+
* the loader's `tool:` namespace). */
|
|
19
|
+
const NETWORK_UTILITY_ID = 'tool:network.utility';
|
|
20
|
+
|
|
21
|
+
export default function ConnectionStatus() {
|
|
22
|
+
// Derived off the host's SHARED availability tick; selector-shaped so a
|
|
23
|
+
// stable connection state re-renders nothing.
|
|
24
|
+
const state = useHostAvailabilitySelector(
|
|
25
|
+
(): ConnectionState | null =>
|
|
26
|
+
editorHost().systems.inspectedNetworking()?.getConnectionState?.() ?? null,
|
|
27
|
+
);
|
|
28
|
+
if (state === null) return null;
|
|
29
|
+
return (
|
|
30
|
+
<Button
|
|
31
|
+
type="button"
|
|
32
|
+
variant="ghost"
|
|
33
|
+
size="compact"
|
|
34
|
+
className="vgai-status-action"
|
|
35
|
+
data-testid="status-connection"
|
|
36
|
+
data-connection-state={state}
|
|
37
|
+
onClick={() => editorHost().workspace.showUtility(NETWORK_UTILITY_ID)}
|
|
38
|
+
title="Network connection state — open Network diagnostics"
|
|
39
|
+
>
|
|
40
|
+
net: {state}
|
|
41
|
+
</Button>
|
|
42
|
+
);
|
|
43
|
+
}
|