forked from XiaoMo/ChatGPT-Next-Web
commit
8870e966a6
@ -26,8 +26,11 @@ export async function requestOpenai(req: NextRequest) {
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${apiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
...(process.env.OPENAI_ORG_ID && { "OpenAI-Organization": process.env.OPENAI_ORG_ID }),
|
...(process.env.OPENAI_ORG_ID && {
|
||||||
|
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
|
cache: "no-store",
|
||||||
method: req.method,
|
method: req.method,
|
||||||
body: req.body,
|
body: req.body,
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import { memo, useState, useRef, useEffect, useLayoutEffect } from "react";
|
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
||||||
|
|
||||||
import SendWhiteIcon from "../icons/send-white.svg";
|
import SendWhiteIcon from "../icons/send-white.svg";
|
||||||
import BrainIcon from "../icons/brain.svg";
|
import BrainIcon from "../icons/brain.svg";
|
||||||
@ -64,12 +64,9 @@ import {
|
|||||||
useMaskStore,
|
useMaskStore,
|
||||||
} from "../store/mask";
|
} from "../store/mask";
|
||||||
|
|
||||||
const Markdown = dynamic(
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
async () => memo((await import("./markdown")).Markdown),
|
loading: () => <LoadingIcon />,
|
||||||
{
|
});
|
||||||
loading: () => <LoadingIcon />,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
function exportMessages(messages: Message[], topic: string) {
|
function exportMessages(messages: Message[], topic: string) {
|
||||||
const mdText =
|
const mdText =
|
||||||
|
@ -9,6 +9,7 @@ import { useRef, useState, RefObject, useEffect } from "react";
|
|||||||
import { copyToClipboard } from "../utils";
|
import { copyToClipboard } from "../utils";
|
||||||
|
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
export function PreCode(props: { children: any }) {
|
export function PreCode(props: { children: any }) {
|
||||||
const ref = useRef<HTMLPreElement>(null);
|
const ref = useRef<HTMLPreElement>(null);
|
||||||
@ -29,6 +30,32 @@ export function PreCode(props: { children: any }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _MarkDownContent(props: { content: string }) {
|
||||||
|
return (
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
||||||
|
rehypePlugins={[
|
||||||
|
RehypeKatex,
|
||||||
|
[
|
||||||
|
RehypeHighlight,
|
||||||
|
{
|
||||||
|
detect: false,
|
||||||
|
ignoreMissing: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
components={{
|
||||||
|
pre: PreCode,
|
||||||
|
}}
|
||||||
|
linkTarget={"_blank"}
|
||||||
|
>
|
||||||
|
{props.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MarkdownContent = React.memo(_MarkDownContent);
|
||||||
|
|
||||||
export function Markdown(
|
export function Markdown(
|
||||||
props: {
|
props: {
|
||||||
content: string;
|
content: string;
|
||||||
@ -38,69 +65,53 @@ export function Markdown(
|
|||||||
} & React.DOMAttributes<HTMLDivElement>,
|
} & React.DOMAttributes<HTMLDivElement>,
|
||||||
) {
|
) {
|
||||||
const mdRef = useRef<HTMLDivElement>(null);
|
const mdRef = useRef<HTMLDivElement>(null);
|
||||||
|
const renderedHeight = useRef(0);
|
||||||
|
const inView = useRef(false);
|
||||||
|
|
||||||
const parent = props.parentRef.current;
|
const parent = props.parentRef.current;
|
||||||
const md = mdRef.current;
|
const md = mdRef.current;
|
||||||
const rendered = useRef(true); // disable lazy loading for bad ux
|
|
||||||
const [counter, setCounter] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const checkInView = () => {
|
||||||
// to triggr rerender
|
if (parent && md) {
|
||||||
setCounter(counter + 1);
|
const parentBounds = parent.getBoundingClientRect();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
const twoScreenHeight = Math.max(500, parentBounds.height * 2);
|
||||||
}, [props.loading]);
|
const mdBounds = md.getBoundingClientRect();
|
||||||
|
const isInRange = (x: number) =>
|
||||||
|
x <= parentBounds.bottom + twoScreenHeight &&
|
||||||
|
x >= parentBounds.top - twoScreenHeight;
|
||||||
|
inView.current = isInRange(mdBounds.top) || isInRange(mdBounds.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
const inView =
|
if (inView.current && md) {
|
||||||
rendered.current ||
|
renderedHeight.current = Math.max(
|
||||||
(() => {
|
renderedHeight.current,
|
||||||
if (parent && md) {
|
md.getBoundingClientRect().height,
|
||||||
const parentBounds = parent.getBoundingClientRect();
|
);
|
||||||
const mdBounds = md.getBoundingClientRect();
|
}
|
||||||
const isInRange = (x: number) =>
|
};
|
||||||
x <= parentBounds.bottom && x >= parentBounds.top;
|
|
||||||
const inView = isInRange(mdBounds.top) || isInRange(mdBounds.bottom);
|
|
||||||
|
|
||||||
if (inView) {
|
checkInView();
|
||||||
rendered.current = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inView;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const shouldLoading = props.loading || !inView;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="markdown-body"
|
className="markdown-body"
|
||||||
style={{ fontSize: `${props.fontSize ?? 14}px` }}
|
style={{
|
||||||
|
fontSize: `${props.fontSize ?? 14}px`,
|
||||||
|
height:
|
||||||
|
!inView.current && renderedHeight.current > 0
|
||||||
|
? renderedHeight.current
|
||||||
|
: "auto",
|
||||||
|
}}
|
||||||
ref={mdRef}
|
ref={mdRef}
|
||||||
onContextMenu={props.onContextMenu}
|
onContextMenu={props.onContextMenu}
|
||||||
onDoubleClickCapture={props.onDoubleClickCapture}
|
onDoubleClickCapture={props.onDoubleClickCapture}
|
||||||
>
|
>
|
||||||
{shouldLoading ? (
|
{inView.current &&
|
||||||
<LoadingIcon />
|
(props.loading ? (
|
||||||
) : (
|
<LoadingIcon />
|
||||||
<ReactMarkdown
|
) : (
|
||||||
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
|
<MarkdownContent content={props.content} />
|
||||||
rehypePlugins={[
|
))}
|
||||||
RehypeKatex,
|
|
||||||
[
|
|
||||||
RehypeHighlight,
|
|
||||||
{
|
|
||||||
detect: false,
|
|
||||||
ignoreMissing: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
components={{
|
|
||||||
pre: PreCode,
|
|
||||||
}}
|
|
||||||
linkTarget={"_blank"}
|
|
||||||
>
|
|
||||||
{props.content}
|
|
||||||
</ReactMarkdown>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,20 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-prompt-modal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.edit-prompt-title {
|
||||||
|
max-width: unset;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.edit-prompt-content {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.user-prompt-modal {
|
.user-prompt-modal {
|
||||||
min-height: 40vh;
|
min-height: 40vh;
|
||||||
|
|
||||||
@ -18,47 +32,42 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-prompt-list {
|
.user-prompt-list {
|
||||||
padding: 10px 0;
|
border: var(--border-in-light);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
.user-prompt-item {
|
.user-prompt-item {
|
||||||
margin-bottom: 10px;
|
display: flex;
|
||||||
widows: 100%;
|
justify-content: space-between;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: var(--border-in-light);
|
||||||
|
}
|
||||||
|
|
||||||
.user-prompt-header {
|
.user-prompt-header {
|
||||||
display: flex;
|
max-width: calc(100% - 100px);
|
||||||
widows: 100%;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.user-prompt-title {
|
.user-prompt-title {
|
||||||
flex-grow: 1;
|
font-size: 14px;
|
||||||
max-width: 100%;
|
line-height: 2;
|
||||||
margin-right: 5px;
|
font-weight: bold;
|
||||||
padding: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
|
.user-prompt-content {
|
||||||
.user-prompt-buttons {
|
font-size: 12px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.user-prompt-button {
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-prompt-content {
|
.user-prompt-buttons {
|
||||||
width: 100%;
|
display: flex;
|
||||||
box-sizing: border-box;
|
align-items: center;
|
||||||
padding: 5px;
|
|
||||||
margin-right: 10px;
|
.user-prompt-button {
|
||||||
font-size: 12px;
|
height: 100%;
|
||||||
flex-grow: 1;
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,12 @@ import { useState, useEffect, useMemo, HTMLProps, useRef } from "react";
|
|||||||
import styles from "./settings.module.scss";
|
import styles from "./settings.module.scss";
|
||||||
|
|
||||||
import ResetIcon from "../icons/reload.svg";
|
import ResetIcon from "../icons/reload.svg";
|
||||||
|
import AddIcon from "../icons/add.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
import CloseIcon from "../icons/close.svg";
|
||||||
import CopyIcon from "../icons/copy.svg";
|
import CopyIcon from "../icons/copy.svg";
|
||||||
import ClearIcon from "../icons/clear.svg";
|
import ClearIcon from "../icons/clear.svg";
|
||||||
import EditIcon from "../icons/edit.svg";
|
import EditIcon from "../icons/edit.svg";
|
||||||
|
import EyeIcon from "../icons/eye.svg";
|
||||||
import { Input, List, ListItem, Modal, PasswordInput, Popover } from "./ui-lib";
|
import { Input, List, ListItem, Modal, PasswordInput, Popover } from "./ui-lib";
|
||||||
import { ModelConfigList } from "./model-config";
|
import { ModelConfigList } from "./model-config";
|
||||||
|
|
||||||
@ -30,6 +32,55 @@ import { InputRange } from "./input-range";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Avatar, AvatarPicker } from "./emoji";
|
import { Avatar, AvatarPicker } from "./emoji";
|
||||||
|
|
||||||
|
function EditPromptModal(props: { id: number; onClose: () => void }) {
|
||||||
|
const promptStore = usePromptStore();
|
||||||
|
const prompt = promptStore.get(props.id);
|
||||||
|
|
||||||
|
return prompt ? (
|
||||||
|
<div className="modal-mask">
|
||||||
|
<Modal
|
||||||
|
title={Locale.Settings.Prompt.EditModal.Title}
|
||||||
|
onClose={props.onClose}
|
||||||
|
actions={[
|
||||||
|
<IconButton
|
||||||
|
key=""
|
||||||
|
onClick={props.onClose}
|
||||||
|
text={Locale.UI.Confirm}
|
||||||
|
bordered
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div className={styles["edit-prompt-modal"]}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={prompt.title}
|
||||||
|
readOnly={!prompt.isUser}
|
||||||
|
className={styles["edit-prompt-title"]}
|
||||||
|
onInput={(e) =>
|
||||||
|
promptStore.update(
|
||||||
|
props.id,
|
||||||
|
(prompt) => (prompt.title = e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
<Input
|
||||||
|
value={prompt.content}
|
||||||
|
readOnly={!prompt.isUser}
|
||||||
|
className={styles["edit-prompt-content"]}
|
||||||
|
rows={10}
|
||||||
|
onInput={(e) =>
|
||||||
|
promptStore.update(
|
||||||
|
props.id,
|
||||||
|
(prompt) => (prompt.content = e.currentTarget.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></Input>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
function UserPromptModal(props: { onClose?: () => void }) {
|
function UserPromptModal(props: { onClose?: () => void }) {
|
||||||
const promptStore = usePromptStore();
|
const promptStore = usePromptStore();
|
||||||
const userPrompts = promptStore.getUserPrompts();
|
const userPrompts = promptStore.getUserPrompts();
|
||||||
@ -39,6 +90,8 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
|||||||
const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
|
const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
|
||||||
const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
|
const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
|
||||||
|
|
||||||
|
const [editingPromptId, setEditingPromptId] = useState<number>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchInput.length > 0) {
|
if (searchInput.length > 0) {
|
||||||
const searchResult = SearchService.search(searchInput);
|
const searchResult = SearchService.search(searchInput);
|
||||||
@ -56,8 +109,13 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
|||||||
actions={[
|
actions={[
|
||||||
<IconButton
|
<IconButton
|
||||||
key="add"
|
key="add"
|
||||||
onClick={() => promptStore.add({ title: "", content: "" })}
|
onClick={() =>
|
||||||
icon={<ClearIcon />}
|
promptStore.add({
|
||||||
|
title: "Empty Prompt",
|
||||||
|
content: "Empty Prompt Content",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
icon={<AddIcon />}
|
||||||
bordered
|
bordered
|
||||||
text={Locale.Settings.Prompt.Modal.Add}
|
text={Locale.Settings.Prompt.Modal.Add}
|
||||||
/>,
|
/>,
|
||||||
@ -76,57 +134,51 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
|||||||
{prompts.map((v, _) => (
|
{prompts.map((v, _) => (
|
||||||
<div className={styles["user-prompt-item"]} key={v.id ?? v.title}>
|
<div className={styles["user-prompt-item"]} key={v.id ?? v.title}>
|
||||||
<div className={styles["user-prompt-header"]}>
|
<div className={styles["user-prompt-header"]}>
|
||||||
<input
|
<div className={styles["user-prompt-title"]}>{v.title}</div>
|
||||||
type="text"
|
<div className={styles["user-prompt-content"] + " one-line"}>
|
||||||
className={styles["user-prompt-title"]}
|
{v.content}
|
||||||
value={v.title}
|
|
||||||
readOnly={!v.isUser}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (v.isUser) {
|
|
||||||
promptStore.updateUserPrompts(
|
|
||||||
v.id!,
|
|
||||||
(prompt) => (prompt.title = e.currentTarget.value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
></input>
|
|
||||||
|
|
||||||
<div className={styles["user-prompt-buttons"]}>
|
|
||||||
{v.isUser && (
|
|
||||||
<IconButton
|
|
||||||
icon={<ClearIcon />}
|
|
||||||
bordered
|
|
||||||
className={styles["user-prompt-button"]}
|
|
||||||
onClick={() => promptStore.remove(v.id!)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<IconButton
|
|
||||||
icon={<CopyIcon />}
|
|
||||||
bordered
|
|
||||||
className={styles["user-prompt-button"]}
|
|
||||||
onClick={() => copyToClipboard(v.content)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
|
||||||
rows={2}
|
<div className={styles["user-prompt-buttons"]}>
|
||||||
value={v.content}
|
{v.isUser && (
|
||||||
className={styles["user-prompt-content"]}
|
<IconButton
|
||||||
readOnly={!v.isUser}
|
icon={<ClearIcon />}
|
||||||
onChange={(e) => {
|
className={styles["user-prompt-button"]}
|
||||||
if (v.isUser) {
|
onClick={() => promptStore.remove(v.id!)}
|
||||||
promptStore.updateUserPrompts(
|
/>
|
||||||
v.id!,
|
)}
|
||||||
(prompt) => (prompt.content = e.currentTarget.value),
|
{v.isUser ? (
|
||||||
);
|
<IconButton
|
||||||
}
|
icon={<EditIcon />}
|
||||||
}}
|
className={styles["user-prompt-button"]}
|
||||||
/>
|
onClick={() => setEditingPromptId(v.id)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconButton
|
||||||
|
icon={<EyeIcon />}
|
||||||
|
className={styles["user-prompt-button"]}
|
||||||
|
onClick={() => setEditingPromptId(v.id)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
icon={<CopyIcon />}
|
||||||
|
className={styles["user-prompt-button"]}
|
||||||
|
onClick={() => copyToClipboard(v.content)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{editingPromptId !== undefined && (
|
||||||
|
<EditPromptModal
|
||||||
|
id={editingPromptId!}
|
||||||
|
onClose={() => setEditingPromptId(undefined)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -116,9 +116,12 @@ const cn = {
|
|||||||
Edit: "编辑",
|
Edit: "编辑",
|
||||||
Modal: {
|
Modal: {
|
||||||
Title: "提示词列表",
|
Title: "提示词列表",
|
||||||
Add: "增加一条",
|
Add: "新建",
|
||||||
Search: "搜索提示词",
|
Search: "搜索提示词",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "编辑提示词",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "附带历史消息数",
|
Title: "附带历史消息数",
|
||||||
@ -223,6 +226,14 @@ const cn = {
|
|||||||
SubTitle: "现在开始,与面具背后的灵魂思维碰撞",
|
SubTitle: "现在开始,与面具背后的灵魂思维碰撞",
|
||||||
More: "搜索更多",
|
More: "搜索更多",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "确认",
|
||||||
|
Cancel: "取消",
|
||||||
|
Close: "关闭",
|
||||||
|
Create: "新建",
|
||||||
|
Edit: "编辑",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LocaleType = typeof cn;
|
export type LocaleType = typeof cn;
|
||||||
|
@ -121,6 +121,9 @@ const de: LocaleType = {
|
|||||||
Add: "Add One",
|
Add: "Add One",
|
||||||
Search: "Search Prompts",
|
Search: "Search Prompts",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Edit Prompt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Anzahl der angehängten Nachrichten",
|
Title: "Anzahl der angehängten Nachrichten",
|
||||||
@ -230,6 +233,14 @@ const de: LocaleType = {
|
|||||||
NotShow: "Not Show Again",
|
NotShow: "Not Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Confirm",
|
||||||
|
Cancel: "Cancel",
|
||||||
|
Close: "Close",
|
||||||
|
Create: "Create",
|
||||||
|
Edit: "Edit",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default de;
|
export default de;
|
||||||
|
@ -120,6 +120,9 @@ const en: LocaleType = {
|
|||||||
Add: "Add One",
|
Add: "Add One",
|
||||||
Search: "Search Prompts",
|
Search: "Search Prompts",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Edit Prompt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Attached Messages Count",
|
Title: "Attached Messages Count",
|
||||||
@ -226,6 +229,14 @@ const en: LocaleType = {
|
|||||||
NotShow: "Not Show Again",
|
NotShow: "Not Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Confirm",
|
||||||
|
Cancel: "Cancel",
|
||||||
|
Close: "Close",
|
||||||
|
Create: "Create",
|
||||||
|
Edit: "Edit",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default en;
|
export default en;
|
||||||
|
@ -120,6 +120,9 @@ const es: LocaleType = {
|
|||||||
Add: "Add One",
|
Add: "Add One",
|
||||||
Search: "Search Prompts",
|
Search: "Search Prompts",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Edit Prompt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Cantidad de mensajes adjuntos",
|
Title: "Cantidad de mensajes adjuntos",
|
||||||
@ -227,6 +230,14 @@ const es: LocaleType = {
|
|||||||
NotShow: "Not Show Again",
|
NotShow: "Not Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Confirm",
|
||||||
|
Cancel: "Cancel",
|
||||||
|
Close: "Close",
|
||||||
|
Create: "Create",
|
||||||
|
Edit: "Edit",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default es;
|
export default es;
|
||||||
|
@ -120,6 +120,9 @@ const it: LocaleType = {
|
|||||||
Add: "Add One",
|
Add: "Add One",
|
||||||
Search: "Search Prompts",
|
Search: "Search Prompts",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Edit Prompt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Conteggio dei messaggi allegati",
|
Title: "Conteggio dei messaggi allegati",
|
||||||
@ -228,6 +231,14 @@ const it: LocaleType = {
|
|||||||
NotShow: "Not Show Again",
|
NotShow: "Not Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Confirm",
|
||||||
|
Cancel: "Cancel",
|
||||||
|
Close: "Close",
|
||||||
|
Create: "Create",
|
||||||
|
Edit: "Edit",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default it;
|
export default it;
|
||||||
|
@ -122,6 +122,9 @@ const jp: LocaleType = {
|
|||||||
Add: "新規追加",
|
Add: "新規追加",
|
||||||
Search: "プロンプトワード検索",
|
Search: "プロンプトワード検索",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "编辑提示词",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "履歴メッセージ数を添付",
|
Title: "履歴メッセージ数を添付",
|
||||||
@ -226,6 +229,14 @@ const jp: LocaleType = {
|
|||||||
NotShow: "不再展示",
|
NotShow: "不再展示",
|
||||||
ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
|
ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "确认",
|
||||||
|
Cancel: "取消",
|
||||||
|
Close: "关闭",
|
||||||
|
Create: "新建",
|
||||||
|
Edit: "编辑",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default jp;
|
export default jp;
|
||||||
|
@ -120,6 +120,9 @@ const tr: LocaleType = {
|
|||||||
Add: "Add One",
|
Add: "Add One",
|
||||||
Search: "Search Prompts",
|
Search: "Search Prompts",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "Edit Prompt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "Ekli Mesaj Sayısı",
|
Title: "Ekli Mesaj Sayısı",
|
||||||
@ -228,6 +231,14 @@ const tr: LocaleType = {
|
|||||||
NotShow: "Not Show Again",
|
NotShow: "Not Show Again",
|
||||||
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
ConfirmNoShow: "Confirm to disable?You can enable it in settings later.",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "Confirm",
|
||||||
|
Cancel: "Cancel",
|
||||||
|
Close: "Close",
|
||||||
|
Create: "Create",
|
||||||
|
Edit: "Edit",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tr;
|
export default tr;
|
||||||
|
@ -118,6 +118,9 @@ const tw: LocaleType = {
|
|||||||
Add: "新增一條",
|
Add: "新增一條",
|
||||||
Search: "搜尋提示詞",
|
Search: "搜尋提示詞",
|
||||||
},
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "编辑提示词",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
HistoryCount: {
|
HistoryCount: {
|
||||||
Title: "附帶歷史訊息數",
|
Title: "附帶歷史訊息數",
|
||||||
@ -219,6 +222,13 @@ const tw: LocaleType = {
|
|||||||
NotShow: "不再展示",
|
NotShow: "不再展示",
|
||||||
ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
|
ConfirmNoShow: "确认禁用?禁用后可以随时在设置中重新启用。",
|
||||||
},
|
},
|
||||||
|
UI: {
|
||||||
|
Confirm: "确认",
|
||||||
|
Cancel: "取消",
|
||||||
|
Close: "关闭",
|
||||||
|
Create: "新建",
|
||||||
|
Edit: "编辑",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default tw;
|
export default tw;
|
||||||
|
@ -17,11 +17,12 @@ export interface PromptStore {
|
|||||||
prompts: Record<number, Prompt>;
|
prompts: Record<number, Prompt>;
|
||||||
|
|
||||||
add: (prompt: Prompt) => number;
|
add: (prompt: Prompt) => number;
|
||||||
|
get: (id: number) => Prompt | undefined;
|
||||||
remove: (id: number) => void;
|
remove: (id: number) => void;
|
||||||
search: (text: string) => Prompt[];
|
search: (text: string) => Prompt[];
|
||||||
|
update: (id: number, updater: (prompt: Prompt) => void) => void;
|
||||||
|
|
||||||
getUserPrompts: () => Prompt[];
|
getUserPrompts: () => Prompt[];
|
||||||
updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchService = {
|
export const SearchService = {
|
||||||
@ -81,6 +82,16 @@ export const usePromptStore = create<PromptStore>()(
|
|||||||
return prompt.id!;
|
return prompt.id!;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get(id) {
|
||||||
|
const targetPrompt = get().prompts[id];
|
||||||
|
|
||||||
|
if (!targetPrompt) {
|
||||||
|
return SearchService.builtinPrompts.find((v) => v.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetPrompt;
|
||||||
|
},
|
||||||
|
|
||||||
remove(id) {
|
remove(id) {
|
||||||
const prompts = get().prompts;
|
const prompts = get().prompts;
|
||||||
delete prompts[id];
|
delete prompts[id];
|
||||||
@ -98,7 +109,7 @@ export const usePromptStore = create<PromptStore>()(
|
|||||||
return userPrompts;
|
return userPrompts;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateUserPrompts(id: number, updater) {
|
update(id: number, updater) {
|
||||||
const prompt = get().prompts[id] ?? {
|
const prompt = get().prompts[id] ?? {
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
dir="$(dirname "$0")"
|
dir="$(dirname "$0")"
|
||||||
config=$dir/proxychains.conf
|
config=$dir/proxychains.conf
|
||||||
host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
|
host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //')
|
||||||
|
echo "proxying to $host_ip"
|
||||||
cp $dir/proxychains.template.conf $config
|
cp $dir/proxychains.template.conf $config
|
||||||
sed -i "\$s/.*/http $host_ip 7890/" $config
|
sed -i "\$s/.*/http $host_ip 7890/" $config
|
||||||
|
Loading…
Reference in New Issue
Block a user