Compare commits
15 Commits
0701303563
...
c75663bf90
Author | SHA1 | Date |
---|---|---|
Cogent Apps | c75663bf90 | |
Cogent Apps | 946190eb9d | |
ZauberNerd | 36c1cb47c0 | |
Cogent Apps | 8184c950d7 | |
Cogent Apps | a13fff35da | |
Cogent Apps | f9122c5227 | |
Cogent Apps | 4f5e7fbdc8 | |
Cogent Apps | 71a6e699c8 | |
Cogent Apps | d7e493c347 | |
Cogent Apps | 25fd17a554 | |
ZauberNerd | 6e791e1b97 | |
ZauberNerd | ba6544ba53 | |
ZauberNerd | d5131d167d | |
QJoly | c85299f816 | |
QJoly | d7fe13b324 |
|
@ -11,7 +11,9 @@ name: publish image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['release']
|
branches:
|
||||||
|
- release
|
||||||
|
- beta
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
|
@ -28,6 +30,15 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
with:
|
||||||
|
image: tonistiigi/binfmt:latest
|
||||||
|
platforms: arm64
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||||
with:
|
with:
|
||||||
|
@ -46,5 +57,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
13
Dockerfile
13
Dockerfile
|
@ -14,9 +14,12 @@ COPY ./app/tsconfig.json ./
|
||||||
# Install Node.js dependencies
|
# Install Node.js dependencies
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
|
COPY ./app/vite.config.js ./
|
||||||
|
|
||||||
# Copy public, and src directories
|
# Copy public, and src directories
|
||||||
COPY ./app/public ./public
|
COPY ./app/public ./public
|
||||||
COPY ./app/src ./src
|
COPY ./app/src ./src
|
||||||
|
COPY ./app/index.html ./
|
||||||
|
|
||||||
# Set environment variables
|
# Set environment variables
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
@ -29,14 +32,6 @@ FROM node:19-bullseye-slim AS server
|
||||||
# Set the working directory
|
# Set the working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Update the package index and install required dependencies
|
|
||||||
# RUN apt-get update && \
|
|
||||||
# apt-get install -y \
|
|
||||||
# curl \
|
|
||||||
# build-essential \
|
|
||||||
# libssl-dev \
|
|
||||||
# openssl
|
|
||||||
|
|
||||||
COPY ./server/package.json ./server/tsconfig.json ./
|
COPY ./server/package.json ./server/tsconfig.json ./
|
||||||
|
|
||||||
# Install Node.js dependencies from package.json
|
# Install Node.js dependencies from package.json
|
||||||
|
@ -45,7 +40,7 @@ RUN npm install
|
||||||
# Copy the rest of the application code into the working directory
|
# Copy the rest of the application code into the working directory
|
||||||
COPY ./server/src ./src
|
COPY ./server/src ./src
|
||||||
|
|
||||||
RUN CI=true sh -c "cd /app && mkdir data && npm run start && rm -rf data"
|
RUN CI=true sh -c "cd /app && npm run start && rm -rf data"
|
||||||
|
|
||||||
COPY --from=build /app/build /app/public
|
COPY --from=build /app/build /app/public
|
||||||
|
|
||||||
|
|
|
@ -3,33 +3,24 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||||||
|
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
|
||||||
|
<link rel="preconnect" crossorigin="anonymous" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" crossorigin="anonymous" href="https://cdnjs.cloudflare.com">
|
||||||
|
<link rel="preconnect" crossorigin="anonymous" href="https://cdn.jsdelivr.net">
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<!-- mobile app viewport -->
|
<!-- mobile app viewport -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="An open source ChatGPT app with a voice." />
|
<meta name="description" content="An open source ChatGPT app with a voice." />
|
||||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>Chat with GPT | Unofficial ChatGPT app</title>
|
<title>Chat with GPT | Unofficial ChatGPT app</title>
|
||||||
<link rel="stylesheet" media="all" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" />
|
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" />
|
||||||
<link rel="stylesheet" media="all" href="https://fonts.googleapis.com/css?family=Open+Sans:100,400,300,500,700,800" />
|
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://fonts.googleapis.com/css?family=Open+Sans:100,400,300,500,700,800" />
|
||||||
<link rel="stylesheet" media="all" href="https://fonts.googleapis.com/css?family=Fira+Code:100,400,300,500,700,800" />
|
<link rel="stylesheet" crossorigin="anonymous" media="all" href="https://fonts.googleapis.com/css?family=Fira+Code:100,400,300,500,700,800" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Work+Sans:300,400,500,600,700&display=swap" rel="stylesheet">
|
<link rel="stylesheet" crossorigin="anonymous" href="https://fonts.googleapis.com/css?family=Work+Sans:300,400,500,600,700&display=swap">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css"
|
<link rel="stylesheet" crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC">
|
||||||
integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="/prose.css" />
|
<link rel="stylesheet" href="/prose.css" />
|
||||||
<link rel="canonical" href="https://www.chatwithgpt.ai" />
|
<link rel="canonical" href="https://www.chatwithgpt.ai" />
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -69,6 +69,8 @@
|
||||||
"@vitejs/plugin-react": "^4.0.2",
|
"@vitejs/plugin-react": "^4.0.2",
|
||||||
"babel-plugin-formatjs": "^10.5.3",
|
"babel-plugin-formatjs": "^10.5.3",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.4.1"
|
"vite": "^4.4.1",
|
||||||
|
"vite-plugin-pwa": "^0.16.4",
|
||||||
|
"workbox-window": "^7.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"short_name": "Chat with GPT",
|
|
||||||
"name": "Chat with GPT",
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@ import { useChatSpotlightProps } from '../spotlight';
|
||||||
import { LoginModal, CreateAccountModal } from './auth-modals';
|
import { LoginModal, CreateAccountModal } from './auth-modals';
|
||||||
import Header, { HeaderProps, SubHeader } from './header';
|
import Header, { HeaderProps, SubHeader } from './header';
|
||||||
import MessageInput from './input';
|
import MessageInput from './input';
|
||||||
|
import { InstallUpdateNotification } from './pwa-notifications';
|
||||||
import SettingsDrawer from './settings';
|
import SettingsDrawer from './settings';
|
||||||
import Sidebar from './sidebar';
|
import Sidebar from './sidebar';
|
||||||
import AudioControls from './tts-controls';
|
import AudioControls from './tts-controls';
|
||||||
|
@ -90,6 +91,7 @@ export function Page(props: {
|
||||||
<SettingsDrawer />
|
<SettingsDrawer />
|
||||||
<LoginModal />
|
<LoginModal />
|
||||||
<CreateAccountModal />
|
<CreateAccountModal />
|
||||||
|
<InstallUpdateNotification />
|
||||||
</Main>
|
</Main>
|
||||||
</Container>
|
</Container>
|
||||||
</SpotlightProvider>;
|
</SpotlightProvider>;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Button, Notification } from "@mantine/core";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||||
|
|
||||||
|
export function InstallUpdateNotification() {
|
||||||
|
const {
|
||||||
|
offlineReady: [_, setOfflineReady],
|
||||||
|
needRefresh: [needRefresh, setNeedRefresh],
|
||||||
|
updateServiceWorker,
|
||||||
|
} = useRegisterSW({
|
||||||
|
onRegistered(r) {
|
||||||
|
console.log("SW Registered:", r);
|
||||||
|
},
|
||||||
|
onRegisterError(error) {
|
||||||
|
console.log("SW registration error", error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
setOfflineReady(false);
|
||||||
|
setNeedRefresh(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUpdate = useCallback(async () => {
|
||||||
|
updateServiceWorker(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return needRefresh ? (
|
||||||
|
<Notification title="Update available!" onClose={onClose}>
|
||||||
|
Click{" "}
|
||||||
|
<Button compact onClick={onUpdate}>
|
||||||
|
Update now
|
||||||
|
</Button>{" "}
|
||||||
|
to get the latest version.
|
||||||
|
</Notification>
|
||||||
|
) : null;
|
||||||
|
}
|
|
@ -1,11 +1,20 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||||
|
import {
|
||||||
|
FLUSH,
|
||||||
|
PAUSE,
|
||||||
|
PERSIST,
|
||||||
|
PURGE,
|
||||||
|
REGISTER,
|
||||||
|
REHYDRATE,
|
||||||
|
persistReducer,
|
||||||
|
persistStore,
|
||||||
|
} from "redux-persist";
|
||||||
import storage from 'redux-persist/lib/storage';
|
import storage from 'redux-persist/lib/storage';
|
||||||
import { persistReducer, persistStore } from 'redux-persist';
|
|
||||||
import messageReducer from './message';
|
import messageReducer from './message';
|
||||||
import uiReducer from './ui';
|
|
||||||
import settingsUIReducer from './settings-ui';
|
import settingsUIReducer from './settings-ui';
|
||||||
import sidebarReducer from './sidebar';
|
import sidebarReducer from './sidebar';
|
||||||
|
import uiReducer from './ui';
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
key: 'root',
|
key: 'root',
|
||||||
|
@ -22,16 +31,20 @@ const persistMessageConfig = {
|
||||||
storage,
|
storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
message: persistReducer(persistMessageConfig, messageReducer),
|
message: persistReducer<ReturnType<typeof messageReducer>>(persistMessageConfig, messageReducer),
|
||||||
ui: uiReducer,
|
ui: uiReducer,
|
||||||
settingsUI: settingsUIReducer,
|
settingsUI: settingsUIReducer,
|
||||||
sidebar: persistReducer(persistSidebarConfig, sidebarReducer),
|
sidebar: persistReducer<ReturnType<typeof sidebarReducer>>(persistSidebarConfig, sidebarReducer),
|
||||||
},
|
},
|
||||||
})
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
getDefaultMiddleware({
|
||||||
|
serializableCheck: {
|
||||||
|
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof store.getState>;
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
export type AppDispatch = typeof store.dispatch;
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
/// <reference types="vite-plugin-comlink/client" />
|
/// <reference types="vite-plugin-comlink/client" />
|
||||||
|
/// <reference types="vite-plugin-pwa/client" />
|
|
@ -1,5 +1,6 @@
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
|
import { VitePWA } from "vite-plugin-pwa";
|
||||||
|
|
||||||
export default defineConfig(() => {
|
export default defineConfig(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -14,6 +15,11 @@ export default defineConfig(() => {
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: "build",
|
outDir: "build",
|
||||||
|
target: "es2020",
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
esbuild: {
|
||||||
|
target: "es2020",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
@ -34,6 +40,90 @@ export default defineConfig(() => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
VitePWA({
|
||||||
|
registerType: "autoUpdate",
|
||||||
|
includeAssets: ["favicon.ico", "lang/*.json"],
|
||||||
|
manifest: {
|
||||||
|
short_name: "Chat with GPT",
|
||||||
|
name: "Chat with GPT",
|
||||||
|
start_url: ".",
|
||||||
|
display: "standalone",
|
||||||
|
theme_color: "#000000",
|
||||||
|
background_color: "#ffffff",
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: "logo192.png",
|
||||||
|
type: "image/png",
|
||||||
|
sizes: "192x192",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "logo512.png",
|
||||||
|
type: "image/png",
|
||||||
|
sizes: "512x512",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
workbox: {
|
||||||
|
runtimeCaching: [
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
||||||
|
handler: "CacheFirst",
|
||||||
|
options: {
|
||||||
|
cacheName: "google-fonts-cache",
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 10,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
||||||
|
handler: "CacheFirst",
|
||||||
|
options: {
|
||||||
|
cacheName: "gstatic-fonts-cache",
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 10,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/cdnjs\.cloudflare\.com\/.*/i,
|
||||||
|
handler: "CacheFirst",
|
||||||
|
options: {
|
||||||
|
cacheName: "cloudflare-js-cdn",
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 10,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/cdn\.jsdelivr\.net\/.*/i,
|
||||||
|
handler: "CacheFirst",
|
||||||
|
options: {
|
||||||
|
cacheName: "jsdelivr-cdn",
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 10,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||||
|
},
|
||||||
|
cacheableResponse: {
|
||||||
|
statuses: [0, 200],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue