reconstitution ui with daisyui
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html data-theme="cupcake" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta
|
<meta
|
||||||
|
|||||||
2268
package-lock.json
generated
Normal file
2268
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@heroicons/react": "^2.1.5",
|
||||||
"@types/ungap__structured-clone": "^0.3.1",
|
"@types/ungap__structured-clone": "^0.3.1",
|
||||||
"@ungap/structured-clone": "^1.2.0",
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.6.0",
|
"@preact/preset-vite": "^2.6.0",
|
||||||
|
"daisyui": "^4.12.10",
|
||||||
|
"theme-change": "^2.5.0",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.5.0"
|
"vite": "^4.5.0"
|
||||||
}
|
}
|
||||||
|
|||||||
97
src/app.tsx
97
src/app.tsx
@@ -228,7 +228,9 @@ export function App() {
|
|||||||
|
|
||||||
if (oldVersion < 11) {
|
if (oldVersion < 11) {
|
||||||
if (oldVersion < 11 && oldVersion >= 1) {
|
if (oldVersion < 11 && oldVersion >= 1) {
|
||||||
alert("Start upgrading storage, just a sec... (Click OK to continue)");
|
alert(
|
||||||
|
"Start upgrading storage, just a sec... (Click OK to continue)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
transaction
|
transaction
|
||||||
@@ -401,16 +403,16 @@ export function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex text-sm h-full bg-slate-200 dark:bg-slate-800 dark:text-white">
|
<div className="flex text-sm h-full">
|
||||||
<div className="flex flex-col h-full p-2 border-r-indigo-500 border-2 dark:border-slate-800 dark:border-r-indigo-500 dark:text-black">
|
<div className="flex flex-col h-full p-2 bg-primary">
|
||||||
<div className="grow overflow-scroll">
|
<div className="grow overflow-scroll">
|
||||||
<button
|
<button
|
||||||
className="w-full bg-violet-300 p-1 rounded hover:bg-violet-400"
|
className="btn btn-sm btn-info p-1 my-1 w-full"
|
||||||
onClick={handleNewChatStore}
|
onClick={handleNewChatStore}
|
||||||
>
|
>
|
||||||
{Tr("NEW")}
|
{Tr("NEW")}
|
||||||
</button>
|
</button>
|
||||||
<ul>
|
<ul class="pt-2">
|
||||||
{(allChatStoreIndexes as number[])
|
{(allChatStoreIndexes as number[])
|
||||||
.slice()
|
.slice()
|
||||||
.reverse()
|
.reverse()
|
||||||
@@ -419,8 +421,8 @@ export function App() {
|
|||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
className={`w-full my-1 p-1 rounded hover:bg-blue-500 ${
|
className={`w-full my-1 p-1 btn btn-sm ${
|
||||||
i === selectedChatIndex ? "bg-blue-500" : "bg-blue-200"
|
i === selectedChatIndex ? "btn-accent" : "btn-secondary"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedChatIndex(i);
|
setSelectedChatIndex(i);
|
||||||
@@ -433,52 +435,59 @@ export function App() {
|
|||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div>
|
||||||
className="rounded bg-rose-400 p-1 my-1 w-full"
|
|
||||||
onClick={async () => {
|
|
||||||
if (!confirm("Are you sure you want to delete this chat history?"))
|
|
||||||
return;
|
|
||||||
console.log("remove item", `${STORAGE_NAME}-${selectedChatIndex}`);
|
|
||||||
(await db).delete(STORAGE_NAME, selectedChatIndex);
|
|
||||||
const newAllChatStoreIndexes = await (
|
|
||||||
await db
|
|
||||||
).getAllKeys(STORAGE_NAME);
|
|
||||||
|
|
||||||
if (newAllChatStoreIndexes.length === 0) {
|
|
||||||
handleNewChatStore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find nex selected chat index
|
|
||||||
const next =
|
|
||||||
newAllChatStoreIndexes[newAllChatStoreIndexes.length - 1];
|
|
||||||
console.log("next is", next);
|
|
||||||
setSelectedChatIndex(next as number);
|
|
||||||
setAllChatStoreIndexes(newAllChatStoreIndexes);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Tr("DEL")}
|
|
||||||
</button>
|
|
||||||
{chatStore.develop_mode && (
|
|
||||||
<button
|
<button
|
||||||
className="rounded bg-rose-800 p-1 my-1 w-full text-white"
|
className="btn btn-warning btn-sm p-1 my-1 w-full"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm("Are you sure you want to delete this chat history?")
|
||||||
"Are you sure you want to delete **ALL** chat history?"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
console.log(
|
||||||
|
"remove item",
|
||||||
|
`${STORAGE_NAME}-${selectedChatIndex}`
|
||||||
|
);
|
||||||
|
(await db).delete(STORAGE_NAME, selectedChatIndex);
|
||||||
|
const newAllChatStoreIndexes = await (
|
||||||
|
await db
|
||||||
|
).getAllKeys(STORAGE_NAME);
|
||||||
|
|
||||||
await (await db).clear(STORAGE_NAME);
|
if (newAllChatStoreIndexes.length === 0) {
|
||||||
setAllChatStoreIndexes([]);
|
handleNewChatStore();
|
||||||
setSelectedChatIndex(1);
|
return;
|
||||||
window.location.reload();
|
}
|
||||||
|
|
||||||
|
// find nex selected chat index
|
||||||
|
const next =
|
||||||
|
newAllChatStoreIndexes[newAllChatStoreIndexes.length - 1];
|
||||||
|
console.log("next is", next);
|
||||||
|
setSelectedChatIndex(next as number);
|
||||||
|
setAllChatStoreIndexes(newAllChatStoreIndexes);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Tr("CLS")}
|
{Tr("DEL")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
{chatStore.develop_mode && (
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-warning p-1 my-1 w-full"
|
||||||
|
onClick={async () => {
|
||||||
|
if (
|
||||||
|
!confirm(
|
||||||
|
"Are you sure you want to delete **ALL** chat history?"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await (await db).clear(STORAGE_NAME);
|
||||||
|
setAllChatStoreIndexes([]);
|
||||||
|
setSelectedChatIndex(1);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Tr("CLS")}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChatBOX
|
<ChatBOX
|
||||||
db={db}
|
db={db}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { ListToolsTempaltes } from "./listToolsTemplates";
|
|||||||
import { autoHeight } from "./textarea";
|
import { autoHeight } from "./textarea";
|
||||||
import Search from "./search";
|
import Search from "./search";
|
||||||
import { IDBPDatabase } from "idb";
|
import { IDBPDatabase } from "idb";
|
||||||
|
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
export interface TemplateChatStore extends ChatStore {
|
export interface TemplateChatStore extends ChatStore {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -434,7 +435,7 @@ export default function ChatBOX(props: {
|
|||||||
const userInputRef = createRef();
|
const userInputRef = createRef();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grow flex flex-col p-2 dark:text-black">
|
<div className="grow flex flex-col p-2">
|
||||||
{showSettings && (
|
{showSettings && (
|
||||||
<Settings
|
<Settings
|
||||||
chatStore={chatStore}
|
chatStore={chatStore}
|
||||||
@@ -464,11 +465,11 @@ export default function ChatBOX(props: {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="relative cursor-pointer rounded bg-cyan-300 dark:text-white p-1 dark:bg-cyan-800"
|
className="relative cursor-pointer rounded p-2 bg-base-200"
|
||||||
onClick={() => setShowSettings(true)}
|
onClick={() => setShowSettings(true)}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className="absolute right-1 bg-gray-300 rounded p-1 m-1"
|
className="absolute right-1 rounded p-1 m-1"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
// stop propagation to parent
|
// stop propagation to parent
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -476,7 +477,7 @@ export default function ChatBOX(props: {
|
|||||||
setShowSearch(true);
|
setShowSearch(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🔍
|
<MagnifyingGlassIcon class="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
<div>
|
<div>
|
||||||
<button className="underline">
|
<button className="underline">
|
||||||
@@ -514,12 +515,12 @@ export default function ChatBOX(props: {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grow overflow-scroll">
|
<div className="grow overflow-scroll">
|
||||||
{!chatStore.apiKey && (
|
{!chatStore.apiKey && (
|
||||||
<p className="opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
<p className="bg-base-200 p-6 rounded my-3 text-left">
|
||||||
{Tr("Please click above to set")} (OpenAI) API KEY
|
{Tr("Please click above to set")} (OpenAI) API KEY
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{!chatStore.apiEndpoint && (
|
{!chatStore.apiEndpoint && (
|
||||||
<p className="opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
<p className="bg-base-200 p-6 rounded my-3 text-left">
|
||||||
{Tr("Please click above to set")} API Endpoint
|
{Tr("Please click above to set")} API Endpoint
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -581,7 +582,7 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{chatStore.history.filter((msg) => !msg.example).length == 0 && (
|
{chatStore.history.filter((msg) => !msg.example).length == 0 && (
|
||||||
<div className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black">
|
<div className="bg-base-200 break-all p-3 my-3 text-left">
|
||||||
<h2>
|
<h2>
|
||||||
<span>{Tr("Saved prompt templates")}</span>
|
<span>{Tr("Saved prompt templates")}</span>
|
||||||
<button
|
<button
|
||||||
@@ -596,7 +597,7 @@ export default function ChatBOX(props: {
|
|||||||
{Tr("Reset Current")}
|
{Tr("Reset Current")}
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<hr className="my-2" />
|
<div class="divider"></div>
|
||||||
<div className="flex flex-wrap">
|
<div className="flex flex-wrap">
|
||||||
{templates.map((t, index) => (
|
{templates.map((t, index) => (
|
||||||
<div
|
<div
|
||||||
@@ -729,7 +730,7 @@ export default function ChatBOX(props: {
|
|||||||
<p className="text-center">
|
<p className="text-center">
|
||||||
{chatStore.history.length > 0 && (
|
{chatStore.history.length > 0 && (
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-2 p-2 border-2 bg-teal-500 hover:bg-teal-600"
|
className="btn btn-warning disabled:line-through disabled:btn-neutral disabled:text-white m-2 p-2"
|
||||||
disabled={showGenerating}
|
disabled={showGenerating}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const messageIndex = chatStore.history.length - 1;
|
const messageIndex = chatStore.history.length - 1;
|
||||||
@@ -749,7 +750,7 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
{chatStore.develop_mode && chatStore.history.length > 0 && (
|
{chatStore.develop_mode && chatStore.history.length > 0 && (
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-2 p-2 border-2 bg-yellow-500 hover:bg-yellow-600"
|
className="btn btn-warning disabled:line-through disabled:bg-neural m-2 p-2"
|
||||||
disabled={showGenerating}
|
disabled={showGenerating}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await complete();
|
await complete();
|
||||||
@@ -852,15 +853,16 @@ export default function ChatBOX(props: {
|
|||||||
<input type="checkbox" checked={follow} />
|
<input type="checkbox" checked={follow} />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600"
|
className="btn btn-primary disabled:line-through disabled:text-white disabled:bg-neutral m-1 p-1"
|
||||||
disabled={showGenerating || !chatStore.apiKey}
|
disabled={showGenerating || !chatStore.apiKey}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowAddImage(!showAddImage);
|
setShowAddImage(!showAddImage);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Img
|
Image
|
||||||
</button>
|
</button>
|
||||||
{showAddImage && (
|
{showAddImage && (
|
||||||
<AddImage
|
<AddImage
|
||||||
@@ -891,11 +893,11 @@ export default function ChatBOX(props: {
|
|||||||
autoHeight(event.target);
|
autoHeight(event.target);
|
||||||
setInputMsg(event.target.value);
|
setInputMsg(event.target.value);
|
||||||
}}
|
}}
|
||||||
className="rounded grow m-1 p-1 border-2 border-gray-400 w-0"
|
className="textarea textarea-bordered textarea-xs grow m-1 p-1 w-0"
|
||||||
placeholder="Type here..."
|
placeholder="Type here..."
|
||||||
></textarea>
|
></textarea>
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600"
|
className="btn btn-primary disabled:btn-neutral disabled:line-through m-1 p-1"
|
||||||
disabled={showGenerating}
|
disabled={showGenerating}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
send(inputMsg, true);
|
send(inputMsg, true);
|
||||||
@@ -909,10 +911,8 @@ export default function ChatBOX(props: {
|
|||||||
chatStore.whisper_key &&
|
chatStore.whisper_key &&
|
||||||
(chatStore.whisper_key || chatStore.apiKey) && (
|
(chatStore.whisper_key || chatStore.apiKey) && (
|
||||||
<button
|
<button
|
||||||
className={`disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 ${
|
className={`btn disabled:line-through disabled:btn-neutral disabled:text-white m-1 p-1 ${
|
||||||
isRecording === "Recording"
|
isRecording === "Recording" ? "btn-error" : "btn-success"
|
||||||
? "bg-red-400 hover:bg-red-600"
|
|
||||||
: "bg-cyan-400 hover:bg-cyan-600"
|
|
||||||
} ${isRecording !== "Mic" ? "animate-pulse" : ""}`}
|
} ${isRecording !== "Mic" ? "animate-pulse" : ""}`}
|
||||||
disabled={isRecording === "Transcribing"}
|
disabled={isRecording === "Transcribing"}
|
||||||
ref={mediaRef}
|
ref={mediaRef}
|
||||||
@@ -1027,7 +1027,7 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
{chatStore.develop_mode && (
|
{chatStore.develop_mode && (
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600"
|
className="btn disabled:line-through disabled:btn-neutral disabled:text-white m-1 p-1"
|
||||||
disabled={showGenerating || !chatStore.apiKey}
|
disabled={showGenerating || !chatStore.apiKey}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
chatStore.history.push({
|
chatStore.history.push({
|
||||||
@@ -1051,7 +1051,7 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
{chatStore.develop_mode && (
|
{chatStore.develop_mode && (
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600"
|
className="btn disabled:line-through disabled:btn-neutral disabled:text-white m-1 p-1"
|
||||||
disabled={showGenerating || !chatStore.apiKey}
|
disabled={showGenerating || !chatStore.apiKey}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
send(inputMsg, false);
|
send(inputMsg, false);
|
||||||
@@ -1062,7 +1062,7 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
{chatStore.develop_mode && (
|
{chatStore.develop_mode && (
|
||||||
<button
|
<button
|
||||||
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600"
|
className="btn disabled:line-through disabled:btn-neutral disabled:text-white m-1 p-1"
|
||||||
disabled={showGenerating || !chatStore.apiKey}
|
disabled={showGenerating || !chatStore.apiKey}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowAddToolMsg(true);
|
setShowAddToolMsg(true);
|
||||||
|
|||||||
19
src/main.tsx
19
src/main.tsx
@@ -3,25 +3,28 @@ import { App } from "./app";
|
|||||||
import { useState, useEffect } from "preact/hooks";
|
import { useState, useEffect } from "preact/hooks";
|
||||||
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
import { Tr, langCodeContext, LANG_OPTIONS } from "./translate";
|
||||||
|
|
||||||
|
import { themeChange } from "theme-change";
|
||||||
|
|
||||||
function Base() {
|
function Base() {
|
||||||
const [langCode, _setLangCode] = useState("en-US");
|
const [langCode, _setLangCode] = useState("en-US");
|
||||||
|
|
||||||
const setLangCode = (langCode: string) => {
|
const setLangCode = (langCode: string) => {
|
||||||
_setLangCode(langCode)
|
_setLangCode(langCode);
|
||||||
if (!localStorage) return
|
if (!localStorage) return;
|
||||||
|
|
||||||
localStorage.setItem('chatgpt-api-web-lang', langCode)
|
localStorage.setItem("chatgpt-api-web-lang", langCode);
|
||||||
}
|
};
|
||||||
|
|
||||||
// select language
|
// select language
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
themeChange(false);
|
||||||
// query localStorage
|
// query localStorage
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
const lang = localStorage.getItem('chatgpt-api-web-lang')
|
const lang = localStorage.getItem("chatgpt-api-web-lang");
|
||||||
if (lang) {
|
if (lang) {
|
||||||
console.log(`query langCode ${lang} from localStorage`)
|
console.log(`query langCode ${lang} from localStorage`);
|
||||||
_setLangCode(lang)
|
_setLangCode(lang);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
922
src/settings.tsx
922
src/settings.tsx
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,42 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
daisyui: {
|
||||||
|
themes: ["light",
|
||||||
|
"dark",
|
||||||
|
"cupcake",
|
||||||
|
"bumblebee",
|
||||||
|
"emerald",
|
||||||
|
"corporate",
|
||||||
|
"synthwave",
|
||||||
|
"retro",
|
||||||
|
"cyberpunk",
|
||||||
|
"valentine",
|
||||||
|
"halloween",
|
||||||
|
"garden",
|
||||||
|
"forest",
|
||||||
|
"aqua",
|
||||||
|
"lofi",
|
||||||
|
"pastel",
|
||||||
|
"fantasy",
|
||||||
|
"wireframe",
|
||||||
|
"black",
|
||||||
|
"luxury",
|
||||||
|
"dracula",
|
||||||
|
"cmyk",
|
||||||
|
"autumn",
|
||||||
|
"business",
|
||||||
|
"acid",
|
||||||
|
"lemonade",
|
||||||
|
"night",
|
||||||
|
"coffee",
|
||||||
|
"winter",
|
||||||
|
"dim",
|
||||||
|
"nord",
|
||||||
|
"sunset",],
|
||||||
|
},
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [require('daisyui')],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user