mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 08:13:43 +08:00 
			
		
		
		
	Merge pull request #2173 from Yidadaa/bugfix-0627
feat: improve chat commands
This commit is contained in:
		@@ -515,14 +515,6 @@ export function Chat() {
 | 
			
		||||
    { leading: true, trailing: true },
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const onPromptSelect = (prompt: Prompt) => {
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      setPromptHints([]);
 | 
			
		||||
      setUserInput(prompt.content);
 | 
			
		||||
      inputRef.current?.focus();
 | 
			
		||||
    }, 30);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // auto grow input
 | 
			
		||||
  const [inputRows, setInputRows] = useState(2);
 | 
			
		||||
  const measure = useDebouncedCallback(
 | 
			
		||||
@@ -595,6 +587,23 @@ export function Chat() {
 | 
			
		||||
    setAutoScroll(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onPromptSelect = (prompt: Prompt) => {
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      setPromptHints([]);
 | 
			
		||||
 | 
			
		||||
      const matchedChatCommand = chatCommands.match(prompt.content);
 | 
			
		||||
      if (matchedChatCommand.matched) {
 | 
			
		||||
        // if user is selecting a chat command, just trigger it
 | 
			
		||||
        matchedChatCommand.invoke();
 | 
			
		||||
        setUserInput("");
 | 
			
		||||
      } else {
 | 
			
		||||
        // or fill the prompt
 | 
			
		||||
        setUserInput(prompt.content);
 | 
			
		||||
      }
 | 
			
		||||
      inputRef.current?.focus();
 | 
			
		||||
    }, 30);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // stop response
 | 
			
		||||
  const onUserStop = (messageId: number) => {
 | 
			
		||||
    ChatControllerPool.stop(sessionIndex, messageId);
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ import { InputRange } from "./input-range";
 | 
			
		||||
import { useNavigate } from "react-router-dom";
 | 
			
		||||
import { Avatar, AvatarPicker } from "./emoji";
 | 
			
		||||
import { getClientConfig } from "../config/client";
 | 
			
		||||
import { useSyncStore } from "../store/sync";
 | 
			
		||||
 | 
			
		||||
function EditPromptModal(props: { id: number; onClose: () => void }) {
 | 
			
		||||
  const promptStore = usePromptStore();
 | 
			
		||||
@@ -198,6 +199,78 @@ function UserPromptModal(props: { onClose?: () => void }) {
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function SyncItems() {
 | 
			
		||||
  const syncStore = useSyncStore();
 | 
			
		||||
  const webdav = syncStore.webDavConfig;
 | 
			
		||||
 | 
			
		||||
  // not ready: https://github.com/Yidadaa/ChatGPT-Next-Web/issues/920#issuecomment-1609866332
 | 
			
		||||
  return null;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <List>
 | 
			
		||||
      <ListItem
 | 
			
		||||
        title={"上次同步:" + new Date().toLocaleString()}
 | 
			
		||||
        subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
 | 
			
		||||
      >
 | 
			
		||||
        <IconButton
 | 
			
		||||
          icon={<ResetIcon />}
 | 
			
		||||
          text="同步"
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            syncStore.check().then(console.log);
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </ListItem>
 | 
			
		||||
 | 
			
		||||
      <ListItem
 | 
			
		||||
        title={"本地备份"}
 | 
			
		||||
        subTitle={"20 次对话,100 条消息,200 提示词,20 面具"}
 | 
			
		||||
      ></ListItem>
 | 
			
		||||
 | 
			
		||||
      <ListItem
 | 
			
		||||
        title={"Web Dav Server"}
 | 
			
		||||
        subTitle={Locale.Settings.AccessCode.SubTitle}
 | 
			
		||||
      >
 | 
			
		||||
        <input
 | 
			
		||||
          value={webdav.server}
 | 
			
		||||
          type="text"
 | 
			
		||||
          placeholder={"https://example.com"}
 | 
			
		||||
          onChange={(e) => {
 | 
			
		||||
            syncStore.update(
 | 
			
		||||
              (config) => (config.server = e.currentTarget.value),
 | 
			
		||||
            );
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </ListItem>
 | 
			
		||||
 | 
			
		||||
      <ListItem title="Web Dav User Name" subTitle="user name here">
 | 
			
		||||
        <input
 | 
			
		||||
          value={webdav.username}
 | 
			
		||||
          type="text"
 | 
			
		||||
          placeholder={"username"}
 | 
			
		||||
          onChange={(e) => {
 | 
			
		||||
            syncStore.update(
 | 
			
		||||
              (config) => (config.username = e.currentTarget.value),
 | 
			
		||||
            );
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </ListItem>
 | 
			
		||||
 | 
			
		||||
      <ListItem title="Web Dav Password" subTitle="password here">
 | 
			
		||||
        <input
 | 
			
		||||
          value={webdav.password}
 | 
			
		||||
          type="text"
 | 
			
		||||
          placeholder={"password"}
 | 
			
		||||
          onChange={(e) => {
 | 
			
		||||
            syncStore.update(
 | 
			
		||||
              (config) => (config.password = e.currentTarget.value),
 | 
			
		||||
            );
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </ListItem>
 | 
			
		||||
    </List>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatVersionDate(t: string) {
 | 
			
		||||
  const d = new Date(+t);
 | 
			
		||||
  const year = d.getUTCFullYear();
 | 
			
		||||
@@ -556,6 +629,7 @@ export function Settings() {
 | 
			
		||||
              <input
 | 
			
		||||
                type="text"
 | 
			
		||||
                value={accessStore.openaiUrl}
 | 
			
		||||
                placeholder="https://api.openai.com/"
 | 
			
		||||
                onChange={(e) =>
 | 
			
		||||
                  accessStore.updateOpenAiUrl(e.currentTarget.value)
 | 
			
		||||
                }
 | 
			
		||||
@@ -596,6 +670,8 @@ export function Settings() {
 | 
			
		||||
          </ListItem>
 | 
			
		||||
        </List>
 | 
			
		||||
 | 
			
		||||
        <SyncItems />
 | 
			
		||||
 | 
			
		||||
        <List>
 | 
			
		||||
          <ModelConfigList
 | 
			
		||||
            modelConfig={config.modelConfig}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ export enum StoreKey {
 | 
			
		||||
  Mask = "mask-store",
 | 
			
		||||
  Prompt = "prompt-store",
 | 
			
		||||
  Update = "chat-update",
 | 
			
		||||
  Sync = "sync",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const MAX_SIDEBAR_WIDTH = 500;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								app/store/sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								app/store/sync.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
import { Updater } from "../typing";
 | 
			
		||||
import { create } from "zustand";
 | 
			
		||||
import { persist } from "zustand/middleware";
 | 
			
		||||
import { StoreKey } from "../constant";
 | 
			
		||||
 | 
			
		||||
export interface WebDavConfig {
 | 
			
		||||
  server: string;
 | 
			
		||||
  username: string;
 | 
			
		||||
  password: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SyncStore {
 | 
			
		||||
  webDavConfig: WebDavConfig;
 | 
			
		||||
  lastSyncTime: number;
 | 
			
		||||
 | 
			
		||||
  update: Updater<WebDavConfig>;
 | 
			
		||||
  check: () => Promise<boolean>;
 | 
			
		||||
 | 
			
		||||
  path: (path: string) => string;
 | 
			
		||||
  headers: () => { Authorization: string };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FILE = {
 | 
			
		||||
  root: "/chatgpt-next-web/",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useSyncStore = create<SyncStore>()(
 | 
			
		||||
  persist(
 | 
			
		||||
    (set, get) => ({
 | 
			
		||||
      webDavConfig: {
 | 
			
		||||
        server: "",
 | 
			
		||||
        username: "",
 | 
			
		||||
        password: "",
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      lastSyncTime: 0,
 | 
			
		||||
 | 
			
		||||
      update(updater) {
 | 
			
		||||
        const config = { ...get().webDavConfig };
 | 
			
		||||
        updater(config);
 | 
			
		||||
        set({ webDavConfig: config });
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      async check() {
 | 
			
		||||
        try {
 | 
			
		||||
          const res = await fetch(this.path(""), {
 | 
			
		||||
            method: "PROFIND",
 | 
			
		||||
            headers: this.headers(),
 | 
			
		||||
          });
 | 
			
		||||
          console.log(res);
 | 
			
		||||
          return res.status === 207;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          console.error("[Sync] ", e);
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      path(path: string) {
 | 
			
		||||
        let url = get().webDavConfig.server;
 | 
			
		||||
 | 
			
		||||
        if (!url.endsWith("/")) {
 | 
			
		||||
          url += "/";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (path.startsWith("/")) {
 | 
			
		||||
          path = path.slice(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return url + path;
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      headers() {
 | 
			
		||||
        const auth = btoa(
 | 
			
		||||
          [get().webDavConfig.username, get().webDavConfig.password].join(":"),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
          Authorization: `Basic ${auth}`,
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
    }),
 | 
			
		||||
    {
 | 
			
		||||
      name: StoreKey.Sync,
 | 
			
		||||
      version: 1,
 | 
			
		||||
    },
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
		Reference in New Issue
	
	Block a user