- {(referenceImages.length > 0 || uploadingImages.length > 0) && (
-
- {referenceImages.map((image) => (
-
-

-
-
- ))}
- {uploadingImages.map(({ id, file }) => {
- const previewUrl = URL.createObjectURL(file);
- return (
+ <>
+
+ {(referenceImages.length > 0 || uploadingImages.length > 0) && (
+
+ {referenceImages.map((image) => (
-
-

+

+
+
+ ))}
+ {uploadingImages.map(({ id, file }) => {
+ const previewUrl = URL.createObjectURL(file);
+ return (
+
+
+

+
+
+
+
-
+ );
+ })}
+
+ )}
+
+
+
+
,
+ action: "upload",
+ },
+ {
+ label: "Choose from library",
+ selected: false,
+ icon:
,
+ action: "gallery",
+ },
+ ]}
+ onPanelAction={handleAction}
+ showIconsInList
+ buttonClassName="backdrop-blur-none bg-transparent hover:bg-transparent py-1.5 px-0 pr-1 m-0 hover:opacity-50 transition-opacity duration-100 ring-0 border-none focus:ring-0 outline-none"
+ triggerIcon={
+
+ }
+ />
+
+
+
+
+
+
+ }
+ />
+
+
+ setUseSystemPrompt(!useSystemPrompt)}
+ />
+
+
+
+
-
- );
- })}
-
- )}
-
-
-
-
,
- action: "upload",
- },
- {
- label: "Choose from library",
- selected: false,
- icon:
,
- action: "library",
- disabled: true,
- },
- ]}
- onPanelAction={handleAction}
- showIconsInList
- buttonClassName="backdrop-blur-none bg-transparent hover:bg-transparent py-1.5 px-0 pr-1 m-0 hover:opacity-50 transition-opacity duration-100 ring-0 border-none focus:ring-0 outline-none"
- triggerIcon={
}
- />
-
-
-
-
-
-
- }
- />
-
-
- setUseSystemPrompt(!useSystemPrompt)}
- />
-
-
-
-
+ ) : (
+ "Generate"
+ )}
+
+
-
+
+ >
);
};
diff --git a/frontend/apps/editor2d/src/components/features/ToolbarTopRight/index.tsx b/frontend/apps/editor2d/src/components/features/ToolbarTopRight/index.tsx
index 61d810dacc..2834fe4cde 100644
--- a/frontend/apps/editor2d/src/components/features/ToolbarTopRight/index.tsx
+++ b/frontend/apps/editor2d/src/components/features/ToolbarTopRight/index.tsx
@@ -1,15 +1,16 @@
-import { faGear } from "@fortawesome/pro-solid-svg-icons";
+import { faGear, faImages } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
import { Activity } from "~/components/ui/Activity/Activity";
-// import { paperWrapperStyles } from "~/components/styles";
-// import { faPlus, faQuestion } from "@fortawesome/pro-solid-svg-icons";
-// import { ToolbarButton } from "~/components/features/ToolbarButton";
+import { GalleryModal } from "@storyteller/ui-gallery-modal";
import { Button } from "@storyteller/ui-button";
import { SettingsModal } from "@storyteller/ui-settings-modal";
import { useState } from "react";
export const ToolbarTopRight = () => {
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
+ const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
+ const [activeGalleryTab, setActiveGalleryTab] = useState("my-media");
+
return (
<>
{
className="h-[42px] w-[42px] bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90"
onClick={() => setIsSettingsModalOpen(true)}
/>
+
@@ -27,6 +35,18 @@ export const ToolbarTopRight = () => {
isOpen={isSettingsModalOpen}
onClose={() => setIsSettingsModalOpen(false)}
/>
+
+
setIsGalleryModalOpen(false)}
+ mode="view"
+ tabs={[
+ { id: "my-media", label: "My generations" },
+ { id: "uploads", label: "My uploads" },
+ ]}
+ activeTab={activeGalleryTab}
+ onTabChange={setActiveGalleryTab}
+ />
>
);
};
diff --git a/frontend/apps/editor2d/tsconfig.app.json b/frontend/apps/editor2d/tsconfig.app.json
index 0ad9c59eb7..ac57c88567 100644
--- a/frontend/apps/editor2d/tsconfig.app.json
+++ b/frontend/apps/editor2d/tsconfig.app.json
@@ -34,6 +34,9 @@
{
"path": "../../libs/components/button/tsconfig.lib.json"
},
+ {
+ "path": "../../libs/components/gallery-modal/tsconfig.lib.json"
+ },
{
"path": "../../libs/tauri-utils/tsconfig.lib.json"
},
diff --git a/frontend/apps/editor2d/tsconfig.json b/frontend/apps/editor2d/tsconfig.json
index 04e53ea42f..45a4ae0f21 100644
--- a/frontend/apps/editor2d/tsconfig.json
+++ b/frontend/apps/editor2d/tsconfig.json
@@ -7,6 +7,9 @@
{
"path": "../../libs/components/button"
},
+ {
+ "path": "../../libs/components/gallery-modal"
+ },
{
"path": "../../libs/tauri-utils"
},
diff --git a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx
index d691fa02ef..c43ca384f4 100644
--- a/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx
+++ b/frontend/apps/editor3d/app/src/components/signaled/TopBar/TopBar.tsx
@@ -11,7 +11,7 @@ import { AuthButtons } from "./AuthButtons";
import { SceneTitleInput } from "./SceneTitleInput";
import { getCurrentLocationWithoutParams } from "~/utilities";
import { Activity } from "~/pages/PageEnigma/comps/GenerateModals/Activity";
-import { LibraryModal } from "~/pages/PageEnigma/comps/LibraryModal/LibraryModal";
+import { GalleryModal } from "@storyteller/ui-gallery-modal";
import { SettingsModal } from "@storyteller/ui-settings-modal";
import { Tooltip } from "@storyteller/ui-tooltip";
@@ -106,13 +106,13 @@ export const TopBar = ({ pageName }: Props) => {
onClose={() => setIsSettingsModalOpen(false)}
/>
- setIsLibraryModalOpen(false)}
mode="view"
tabs={[
- { id: "my-media", label: "My media" },
- { id: "uploads", label: "Uploads" },
+ { id: "my-media", label: "My generations" },
+ { id: "uploads", label: "My uploads" },
]}
activeTab={activeLibraryTab}
onTabChange={setActiveLibraryTab}
diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx
index 4378467f3b..f20f3668ec 100644
--- a/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx
+++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/comps/PromptBox/PromptBox.tsx
@@ -38,8 +38,6 @@ import {
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { toEngineActions } from "~/pages/PageEnigma/Queue/toEngineActions";
-import { LibraryModal } from "../LibraryModal/LibraryModal";
-import type { LibraryItem } from "../LibraryModal/LibraryModal";
import {
cameras,
selectedCameraId,
@@ -61,6 +59,7 @@ import { EngineApi } from "~/Classes/ApiManager/EngineApi";
import { UploaderStates } from "~/enums/UploaderStates";
import { CameraSettingsModal } from "../CameraSettingsModal";
import { twMerge } from "tailwind-merge";
+import { GalleryModal, GalleryItem } from "@storyteller/ui-gallery-modal";
export const PromptBox = () => {
useSignals();
@@ -92,11 +91,11 @@ export const PromptBox = () => {
},
]);
const [isCameraSettingsOpen, setIsCameraSettingsOpen] = useState(false);
- const [isLibraryModalOpen, setIsLibraryModalOpen] = useState(false);
- const [selectedLibraryImages, setSelectedLibraryImages] = useState(
+ const [isGalleryModalOpen, setIsGalleryModalOpen] = useState(false);
+ const [selectedGalleryImages, setSelectedGalleryImages] = useState(
[],
);
- const [activeLibraryTab, setActiveLibraryTab] = useState("my-media");
+ const [activeGalleryTab, setActiveGalleryTab] = useState("my-media");
useEffect(() => {
if (textareaRef.current) {
@@ -359,17 +358,17 @@ export const PromptBox = () => {
fileInputRef.current?.click();
};
- const handleLibrarySelect = () => {
- setIsLibraryModalOpen(true);
+ const handleGallerySelect = () => {
+ setIsGalleryModalOpen(true);
};
- const handleLibraryClose = () => {
- setIsLibraryModalOpen(false);
- setSelectedLibraryImages([]);
+ const handleGalleryClose = () => {
+ setIsGalleryModalOpen(false);
+ setSelectedGalleryImages([]);
};
const handleImageSelect = (id: string) => {
- setSelectedLibraryImages((prev) => {
+ setSelectedGalleryImages((prev) => {
if (prev.includes(id)) {
return prev.filter((imageId) => imageId !== id);
}
@@ -380,19 +379,19 @@ export const PromptBox = () => {
});
};
- const handleLibraryImages = (selectedItems: LibraryItem[]) => {
+ const handleGalleryImages = (selectedItems: GalleryItem[]) => {
selectedItems.forEach((item) => {
if (!item.fullImage) return;
const referenceImage: ReferenceImage = {
id: Math.random().toString(36).substring(7),
url: item.fullImage,
- file: new File([], "library-image"),
+ file: new File([], "gallery-image"),
mediaToken: item.id,
};
setReferenceImages((prev) => [...prev, referenceImage]);
});
- setIsLibraryModalOpen(false);
- setSelectedLibraryImages([]);
+ setIsGalleryModalOpen(false);
+ setSelectedGalleryImages([]);
};
const handleAction = (action: string) => {
@@ -400,8 +399,8 @@ export const PromptBox = () => {
case "upload":
handleUploadClick();
break;
- case "library":
- handleLibrarySelect();
+ case "gallery":
+ handleGallerySelect();
break;
default:
console.log("Unknown action:", action);
@@ -581,7 +580,7 @@ export const PromptBox = () => {
label: "Choose from library",
selected: false,
icon: ,
- action: "library",
+ action: "gallery",
},
]}
onPanelAction={handleAction}
@@ -762,20 +761,20 @@ export const PromptBox = () => {
onDeleteCamera={deleteCamera}
/>
-
>
);
diff --git a/frontend/apps/editor3d/tsconfig.json b/frontend/apps/editor3d/tsconfig.json
index a416cd3626..2c77f20f8c 100644
--- a/frontend/apps/editor3d/tsconfig.json
+++ b/frontend/apps/editor3d/tsconfig.json
@@ -44,6 +44,9 @@
},
{
"path": "../../libs/components/settings-modal"
+ },
+ {
+ "path": "../../libs/components/gallery-modal"
}
]
}
diff --git a/frontend/libs/api/src/lib/LibraryModalApi.ts b/frontend/libs/api/src/lib/LibraryModalApi.ts
index b23f1f208f..317df7b0b0 100644
--- a/frontend/libs/api/src/lib/LibraryModalApi.ts
+++ b/frontend/libs/api/src/lib/LibraryModalApi.ts
@@ -6,12 +6,12 @@ import {
} from "lib/enums/QueryFilters.js";
interface ListUserMediaQuery {
- include_user_uploads?: boolean;
+ username: string;
filter_media_classes?: FilterMediaClasses[];
filter_media_type?: FilterMediaType[];
filter_engine_categories?: FilterEngineCategories[];
- page?: number;
- limit?: number;
+ user_uploads_only?: boolean;
+ include_user_uploads?: boolean;
}
export class LibraryModalApi extends MediaFilesApi {
@@ -25,6 +25,9 @@ export class LibraryModalApi extends MediaFilesApi {
filter_media_classes: query.filter_media_classes,
filter_media_type: query.filter_media_type,
filter_engine_categories: query.filter_engine_categories,
+ username: query.username,
+ user_uploads_only: query.user_uploads_only,
+ include_user_uploads: query.include_user_uploads,
});
}
}
diff --git a/frontend/libs/api/src/lib/MediaFilesApi.ts b/frontend/libs/api/src/lib/MediaFilesApi.ts
index 8a9977123e..716b4260fe 100644
--- a/frontend/libs/api/src/lib/MediaFilesApi.ts
+++ b/frontend/libs/api/src/lib/MediaFilesApi.ts
@@ -27,6 +27,8 @@ interface ListUserMediaQuery {
filter_media_classes?: FilterMediaClasses[];
filter_media_type?: FilterMediaType[];
filter_engine_categories?: FilterEngineCategories[];
+ user_uploads_only?: boolean;
+ include_user_uploads?: boolean;
}
interface SearchFeaturedMediaQuery {
@@ -115,7 +117,7 @@ export class MediaFilesApi extends ApiManager {
}
public async ListMediaFiles(
- query: ListMediaQuery,
+ query: ListMediaQuery
): Promise
> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list`;
const queryWithStrings = {
@@ -149,7 +151,7 @@ export class MediaFilesApi extends ApiManager {
}
public async ListFeaturedMediaFiles(
- query: ListMediaQuery,
+ query: ListMediaQuery
): Promise> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list_featured`;
const queryWithStrings = {
@@ -183,13 +185,13 @@ export class MediaFilesApi extends ApiManager {
}
public async ListUserMediaFiles(
- query: ListUserMediaQuery,
+ query: ListUserMediaQuery
): Promise> {
const userName = query.username;
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/list/user/${userName}`;
const queryWithStrings = {
...query,
- include_user_uploads: true,
+ include_user_uploads: query.include_user_uploads,
filter_media_classes: query.filter_media_classes
? query.filter_media_classes.join(",")
: undefined,
@@ -205,11 +207,17 @@ export class MediaFilesApi extends ApiManager {
results: MediaFile[];
pagination?: Pagination;
}>({ endpoint, query: queryWithStrings })
- .then((response) => ({
- success: response.success,
- data: response.results ?? [],
- pagination: response.pagination,
- }))
+ .then((response) => {
+ let results = response.results ?? [];
+ if (query.user_uploads_only) {
+ results = results.filter((file) => file.origin_category === "upload");
+ }
+ return {
+ success: response.success,
+ data: results,
+ pagination: response.pagination,
+ };
+ })
.catch((err) => {
return {
success: false,
@@ -219,7 +227,7 @@ export class MediaFilesApi extends ApiManager {
}
public async SearchFeaturedMediaFiles(
- query: SearchFeaturedMediaQuery,
+ query: SearchFeaturedMediaQuery
): Promise> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/search_featured`;
const queryWithStrings = {
@@ -251,7 +259,7 @@ export class MediaFilesApi extends ApiManager {
}
public async SearchUserMediaFiles(
- query: SearchFeaturedMediaQuery,
+ query: SearchFeaturedMediaQuery
): Promise> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/search_session`;
const queryWithStrings = {
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 08b7125e24..cb4eab1e9f 100644
--- a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx
+++ b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx
@@ -11,10 +11,11 @@ import {
GetCdnOrigin,
FilterMediaClasses,
LibraryModalApi,
+ UsersApi,
} from "@storyteller/api";
import { twMerge } from "tailwind-merge";
-export interface LibraryItem {
+export interface GalleryItem {
id: string;
label: string;
thumbnail: string | null;
@@ -23,7 +24,7 @@ export interface LibraryItem {
}
interface GroupedItems {
- [date: string]: LibraryItem[];
+ [date: string]: GalleryItem[];
}
type ModalMode = "select" | "view";
@@ -35,7 +36,7 @@ interface LibraryModalProps {
selectedItemIds?: string[];
onSelectItem?: (id: string) => void;
maxSelections?: number;
- onUseSelected?: (selectedItems: LibraryItem[]) => void;
+ onUseSelected?: (selectedItems: GalleryItem[]) => void;
tabs: { id: string; label: string }[];
activeTab: string;
onTabChange: (tabId: string) => void;
@@ -56,14 +57,16 @@ export const GalleryModal = React.memo(
}: LibraryModalProps) => {
const [groupedItems, setGroupedItems] = useState({});
const [loading, setLoading] = useState(false);
- const [lightboxImage, setLightboxImage] = useState(
+ const [lightboxImage, setLightboxImage] = useState(
null
);
const [isLightboxVisible, setIsLightboxVisible] = useState(false);
const [failedImageUrls] = useState>(new Set());
+ const [username, setUsername] = useState("");
const cdnOrigin = GetCdnOrigin();
const imageUrl = lightboxImage?.fullImage || "";
const api = useMemo(() => new LibraryModalApi(), []);
+ const usersApi = useMemo(() => new UsersApi(), []);
const formatDate = useCallback((date: string) => {
const d = new Date(date);
@@ -75,7 +78,7 @@ export const GalleryModal = React.memo(
}, []);
const groupItemsByDate = useCallback(
- (items: LibraryItem[]) => {
+ (items: GalleryItem[]) => {
const grouped = items.reduce((acc: GroupedItems, item) => {
const dateKey = formatDate(item.createdAt);
if (!acc[dateKey]) {
@@ -114,15 +117,29 @@ export const GalleryModal = React.memo(
[cdnOrigin, failedImageUrls]
);
+ useEffect(() => {
+ const getUsername = async () => {
+ const session = await usersApi.GetSession();
+ if (session.success && session.data?.user) {
+ setUsername(session.data.user.username);
+ }
+ };
+ getUsername();
+ }, [usersApi]);
+
useEffect(() => {
const loadItems = async () => {
+ if (!username) return;
setLoading(true);
try {
const response = await api.listUserMediaFiles({
filter_media_classes: [FilterMediaClasses.IMAGE],
+ username: username,
+ include_user_uploads: activeTab === "uploads",
+ user_uploads_only: activeTab === "uploads",
});
if (response.success && response.data) {
- const newItems = response.data.map((item) => ({
+ const newItems = response.data.map((item: any) => ({
id: item.token,
label: item.maybe_title || "Image Generation",
thumbnail: getImageUrl(item.public_bucket_path),
@@ -141,7 +158,7 @@ export const GalleryModal = React.memo(
loadItems();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [activeTab, isOpen]);
+ }, [activeTab, isOpen, username]);
// useEffect(() => {
// console.log("LibraryModal render caused by:", {
@@ -152,7 +169,7 @@ export const GalleryModal = React.memo(
// }, [isOpen, activeTab, selectedItemIds]);
const handleItemClick = useCallback(
- (item: LibraryItem) => {
+ (item: GalleryItem) => {
if (mode === "select" && onSelectItem) {
onSelectItem(item.id);
} else {
@@ -190,12 +207,13 @@ export const GalleryModal = React.memo(
mode === "view" && "h-[80vh] max-h-[870px] max-w-7xl"
)}
childPadding={false}
+ showClose={false}
>
- {mode === "select" ? "Select Images" : "My Gallery"}
+ {mode === "select" ? "Select Images" : "Gallery"}
-
+
{loading ? (
@@ -219,7 +237,7 @@ export const GalleryModal = React.memo(
) : (
-
+
{Object.entries(groupedItems).map(([date, dateItems]) => (
@@ -238,7 +256,7 @@ export const GalleryModal = React.memo(
"group relative aspect-square overflow-hidden rounded-md border-[3px] transition-all",
mode === "select" &&
selectedItemIds.includes(item.id)
- ? "border-brand-primary"
+ ? "border-primary"
: "border-transparent hover:border-white"
)}
onClick={() => handleItemClick(item)}
@@ -269,7 +287,7 @@ export const GalleryModal = React.memo(
{mode === "select" &&
selectedItemIds.includes(item.id) && (
-
+