From 6d3ec8bfeea5f59fec70d63b5dc491fdda6abb23 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Wed, 11 Mar 2026 14:17:12 +0700 Subject: [PATCH] "Make 3D" button to not enqueue directly but go to 3D object page --- .../experiences/ImageTo3DExperience.tsx | 50 ++++++++++++------- .../src/components/signaled/TopBar/TopBar.tsx | 19 +++++++ .../PageImageTo3DObject/ImageTo3DStore.ts | 15 ++++++ .../gallery-modal/src/lib/gallery-modal.tsx | 6 +++ .../lightbox-modal/src/lib/lightbox-modal.tsx | 49 ++++++++++-------- 5 files changed, 100 insertions(+), 39 deletions(-) diff --git a/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx b/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx index 624bf1bf21..f652d1f1cf 100644 --- a/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx +++ b/frontend/apps/artcraft/app/src/components/experiences/ImageTo3DExperience.tsx @@ -140,6 +140,12 @@ export const ImageTo3DExperience = ({ const clearPendingExternalImage = useImageTo3DWorldStore( (s) => s.clearPendingExternalImage, ); + const pendingObjectImage = useImageTo3DStore( + (s) => s.pendingExternalImage, + ); + const clearPendingObjectImage = useImageTo3DStore( + (s) => s.clearPendingExternalImage, + ); const results = variant === "object" ? objectResults : worldResults; const resetResults = variant === "object" ? objectReset : worldReset; @@ -153,24 +159,34 @@ export const ImageTo3DExperience = ({ useEffect(() => { // Read from store directly to avoid Strict Mode double-execution adding the image twice - const pending = useImageTo3DWorldStore.getState().pendingExternalImage; - if (variant === "world" && pending) { - setWorldImages((prev) => { - if (prev.length >= MAX_WORLD_IMAGES) return prev; - return [ - ...prev, - { - id: generateId(), - preview: pending.url, - mediaToken: pending.mediaToken, - name: "Library Image", - isUploading: false, - }, - ]; - }); - clearPendingExternalImage(); + if (variant === "world") { + const pending = useImageTo3DWorldStore.getState().pendingExternalImage; + if (pending) { + setWorldImages((prev) => { + if (prev.length >= MAX_WORLD_IMAGES) return prev; + return [ + ...prev, + { + id: generateId(), + preview: pending.url, + mediaToken: pending.mediaToken, + name: "Library Image", + isUploading: false, + }, + ]; + }); + clearPendingExternalImage(); + } + } else if (variant === "object") { + const pending = useImageTo3DStore.getState().pendingExternalImage; + if (pending) { + setUploadedPreview(pending.url); + setUploadedMediaToken(pending.mediaToken); + setUploadedName("Library Image"); + clearPendingObjectImage(); + } } - }, [variant, pendingExternalImage, clearPendingExternalImage]); + }, [variant, pendingExternalImage, clearPendingExternalImage, pendingObjectImage, clearPendingObjectImage]); useEffect(() => { const onResize = () => setVh(window.innerHeight); diff --git a/frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx b/frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx index 3692b1dbcf..4271ebe9ac 100644 --- a/frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx +++ b/frontend/apps/artcraft/app/src/components/signaled/TopBar/TopBar.tsx @@ -63,6 +63,7 @@ import { is3DSceneLoaded, set3DPageMounted, } from "~/pages/PageEnigma/Editor/editor"; +import { useImageTo3DStore } from "~/pages/PageImageTo3DObject/ImageTo3DStore"; import { useImageTo3DWorldStore } from "~/pages/PageImageTo3DWorld/ImageTo3DWorldStore"; import { useRemoveBackgroundStore } from "~/pages/PageRemoveBackground/RemoveBackgroundStore"; import { TabId, useTabStore } from "~/pages/Stores/TabState"; @@ -274,6 +275,23 @@ export const TopBar = ({ pageName }: Props) => { } }; + const handleMake3DObjectFromGallery = async ( + url: string, + mediaId?: string, + ) => { + try { + if (mediaId) { + useImageTo3DStore.getState().setPendingExternalImage(url, mediaId); + } + useTabStore.getState().setActiveTab("IMAGE_TO_3D_OBJECT"); + galleryModalVisibleViewMode.value = false; + galleryModalVisibleDuringDrag.value = false; + galleryModalLightboxVisible.value = false; + } catch (e) { + // no-op + } + }; + const handleMake3DWorldFromGallery = async ( url: string, mediaId?: string, @@ -620,6 +638,7 @@ export const TopBar = ({ pageName }: Props) => { onEditClicked={handleEditFromGallery} onTurnIntoVideoClicked={handleTurnIntoVideoFromGallery} onRemoveBackgroundClicked={handleRemoveBackgroundFromGallery} + onMake3DObjectClicked={handleMake3DObjectFromGallery} onMake3DWorldClicked={handleMake3DWorldFromGallery} /> diff --git a/frontend/apps/artcraft/app/src/pages/PageImageTo3DObject/ImageTo3DStore.ts b/frontend/apps/artcraft/app/src/pages/PageImageTo3DObject/ImageTo3DStore.ts index 01768dd573..ea3a6e4079 100644 --- a/frontend/apps/artcraft/app/src/pages/PageImageTo3DObject/ImageTo3DStore.ts +++ b/frontend/apps/artcraft/app/src/pages/PageImageTo3DObject/ImageTo3DStore.ts @@ -129,6 +129,11 @@ async function captureModelThumbnail(modelUrl: string): Promise { }); } +export interface PendingExternalImage { + url: string; + mediaToken: string; +} + export type ImageTo3DResult = { id: string; mode: "image" | "text"; @@ -149,6 +154,9 @@ export const coverImageCache = new Map(); type ImageTo3DState = { results: ImageTo3DResult[]; + pendingExternalImage: PendingExternalImage | null; + setPendingExternalImage: (url: string, mediaToken: string) => void; + clearPendingExternalImage: () => void; startGeneration: ( mode: "image" | "text", note: string, @@ -175,6 +183,13 @@ type ImageTo3DState = { export const useImageTo3DStore = create((set, get) => ({ results: [], + pendingExternalImage: null, + setPendingExternalImage: (url: string, mediaToken: string) => { + set({ pendingExternalImage: { url, mediaToken } }); + }, + clearPendingExternalImage: () => { + set({ pendingExternalImage: null }); + }, startGeneration: ( mode: "image" | "text", note: string, diff --git a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx index ec772346c6..53fbf7c7dd 100644 --- a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx +++ b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx @@ -241,6 +241,10 @@ interface GalleryModalProps { url: string, media_id?: string, ) => Promise | void; + onMake3DObjectClicked?: ( + url: string, + media_id?: string, + ) => Promise | void; onMake3DWorldClicked?: ( url: string, media_id?: string, @@ -356,6 +360,7 @@ export const GalleryModal = React.memo( onEditClicked, onTurnIntoVideoClicked, onRemoveBackgroundClicked, + onMake3DObjectClicked, onMake3DWorldClicked, }: GalleryModalProps) => { const [loading, setLoading] = useState(false); @@ -1286,6 +1291,7 @@ export const GalleryModal = React.memo( onEditClicked={onEditClicked} onTurnIntoVideoClicked={onTurnIntoVideoClicked} onRemoveBackgroundClicked={onRemoveBackgroundClicked} + onMake3DObjectClicked={onMake3DObjectClicked} onMake3DWorldClicked={onMake3DWorldClicked} onNavigatePrev={handleNavigatePrev} onNavigateNext={handleNavigateNext} diff --git a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx index 39d4e49f3c..d7dabd5c28 100644 --- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx +++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx @@ -15,11 +15,7 @@ import { faVideo, faWandMagicSparkles, } from "@fortawesome/pro-solid-svg-icons"; -import { - EnqueueImageTo3dObject, - EnqueueImageTo3dObjectModel, - MediaFileDelete, -} from "@storyteller/tauri-api"; +import { MediaFileDelete } from "@storyteller/tauri-api"; import { LoadingSpinner } from "@storyteller/ui-loading-spinner"; import { Viewer3D } from "@storyteller/ui-viewer-3d"; import { @@ -85,6 +81,10 @@ interface LightboxModalProps { url: string, media_id?: string, ) => Promise | void; + onMake3DObjectClicked?: ( + url: string, + media_id?: string, + ) => Promise | void; onMake3DWorldClicked?: ( url: string, media_id?: string, @@ -115,6 +115,7 @@ export function LightboxModal({ onEditClicked, onTurnIntoVideoClicked, onRemoveBackgroundClicked, + onMake3DObjectClicked, onMake3DWorldClicked, batchImageToken, onNavigatePrev, @@ -985,23 +986,27 @@ export function LightboxModal({ )} - {derivedMediaClass === "image" && ( - - )} + {onMake3DObjectClicked && + actionUrl && + derivedMediaClass === "image" && ( + + )} {onMake3DWorldClicked && actionUrl &&