import chatStyles from "@/app/components/chat.module.scss"; import styles from "@/app/components/sd.module.scss"; import { IconButton } from "@/app/components/button"; import ReturnIcon from "@/app/icons/return.svg"; import Locale from "@/app/locales"; import { Path, StoreKey } from "@/app/constant"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { copyToClipboard, getMessageTextContent, useMobileScreen, } from "@/app/utils"; import { useNavigate } from "react-router-dom"; import { useAppConfig } from "@/app/store"; import MinIcon from "@/app/icons/min.svg"; import MaxIcon from "@/app/icons/max.svg"; import { getClientConfig } from "@/app/config/client"; import { ChatAction } from "@/app/components/chat"; import DeleteIcon from "@/app/icons/clear.svg"; import CopyIcon from "@/app/icons/copy.svg"; import PromptIcon from "@/app/icons/prompt.svg"; import ResetIcon from "@/app/icons/reload.svg"; import { useIndexedDB } from "react-indexed-db-hook"; import { useSdStore } from "@/app/store/sd"; import locales from "@/app/locales"; import LoadingIcon from "../icons/three-dots.svg"; import ErrorIcon from "../icons/delete.svg"; import { Property } from "csstype"; import { showConfirm, showImageModal, showModal, } from "@/app/components/ui-lib"; import { func } from "prop-types"; function getBase64ImgUrl(base64Data: string, contentType: string) { const byteCharacters = atob(base64Data); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); const blob = new Blob([byteArray], { type: contentType }); return URL.createObjectURL(blob); } function getSdTaskStatus(item: any) { let s: string; let color: Property.Color | undefined = undefined; switch (item.status) { case "success": s = Locale.Sd.Status.Success; color = "green"; break; case "error": s = Locale.Sd.Status.Error; color = "red"; break; case "wait": s = Locale.Sd.Status.Wait; color = "yellow"; break; case "running": s = Locale.Sd.Status.Running; color = "blue"; break; default: s = item.status.toUpperCase(); } return (

{locales.Sd.Status.Name}: {s} {item.status === "error" && ( { showModal({ title: locales.Sd.Detail, children: (

{item.error}
), }); }} > {" "} - {item.error} )}

); } function IndexDBImage({ img_data, title, isMobileScreen }) { const [src, setSrc] = useState(img_data); const sdListDb = useIndexedDB(StoreKey.SdList); const img_id = useMemo( () => img_data.replace("indexeddb://", "").split("@").pop(), [img_data], ); useEffect(() => { sdListDb .getByID(img_id) .then(({ data }) => { setSrc(data); }) .catch((e) => { setSrc(img_data); }); }, [img_data, img_id]); return ( {title} { showImageModal( getBase64ImgUrl(src, "image/png"), true, isMobileScreen ? { width: "100%", height: "fit-content" } : { maxWidth: "100%", maxHeight: "100%" }, isMobileScreen ? { width: "100%", height: "fit-content" } : { width: "100%", height: "100%" }, ); }} /> ); } export function Sd() { const isMobileScreen = useMobileScreen(); const navigate = useNavigate(); const clientConfig = useMemo(() => getClientConfig(), []); const showMaxIcon = !isMobileScreen && !clientConfig?.isApp; const config = useAppConfig(); const scrollRef = useRef(null); const sdListDb = useIndexedDB(StoreKey.SdList); const sdStore = useSdStore(); const [sdImages, setSdImages] = useState(sdStore.draw); useEffect(() => { setSdImages(sdStore.draw); }, [sdStore.currentId]); return (
{isMobileScreen && (
} bordered title={Locale.Chat.Actions.ChatList} onClick={() => navigate(Path.SdPanel)} />
)}
Stability AI
{Locale.Sd.SubTitle(sdImages.length || 0)}
{showMaxIcon && (
: } bordered onClick={() => { config.update( (config) => (config.tightBorder = !config.tightBorder), ); }} />
)}
{sdImages.length > 0 ? ( sdImages.map((item: any) => { return (
{item.status === "success" ? ( <> ) : item.status === "error" ? (
) : (
)}

{locales.SdPanel.Prompt}:{" "} { showModal({ title: locales.Sd.Detail, children: (

{item.params.prompt}
), }); }} > {item.params.prompt}

{locales.SdPanel.AIModel}: {item.model_name}

{getSdTaskStatus(item)}

{item.created_at}

} onClick={() => { showModal({ title: locales.Sd.GenerateParams, children: (
{Object.keys(item.params).map((key) => (
{key}: {item.params[key]}
))}
), }); }} /> } onClick={() => copyToClipboard( getMessageTextContent({ role: "user", content: item.params.prompt, }), ) } /> } onClick={() => { const reqData = { model: item.model, model_name: item.model_name, status: "wait", params: { ...item.params }, created_at: new Date().toLocaleString(), img_data: "", }; sdStore.sendTask(reqData, sdListDb); }} /> } onClick={async () => { if (await showConfirm(Locale.Sd.Danger.Delete)) { // remove img_data + remove item in list sdListDb.deleteRecord(item.id).then( () => { sdStore.draw = sdImages.filter( (i: any) => i.id !== item.id, ); sdStore.getNextId(); }, (error) => { console.error(error); }, ); } }} />
); }) ) : (
{locales.Sd.EmptyRecord}
)}
); }