diff --git a/src/app/App.tsx b/src/app/App.tsx index e2d82b8b..705450b6 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -14244,6 +14244,7 @@ export function App({ store, initialStatusMessage }: AppProps) { world={editorState.document.world} sceneDocument={editorState.document} editorSimulationController={editorSimulationController} + editorSimulationPlaying={editorSimulationPlaying} projectAssets={editorState.document.assets} loadedModelAssets={loadedModelAssets} loadedImageAssets={loadedImageAssets} @@ -14308,6 +14309,9 @@ export function App({ store, initialStatusMessage }: AppProps) { onSelectionChange={(selection) => applySelection(selection, "viewport") } + onPlayEditorSimulation={handlePlayEditorSimulation} + onPauseEditorSimulation={handlePauseEditorSimulation} + onStepEditorSimulation={handleStepEditorSimulation} /> ))} {layoutMode !== "quad" ? null : ( diff --git a/src/app/app.css b/src/app/app.css index 62e9da9c..ac2f6c2b 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -1411,6 +1411,10 @@ button:disabled { pointer-events: none; } +.viewport-canvas[data-active="true"] .viewport-canvas__overlay { + max-width: calc(100% - 132px); +} + .viewport-canvas__overlay-badges { display: flex; flex-wrap: wrap; @@ -1447,6 +1451,72 @@ button:disabled { backdrop-filter: blur(10px); } +.viewport-canvas__time-transport { + position: absolute; + right: 10px; + bottom: 10px; + z-index: 1; + display: flex; + align-items: center; + gap: 3px; + padding: 4px; + color: #f8efe3; + background: rgba(8, 10, 14, 0.72); + border: 1px solid rgba(255, 255, 255, 0.16); + border-radius: 8px; + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22); + backdrop-filter: blur(10px); + pointer-events: auto; +} + +.viewport-canvas__time-button { + width: 30px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + color: #f8efe3; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: 6px; + font-size: 0.72rem; + font-weight: 800; + line-height: 1; + cursor: pointer; + touch-action: none; + user-select: none; +} + +.viewport-canvas__time-button:hover, +.viewport-canvas__time-button:focus-visible { + color: #ffffff; + background: rgba(255, 255, 255, 0.16); + border-color: rgba(255, 255, 255, 0.28); +} + +.viewport-canvas__time-button:focus-visible { + outline: 2px solid rgba(240, 197, 116, 0.78); + outline-offset: 2px; +} + +.viewport-canvas__time-button:active { + transform: translateY(1px); +} + +.viewport-canvas__time-button--primary { + width: 34px; + color: #20180e; + background: #f0c574; + border-color: rgba(255, 255, 255, 0.24); +} + +.viewport-canvas__time-button--primary:hover, +.viewport-canvas__time-button--primary:focus-visible { + color: #140f08; + background: #ffd68a; +} + .viewport-region__splitter { position: absolute; z-index: 3; diff --git a/src/viewport-three/ViewportPanel.tsx b/src/viewport-three/ViewportPanel.tsx index bfc583ea..595383ea 100644 --- a/src/viewport-three/ViewportPanel.tsx +++ b/src/viewport-three/ViewportPanel.tsx @@ -54,6 +54,7 @@ interface ViewportPanelProps { world: WorldSettings; sceneDocument: SceneDocument; editorSimulationController: EditorSimulationController; + editorSimulationPlaying: boolean; projectAssets: Record; loadedModelAssets: Record; loadedImageAssets: Record; @@ -103,6 +104,9 @@ interface ViewportPanelProps { onTransformCommit(transformSession: ActiveTransformSession): void; onTransformCancel(): void; onSelectionChange(selection: EditorSelection): void; + onPlayEditorSimulation(): void; + onPauseEditorSimulation(): void; + onStepEditorSimulation(deltaHours: number): void; } const VIEWPORT_DISPLAY_MODES = ["normal", "authoring", "wireframe"] as const; @@ -130,6 +134,7 @@ export function ViewportPanel({ world, sceneDocument, editorSimulationController, + editorSimulationPlaying, projectAssets, loadedModelAssets, loadedImageAssets, @@ -172,7 +177,10 @@ export function ViewportPanel({ onTransformPreviewChange, onTransformCommit, onTransformCancel, - onSelectionChange + onSelectionChange, + onPlayEditorSimulation, + onPauseEditorSimulation, + onStepEditorSimulation }: ViewportPanelProps) { const shouldShow = layoutMode === "quad" || isActive; const panelStyle = shouldShow ? style : { ...(style ?? {}), display: "none" }; @@ -195,6 +203,7 @@ export function ViewportPanel({ world={world} sceneDocument={sceneDocument} editorSimulationController={editorSimulationController} + editorSimulationPlaying={editorSimulationPlaying} projectAssets={projectAssets} loadedModelAssets={loadedModelAssets} loadedImageAssets={loadedImageAssets} @@ -224,6 +233,9 @@ export function ViewportPanel({ onTransformPreviewChange={onTransformPreviewChange} onTransformCommit={onTransformCommit} onTransformCancel={onTransformCancel} + onPlayEditorSimulation={onPlayEditorSimulation} + onPauseEditorSimulation={onPauseEditorSimulation} + onStepEditorSimulation={onStepEditorSimulation} />