From 5add9156384bd2e568eed69001cfd02f9d1fa8fe Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Wed, 10 May 2023 17:09:41 +0800 Subject: [PATCH 01/15] fix(sidebar): hidden slidebar on lg screen --- src/components/header/Header.tsx | 6 ++++++ src/hooks/index.ts | 1 + src/hooks/useLargeScreen.ts | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/hooks/useLargeScreen.ts diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 01ea4f18..400af9a7 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -1,8 +1,14 @@ import { scrollController, showConversationSidebar, showSettingsSidebar } from '@/stores/ui' +import { useLargeScreen } from '@/hooks' import ConversationHeaderInfo from './ConversationHeaderInfo' import ConversationMessageClearButton from './ConversationMessageClearButton' export default () => { + useLargeScreen(() => { + // bug: when click the setting btn, toggle moible or PC mode, the sidebar will not close + showConversationSidebar.set(false) + showSettingsSidebar.set(false) + }) return (
diff --git a/src/hooks/index.ts b/src/hooks/index.ts index a679460a..71877fc3 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useDark' export * from './useCopy' export * from './useClickOutside' +export * from './useLargeScreen' diff --git a/src/hooks/useLargeScreen.ts b/src/hooks/useLargeScreen.ts new file mode 100644 index 00000000..4182c9f5 --- /dev/null +++ b/src/hooks/useLargeScreen.ts @@ -0,0 +1,22 @@ +import { createSignal, onCleanup, onMount } from 'solid-js' +import { throttle } from '@solid-primitives/scheduled' + +export const useLargeScreen = (handler: (e: UIEvent) => void) => { + const [isLargeScreen, setIsLargeScreen] = createSignal(false) + + const handleResize = throttle((e: UIEvent) => { + setIsLargeScreen(window.innerWidth > 1024) + if (window.innerWidth > 1024) + return handler(e) + }, 200) + + onMount(() => { + window.addEventListener('resize', handleResize) + }) + + onCleanup(() => { + window.removeEventListener('resize', handleResize) + }) + + return isLargeScreen +} From d83c17ccf43bd026ead5de0486cc69fc7048aac5 Mon Sep 17 00:00:00 2001 From: ddiu8081 Date: Thu, 11 May 2023 01:20:32 +0800 Subject: [PATCH 02/15] fix: `window` not defined in ssr --- src/components/header/Header.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 400af9a7..cce7c91e 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -1,13 +1,16 @@ +import { onMount } from 'solid-js' import { scrollController, showConversationSidebar, showSettingsSidebar } from '@/stores/ui' import { useLargeScreen } from '@/hooks' import ConversationHeaderInfo from './ConversationHeaderInfo' import ConversationMessageClearButton from './ConversationMessageClearButton' export default () => { - useLargeScreen(() => { - // bug: when click the setting btn, toggle moible or PC mode, the sidebar will not close - showConversationSidebar.set(false) - showSettingsSidebar.set(false) + onMount(() => { + useLargeScreen(() => { + // bug: when click the setting btn, toggle moible or PC mode, the sidebar will not close + showConversationSidebar.set(false) + showSettingsSidebar.set(false) + }) }) return (
From 115094d219869984a16fb88b1c95de0f48821262 Mon Sep 17 00:00:00 2001 From: Muspi Merol <74518716+CNSeniorious000@users.noreply.github.com> Date: Thu, 11 May 2023 01:39:37 +0800 Subject: [PATCH 03/15] fix(ui): manually implement close-on-outside-click feature for modals 1. use `pointer-events-auto` to allow backdrop layer to receive event 2. bind a onclick to the backdrop layer --- src/components/ui/Modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index dc2d8c9d..62002c52 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -41,7 +41,7 @@ export default (props: Props) => {
-
+
api().close()} />
From 6965a65b943dd6d0b6b485e6b074d188ebef7d0b Mon Sep 17 00:00:00 2001 From: Muspi Merol <74518716+CNSeniorious000@users.noreply.github.com> Date: Thu, 11 May 2023 09:42:49 +0800 Subject: [PATCH 04/15] fix: remove quotation marks from the generated titles (#22) --- src/logics/conversation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logics/conversation.ts b/src/logics/conversation.ts index b4add50a..0217ff73 100644 --- a/src/logics/conversation.ts +++ b/src/logics/conversation.ts @@ -90,7 +90,7 @@ export const handlePrompt = async(conversation: Conversation, prompt?: string, s const rapidPayload = generateRapidProviderPayload(promptHelper.summarizeText(inputText), provider.id) const generatedTitle = await getProviderResponse(provider.id, rapidPayload).catch(() => {}) as string || inputText updateConversationById(conversation.id, { - name: generatedTitle, + name: generatedTitle.replace(/^['"\s]+|['"\s]+$/g, ''), }) } } From 65e6f9cff769f7957b4287bf6f67cb26dfa9eea2 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 11 May 2023 09:48:04 +0800 Subject: [PATCH 05/15] feat: add simple-git-hooks lint code --- package.json | 13 ++- pnpm-lock.yaml | 239 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 222 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 20524408..09cff18a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "astro": "astro", "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.astro", "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx,.astro --fix", - "release": "bumpp" + "release": "bumpp", + "postinstall": "npx simple-git-hooks" }, "dependencies": { "@astrojs/netlify": "2.0.0", @@ -66,8 +67,16 @@ "@unocss/preset-icons": "^0.50.6", "@unocss/preset-typography": "^0.50.6", "eslint-plugin-astro": "^0.24.0", + "lint-staged": "^13.2.2", "punycode": "^2.3.0", + "simple-git-hooks": "^2.8.1", "unocss": "^0.50.6", "vite-plugin-pwa": "^0.14.7" - } + }, + "simple-git-hooks": { + "pre-commit": "pnpm lint-staged" + }, + "lint-staged": { + "*": "pnpm lint:fix" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7661ffa..9563f3e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,9 +150,15 @@ devDependencies: eslint-plugin-astro: specifier: ^0.24.0 version: 0.24.0(eslint@8.37.0) + lint-staged: + specifier: ^13.2.2 + version: 13.2.2 punycode: specifier: ^2.3.0 version: 2.3.0 + simple-git-hooks: + specifier: ^2.8.1 + version: 2.8.1 unocss: specifier: ^0.50.6 version: 0.50.6(postcss@8.4.21)(rollup@2.79.1)(vite@4.2.1) @@ -1751,7 +1757,7 @@ packages: eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.37.0) eslint-plugin-eslint-comments: 3.2.0(eslint@8.37.0) eslint-plugin-html: 6.2.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0) eslint-plugin-jsonc: 2.7.0(eslint@8.37.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.37.0) eslint-plugin-markdown: 2.2.1(eslint@8.37.0) @@ -2914,6 +2920,14 @@ packages: - supports-color dev: false + /aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -2948,7 +2962,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 - dev: false /ansi-regex@2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} @@ -2972,7 +2985,6 @@ packages: /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: false /ansi-styles@2.2.1: resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} @@ -2994,7 +3006,6 @@ packages: /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: false /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -3091,6 +3102,11 @@ packages: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} dev: true + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + /astro-eslint-parser@0.12.0: resolution: {integrity: sha512-1r8TXPHoUk6OSNzeGOGpklmPedleYVpZU50jIPfsQjzrGMs+XpCSCc8+AATnKraPYnTvkmNClhjafSY6w3H90g==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3547,7 +3563,6 @@ packages: /chalk@5.2.0: resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false /character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -3612,6 +3627,11 @@ packages: escape-string-regexp: 1.0.5 dev: true + /clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + /cli-boxes@1.0.0: resolution: {integrity: sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==} engines: {node: '>=0.10.0'} @@ -3632,7 +3652,6 @@ packages: engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 - dev: false /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} @@ -3646,6 +3665,22 @@ packages: engines: {node: '>=6'} dev: false + /cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + + /cli-truncate@3.1.0: + resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + slice-ansi: 5.0.0 + string-width: 5.1.2 + dev: true + /cli-width@3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} @@ -3703,6 +3738,11 @@ packages: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} dev: false + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -4088,7 +4128,6 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: false /editorconfig@0.15.3: resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} @@ -4128,7 +4167,6 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: false /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -4515,7 +4553,7 @@ packages: eslint-plugin-promise: ^6.0.0 dependencies: eslint: 8.37.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0) eslint-plugin-n: 15.7.0(eslint@8.37.0) eslint-plugin-promise: 6.1.1(eslint@8.37.0) dev: true @@ -4539,7 +4577,7 @@ packages: dependencies: debug: 4.3.4 eslint: 8.37.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0) glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.2 @@ -4548,7 +4586,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0): + /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.37.0): resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -4573,7 +4611,6 @@ packages: debug: 3.2.7 eslint: 8.37.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.37.0) transitivePeerDependencies: - supports-color dev: true @@ -4623,7 +4660,7 @@ packages: htmlparser2: 7.2.0 dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.57.1)(eslint@8.37.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -4641,7 +4678,7 @@ packages: doctrine: 2.1.0 eslint: 8.37.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.37.0) + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.7)(eslint@8.37.0) has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -5023,6 +5060,21 @@ packages: strip-final-newline: 3.0.0 dev: false + /execa@7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -5712,6 +5764,11 @@ packages: engines: {node: '>=12.20.0'} dev: false + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + /hyphenate-style-name@1.0.4: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} dev: false @@ -5938,7 +5995,11 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: false + + /is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -6074,7 +6135,6 @@ packages: /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: false /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} @@ -6379,15 +6439,62 @@ packages: yargs: 14.2.3 dev: false + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /lint-staged@13.2.2: + resolution: {integrity: sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + dependencies: + chalk: 5.2.0 + cli-truncate: 3.1.0 + commander: 10.0.1 + debug: 4.3.4 + execa: 7.1.1 + lilconfig: 2.1.0 + listr2: 5.0.8 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-inspect: 1.12.3 + pidtree: 0.6.0 + string-argv: 0.3.1 + yaml: 2.2.2 + transitivePeerDependencies: + - enquirer + - supports-color + dev: true + /listify@1.0.3: resolution: {integrity: sha512-083swF7iH7bx8666zdzBColpgEuy46HjN3r1isD4zV6Ix7FuHfb/2/WVnl4CH8hjuoWeFF7P5KkKNXUnJCFEJg==} engines: {node: '>= 0.4'} dev: false + /listr2@5.0.8: + resolution: {integrity: sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + enquirer: '>= 2.3.0 < 3' + peerDependenciesMeta: + enquirer: + optional: true + dependencies: + cli-truncate: 2.1.0 + colorette: 2.0.19 + log-update: 4.0.0 + p-map: 4.0.0 + rfdc: 1.3.0 + rxjs: 7.8.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + /load-yaml-file@0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} @@ -6445,6 +6552,16 @@ packages: is-unicode-supported: 1.3.0 dev: false + /log-update@4.0.0: + resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} + engines: {node: '>=10'} + dependencies: + ansi-escapes: 4.3.2 + cli-cursor: 3.1.0 + slice-ansi: 4.0.0 + wrap-ansi: 6.2.0 + dev: true + /longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} dev: false @@ -6993,7 +7110,6 @@ packages: /mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} - dev: false /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} @@ -7229,7 +7345,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: path-key: 4.0.0 - dev: false /npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} @@ -7346,7 +7461,6 @@ packages: engines: {node: '>=12'} dependencies: mimic-fn: 4.0.0 - dev: false /open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} @@ -7440,6 +7554,13 @@ packages: dependencies: p-limit: 3.1.0 + /p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -7552,7 +7673,6 @@ packages: /path-key@4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} - dev: false /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -7579,6 +7699,12 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + /pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -8084,7 +8210,6 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: false /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} @@ -8133,6 +8258,10 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + /rfdc@1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + /rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} hasBin: true @@ -8191,6 +8320,12 @@ packages: tslib: 1.14.1 dev: false + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.5.0 + dev: true + /s.color@0.0.15: resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} dev: false @@ -8362,6 +8497,12 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /simple-git-hooks@2.8.1: + resolution: {integrity: sha512-DYpcVR1AGtSfFUNzlBdHrQGPsOhuuEJ/FkmPOOlFysP60AHd3nsEpkGq/QEOdtUyT1Qhk7w9oLmFoMG+75BDog==} + hasBin: true + requiresBuild: true + dev: true + /sirv@2.0.2: resolution: {integrity: sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==} engines: {node: '>= 10'} @@ -8385,6 +8526,32 @@ packages: engines: {node: '>=12'} dev: false + /slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + dev: true + /smpltmpl@1.0.2: resolution: {integrity: sha512-Hq23NNgeZigOzIiX1dkb6W3gFn2/XQj43KhPxu65IMieG/gIwf/lQb1IudjYv0c/5LwJeS/mPayYzyo+8WJMxQ==} engines: {node: '>=4'} @@ -8516,7 +8683,6 @@ packages: /string-argv@0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} engines: {node: '>=0.6.19'} - dev: false /string-width@2.1.1: resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} @@ -8542,7 +8708,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: false /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -8551,7 +8716,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.0.1 - dev: false /string.prototype.matchall@4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} @@ -8645,7 +8809,6 @@ packages: engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: false /strip-bom-string@1.0.0: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} @@ -8679,7 +8842,6 @@ packages: /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} - dev: false /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} @@ -8796,7 +8958,6 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: false /timed-out@4.0.1: resolution: {integrity: sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==} @@ -8922,7 +9083,6 @@ packages: /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - dev: false /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} @@ -9758,6 +9918,24 @@ packages: strip-ansi: 5.2.0 dev: false + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} @@ -9835,6 +10013,11 @@ packages: engines: {node: '>= 14'} dev: false + /yaml@2.2.2: + resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} + engines: {node: '>= 14'} + dev: true + /yargs-parser@15.0.3: resolution: {integrity: sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==} dependencies: From 5a85c311ee8872446278645e946292b50516095c Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 11 May 2023 09:52:35 +0800 Subject: [PATCH 06/15] chore: update pwa info --- astro.config.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 6aa43890..10584f64 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -37,9 +37,9 @@ export default defineConfig({ process.env.OUTPUT !== 'netlify' && VitePWA({ registerType: 'autoUpdate', manifest: { - name: 'ChatGPT-API Demo', - short_name: 'ChatGPT Demo', - description: 'A demo repo based on OpenAI API', + name: 'Anse', + short_name: 'Anse', + description: 'Anse is a fully optimized UI for AI Chats.', theme_color: '#212129', background_color: '#ffffff', icons: [ From 96cc057affb6ea0b267bc4b77f1503a563a6ace0 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 11 May 2023 10:21:20 +0800 Subject: [PATCH 07/15] docs: add README.zh-CN --- README.md | 2 ++ README.zh-CN.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 README.zh-CN.md diff --git a/README.md b/README.md index 2fa52974..144874e8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ # Anse +English | [简体中文](./README.zh-CN.md) + Anse is a fully optimized UI for AI Chats. - 🍿 **Live preview**: https://anse.app diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..f920a97e --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,78 @@ +![Banner](https://user-images.githubusercontent.com/1998168/235366625-e615e68d-592c-4f18-9c9f-1e5cd1778557.png) + +# Anse + +[English](./README.md) | 简体中文 + +Anse 是一个极致优化的 AI 聊天 UI. + +- 🍿 **在线预览**: https://anse.app +- 📖 **文档地址**: https://docs.anse.app +- ✨ **版本日志**: https://github.com/anse-app/anse/releases + +## 功能 + +- **🚀 强大的插件系统** - 归功于 `Provider plugin` ,轻松扩展类似于 [OpenAI](https://openai.com/), [Replicate](https://replicate.com/) 等 AI 平台, 并且支持自定义模型参数. +- **💬 会话记录保存** - 使用 `IndexDB` 保存本地数据,不会上传到服务器,保证安全问题。. +- **🎉 多种对话模式** - 提供不同的对话模式:`单词对话`, `连续对话`, `OpenAI 图像生成`、`Stable Diffusion` 和更多. +- **💎 优化用户界面体验** - 我们对上一个版本重构了网站用户界面,优化了很多细节,还适应了移动端和黑暗模式. +- **🌈 一键部署** -支持一键部署,不再需要环境变量,可以参考我们的留档将网站部署到 [Vercel](https://vercel.com/), [Netlify](https://www.netlify.com/), `Docker`, `Node` 和更多平台. + +## 本地运行 + +### 前置环境 +1. **Node**: 检查您的开发环境和部署环境是否都使用 `Node v18` 或更高版本。你可以使用 [nvm](https://github.com/nvm-sh/nvm) 管理本地多个 `node` 版本 + ```bash + node -v + ``` +2. **PNPM**: 我们推荐使用 [pnpm](https://pnpm.io/) 来管理依赖,如果你从来没有安装过 pnpm,可以使用下面的命令安装: + ```bash + npm i -g pnpm + ``` +3. **OPENAI_API_KEY**: 在运行此应用程序之前,您需要从 OpenAI 获取 API 密钥。您可以在 [https://beta.openai.com/signup](https://beta.openai.com/signup) 注册 API 密钥 + +### 起步运行 + +1. 安装依赖 + ```bash + pnpm install + ``` +2. 运行应用,本地项目运行在 `http://localhost:3000/` + ```bash + pnpm run dev + ``` +3. 在设置面板添加你的 [OpenAI API key](https://platform.openai.com/account/api-keys), 然后尽情享受吧! + +## 部署 + +获取更多信息,请参考部署文档: https://docs.anse.app/self-deploy + +## 常见问题 + +Q: TypeError: fetch failed (can't connect to OpenAI Api) + +A: 参考: https://github.com/anse-app/chatgpt-demo/issues/34 + +Q: throw new TypeError(`${context}` is not a ReadableStream.) + +A: Node 版本需要在 `v18` 或者更高,参考: https://github.com/anse-app/chatgpt-demo/issues/65 + +Q: 无需代理部署教程即可加速国内访问?? + +A: 你可以参考此教程: https://github.com/anse-app/chatgpt-demo/discussions/270 + +Q: `PWA` 不工作? + +A: 当前的 PWA 不支持 Netlify 部署,您可以选择 vercel 或 node 部署。 + +## 参与贡献 + +这个项目的存在要感谢所有做出贡献的人。 + +感谢我们所有的支持者!🙏 + +[![img](https://contributors.nn.ci/api?repo=anse-app/anse)](https://github.com/anse-app/anse/graphs/contributors) + +## License + +MIT © [ddiu8081](https://github.com/anse-app/anse/blob/main/LICENSE) From 463a280571ff7702bfe5ad51bab62109f5313df2 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 11 May 2023 11:47:39 +0800 Subject: [PATCH 08/15] fix(ui): use sticky layout at mobile platform, fix chrome scroll bug --- src/components/Main.astro | 2 +- src/components/Send.tsx | 8 +++++++- src/hooks/index.ts | 1 + src/hooks/useMobileScreen.ts | 22 ++++++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/hooks/useMobileScreen.ts diff --git a/src/components/Main.astro b/src/components/Main.astro index d027d0eb..a51a8bfc 100644 --- a/src/components/Main.astro +++ b/src/components/Main.astro @@ -7,7 +7,7 @@ import '@/assets/transition.css' import Conversation from './main/Conversation' --- -
+
diff --git a/src/components/Send.tsx b/src/components/Send.tsx index b84bc7b8..e2eb5087 100644 --- a/src/components/Send.tsx +++ b/src/components/Send.tsx @@ -6,6 +6,7 @@ import { addConversation, conversationMap, currentConversationId } from '@/store import { loadingStateMap, streamsMap } from '@/stores/streams' import { handlePrompt } from '@/logics/conversation' import { globalAbortController } from '@/stores/settings' +import { useMobileScreen } from '@/hooks' export default () => { let inputRef: HTMLTextAreaElement @@ -18,6 +19,7 @@ export default () => { const $globalAbortController = useStore(globalAbortController) const [inputPrompt, setInputPrompt] = createSignal('') + const [footerClass, setFooterClass] = createSignal('') const isEditing = () => inputPrompt() || $isSendBoxFocus() const currentConversation = () => { return $conversationMap()[$currentConversationId()] @@ -29,6 +31,10 @@ export default () => { createShortcut(['Control', 'Enter'], () => { $isSendBoxFocus() && handleSend() }) + + useMobileScreen(() => { + setFooterClass('sticky bottom-0 left-0 right-0 overflow-hidden') + }) }) const stateType = () => { @@ -155,7 +161,7 @@ export default () => { } return ( -
+
}> diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 71877fc3..2a9d2e33 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,3 +2,4 @@ export * from './useDark' export * from './useCopy' export * from './useClickOutside' export * from './useLargeScreen' +export * from './useMobileScreen' diff --git a/src/hooks/useMobileScreen.ts b/src/hooks/useMobileScreen.ts new file mode 100644 index 00000000..6201c9a7 --- /dev/null +++ b/src/hooks/useMobileScreen.ts @@ -0,0 +1,22 @@ +import { createSignal, onCleanup, onMount } from 'solid-js' +import { throttle } from '@solid-primitives/scheduled' + +export const useMobileScreen = (handler: (e: UIEvent) => void) => { + const [isMobileScreen, setIsMobileScreen] = createSignal(false) + + const handleResize = throttle((e: UIEvent) => { + setIsMobileScreen(window.innerWidth < 640) + if (window.innerWidth < 640) + return handler(e) + }, 200) + + onMount(() => { + window.addEventListener('resize', handleResize) + }) + + onCleanup(() => { + window.removeEventListener('resize', handleResize) + }) + + return isMobileScreen +} From bb59cdc8932644593f9443a4cf4dc4387afdd31a Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Mon, 15 May 2023 01:36:11 +0800 Subject: [PATCH 09/15] feat: add useDisableTransition to transition between themes smoothly --- src/components/ui/ThemeToggle.tsx | 5 ++++- src/hooks/index.ts | 1 + src/hooks/useDisableTransition.ts | 32 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/hooks/useDisableTransition.ts diff --git a/src/components/ui/ThemeToggle.tsx b/src/components/ui/ThemeToggle.tsx index 1958a449..d24021a5 100644 --- a/src/components/ui/ThemeToggle.tsx +++ b/src/components/ui/ThemeToggle.tsx @@ -1,17 +1,20 @@ import { Show, onMount } from 'solid-js' -import { useDark } from '@/hooks' +import { useDark, useDisableTransition } from '@/hooks' export default () => { const [isDark, setIsDark] = useDark() + const { disableTransition, removeDisableTransition } = useDisableTransition() onMount(() => { document.querySelector('meta[name="theme-color"]')?.setAttribute('content', isDark() ? '#222222' : '#fafafa') }) const handleDarkChanged = () => { + disableTransition() const dark = !isDark() document.querySelector('meta[name="theme-color"]')?.setAttribute('content', dark ? '#222222' : '#fafafa') setIsDark(dark) + removeDisableTransition() } return ( diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 2a9d2e33..62f330b3 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -3,3 +3,4 @@ export * from './useCopy' export * from './useClickOutside' export * from './useLargeScreen' export * from './useMobileScreen' +export * from './useDisableTransition' diff --git a/src/hooks/useDisableTransition.ts b/src/hooks/useDisableTransition.ts new file mode 100644 index 00000000..5b6330d0 --- /dev/null +++ b/src/hooks/useDisableTransition.ts @@ -0,0 +1,32 @@ +export const useDisableTransition = () => { + // https://paco.me/writing/disable-theme-transitions + const css = document.createElement('style') + const disableTransition = () => { + css.type = 'text/css' + css.appendChild( + document.createTextNode( + `* { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + -ms-transition: none !important; + transition: none !important; + }`, + ), + ) + document.head.appendChild(css) + } + + // Calling getComputedStyle forces the browser to redraw + const removeDisableTransition = () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _ = window.getComputedStyle(css).opacity + document.head.removeChild(css) + } + + return { + css, + disableTransition, + removeDisableTransition, + } +} From 3ed783a37855dbd6ffa57f3f33f457141d29e706 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Tue, 16 May 2023 10:51:14 +0800 Subject: [PATCH 10/15] feat: add i18n --- .eslintrc.js | 1 + .vscode/settings.json | 4 ++ package.json | 12 +++--- .../settings/AppGeneralSettings.tsx | 23 ++++++++--- src/hooks/index.ts | 2 + src/hooks/useDepGet.ts | 23 +++++++++++ src/hooks/useI18n.ts | 41 +++++++++++++++++++ src/locale/index.ts | 18 ++++++++ src/locale/lang/en.ts | 24 +++++++++++ src/locale/lang/index.ts | 2 + src/locale/lang/zh-cn.ts | 24 +++++++++++ src/pages/index.astro | 22 +++++----- src/types/app.ts | 1 + 13 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 src/hooks/useDepGet.ts create mode 100644 src/hooks/useI18n.ts create mode 100644 src/locale/index.ts create mode 100644 src/locale/lang/en.ts create mode 100644 src/locale/lang/index.ts create mode 100644 src/locale/lang/zh-cn.ts diff --git a/.eslintrc.js b/.eslintrc.js index a109de9d..cb63f0b4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,7 @@ module.exports = { '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-unused-vars': 'warn', 'react/jsx-key': 'off', + 'import/namespace': 'off', }, overrides: [ { diff --git a/.vscode/settings.json b/.vscode/settings.json index 62268485..22ed1ac2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,9 @@ "astro", // Enable .astro "typescript", // Enable .ts "typescriptreact" // Enable .tsx + ], + "i18n-ally.localesPaths": [ + "src/locale", + "src/locale/lang" ] } diff --git a/package.json b/package.json index 09cff18a..a1f88b2b 100644 --- a/package.json +++ b/package.json @@ -73,10 +73,10 @@ "unocss": "^0.50.6", "vite-plugin-pwa": "^0.14.7" }, - "simple-git-hooks": { - "pre-commit": "pnpm lint-staged" - }, - "lint-staged": { - "*": "pnpm lint:fix" - } + "simple-git-hooks": { + "pre-commit": "pnpm lint-staged" + }, + "lint-staged": { + "*": "pnpm lint:fix" + } } diff --git a/src/components/settings/AppGeneralSettings.tsx b/src/components/settings/AppGeneralSettings.tsx index ab7961b0..31e9ad14 100644 --- a/src/components/settings/AppGeneralSettings.tsx +++ b/src/components/settings/AppGeneralSettings.tsx @@ -1,28 +1,41 @@ import { For } from 'solid-js' +import { localesOptions } from '@/locale' +import { useI18n } from '@/hooks' import SettingsUIComponent from './SettingsUIComponent' import type { Accessor } from 'solid-js' import type { GeneralSettings } from '@/types/app' +import type { SettingsUI } from '@/types/provider' interface Props { settingsValue: Accessor updateSettings: (v: Partial) => void } -const settingsUIList = [ +const settingsUIList: SettingsUI[] = [ { key: 'requestWithBackend', name: 'Request With Backend', type: 'toggle', + default: false, }, -] as const + { + key: 'locale', + name: 'Change system language', + type: 'select', + default: 'en', + options: localesOptions, + }, +] export default (props: Props) => { + const { t } = useI18n() + return (

-
General
+
{t('settings.general.title')}

@@ -32,9 +45,9 @@ export default (props: Props) => { true} - value={() => props.settingsValue()[item.key] || false} + value={() => props.settingsValue()[item.key as keyof GeneralSettings] || false} setValue={(v) => { - props.updateSettings({ [item.key]: v as boolean }) + props.updateSettings({ [item.key]: v }) }} /> ) diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 2a9d2e33..1ac8822f 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -3,3 +3,5 @@ export * from './useCopy' export * from './useClickOutside' export * from './useLargeScreen' export * from './useMobileScreen' +export * from './useDepGet' +export * from './useI18n' diff --git a/src/hooks/useDepGet.ts b/src/hooks/useDepGet.ts new file mode 100644 index 00000000..8d1baf85 --- /dev/null +++ b/src/hooks/useDepGet.ts @@ -0,0 +1,23 @@ +export function useDeepGet(target: any, path: string | string[], defaultValue: any) { + if (!Array.isArray(path) && typeof path !== 'string') + throw new TypeError('path must be string or array') + if (target === null) + return defaultValue + + let pathArray = path + if (typeof path === 'string') { + path = path.replace(/\[(\w*)\]/g, '.$1') + path = path.startsWith('.') ? path.slice(1) : path + + pathArray = path.split('.') + } + + let index = 0 + let levelPath: string + while (target !== null && index < pathArray.length) { + levelPath = pathArray[index++] + target = target[levelPath] + } + + return index === pathArray.length ? target : defaultValue +} diff --git a/src/hooks/useI18n.ts b/src/hooks/useI18n.ts new file mode 100644 index 00000000..b3b9d551 --- /dev/null +++ b/src/hooks/useI18n.ts @@ -0,0 +1,41 @@ +import { createSignal } from 'solid-js' +import { en } from '@/locale/lang' +import { locales } from '@/locale' +import { providerSettingsMap } from '@/stores/settings' +import { useDeepGet } from './useDepGet' +import type { Accessor } from 'solid-js' +import type { TranslatePair } from '@/locale' +import type { GeneralSettings } from '@/types/app' + +const [currentLocale, setCurrentLocale] = createSignal(en.locales) + +export type TranslatorOption = Record +export type Translator = (path: string, option?: TranslatorOption) => string +export interface I18nContext { + locale: TranslatePair + t: Translator +} + +export const translate = (path: string, option: TranslatorOption | undefined) => { + return currentLocale() ? (useDeepGet(currentLocale(), path, path) as string).replace(/\{(\w+)\}/g, (_, key) => `${option?.[key] ?? `{${key}}`}`) : '' +} + +export const buildTranslator = (): Translator => (path, option) => translate(path, option) +export const buildI18nContext = (locale: Accessor): I18nContext => { + return { + locale: locale(), + t: buildTranslator(), + } +} + +export function useI18n() { + let defaultLocale = providerSettingsMap.get()?.general?.locale ?? 'en' + providerSettingsMap.listen((value, changedKey) => { + const general = value[changedKey] as unknown as GeneralSettings + defaultLocale = general?.locale + setCurrentLocale(locales[defaultLocale as string]) + }) + + setCurrentLocale(locales[defaultLocale as string]) + return buildI18nContext(currentLocale) +} diff --git a/src/locale/index.ts b/src/locale/index.ts new file mode 100644 index 00000000..1fe582c7 --- /dev/null +++ b/src/locale/index.ts @@ -0,0 +1,18 @@ +import * as Langs from './lang' +import type { SelectOptionType } from '@/types/provider' + +export type LanguageType = keyof typeof Langs + +export interface TranslatePair { + [key: string]: string | string[] | TranslatePair +} + +export interface language { + name: string + desc: string + locales: TranslatePair +} + +export const locales = Object.fromEntries(Object.entries(Langs).map(([key, value]) => [key, value.locales])) + +export const localesOptions: SelectOptionType[] = Object.entries(Langs).map(([key, value]) => ({ label: value.desc, value: key })) diff --git a/src/locale/lang/en.ts b/src/locale/lang/en.ts new file mode 100644 index 00000000..0e8e30ff --- /dev/null +++ b/src/locale/lang/en.ts @@ -0,0 +1,24 @@ +import type { language } from '..' + +export const en = { + name: 'en', + desc: 'English', + locales: { + settings: { + btn: 'TEST', + general: { + title: 'General', + requestWithBackend: 'Request With Backend', + locale: 'Change system language', + }, + openai: { + title: 'OpenAI', + key: '', + }, + replicate: {}, + }, + conversations: { + title: 'CONVERSATIONS', + }, + }, +} as language diff --git a/src/locale/lang/index.ts b/src/locale/lang/index.ts new file mode 100644 index 00000000..0ab03a6a --- /dev/null +++ b/src/locale/lang/index.ts @@ -0,0 +1,2 @@ +export * from './en' +export * from './zh-cn' diff --git a/src/locale/lang/zh-cn.ts b/src/locale/lang/zh-cn.ts new file mode 100644 index 00000000..1ebb2d5c --- /dev/null +++ b/src/locale/lang/zh-cn.ts @@ -0,0 +1,24 @@ +import type { language } from '..' + +export const zhCN = { + name: 'zhCN', + desc: '简体中文', + locales: { + settings: { + btn: 'TEST', + general: { + title: '通用', + requestWithBackend: '请求代理后端', + locale: '切换语言', + }, + openai: { + title: 'OpenAI', + key: '', + }, + replicate: {}, + }, + conversations: { + title: 'CONVERSATIONS', + }, + }, +} as language diff --git a/src/pages/index.astro b/src/pages/index.astro index 226ff659..231783c6 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -9,15 +9,15 @@ import BuildStores from '@/components/client-only/BuildStores' --- -
- -
- -
- - +
+ +
+ +
+ +
diff --git a/src/types/app.ts b/src/types/app.ts index 29561406..edb7cb44 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -4,6 +4,7 @@ import type { SettingsUI } from './provider' export interface GeneralSettings { /** Default request directly, can choose to request via proxy */ requestWithBackend: boolean + locale: string } export interface BotMeta { From 473aedc95ee22390f53997b2d9968199cc304fea Mon Sep 17 00:00:00 2001 From: ddiu8081 Date: Wed, 17 May 2023 01:37:50 +0800 Subject: [PATCH 11/15] fix: set default locale when no local changes --- src/components/settings/AppGeneralSettings.tsx | 2 +- src/components/settings/ProviderGlobalSettings.tsx | 2 +- src/hooks/useI18n.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/settings/AppGeneralSettings.tsx b/src/components/settings/AppGeneralSettings.tsx index 31e9ad14..875521c3 100644 --- a/src/components/settings/AppGeneralSettings.tsx +++ b/src/components/settings/AppGeneralSettings.tsx @@ -45,7 +45,7 @@ export default (props: Props) => { true} - value={() => props.settingsValue()[item.key as keyof GeneralSettings] || false} + value={() => props.settingsValue()[item.key as keyof GeneralSettings] || item.default || ''} setValue={(v) => { props.updateSettings({ [item.key]: v }) }} diff --git a/src/components/settings/ProviderGlobalSettings.tsx b/src/components/settings/ProviderGlobalSettings.tsx index 98335ad7..aa4869e7 100644 --- a/src/components/settings/ProviderGlobalSettings.tsx +++ b/src/components/settings/ProviderGlobalSettings.tsx @@ -69,7 +69,7 @@ export default ({ config, settingsValue, setSettings }: Props) => { formData()[item.key] || ''} + value={() => formData()[item.key]} setValue={(v) => { setEditFormData({ ...formData(), [item.key]: v }) }} diff --git a/src/hooks/useI18n.ts b/src/hooks/useI18n.ts index b3b9d551..5d1e435c 100644 --- a/src/hooks/useI18n.ts +++ b/src/hooks/useI18n.ts @@ -33,7 +33,7 @@ export function useI18n() { providerSettingsMap.listen((value, changedKey) => { const general = value[changedKey] as unknown as GeneralSettings defaultLocale = general?.locale - setCurrentLocale(locales[defaultLocale as string]) + defaultLocale && setCurrentLocale(locales[defaultLocale as string]) }) setCurrentLocale(locales[defaultLocale as string]) From 9d4dc329475710ccbf7f7ee8aa08add56247c34a Mon Sep 17 00:00:00 2001 From: ddiu8081 Date: Wed, 17 May 2023 04:01:13 +0800 Subject: [PATCH 12/15] chore(i18n): add more locale texts --- .../conversations/ConversationEditModal.tsx | 4 ++- .../conversations/ConversationSidebar.tsx | 4 ++- .../conversations/ConversationSidebarAdd.tsx | 4 ++- src/components/main/Welcome.tsx | 8 +++-- .../settings/AppGeneralSettings.tsx | 34 +++++++++---------- src/components/settings/SettingsSidebar.tsx | 4 ++- src/locale/lang/en.ts | 8 +++-- src/locale/lang/zh-cn.ts | 8 +++-- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/components/conversations/ConversationEditModal.tsx b/src/components/conversations/ConversationEditModal.tsx index 68dd5865..009b0de4 100644 --- a/src/components/conversations/ConversationEditModal.tsx +++ b/src/components/conversations/ConversationEditModal.tsx @@ -1,10 +1,12 @@ import { useStore } from '@nanostores/solid' +import { useI18n } from '@/hooks' import { currentConversation, updateConversationById } from '@/stores/conversation' import { showConversationEditModal } from '@/stores/ui' import ConversationEdit from './ConversationEdit' import type { Conversation } from '@/types/conversation' export default () => { + const { t } = useI18n() const $currentConversation = useStore(currentConversation) let modifiedConversationPayload: Partial = {} @@ -29,7 +31,7 @@ export default () => { handleChange={handleChange} />
-
Save
+
{t('settings.save')}
) } diff --git a/src/components/conversations/ConversationSidebar.tsx b/src/components/conversations/ConversationSidebar.tsx index b7e159d3..af04fe60 100644 --- a/src/components/conversations/ConversationSidebar.tsx +++ b/src/components/conversations/ConversationSidebar.tsx @@ -1,16 +1,18 @@ import { For } from 'solid-js' import { useStore } from '@nanostores/solid' +import { useI18n } from '@/hooks' import { conversationMapSortList } from '@/stores/conversation' import ConversationSidebarItem from './ConversationSidebarItem' import ConversationSidebarAdd from './ConversationSidebarAdd' export default () => { + const { t } = useI18n() const $conversationMapSortList = useStore(conversationMapSortList) return (
- Conversations + {t('conversations.title')}
diff --git a/src/components/conversations/ConversationSidebarAdd.tsx b/src/components/conversations/ConversationSidebarAdd.tsx index 97067670..4d4d4a35 100644 --- a/src/components/conversations/ConversationSidebarAdd.tsx +++ b/src/components/conversations/ConversationSidebarAdd.tsx @@ -1,6 +1,8 @@ +import { useI18n } from '@/hooks' import { addConversation } from '@/stores/conversation' export default () => { + const { t } = useI18n() const handleAdd = () => { addConversation() } @@ -13,7 +15,7 @@ export default () => {
-
New Conversation
+
{t('conversations.add')}
) } diff --git a/src/components/main/Welcome.tsx b/src/components/main/Welcome.tsx index 6196b04e..57da60de 100644 --- a/src/components/main/Welcome.tsx +++ b/src/components/main/Welcome.tsx @@ -1,15 +1,17 @@ import { For, Show } from 'solid-js' import { useStore } from '@nanostores/solid' +import { useI18n } from '@/hooks' import { addConversation, conversationMapSortList, currentConversationId } from '@/stores/conversation' export default () => { + const { t } = useI18n() const $conversationMapSortList = useStore(conversationMapSortList) return (
-

Recents

+

{t('conversations.recent')}

{instance => ( @@ -20,7 +22,7 @@ export default () => { )} -
No Recents
+
{t('conversations.noRecent')}
@@ -29,7 +31,7 @@ export default () => { onClick={() => addConversation()} >
-
New Conversation
+
{t('conversations.add')}
diff --git a/src/components/settings/AppGeneralSettings.tsx b/src/components/settings/AppGeneralSettings.tsx index 875521c3..08121d92 100644 --- a/src/components/settings/AppGeneralSettings.tsx +++ b/src/components/settings/AppGeneralSettings.tsx @@ -11,25 +11,25 @@ interface Props { updateSettings: (v: Partial) => void } -const settingsUIList: SettingsUI[] = [ - { - key: 'requestWithBackend', - name: 'Request With Backend', - type: 'toggle', - default: false, - }, - { - key: 'locale', - name: 'Change system language', - type: 'select', - default: 'en', - options: localesOptions, - }, -] - export default (props: Props) => { const { t } = useI18n() + const settingsUIList = () => ([ + { + key: 'requestWithBackend', + name: t('settings.general.requestWithBackend'), + type: 'toggle', + default: false, + }, + { + key: 'locale', + name: t('settings.general.locale'), + type: 'select', + default: 'en', + options: localesOptions, + }, + ] as SettingsUI[]) + return (

@@ -39,7 +39,7 @@ export default (props: Props) => {

- + {(item) => { return ( { + const { t } = useI18n() const $providerSettingsMap = useStore(providerSettingsMap) const generalSettings = () => { return ($providerSettingsMap().general || {}) as unknown as GeneralSettings @@ -16,7 +18,7 @@ export default () => { return (
- Settings + {t('settings.title')}
Date: Wed, 17 May 2023 16:55:51 +0800 Subject: [PATCH 13/15] fix:use innerText to escape htmlString when showRawCode is on (#28) --- src/components/Markdown.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index d85abc24..9a20f0cb 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -1,3 +1,4 @@ +import { Show } from 'solid-js' import { unified } from 'unified' import remarkParse from 'remark-parse' import remarkGfm from 'remark-gfm' @@ -31,9 +32,8 @@ export default (props: Props) => { const htmlString = () => props.showRawCode ? props.text : parseMarkdown(props.text) return ( -
+ }> +
+ ) } From 9b03e174279a2e4a914bc9e2aeec9e8f837d3e01 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 18 May 2023 14:03:11 +0800 Subject: [PATCH 14/15] fix: temporarily add the click event to fix #29 --- src/components/ui/base/Select.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/ui/base/Select.tsx b/src/components/ui/base/Select.tsx index f5040f11..e7d26959 100644 --- a/src/components/ui/base/Select.tsx +++ b/src/components/ui/base/Select.tsx @@ -24,6 +24,7 @@ export const Select = (inputProps: Props) => { selectedOption: props.options.find(o => o.value === props.value), readOnly: props.readonly, onChange: (detail) => { + console.log('trigger') if (detail) { setSelectedItem(props.options.find(o => o.value === detail.value)) props.onChange(detail.value) @@ -35,10 +36,8 @@ export const Select = (inputProps: Props) => { createEffect(on(() => props.value, () => { const option = props.options.find(o => o.value === props.value) - if (option) { - api().setSelectedOption(option) + if (option) setSelectedItem(option) - } })) const selectedComponent = (item: T | null) => { @@ -81,8 +80,14 @@ export const Select = (inputProps: Props) => {
    {props.options.map(item => ( -
  • - {itemComponent(item, item.value === api().selectedOption?.value)} +
  • { + setSelectedItem(item) + props.onChange(item.value) + }} + > + {itemComponent(item, item.value === selectedItem()?.value)}
  • ))}
From 38f141f0e6340b05b52e4f98e58c52ff0b34a5b0 Mon Sep 17 00:00:00 2001 From: yzh990918 <251205668@qq.com> Date: Thu, 18 May 2023 14:08:17 +0800 Subject: [PATCH 15/15] chore: release v1.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1f88b2b..331b74e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anse", - "version": "1.1.2", + "version": "1.1.3", "packageManager": "pnpm@7.28.0", "scripts": { "dev": "astro dev",