diff --git a/.gitignore b/.gitignore index a24c6e047..2ff556f64 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,5 @@ dev *.key *.key.pub + +masks.json diff --git a/app/components/home.tsx b/app/components/home.tsx index aacd23264..576112715 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -59,7 +59,7 @@ const Sd = dynamic(async () => (await import("./sd")).Sd, { loading: () => , }); -const SdPanel = dynamic(async () => (await import("./sd-panel")).SdPanel, { +const SdPanel = dynamic(async () => (await import("./sd")).SdPanel, { loading: () => , }); @@ -130,12 +130,22 @@ const loadAsyncGoogleFont = () => { document.head.appendChild(linkEl); }; +export function WindowContent(props: { children: React.ReactNode }) { + return ( +
+ {props?.children} +
+ ); +} + function Screen() { const config = useAppConfig(); const location = useLocation(); - const isHome = - location.pathname === Path.Home || location.pathname === Path.SdPanel; + const isHome = location.pathname === Path.Home; const isAuth = location.pathname === Path.Auth; + const isSd = location.pathname === Path.Sd; + const isSdPanel = location.pathname === Path.SdPanel; + const isMobileScreen = useMobileScreen(); const shouldTightBorder = getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen); @@ -143,35 +153,36 @@ function Screen() { useEffect(() => { loadAsyncGoogleFont(); }, []); + + const renderContent = () => { + if (isAuth) return ; + if (isSd) return ; + if (isSdPanel) return ; + return ( + <> + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); + }; + return (
- {isAuth ? ( - <> - - - ) : ( - <> - -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
- - )} + {renderContent()}
); } diff --git a/app/components/sd.tsx b/app/components/sd.tsx deleted file mode 100644 index 19684c240..000000000 --- a/app/components/sd.tsx +++ /dev/null @@ -1,278 +0,0 @@ -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 } 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 { 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 { removeImage } from "@/app/utils/chat"; - -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} - - )} -

- ); -} - -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 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.id} - showImageModal( - item.img_data, - true, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { maxWidth: "100%", maxHeight: "100%" }, - isMobileScreen - ? { width: "100%", height: "fit-content" } - : { width: "100%", height: "100%" }, - ) - } - /> - ) : 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); - }} - /> - } - onClick={async () => { - if (await showConfirm(Locale.Sd.Danger.Delete)) { - // remove img_data + remove item in list - removeImage(item.img_data).finally(() => { - sdStore.draw = sdImages.filter( - (i: any) => i.id !== item.id, - ); - sdStore.getNextId(); - }); - } - }} - /> -
-
-
-
- ); - }) - ) : ( -
{locales.Sd.EmptyRecord}
- )} -
-
-
- ); -} diff --git a/app/components/sd/index.tsx b/app/components/sd/index.tsx new file mode 100644 index 000000000..d442c22bc --- /dev/null +++ b/app/components/sd/index.tsx @@ -0,0 +1,2 @@ +export * from "./sd"; +export * from "./sd-panel"; diff --git a/app/components/sd-panel.module.scss b/app/components/sd/sd-panel.module.scss similarity index 58% rename from app/components/sd-panel.module.scss rename to app/components/sd/sd-panel.module.scss index 1b31faeac..c71ba557a 100644 --- a/app/components/sd-panel.module.scss +++ b/app/components/sd/sd-panel.module.scss @@ -10,7 +10,7 @@ display: flex; align-items: center; - .ctrl-param-item-title{ + .ctrl-param-item-title { font-size: 14px; font-weight: bolder; margin-bottom: 5px; @@ -22,12 +22,24 @@ font-weight: normal; margin-top: 3px; } + textarea { + appearance: none; + border-radius: 10px; + border: var(--border-in-light); + min-height: 36px; + box-sizing: border-box; + background: var(--white); + color: var(--black); + padding: 0 10px; + max-width: 50%; + font-family: inherit; + } } -.ai-models{ - button{ +.ai-models { + button { margin-bottom: 10px; - padding:10px; + padding: 10px; width: 100%; } -} \ No newline at end of file +} diff --git a/app/components/sd-panel.tsx b/app/components/sd/sd-panel.tsx similarity index 82% rename from app/components/sd-panel.tsx rename to app/components/sd/sd-panel.tsx index c6b28f221..592aa0dad 100644 --- a/app/components/sd-panel.tsx +++ b/app/components/sd/sd-panel.tsx @@ -2,7 +2,7 @@ import styles from "./sd-panel.module.scss"; import React, { useState } from "react"; import { Select, showToast } from "@/app/components/ui-lib"; import { IconButton } from "@/app/components/button"; -import locales from "@/app/locales"; +import Locale from "@/app/locales"; import { nanoid } from "nanoid"; import { StoreKey } from "@/app/constant"; import { useSdStore } from "@/app/store/sd"; @@ -10,14 +10,14 @@ import { useSdStore } from "@/app/store/sd"; const sdCommonParams = (model: string, data: any) => { return [ { - name: locales.SdPanel.Prompt, + name: Locale.SdPanel.Prompt, value: "prompt", type: "textarea", - placeholder: locales.SdPanel.PleaseInput(locales.SdPanel.Prompt), + placeholder: Locale.SdPanel.PleaseInput(Locale.SdPanel.Prompt), required: true, }, { - name: locales.SdPanel.ModelVersion, + name: Locale.SdPanel.ModelVersion, value: "model", type: "select", default: "sd3-medium", @@ -29,13 +29,13 @@ const sdCommonParams = (model: string, data: any) => { ], }, { - name: locales.SdPanel.NegativePrompt, + name: Locale.SdPanel.NegativePrompt, value: "negative_prompt", type: "textarea", - placeholder: locales.SdPanel.PleaseInput(locales.SdPanel.NegativePrompt), + placeholder: Locale.SdPanel.PleaseInput(Locale.SdPanel.NegativePrompt), }, { - name: locales.SdPanel.AspectRatio, + name: Locale.SdPanel.AspectRatio, value: "aspect_ratio", type: "select", default: "1:1", @@ -52,32 +52,32 @@ const sdCommonParams = (model: string, data: any) => { ], }, { - name: locales.SdPanel.ImageStyle, + name: Locale.SdPanel.ImageStyle, value: "style", type: "select", default: "3d", support: ["core"], options: [ - { name: locales.SdPanel.Styles.D3Model, value: "3d-model" }, - { name: locales.SdPanel.Styles.AnalogFilm, value: "analog-film" }, - { name: locales.SdPanel.Styles.Anime, value: "anime" }, - { name: locales.SdPanel.Styles.Cinematic, value: "cinematic" }, - { name: locales.SdPanel.Styles.ComicBook, value: "comic-book" }, - { name: locales.SdPanel.Styles.DigitalArt, value: "digital-art" }, - { name: locales.SdPanel.Styles.Enhance, value: "enhance" }, - { name: locales.SdPanel.Styles.FantasyArt, value: "fantasy-art" }, - { name: locales.SdPanel.Styles.Isometric, value: "isometric" }, - { name: locales.SdPanel.Styles.LineArt, value: "line-art" }, - { name: locales.SdPanel.Styles.LowPoly, value: "low-poly" }, + { name: Locale.SdPanel.Styles.D3Model, value: "3d-model" }, + { name: Locale.SdPanel.Styles.AnalogFilm, value: "analog-film" }, + { name: Locale.SdPanel.Styles.Anime, value: "anime" }, + { name: Locale.SdPanel.Styles.Cinematic, value: "cinematic" }, + { name: Locale.SdPanel.Styles.ComicBook, value: "comic-book" }, + { name: Locale.SdPanel.Styles.DigitalArt, value: "digital-art" }, + { name: Locale.SdPanel.Styles.Enhance, value: "enhance" }, + { name: Locale.SdPanel.Styles.FantasyArt, value: "fantasy-art" }, + { name: Locale.SdPanel.Styles.Isometric, value: "isometric" }, + { name: Locale.SdPanel.Styles.LineArt, value: "line-art" }, + { name: Locale.SdPanel.Styles.LowPoly, value: "low-poly" }, { - name: locales.SdPanel.Styles.ModelingCompound, + name: Locale.SdPanel.Styles.ModelingCompound, value: "modeling-compound", }, - { name: locales.SdPanel.Styles.NeonPunk, value: "neon-punk" }, - { name: locales.SdPanel.Styles.Origami, value: "origami" }, - { name: locales.SdPanel.Styles.Photographic, value: "photographic" }, - { name: locales.SdPanel.Styles.PixelArt, value: "pixel-art" }, - { name: locales.SdPanel.Styles.TileTexture, value: "tile-texture" }, + { name: Locale.SdPanel.Styles.NeonPunk, value: "neon-punk" }, + { name: Locale.SdPanel.Styles.Origami, value: "origami" }, + { name: Locale.SdPanel.Styles.Photographic, value: "photographic" }, + { name: Locale.SdPanel.Styles.PixelArt, value: "pixel-art" }, + { name: Locale.SdPanel.Styles.TileTexture, value: "tile-texture" }, ], }, { @@ -89,7 +89,7 @@ const sdCommonParams = (model: string, data: any) => { max: 4294967294, }, { - name: locales.SdPanel.OutFormat, + name: Locale.SdPanel.OutFormat, value: "output_format", type: "select", default: "png", @@ -292,7 +292,7 @@ export function SdPanel() { reqParams[item.value] = params[item.value] ?? null; if (item.required) { if (!reqParams[item.value]) { - showToast(locales.SdPanel.ParamIsRequired(item.name)); + showToast(Locale.SdPanel.ParamIsRequired(item.name)); return; } } @@ -311,7 +311,7 @@ export function SdPanel() { }; return ( <> - +
{models.map((item) => { return ( @@ -332,7 +332,7 @@ export function SdPanel() { onChange={handleValueChange} > (await import("@/app/components/sd/sd-panel")).SdPanel, + { + loading: () => null, + }, +); + +export function SideBar(props: { className?: string }) { + useHotKey(); + const { onDragStart, shouldNarrow } = useDragSideBar(); + const navigate = useNavigate(); + + return ( + +
+
+ } + bordered + title={Locale.Chat.Actions.ChatList} + onClick={() => navigate(Path.Chat)} + /> +
+
+ +
+
+ + + +
+ +
+
+ ); +} diff --git a/app/components/sd.module.scss b/app/components/sd/sd.module.scss similarity index 100% rename from app/components/sd.module.scss rename to app/components/sd/sd.module.scss diff --git a/app/components/sd/sd.tsx b/app/components/sd/sd.tsx new file mode 100644 index 000000000..547a877ee --- /dev/null +++ b/app/components/sd/sd.tsx @@ -0,0 +1,292 @@ +import chatStyles from "@/app/components/chat.module.scss"; +import styles from "@/app/components/sd/sd.module.scss"; +import { IconButton } from "@/app/components/button"; +import ReturnIcon from "@/app/icons/return.svg"; +import Locale from "@/app/locales"; +import { Path } 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 { useSdStore } from "@/app/store/sd"; +import locales from "@/app/locales"; +import LoadingIcon from "@/app/icons/three-dots.svg"; +import ErrorIcon from "@/app/icons/delete.svg"; +import { Property } from "csstype"; +import { + showConfirm, + showImageModal, + showModal, +} from "@/app/components/ui-lib"; +import { removeImage } from "@/app/utils/chat"; +import { SideBar } from "./sd-sidebar"; +import { WindowContent } from "@/app/components/home"; + +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} + + )} +

+ ); +} + +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 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.id} + showImageModal( + item.img_data, + true, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { maxWidth: "100%", maxHeight: "100%" }, + isMobileScreen + ? { width: "100%", height: "fit-content" } + : { width: "100%", height: "100%" }, + ) + } + /> + ) : 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); + }} + /> + } + onClick={async () => { + if ( + await showConfirm(Locale.Sd.Danger.Delete) + ) { + // remove img_data + remove item in list + removeImage(item.img_data).finally(() => { + sdStore.draw = sdImages.filter( + (i: any) => i.id !== item.id, + ); + sdStore.getNextId(); + }); + } + }} + /> +
+
+
+
+ ); + }) + ) : ( +
{locales.Sd.EmptyRecord}
+ )} +
+
+
+
+ + ); +} diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index dbe2f2d1a..79af7e121 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useMemo, useState } from "react"; +import { useEffect, useRef, useMemo, useState } from "react"; import styles from "./home.module.scss"; @@ -15,7 +15,7 @@ import DragIcon from "../icons/drag.svg"; import Locale from "../locales"; -import { ModelType, useAppConfig, useChatStore } from "../store"; +import { useAppConfig, useChatStore } from "../store"; import { DEFAULT_SIDEBAR_WIDTH, @@ -27,20 +27,16 @@ import { REPO_URL, } from "../constant"; -import { Link, useLocation, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { isIOS, useMobileScreen } from "../utils"; import dynamic from "next/dynamic"; -import { Selector, showConfirm, showToast } from "./ui-lib"; +import { showConfirm, Selector } from "./ui-lib"; const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => null, }); -const SdPanel = dynamic(async () => (await import("./sd-panel")).SdPanel, { - loading: () => null, -}); - -function useHotKey() { +export function useHotKey() { const chatStore = useChatStore(); useEffect(() => { @@ -59,7 +55,7 @@ function useHotKey() { }); } -function useDragSideBar() { +export function useDragSideBar() { const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); const config = useAppConfig(); @@ -132,39 +128,21 @@ function useDragSideBar() { shouldNarrow, }; } - -export function SideBar(props: { className?: string }) { - const chatStore = useChatStore(); - - // drag side bar - const { onDragStart, shouldNarrow } = useDragSideBar(); - const navigate = useNavigate(); - const config = useAppConfig(); +export function SideBarContainer(props: { + children: React.ReactNode; + onDragStart: (e: MouseEvent) => void; + shouldNarrow: boolean; + className?: string; +}) { const isMobileScreen = useMobileScreen(); const isIOSMobile = useMemo( () => isIOS() && isMobileScreen, [isMobileScreen], ); - const [showPluginSelector, setShowPluginSelector] = useState(false); - const location = useLocation(); - - useHotKey(); - - let bodyComponent: React.JSX.Element; - let isChat: boolean = false; - switch (location.pathname) { - case Path.Sd: - case Path.SdPanel: - bodyComponent = ; - break; - default: - isChat = true; - bodyComponent = ; - } - // @ts-ignore + const { children, className, onDragStart, shouldNarrow } = props; return (
+ {children} +
onDragStart(e as any)} + > + +
+
+ ); +} + +export function SideBarHeader(props: { shouldNarrow: boolean }) { + const navigate = useNavigate(); + const config = useAppConfig(); + const { shouldNarrow } = props; + const [showPluginSelector, setShowPluginSelector] = useState(false); + + return ( + <>
NextChat @@ -206,68 +203,6 @@ export function SideBar(props: { className?: string }) { shadow />
- -
{ - if (isChat && e.target === e.currentTarget) { - navigate(Path.Home); - } - }} - > - {bodyComponent} -
- -
-
- {isChat && ( -
- } - onClick={async () => { - if (await showConfirm(Locale.Home.DeleteChat)) { - chatStore.deleteSession(chatStore.currentSessionIndex); - } - }} - /> -
- )} -
- - } shadow /> - -
- -
- {isChat && ( -
- } - text={shouldNarrow ? undefined : Locale.Home.NewChat} - onClick={() => { - if (config.dontShowMaskSplashScreen) { - chatStore.newSession(); - navigate(Path.Chat); - } else { - navigate(Path.NewChat); - } - }} - shadow - /> -
- )} -
- -
onDragStart(e as any)} - > - -
{showPluginSelector && ( )} + + ); +} + +export function SideBarBody(props: { + children: React.ReactNode; + onClick?: (e: React.MouseEvent) => void; +}) { + const { onClick, children } = props; + return ( +
+ {children}
); } + +export function SideBarTail(props: { shouldNarrow: boolean }) { + const { shouldNarrow } = props; + const chatStore = useChatStore(); + const navigate = useNavigate(); + const config = useAppConfig(); + return ( +
+
+
+ } + onClick={async () => { + if (await showConfirm(Locale.Home.DeleteChat)) { + chatStore.deleteSession(chatStore.currentSessionIndex); + } + }} + /> +
+
+ + } shadow /> + +
+ +
+
+ } + text={shouldNarrow ? undefined : Locale.Home.NewChat} + onClick={() => { + if (config.dontShowMaskSplashScreen) { + chatStore.newSession(); + navigate(Path.Chat); + } else { + navigate(Path.NewChat); + } + }} + shadow + /> +
+
+ ); +} + +export function SideBar(props: { className?: string }) { + useHotKey(); + const { onDragStart, shouldNarrow } = useDragSideBar(); + const navigate = useNavigate(); + + return ( + + + { + if (e.target === e.currentTarget) { + navigate(Path.Home); + } + }} + > + + + + + ); +} diff --git a/app/icons/sd.svg b/app/icons/sd.svg new file mode 100644 index 000000000..1c5e73021 --- /dev/null +++ b/app/icons/sd.svg @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/styles/globals.scss b/app/styles/globals.scss index f612e462e..3c59f2d44 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -226,8 +226,7 @@ input[type="range"]::-ms-thumb:hover { input[type="number"], input[type="text"], -input[type="password"], -textarea { +input[type="password"] { appearance: none; border-radius: 10px; border: var(--border-in-light);