Dogtiti преди 1 година
родител
ревизия
6b98b14179
променени са 10 файла, в които са добавени 237 реда и са изтрити 132 реда
  1. 3 5
      app/components/home.tsx
  2. 1 0
      app/components/sd/index.tsx
  3. 85 0
      app/components/sd/sd-new.tsx
  4. 17 21
      app/components/sd/sd-sidebar.tsx
  5. 1 1
      app/components/sd/sd.tsx
  6. 115 104
      app/components/sidebar.tsx
  7. 1 1
      app/constant.ts
  8. 10 0
      app/icons/history.svg
  9. 2 0
      app/locales/cn.ts
  10. 2 0
      app/locales/en.ts

+ 3 - 5
app/components/home.tsx

@@ -59,7 +59,7 @@ const Sd = dynamic(async () => (await import("./sd")).Sd, {
   loading: () => <Loading noLogo />,
 });
 
-const SdPanel = dynamic(async () => (await import("./sd")).SdPanel, {
+const SdNew = dynamic(async () => (await import("./sd")).SdNew, {
   loading: () => <Loading noLogo />,
 });
 
@@ -144,7 +144,7 @@ function Screen() {
   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 isSdNew = location.pathname === Path.SdNew;
 
   const isMobileScreen = useMobileScreen();
   const shouldTightBorder =
@@ -157,7 +157,7 @@ function Screen() {
   const renderContent = () => {
     if (isAuth) return <AuthPage />;
     if (isSd) return <Sd />;
-    if (isSdPanel) return <SdPanel />;
+    if (isSdNew) return <SdNew />;
     return (
       <>
         <SideBar className={isHome ? styles["sidebar-show"] : ""} />
@@ -167,8 +167,6 @@ function Screen() {
             <Route path={Path.NewChat} element={<NewChat />} />
             <Route path={Path.Masks} element={<MaskPage />} />
             <Route path={Path.Chat} element={<Chat />} />
-            <Route path={Path.Sd} element={<Sd />} />
-            <Route path={Path.SdPanel} element={<Sd />} />
             <Route path={Path.Settings} element={<Settings />} />
           </Routes>
         </WindowContent>

+ 1 - 0
app/components/sd/index.tsx

@@ -1,2 +1,3 @@
 export * from "./sd";
 export * from "./sd-panel";
+export * from "./sd-new";

+ 85 - 0
app/components/sd/sd-new.tsx

@@ -0,0 +1,85 @@
+import homeStyles from "@/app/components/home.module.scss";
+
+import { IconButton } from "@/app/components/button";
+import GithubIcon from "@/app/icons/github.svg";
+import ReturnIcon from "@/app/icons/return.svg";
+import Locale from "@/app/locales";
+import HistoryIcon from "@/app/icons/history.svg";
+
+import { Path, REPO_URL } from "@/app/constant";
+
+import { useNavigate } from "react-router-dom";
+import dynamic from "next/dynamic";
+import {
+  SideBarContainer,
+  SideBarBody,
+  SideBarTail,
+  useDragSideBar,
+  useHotKey,
+} from "@/app/components/sidebar";
+
+const SdPanel = dynamic(
+  async () => (await import("@/app/components/sd")).SdPanel,
+  {
+    loading: () => null,
+  },
+);
+
+export function SdNew() {
+  useHotKey();
+  const { onDragStart, shouldNarrow } = useDragSideBar();
+  const navigate = useNavigate();
+  return (
+    <SideBarContainer
+      onDragStart={onDragStart}
+      shouldNarrow={shouldNarrow}
+      className={homeStyles["sidebar-show"]}
+    >
+      <div
+        className="window-header"
+        data-tauri-drag-region
+        style={{
+          paddingLeft: 0,
+          paddingRight: 0,
+        }}
+      >
+        {
+          <div className="window-actions">
+            <div className="window-action-button">
+              <IconButton
+                icon={<ReturnIcon />}
+                bordered
+                title={Locale.Sd.Actions.ReturnHome}
+                onClick={() => navigate(Path.Home)}
+              />
+            </div>
+          </div>
+        }
+
+        <div className={`window-header-title`}>
+          <div className={`window-header-main-title`}>Stability</div>
+        </div>
+        <div className="window-actions">
+          <div className="window-action-button">
+            <IconButton
+              icon={<HistoryIcon />}
+              bordered
+              title={Locale.Sd.Actions.History}
+              onClick={() => navigate(Path.Sd)}
+            />
+          </div>
+        </div>
+      </div>
+      <SideBarBody>
+        <SdPanel />
+      </SideBarBody>
+      <SideBarTail
+        primaryAction={
+          <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
+            <IconButton icon={<GithubIcon />} shadow />
+          </a>
+        }
+      />
+    </SideBarContainer>
+  );
+}

+ 17 - 21
app/components/sd/sd-sidebar.tsx

@@ -1,5 +1,3 @@
-import styles from "@/app/components/home.module.scss";
-
 import { IconButton } from "@/app/components/button";
 import GithubIcon from "@/app/icons/github.svg";
 import SDIcon from "@/app/icons/sd.svg";
@@ -13,12 +11,14 @@ import dynamic from "next/dynamic";
 import {
   SideBarContainer,
   SideBarBody,
+  SideBarHeader,
+  SideBarTail,
   useDragSideBar,
   useHotKey,
 } from "@/app/components/sidebar";
 
 const SdPanel = dynamic(
-  async () => (await import("@/app/components/sd/sd-panel")).SdPanel,
+  async () => (await import("@/app/components/sd")).SdPanel,
   {
     loading: () => null,
   },
@@ -35,31 +35,27 @@ export function SideBar(props: { className?: string }) {
       shouldNarrow={shouldNarrow}
       {...props}
     >
-      <div className={styles["sidebar-header"]} data-tauri-drag-region>
-        <div className={styles["sidebar-title"]} data-tauri-drag-region>
+      <SideBarHeader
+        title={
           <IconButton
             icon={<ReturnIcon />}
             bordered
-            title={Locale.Chat.Actions.ChatList}
-            onClick={() => navigate(Path.Chat)}
+            title={Locale.Sd.Actions.ReturnHome}
+            onClick={() => navigate(Path.Home)}
           />
-        </div>
-        <div className={styles["sidebar-logo"] + " no-dark"}>
-          <SDIcon width={38} height={38} />
-        </div>
-      </div>
+        }
+        logo={<SDIcon width={38} height={38} />}
+      ></SideBarHeader>
       <SideBarBody>
         <SdPanel />
       </SideBarBody>
-      <div className={styles["sidebar-tail"]}>
-        <div className={styles["sidebar-actions"]}>
-          <div className={styles["sidebar-action"]}>
-            <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
-              <IconButton icon={<GithubIcon />} shadow />
-            </a>
-          </div>
-        </div>
-      </div>
+      <SideBarTail
+        primaryAction={
+          <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
+            <IconButton icon={<GithubIcon />} shadow />
+          </a>
+        }
+      />
     </SideBarContainer>
   );
 }

+ 1 - 1
app/components/sd/sd.tsx

@@ -111,7 +111,7 @@ export function Sd() {
                     icon={<ReturnIcon />}
                     bordered
                     title={Locale.Chat.Actions.ChatList}
-                    onClick={() => navigate(Path.SdPanel)}
+                    onClick={() => navigate(Path.SdNew)}
                   />
                 </div>
               </div>

+ 115 - 104
app/components/sidebar.tsx

@@ -1,4 +1,4 @@
-import { useEffect, useRef, useMemo, useState } from "react";
+import React, { useEffect, useRef, useMemo, useState, Fragment } from "react";
 
 import styles from "./home.module.scss";
 
@@ -161,70 +161,24 @@ export function SideBarContainer(props: {
   );
 }
 
-export function SideBarHeader(props: { shouldNarrow: boolean }) {
-  const navigate = useNavigate();
-  const config = useAppConfig();
-  const { shouldNarrow } = props;
-  const [showPluginSelector, setShowPluginSelector] = useState(false);
-
+export function SideBarHeader(props: {
+  title?: string | React.ReactNode;
+  subTitle?: string | React.ReactNode;
+  logo?: React.ReactNode;
+  children?: React.ReactNode;
+}) {
+  const { title, subTitle, logo, children } = props;
   return (
-    <>
+    <Fragment>
       <div className={styles["sidebar-header"]} data-tauri-drag-region>
         <div className={styles["sidebar-title"]} data-tauri-drag-region>
-          NextChat
-        </div>
-        <div className={styles["sidebar-sub-title"]}>
-          Build your own AI assistant.
+          {title}
         </div>
-        <div className={styles["sidebar-logo"] + " no-dark"}>
-          <ChatGptIcon />
-        </div>
-      </div>
-
-      <div className={styles["sidebar-header-bar"]}>
-        <IconButton
-          icon={<MaskIcon />}
-          text={shouldNarrow ? undefined : Locale.Mask.Name}
-          className={styles["sidebar-bar-button"]}
-          onClick={() => {
-            if (config.dontShowMaskSplashScreen !== true) {
-              navigate(Path.NewChat, { state: { fromHome: true } });
-            } else {
-              navigate(Path.Masks, { state: { fromHome: true } });
-            }
-          }}
-          shadow
-        />
-        <IconButton
-          icon={<PluginIcon />}
-          text={shouldNarrow ? undefined : Locale.Plugin.Name}
-          className={styles["sidebar-bar-button"]}
-          onClick={() => setShowPluginSelector(true)}
-          shadow
-        />
+        <div className={styles["sidebar-sub-title"]}>{subTitle}</div>
+        <div className={styles["sidebar-logo"] + " no-dark"}>{logo}</div>
       </div>
-      {showPluginSelector && (
-        <Selector
-          items={[
-            {
-              title: "👇 Please select the plugin you need to use",
-              value: "-",
-              disable: true,
-            },
-            ...PLUGINS.map((item) => {
-              return {
-                title: item.name,
-                value: item.path,
-              };
-            }),
-          ]}
-          onClose={() => setShowPluginSelector(false)}
-          onSelection={(s) => {
-            navigate(s[0], { state: { fromHome: true } });
-          }}
-        />
-      )}
-    </>
+      {children}
+    </Fragment>
   );
 }
 
@@ -240,50 +194,16 @@ export function SideBarBody(props: {
   );
 }
 
-export function SideBarTail(props: { shouldNarrow: boolean }) {
-  const { shouldNarrow } = props;
-  const chatStore = useChatStore();
-  const navigate = useNavigate();
-  const config = useAppConfig();
+export function SideBarTail(props: {
+  primaryAction?: React.ReactNode;
+  secondaryAction?: React.ReactNode;
+}) {
+  const { primaryAction, secondaryAction } = props;
+
   return (
     <div className={styles["sidebar-tail"]}>
-      <div className={styles["sidebar-actions"]}>
-        <div className={styles["sidebar-action"] + " " + styles.mobile}>
-          <IconButton
-            icon={<DeleteIcon />}
-            onClick={async () => {
-              if (await showConfirm(Locale.Home.DeleteChat)) {
-                chatStore.deleteSession(chatStore.currentSessionIndex);
-              }
-            }}
-          />
-        </div>
-        <div className={styles["sidebar-action"]}>
-          <Link to={Path.Settings}>
-            <IconButton icon={<SettingsIcon />} shadow />
-          </Link>
-        </div>
-        <div className={styles["sidebar-action"]}>
-          <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
-            <IconButton icon={<GithubIcon />} shadow />
-          </a>
-        </div>
-      </div>
-      <div>
-        <IconButton
-          icon={<AddIcon />}
-          text={shouldNarrow ? undefined : Locale.Home.NewChat}
-          onClick={() => {
-            if (config.dontShowMaskSplashScreen) {
-              chatStore.newSession();
-              navigate(Path.Chat);
-            } else {
-              navigate(Path.NewChat);
-            }
-          }}
-          shadow
-        />
-      </div>
+      <div className={styles["sidebar-actions"]}>{primaryAction}</div>
+      <div className={styles["sidebar-actions"]}>{secondaryAction}</div>
     </div>
   );
 }
@@ -291,7 +211,10 @@ export function SideBarTail(props: { shouldNarrow: boolean }) {
 export function SideBar(props: { className?: string }) {
   useHotKey();
   const { onDragStart, shouldNarrow } = useDragSideBar();
+  const [showPluginSelector, setShowPluginSelector] = useState(false);
   const navigate = useNavigate();
+  const config = useAppConfig();
+  const chatStore = useChatStore();
 
   return (
     <SideBarContainer
@@ -299,7 +222,55 @@ export function SideBar(props: { className?: string }) {
       shouldNarrow={shouldNarrow}
       {...props}
     >
-      <SideBarHeader shouldNarrow={shouldNarrow} />
+      <SideBarHeader
+        title="NextChat"
+        subTitle="Build your own AI assistant."
+        logo={<ChatGptIcon />}
+      >
+        <div className={styles["sidebar-header-bar"]}>
+          <IconButton
+            icon={<MaskIcon />}
+            text={shouldNarrow ? undefined : Locale.Mask.Name}
+            className={styles["sidebar-bar-button"]}
+            onClick={() => {
+              if (config.dontShowMaskSplashScreen !== true) {
+                navigate(Path.NewChat, { state: { fromHome: true } });
+              } else {
+                navigate(Path.Masks, { state: { fromHome: true } });
+              }
+            }}
+            shadow
+          />
+          <IconButton
+            icon={<PluginIcon />}
+            text={shouldNarrow ? undefined : Locale.Plugin.Name}
+            className={styles["sidebar-bar-button"]}
+            onClick={() => setShowPluginSelector(true)}
+            shadow
+          />
+        </div>
+        {showPluginSelector && (
+          <Selector
+            items={[
+              {
+                title: "👇 Please select the plugin you need to use",
+                value: "-",
+                disable: true,
+              },
+              ...PLUGINS.map((item) => {
+                return {
+                  title: item.name,
+                  value: item.path,
+                };
+              }),
+            ]}
+            onClose={() => setShowPluginSelector(false)}
+            onSelection={(s) => {
+              navigate(s[0], { state: { fromHome: true } });
+            }}
+          />
+        )}
+      </SideBarHeader>
       <SideBarBody
         onClick={(e) => {
           if (e.target === e.currentTarget) {
@@ -309,7 +280,47 @@ export function SideBar(props: { className?: string }) {
       >
         <ChatList narrow={shouldNarrow} />
       </SideBarBody>
-      <SideBarTail shouldNarrow={shouldNarrow} />
+      <SideBarTail
+        primaryAction={
+          <>
+            <div className={styles["sidebar-action"] + " " + styles.mobile}>
+              <IconButton
+                icon={<DeleteIcon />}
+                onClick={async () => {
+                  if (await showConfirm(Locale.Home.DeleteChat)) {
+                    chatStore.deleteSession(chatStore.currentSessionIndex);
+                  }
+                }}
+              />
+            </div>
+            <div className={styles["sidebar-action"]}>
+              <Link to={Path.Settings}>
+                <IconButton icon={<SettingsIcon />} shadow />
+              </Link>
+            </div>
+            <div className={styles["sidebar-action"]}>
+              <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
+                <IconButton icon={<GithubIcon />} shadow />
+              </a>
+            </div>
+          </>
+        }
+        secondaryAction={
+          <IconButton
+            icon={<AddIcon />}
+            text={shouldNarrow ? undefined : Locale.Home.NewChat}
+            onClick={() => {
+              if (config.dontShowMaskSplashScreen) {
+                chatStore.newSession();
+                navigate(Path.Chat);
+              } else {
+                navigate(Path.NewChat);
+              }
+            }}
+            shadow
+          />
+        }
+      />
     </SideBarContainer>
   );
 }

+ 1 - 1
app/constant.ts

@@ -34,7 +34,7 @@ export enum Path {
   Masks = "/masks",
   Auth = "/auth",
   Sd = "/sd",
-  SdPanel = "/sd-panel",
+  SdNew = "/sd-new",
 }
 
 export enum ApiPath {

+ 10 - 0
app/icons/history.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+    <path d="M5.81836 6.72729V14H13.0911" stroke="#333" stroke-width="4" stroke-linecap="round"
+        stroke-linejoin="round" />
+    <path
+        d="M4 24C4 35.0457 12.9543 44 24 44V44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C16.598 4 10.1351 8.02111 6.67677 13.9981"
+        stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
+    <path d="M24.005 12L24.0038 24.0088L32.4832 32.4882" stroke="#333" stroke-width="4"
+        stroke-linecap="round" stroke-linejoin="round" />
+</svg>

+ 2 - 0
app/locales/cn.ts

@@ -564,6 +564,8 @@ const cn = {
       Copy: "复制提示词",
       Delete: "删除",
       Retry: "重试",
+      ReturnHome: "返回首页",
+      History: "查看历史",
     },
     EmptyRecord: "暂无绘画记录",
     Status: {

+ 2 - 0
app/locales/en.ts

@@ -570,6 +570,8 @@ const en: LocaleType = {
       Copy: "Copy Prompt",
       Delete: "Delete",
       Retry: "Retry",
+      ReturnHome: "Return Home",
+      History: "History",
     },
     EmptyRecord: "No images yet",
     Status: {