move status to AppContext
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { MessageDetail } from "@/chatgpt";
|
import { MessageDetail } from "@/chatgpt";
|
||||||
import { Tr } from "@/translate";
|
import { Tr } from "@/translate";
|
||||||
@@ -20,10 +20,9 @@ import { Checkbox } from "./components/ui/checkbox";
|
|||||||
import { Label } from "./components/ui/label";
|
import { Label } from "./components/ui/label";
|
||||||
import { Textarea } from "./components/ui/textarea";
|
import { Textarea } from "./components/ui/textarea";
|
||||||
import { Separator } from "./components/ui/separator";
|
import { Separator } from "./components/ui/separator";
|
||||||
|
import { AppContext } from "./pages/App";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
images: MessageDetail[];
|
images: MessageDetail[];
|
||||||
showAddImage: boolean;
|
showAddImage: boolean;
|
||||||
setShowAddImage: (se: boolean) => void;
|
setShowAddImage: (se: boolean) => void;
|
||||||
@@ -35,13 +34,14 @@ interface ImageResponse {
|
|||||||
revised_prompt: string;
|
revised_prompt: string;
|
||||||
}
|
}
|
||||||
export function AddImage({
|
export function AddImage({
|
||||||
chatStore,
|
|
||||||
setChatStore,
|
|
||||||
showAddImage,
|
showAddImage,
|
||||||
setShowAddImage,
|
setShowAddImage,
|
||||||
setImages,
|
setImages,
|
||||||
images,
|
images,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
|
|
||||||
const [enableHighResolution, setEnableHighResolution] = useState(true);
|
const [enableHighResolution, setEnableHighResolution] = useState(true);
|
||||||
const [imageGenPrompt, setImageGenPrompt] = useState("");
|
const [imageGenPrompt, setImageGenPrompt] = useState("");
|
||||||
const [imageGenModel, setImageGenModel] = useState("dall-e-3");
|
const [imageGenModel, setImageGenModel] = useState("dall-e-3");
|
||||||
@@ -134,7 +134,7 @@ export function AddImage({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-2" />
|
<Separator className="my-2" />
|
||||||
{chatStore.image_gen_api && chatStore.image_gen_key && (
|
{ctx.chatStore.image_gen_api && ctx.chatStore.image_gen_key && (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h3>Generate Image</h3>
|
<h3>Generate Image</h3>
|
||||||
<span className="flex flex-col justify-between m-1 p-1">
|
<span className="flex flex-col justify-between m-1 p-1">
|
||||||
@@ -240,11 +240,11 @@ export function AddImage({
|
|||||||
body.style = imageGenStyle;
|
body.style = imageGenStyle;
|
||||||
}
|
}
|
||||||
const resp: ImageResponse[] = (
|
const resp: ImageResponse[] = (
|
||||||
await fetch(chatStore.image_gen_api, {
|
await fetch(ctx.chatStore.image_gen_api, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${chatStore.image_gen_key}`,
|
Authorization: `Bearer ${ctx.chatStore.image_gen_key}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
}).then((resp) => resp.json())
|
}).then((resp) => resp.json())
|
||||||
@@ -258,7 +258,7 @@ export function AddImage({
|
|||||||
url = "data:image/png;base64," + image.b64_json;
|
url = "data:image/png;base64," + image.b64_json;
|
||||||
if (!url) continue;
|
if (!url) continue;
|
||||||
|
|
||||||
chatStore.history.push({
|
ctx.chatStore.history.push({
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
@@ -281,7 +281,7 @@ export function AddImage({
|
|||||||
response_model_name: imageGenModel,
|
response_model_name: imageGenModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
setChatStore({ ...chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ import {
|
|||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { AppContext } from "@/pages/App";
|
||||||
|
|
||||||
const TTS_VOICES: string[] = [
|
const TTS_VOICES: string[] = [
|
||||||
"alloy",
|
"alloy",
|
||||||
@@ -96,14 +97,13 @@ const Help = (props: { children: any; help: string; field: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectModel = (props: {
|
const SelectModel = (props: { help: string }) => {
|
||||||
chatStore: ChatStore;
|
const ctx = useContext(AppContext);
|
||||||
setChatStore: (cs: ChatStore) => void;
|
if (ctx === null) return <></>;
|
||||||
help: string;
|
|
||||||
}) => {
|
|
||||||
let shouldIUseCustomModel: boolean = true;
|
let shouldIUseCustomModel: boolean = true;
|
||||||
for (const model in models) {
|
for (const model in models) {
|
||||||
if (props.chatStore.model === model) {
|
if (ctx.chatStore.model === model) {
|
||||||
shouldIUseCustomModel = false;
|
shouldIUseCustomModel = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,22 +143,22 @@ const SelectModel = (props: {
|
|||||||
|
|
||||||
{useCustomModel ? (
|
{useCustomModel ? (
|
||||||
<Input
|
<Input
|
||||||
value={props.chatStore.model}
|
value={ctx.chatStore.model}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
props.chatStore.model = e.target.value;
|
ctx.chatStore.model = e.target.value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Select
|
<Select
|
||||||
value={props.chatStore.model}
|
value={ctx.chatStore.model}
|
||||||
onValueChange={(model: string) => {
|
onValueChange={(model: string) => {
|
||||||
props.chatStore.model = model;
|
ctx.chatStore.model = model;
|
||||||
props.chatStore.maxTokens = getDefaultParams(
|
ctx.chatStore.maxTokens = getDefaultParams(
|
||||||
"max",
|
"max",
|
||||||
models[model].maxToken
|
models[model].maxToken
|
||||||
);
|
);
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -181,12 +181,12 @@ const SelectModel = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LongInput = (props: {
|
const LongInput = (props: {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
field: "systemMessageContent" | "toolsString";
|
field: "systemMessageContent" | "toolsString";
|
||||||
label: string;
|
label: string;
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="name" className="text-right">
|
<Label htmlFor="name" className="text-right">
|
||||||
@@ -208,10 +208,10 @@ const LongInput = (props: {
|
|||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-24 w-full"
|
className="h-24 w-full"
|
||||||
value={props.chatStore[props.field]}
|
value={ctx.chatStore[props.field]}
|
||||||
onChange={(event: any) => {
|
onChange={(event: any) => {
|
||||||
props.chatStore[props.field] = event.target.value;
|
ctx.chatStore[props.field] = event.target.value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
autoHeight(event.target);
|
autoHeight(event.target);
|
||||||
}}
|
}}
|
||||||
onKeyPress={(event: any) => {
|
onKeyPress={(event: any) => {
|
||||||
@@ -223,8 +223,6 @@ const LongInput = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InputField = (props: {
|
const InputField = (props: {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
field:
|
field:
|
||||||
| "apiKey"
|
| "apiKey"
|
||||||
| "apiEndpoint"
|
| "apiEndpoint"
|
||||||
@@ -236,6 +234,8 @@ const InputField = (props: {
|
|||||||
| "image_gen_key";
|
| "image_gen_key";
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
const [hideInput, setHideInput] = useState(true);
|
const [hideInput, setHideInput] = useState(true);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -261,10 +261,10 @@ const InputField = (props: {
|
|||||||
<div className="flex w-full items-center space-x-2">
|
<div className="flex w-full items-center space-x-2">
|
||||||
<Input
|
<Input
|
||||||
type={hideInput ? "password" : "text"}
|
type={hideInput ? "password" : "text"}
|
||||||
value={props.chatStore[props.field]}
|
value={ctx.chatStore[props.field]}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
props.chatStore[props.field] = event.target.value;
|
ctx.chatStore[props.field] = event.target.value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -285,30 +285,30 @@ const InputField = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Slicer = (props: {
|
const Slicer = (props: {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
field: "temperature" | "top_p" | "tts_speed";
|
field: "temperature" | "top_p" | "tts_speed";
|
||||||
help: string;
|
help: string;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
|
const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
|
||||||
`${props.field}_enabled` as any;
|
`${props.field}_enabled` as any;
|
||||||
|
|
||||||
const enabled = props.chatStore[enable_filed_name];
|
const enabled = ctx.chatStore[enable_filed_name];
|
||||||
|
|
||||||
if (enabled === null || enabled === undefined) {
|
if (enabled === null || enabled === undefined) {
|
||||||
if (props.field === "temperature") {
|
if (props.field === "temperature") {
|
||||||
props.chatStore[enable_filed_name] = true;
|
ctx.chatStore[enable_filed_name] = true;
|
||||||
}
|
}
|
||||||
if (props.field === "top_p") {
|
if (props.field === "top_p") {
|
||||||
props.chatStore[enable_filed_name] = false;
|
ctx.chatStore[enable_filed_name] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setEnabled = (state: boolean) => {
|
const setEnabled = (state: boolean) => {
|
||||||
props.chatStore[enable_filed_name] = state;
|
ctx.chatStore[enable_filed_name] = state;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -329,10 +329,10 @@ const Slicer = (props: {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={props.chatStore[enable_filed_name]}
|
checked={ctx.chatStore[enable_filed_name]}
|
||||||
onCheckedChange={(checked: boolean) => setEnabled(!!checked)}
|
onCheckedChange={(checked: boolean) => setEnabled(!!checked)}
|
||||||
/>
|
/>
|
||||||
{!props.chatStore[enable_filed_name] && (
|
{!ctx.chatStore[enable_filed_name] && (
|
||||||
<span className="text-xs text-muted-foreground">disabled</span>
|
<span className="text-xs text-muted-foreground">disabled</span>
|
||||||
)}
|
)}
|
||||||
</Label>
|
</Label>
|
||||||
@@ -345,10 +345,10 @@ const Slicer = (props: {
|
|||||||
min={props.min}
|
min={props.min}
|
||||||
max={props.max}
|
max={props.max}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
value={[props.chatStore[props.field]]}
|
value={[ctx.chatStore[props.field]]}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
props.chatStore[props.field] = value[0];
|
ctx.chatStore[props.field] = value[0];
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -356,11 +356,11 @@ const Slicer = (props: {
|
|||||||
type="number"
|
type="number"
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
className="w-24"
|
className="w-24"
|
||||||
value={props.chatStore[props.field]}
|
value={ctx.chatStore[props.field]}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = parseFloat(e.target.value);
|
const value = parseFloat(e.target.value);
|
||||||
props.chatStore[props.field] = value;
|
ctx.chatStore[props.field] = value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -370,8 +370,6 @@ const Slicer = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Number = (props: {
|
const Number = (props: {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
field:
|
field:
|
||||||
| "totalTokens"
|
| "totalTokens"
|
||||||
| "maxTokens"
|
| "maxTokens"
|
||||||
@@ -383,6 +381,8 @@ const Number = (props: {
|
|||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="flex items-center gap-2">
|
<Label className="flex items-center gap-2">
|
||||||
@@ -404,12 +404,12 @@ const Number = (props: {
|
|||||||
|
|
||||||
{props.field === "maxGenTokens" && (
|
{props.field === "maxGenTokens" && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={props.chatStore.maxGenTokens_enabled}
|
checked={ctx.chatStore.maxGenTokens_enabled}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
const newChatStore = { ...props.chatStore };
|
const newChatStore = { ...ctx.chatStore };
|
||||||
newChatStore.maxGenTokens_enabled =
|
newChatStore.maxGenTokens_enabled =
|
||||||
!newChatStore.maxGenTokens_enabled;
|
!newChatStore.maxGenTokens_enabled;
|
||||||
props.setChatStore({ ...newChatStore });
|
ctx.setChatStore({ ...newChatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -419,15 +419,14 @@ const Number = (props: {
|
|||||||
type="number"
|
type="number"
|
||||||
readOnly={props.readOnly}
|
readOnly={props.readOnly}
|
||||||
disabled={
|
disabled={
|
||||||
props.field === "maxGenTokens" &&
|
props.field === "maxGenTokens" && !ctx.chatStore.maxGenTokens_enabled
|
||||||
!props.chatStore.maxGenTokens_enabled
|
|
||||||
}
|
}
|
||||||
value={props.chatStore[props.field]}
|
value={ctx.chatStore[props.field]}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
let newNumber = parseFloat(event.target.value);
|
let newNumber = parseFloat(event.target.value);
|
||||||
if (newNumber < 0) newNumber = 0;
|
if (newNumber < 0) newNumber = 0;
|
||||||
props.chatStore[props.field] = newNumber;
|
ctx.chatStore[props.field] = newNumber;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -435,20 +434,21 @@ const Number = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Choice = (props: {
|
const Choice = (props: {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
|
field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${props.field}-checkbox`}
|
id={`${props.field}-checkbox`}
|
||||||
checked={props.chatStore[props.field]}
|
checked={ctx.chatStore[props.field]}
|
||||||
onCheckedChange={(checked: boolean) => {
|
onCheckedChange={(checked: boolean) => {
|
||||||
props.chatStore[props.field] = checked;
|
ctx.chatStore[props.field] = checked;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -475,37 +475,21 @@ const Choice = (props: {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (props: {
|
export default (props: { setShow: Dispatch<boolean> }) => {
|
||||||
chatStore: ChatStore;
|
const ctx = useContext(AppContext);
|
||||||
setChatStore: (cs: ChatStore) => void;
|
if (ctx === null) return <></>;
|
||||||
setShow: Dispatch<boolean>;
|
|
||||||
selectedChatStoreIndex: number;
|
|
||||||
templates: TemplateChatStore[];
|
|
||||||
setTemplates: (templates: TemplateChatStore[]) => void;
|
|
||||||
templateAPIs: TemplateAPI[];
|
|
||||||
setTemplateAPIs: (templateAPIs: TemplateAPI[]) => void;
|
|
||||||
templateAPIsWhisper: TemplateAPI[];
|
|
||||||
setTemplateAPIsWhisper: (templateAPIs: TemplateAPI[]) => void;
|
|
||||||
templateAPIsTTS: TemplateAPI[];
|
|
||||||
setTemplateAPIsTTS: (templateAPIs: TemplateAPI[]) => void;
|
|
||||||
templateAPIsImageGen: TemplateAPI[];
|
|
||||||
setTemplateAPIsImageGen: (templateAPIs: TemplateAPI[]) => void;
|
|
||||||
templateTools: TemplateTools[];
|
|
||||||
setTemplateTools: (templateTools: TemplateTools[]) => void;
|
|
||||||
}) => {
|
|
||||||
let link =
|
let link =
|
||||||
location.protocol +
|
location.protocol +
|
||||||
"//" +
|
"//" +
|
||||||
location.host +
|
location.host +
|
||||||
location.pathname +
|
location.pathname +
|
||||||
`?key=${encodeURIComponent(
|
`?key=${encodeURIComponent(ctx.chatStore.apiKey)}&api=${encodeURIComponent(
|
||||||
props.chatStore.apiKey
|
ctx.chatStore.apiEndpoint
|
||||||
)}&api=${encodeURIComponent(props.chatStore.apiEndpoint)}&mode=${
|
)}&mode=${ctx.chatStore.streamMode ? "stream" : "fetch"}&model=${
|
||||||
props.chatStore.streamMode ? "stream" : "fetch"
|
ctx.chatStore.model
|
||||||
}&model=${props.chatStore.model}&sys=${encodeURIComponent(
|
}&sys=${encodeURIComponent(ctx.chatStore.systemMessageContent)}`;
|
||||||
props.chatStore.systemMessageContent
|
if (ctx.chatStore.develop_mode) {
|
||||||
)}`;
|
|
||||||
if (props.chatStore.develop_mode) {
|
|
||||||
link = link + `&dev=true`;
|
link = link + `&dev=true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +546,7 @@ export default (props: {
|
|||||||
$ USD
|
$ USD
|
||||||
</span>
|
</span>
|
||||||
<span className="text-lg font-bold leading-none sm:text-3xl">
|
<span className="text-lg font-bold leading-none sm:text-3xl">
|
||||||
{props.chatStore.cost?.toFixed(4)}
|
{ctx.chatStore.cost?.toFixed(4)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -583,7 +567,7 @@ export default (props: {
|
|||||||
/>
|
/>
|
||||||
<span className="pt-1">
|
<span className="pt-1">
|
||||||
JSON Check:{" "}
|
JSON Check:{" "}
|
||||||
{isVailedJSON(props.chatStore.toolsString) ? (
|
{isVailedJSON(ctx.chatStore.toolsString) ? (
|
||||||
<CheckIcon className="inline w-3 h-3" />
|
<CheckIcon className="inline w-3 h-3" />
|
||||||
) : (
|
) : (
|
||||||
<BanIcon className="inline w-3 h-3" />
|
<BanIcon className="inline w-3 h-3" />
|
||||||
@@ -591,7 +575,7 @@ export default (props: {
|
|||||||
</span>
|
</span>
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="flex justify-evenly flex-wrap">
|
<div className="flex justify-evenly flex-wrap">
|
||||||
{props.chatStore.toolsString.trim() && (
|
{ctx.chatStore.toolsString.trim() && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const name = prompt(
|
const name = prompt(
|
||||||
@@ -603,10 +587,10 @@ export default (props: {
|
|||||||
}
|
}
|
||||||
const newToolsTmp: TemplateTools = {
|
const newToolsTmp: TemplateTools = {
|
||||||
name,
|
name,
|
||||||
toolsString: props.chatStore.toolsString,
|
toolsString: ctx.chatStore.toolsString,
|
||||||
};
|
};
|
||||||
props.templateTools.push(newToolsTmp);
|
ctx.templateTools.push(newToolsTmp);
|
||||||
props.setTemplateTools([...props.templateTools]);
|
ctx.setTemplateTools([...ctx.templateTools]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Tr(`Save Tools`)}
|
{Tr(`Save Tools`)}
|
||||||
@@ -710,12 +694,12 @@ export default (props: {
|
|||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.chatStore.history =
|
ctx.chatStore.history =
|
||||||
props.chatStore.history.filter(
|
ctx.chatStore.history.filter(
|
||||||
(msg) => msg.example && !msg.hide
|
(msg) => msg.example && !msg.hide
|
||||||
);
|
);
|
||||||
props.chatStore.postBeginIndex = 0;
|
ctx.chatStore.postBeginIndex = 0;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Yes, clear all history
|
Yes, clear all history
|
||||||
@@ -731,14 +715,14 @@ export default (props: {
|
|||||||
let dataStr =
|
let dataStr =
|
||||||
"data:text/json;charset=utf-8," +
|
"data:text/json;charset=utf-8," +
|
||||||
encodeURIComponent(
|
encodeURIComponent(
|
||||||
JSON.stringify(props.chatStore, null, "\t")
|
JSON.stringify(ctx.chatStore, null, "\t")
|
||||||
);
|
);
|
||||||
let downloadAnchorNode =
|
let downloadAnchorNode =
|
||||||
document.createElement("a");
|
document.createElement("a");
|
||||||
downloadAnchorNode.setAttribute("href", dataStr);
|
downloadAnchorNode.setAttribute("href", dataStr);
|
||||||
downloadAnchorNode.setAttribute(
|
downloadAnchorNode.setAttribute(
|
||||||
"download",
|
"download",
|
||||||
`chatgpt-api-web-${props.selectedChatStoreIndex}.json`
|
`chatgpt-api-web-${ctx.selectedChatIndex}.json`
|
||||||
);
|
);
|
||||||
document.body.appendChild(downloadAnchorNode);
|
document.body.appendChild(downloadAnchorNode);
|
||||||
downloadAnchorNode.click();
|
downloadAnchorNode.click();
|
||||||
@@ -760,7 +744,7 @@ export default (props: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tmp: ChatStore = structuredClone(
|
const tmp: ChatStore = structuredClone(
|
||||||
props.chatStore
|
ctx.chatStore
|
||||||
);
|
);
|
||||||
tmp.history = tmp.history.filter((h) => h.example);
|
tmp.history = tmp.history.filter((h) => h.example);
|
||||||
tmp.apiEndpoint = "";
|
tmp.apiEndpoint = "";
|
||||||
@@ -773,8 +757,8 @@ export default (props: {
|
|||||||
tmp.image_gen_key = "";
|
tmp.image_gen_key = "";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
tmp.name = name;
|
tmp.name = name;
|
||||||
props.templates.push(tmp as TemplateChatStore);
|
ctx.templates.push(tmp as TemplateChatStore);
|
||||||
props.setTemplates([...props.templates]);
|
ctx.setTemplates([...ctx.templates]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Tr("As template")}
|
{Tr("As template")}
|
||||||
@@ -826,7 +810,7 @@ export default (props: {
|
|||||||
langCode
|
langCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
props.setChatStore({ ...newChatStore });
|
ctx.setChatStore({ ...newChatStore });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(
|
alert(
|
||||||
tr(
|
tr(
|
||||||
@@ -868,10 +852,10 @@ export default (props: {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Chat API"
|
label="Chat API"
|
||||||
endpoint={props.chatStore.apiEndpoint}
|
endpoint={ctx.chatStore.apiEndpoint}
|
||||||
APIkey={props.chatStore.apiKey}
|
APIkey={ctx.chatStore.apiKey}
|
||||||
tmps={props.templateAPIs}
|
tmps={ctx.templateAPIs}
|
||||||
setTmps={props.setTemplateAPIs}
|
setTmps={ctx.setTemplateAPIs}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -973,10 +957,10 @@ export default (props: {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Whisper API"
|
label="Whisper API"
|
||||||
endpoint={props.chatStore.whisper_api}
|
endpoint={ctx.chatStore.whisper_api}
|
||||||
APIkey={props.chatStore.whisper_key}
|
APIkey={ctx.chatStore.whisper_key}
|
||||||
tmps={props.templateAPIsWhisper}
|
tmps={ctx.templateAPIsWhisper}
|
||||||
setTmps={props.setTemplateAPIsWhisper}
|
setTmps={ctx.setTemplateAPIsWhisper}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1006,10 +990,10 @@ export default (props: {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="TTS API"
|
label="TTS API"
|
||||||
endpoint={props.chatStore.tts_api}
|
endpoint={ctx.chatStore.tts_api}
|
||||||
APIkey={props.chatStore.tts_key}
|
APIkey={ctx.chatStore.tts_key}
|
||||||
tmps={props.templateAPIsTTS}
|
tmps={ctx.templateAPIsTTS}
|
||||||
setTmps={props.setTemplateAPIsTTS}
|
setTmps={ctx.setTemplateAPIsTTS}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1034,10 +1018,10 @@ export default (props: {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
value={props.chatStore.tts_voice}
|
value={ctx.chatStore.tts_voice}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
props.chatStore.tts_voice = value;
|
ctx.chatStore.tts_voice = value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -1084,10 +1068,10 @@ export default (props: {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
value={props.chatStore.tts_format}
|
value={ctx.chatStore.tts_format}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
props.chatStore.tts_format = value;
|
ctx.chatStore.tts_format = value;
|
||||||
props.setChatStore({ ...props.chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -1131,10 +1115,10 @@ export default (props: {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Image Gen API"
|
label="Image Gen API"
|
||||||
endpoint={props.chatStore.image_gen_api}
|
endpoint={ctx.chatStore.image_gen_api}
|
||||||
APIkey={props.chatStore.image_gen_key}
|
APIkey={ctx.chatStore.image_gen_key}
|
||||||
tmps={props.templateAPIsImageGen}
|
tmps={ctx.templateAPIsImageGen}
|
||||||
setTmps={props.setTemplateAPIsImageGen}
|
setTmps={ctx.setTemplateAPIsImageGen}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1144,7 +1128,7 @@ export default (props: {
|
|||||||
<div className="pt-4 space-y-2">
|
<div className="pt-4 space-y-2">
|
||||||
<p className="text-sm text-muted-foreground text-center">
|
<p className="text-sm text-muted-foreground text-center">
|
||||||
chatgpt-api-web ChatStore {Tr("Version")}{" "}
|
chatgpt-api-web ChatStore {Tr("Version")}{" "}
|
||||||
{props.chatStore.chatgpt_api_web_version}
|
{ctx.chatStore.chatgpt_api_web_version}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-muted-foreground text-center">
|
<p className="text-sm text-muted-foreground text-center">
|
||||||
{Tr("Documents and source code are avaliable here")}:{" "}
|
{Tr("Documents and source code are avaliable here")}:{" "}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
import { AppContext } from "@/pages/App";
|
||||||
import { TemplateChatStore } from "@/types/chatstore";
|
import { TemplateChatStore } from "@/types/chatstore";
|
||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { getDefaultParams } from "@/utils/getDefaultParam";
|
import { getDefaultParams } from "@/utils/getDefaultParam";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
const Templates = () => {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
|
const { templates, chatStore, setChatStore, setTemplates } = ctx;
|
||||||
|
|
||||||
const Templates = (props: {
|
|
||||||
templates: TemplateChatStore[];
|
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
setTemplates: (templates: TemplateChatStore[]) => void;
|
|
||||||
}) => {
|
|
||||||
const { templates, chatStore, setChatStore, setTemplates } = props;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{templates.map((t, index) => (
|
{templates.map((t, index) => (
|
||||||
|
|||||||
@@ -13,61 +13,54 @@ import {
|
|||||||
} from "@/components/ui/navigation-menu";
|
} from "@/components/ui/navigation-menu";
|
||||||
import { Button } from "./components/ui/button";
|
import { Button } from "./components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { AppContext } from "./pages/App";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
tmps: TemplateAPI[];
|
|
||||||
setTmps: (tmps: TemplateAPI[]) => void;
|
|
||||||
label: string;
|
label: string;
|
||||||
apiField: string;
|
apiField: string;
|
||||||
keyField: string;
|
keyField: string;
|
||||||
}
|
}
|
||||||
export function ListAPIs({
|
export function ListAPIs({ label, apiField, keyField }: Props) {
|
||||||
tmps,
|
const ctx = useContext(AppContext);
|
||||||
setTmps,
|
if (ctx === null) return <></>;
|
||||||
chatStore,
|
|
||||||
setChatStore,
|
|
||||||
label,
|
|
||||||
apiField,
|
|
||||||
keyField,
|
|
||||||
}: Props) {
|
|
||||||
return (
|
return (
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
<NavigationMenuTrigger>
|
<NavigationMenuTrigger>
|
||||||
{label}{" "}
|
{label}{" "}
|
||||||
<span className="hidden lg:inline">
|
<span className="hidden lg:inline">
|
||||||
{tmps.find(
|
{ctx.templateAPIs.find(
|
||||||
(t) =>
|
(t) =>
|
||||||
chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
||||||
chatStore[keyField as keyof ChatStore] === t.key
|
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
||||||
)?.name &&
|
)?.name &&
|
||||||
`: ${
|
`: ${
|
||||||
tmps.find(
|
ctx.templateAPIs.find(
|
||||||
(t) =>
|
(t) =>
|
||||||
chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
||||||
chatStore[keyField as keyof ChatStore] === t.key
|
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
||||||
)?.name
|
)?.name
|
||||||
}`}
|
}`}
|
||||||
</span>
|
</span>
|
||||||
</NavigationMenuTrigger>
|
</NavigationMenuTrigger>
|
||||||
<NavigationMenuContent>
|
<NavigationMenuContent>
|
||||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||||
{tmps.map((t, index) => (
|
{ctx.templateAPIs.map((t, index) => (
|
||||||
<li>
|
<li>
|
||||||
<NavigationMenuLink asChild>
|
<NavigationMenuLink asChild>
|
||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
chatStore[apiField] = t.endpoint;
|
ctx.chatStore[apiField as keyof ChatStore] = t.endpoint;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
chatStore[keyField] = t.key;
|
ctx.chatStore[keyField] = t.key;
|
||||||
setChatStore({ ...chatStore });
|
ctx.setChatStore({ ...ctx.chatStore });
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
||||||
chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
||||||
chatStore[keyField as keyof ChatStore] === t.key
|
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
||||||
? "bg-accent text-accent-foreground"
|
? "bg-accent text-accent-foreground"
|
||||||
: ""
|
: ""
|
||||||
)}
|
)}
|
||||||
@@ -88,7 +81,7 @@ export function ListAPIs({
|
|||||||
const name = prompt(`Give **${label}** template a name`);
|
const name = prompt(`Give **${label}** template a name`);
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
t.name = name;
|
t.name = name;
|
||||||
setTmps(structuredClone(tmps));
|
ctx.setTemplateAPIs(structuredClone(ctx.templateAPIs));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
@@ -104,8 +97,8 @@ export function ListAPIs({
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tmps.splice(index, 1);
|
ctx.templateAPIs.splice(index, 1);
|
||||||
setTmps(structuredClone(tmps));
|
ctx.setTemplateAPIs(structuredClone(ctx.templateAPIs));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import { useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
|
|
||||||
import { Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
|
import { Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
|
||||||
import { ChatStore, ChatStoreMessage } from "@/types/chatstore";
|
import { ChatStore, ChatStoreMessage } from "@/types/chatstore";
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
MessageSquareOffIcon,
|
MessageSquareOffIcon,
|
||||||
MessageSquarePlusIcon,
|
MessageSquarePlusIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { AppContext } from "./pages/App";
|
||||||
|
|
||||||
export const isVailedJSON = (str: string): boolean => {
|
export const isVailedJSON = (str: string): boolean => {
|
||||||
try {
|
try {
|
||||||
@@ -36,14 +37,12 @@ export const isVailedJSON = (str: string): boolean => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
export default function Message(props: { messageIndex: number }) {
|
||||||
messageIndex: number;
|
const ctx = useContext(AppContext);
|
||||||
chatStore: ChatStore;
|
if (ctx === null) return <></>;
|
||||||
setChatStore: (cs: ChatStore) => void;
|
const { messageIndex } = props;
|
||||||
}
|
const { chatStore, setChatStore } = ctx;
|
||||||
|
|
||||||
export default function Message(props: Props) {
|
|
||||||
const { chatStore, messageIndex, setChatStore } = props;
|
|
||||||
const chat = chatStore.history[messageIndex];
|
const chat = chatStore.history[messageIndex];
|
||||||
const [showEdit, setShowEdit] = useState(false);
|
const [showEdit, setShowEdit] = useState(false);
|
||||||
const [showCopiedHint, setShowCopiedHint] = useState(false);
|
const [showCopiedHint, setShowCopiedHint] = useState(false);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { openDB } from "idb";
|
import { IDBPDatabase, openDB } from "idb";
|
||||||
import { useEffect, useState } from "react";
|
import { createContext, useEffect, useState } from "react";
|
||||||
import "@/global.css";
|
import "@/global.css";
|
||||||
|
|
||||||
import { calculate_token_length } from "@/chatgpt";
|
import { calculate_token_length } from "@/chatgpt";
|
||||||
@@ -14,6 +14,43 @@ import { STORAGE_NAME, STORAGE_NAME_SELECTED } from "@/const";
|
|||||||
import { upgrade } from "@/indexedDB/upgrade";
|
import { upgrade } from "@/indexedDB/upgrade";
|
||||||
import { getTotalCost } from "@/utils/totalCost";
|
import { getTotalCost } from "@/utils/totalCost";
|
||||||
|
|
||||||
|
import {
|
||||||
|
STORAGE_NAME_TEMPLATE,
|
||||||
|
STORAGE_NAME_TEMPLATE_API,
|
||||||
|
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
|
||||||
|
STORAGE_NAME_TEMPLATE_API_TTS,
|
||||||
|
STORAGE_NAME_TEMPLATE_API_WHISPER,
|
||||||
|
STORAGE_NAME_TEMPLATE_TOOLS,
|
||||||
|
} from "@/const";
|
||||||
|
import {
|
||||||
|
ChatStoreMessage,
|
||||||
|
TemplateChatStore,
|
||||||
|
TemplateAPI,
|
||||||
|
TemplateTools,
|
||||||
|
} from "../types/chatstore";
|
||||||
|
|
||||||
|
interface AppContextType {
|
||||||
|
db: Promise<IDBPDatabase<ChatStore>>;
|
||||||
|
chatStore: ChatStore;
|
||||||
|
setChatStore: (cs: ChatStore) => void;
|
||||||
|
selectedChatIndex: number;
|
||||||
|
setSelectedChatIndex: (i: number) => void;
|
||||||
|
templates: TemplateChatStore[];
|
||||||
|
setTemplates: (t: TemplateChatStore[]) => void;
|
||||||
|
templateAPIs: TemplateAPI[];
|
||||||
|
setTemplateAPIs: (t: TemplateAPI[]) => void;
|
||||||
|
templateAPIsWhisper: TemplateAPI[];
|
||||||
|
setTemplateAPIsWhisper: (t: TemplateAPI[]) => void;
|
||||||
|
templateAPIsTTS: TemplateAPI[];
|
||||||
|
setTemplateAPIsTTS: (t: TemplateAPI[]) => void;
|
||||||
|
templateAPIsImageGen: TemplateAPI[];
|
||||||
|
setTemplateAPIsImageGen: (t: TemplateAPI[]) => void;
|
||||||
|
templateTools: TemplateTools[];
|
||||||
|
setTemplateTools: (t: TemplateTools[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppContext = createContext<AppContextType | null>(null);
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
@@ -247,6 +284,77 @@ export function App() {
|
|||||||
run();
|
run();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [templates, _setTemplates] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE) || "[]"
|
||||||
|
) as TemplateChatStore[]
|
||||||
|
);
|
||||||
|
const [templateAPIs, _setTemplateAPIs] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE_API) || "[]"
|
||||||
|
) as TemplateAPI[]
|
||||||
|
);
|
||||||
|
const [templateAPIsWhisper, _setTemplateAPIsWhisper] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_WHISPER) || "[]"
|
||||||
|
) as TemplateAPI[]
|
||||||
|
);
|
||||||
|
const [templateAPIsTTS, _setTemplateAPIsTTS] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_TTS) || "[]"
|
||||||
|
) as TemplateAPI[]
|
||||||
|
);
|
||||||
|
const [templateAPIsImageGen, _setTemplateAPIsImageGen] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_IMAGE_GEN) || "[]"
|
||||||
|
) as TemplateAPI[]
|
||||||
|
);
|
||||||
|
const [templateTools, _setTemplateTools] = useState(
|
||||||
|
JSON.parse(
|
||||||
|
localStorage.getItem(STORAGE_NAME_TEMPLATE_TOOLS) || "[]"
|
||||||
|
) as TemplateTools[]
|
||||||
|
);
|
||||||
|
const setTemplates = (templates: TemplateChatStore[]) => {
|
||||||
|
localStorage.setItem(STORAGE_NAME_TEMPLATE, JSON.stringify(templates));
|
||||||
|
_setTemplates(templates);
|
||||||
|
};
|
||||||
|
const setTemplateAPIs = (templateAPIs: TemplateAPI[]) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_NAME_TEMPLATE_API,
|
||||||
|
JSON.stringify(templateAPIs)
|
||||||
|
);
|
||||||
|
_setTemplateAPIs(templateAPIs);
|
||||||
|
};
|
||||||
|
const setTemplateAPIsWhisper = (templateAPIWhisper: TemplateAPI[]) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_NAME_TEMPLATE_API_WHISPER,
|
||||||
|
JSON.stringify(templateAPIWhisper)
|
||||||
|
);
|
||||||
|
_setTemplateAPIsWhisper(templateAPIWhisper);
|
||||||
|
};
|
||||||
|
const setTemplateAPIsTTS = (templateAPITTS: TemplateAPI[]) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_NAME_TEMPLATE_API_TTS,
|
||||||
|
JSON.stringify(templateAPITTS)
|
||||||
|
);
|
||||||
|
_setTemplateAPIsTTS(templateAPITTS);
|
||||||
|
};
|
||||||
|
const setTemplateAPIsImageGen = (templateAPIImageGen: TemplateAPI[]) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
|
||||||
|
JSON.stringify(templateAPIImageGen)
|
||||||
|
);
|
||||||
|
_setTemplateAPIsImageGen(templateAPIImageGen);
|
||||||
|
};
|
||||||
|
const setTemplateTools = (templateTools: TemplateTools[]) => {
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_NAME_TEMPLATE_TOOLS,
|
||||||
|
JSON.stringify(templateTools)
|
||||||
|
);
|
||||||
|
_setTemplateTools(templateTools);
|
||||||
|
};
|
||||||
|
console.log("[PERFORMANCE!] reading localStorage");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
@@ -410,13 +518,29 @@ export function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<ChatBOX
|
<AppContext.Provider
|
||||||
db={db}
|
value={{
|
||||||
chatStore={chatStore}
|
db,
|
||||||
setChatStore={setChatStore}
|
chatStore,
|
||||||
selectedChatIndex={selectedChatIndex}
|
setChatStore,
|
||||||
setSelectedChatIndex={setSelectedChatIndex}
|
selectedChatIndex,
|
||||||
/>
|
setSelectedChatIndex,
|
||||||
|
templates,
|
||||||
|
setTemplates,
|
||||||
|
templateAPIs,
|
||||||
|
setTemplateAPIs,
|
||||||
|
templateAPIsWhisper,
|
||||||
|
setTemplateAPIsWhisper,
|
||||||
|
templateAPIsTTS,
|
||||||
|
setTemplateAPIsTTS,
|
||||||
|
templateAPIsImageGen,
|
||||||
|
setTemplateAPIsImageGen,
|
||||||
|
templateTools,
|
||||||
|
setTemplateTools,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChatBOX />
|
||||||
|
</AppContext.Provider>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import { IDBPDatabase } from "idb";
|
import { IDBPDatabase } from "idb";
|
||||||
import { useRef } from "react";
|
import { useContext, useRef } from "react";
|
||||||
import { useEffect, useState, Dispatch } from "react";
|
import { useEffect, useState, Dispatch } from "react";
|
||||||
import { Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
|
import { Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
|
||||||
import {
|
|
||||||
STORAGE_NAME_TEMPLATE,
|
|
||||||
STORAGE_NAME_TEMPLATE_API,
|
|
||||||
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
|
|
||||||
STORAGE_NAME_TEMPLATE_API_TTS,
|
|
||||||
STORAGE_NAME_TEMPLATE_API_WHISPER,
|
|
||||||
STORAGE_NAME_TEMPLATE_TOOLS,
|
|
||||||
} from "@/const";
|
|
||||||
import { addTotalCost, getTotalCost } from "@/utils/totalCost";
|
import { addTotalCost, getTotalCost } from "@/utils/totalCost";
|
||||||
import ChatGPT, {
|
import ChatGPT, {
|
||||||
calculate_token_length,
|
calculate_token_length,
|
||||||
@@ -79,14 +71,18 @@ import {
|
|||||||
navigationMenuTriggerStyle,
|
navigationMenuTriggerStyle,
|
||||||
} from "@/components/ui/navigation-menu";
|
} from "@/components/ui/navigation-menu";
|
||||||
|
|
||||||
export default function ChatBOX(props: {
|
import { AppContext } from "./App";
|
||||||
db: Promise<IDBPDatabase<ChatStore>>;
|
|
||||||
chatStore: ChatStore;
|
export default function ChatBOX() {
|
||||||
setChatStore: (cs: ChatStore) => void;
|
const ctx = useContext(AppContext);
|
||||||
selectedChatIndex: number;
|
if (ctx === null) return <></>;
|
||||||
setSelectedChatIndex: Dispatch<number>;
|
const {
|
||||||
}) {
|
db,
|
||||||
const { chatStore, setChatStore } = props;
|
chatStore,
|
||||||
|
setChatStore,
|
||||||
|
selectedChatIndex,
|
||||||
|
setSelectedChatIndex,
|
||||||
|
} = ctx;
|
||||||
// prevent error
|
// prevent error
|
||||||
if (chatStore === undefined) return <div></div>;
|
if (chatStore === undefined) return <div></div>;
|
||||||
const [inputMsg, setInputMsg] = useState("");
|
const [inputMsg, setInputMsg] = useState("");
|
||||||
@@ -357,7 +353,7 @@ export default function ChatBOX(props: {
|
|||||||
alert(error);
|
alert(error);
|
||||||
} finally {
|
} finally {
|
||||||
setShowGenerating(false);
|
setShowGenerating(false);
|
||||||
props.setSelectedChatIndex(props.selectedChatIndex);
|
setSelectedChatIndex(selectedChatIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -401,102 +397,13 @@ export default function ChatBOX(props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
|
|
||||||
const [templates, _setTemplates] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE) || "[]"
|
|
||||||
) as TemplateChatStore[]
|
|
||||||
);
|
|
||||||
const [templateAPIs, _setTemplateAPIs] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE_API) || "[]"
|
|
||||||
) as TemplateAPI[]
|
|
||||||
);
|
|
||||||
const [templateAPIsWhisper, _setTemplateAPIsWhisper] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_WHISPER) || "[]"
|
|
||||||
) as TemplateAPI[]
|
|
||||||
);
|
|
||||||
const [templateAPIsTTS, _setTemplateAPIsTTS] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_TTS) || "[]"
|
|
||||||
) as TemplateAPI[]
|
|
||||||
);
|
|
||||||
const [templateAPIsImageGen, _setTemplateAPIsImageGen] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_IMAGE_GEN) || "[]"
|
|
||||||
) as TemplateAPI[]
|
|
||||||
);
|
|
||||||
const [toolsTemplates, _setToolsTemplates] = useState(
|
|
||||||
JSON.parse(
|
|
||||||
localStorage.getItem(STORAGE_NAME_TEMPLATE_TOOLS) || "[]"
|
|
||||||
) as TemplateTools[]
|
|
||||||
);
|
|
||||||
const setTemplates = (templates: TemplateChatStore[]) => {
|
|
||||||
localStorage.setItem(STORAGE_NAME_TEMPLATE, JSON.stringify(templates));
|
|
||||||
_setTemplates(templates);
|
|
||||||
};
|
|
||||||
const setTemplateAPIs = (templateAPIs: TemplateAPI[]) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
STORAGE_NAME_TEMPLATE_API,
|
|
||||||
JSON.stringify(templateAPIs)
|
|
||||||
);
|
|
||||||
_setTemplateAPIs(templateAPIs);
|
|
||||||
};
|
|
||||||
const setTemplateAPIsWhisper = (templateAPIWhisper: TemplateAPI[]) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
STORAGE_NAME_TEMPLATE_API_WHISPER,
|
|
||||||
JSON.stringify(templateAPIWhisper)
|
|
||||||
);
|
|
||||||
_setTemplateAPIsWhisper(templateAPIWhisper);
|
|
||||||
};
|
|
||||||
const setTemplateAPIsTTS = (templateAPITTS: TemplateAPI[]) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
STORAGE_NAME_TEMPLATE_API_TTS,
|
|
||||||
JSON.stringify(templateAPITTS)
|
|
||||||
);
|
|
||||||
_setTemplateAPIsTTS(templateAPITTS);
|
|
||||||
};
|
|
||||||
const setTemplateAPIsImageGen = (templateAPIImageGen: TemplateAPI[]) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
|
|
||||||
JSON.stringify(templateAPIImageGen)
|
|
||||||
);
|
|
||||||
_setTemplateAPIsImageGen(templateAPIImageGen);
|
|
||||||
};
|
|
||||||
const setTemplateTools = (templateTools: TemplateTools[]) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
STORAGE_NAME_TEMPLATE_TOOLS,
|
|
||||||
JSON.stringify(templateTools)
|
|
||||||
);
|
|
||||||
_setToolsTemplates(templateTools);
|
|
||||||
};
|
|
||||||
const userInputRef = useRef<HTMLInputElement>(null);
|
const userInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col p-2 gap-2 w-full">
|
<div className="flex flex-col p-2 gap-2 w-full">
|
||||||
<div className="flex items-center gap-2 justify-between">
|
<div className="flex items-center gap-2 justify-between">
|
||||||
{true && (
|
{true && <Settings setShow={setShowSettings} />}
|
||||||
<Settings
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
setShow={setShowSettings}
|
|
||||||
selectedChatStoreIndex={props.selectedChatIndex}
|
|
||||||
templates={templates}
|
|
||||||
setTemplates={setTemplates}
|
|
||||||
templateAPIs={templateAPIs}
|
|
||||||
setTemplateAPIs={setTemplateAPIs}
|
|
||||||
templateAPIsWhisper={templateAPIsWhisper}
|
|
||||||
setTemplateAPIsWhisper={setTemplateAPIsWhisper}
|
|
||||||
templateAPIsTTS={templateAPIsTTS}
|
|
||||||
setTemplateAPIsTTS={setTemplateAPIsTTS}
|
|
||||||
templateAPIsImageGen={templateAPIsImageGen}
|
|
||||||
setTemplateAPIsImageGen={setTemplateAPIsImageGen}
|
|
||||||
templateTools={toolsTemplates}
|
|
||||||
setTemplateTools={setTemplateTools}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -505,15 +412,7 @@ export default function ChatBOX(props: {
|
|||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{showSearch && (
|
{showSearch && <Search show={showSearch} setShow={setShowSearch} />}
|
||||||
<Search
|
|
||||||
setSelectedChatIndex={props.setSelectedChatIndex}
|
|
||||||
db={props.db}
|
|
||||||
chatStore={chatStore}
|
|
||||||
show={showSearch}
|
|
||||||
setShow={setShowSearch}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!chatStore.apiKey && (
|
{!chatStore.apiKey && (
|
||||||
<Alert>
|
<Alert>
|
||||||
@@ -535,54 +434,30 @@ export default function ChatBOX(props: {
|
|||||||
)}
|
)}
|
||||||
<NavigationMenu>
|
<NavigationMenu>
|
||||||
<NavigationMenuList>
|
<NavigationMenuList>
|
||||||
{templateAPIs.length > 0 && (
|
{ctx.templateAPIs.length > 0 && (
|
||||||
<ListAPIs
|
<ListAPIs label="API" apiField="apiEndpoint" keyField="apiKey" />
|
||||||
label="API"
|
|
||||||
tmps={templateAPIs}
|
|
||||||
setTmps={setTemplateAPIs}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
apiField="apiEndpoint"
|
|
||||||
keyField="apiKey"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{templateAPIsWhisper.length > 0 && (
|
{ctx.templateAPIsWhisper.length > 0 && (
|
||||||
<ListAPIs
|
<ListAPIs
|
||||||
label="Whisper API"
|
label="Whisper API"
|
||||||
tmps={templateAPIsWhisper}
|
|
||||||
setTmps={setTemplateAPIsWhisper}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
apiField="whisper_api"
|
apiField="whisper_api"
|
||||||
keyField="whisper_key"
|
keyField="whisper_key"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{templateAPIsTTS.length > 0 && (
|
{ctx.templateAPIsTTS.length > 0 && (
|
||||||
<ListAPIs
|
<ListAPIs label="TTS API" apiField="tts_api" keyField="tts_key" />
|
||||||
label="TTS API"
|
|
||||||
tmps={templateAPIsTTS}
|
|
||||||
setTmps={setTemplateAPIsTTS}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
apiField="tts_api"
|
|
||||||
keyField="tts_key"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{templateAPIsImageGen.length > 0 && (
|
{ctx.templateAPIsImageGen.length > 0 && (
|
||||||
<ListAPIs
|
<ListAPIs
|
||||||
label="Image Gen API"
|
label="Image Gen API"
|
||||||
tmps={templateAPIsImageGen}
|
|
||||||
setTmps={setTemplateAPIsImageGen}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
apiField="image_gen_api"
|
apiField="image_gen_api"
|
||||||
keyField="image_gen_key"
|
keyField="image_gen_key"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{toolsTemplates.length > 0 && (
|
{ctx.templateTools.length > 0 && (
|
||||||
<ListToolsTempaltes
|
<ListToolsTempaltes
|
||||||
templateTools={toolsTemplates}
|
templateTools={ctx.templateTools}
|
||||||
setTemplateTools={setTemplateTools}
|
setTemplateTools={ctx.setTemplateTools}
|
||||||
chatStore={chatStore}
|
chatStore={chatStore}
|
||||||
setChatStore={setChatStore}
|
setChatStore={setChatStore}
|
||||||
/>
|
/>
|
||||||
@@ -611,12 +486,7 @@ export default function ChatBOX(props: {
|
|||||||
</h2>
|
</h2>
|
||||||
<div className="divider"></div>
|
<div className="divider"></div>
|
||||||
<div className="flex flex-wrap">
|
<div className="flex flex-wrap">
|
||||||
<Templates
|
<Templates />
|
||||||
templates={templates}
|
|
||||||
setTemplates={setTemplates}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -675,11 +545,7 @@ export default function ChatBOX(props: {
|
|||||||
</ChatBubble>
|
</ChatBubble>
|
||||||
)}
|
)}
|
||||||
{chatStore.history.map((_, messageIndex) => (
|
{chatStore.history.map((_, messageIndex) => (
|
||||||
<Message
|
<Message messageIndex={messageIndex} />
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
messageIndex={messageIndex}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
{showGenerating && (
|
{showGenerating && (
|
||||||
<ChatBubble variant="received">
|
<ChatBubble variant="received">
|
||||||
@@ -841,8 +707,6 @@ export default function ChatBOX(props: {
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<AddImage
|
<AddImage
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
setShowAddImage={setShowAddImage}
|
setShowAddImage={setShowAddImage}
|
||||||
images={images}
|
images={images}
|
||||||
showAddImage={showAddImage}
|
showAddImage={showAddImage}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IDBPDatabase } from "idb";
|
import { IDBPDatabase } from "idb";
|
||||||
import { useRef, useState, Dispatch } from "react";
|
import { useRef, useState, Dispatch, useContext } from "react";
|
||||||
|
|
||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { MessageDetail } from "./chatgpt";
|
import { MessageDetail } from "./chatgpt";
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from "@/components/ui/pagination";
|
} from "@/components/ui/pagination";
|
||||||
|
|
||||||
import { Input } from "./components/ui/input";
|
import { Input } from "./components/ui/input";
|
||||||
|
import { AppContext } from "./pages/App";
|
||||||
|
|
||||||
interface ChatStoreSearchResult {
|
interface ChatStoreSearchResult {
|
||||||
key: IDBValidKey;
|
key: IDBValidKey;
|
||||||
@@ -33,12 +34,13 @@ interface ChatStoreSearchResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Search(props: {
|
export default function Search(props: {
|
||||||
db: Promise<IDBPDatabase<ChatStore>>;
|
|
||||||
setSelectedChatIndex: Dispatch<number>;
|
|
||||||
chatStore: ChatStore;
|
|
||||||
show: boolean;
|
show: boolean;
|
||||||
setShow: (show: boolean) => void;
|
setShow: (show: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
if (ctx === null) return <></>;
|
||||||
|
const { setSelectedChatIndex, db } = ctx;
|
||||||
|
|
||||||
const [searchResult, setSearchResult] = useState<ChatStoreSearchResult[]>([]);
|
const [searchResult, setSearchResult] = useState<ChatStoreSearchResult[]>([]);
|
||||||
const [searching, setSearching] = useState<boolean>(false);
|
const [searching, setSearching] = useState<boolean>(false);
|
||||||
const [searchingNow, setSearchingNow] = useState<number>(0);
|
const [searchingNow, setSearchingNow] = useState<number>(0);
|
||||||
@@ -76,8 +78,8 @@ export default function Search(props: {
|
|||||||
|
|
||||||
setSearching(true);
|
setSearching(true);
|
||||||
|
|
||||||
const db = await props.db;
|
const idb = await db;
|
||||||
const resultKeys = await db.getAllKeys("chatgpt-api-web");
|
const resultKeys = await idb.getAllKeys("chatgpt-api-web");
|
||||||
|
|
||||||
const result: ChatStoreSearchResult[] = [];
|
const result: ChatStoreSearchResult[] = [];
|
||||||
for (const key of resultKeys) {
|
for (const key of resultKeys) {
|
||||||
@@ -91,7 +93,7 @@ export default function Search(props: {
|
|||||||
);
|
);
|
||||||
if (now !== searchingNow) setSearchingNow(now);
|
if (now !== searchingNow) setSearchingNow(now);
|
||||||
|
|
||||||
const value: ChatStore = await db.get("chatgpt-api-web", key);
|
const value: ChatStore = await idb.get("chatgpt-api-web", key);
|
||||||
|
|
||||||
let preview: string = "";
|
let preview: string = "";
|
||||||
for (const msg of value.history) {
|
for (const msg of value.history) {
|
||||||
@@ -157,7 +159,7 @@ export default function Search(props: {
|
|||||||
className="flex justify-start p-1 m-1 rounded border bg-base-200 cursor-pointer"
|
className="flex justify-start p-1 m-1 rounded border bg-base-200 cursor-pointer"
|
||||||
key={result.key as number}
|
key={result.key as number}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.setSelectedChatIndex(parseInt(result.key.toString()));
|
setSelectedChatIndex(parseInt(result.key.toString()));
|
||||||
props.setShow(false);
|
props.setShow(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user