mirror of
				https://github.com/Yidadaa/ChatGPT-Next-Web.git
				synced 2025-11-01 06:19:35 +08:00 
			
		
		
		
	Merge remote-tracking branch 'connectai/main' into feature/H0llyW00dzZ-updater
This commit is contained in:
		
							
								
								
									
										4
									
								
								.github/workflows/deploy_preview.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/deploy_preview.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,9 +3,7 @@ name: VercelPreviewDeployment | |||||||
| on: | on: | ||||||
|   pull_request_target: |   pull_request_target: | ||||||
|     types: |     types: | ||||||
|       - opened |       - review_requested | ||||||
|       - synchronize |  | ||||||
|       - reopened |  | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }} |   VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }} | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | name: Run Tests | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - main | ||||||
|  |     tags: | ||||||
|  |       - "!*" | ||||||
|  |   pull_request: | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   test: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout repository | ||||||
|  |         uses: actions/checkout@v4 | ||||||
|  |  | ||||||
|  |       - name: Set up Node.js | ||||||
|  |         uses: actions/setup-node@v3 | ||||||
|  |         with: | ||||||
|  |           node-version: 18 | ||||||
|  |           cache: "yarn" | ||||||
|  |  | ||||||
|  |       - name: Cache node_modules | ||||||
|  |         uses: actions/cache@v4 | ||||||
|  |         with: | ||||||
|  |           path: node_modules | ||||||
|  |           key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} | ||||||
|  |           restore-keys: | | ||||||
|  |             ${{ runner.os }}-node_modules- | ||||||
|  |  | ||||||
|  |       - name: Install dependencies | ||||||
|  |         run: yarn install | ||||||
|  |  | ||||||
|  |       - name: Run Jest tests | ||||||
|  |         run: yarn test:ci | ||||||
| @@ -352,7 +352,7 @@ export class ChatGPTApi implements LLMApi { | |||||||
|         // make a fetch request |         // make a fetch request | ||||||
|         const requestTimeoutId = setTimeout( |         const requestTimeoutId = setTimeout( | ||||||
|           () => controller.abort(), |           () => controller.abort(), | ||||||
|           isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 2 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow. |           isDalle3 || isO1 ? REQUEST_TIMEOUT_MS * 4 : REQUEST_TIMEOUT_MS, // dalle3 using b64_json is slow. | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         const res = await fetch(chatPath, chatPayload); |         const res = await fetch(chatPath, chatPayload); | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import Logo from "../icons/logo.svg"; | |||||||
| import { useMobileScreen } from "@/app/utils"; | import { useMobileScreen } from "@/app/utils"; | ||||||
| import BotIcon from "../icons/bot.svg"; | import BotIcon from "../icons/bot.svg"; | ||||||
| import { getClientConfig } from "../config/client"; | import { getClientConfig } from "../config/client"; | ||||||
|  | import { PasswordInput } from "./ui-lib"; | ||||||
| import LeftIcon from "@/app/icons/left.svg"; | import LeftIcon from "@/app/icons/left.svg"; | ||||||
| import { safeLocalStorage } from "@/app/utils"; | import { safeLocalStorage } from "@/app/utils"; | ||||||
| import { | import { | ||||||
| @@ -60,36 +61,43 @@ export function AuthPage() { | |||||||
|       <div className={styles["auth-title"]}>{Locale.Auth.Title}</div> |       <div className={styles["auth-title"]}>{Locale.Auth.Title}</div> | ||||||
|       <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div> |       <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div> | ||||||
|  |  | ||||||
|       <input |       <PasswordInput | ||||||
|         className={styles["auth-input"]} |         style={{ marginTop: "3vh", marginBottom: "3vh" }} | ||||||
|         type="password" |         aria={Locale.Settings.ShowPassword} | ||||||
|         placeholder={Locale.Auth.Input} |         aria-label={Locale.Auth.Input} | ||||||
|         value={accessStore.accessCode} |         value={accessStore.accessCode} | ||||||
|  |         type="text" | ||||||
|  |         placeholder={Locale.Auth.Input} | ||||||
|         onChange={(e) => { |         onChange={(e) => { | ||||||
|           accessStore.update( |           accessStore.update( | ||||||
|             (access) => (access.accessCode = e.currentTarget.value), |             (access) => (access.accessCode = e.currentTarget.value), | ||||||
|           ); |           ); | ||||||
|         }} |         }} | ||||||
|       /> |       /> | ||||||
|  |  | ||||||
|       {!accessStore.hideUserApiKey ? ( |       {!accessStore.hideUserApiKey ? ( | ||||||
|         <> |         <> | ||||||
|           <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div> |           <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div> | ||||||
|           <input |           <PasswordInput | ||||||
|             className={styles["auth-input"]} |             style={{ marginTop: "3vh", marginBottom: "3vh" }} | ||||||
|             type="password" |             aria={Locale.Settings.ShowPassword} | ||||||
|             placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder} |             aria-label={Locale.Settings.Access.OpenAI.ApiKey.Placeholder} | ||||||
|             value={accessStore.openaiApiKey} |             value={accessStore.openaiApiKey} | ||||||
|  |             type="text" | ||||||
|  |             placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder} | ||||||
|             onChange={(e) => { |             onChange={(e) => { | ||||||
|               accessStore.update( |               accessStore.update( | ||||||
|                 (access) => (access.openaiApiKey = e.currentTarget.value), |                 (access) => (access.openaiApiKey = e.currentTarget.value), | ||||||
|               ); |               ); | ||||||
|             }} |             }} | ||||||
|           /> |           /> | ||||||
|           <input |           <PasswordInput | ||||||
|             className={styles["auth-input-second"]} |             style={{ marginTop: "3vh", marginBottom: "3vh" }} | ||||||
|             type="password" |             aria={Locale.Settings.ShowPassword} | ||||||
|             placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder} |             aria-label={Locale.Settings.Access.Google.ApiKey.Placeholder} | ||||||
|             value={accessStore.googleApiKey} |             value={accessStore.googleApiKey} | ||||||
|  |             type="text" | ||||||
|  |             placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder} | ||||||
|             onChange={(e) => { |             onChange={(e) => { | ||||||
|               accessStore.update( |               accessStore.update( | ||||||
|                 (access) => (access.googleApiKey = e.currentTarget.value), |                 (access) => (access.googleApiKey = e.currentTarget.value), | ||||||
|   | |||||||
| @@ -115,11 +115,14 @@ import { getClientConfig } from "../config/client"; | |||||||
| import { useAllModels } from "../utils/hooks"; | import { useAllModels } from "../utils/hooks"; | ||||||
| import { MultimodalContent } from "../client/api"; | import { MultimodalContent } from "../client/api"; | ||||||
|  |  | ||||||
| const localStorage = safeLocalStorage(); |  | ||||||
| import { ClientApi } from "../client/api"; | import { ClientApi } from "../client/api"; | ||||||
| import { createTTSPlayer } from "../utils/audio"; | import { createTTSPlayer } from "../utils/audio"; | ||||||
| import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; | import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; | ||||||
|  |  | ||||||
|  | import { isEmpty } from "lodash-es"; | ||||||
|  |  | ||||||
|  | const localStorage = safeLocalStorage(); | ||||||
|  |  | ||||||
| const ttsPlayer = createTTSPlayer(); | const ttsPlayer = createTTSPlayer(); | ||||||
|  |  | ||||||
| const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { | const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { | ||||||
| @@ -1015,7 +1018,7 @@ function _Chat() { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const doSubmit = (userInput: string) => { |   const doSubmit = (userInput: string) => { | ||||||
|     if (userInput.trim() === "") return; |     if (userInput.trim() === "" && isEmpty(attachImages)) return; | ||||||
|     const matchCommand = chatCommands.match(userInput); |     const matchCommand = chatCommands.match(userInput); | ||||||
|     if (matchCommand.matched) { |     if (matchCommand.matched) { | ||||||
|       setUserInput(""); |       setUserInput(""); | ||||||
|   | |||||||
| @@ -140,6 +140,9 @@ | |||||||
|   display: flex; |   display: flex; | ||||||
|   justify-content: space-between; |   justify-content: space-between; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|  |   &-narrow { | ||||||
|  |     justify-content: center; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .sidebar-logo { | .sidebar-logo { | ||||||
|   | |||||||
| @@ -169,6 +169,12 @@ export function PreCode(props: { children: any }) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function CustomCode(props: { children: any; className?: string }) { | function CustomCode(props: { children: any; className?: string }) { | ||||||
|  |   const chatStore = useChatStore(); | ||||||
|  |   const session = chatStore.currentSession(); | ||||||
|  |   const config = useAppConfig(); | ||||||
|  |   const enableCodeFold = | ||||||
|  |     session.mask?.enableCodeFold !== false && config.enableCodeFold; | ||||||
|  |  | ||||||
|   const ref = useRef<HTMLPreElement>(null); |   const ref = useRef<HTMLPreElement>(null); | ||||||
|   const [collapsed, setCollapsed] = useState(true); |   const [collapsed, setCollapsed] = useState(true); | ||||||
|   const [showToggle, setShowToggle] = useState(false); |   const [showToggle, setShowToggle] = useState(false); | ||||||
| @@ -184,25 +190,30 @@ function CustomCode(props: { children: any; className?: string }) { | |||||||
|   const toggleCollapsed = () => { |   const toggleCollapsed = () => { | ||||||
|     setCollapsed((collapsed) => !collapsed); |     setCollapsed((collapsed) => !collapsed); | ||||||
|   }; |   }; | ||||||
|  |   const renderShowMoreButton = () => { | ||||||
|  |     if (showToggle && enableCodeFold && collapsed) { | ||||||
|  |       return ( | ||||||
|  |         <div className={`show-hide-button ${collapsed ? "collapsed" : "expanded"}`}> | ||||||
|  |           <button onClick={toggleCollapsed}>{Locale.NewChat.More}</button> | ||||||
|  |         </div> | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   }; | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <code |       <code | ||||||
|         className={props?.className} |         className={props?.className} | ||||||
|         ref={ref} |         ref={ref} | ||||||
|         style={{ |         style={{ | ||||||
|           maxHeight: collapsed ? "400px" : "none", |           maxHeight: enableCodeFold && collapsed ? "400px" : "none", | ||||||
|           overflowY: "hidden", |           overflowY: "hidden", | ||||||
|         }} |         }} | ||||||
|       > |       > | ||||||
|         {props.children} |         {props.children} | ||||||
|       </code> |       </code> | ||||||
|       {showToggle && collapsed && ( |  | ||||||
|         <div |       {renderShowMoreButton()} | ||||||
|           className={`show-hide-button ${collapsed ? "collapsed" : "expanded"}`} |  | ||||||
|         > |  | ||||||
|           <button onClick={toggleCollapsed}>{Locale.NewChat.More}</button> |  | ||||||
|         </div> |  | ||||||
|       )} |  | ||||||
|     </> |     </> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -183,6 +183,23 @@ export function MaskConfig(props: { | |||||||
|             ></input> |             ></input> | ||||||
|           </ListItem> |           </ListItem> | ||||||
|         )} |         )} | ||||||
|  |         {globalConfig.enableCodeFold && ( | ||||||
|  |           <ListItem | ||||||
|  |             title={Locale.Mask.Config.CodeFold.Title} | ||||||
|  |             subTitle={Locale.Mask.Config.CodeFold.SubTitle} | ||||||
|  |           > | ||||||
|  |             <input | ||||||
|  |               aria-label={Locale.Mask.Config.CodeFold.Title} | ||||||
|  |               type="checkbox" | ||||||
|  |               checked={props.mask.enableCodeFold !== false} | ||||||
|  |               onChange={(e) => { | ||||||
|  |                 props.updateMask((mask) => { | ||||||
|  |                   mask.enableCodeFold = e.currentTarget.checked; | ||||||
|  |                 }); | ||||||
|  |               }} | ||||||
|  |             ></input> | ||||||
|  |           </ListItem> | ||||||
|  |         )} | ||||||
|  |  | ||||||
|         {!props.shouldSyncFromGlobal ? ( |         {!props.shouldSyncFromGlobal ? ( | ||||||
|           <ListItem |           <ListItem | ||||||
|   | |||||||
| @@ -1517,6 +1517,22 @@ export function Settings() { | |||||||
|               } |               } | ||||||
|             ></input> |             ></input> | ||||||
|           </ListItem> |           </ListItem> | ||||||
|  |           <ListItem | ||||||
|  |             title={Locale.Mask.Config.CodeFold.Title} | ||||||
|  |             subTitle={Locale.Mask.Config.CodeFold.SubTitle} | ||||||
|  |           > | ||||||
|  |             <input | ||||||
|  |               aria-label={Locale.Mask.Config.CodeFold.Title} | ||||||
|  |               type="checkbox" | ||||||
|  |               checked={config.enableCodeFold} | ||||||
|  |               data-testid="enable-code-fold-checkbox" | ||||||
|  |               onChange={(e) => | ||||||
|  |                 updateConfig( | ||||||
|  |                   (config) => (config.enableCodeFold = e.currentTarget.checked), | ||||||
|  |                 ) | ||||||
|  |               } | ||||||
|  |             ></input> | ||||||
|  |           </ListItem> | ||||||
|         </List> |         </List> | ||||||
|  |  | ||||||
|         <SyncItems /> |         <SyncItems /> | ||||||
|   | |||||||
| @@ -165,11 +165,17 @@ export function SideBarHeader(props: { | |||||||
|   subTitle?: string | React.ReactNode; |   subTitle?: string | React.ReactNode; | ||||||
|   logo?: React.ReactNode; |   logo?: React.ReactNode; | ||||||
|   children?: React.ReactNode; |   children?: React.ReactNode; | ||||||
|  |   shouldNarrow?: boolean; | ||||||
| }) { | }) { | ||||||
|   const { title, subTitle, logo, children } = props; |   const { title, subTitle, logo, children, shouldNarrow } = props; | ||||||
|   return ( |   return ( | ||||||
|     <Fragment> |     <Fragment> | ||||||
|       <div className={styles["sidebar-header"]} data-tauri-drag-region> |       <div | ||||||
|  |         className={`${styles["sidebar-header"]} ${ | ||||||
|  |           shouldNarrow ? styles["sidebar-header-narrow"] : "" | ||||||
|  |         }`} | ||||||
|  |         data-tauri-drag-region | ||||||
|  |       > | ||||||
|         <div className={styles["sidebar-title-container"]}> |         <div className={styles["sidebar-title-container"]}> | ||||||
|           <div className={styles["sidebar-title"]} data-tauri-drag-region> |           <div className={styles["sidebar-title"]} data-tauri-drag-region> | ||||||
|             {title} |             {title} | ||||||
| @@ -227,6 +233,7 @@ export function SideBar(props: { className?: string }) { | |||||||
|         title="NextChat" |         title="NextChat" | ||||||
|         subTitle="Build your own AI assistant." |         subTitle="Build your own AI assistant." | ||||||
|         logo={<ChatGptIcon />} |         logo={<ChatGptIcon />} | ||||||
|  |         shouldNarrow={shouldNarrow} | ||||||
|       > |       > | ||||||
|         <div className={styles["sidebar-header-bar"]}> |         <div className={styles["sidebar-header-bar"]}> | ||||||
|           <IconButton |           <IconButton | ||||||
|   | |||||||
| @@ -497,8 +497,8 @@ const cn = { | |||||||
|  |  | ||||||
|     Model: "模型 (model)", |     Model: "模型 (model)", | ||||||
|     CompressModel: { |     CompressModel: { | ||||||
|       Title: "压缩模型", |       Title: "对话摘要模型", | ||||||
|       SubTitle: "用于压缩历史记录的模型", |       SubTitle: "用于压缩历史记录、生成对话标题的模型", | ||||||
|     }, |     }, | ||||||
|     Temperature: { |     Temperature: { | ||||||
|       Title: "随机性 (temperature)", |       Title: "随机性 (temperature)", | ||||||
| @@ -667,6 +667,10 @@ const cn = { | |||||||
|         Title: "启用Artifacts", |         Title: "启用Artifacts", | ||||||
|         SubTitle: "启用之后可以直接渲染HTML页面", |         SubTitle: "启用之后可以直接渲染HTML页面", | ||||||
|       }, |       }, | ||||||
|  |       CodeFold: { | ||||||
|  |         Title: "启用代码折叠", | ||||||
|  |         SubTitle: "启用之后可以自动折叠/展开过长的代码块", | ||||||
|  |       }, | ||||||
|       Share: { |       Share: { | ||||||
|         Title: "分享此面具", |         Title: "分享此面具", | ||||||
|         SubTitle: "生成此面具的直达链接", |         SubTitle: "生成此面具的直达链接", | ||||||
|   | |||||||
| @@ -502,8 +502,8 @@ const en: LocaleType = { | |||||||
|  |  | ||||||
|     Model: "Model", |     Model: "Model", | ||||||
|     CompressModel: { |     CompressModel: { | ||||||
|       Title: "Compression Model", |       Title: "Summary Model", | ||||||
|       SubTitle: "Model used to compress history", |       SubTitle: "Model used to compress history and generate title", | ||||||
|     }, |     }, | ||||||
|     Temperature: { |     Temperature: { | ||||||
|       Title: "Temperature", |       Title: "Temperature", | ||||||
| @@ -677,6 +677,11 @@ const en: LocaleType = { | |||||||
|         Title: "Enable Artifacts", |         Title: "Enable Artifacts", | ||||||
|         SubTitle: "Can render HTML page when enable artifacts.", |         SubTitle: "Can render HTML page when enable artifacts.", | ||||||
|       }, |       }, | ||||||
|  |       CodeFold: { | ||||||
|  |         Title: "Enable CodeFold", | ||||||
|  |         SubTitle: | ||||||
|  |           "Automatically collapse/expand overly long code blocks when CodeFold is enabled", | ||||||
|  |       }, | ||||||
|       Share: { |       Share: { | ||||||
|         Title: "Share This Mask", |         Title: "Share This Mask", | ||||||
|         SubTitle: "Generate a link to this mask", |         SubTitle: "Generate a link to this mask", | ||||||
|   | |||||||
| @@ -372,22 +372,16 @@ export const useChatStore = createPersistStore( | |||||||
|  |  | ||||||
|         if (attachImages && attachImages.length > 0) { |         if (attachImages && attachImages.length > 0) { | ||||||
|           mContent = [ |           mContent = [ | ||||||
|             { |             ...(userContent | ||||||
|               type: "text", |               ? [{ type: "text" as const, text: userContent }] | ||||||
|               text: userContent, |               : []), | ||||||
|             }, |             ...attachImages.map((url) => ({ | ||||||
|  |               type: "image_url" as const, | ||||||
|  |               image_url: { url }, | ||||||
|  |             })), | ||||||
|           ]; |           ]; | ||||||
|           mContent = mContent.concat( |  | ||||||
|             attachImages.map((url) => { |  | ||||||
|               return { |  | ||||||
|                 type: "image_url", |  | ||||||
|                 image_url: { |  | ||||||
|                   url: url, |  | ||||||
|                 }, |  | ||||||
|               }; |  | ||||||
|             }), |  | ||||||
|           ); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let userMessage: ChatMessage = createMessage({ |         let userMessage: ChatMessage = createMessage({ | ||||||
|           role: "user", |           role: "user", | ||||||
|           content: mContent, |           content: mContent, | ||||||
|   | |||||||
| @@ -52,6 +52,8 @@ export const DEFAULT_CONFIG = { | |||||||
|  |  | ||||||
|   enableArtifacts: true, // show artifacts config |   enableArtifacts: true, // show artifacts config | ||||||
|  |  | ||||||
|  |   enableCodeFold: true, // code fold config | ||||||
|  |  | ||||||
|   disablePromptHint: false, |   disablePromptHint: false, | ||||||
|  |  | ||||||
|   dontShowMaskSplashScreen: false, // dont show splash screen when create chat |   dontShowMaskSplashScreen: false, // dont show splash screen when create chat | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ export type Mask = { | |||||||
|   builtin: boolean; |   builtin: boolean; | ||||||
|   plugin?: string[]; |   plugin?: string[]; | ||||||
|   enableArtifacts?: boolean; |   enableArtifacts?: boolean; | ||||||
|  |   enableCodeFold?: boolean; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export const DEFAULT_MASK_STATE = { | export const DEFAULT_MASK_STATE = { | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     "mask": "npx tsx app/masks/build.ts", |     "mask": "npx tsx app/masks/build.ts", | ||||||
|     "mask:watch": "npx watch \"yarn mask\" app/masks", |     "mask:watch": "npx watch \"yarn mask\" app/masks", | ||||||
|     "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", |     "dev": "concurrently -r \"yarn run mask:watch\" \"next dev\"", | ||||||
|     "build": "yarn test:ci && yarn mask && cross-env BUILD_MODE=standalone next build", |     "build": "yarn mask && cross-env BUILD_MODE=standalone next build", | ||||||
|     "start": "next start", |     "start": "next start", | ||||||
|     "lint": "next lint", |     "lint": "next lint", | ||||||
|     "export": "yarn test:ci && yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", |     "export": "yarn mask && cross-env BUILD_MODE=export BUILD_APP=1 next build", | ||||||
|     "export:dev": "concurrently -r \"yarn mask:watch\"  \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", |     "export:dev": "concurrently -r \"yarn mask:watch\"  \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"", | ||||||
|     "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", |     "app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"", | ||||||
|     "app:build": "yarn test:ci && yarn mask && yarn tauri build", |     "app:build": "yarn mask && yarn tauri build", | ||||||
|     "prompts": "node ./scripts/fetch-prompts.mjs", |     "prompts": "node ./scripts/fetch-prompts.mjs", | ||||||
|     "prepare": "husky install", |     "prepare": "husky install", | ||||||
|     "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", |     "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user