PageEnigma refactor Phase 5a: migrate engine.ts signal slice to the store

Migrates components and engine code from reading signals/engine.ts
(cameraAspectRatio, editorState, gridVisibility, previewSrc) to reading
the Zustand store.

Components migrated to usePageEnigmaStore reads:
- AspectRatioMenu (also dropped useSignals + collapsed two
  duplicated lookup ladders into ICON_BY_RATIO / LABEL_BY_RATIO)
- PreviewFrameImage
- Letterbox (no longer reactive via signals; reads camAspect from store)
- StyleButtons (editorState, previewSrc, setEditorState)
- Outliner (cameraAspectRatio)
- SidePanelMenu (editorState + setEditorState)
- ControlPanelSceneObject (editorState)
- PreviewEngineCamera (cameraAspectRatio + editorState)
- PageEditor (cameraAspectRatio for getScale; the signal version still
  flows into PromptBox3D as a prop)

Engine code migrated to usePageEnigmaStore.getState():
- editor.ts: snapShotOfCurrentFrame reads cameraAspectRatio + grid;
  switchPreview / switchEdit set editorState
- helper.ts: switchCameraView writes editorState
- video_generation.ts: generateFrame writes previewSrc + editorState
- SceneMetadata.ts: cameraAspectRatio for snapshot metadata
- scene.ts: subscribes to store gridVisible (not the signal)

signals/engine.ts becomes a thin compatibility layer for PromptBox3D's
signal-typed props. Its setters dual-write into the store so any
PromptBox3D-driven changes (grid toggle, aspect ratio dropdown) keep
the store authoritative. editorState / previewSrc / setEditorState
removed entirely -- nothing outside the store needs them anymore.

Net effect on typecheck: 148 -> 147 errors in PageEnigma. Zero new
errors introduced.
This commit is contained in:
Tanish Manku committed 2026-04-28 23:05:54 -07:00
1 parent 35aae54d49
commit 4b0618e50b
15 files changed
+173 -192

No files matched your search

@@ -1,6 +1,5 @@
import { getArtStyle } from "~/enums/ArtStyle";
import {
cameraAspectRatio,
cinematic,
enginePreProcessing,
faceDetail,
@@ -11,6 +10,7 @@ import {
} from "~/pages/PageEnigma/signals";
import { SceneGenereationMetaData as SceneGenerationMetaData } from "~/pages/PageEnigma/models/sceneGenerationMetadata";
import Editor from "./editor";
import { usePageEnigmaStore } from "../PageEnigmaStore";
export const getSceneGenerationMetaData = (
editorEngine: Editor,
@@ -20,7 +20,7 @@ export const getSceneGenerationMetaData = (
positivePrompt: editorEngine.positive_prompt,
negativePrompt: editorEngine.negative_prompt,
artisticStyle: getArtStyle(editorEngine.art_style.toString()),
cameraAspectRatio: cameraAspectRatio.value,
cameraAspectRatio: usePageEnigmaStore.getState().cameraAspectRatio,
globalIPAMediaToken: globalIPAMediaToken.value || undefined,
upscale: upscale.value,
faceDetail: faceDetail.value,
@@ -16,11 +16,6 @@ import { PointerLockControls } from "three/addons/controls/PointerLockControls.j
import { EditorStates, CameraAspectRatio } from "~/pages/PageEnigma/enums";
import { AssetType, ClipGroup } from "~/enums";
import { XYZ } from "../datastructures/common";
import {
editorState,
cameraAspectRatio,
gridVisibility,
} from "../signals/engine";
import { SceneUtils } from "./helper";
import { VideoGeneration } from "./video_generation";
import { MouseControls } from "./keybinds_controls";
@@ -698,11 +693,12 @@ class Editor {
return null;
}
const currentAspectRatio = cameraAspectRatio.value;
const store = usePageEnigmaStore.getState();
const currentAspectRatio = store.cameraAspectRatio;
// Store grid visibility state and hide grid
const wasGridVisible = gridVisibility.value;
gridVisibility.value = false;
const wasGridVisible = store.gridVisible;
store.setGridVisible(false);
// Store and hide transform controls
const wasControlVisible = this.control?.visible ?? false;
@@ -810,7 +806,7 @@ class Editor {
}
// Restore grid visibility
gridVisibility.value = wasGridVisible;
usePageEnigmaStore.getState().setGridVisible(wasGridVisible);
// Restore transform controls visibility
if (this.control) {
@@ -1336,7 +1332,7 @@ class Editor {
async switchPreview() {
if (!this.switchPreviewToggle) {
this.switchPreviewToggle = true;
editorState.value = EditorStates.PREVIEW;
usePageEnigmaStore.getState().setEditorState(EditorStates.PREVIEW);
await this.generateFrame();
}
}
@@ -1348,7 +1344,7 @@ class Editor {
this.rawRenderPass
) {
this.switchPreviewToggle = false;
editorState.value = EditorStates.EDIT;
usePageEnigmaStore.getState().setEditorState(EditorStates.EDIT);
setTimeout(() => {
// if (!this.canvasRenderCamReference) {
// this.canvasRenderCamReference =
@@ -2,7 +2,6 @@ import Scene from "./scene";
import Editor from "./editor";
import * as THREE from "three";
import { XYZ } from "../datastructures/common";
import { editorState } from "../signals/engine";
import { EditorStates } from "../enums";
import { hideObjectPanel } from "../signals";
import { usePageEnigmaStore } from "../PageEnigmaStore";
@@ -141,7 +140,7 @@ export class SceneUtils {
this.editor.selected = this.editor.cam_obj;
this.editor.publishSelect();
this.editor.updateSelectedUI();
editorState.value = EditorStates.CAMERA_VIEW;
usePageEnigmaStore.getState().setEditorState(EditorStates.CAMERA_VIEW);
if (this.editor.activeScene.hot_items) {
this.editor.activeScene.hot_items.forEach((element) => {
element.visible = false;
@@ -174,7 +173,7 @@ export class SceneUtils {
}
hideObjectPanel();
editorState.value = EditorStates.EDIT;
usePageEnigmaStore.getState().setEditorState(EditorStates.EDIT);
}
}
}
@@ -9,7 +9,7 @@ import { MMDAnimationHelper, Water } from "three/examples/jsm/Addons.js";
import { MediaFileType } from "../enums";
import { ChromaKeyMaterial } from "./chromakey";
import { LoadWithoutCors } from "@storyteller/tauri-api";
import { gridVisibility } from "../signals/engine";
import { usePageEnigmaStore } from "../PageEnigmaStore";
import { InfiniteGridHelper } from "./InfiniteGridHelper";
import { cameras, selectedCameraId } from "../signals/camera";
import { MediaFilesApi } from "@storyteller/api";
@@ -91,16 +91,15 @@ class Scene {
this.helper = new MMDAnimationHelper({ afterglow: 0.0 });
this.scene.userData["helper"] = this.helper;
// Subscribe to grid visibility changes
gridVisibility.subscribe((isVisible) => {
if (this.gridHelper) {
if (isVisible) {
if (!this.scene.children.includes(this.gridHelper)) {
this.scene.add(this.gridHelper);
}
} else {
this.scene.remove(this.gridHelper);
// Subscribe to grid visibility changes from the store
usePageEnigmaStore.subscribe((state, prev) => {
if (state.gridVisible === prev.gridVisible || !this.gridHelper) return;
if (state.gridVisible) {
if (!this.scene.children.includes(this.gridHelper)) {
this.scene.add(this.gridHelper);
}
} else {
this.scene.remove(this.gridHelper);
}
});
}
@@ -1093,9 +1092,9 @@ class Scene {
this.scene.add(this.groundPlane);
}
// Update grid visibility based on the signal
// Update grid visibility based on the store
updateGridVisibility() {
if (gridVisibility.value) {
if (usePageEnigmaStore.getState().gridVisible) {
if (!this.scene.children.includes(this.gridHelper)) {
this.scene.add(this.gridHelper);
}
@@ -1,6 +1,6 @@
import Editor from "./editor";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { editorState, previewSrc } from "../signals/engine";
import { usePageEnigmaStore } from "../PageEnigmaStore";
import { ToastTypes, ClipType } from "~/enums";
import { Visibility } from "./api_manager.js";
@@ -109,7 +109,7 @@ export class VideoGeneration {
});
}
previewSrc.value = "";
usePageEnigmaStore.getState().setPreviewSrc("");
this.editor.rawRenderer.setSize(
this.editor.render_width,
@@ -132,7 +132,7 @@ export class VideoGeneration {
if (!this.editor.camera_person_mode) {
this.editor.switchCameraView();
editorState.value = EditorStates.PREVIEW;
usePageEnigmaStore.getState().setEditorState(EditorStates.PREVIEW);
}
this.editor.generating_preview = false;
@@ -146,7 +146,7 @@ export class VideoGeneration {
this.editor.negative_prompt,
);
previewSrc.value = url;
usePageEnigmaStore.getState().setPreviewSrc(url);
return Promise.resolve(url);
} catch (err: unknown) {
const errorMessage =
@@ -61,6 +61,7 @@ import {
} from "~/components/signaled/TopBar/TopBar";
import { uploadPlaneFromMediaToken } from "~/components/reusable/UploadModalMedia/uploadPlane";
import { addObject, addCharacter } from "./actions";
import { usePageEnigmaStore } from "./PageEnigmaStore";
import { AssetType, AUTH_STATUS } from "~/enums";
import { v4 as uuidv4 } from "uuid";
import { MediaItem } from "~/pages/PageEnigma/models";
@@ -106,6 +107,7 @@ export const PageEditor = () => {
const tabStore = useTabStore();
const storyboardPageEnabled = useStoryboardPageEnabled();
const camAspect = usePageEnigmaStore((s) => s.cameraAspectRatio);
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
};
@@ -134,7 +136,7 @@ export const PageEditor = () => {
const scaleHeight = height < 610 ? height / 610 : 1;
if (
cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16 &&
camAspect === CameraAspectRatio.VERTICAL_9_16 &&
outlinerIsShowing.value &&
height < 900
) {
@@ -145,7 +147,7 @@ export const PageEditor = () => {
}
if (
cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1 &&
camAspect === CameraAspectRatio.SQUARE_1_1 &&
pageWidth.value < 2000
) {
return scaleHeight * 0.85;
@@ -1,4 +1,3 @@
import { useSignals } from "@preact/signals-react/runtime";
import {
faRectangle,
faRectangleVertical,
@@ -6,45 +5,42 @@ import {
} from "@fortawesome/pro-solid-svg-icons";
import { useContext } from "react";
import { CameraAspectRatio } from "~/pages/PageEnigma/enums";
import { cameraAspectRatio } from "~/pages/PageEnigma/signals";
import { setCameraAspect } from "~/pages/PageEnigma/actions";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext";
import { ButtonDropdown } from "@storyteller/ui-button-dropdown";
const ICON_BY_RATIO: Record<CameraAspectRatio, typeof faRectangle> = {
[CameraAspectRatio.HORIZONTAL_16_9]: faRectangle,
[CameraAspectRatio.VERTICAL_9_16]: faRectangleVertical,
[CameraAspectRatio.HORIZONTAL_3_2]: faRectangle,
[CameraAspectRatio.VERTICAL_2_3]: faRectangleVertical,
[CameraAspectRatio.SQUARE_1_1]: faSquare,
};
const LABEL_BY_RATIO: Record<CameraAspectRatio, string> = {
[CameraAspectRatio.HORIZONTAL_16_9]: "16:9 Horizontal",
[CameraAspectRatio.VERTICAL_9_16]: "9:16 Vertical",
[CameraAspectRatio.HORIZONTAL_3_2]: "3:2 Horizontal",
[CameraAspectRatio.VERTICAL_2_3]: "2:3 Vertical",
[CameraAspectRatio.SQUARE_1_1]: "1:1 Squared",
};
export const AspectRatioMenu = () => {
useSignals();
const aspect = usePageEnigmaStore((s) => s.cameraAspectRatio);
const editor = useContext(EngineContext);
const buttonIcon =
cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9
? faRectangle
: cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16
? faRectangleVertical
: cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2
? faRectangle
: cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3
? faRectangleVertical
: faSquare;
const buttonText =
cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9
? "16:9 Horizontal"
: cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16
? "9:16 Vertical"
: cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2
? "3:2 Horizontal"
: cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3
? "2:3 Vertical"
: "1:1 Squared";
const handleChangeAspectRatio = (newRatio: CameraAspectRatio) => {
if (!editor) return;
setCameraAspect(editor, newRatio);
};
return (
<div className="absolute right-0 top-0 m-2 flex flex-col items-end">
<ButtonDropdown
label={`${buttonText}`}
label={LABEL_BY_RATIO[aspect]}
className="shadow-xl"
icon={buttonIcon}
icon={ICON_BY_RATIO[aspect]}
align="right"
showSelected={true}
options={[
@@ -53,8 +49,7 @@ export const AspectRatioMenu = () => {
icon: faRectangle,
className: "pl-4",
description: "Horizontal",
selected:
cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9,
selected: aspect === CameraAspectRatio.HORIZONTAL_16_9,
onClick: () =>
handleChangeAspectRatio(CameraAspectRatio.HORIZONTAL_16_9),
},
@@ -63,8 +58,7 @@ export const AspectRatioMenu = () => {
icon: faRectangle,
className: "pl-4",
description: "Horizontal",
selected:
cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_3_2,
selected: aspect === CameraAspectRatio.HORIZONTAL_3_2,
onClick: () =>
handleChangeAspectRatio(CameraAspectRatio.HORIZONTAL_3_2),
},
@@ -73,8 +67,7 @@ export const AspectRatioMenu = () => {
icon: faRectangleVertical,
className: "pl-4",
description: "Vertical",
selected:
cameraAspectRatio.value === CameraAspectRatio.VERTICAL_2_3,
selected: aspect === CameraAspectRatio.VERTICAL_2_3,
onClick: () =>
handleChangeAspectRatio(CameraAspectRatio.VERTICAL_2_3),
},
@@ -83,8 +76,7 @@ export const AspectRatioMenu = () => {
icon: faRectangleVertical,
className: "pl-4",
description: "Vertical",
selected:
cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16,
selected: aspect === CameraAspectRatio.VERTICAL_9_16,
onClick: () =>
handleChangeAspectRatio(CameraAspectRatio.VERTICAL_9_16),
},
@@ -93,7 +85,7 @@ export const AspectRatioMenu = () => {
icon: faSquare,
className: "pl-4",
description: "Squared",
selected: cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1,
selected: aspect === CameraAspectRatio.SQUARE_1_1,
onClick: () =>
handleChangeAspectRatio(CameraAspectRatio.SQUARE_1_1),
},
@@ -17,7 +17,7 @@ import {
import { EngineContext } from "~/pages/PageEnigma/contexts/EngineContext";
import { InputVector } from "@storyteller/ui-input";
import { Button } from "@storyteller/ui-button";
import { editorState } from "~/pages/PageEnigma/signals/engine";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import { twMerge } from "tailwind-merge";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { sanitize } from "./utils/sanitize";
@@ -43,6 +43,7 @@ const defaultAxises: Record<string, string> = {
export const ControlPanelSceneObject = () => {
useSignals();
const editorState = usePageEnigmaStore((s) => s.editorState);
const { isShowing, currentObject } = objectPanelSignals;
const editorEngine = useContext(EngineContext);
@@ -114,7 +115,7 @@ export const ControlPanelSceneObject = () => {
setColor(editorEngine?.selected?.userData.color);
}, [currentSceneObject, editorEngine]);
if (!currentSceneObject || editorState.value === EditorStates.PREVIEW) {
if (!currentSceneObject || editorState === EditorStates.PREVIEW) {
return null;
}
@@ -23,7 +23,7 @@ import { Transition } from "@headlessui/react";
import { twMerge } from "tailwind-merge";
import { useSignals } from "@preact/signals-react/runtime";
import { EngineContext } from "../../contexts/EngineContext";
import { cameraAspectRatio } from "../../signals";
import { usePageEnigmaStore } from "../../PageEnigmaStore";
import { pageHeight, pageWidth } from "~/signals";
import { CameraAspectRatio } from "../../enums";
import { effect } from "@preact/signals-react";
@@ -121,6 +121,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
export const Outliner = () => {
useSignals();
const camAspect = usePageEnigmaStore((s) => s.cameraAspectRatio);
const [searchTerm, setSearchTerm] = useState("");
const [editorHeight, setEditorHeight] = useState(0);
@@ -158,9 +159,9 @@ export const Outliner = () => {
const getOutlinerHeightClass = () => {
if (pageWidth.value >= 2000) {
if (cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16) {
if (camAspect === CameraAspectRatio.VERTICAL_9_16) {
return `${editorHeight * 0.5 - 120}px`;
} else if (cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1) {
} else if (camAspect === CameraAspectRatio.SQUARE_1_1) {
return `${editorHeight * 0.42}px`;
} else {
return `${editorHeight * 0.54}px`;
@@ -168,9 +169,9 @@ export const Outliner = () => {
}
if (pageWidth.value < 2000) {
if (cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16) {
if (camAspect === CameraAspectRatio.VERTICAL_9_16) {
return `${editorHeight * 0.7 - 10}px`;
} else if (cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1) {
} else if (camAspect === CameraAspectRatio.SQUARE_1_1) {
return `${editorHeight * 0.7}px`;
} else {
return `${editorHeight * 0.7}px`;
@@ -7,14 +7,14 @@ import {
faCameraViewfinder,
faSpinnerThird,
} from "@fortawesome/pro-solid-svg-icons";
import { useShallow } from "zustand/shallow";
import {
cameraAspectRatio,
editorState,
editorLetterBox,
toggleEditorLetterBox,
sidePanelHeight,
outlinerIsShowing,
} from "~/pages/PageEnigma/signals";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import { pageWidth } from "~/signals";
import { CameraAspectRatio, EditorStates } from "~/pages/PageEnigma/enums";
import { CameraViewCanvas } from "~/pages/PageEnigma/comps/EngineCanvases";
@@ -25,26 +25,31 @@ import { useEffect } from "react";
export const PreviewEngineCamera = () => {
useSignals();
const { camAspect, editorState } = usePageEnigmaStore(
useShallow((s) => ({
camAspect: s.cameraAspectRatio,
editorState: s.editorState,
})),
);
const handleButtonCameraView = () => {
// TODO: wire to editor.toggleCameraView() once editor.ts is migrated.
};
const getLargeScreenHeightClass = () => {
if (cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16) {
if (camAspect === CameraAspectRatio.VERTICAL_9_16) {
return pageWidth.value >= 2000
? "w-44 justify-center"
: "w-36 justify-center";
} else {
return pageWidth.value >= 2000
? "w-72 justify-between"
: "w-64 justify-between";
}
return pageWidth.value >= 2000
? "w-72 justify-between"
: "w-64 justify-between";
};
const getSmallScreenHeightClass = () => {
if (
cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16 &&
camAspect === CameraAspectRatio.VERTICAL_9_16 &&
sidePanelHeight.value < 2000 &&
outlinerIsShowing.value
) {
@@ -54,23 +59,24 @@ export const PreviewEngineCamera = () => {
};
const getSquareAspectRatioClass = () => {
if (cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1) {
if (camAspect === CameraAspectRatio.SQUARE_1_1) {
return pageWidth.value >= 2000
? "w-[240px] justify-between"
: "w-60 justify-between";
}
return undefined;
};
useEffect(() => {
console.log("Editor state changed:", editorState.value);
}, [editorState.value]);
console.log("Editor state changed:", editorState);
}, [editorState]);
return (
<div
id="preview-engine-camera"
className={twMerge(
"hidden origin-bottom-left shadow-lg", //hidden right now with css
editorState.value === EditorStates.PREVIEW
editorState === EditorStates.PREVIEW
? "invisible h-0 w-0"
: "visible",
)}
@@ -86,17 +92,16 @@ export const PreviewEngineCamera = () => {
<div
className={twMerge(
"origin -z-10 flex h-auto w-full flex-wrap items-center gap-1.5 rounded-t-lg bg-ui-panel p-2 text-white",
cameraAspectRatio.value !== CameraAspectRatio.VERTICAL_9_16
camAspect !== CameraAspectRatio.VERTICAL_9_16
? "justify-between"
: "flex-col justify-center",
cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1 &&
"justify-center",
camAspect === CameraAspectRatio.SQUARE_1_1 && "justify-center",
)}
>
<div
className={twMerge(
"ms-1 flex grow items-center gap-2",
cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16 &&
camAspect === CameraAspectRatio.VERTICAL_9_16 &&
"-ms-1 justify-center",
)}
>
@@ -105,7 +110,7 @@ export const PreviewEngineCamera = () => {
</div>
<div className="flex gap-1.5">
{editorState.value === EditorStates.CAMERA_VIEW && (
{editorState === EditorStates.CAMERA_VIEW && (
<Tooltip content="Toggle Letterbox" position={"top"}>
<ButtonIcon
icon={editorLetterBox.value ? faBlinds : faBlindsRaised}
@@ -120,20 +125,18 @@ export const PreviewEngineCamera = () => {
onClick={handleButtonCameraView}
className="rounded-md px-2 py-1 text-sm"
>
{editorState.value === EditorStates.EDIT
? "Enter View"
: "Exit View"}
{editorState === EditorStates.EDIT ? "Enter View" : "Exit View"}
</Button>
</div>
</div>
<div
className={twMerge(
"relative overflow-hidden rounded-b-lg border border-gray-600",
cameraAspectRatio.value === CameraAspectRatio.HORIZONTAL_16_9
camAspect === CameraAspectRatio.HORIZONTAL_16_9
? "aspect-[16/9]"
: cameraAspectRatio.value === CameraAspectRatio.VERTICAL_9_16
: camAspect === CameraAspectRatio.VERTICAL_9_16
? "aspect-[9/16]"
: cameraAspectRatio.value === CameraAspectRatio.SQUARE_1_1
: camAspect === CameraAspectRatio.SQUARE_1_1
? "aspect-[1/1]"
: "aspect-video",
)}
@@ -1,52 +1,49 @@
import { useSignals } from "@preact/signals-react/runtime";
import { faSpinnerThird } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useShallow } from "zustand/shallow";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { pageHeight, pageWidth } from "~/signals";
import { editorState, previewSrc } from "~/pages/PageEnigma/signals/engine";
import {
sidePanelWidth,
sidePanelVisible,
} from "~/pages/PageEnigma/signals";
import { sidePanelWidth, sidePanelVisible } from "~/pages/PageEnigma/signals";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
export const PreviewFrameImage = () => {
useSignals();
if (editorState.value === EditorStates.PREVIEW) {
if (previewSrc.value === "") {
return (
<div
className="absolute inset-0"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - 56,
}}
>
<div className="relative flex h-full w-full flex-col items-center justify-center gap-5">
<span className="absolute h-full w-full bg-black opacity-50" />
<FontAwesomeIcon icon={faSpinnerThird} spin size="4x" />
<h3 className="z-20 text-white">Generating Preview...</h3>
</div>
</div>
);
} else {
return (
<img
alt="preview of the art style that renders over the 3d scene"
className="absolute inset-0 object-cover"
src={previewSrc.value}
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - 56,
}}
/>
);
}
const { editorState, previewSrc } = usePageEnigmaStore(
useShallow((s) => ({
editorState: s.editorState,
previewSrc: s.previewSrc,
})),
);
if (editorState !== EditorStates.PREVIEW) {
return null;
}
return null;
const width =
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84;
const height = pageHeight.value - 56;
if (previewSrc === "") {
return (
<div className="absolute inset-0" style={{ width, height }}>
<div className="relative flex h-full w-full flex-col items-center justify-center gap-5">
<span className="absolute h-full w-full bg-black opacity-50" />
<FontAwesomeIcon icon={faSpinnerThird} spin size="4x" />
<h3 className="z-20 text-white">Generating Preview...</h3>
</div>
</div>
);
}
return (
<img
alt="preview of the art style that renders over the 3d scene"
className="absolute inset-0 object-cover"
src={previewSrc}
style={{ width, height }}
/>
);
};
@@ -1,6 +1,5 @@
import { useSignals } from "@preact/signals-react/runtime";
import { twMerge } from "tailwind-merge";
import { cameraAspectRatio } from "~/pages/PageEnigma/signals";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import {
calcMatteWidth,
@@ -18,32 +17,11 @@ export const Letterbox = ({
width: number;
height: number;
}) => {
useSignals();
const camAspect = usePageEnigmaStore((s) => s.cameraAspectRatio);
// if (editorState.value !== EditorStates.CAMERA_VIEW || !isShowing) {
// //case of Letterbox should not show
// return;
// }
const matteOri = getMatteOrientation({
camAspect: cameraAspectRatio.value,
width,
height,
});
const matteWidth = calcMatteWidth({
matteOri,
camAspect: cameraAspectRatio.value,
width,
height,
});
const matteHeight = calcMatteHeight({
matteOri,
camAspect: cameraAspectRatio.value,
width,
height,
});
const matteOri = getMatteOrientation({ camAspect, width, height });
const matteWidth = calcMatteWidth({ matteOri, camAspect, width, height });
const matteHeight = calcMatteHeight({ matteOri, camAspect, width, height });
return (
<div
@@ -1,12 +1,13 @@
import { useSignals } from "@preact/signals-react/runtime";
import { twMerge } from "tailwind-merge";
import { useShallow } from "zustand/shallow";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FeatureFlags, TabTitles } from "~/enums";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { editorState, setEditorState } from "~/pages/PageEnigma/signals/engine";
import { sidePanelVisible } from "~/pages/PageEnigma/signals/sidePanel";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import { TabItem } from "../SidePanel/tabList";
import { currentPage, pageHeight } from "~/signals";
@@ -23,6 +24,12 @@ export const SidePanelMenu = ({
}) => {
useSignals();
const showStylePage = usePosthogFeatureFlag(FeatureFlags.SHOW_STYLE_PAGE);
const { editorState, setEditorState } = usePageEnigmaStore(
useShallow((s) => ({
editorState: s.editorState,
setEditorState: s.setEditorState,
})),
);
return (
<div
className={twMerge(
@@ -60,7 +67,7 @@ export const SidePanelMenu = ({
if (!sidePanelVisible.value) {
sidePanelVisible.value = true;
}
if (editorState.value === EditorStates.PREVIEW) {
if (editorState === EditorStates.PREVIEW) {
setEditorState(EditorStates.EDIT);
}
}}
@@ -4,13 +4,20 @@ import {
faArrowsRotate,
faChevronLeft,
} from "@fortawesome/pro-solid-svg-icons";
import { editorState, previewSrc, setEditorState } from "~/pages/PageEnigma/signals/engine";
import { useShallow } from "zustand/shallow";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { useSignals } from "@preact/signals-react/runtime";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
import { StyleStrength } from "~/pages/PageEnigma/comps/SidePanelTabs/tabComps/StylizeTab/StyleStrength";
import { StyleOptions } from "~/pages/PageEnigma/comps/SidePanelTabs/tabComps/StylizeTab/StyleOptions";
export function StyleButtons() {
useSignals();
const { editorState, previewSrc, setEditorState } = usePageEnigmaStore(
useShallow((s) => ({
editorState: s.editorState,
previewSrc: s.previewSrc,
setEditorState: s.setEditorState,
})),
);
const switchPreview = async () => {
setEditorState(EditorStates.PREVIEW);
@@ -32,19 +39,17 @@ export function StyleButtons() {
<div className="mb-2 text-xs text-white/70">
(This helps you test and re-test your scene)
</div>
{editorState.value !== EditorStates.PREVIEW && (
<>
<Button
icon={faArrowsRotate}
variant="action"
className="mt-1.5 w-full"
onClick={switchPreview}
>
Preview Frame
</Button>
</>
{editorState !== EditorStates.PREVIEW && (
<Button
icon={faArrowsRotate}
variant="action"
className="mt-1.5 w-full"
onClick={switchPreview}
>
Preview Frame
</Button>
)}
{editorState.value === EditorStates.PREVIEW && (
{editorState === EditorStates.PREVIEW && (
<div className="flex gap-2">
<Button
icon={faChevronLeft}
@@ -58,9 +63,9 @@ export function StyleButtons() {
variant="primary"
onClick={refreshPreview}
className="grow"
loading={previewSrc.value === ""}
loading={previewSrc === ""}
>
{previewSrc.value === "" ? "Rendering..." : "Re-render Preview"}
{previewSrc === "" ? "Rendering..." : "Re-render Preview"}
</Button>
</div>
)}
@@ -1,11 +1,11 @@
import { signal } from "@preact/signals-core";
import { CameraAspectRatio, EditorStates } from "~/pages/PageEnigma/enums";
import { usePageEnigmaStore } from "~/pages/PageEnigma/PageEnigmaStore";
export const editorState = signal<EditorStates>(EditorStates.EDIT);
export const setEditorState = (newState: EditorStates) => {
editorState.value = newState;
};
// These signals exist only to satisfy PromptBox3D's signal-typed props.
// Components inside PageEnigma read from the Zustand store; the setters
// below dual-write into the store so PromptBox3D's onChange callbacks
// keep the store authoritative.
export const cameraAspectRatio = signal<CameraAspectRatio>(
CameraAspectRatio.HORIZONTAL_3_2,
@@ -13,11 +13,12 @@ export const cameraAspectRatio = signal<CameraAspectRatio>(
export const setCameraAspectRatio = (newAspectRatio: CameraAspectRatio) => {
cameraAspectRatio.value = newAspectRatio;
usePageEnigmaStore.getState().setCameraAspectRatio(newAspectRatio);
};
export const previewSrc = signal("");
export const gridVisibility = signal<boolean>(true);
export const setGridVisibility = (isVisible: boolean) => {
gridVisibility.value = isVisible;
usePageEnigmaStore.getState().setGridVisible(isVisible);
};