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 },
 | 
					    { leading: true, trailing: true },
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onPromptSelect = (prompt: Prompt) => {
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
      setPromptHints([]);
 | 
					 | 
				
			||||||
      setUserInput(prompt.content);
 | 
					 | 
				
			||||||
      inputRef.current?.focus();
 | 
					 | 
				
			||||||
    }, 30);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // auto grow input
 | 
					  // auto grow input
 | 
				
			||||||
  const [inputRows, setInputRows] = useState(2);
 | 
					  const [inputRows, setInputRows] = useState(2);
 | 
				
			||||||
  const measure = useDebouncedCallback(
 | 
					  const measure = useDebouncedCallback(
 | 
				
			||||||
@@ -595,6 +587,23 @@ export function Chat() {
 | 
				
			|||||||
    setAutoScroll(true);
 | 
					    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
 | 
					  // stop response
 | 
				
			||||||
  const onUserStop = (messageId: number) => {
 | 
					  const onUserStop = (messageId: number) => {
 | 
				
			||||||
    ChatControllerPool.stop(sessionIndex, messageId);
 | 
					    ChatControllerPool.stop(sessionIndex, messageId);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,7 @@ import { InputRange } from "./input-range";
 | 
				
			|||||||
import { useNavigate } from "react-router-dom";
 | 
					import { useNavigate } from "react-router-dom";
 | 
				
			||||||
import { Avatar, AvatarPicker } from "./emoji";
 | 
					import { Avatar, AvatarPicker } from "./emoji";
 | 
				
			||||||
import { getClientConfig } from "../config/client";
 | 
					import { getClientConfig } from "../config/client";
 | 
				
			||||||
 | 
					import { useSyncStore } from "../store/sync";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function EditPromptModal(props: { id: number; onClose: () => void }) {
 | 
					function EditPromptModal(props: { id: number; onClose: () => void }) {
 | 
				
			||||||
  const promptStore = usePromptStore();
 | 
					  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) {
 | 
					function formatVersionDate(t: string) {
 | 
				
			||||||
  const d = new Date(+t);
 | 
					  const d = new Date(+t);
 | 
				
			||||||
  const year = d.getUTCFullYear();
 | 
					  const year = d.getUTCFullYear();
 | 
				
			||||||
@@ -556,6 +629,7 @@ export function Settings() {
 | 
				
			|||||||
              <input
 | 
					              <input
 | 
				
			||||||
                type="text"
 | 
					                type="text"
 | 
				
			||||||
                value={accessStore.openaiUrl}
 | 
					                value={accessStore.openaiUrl}
 | 
				
			||||||
 | 
					                placeholder="https://api.openai.com/"
 | 
				
			||||||
                onChange={(e) =>
 | 
					                onChange={(e) =>
 | 
				
			||||||
                  accessStore.updateOpenAiUrl(e.currentTarget.value)
 | 
					                  accessStore.updateOpenAiUrl(e.currentTarget.value)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -596,6 +670,8 @@ export function Settings() {
 | 
				
			|||||||
          </ListItem>
 | 
					          </ListItem>
 | 
				
			||||||
        </List>
 | 
					        </List>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <SyncItems />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <List>
 | 
					        <List>
 | 
				
			||||||
          <ModelConfigList
 | 
					          <ModelConfigList
 | 
				
			||||||
            modelConfig={config.modelConfig}
 | 
					            modelConfig={config.modelConfig}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ export enum StoreKey {
 | 
				
			|||||||
  Mask = "mask-store",
 | 
					  Mask = "mask-store",
 | 
				
			||||||
  Prompt = "prompt-store",
 | 
					  Prompt = "prompt-store",
 | 
				
			||||||
  Update = "chat-update",
 | 
					  Update = "chat-update",
 | 
				
			||||||
 | 
					  Sync = "sync",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const MAX_SIDEBAR_WIDTH = 500;
 | 
					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