aboutsummaryrefslogtreecommitdiff
path: root/src/store/store.ts
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2026-01-30 11:31:24 +0100
committerRuben Beltran del Rio <jj@r.bdr.sh>2026-01-30 13:05:48 +0100
commit0d4b61993cba37c180ac91e40a0fb362711de8de (patch)
treef41dd68420b51766c1ca6fc1ee2c20dc5df830d1 /src/store/store.ts
Initial implementation
Diffstat (limited to 'src/store/store.ts')
-rw-r--r--src/store/store.ts119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/store/store.ts b/src/store/store.ts
new file mode 100644
index 0000000..94519e4
--- /dev/null
+++ b/src/store/store.ts
@@ -0,0 +1,119 @@
+import { create } from "zustand";
+import { availableModels } from "../data/availableModels.ts";
+
+export interface Vector3D {
+ x: number;
+ y: number;
+ z: number;
+}
+
+export interface HueShift {
+ red: number;
+ green: number;
+ blue: number;
+}
+
+export interface Model3D {
+ id: string;
+ sourceId: string;
+ name: string;
+ url: string;
+ position: Vector3D;
+ rotation: Vector3D;
+ hueShift: HueShift;
+}
+
+export interface AvailableModel {
+ id: string;
+ name: string;
+ url: string;
+}
+
+const CAMERA_VIEWS = [
+ "perspective",
+ "ortho-top",
+ "ortho-front",
+ "ortho-side",
+] as const;
+export type CameraView = (typeof CAMERA_VIEWS)[number];
+
+export function isValidCameraView(value: string): value is CameraView {
+ return CAMERA_VIEWS.includes(value as CameraView);
+}
+
+export interface StoreState {
+ models: Model3D[];
+ selectedModelId: string | null;
+ cameraView: CameraView;
+ backdropUrl: string | null;
+ availableModels: AvailableModel[];
+ addModel: (sourceId: string, position: Vector3D) => void;
+ removeModel: (id: string) => void;
+ selectModel: (id: string | null) => void;
+ updateModel: (
+ id: string,
+ updates: Partial<Pick<Model3D, "position" | "rotation" | "hueShift">>,
+ ) => void;
+ setCameraView: (view: CameraView) => void;
+ setBackdrop: (url: string | null) => void;
+}
+
+export const useStore = create<StoreState>((set, get) => ({
+ models: [],
+ selectedModelId: null,
+ cameraView: "perspective",
+ backdropUrl: null,
+ availableModels,
+
+ addModel: (sourceId: string, position: Vector3D) => {
+ const { availableModels } = get();
+ const source = availableModels.find((model) => model.id === sourceId);
+ if (!source) return;
+
+ const newModel: Model3D = {
+ id: crypto.randomUUID(),
+ sourceId,
+ name: source.name,
+ url: source.url,
+ position,
+ rotation: { x: 0, y: 0, z: 0 },
+ hueShift: { red: 0, green: 0, blue: 0 },
+ };
+
+ set((state) => ({
+ models: [...state.models, newModel],
+ selectedModelId: newModel.id,
+ }));
+ },
+
+ removeModel: (id: string) => {
+ set((state) => ({
+ models: state.models.filter((model) => model.id !== id),
+ selectedModelId:
+ state.selectedModelId === id ? null : state.selectedModelId,
+ }));
+ },
+
+ selectModel: (id: string | null) => {
+ set({ selectedModelId: id });
+ },
+
+ updateModel: (
+ id: string,
+ updates: Partial<Pick<Model3D, "position" | "rotation" | "hueShift">>,
+ ) => {
+ set((state) => ({
+ models: state.models.map((model) =>
+ model.id === id ? { ...model, ...updates } : model,
+ ),
+ }));
+ },
+
+ setCameraView: (view: CameraView) => {
+ set({ cameraView: view });
+ },
+
+ setBackdrop: (url: string | null) => {
+ set({ backdropUrl: url });
+ },
+}));