Compare commits

...
Author SHA1 Message Date
bflatastic 6428fe0319 PageEditor comments for clarity on image drop functionality 2025-05-23 01:25:56 +07:00
bflatastic 043dc47979 bug fix test 2 2025-05-23 01:24:43 +07:00
bflatastic 6eecd20587 bug fix test 1 2025-05-23 00:30:34 +07:00
13 changed files with 121 additions and 46 deletions
@@ -0,0 +1,8 @@
{
"build": {
"frontendDist": "../../../frontend/apps/editor3d/dist",
"beforeDevCommand": "echo 'no dev'",
"devUrl": "http://localhost:5173",
"beforeBuildCommand": "npx nx run editor3d:build"
}
}
@@ -12,6 +12,7 @@ import { getCurrentLocationWithoutParams } from "~/utilities";
import { Activity } from "~/pages/PageEnigma/comps/GenerateModals/Activity";
import {
GalleryModal,
galleryModalVisibleViewMode,
galleryModalVisibleDuringDrag,
} from "@storyteller/ui-gallery-modal";
import { SettingsModal } from "@storyteller/ui-settings-modal";
@@ -110,6 +111,7 @@ export const TopBar = ({
};
const handleOpenGalleryModal = () => {
galleryModalVisibleViewMode.value = true;
galleryModalVisibleDuringDrag.value = true;
};
@@ -185,8 +187,6 @@ export const TopBar = ({
/>
<GalleryModal
isOpen={galleryModalVisibleDuringDrag.value}
onClose={() => (galleryModalVisibleDuringDrag.value = false)}
mode="view"
onDownloadClicked={downloadFileFromUrl}
onAddToSceneClicked={handleAddToScene}
@@ -62,6 +62,22 @@ import { v4 as uuidv4 } from "uuid";
import { MediaItem } from "~/pages/PageEnigma/models";
import { GalleryItem } from "../../../../../../libs/components/gallery-modal/src/lib/gallery-modal";
import { UploaderState } from "~/models";
import * as THREE from "three";
// Utility to convert screen (client) coordinates to world coordinates for the image drop
function screenToWorld(
x: number,
y: number,
camera: THREE.Camera,
renderer: THREE.WebGLRenderer,
): THREE.Vector3 {
const rect = renderer.domElement.getBoundingClientRect();
const ndcX = ((x - rect.left) / rect.width) * 2 - 1;
const ndcY = -((y - rect.top) / rect.height) * 2 + 1;
const vector = new THREE.Vector3(ndcX, ndcY, 0.5); // z=0.5 for middle of the frustum
vector.unproject(camera);
return vector;
}
export const PageEditor = () => {
useSignals();
@@ -78,6 +94,7 @@ export const PageEditor = () => {
};
}, []);
// Image drop from gallery/library modal
useEffect(() => {
setOnImageDrop(
async (item: GalleryItem, _position: { x: number; y: number }) => {
@@ -91,12 +108,30 @@ export const PageEditor = () => {
},
});
console.log("Upload complete, adding object to scene");
// Calculate world position from cursor
let worldPosition = undefined;
if (editorEngine?.camera && editorEngine?.renderer && _position) {
worldPosition = screenToWorld(
_position.x,
_position.y,
editorEngine.camera,
editorEngine.renderer,
);
}
const mediaItem: MediaItem = {
version: 1,
type: AssetType.OBJECT,
media_id: item.id || uuidv4(),
name: item.label || "Image Plane",
// Add other fields as needed
...(worldPosition && {
position: {
x: worldPosition.x,
y: worldPosition.y,
z: worldPosition.z,
},
}),
};
addObject(mediaItem);
console.log("Object added to scene:", mediaItem);
+3 -6
View File
@@ -37,9 +37,6 @@
{
"path": "../../libs/components/close-button"
},
{
"path": "../../libs/tauri-api"
},
{
"path": "../../libs/components/toaster"
},
@@ -61,6 +58,9 @@
{
"path": "../../libs/common"
},
{
"path": "../../libs/tauri-api"
},
{
"path": "../../libs/components/tab-selector"
},
@@ -84,9 +84,6 @@
},
{
"path": "../../libs/tauri-utils"
},
{
"path": "../../libs/build-env"
}
]
}
@@ -15,6 +15,7 @@ import { useSignals } from "@preact/signals-react/runtime";
import {
galleryModalVisibleDuringDrag,
galleryReopenAfterDragSignal,
galleryModalVisibleViewMode,
} from "./galleryModalSignals";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
@@ -46,8 +47,7 @@ interface GroupedItems {
type ModalMode = "select" | "view";
interface GalleryModalProps {
isOpen: boolean;
onClose: () => void;
onClose?: () => void;
mode: ModalMode;
selectedItemIds?: string[];
onSelectItem?: (id: string) => void;
@@ -58,11 +58,11 @@ interface GalleryModalProps {
url: string,
media_id: string | undefined
) => Promise<void>;
isOpen?: boolean;
}
export const GalleryModal = React.memo(
({
isOpen,
onClose,
mode = "view",
selectedItemIds = [],
@@ -71,6 +71,7 @@ export const GalleryModal = React.memo(
onUseSelected,
onDownloadClicked,
onAddToSceneClicked,
isOpen,
}: GalleryModalProps) => {
const [loading, setLoading] = useState(false);
const [lightboxImage, setLightboxImage] = useState<GalleryItem | null>(
@@ -145,10 +146,10 @@ export const GalleryModal = React.memo(
setUsername(session.data.user.username);
}
};
if (isOpen) {
if (isOpen || (mode === "view" && galleryModalVisibleViewMode.value)) {
getUsername();
}
}, [usersApi, isOpen]);
}, [usersApi, mode, galleryModalVisibleViewMode.value, isOpen]);
// filter state
const FILTERS = [
@@ -286,7 +287,12 @@ export const GalleryModal = React.memo(
// Soft refresh on open: fetch first page and prepend new items
useEffect(() => {
if (isOpen && allItems.length > 0 && username) {
if (
mode === "view" &&
galleryModalVisibleViewMode.value &&
allItems.length > 0 &&
username
) {
const fetchLatest = async () => {
const filterMediaClasses = getFilterMediaClass(activeFilter);
const response = await api.listUserMediaFiles({
@@ -327,17 +333,26 @@ export const GalleryModal = React.memo(
fetchLatest();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen]);
}, [mode, galleryModalVisibleViewMode.value]);
return (
<>
<Modal
isOpen={
mode === "view"
? isOpen && galleryModalVisibleDuringDrag.value
: isOpen
? galleryModalVisibleViewMode.value &&
galleryModalVisibleDuringDrag.value
: typeof isOpen === "boolean"
? isOpen
: true
}
onClose={onClose}
onClose={() => {
if (mode === "view") {
onClose?.() || (galleryModalVisibleViewMode.value = false);
} else {
onClose?.();
}
}}
className={twMerge(
"h-[620px] max-w-4xl",
mode === "view" && "h-[640px] max-w-4xl"
@@ -440,7 +455,10 @@ export const GalleryModal = React.memo(
showIconsInList={true}
/>
{mode === "view" && <Modal.ExpandButton />}
<CloseButton onClick={onClose} className="relative z-[51]" />
<CloseButton
onClick={() => (galleryModalVisibleViewMode.value = false)}
className="relative z-[51]"
/>
</div>
</div>
</div>
@@ -541,7 +559,11 @@ export const GalleryModal = React.memo(
<LightboxModal
isOpen={isLightboxVisible}
onClose={handleCloseLightbox}
onCloseGallery={onClose}
onCloseGallery={() =>
mode === "view"
? (galleryModalVisibleViewMode.value = false)
: onClose?.()
}
imageUrl={imageUrl}
imageAlt={lightboxImage?.label || ""}
onImageError={() => imageUrl && handleImageError(imageUrl)}
@@ -1,4 +1,5 @@
import { signal } from "@preact/signals-react";
export const galleryModalVisibleDuringDrag = signal(false);
export const galleryModalVisibleDuringDrag = signal(true);
export const galleryReopenAfterDragSignal = signal(false);
export const galleryModalVisibleViewMode = signal(false);
@@ -2,6 +2,12 @@
"files": [],
"include": [],
"references": [
{
"path": "../slider-v2"
},
{
"path": "../popover"
},
{
"path": "../../api"
},
@@ -14,15 +20,15 @@
{
"path": "../button"
},
{
"path": "../tab-selector"
},
{
"path": "../lightbox-modal"
},
{
"path": "../modal"
},
{
"path": "../tooltip"
},
{
"path": "./tsconfig.lib.json"
},
@@ -43,6 +43,12 @@
"src/**/*.tsx"
],
"references": [
{
"path": "../slider-v2/tsconfig.lib.json"
},
{
"path": "../popover/tsconfig.lib.json"
},
{
"path": "../../api/tsconfig.lib.json"
},
@@ -55,14 +61,14 @@
{
"path": "../button/tsconfig.lib.json"
},
{
"path": "../tab-selector/tsconfig.lib.json"
},
{
"path": "../lightbox-modal/tsconfig.lib.json"
},
{
"path": "../modal/tsconfig.lib.json"
},
{
"path": "../tooltip/tsconfig.lib.json"
}
]
}
@@ -2,6 +2,9 @@
"files": [],
"include": [],
"references": [
{
"path": "../../tauri-api"
},
{
"path": "../button"
},
@@ -43,6 +43,9 @@
"src/**/*.tsx"
],
"references": [
{
"path": "../../tauri-api/tsconfig.lib.json"
},
{
"path": "../button/tsconfig.lib.json"
},
@@ -82,7 +82,6 @@ export const PromptBox2D = ({
const [prompt, setPrompt] = useState("");
const [isEnqueueing, setIsEnqueueing] = useState(false);
const [useSystemPrompt, setUseSystemPrompt] = useState(true);
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
const [selectedGalleryImages, setSelectedGalleryImages] = useState<string[]>(
[]
);
@@ -111,6 +110,8 @@ export const PromptBox2D = ({
const fileInputRef = useRef<HTMLInputElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
@@ -185,9 +186,7 @@ export const PromptBox2D = ({
fileInputRef.current?.click();
};
const handleGallerySelect = () => {
setIsGalleryModalOpen(true);
};
const handleGallerySelect = () => setIsGalleryModalOpen(true);
const handleGalleryClose = () => {
setIsGalleryModalOpen(false);
@@ -638,7 +637,7 @@ export const PromptBox2D = ({
</div>
</div>
<GalleryModal
isOpen={isGalleryModalOpen}
isOpen={!!isGalleryModalOpen}
onClose={handleGalleryClose}
mode="select"
selectedItemIds={selectedGalleryImages}
@@ -139,10 +139,10 @@ export const PromptBox3D = ({
},
]);
const [isCameraSettingsOpen, setIsCameraSettingsOpen] = useState(false);
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
const [selectedGalleryImages, setSelectedGalleryImages] = useState<string[]>(
[]
);
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
useEffect(() => {
if (textareaRef.current) {
@@ -257,10 +257,7 @@ export const PromptBox3D = ({
fileInputRef.current?.click();
};
const handleGallerySelect = () => {
setIsGalleryModalOpen(true);
};
const handleGallerySelect = () => setIsGalleryModalOpen(true);
const handleGalleryClose = () => {
setIsGalleryModalOpen(false);
setSelectedGalleryImages([]);
@@ -521,7 +518,7 @@ export const PromptBox3D = ({
}
};
const getCurrentSoraRemixAspectRatio = () : SoraImageRemixAspectRatio => {
const getCurrentSoraRemixAspectRatio = (): SoraImageRemixAspectRatio => {
switch (cameraAspectRatio.value) {
case CameraAspectRatio.HORIZONTAL_3_2:
return SoraImageRemixAspectRatio.Wide;
@@ -532,7 +529,7 @@ export const PromptBox3D = ({
default:
return SoraImageRemixAspectRatio.Square;
}
}
};
const handleSaveFrame = async () => {
if (!snapshotCurrentFrame) {
@@ -828,7 +825,7 @@ export const PromptBox3D = ({
/>
</div>
<GalleryModal
isOpen={isGalleryModalOpen}
isOpen={!!isGalleryModalOpen}
onClose={handleGalleryClose}
mode="select"
selectedItemIds={selectedGalleryImages}
@@ -73,7 +73,6 @@ export const PromptBoxVideo = ({
const [prompt, setPrompt] = useState("");
const [isEnqueueing, setIsEnqueueing] = useState(false);
const [useSystemPrompt, setUseSystemPrompt] = useState(true);
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
const [selectedGalleryImages, setSelectedGalleryImages] = useState<string[]>(
[]
);
@@ -132,10 +131,9 @@ export const PromptBoxVideo = ({
}
};
const handleGallerySelect = () => {
setIsGalleryModalOpen(true);
};
const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
const handleGallerySelect = () => setIsGalleryModalOpen(true);
const handleGalleryClose = () => {
setIsGalleryModalOpen(false);
setSelectedGalleryImages([]);
@@ -411,7 +409,7 @@ export const PromptBoxVideo = ({
</div>
</div>
<GalleryModal
isOpen={isGalleryModalOpen}
isOpen={!!isGalleryModalOpen}
onClose={handleGalleryClose}
mode="select"
selectedItemIds={selectedGalleryImages}