├── .npmrc ├── public ├── pwa.png ├── avatar.png ├── favicon.ico ├── apple-touch-icon.png └── icon.svg ├── vercel.json ├── .dockerignore ├── renovate.json ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── src ├── utils │ ├── constants.ts │ ├── track.ts │ ├── client.ts │ ├── events.ts │ ├── streaming.ts │ ├── cf-workers-ai.ts │ ├── header.ts │ ├── deepseek.ts │ ├── auth.ts │ ├── record.ts │ ├── color.ts │ ├── tiktoken.ts │ ├── misc.ts │ └── ripple.ts ├── types.ts ├── pages │ ├── beta │ │ └── translate.astro │ ├── api │ │ ├── transcript.ts │ │ ├── translate.ts │ │ ├── moderate.ts │ │ └── title-gen.ts │ ├── tutorial │ │ └── index.astro │ ├── password.astro │ └── index.astro ├── components │ ├── ChatInterface.tsx │ ├── icons │ │ ├── Clear.tsx │ │ ├── X.tsx │ │ ├── Env.tsx │ │ └── Refresh.tsx │ ├── controls │ │ ├── Group.svelte │ │ ├── Hr.svelte │ │ ├── APIKeyInput.svelte │ │ ├── Section.svelte │ │ ├── Toggle.svelte │ │ ├── Slider.svelte │ │ ├── ModelItem.svelte │ │ └── ModelSelector.svelte │ ├── Inview.svelte │ ├── UseCopy.svelte │ ├── CheckStatus.svelte │ ├── Inview.tsx │ ├── Ad.svelte │ ├── StickToBottomButton.tsx │ ├── MessageList.tsx │ ├── ErrorMessageItem.tsx │ ├── ThemeColor.svelte │ ├── Tips.tsx │ ├── Suggestions.tsx │ ├── Settings.svelte │ ├── Footer.svelte │ ├── Popup.astro │ ├── BackTop.astro │ ├── SystemRoleSettings.tsx │ ├── TokenCounter.tsx │ ├── ParallaxBackground.tsx │ ├── Header.svelte │ ├── Modal.svelte │ ├── Themetoggle.svelte │ ├── Sponsorship.svelte │ ├── ChatInput.tsx │ ├── Translator.svelte │ ├── MessageItem.tsx │ ├── Generator.tsx │ └── Sponsor.svelte ├── env.d.ts ├── message.css ├── hooks │ └── createSmoothStreaming.ts ├── layouts │ └── Layout.astro └── context │ └── ChatContext.tsx ├── docker-compose.yml ├── Dockerfile ├── netlify.toml ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── update.yml ├── shims.d.ts ├── tsconfig.json ├── plugins └── disableBlocks.ts ├── .gitignore ├── prepare.cjs ├── .env.example ├── eslint.config.mjs ├── LICENSE ├── README.zh-CN.md ├── package.json ├── README.md ├── uno.config.ts └── astro.config.mjs /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | strict-peer-dependencies=true 3 | -------------------------------------------------------------------------------- /public/pwa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNSeniorious000/free-chat/HEAD/public/pwa.png -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildCommand": "node prepare.cjs & OUTPUT=vercel astro build" 3 | } 4 | -------------------------------------------------------------------------------- /public/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNSeniorious000/free-chat/HEAD/public/avatar.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNSeniorious000/free-chat/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CNSeniorious000/free-chat/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *.md 2 | Dockerfile 3 | docker-compose.yml 4 | LICENSE 5 | netlify.toml 6 | vercel.json 7 | node_modules 8 | .vscode 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode", "dbaeumer.vscode-eslint", "antfu.unocss"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_PROMPLATE_DEMO_BASE_URL } from 'astro:env/client' 2 | 3 | export const promplateBaseUrl = PUBLIC_PROMPLATE_DEMO_BASE_URL.replace(/\/$/, '') 4 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface ChatMessage { 2 | role: 'system' | 'user' | 'assistant' 3 | content: string 4 | } 5 | 6 | export interface ErrorMessage { 7 | code: string 8 | message: string 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/beta/translate.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '@/layouts/Layout.astro' 3 | import Translator from '@/components/Translator.svelte' 4 | --- 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | chatgpt-demo: 5 | image: ddiu8081/chatgpt-demo:latest 6 | container_name: chatgpt-demo 7 | restart: always 8 | ports: 9 | - "3000:3000" 10 | volumes: 11 | - .env:/usr/src/.env 12 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | WORKDIR /usr/src 3 | RUN npm install -g pnpm 4 | COPY package.json pnpm-lock.yaml ./ 5 | RUN pnpm install 6 | COPY . . 7 | RUN pnpm run build 8 | ENV HOST=0.0.0.0 PORT=3000 NODE_ENV=production 9 | EXPOSE $PORT 10 | CMD ["node", "dist/server/entry.mjs"] 11 | -------------------------------------------------------------------------------- /src/components/ChatInterface.tsx: -------------------------------------------------------------------------------- 1 | import { ChatProvider } from '@/context/ChatContext' 2 | 3 | import Generator from './Generator' 4 | 5 | export default function ChatInterface() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/track.ts: -------------------------------------------------------------------------------- 1 | export function trackEvent(event: string, data?: Record) { 2 | // @ts-expect-error umami is optional 3 | window.umami && window.umami?.track(event, data) 4 | // @ts-expect-error gtag is optional 5 | window.gtag && window.gtag('event', event, data) 6 | } 7 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NETLIFY_USE_PNPM = "true" 3 | NODE_VERSION = "18" 4 | 5 | [build] 6 | command = "node prepare.cjs & OUTPUT=netlify astro build" 7 | publish = "dist" 8 | 9 | [[headers]] 10 | for = "/manifest.webmanifest" 11 | [headers.values] 12 | Content-Type = "application/manifest+json" 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: npm 5 | directory: / 6 | schedule: 7 | interval: daily 8 | groups: 9 | unocss: 10 | patterns: 11 | - "*unocss*" 12 | - package-ecosystem: github-actions 13 | directory: / 14 | schedule: 15 | interval: daily 16 | -------------------------------------------------------------------------------- /public/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /shims.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace svelteHTML { 2 | import type { AttributifyAttributes } from 'unocss/preset-attributify' 3 | 4 | type HTMLAttributes = AttributifyAttributes 5 | } 6 | 7 | declare namespace JSX { 8 | import type { AttributifyAttributes } from 'unocss/preset-attributify' 9 | 10 | interface HTMLAttributes extends AttributifyAttributes {} 11 | } 12 | -------------------------------------------------------------------------------- /src/components/icons/Clear.tsx: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return ( 3 | 4 | ) 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": "explicit" 4 | }, 5 | "editor.formatOnSave": false, 6 | "eslint.validate": [ 7 | "javascript", 8 | "javascriptreact", 9 | "astro", // Enable .astro 10 | "svelte", // Enable .svelte 11 | "typescript", // Enable .ts 12 | "typescriptreact" // Enable .tsx 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/components/controls/Group.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 |

{title}

12 | {@render children?.()} 13 |
14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": [".astro/types.d.ts", "**/*"], 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "experimentalDecorators": true, 7 | "baseUrl": ".", 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "types": ["vite-plugin-pwa/info"], 11 | "paths": { 12 | "@/*": ["src/*"] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/client.ts: -------------------------------------------------------------------------------- 1 | import { OPENAI_API_BASE_URL, OPENAI_API_KEY, OPENAI_BASE_URL } from 'astro:env/server' 2 | import { createFetch } from 'xsfetch' 3 | 4 | const openaiBaseUrl = (OPENAI_BASE_URL ?? `${OPENAI_API_BASE_URL.trim().replace(/\/$/, '')}/v1`).trim().replace(/\/$/, '') 5 | 6 | export const openaiApiParams = { apiKey: OPENAI_API_KEY ?? '', baseURL: openaiBaseUrl, fetch: createFetch({ retry: 5, debug: true }) } 7 | -------------------------------------------------------------------------------- /plugins/disableBlocks.ts: -------------------------------------------------------------------------------- 1 | export default function plugin() { 2 | const transform = (code: string, id: string) => { 3 | if (id.includes('pages/api/generate.ts')) { 4 | return { 5 | code: code.replace(/^.*?#vercel-disable-blocks([\s\S]+?)#vercel-end.*$/gm, ''), 6 | map: null, 7 | } 8 | } 9 | } 10 | 11 | return { 12 | name: 'vercel-disable-blocks', 13 | enforce: 'pre', 14 | transform, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/icons/X.tsx: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return ( 3 | 4 | ) 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Inview.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /src/utils/events.ts: -------------------------------------------------------------------------------- 1 | export class MessagesEvent extends Event { 2 | public detail: { length: number } 3 | 4 | constructor(eventType: string, length: number) { 5 | super(eventType) 6 | this.detail = { length } 7 | } 8 | } 9 | 10 | export class LocalStorageSetEvent extends Event { 11 | public detail: { key: string, value: string } 12 | 13 | constructor(eventType: string, key: string, value: string) { 14 | super(eventType) 15 | this.detail = { key, value } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .vercel/ 4 | .netlify/ 5 | public/cl100k_base.json 6 | public/tiktoken_bg.wasm 7 | 8 | # generated types 9 | .astro/ 10 | 11 | # dependencies 12 | node_modules/ 13 | 14 | # logs 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | pnpm-debug.log* 19 | 20 | # environment variables 21 | .env 22 | .env.production 23 | 24 | # macOS-specific files 25 | .DS_Store 26 | 27 | # Local 28 | *.local 29 | 30 | **/.DS_Store 31 | 32 | # Editor directories and files 33 | .idea 34 | -------------------------------------------------------------------------------- /src/components/UseCopy.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {@render children?.({ handleClick, displayText })} 21 | -------------------------------------------------------------------------------- /src/components/icons/Env.tsx: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return ( 3 | 4 | ) 5 | } 6 | -------------------------------------------------------------------------------- /src/components/CheckStatus.svelte: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /src/components/controls/Hr.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {#if text} 13 |
14 | {text} 15 |
16 | {/if} 17 | 18 |
19 | -------------------------------------------------------------------------------- /src/utils/streaming.ts: -------------------------------------------------------------------------------- 1 | export async function* responseToAsyncIterator(response: Response): AsyncGenerator { 2 | const { body } = response 3 | if (!body) throw new Error('No data') 4 | const reader = body.getReader() 5 | 6 | const decoder = new TextDecoder() 7 | 8 | try { 9 | while (true) { 10 | const { done, value } = await reader.read() 11 | if (done) 12 | break 13 | 14 | yield decoder.decode(value, { stream: true }) 15 | } 16 | } finally { 17 | reader.releaseLock() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/cf-workers-ai.ts: -------------------------------------------------------------------------------- 1 | import { CF_ACCOUNT_ID, CF_API_TOKEN } from 'astro:env/server' 2 | 3 | export async function run(model: string, input: any) { 4 | if (!CF_ACCOUNT_ID || !CF_API_TOKEN) throw new Error('cloudflare configuration not found') 5 | 6 | const res = await fetch( 7 | `https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/ai/run/${model}`, 8 | { 9 | headers: { Authorization: `Bearer ${CF_API_TOKEN}` }, 10 | method: 'POST', 11 | body: JSON.stringify(input), 12 | }, 13 | ) 14 | return res.json() 15 | } 16 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | interface ImportMetaEnv { 5 | readonly PUBLIC_PROMPLATE_DEMO_BASE_URL: string 6 | readonly OPENAI_API_KEY: string 7 | readonly OPENAI_API_BASE_URL: string 8 | readonly HEAD_SCRIPTS: string 9 | readonly SECRET_KEY: string 10 | readonly PUBLIC_MAX_TOKENS: string 11 | readonly PUBLIC_MIN_MESSAGES: string 12 | readonly PUBLIC_DEFAULT_MODEL: string 13 | readonly PUBLIC_MODERATION_INTERVAL: string 14 | } 15 | 16 | interface ImportMeta { 17 | readonly env: ImportMetaEnv 18 | } 19 | -------------------------------------------------------------------------------- /src/components/controls/APIKeyInput.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/controls/Section.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |

{title}

13 |
14 | {@render children?.()} 15 |
16 | {#if tips} 17 |

18 | * 19 | {tips} 20 |

21 | {/if} 22 |
23 | -------------------------------------------------------------------------------- /src/components/Inview.tsx: -------------------------------------------------------------------------------- 1 | import { onCleanup, onMount, type Setter } from 'solid-js' 2 | 3 | export default (props: { 4 | setInview: Setter 5 | class: string 6 | }) => { 7 | let div: HTMLDivElement | undefined 8 | 9 | onMount(() => { 10 | const observer = new IntersectionObserver( 11 | ([entry]) => { 12 | props.setInview(entry.isIntersecting) 13 | }, 14 | { 15 | threshold: 0, 16 | }, 17 | ) 18 | 19 | observer.observe(div!) 20 | 21 | onCleanup(() => { 22 | observer.disconnect() 23 | }) 24 | }) 25 | 26 | return