diff --git a/.parcelrc b/.parcelrc new file mode 100644 index 0000000..1f3df89 --- /dev/null +++ b/.parcelrc @@ -0,0 +1,4 @@ +{ + "extends": ["@parcel/config-default"], + "reporters": ["...", "parcel-reporter-static-files-copy"] +} diff --git a/README.md b/README.md index a5131b2..50c55c1 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,13 @@ Crafted with love and care to provide the best experience possible. ## Self-host using Docker ``` -docker run --name chatpad -d -p 1234:80 ghcr.io/deiucanta/chatpad:latest +docker run --name chatpad -d -p 8080:80 ghcr.io/deiucanta/chatpad:latest +``` + +## Self-host using Docker with custom config + +``` +docker run --name chatpad -d -v `pwd`/config.json:/usr/share/nginx/html/config.json -p 8080:80 ghcr.io/deiucanta/chatpad:latest ``` ## One click Deployments diff --git a/package-lock.json b/package-lock.json index 8a3c8b8..742a0b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@types/react-dom": "^18.0.11", "buffer": "^5.7.1", "parcel": "^2.8.3", - "path-browserify": "^1.0.1", + "parcel-reporter-static-files-copy": "^1.5.0", "process": "^0.11.10" } }, @@ -6776,6 +6776,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/parcel-reporter-static-files-copy": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.0.tgz", + "integrity": "sha512-dsY3MQkbYSgEqS0/22vtD2mZtel8UC0ItH0ok8LmgFeCMTsdhyOtJgvt945ODIzu9lYc/sCIzksM8C77uSE3Fg==", + "dev": true, + "dependencies": { + "@parcel/plugin": "^2.0.0-beta.1" + }, + "engines": { + "parcel": "^2.0.0-beta.1" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -12924,6 +12936,15 @@ "v8-compile-cache": "^2.0.0" } }, + "parcel-reporter-static-files-copy": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.0.tgz", + "integrity": "sha512-dsY3MQkbYSgEqS0/22vtD2mZtel8UC0ItH0ok8LmgFeCMTsdhyOtJgvt945ODIzu9lYc/sCIzksM8C77uSE3Fg==", + "dev": true, + "requires": { + "@parcel/plugin": "^2.0.0-beta.1" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/package.json b/package.json index c104ce8..93dd9e0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "start": "parcel", "build": "parcel build" }, + "staticFiles": { + "staticPath": "src/static" + }, "devDependencies": { "@parcel/transformer-sass": "^2.8.3", "@types/downloadjs": "^1.4.3", @@ -16,6 +19,7 @@ "buffer": "^5.7.1", "parcel": "^2.8.3", "path-browserify": "^1.0.1", + "parcel-reporter-static-files-copy": "^1.5.0", "process": "^0.11.10" }, "dependencies": { diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index beb7f61..3c39330 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -40,6 +40,7 @@ import { DatabaseModal } from "./DatabaseModal"; import { LogoText } from "./Logo"; import { Prompts } from "./Prompts"; import { SettingsModal } from "./SettingsModal"; +import { config } from "../utils/config"; declare global { interface Window { @@ -186,35 +187,41 @@ export function Layout() {
- - toggleColorScheme()} + {config.allowDarkModeToggle && ( + - {colorScheme === "dark" ? ( - - ) : ( - - )} - - - - - - + toggleColorScheme()} + > + {colorScheme === "dark" ? ( + + ) : ( + + )} - - - - - - - - + )} + {config.allowSettingsModal && ( + + + + + + + + )} + {config.allowDatabaseModal && ( + + + + + + + + )} - - - - - - - { - if (window.todesktop) { - event.preventDefault(); - window.todesktop.contents.openUrlInBrowser( - "https://feedback.chatpad.ai" - ); - } - }} - target="_blank" - sx={{ flex: 1 }} - size="xl" - > - - - + {config.showTwitterLink && ( + + + + + + )} + {config.showFeedbackLink && ( + + { + if (window.todesktop) { + event.preventDefault(); + window.todesktop.contents.openUrlInBrowser( + "https://feedback.chatpad.ai" + ); + } + }} + target="_blank" + sx={{ flex: 1 }} + size="xl" + > + + + + )}
diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 4a1fc0c..824263c 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -6,6 +6,7 @@ import { List, Modal, PasswordInput, + TextInput, Select, Stack, Text, @@ -15,7 +16,7 @@ import { notifications } from "@mantine/notifications"; import { useLiveQuery } from "dexie-react-hooks"; import { cloneElement, ReactElement, useEffect, useState } from "react"; import { db } from "../db"; -import { availableModels, defaultModel } from "../utils/constants"; +import { config } from "../utils/config"; import { checkOpenAIKey } from "../utils/openai"; export function SettingsModal({ children }: { children: ReactElement }) { @@ -23,7 +24,11 @@ export function SettingsModal({ children }: { children: ReactElement }) { const [submitting, setSubmitting] = useState(false); const [value, setValue] = useState(""); - const [model, setModel] = useState(defaultModel); + const [model, setModel] = useState(config.defaultModel); + const [type, setType] = useState(config.defaultType); + const [auth, setAuth] = useState(config.defaultAuth); + const [base, setBase] = useState(""); + const [version, setVersion] = useState(""); const settings = useLiveQuery(async () => { return db.settings.where({ id: "general" }).first(); @@ -36,6 +41,18 @@ export function SettingsModal({ children }: { children: ReactElement }) { if (settings?.openAiModel) { setModel(settings.openAiModel); } + if (settings?.openAiApiType) { + setType(settings.openAiApiType); + } + if (settings?.openAiApiAuth) { + setAuth(settings.openAiApiAuth); + } + if (settings?.openAiApiBase) { + setBase(settings.openAiApiBase); + } + if (settings?.openAiApiVersion) { + setVersion(settings.openAiApiVersion); + } }, [settings]); return ( @@ -111,20 +128,213 @@ export function SettingsModal({ children }: { children: ReactElement }) { { + setSubmitting(true); + try { + await db.settings.update("general", { + openAiModel: value ?? undefined, + }); + notifications.show({ + title: "Saved", + message: "Your OpenAI Model has been saved.", + }); + } catch (error: any) { + if (error.toJSON().message === "Network Error") { + notifications.show({ + title: "Error", + color: "red", + message: "No internet connection.", + }); + } + const message = error.response?.data?.error?.message; + if (message) { + notifications.show({ + title: "Error", + color: "red", + message, + }); + } + } finally { + setSubmitting(false); + } + }} + withinPortal + data={config.availableModels} /> The displayed cost was not updated yet to reflect the costs for each - model. Right now it will always show the cost for GPT-3.5. + model. Right now it will always show the cost for GPT-3.5 on OpenAI. + db.settings.get("general")); @@ -56,16 +57,18 @@ export function IndexRoute() { ))} - - - - {!window.todesktop && ( + {config.allowSettingsModal && ( + + + + )} + {config.showDownloadLink && !window.todesktop && (