diff --git a/.github/workflows/deploy_preview.yml b/.github/workflows/deploy_preview.yml index 30d9b85b44c..b988452433b 100644 --- a/.github/workflows/deploy_preview.yml +++ b/.github/workflows/deploy_preview.yml @@ -3,9 +3,7 @@ name: VercelPreviewDeployment on: pull_request_target: types: - - opened - - synchronize - - reopened + - review_requested env: VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000000..faf7205d9cb --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,39 @@ +name: Run Tests + +on: + push: + branches: + - main + tags: + - "!*" + pull_request: + types: + - review_requested + +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 diff --git a/app/client/platforms/openai.ts b/app/client/platforms/openai.ts index 912c991f4c6..b9443905ecf 100644 --- a/app/client/platforms/openai.ts +++ b/app/client/platforms/openai.ts @@ -394,7 +394,7 @@ export class ChatGPTApi implements LLMApi { // make a fetch request const requestTimeoutId = setTimeout( () => 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); diff --git a/app/components/auth.tsx b/app/components/auth.tsx index e19512d8741..539a52eecc9 100644 --- a/app/components/auth.tsx +++ b/app/components/auth.tsx @@ -11,6 +11,7 @@ import Logo from "../icons/logo.svg"; import { useMobileScreen } from "@/app/utils"; import BotIcon from "../icons/bot.svg"; import { getClientConfig } from "../config/client"; +import { PasswordInput } from "./ui-lib"; import LeftIcon from "@/app/icons/left.svg"; import { safeLocalStorage } from "@/app/utils"; import { @@ -60,36 +61,43 @@ export function AuthPage() {
{Locale.Auth.Title}
{Locale.Auth.Tips}
- { accessStore.update( (access) => (access.accessCode = e.currentTarget.value), ); }} /> + {!accessStore.hideUserApiKey ? ( <>
{Locale.Auth.SubTips}
- { accessStore.update( (access) => (access.openaiApiKey = e.currentTarget.value), ); }} /> - { accessStore.update( (access) => (access.googleApiKey = e.currentTarget.value), diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 5f71bc19280..73ebda4ebae 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -120,12 +120,15 @@ import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; -const localStorage = safeLocalStorage(); import { ClientApi } from "../client/api"; import { createTTSPlayer } from "../utils/audio"; import { OpenAITranscriptionApi, WebTranscriptionApi } from "../utils/speech"; import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; +import { isEmpty } from "lodash-es"; + +const localStorage = safeLocalStorage(); + const ttsPlayer = createTTSPlayer(); const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { @@ -1090,7 +1093,7 @@ function _Chat() { }; const doSubmit = (userInput: string) => { - if (userInput.trim() === "") return; + if (userInput.trim() === "" && isEmpty(attachImages)) return; const matchCommand = chatCommands.match(userInput); if (matchCommand.matched) { setUserInput(""); diff --git a/app/components/home.module.scss b/app/components/home.module.scss index b31334568dd..381b6a9b951 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -140,6 +140,9 @@ display: flex; justify-content: space-between; align-items: center; + &-narrow { + justify-content: center; + } } .sidebar-logo { diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index a25b8537bc1..b1083054fad 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -169,6 +169,12 @@ export function PreCode(props: { children: any }) { } 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(null); const [collapsed, setCollapsed] = useState(true); const [showToggle, setShowToggle] = useState(false); @@ -184,25 +190,32 @@ function CustomCode(props: { children: any; className?: string }) { const toggleCollapsed = () => { setCollapsed((collapsed) => !collapsed); }; + const renderShowMoreButton = () => { + if (showToggle && enableCodeFold && collapsed) { + return ( +
+ +
+ ); + } + return null; + }; return ( <> {props.children} - {showToggle && collapsed && ( -
- -
- )} + + {renderShowMoreButton()} ); } diff --git a/app/components/mask.tsx b/app/components/mask.tsx index c60e7a528fe..12b19e33514 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -183,6 +183,23 @@ export function MaskConfig(props: { > )} + {globalConfig.enableCodeFold && ( + + { + props.updateMask((mask) => { + mask.enableCodeFold = e.currentTarget.checked; + }); + }} + > + + )} {!props.shouldSyncFromGlobal ? ( + + + updateConfig( + (config) => (config.enableCodeFold = e.currentTarget.checked), + ) + } + > + diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 493b1103beb..2a5c308b78c 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -165,11 +165,17 @@ export function SideBarHeader(props: { subTitle?: string | React.ReactNode; logo?: React.ReactNode; children?: React.ReactNode; + shouldNarrow?: boolean; }) { - const { title, subTitle, logo, children } = props; + const { title, subTitle, logo, children, shouldNarrow } = props; return ( -
+
{title} @@ -227,6 +233,7 @@ export function SideBar(props: { className?: string }) { title="NextChat" subTitle="Build your own AI assistant." logo={} + shouldNarrow={shouldNarrow} >
0) { mContent = [ - { - type: "text", - text: userContent, - }, + ...(userContent + ? [{ type: "text" as const, 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({ role: "user", content: mContent, diff --git a/app/store/config.ts b/app/store/config.ts index b0e4c017aeb..cdf1d6e1bea 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -56,6 +56,8 @@ export const DEFAULT_CONFIG = { enableArtifacts: true, // show artifacts config + enableCodeFold: true, // code fold config + disablePromptHint: false, dontShowMaskSplashScreen: false, // dont show splash screen when create chat diff --git a/app/store/mask.ts b/app/store/mask.ts index 0c74a892e56..850abeef6f4 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -19,6 +19,7 @@ export type Mask = { builtin: boolean; plugin?: string[]; enableArtifacts?: boolean; + enableCodeFold?: boolean; }; export const DEFAULT_MASK_STATE = { diff --git a/package.json b/package.json index 6db49241fbd..e433445342e 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "mask": "npx tsx app/masks/build.ts", "mask:watch": "npx watch \"yarn mask\" app/masks", "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", "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\"", "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", "prepare": "husky install", "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev", @@ -88,4 +88,4 @@ "lint-staged/yaml": "^2.2.2" }, "packageManager": "yarn@1.22.19" -} \ No newline at end of file +}