mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-09 19:46:37 +08:00
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
This commit is contained in:
commit
e153cd5020
@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import Locale from "./locales";
|
import Locale from "./locales";
|
||||||
|
|
||||||
@ -11,8 +12,7 @@ interface Commands {
|
|||||||
export function useCommand(commands: Commands = {}) {
|
export function useCommand(commands: Commands = {}) {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
if (commands === undefined) return;
|
useEffect(() => {
|
||||||
|
|
||||||
let shouldUpdate = false;
|
let shouldUpdate = false;
|
||||||
searchParams.forEach((param, name) => {
|
searchParams.forEach((param, name) => {
|
||||||
const commandName = name as keyof Commands;
|
const commandName = name as keyof Commands;
|
||||||
@ -26,6 +26,8 @@ export function useCommand(commands: Commands = {}) {
|
|||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
setSearchParams(searchParams);
|
setSearchParams(searchParams);
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [searchParams, commands]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatCommands {
|
interface ChatCommands {
|
||||||
|
@ -61,7 +61,14 @@ import Locale from "../locales";
|
|||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import styles from "./chat.module.scss";
|
import styles from "./chat.module.scss";
|
||||||
|
|
||||||
import { ListItem, Modal, showConfirm, showPrompt, showToast } from "./ui-lib";
|
import {
|
||||||
|
ListItem,
|
||||||
|
Modal,
|
||||||
|
Selector,
|
||||||
|
showConfirm,
|
||||||
|
showPrompt,
|
||||||
|
showToast,
|
||||||
|
} from "./ui-lib";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
|
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
|
||||||
import { Avatar } from "./emoji";
|
import { Avatar } from "./emoji";
|
||||||
@ -172,10 +179,29 @@ function PromptToast(props: {
|
|||||||
function useSubmitHandler() {
|
function useSubmitHandler() {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const submitKey = config.submitKey;
|
const submitKey = config.submitKey;
|
||||||
|
const isComposing = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onCompositionStart = () => {
|
||||||
|
isComposing.current = true;
|
||||||
|
};
|
||||||
|
const onCompositionEnd = () => {
|
||||||
|
isComposing.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("compositionstart", onCompositionStart);
|
||||||
|
window.addEventListener("compositionend", onCompositionEnd);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("compositionstart", onCompositionStart);
|
||||||
|
window.removeEventListener("compositionend", onCompositionEnd);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (e.key !== "Enter") return false;
|
if (e.key !== "Enter") return false;
|
||||||
if (e.key === "Enter" && e.nativeEvent.isComposing) return false;
|
if (e.key === "Enter" && (e.nativeEvent.isComposing || isComposing.current))
|
||||||
|
return false;
|
||||||
return (
|
return (
|
||||||
(config.submitKey === SubmitKey.AltEnter && e.altKey) ||
|
(config.submitKey === SubmitKey.AltEnter && e.altKey) ||
|
||||||
(config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
|
(config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
|
||||||
@ -385,16 +411,11 @@ export function ChatActions(props: {
|
|||||||
|
|
||||||
// switch model
|
// switch model
|
||||||
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
||||||
function nextModel() {
|
const models = useMemo(
|
||||||
const models = config.models.filter((m) => m.available).map((m) => m.name);
|
() => config.models.filter((m) => m.available).map((m) => m.name),
|
||||||
const modelIndex = models.indexOf(currentModel);
|
[config.models],
|
||||||
const nextIndex = (modelIndex + 1) % models.length;
|
);
|
||||||
const nextModel = models[nextIndex];
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||||
chatStore.updateCurrentSession((session) => {
|
|
||||||
session.mask.modelConfig.model = nextModel as ModelType;
|
|
||||||
session.mask.syncGlobalConfig = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["chat-input-actions"]}>
|
<div className={styles["chat-input-actions"]}>
|
||||||
@ -466,10 +487,28 @@ export function ChatActions(props: {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<ChatAction
|
<ChatAction
|
||||||
onClick={nextModel}
|
onClick={() => setShowModelSelector(true)}
|
||||||
text={currentModel}
|
text={currentModel}
|
||||||
icon={<RobotIcon />}
|
icon={<RobotIcon />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{showModelSelector && (
|
||||||
|
<Selector
|
||||||
|
items={models.map((m) => ({
|
||||||
|
title: m,
|
||||||
|
value: m,
|
||||||
|
}))}
|
||||||
|
onClose={() => setShowModelSelector(false)}
|
||||||
|
onSelection={(s) => {
|
||||||
|
if (s.length === 0) return;
|
||||||
|
chatStore.updateCurrentSession((session) => {
|
||||||
|
session.mask.modelConfig.model = s[0] as ModelType;
|
||||||
|
session.mask.syncGlobalConfig = false;
|
||||||
|
});
|
||||||
|
showToast(s[0]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@
|
|||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
border: var(--border-in-light);
|
border: var(--border-in-light);
|
||||||
|
|
||||||
* {
|
*:not(li) {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
import { ChatMessage, useAppConfig, useChatStore } from "../store";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import styles from "./exporter.module.scss";
|
import styles from "./exporter.module.scss";
|
||||||
import { List, ListItem, Modal, Select, showToast } from "./ui-lib";
|
import {
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
showImageModal,
|
||||||
|
showModal,
|
||||||
|
showToast,
|
||||||
|
} from "./ui-lib";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
import { copyToClipboard, downloadAs, useMobileScreen } from "../utils";
|
import { copyToClipboard, downloadAs, useMobileScreen } from "../utils";
|
||||||
|
|
||||||
@ -23,6 +32,7 @@ import { DEFAULT_MASK_AVATAR } from "../store/mask";
|
|||||||
import { api } from "../client/api";
|
import { api } from "../client/api";
|
||||||
import { prettyObject } from "../utils/format";
|
import { prettyObject } from "../utils/format";
|
||||||
import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
|
import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
|
||||||
|
import { getClientConfig } from "../config/client";
|
||||||
|
|
||||||
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
loading: () => <LoadingIcon />,
|
loading: () => <LoadingIcon />,
|
||||||
@ -369,6 +379,7 @@ export function ImagePreviewer(props: {
|
|||||||
const previewRef = useRef<HTMLDivElement>(null);
|
const previewRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const copy = () => {
|
const copy = () => {
|
||||||
|
showToast(Locale.Export.Image.Toast);
|
||||||
const dom = previewRef.current;
|
const dom = previewRef.current;
|
||||||
if (!dom) return;
|
if (!dom) return;
|
||||||
toBlob(dom).then((blob) => {
|
toBlob(dom).then((blob) => {
|
||||||
@ -393,17 +404,15 @@ export function ImagePreviewer(props: {
|
|||||||
const isMobile = useMobileScreen();
|
const isMobile = useMobileScreen();
|
||||||
|
|
||||||
const download = () => {
|
const download = () => {
|
||||||
|
showToast(Locale.Export.Image.Toast);
|
||||||
const dom = previewRef.current;
|
const dom = previewRef.current;
|
||||||
if (!dom) return;
|
if (!dom) return;
|
||||||
toPng(dom)
|
toPng(dom)
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
if (!blob) return;
|
if (!blob) return;
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile || getClientConfig()?.isApp) {
|
||||||
const image = new Image();
|
showImageModal(blob);
|
||||||
image.src = blob;
|
|
||||||
const win = window.open("");
|
|
||||||
win?.document.write(image.outerHTML);
|
|
||||||
} else {
|
} else {
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.download = `${props.topic}.png`;
|
link.download = `${props.topic}.png`;
|
||||||
|
@ -12,6 +12,7 @@ import mermaid from "mermaid";
|
|||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
|
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
|
||||||
|
import { showImageModal } from "./ui-lib";
|
||||||
|
|
||||||
export function Mermaid(props: { code: string }) {
|
export function Mermaid(props: { code: string }) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
@ -37,11 +38,13 @@ export function Mermaid(props: { code: string }) {
|
|||||||
if (!svg) return;
|
if (!svg) return;
|
||||||
const text = new XMLSerializer().serializeToString(svg);
|
const text = new XMLSerializer().serializeToString(svg);
|
||||||
const blob = new Blob([text], { type: "image/svg+xml" });
|
const blob = new Blob([text], { type: "image/svg+xml" });
|
||||||
const url = URL.createObjectURL(blob);
|
console.log(blob);
|
||||||
const win = window.open(url);
|
// const url = URL.createObjectURL(blob);
|
||||||
if (win) {
|
// const win = window.open(url);
|
||||||
win.onload = () => URL.revokeObjectURL(url);
|
// if (win) {
|
||||||
}
|
// win.onload = () => URL.revokeObjectURL(url);
|
||||||
|
// }
|
||||||
|
showImageModal(URL.createObjectURL(blob));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
|
@ -30,7 +30,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
|
|
||||||
import chatStyle from "./chat.module.scss";
|
import chatStyle from "./chat.module.scss";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { downloadAs, readFromFile } from "../utils";
|
import { copyToClipboard, downloadAs, readFromFile } from "../utils";
|
||||||
import { Updater } from "../typing";
|
import { Updater } from "../typing";
|
||||||
import { ModelConfigList } from "./model-config";
|
import { ModelConfigList } from "./model-config";
|
||||||
import { FileName, Path } from "../constant";
|
import { FileName, Path } from "../constant";
|
||||||
@ -65,6 +65,11 @@ export function MaskConfig(props: {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copyMaskLink = () => {
|
||||||
|
const maskLink = `${location.protocol}//${location.host}/#${Path.NewChat}?mask=${props.mask.id}`;
|
||||||
|
copyToClipboard(maskLink);
|
||||||
|
};
|
||||||
|
|
||||||
const globalConfig = useAppConfig();
|
const globalConfig = useAppConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -125,6 +130,20 @@ export function MaskConfig(props: {
|
|||||||
}}
|
}}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
{!props.shouldSyncFromGlobal ? (
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Mask.Config.Share.Title}
|
||||||
|
subTitle={Locale.Mask.Config.Share.SubTitle}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
icon={<CopyIcon />}
|
||||||
|
text={Locale.Mask.Config.Share.Action}
|
||||||
|
onClick={copyMaskLink}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{props.shouldSyncFromGlobal ? (
|
{props.shouldSyncFromGlobal ? (
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Mask.Config.Sync.Title}
|
title={Locale.Mask.Config.Sync.Title}
|
||||||
|
@ -15,6 +15,7 @@ import { useAppConfig, useChatStore } from "../store";
|
|||||||
import { MaskAvatar } from "./mask";
|
import { MaskAvatar } from "./mask";
|
||||||
import { useCommand } from "../command";
|
import { useCommand } from "../command";
|
||||||
import { showConfirm } from "./ui-lib";
|
import { showConfirm } from "./ui-lib";
|
||||||
|
import { BUILTIN_MASK_STORE } from "../masks";
|
||||||
|
|
||||||
function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
|
function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
|
||||||
const xmin = Math.max(aRect.x, bRect.x);
|
const xmin = Math.max(aRect.x, bRect.x);
|
||||||
@ -93,14 +94,17 @@ export function NewChat() {
|
|||||||
const { state } = useLocation();
|
const { state } = useLocation();
|
||||||
|
|
||||||
const startChat = (mask?: Mask) => {
|
const startChat = (mask?: Mask) => {
|
||||||
|
setTimeout(() => {
|
||||||
chatStore.newSession(mask);
|
chatStore.newSession(mask);
|
||||||
setTimeout(() => navigate(Path.Chat), 1);
|
navigate(Path.Chat);
|
||||||
|
}, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
useCommand({
|
useCommand({
|
||||||
mask: (id) => {
|
mask: (id) => {
|
||||||
try {
|
try {
|
||||||
const mask = maskStore.get(parseInt(id));
|
const intId = parseInt(id);
|
||||||
|
const mask = maskStore.get(intId) ?? BUILTIN_MASK_STORE.get(intId);
|
||||||
startChat(mask ?? undefined);
|
startChat(mask ?? undefined);
|
||||||
} catch {
|
} catch {
|
||||||
console.error("[New Chat] failed to create chat from mask id=", id);
|
console.error("[New Chat] failed to create chat from mask id=", id);
|
||||||
|
@ -542,10 +542,12 @@ export function Settings() {
|
|||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<List>
|
||||||
<ListItem
|
<ListItem
|
||||||
title={Locale.Settings.Mask.Title}
|
title={Locale.Settings.Mask.Splash.Title}
|
||||||
subTitle={Locale.Settings.Mask.SubTitle}
|
subTitle={Locale.Settings.Mask.Splash.SubTitle}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -559,6 +561,22 @@ export function Settings() {
|
|||||||
}
|
}
|
||||||
></input>
|
></input>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem
|
||||||
|
title={Locale.Settings.Mask.Builtin.Title}
|
||||||
|
subTitle={Locale.Settings.Mask.Builtin.SubTitle}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={config.hideBuiltinMasks}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateConfig(
|
||||||
|
(config) =>
|
||||||
|
(config.hideBuiltinMasks = e.currentTarget.checked),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</ListItem>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
animation: slide-in ease 0.3s;
|
animation: slide-in ease 0.3s;
|
||||||
|
background: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list .list-item:last-child {
|
.list .list-item:last-child {
|
||||||
@ -79,6 +80,19 @@
|
|||||||
|
|
||||||
--modal-padding: 20px;
|
--modal-padding: 20px;
|
||||||
|
|
||||||
|
&-max {
|
||||||
|
width: 95vw;
|
||||||
|
max-width: unset;
|
||||||
|
height: 95vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
max-height: unset !important;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding: var(--modal-padding);
|
padding: var(--modal-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -91,14 +105,22 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-close-btn {
|
.modal-header-actions {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.modal-header-action {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.2);
|
filter: brightness(1.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
@ -249,3 +271,34 @@
|
|||||||
border: 1px solid var(--primary);
|
border: 1px solid var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
.list {
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--white);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import styles from "./ui-lib.module.scss";
|
import styles from "./ui-lib.module.scss";
|
||||||
import LoadingIcon from "../icons/three-dots.svg";
|
import LoadingIcon from "../icons/three-dots.svg";
|
||||||
import CloseIcon from "../icons/close.svg";
|
import CloseIcon from "../icons/close.svg";
|
||||||
@ -6,6 +7,8 @@ import EyeOffIcon from "../icons/eye-off.svg";
|
|||||||
import DownIcon from "../icons/down.svg";
|
import DownIcon from "../icons/down.svg";
|
||||||
import ConfirmIcon from "../icons/confirm.svg";
|
import ConfirmIcon from "../icons/confirm.svg";
|
||||||
import CancelIcon from "../icons/cancel.svg";
|
import CancelIcon from "../icons/cancel.svg";
|
||||||
|
import MaxIcon from "../icons/max.svg";
|
||||||
|
import MinIcon from "../icons/min.svg";
|
||||||
|
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
|
|
||||||
@ -44,9 +47,13 @@ export function ListItem(props: {
|
|||||||
children?: JSX.Element | JSX.Element[];
|
children?: JSX.Element | JSX.Element[];
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={styles["list-item"] + ` ${props.className || ""}`}>
|
<div
|
||||||
|
className={styles["list-item"] + ` ${props.className || ""}`}
|
||||||
|
onClick={props.onClick}
|
||||||
|
>
|
||||||
<div className={styles["list-header"]}>
|
<div className={styles["list-header"]}>
|
||||||
{props.icon && <div className={styles["list-icon"]}>{props.icon}</div>}
|
{props.icon && <div className={styles["list-icon"]}>{props.icon}</div>}
|
||||||
<div className={styles["list-item-title"]}>
|
<div className={styles["list-item-title"]}>
|
||||||
@ -93,6 +100,7 @@ interface ModalProps {
|
|||||||
title: string;
|
title: string;
|
||||||
children?: any;
|
children?: any;
|
||||||
actions?: JSX.Element[];
|
actions?: JSX.Element[];
|
||||||
|
defaultMax?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
export function Modal(props: ModalProps) {
|
export function Modal(props: ModalProps) {
|
||||||
@ -111,15 +119,32 @@ export function Modal(props: ModalProps) {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [isMax, setMax] = useState(!!props.defaultMax);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["modal-container"]}>
|
<div
|
||||||
|
className={
|
||||||
|
styles["modal-container"] + ` ${isMax && styles["modal-container-max"]}`
|
||||||
|
}
|
||||||
|
>
|
||||||
<div className={styles["modal-header"]}>
|
<div className={styles["modal-header"]}>
|
||||||
<div className={styles["modal-title"]}>{props.title}</div>
|
<div className={styles["modal-title"]}>{props.title}</div>
|
||||||
|
|
||||||
<div className={styles["modal-close-btn"]} onClick={props.onClose}>
|
<div className={styles["modal-header-actions"]}>
|
||||||
|
<div
|
||||||
|
className={styles["modal-header-action"]}
|
||||||
|
onClick={() => setMax(!isMax)}
|
||||||
|
>
|
||||||
|
{isMax ? <MinIcon /> : <MaxIcon />}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles["modal-header-action"]}
|
||||||
|
onClick={props.onClose}
|
||||||
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles["modal-content"]}>{props.children}</div>
|
<div className={styles["modal-content"]}>{props.children}</div>
|
||||||
|
|
||||||
@ -394,3 +419,54 @@ export function showPrompt(content: any, value = "", rows = 3) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showImageModal(img: string) {
|
||||||
|
showModal({
|
||||||
|
title: Locale.Export.Image.Modal,
|
||||||
|
children: (
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
src={img}
|
||||||
|
alt="preview"
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
}}
|
||||||
|
></img>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Selector<T>(props: {
|
||||||
|
items: Array<{
|
||||||
|
title: string;
|
||||||
|
subTitle?: string;
|
||||||
|
value: T;
|
||||||
|
}>;
|
||||||
|
onSelection?: (selection: T[]) => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
multiple?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={styles["selector"]} onClick={() => props.onClose?.()}>
|
||||||
|
<div className={styles["selector-content"]}>
|
||||||
|
<List>
|
||||||
|
{props.items.map((item, i) => {
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
className={styles["selector-item"]}
|
||||||
|
key={i}
|
||||||
|
title={item.title}
|
||||||
|
subTitle={item.subTitle}
|
||||||
|
onClick={() => {
|
||||||
|
props.onSelection?.([item.value]);
|
||||||
|
props.onClose?.();
|
||||||
|
}}
|
||||||
|
></ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -66,27 +66,27 @@ Current time: {{time}}`;
|
|||||||
export const DEFAULT_MODELS = [
|
export const DEFAULT_MODELS = [
|
||||||
{
|
{
|
||||||
name: "gpt-4",
|
name: "gpt-4",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-4-0314",
|
name: "gpt-4-0314",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-4-0613",
|
name: "gpt-4-0613",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-4-32k",
|
name: "gpt-4-32k",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-4-32k-0314",
|
name: "gpt-4-32k-0314",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-4-32k-0613",
|
name: "gpt-4-32k-0613",
|
||||||
available: false,
|
available: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "gpt-3.5-turbo",
|
name: "gpt-3.5-turbo",
|
||||||
|
@ -130,9 +130,11 @@ const ar: PartialLocaleType = {
|
|||||||
SubTitle: "معاينة Markdown في فقاعة",
|
SubTitle: "معاينة Markdown في فقاعة",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "شاشة تظهر الأقنعة",
|
Title: "شاشة تظهر الأقنعة",
|
||||||
SubTitle: "عرض شاشة تظهر الأقنعة قبل بدء الدردشة الجديدة",
|
SubTitle: "عرض شاشة تظهر الأقنعة قبل بدء الدردشة الجديدة",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "تعطيل الاكتمال التلقائي",
|
Title: "تعطيل الاكتمال التلقائي",
|
||||||
|
338
app/locales/bn.ts
Normal file
338
app/locales/bn.ts
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
import { SubmitKey } from "../store/config";
|
||||||
|
import { PartialLocaleType } from "./index";
|
||||||
|
|
||||||
|
const bn: PartialLocaleType = {
|
||||||
|
WIP: "শীঘ্রই আসছে...",
|
||||||
|
Error: {
|
||||||
|
Unauthorized:
|
||||||
|
"অননুমোদিত অ্যাক্সেস, অনুগ্রহ করে [অথোরাইজশন](/#/auth) পৃষ্ঠায় অ্যাক্সেস কোড ইনপুট করুন।",
|
||||||
|
},
|
||||||
|
Auth: {
|
||||||
|
Title: "একটি অ্যাক্সেস কোড প্রয়োজন",
|
||||||
|
Tips: "নীচে অ্যাক্সেস কোড ইনপুট করুন",
|
||||||
|
Input: "অ্যাক্সেস কোড",
|
||||||
|
Confirm: "নিশ্চিত করুন",
|
||||||
|
Later: "পরে",
|
||||||
|
},
|
||||||
|
ChatItem: {
|
||||||
|
ChatItemCount: (count: number) => `${count} টি বার্তা`,
|
||||||
|
},
|
||||||
|
Chat: {
|
||||||
|
SubTitle: (count: number) => `${count} টি বার্তা`,
|
||||||
|
Actions: {
|
||||||
|
ChatList: "চ্যাট তালিকায় যান",
|
||||||
|
CompressedHistory: "সংক্ষিপ্ত ইতিহাস মেমোরি প্রম্পট",
|
||||||
|
Export: "সমস্ত বার্তা মার্কডাউন হিসাবে রপ্তানি করুন",
|
||||||
|
Copy: "কপি",
|
||||||
|
Stop: "বন্ধ করুন",
|
||||||
|
Retry: "পুনরায় চেষ্টা করুন",
|
||||||
|
Pin: "পিন করুন",
|
||||||
|
PinToastContent: "পিন করা হয়েছে ২টি বার্তা প্রম্পটে",
|
||||||
|
PinToastAction: "দেখুন",
|
||||||
|
Delete: "মুছে ফেলুন",
|
||||||
|
Edit: "সম্পাদন করুন",
|
||||||
|
},
|
||||||
|
Commands: {
|
||||||
|
new: "নতুন চ্যাট শুরু করুন",
|
||||||
|
newm: "মাস্ক সহ নতুন চ্যাট শুরু করুন",
|
||||||
|
next: "পরবর্তী চ্যাট",
|
||||||
|
prev: "পূর্ববর্তী চ্যাট",
|
||||||
|
clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
|
||||||
|
del: "চ্যাট মুছুন",
|
||||||
|
},
|
||||||
|
InputActions: {
|
||||||
|
Stop: "বন্ধ করুন",
|
||||||
|
ToBottom: "সর্বশেষতম দিকে",
|
||||||
|
Theme: {
|
||||||
|
auto: "অটো",
|
||||||
|
light: "হালকা থিম",
|
||||||
|
dark: "ডার্ক থিম",
|
||||||
|
},
|
||||||
|
Prompt: "প্রম্পটগুলিতে",
|
||||||
|
Masks: "মাস্কগুলি",
|
||||||
|
Clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
|
||||||
|
Settings: "সেটিংস",
|
||||||
|
},
|
||||||
|
Rename: "চ্যাট পুনঃনামকরণ করুন",
|
||||||
|
Typing: "টাইপিং...",
|
||||||
|
Input: (submitKey: string) => {
|
||||||
|
var inputHints = `${submitKey} to send`;
|
||||||
|
if (submitKey === String(SubmitKey.Enter)) {
|
||||||
|
inputHints += ", Shift + Enter to wrap";
|
||||||
|
}
|
||||||
|
return inputHints + ", / to search prompts, : to use commands";
|
||||||
|
},
|
||||||
|
Send: "প্রেরণ করুন",
|
||||||
|
Config: {
|
||||||
|
Reset: "ডিফল্টে রিসেট করুন",
|
||||||
|
SaveAs: "মাস্ক হিসাবে সংরক্ষণ করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Export: {
|
||||||
|
Title: "বার্তা রপ্তানিকরণ",
|
||||||
|
Copy: "সমস্তটি কপি করুন",
|
||||||
|
Download: "ডাউনলোড করুন",
|
||||||
|
MessageFromYou: "আপনার বার্তা",
|
||||||
|
MessageFromChatGPT: "চ্যাটজিপিটির বার্তা",
|
||||||
|
Share: "শেয়ার করুন শেয়ারজিপিটি তে",
|
||||||
|
Format: {
|
||||||
|
Title: "রপ্তানি ফরম্যাট",
|
||||||
|
SubTitle: "মার্কডাউন বা পিএনজি চিত্র",
|
||||||
|
},
|
||||||
|
IncludeContext: {
|
||||||
|
Title: "মাস্ক অন্তর্ভুক্ত করুন",
|
||||||
|
SubTitle: "মাস্কগুলি সংরক্ষণ করবেন না কি",
|
||||||
|
},
|
||||||
|
Steps: {
|
||||||
|
Select: "নির্বাচন করুন",
|
||||||
|
Preview: "প্রিভিউ করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
Search: "অনুসন্ধান করুন",
|
||||||
|
All: "সমস্তটি নির্বাচন করুন",
|
||||||
|
Latest: "সর্বশেষতমটি নির্বাচন করুন",
|
||||||
|
Clear: "পরিষ্কার করুন",
|
||||||
|
},
|
||||||
|
Memory: {
|
||||||
|
Title: "মেমোরি প্রম্পট",
|
||||||
|
EmptyContent: "এখনও কিছুই নেই।",
|
||||||
|
Send: "মেমোরি প্রেরণ করুন",
|
||||||
|
Copy: "মেমোরি কপি করুন",
|
||||||
|
Reset: "পুনরায় নিশ্চিত করুন",
|
||||||
|
ResetConfirm:
|
||||||
|
"রিসেট করলে বর্তমান চ্যাট ইতিহাস এবং ঐতিহাসিক মেমোরি মুছে যাবে। পুনরায় নির্দিষ্ট করতে চান তা নিশ্চিত করতে চান?",
|
||||||
|
},
|
||||||
|
Home: {
|
||||||
|
NewChat: "নতুন চ্যাট",
|
||||||
|
DeleteChat: "নির্বাচিত সংলাপটি মুছতে নিশ্চিত করুন?",
|
||||||
|
DeleteToast: "চ্যাটটি মুছেছেন",
|
||||||
|
Revert: "পুনরায়",
|
||||||
|
},
|
||||||
|
Settings: {
|
||||||
|
Title: "সেটিংস",
|
||||||
|
SubTitle: "সমস্ত সেটিংস",
|
||||||
|
Danger: {
|
||||||
|
Reset: {
|
||||||
|
Title: "সমস্ত সেটিংস পুনঃনির্দেশ দিন",
|
||||||
|
SubTitle: "সকল সেটিংস ডিফল্টে পুনঃনির্দেশ দিতে",
|
||||||
|
Action: "পুনঃনির্দেশ দিন",
|
||||||
|
Confirm: "সমস্ত সেটিংস ডিফল্টে পুনঃনির্দেশ করতে নিশ্চিত করতে?",
|
||||||
|
},
|
||||||
|
Clear: {
|
||||||
|
Title: "সমস্ত তথ্য মুছুন",
|
||||||
|
SubTitle: "সমস্ত বার্তা এবং সেটিংস মুছুন",
|
||||||
|
Action: "মুছুন",
|
||||||
|
Confirm: "সমস্ত বার্তা এবং সেটিংস মুছে ফেলতে নিশ্চিত করতে?",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Lang: {
|
||||||
|
Name: "বাংলা", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
|
||||||
|
All: "সমস্ত ভাষা",
|
||||||
|
},
|
||||||
|
Avatar: "অবতার",
|
||||||
|
FontSize: {
|
||||||
|
Title: "ফন্ট সাইজ",
|
||||||
|
SubTitle: "চ্যাট সামগ্রীর ফন্ট সাইজ সংশোধন করুন",
|
||||||
|
},
|
||||||
|
|
||||||
|
InputTemplate: {
|
||||||
|
Title: "ইনপুট টেমপ্লেট",
|
||||||
|
SubTitle: "নতুনতম বার্তা এই টেমপ্লেটে পূরণ হবে",
|
||||||
|
},
|
||||||
|
|
||||||
|
Update: {
|
||||||
|
Version: (x: string) => `Version: ${x}`,
|
||||||
|
IsLatest: "Latest version",
|
||||||
|
CheckUpdate: "Check Update",
|
||||||
|
IsChecking: "Checking update...",
|
||||||
|
FoundUpdate: (x: string) => `Found new version: ${x}`,
|
||||||
|
GoToUpdate: "Update",
|
||||||
|
},
|
||||||
|
SendKey: "প্রেরণ চাবি",
|
||||||
|
Theme: "থিম",
|
||||||
|
TightBorder: "সঙ্গতি সীমা",
|
||||||
|
SendPreviewBubble: {
|
||||||
|
Title: "প্রিভিউ বুলবুল প্রেরণ করুন",
|
||||||
|
SubTitle: "বুলবুলে মার্কডাউন প্রিভিউ করুন",
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Splash: {
|
||||||
|
Title: "মাস্ক স্প্ল্যাশ স্ক্রিন",
|
||||||
|
SubTitle:
|
||||||
|
"নতুন চ্যাট শুরু করার আগে মাস্ক স্প্ল্যাশ স্ক্রিন প্রদর্শন করুন",
|
||||||
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "মূলত মাস্ক গোপন করুন",
|
||||||
|
SubTitle: "মাস্ক তালিকা থেকে মূলত মাস্কগুলি লুকান",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Prompt: {
|
||||||
|
Disable: {
|
||||||
|
Title: "অটো-সম্পূর্ণতা নিষ্ক্রিয় করুন",
|
||||||
|
SubTitle: "অটো-সম্পূর্ণতা চালু করতে / ইনপুট করুন",
|
||||||
|
},
|
||||||
|
List: "প্রম্পট তালিকা",
|
||||||
|
ListCount: (builtin: number, custom: number) =>
|
||||||
|
`${builtin} built-in, ${custom} user-defined`,
|
||||||
|
Edit: "সম্পাদন করুন",
|
||||||
|
Modal: {
|
||||||
|
Title: "প্রম্পট তালিকা",
|
||||||
|
Add: "একটি যোগ করুন",
|
||||||
|
Search: "সন্ধান প্রম্পট",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: "সম্পাদন করুন প্রম্পট",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HistoryCount: {
|
||||||
|
Title: "সংযুক্ত বার্তা সংখ্যা",
|
||||||
|
SubTitle: "প্রতি অনুরোধে প্রেরণ করা গেলে প্রেরণ করা হবে",
|
||||||
|
},
|
||||||
|
CompressThreshold: {
|
||||||
|
Title: "ইতিহাস সঙ্কুচিত করার সীমা",
|
||||||
|
SubTitle:
|
||||||
|
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
|
||||||
|
},
|
||||||
|
Token: {
|
||||||
|
Title: "অ্যাপি কী",
|
||||||
|
SubTitle: "অ্যাক্সেস কোড সীমা উপেক্ষা করতে আপনার কীটি ব্যবহার করুন",
|
||||||
|
Placeholder: "OpenAI API কী",
|
||||||
|
},
|
||||||
|
Usage: {
|
||||||
|
Title: "একাউন্ট ব্যালেন্স",
|
||||||
|
SubTitle(used: any, total: any) {
|
||||||
|
return `এই মাসে ব্যবহৃত $${used}, সাবস্ক্রিপশন $${total}`;
|
||||||
|
},
|
||||||
|
IsChecking: "চেক করা হচ্ছে...",
|
||||||
|
Check: "চেক",
|
||||||
|
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
|
||||||
|
},
|
||||||
|
AccessCode: {
|
||||||
|
Title: "অ্যাক্সেস কোড",
|
||||||
|
SubTitle: "অ্যাক্সেস নিয়ন্ত্রণ সক্রিয়",
|
||||||
|
Placeholder: "অ্যাক্সেস কোড প্রয়োজন",
|
||||||
|
},
|
||||||
|
Endpoint: {
|
||||||
|
Title: "ইনটারপয়েন্ট",
|
||||||
|
SubTitle: "কাস্টম এন্ডপয়েন্টটি হতে হবে http(s):// দিয়ে শুরু হতে হবে",
|
||||||
|
},
|
||||||
|
Model: "মডেল",
|
||||||
|
Temperature: {
|
||||||
|
Title: "তাপমাত্রা",
|
||||||
|
SubTitle: "আরতি মান বেশি করলে বেশি এলোমেলো আউটপুট হবে",
|
||||||
|
},
|
||||||
|
TopP: {
|
||||||
|
Title: "শীর্ষ পি",
|
||||||
|
SubTitle: "তাপমাত্রা সঙ্গে এই মান পরিবর্তন করবেন না",
|
||||||
|
},
|
||||||
|
MaxTokens: {
|
||||||
|
Title: "সর্বাধিক টোকেন",
|
||||||
|
SubTitle: "ইনপুট টোকেন এবং উৎপাদিত টোকেনের সর্বাধিক দৈর্ঘ্য",
|
||||||
|
},
|
||||||
|
PresencePenalty: {
|
||||||
|
Title: "উপস্থিতির জরিমানা",
|
||||||
|
SubTitle: "আরতি মান বেশি করলে নতুন বিষয়গুলি সম্ভাব্যতা বাড়াতে পারে",
|
||||||
|
},
|
||||||
|
FrequencyPenalty: {
|
||||||
|
Title: "ফ্রিকুয়েন্সি জরিমানা",
|
||||||
|
SubTitle:
|
||||||
|
"আরতি মান বাড়ালে একই লাইন পুনরায় ব্যাবহার করার সম্ভাবনা হ্রাস পায়",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Store: {
|
||||||
|
DefaultTopic: "নতুন সংলাপ",
|
||||||
|
BotHello: "হ্যালো! আজকে আপনাকে কিভাবে সাহায্য করতে পারি?",
|
||||||
|
Error: "কিছু নিয়ে ভুল হয়েছে, পরে আবার চেষ্টা করুন।",
|
||||||
|
Prompt: {
|
||||||
|
History: (content: string) =>
|
||||||
|
"এটি চ্যাট ইতিহাসের সংক্ষিপ্ত সংকলনের মতো: " + content,
|
||||||
|
Topic:
|
||||||
|
"আমাদের সংলাপটির চার থেকে পাঁচ শব্দের একটি শিরোনাম তৈরি করুন যা আমাদের আলাপের সংক্ষিপ্তসার হিসাবে যোগ হবে না, যেমন অভিবৃত্তি, বিন্যাস, উদ্ধৃতি, পূর্বচালক চিহ্ন, পূর্বরোবক্তির যেকোনো চিহ্ন বা অতিরিক্ত পাঠ। মেয়াদশেষ উদ্ধৃতি চেষ্টা করুন।",
|
||||||
|
Summarize:
|
||||||
|
"২০০ শব্দের লম্বা হয়ে মুহূর্তে আলোচনা সংক্ষেপের রপ্তানি করুন, যেটি ভবিষ্যতের প্রম্পট হিসাবে ব্যবহার করবেন।",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Copy: {
|
||||||
|
Success: "ক্লিপবোর্ডে কপি করা হয়েছে",
|
||||||
|
Failed: "কপি ব্যর্থ, অনুমতি প্রদান করার জন্য অনুমতি প্রদান করুন",
|
||||||
|
},
|
||||||
|
Context: {
|
||||||
|
Toast: (x: any) => `With ${x} contextual prompts`,
|
||||||
|
Edit: "বর্তমান চ্যাট সেটিংস",
|
||||||
|
Add: "একটি প্রম্পট যোগ করুন",
|
||||||
|
Clear: "সঙ্গতি পরিস্কার করুন",
|
||||||
|
Revert: "পূর্ববর্তী অবস্থানে ফিরে যান",
|
||||||
|
},
|
||||||
|
Plugin: {
|
||||||
|
Name: "প্লাগইন",
|
||||||
|
},
|
||||||
|
Mask: {
|
||||||
|
Name: "মাস্ক",
|
||||||
|
Page: {
|
||||||
|
Title: "প্রম্পট টেমপ্লেট",
|
||||||
|
SubTitle: (count: number) => `${count} টি প্রম্পট টেমপ্লেট`,
|
||||||
|
Search: "টেমপ্লেট অনুসন্ধান করুন",
|
||||||
|
Create: "তৈরি করুন",
|
||||||
|
},
|
||||||
|
Item: {
|
||||||
|
Info: (count: number) => `${count} প্রম্পট`,
|
||||||
|
Chat: "চ্যাট",
|
||||||
|
View: "দেখুন",
|
||||||
|
Edit: "সম্পাদন করুন",
|
||||||
|
Delete: "মুছে ফেলুন",
|
||||||
|
DeleteConfirm: "মুছে ফেলতে নিশ্চিত করুন?",
|
||||||
|
},
|
||||||
|
EditModal: {
|
||||||
|
Title: (readonly: boolean) =>
|
||||||
|
`প্রম্পট টেমপ্লেট সম্পাদন করুন ${readonly ? "(readonly)" : ""}`,
|
||||||
|
Download: "ডাউনলোড করুন",
|
||||||
|
Clone: "ক্লোন করুন",
|
||||||
|
},
|
||||||
|
Config: {
|
||||||
|
Avatar: "বট অবতার",
|
||||||
|
Name: "বটের নাম",
|
||||||
|
Sync: {
|
||||||
|
Title: "গ্লোবাল কনফিগ ব্যবহার করুন",
|
||||||
|
SubTitle: "এই চ্যাটে গ্লোবাল কনফিগ ব্যবহার করুন",
|
||||||
|
Confirm:
|
||||||
|
"গ্লোবাল কনফিগ দ্বারা কাস্টম কনফিগ ওভাররাইড করতে নিশ্চিত করতে?",
|
||||||
|
},
|
||||||
|
HideContext: {
|
||||||
|
Title: "সংশ্লিষ্টতা প্রম্পটগুলি লুকান",
|
||||||
|
SubTitle: "চ্যাটে সংশ্লিষ্টতা প্রম্পটগুলি দেখাবেন না",
|
||||||
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "এই মাস্কটি শেয়ার করুন",
|
||||||
|
SubTitle: "এই মাস্কের একটি লিঙ্ক তৈরি করুন",
|
||||||
|
Action: "লিঙ্ক কপি করুন",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NewChat: {
|
||||||
|
Return: "ফিরে যান",
|
||||||
|
Skip: "শুরু করুন",
|
||||||
|
Title: "মাস্ক নির্বাচন করুন",
|
||||||
|
SubTitle: "মাস্কের পিছনে আত্মার সঙ্গে চ্যাট করুন",
|
||||||
|
More: "আরো খুঁজুন",
|
||||||
|
NotShow: "এখনও দেখাবেন না",
|
||||||
|
ConfirmNoShow:
|
||||||
|
"নিষ্ক্রিয় করতে নিশ্চিত করুন? পরে আপনি এটি সেটিংসে সক্ষম করতে পারবেন।",
|
||||||
|
},
|
||||||
|
|
||||||
|
UI: {
|
||||||
|
Confirm: "নিশ্চিত করুন",
|
||||||
|
Cancel: "বাতিল করুন",
|
||||||
|
Close: "বন্ধ করুন",
|
||||||
|
Create: "তৈরি করুন",
|
||||||
|
Edit: "সম্পাদন করুন",
|
||||||
|
},
|
||||||
|
Exporter: {
|
||||||
|
Model: "মডেল",
|
||||||
|
Messages: "বার্তা",
|
||||||
|
Topic: "টপিক",
|
||||||
|
Time: "সময়",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default bn;
|
@ -86,6 +86,10 @@ const cn = {
|
|||||||
Select: "选取",
|
Select: "选取",
|
||||||
Preview: "预览",
|
Preview: "预览",
|
||||||
},
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "正在生成截图",
|
||||||
|
Modal: "长按或右键保存图片",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "搜索消息",
|
Search: "搜索消息",
|
||||||
@ -156,9 +160,15 @@ const cn = {
|
|||||||
SubTitle: "在预览气泡中预览 Markdown 内容",
|
SubTitle: "在预览气泡中预览 Markdown 内容",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "面具启动页",
|
Title: "面具启动页",
|
||||||
SubTitle: "新建聊天时,展示面具启动页",
|
SubTitle: "新建聊天时,展示面具启动页",
|
||||||
},
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "隐藏内置面具",
|
||||||
|
SubTitle: "在所有面具列表中隐藏内置面具",
|
||||||
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "禁用提示词自动补全",
|
Title: "禁用提示词自动补全",
|
||||||
@ -291,6 +301,11 @@ const cn = {
|
|||||||
Title: "隐藏预设对话",
|
Title: "隐藏预设对话",
|
||||||
SubTitle: "隐藏后预设对话不会出现在聊天界面",
|
SubTitle: "隐藏后预设对话不会出现在聊天界面",
|
||||||
},
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "分享此面具",
|
||||||
|
SubTitle: "生成此面具的直达链接",
|
||||||
|
Action: "复制链接",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
|
@ -87,9 +87,11 @@ const cs: PartialLocaleType = {
|
|||||||
SubTitle: "Zobrazit v náhledu bubliny",
|
SubTitle: "Zobrazit v náhledu bubliny",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Úvodní obrazovka Masek",
|
Title: "Úvodní obrazovka Masek",
|
||||||
SubTitle: "Před zahájením nového chatu zobrazte úvodní obrazovku Masek",
|
SubTitle: "Před zahájením nového chatu zobrazte úvodní obrazovku Masek",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Deaktivovat automatické dokončování",
|
Title: "Deaktivovat automatické dokončování",
|
||||||
|
@ -87,9 +87,11 @@ const de: PartialLocaleType = {
|
|||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Preview markdown in bubble",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle: "Show a mask splash screen before starting new chat",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Autovervollständigung deaktivieren",
|
Title: "Autovervollständigung deaktivieren",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
import { LocaleType } from "./index";
|
import { LocaleType } from "./index";
|
||||||
|
|
||||||
|
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
|
||||||
const en: LocaleType = {
|
const en: LocaleType = {
|
||||||
WIP: "Coming Soon...",
|
WIP: "Coming Soon...",
|
||||||
Error: {
|
Error: {
|
||||||
@ -87,6 +88,10 @@ const en: LocaleType = {
|
|||||||
Select: "Select",
|
Select: "Select",
|
||||||
Preview: "Preview",
|
Preview: "Preview",
|
||||||
},
|
},
|
||||||
|
Image: {
|
||||||
|
Toast: "Capturing Image...",
|
||||||
|
Modal: "Long press or right click to save image",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Select: {
|
Select: {
|
||||||
Search: "Search",
|
Search: "Search",
|
||||||
@ -157,9 +162,15 @@ const en: LocaleType = {
|
|||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Preview markdown in bubble",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle: "Show a mask splash screen before starting new chat",
|
||||||
},
|
},
|
||||||
|
Builtin: {
|
||||||
|
Title: "Hide Builtin Masks",
|
||||||
|
SubTitle: "Hide builtin masks in mask list",
|
||||||
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Disable auto-completion",
|
Title: "Disable auto-completion",
|
||||||
@ -295,6 +306,11 @@ const en: LocaleType = {
|
|||||||
Title: "Hide Context Prompts",
|
Title: "Hide Context Prompts",
|
||||||
SubTitle: "Do not show in-context prompts in chat",
|
SubTitle: "Do not show in-context prompts in chat",
|
||||||
},
|
},
|
||||||
|
Share: {
|
||||||
|
Title: "Share This Mask",
|
||||||
|
SubTitle: "Generate a link to this mask",
|
||||||
|
Action: "Copy Link",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NewChat: {
|
NewChat: {
|
||||||
|
@ -87,9 +87,11 @@ const es: PartialLocaleType = {
|
|||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Preview markdown in bubble",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle: "Show a mask splash screen before starting new chat",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Desactivar autocompletado",
|
Title: "Desactivar autocompletado",
|
||||||
|
@ -88,10 +88,12 @@ const fr: PartialLocaleType = {
|
|||||||
SubTitle: "Aperçu du Markdown dans une bulle",
|
SubTitle: "Aperçu du Markdown dans une bulle",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Écran de masque",
|
Title: "Écran de masque",
|
||||||
SubTitle:
|
SubTitle:
|
||||||
"Afficher un écran de masque avant de démarrer une nouvelle discussion",
|
"Afficher un écran de masque avant de démarrer une nouvelle discussion",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Désactiver la saisie semi-automatique",
|
Title: "Désactiver la saisie semi-automatique",
|
||||||
|
@ -13,6 +13,7 @@ import no from "./no";
|
|||||||
import cs from "./cs";
|
import cs from "./cs";
|
||||||
import ko from "./ko";
|
import ko from "./ko";
|
||||||
import ar from "./ar";
|
import ar from "./ar";
|
||||||
|
import bn from "./bn";
|
||||||
import { merge } from "../utils/merge";
|
import { merge } from "../utils/merge";
|
||||||
|
|
||||||
import type { LocaleType } from "./cn";
|
import type { LocaleType } from "./cn";
|
||||||
@ -34,6 +35,7 @@ const ALL_LANGS = {
|
|||||||
cs,
|
cs,
|
||||||
no,
|
no,
|
||||||
ar,
|
ar,
|
||||||
|
bn,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Lang = keyof typeof ALL_LANGS;
|
export type Lang = keyof typeof ALL_LANGS;
|
||||||
@ -56,6 +58,7 @@ export const ALL_LANG_OPTIONS: Record<Lang, string> = {
|
|||||||
cs: "Čeština",
|
cs: "Čeština",
|
||||||
no: "Nynorsk",
|
no: "Nynorsk",
|
||||||
ar: "العربية",
|
ar: "العربية",
|
||||||
|
bn: "বাংলা",
|
||||||
};
|
};
|
||||||
|
|
||||||
const LANG_KEY = "lang";
|
const LANG_KEY = "lang";
|
||||||
|
@ -87,9 +87,11 @@ const it: PartialLocaleType = {
|
|||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Preview markdown in bubble",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle: "Show a mask splash screen before starting new chat",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Disabilita l'auto completamento",
|
Title: "Disabilita l'auto completamento",
|
||||||
|
@ -104,9 +104,11 @@ const jp: PartialLocaleType = {
|
|||||||
SubTitle: "プレビューバブルでマークダウンコンテンツをプレビュー",
|
SubTitle: "プレビューバブルでマークダウンコンテンツをプレビュー",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "キャラクターページ",
|
Title: "キャラクターページ",
|
||||||
SubTitle: "新規チャット作成時にキャラクターページを表示する",
|
SubTitle: "新規チャット作成時にキャラクターページを表示する",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "プロンプトの自動補完を無効にする",
|
Title: "プロンプトの自動補完を無効にする",
|
||||||
|
@ -87,9 +87,11 @@ const ko: PartialLocaleType = {
|
|||||||
SubTitle: "버블에서 마크다운 미리 보기",
|
SubTitle: "버블에서 마크다운 미리 보기",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "마스크 시작 화면",
|
Title: "마스크 시작 화면",
|
||||||
SubTitle: "새로운 채팅 시작 전에 마스크 시작 화면 표시",
|
SubTitle: "새로운 채팅 시작 전에 마스크 시작 화면 표시",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "자동 완성 비활성화",
|
Title: "자동 완성 비활성화",
|
||||||
|
@ -87,9 +87,11 @@ const ru: PartialLocaleType = {
|
|||||||
SubTitle: "Предварительный просмотр markdown в пузыре",
|
SubTitle: "Предварительный просмотр markdown в пузыре",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Экран заставки маски",
|
Title: "Экран заставки маски",
|
||||||
SubTitle: "Показывать экран заставки маски перед началом нового чата",
|
SubTitle: "Показывать экран заставки маски перед началом нового чата",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Отключить автозаполнение",
|
Title: "Отключить автозаполнение",
|
||||||
|
@ -87,9 +87,11 @@ const tr: PartialLocaleType = {
|
|||||||
SubTitle: "Preview markdown in bubble",
|
SubTitle: "Preview markdown in bubble",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Show a mask splash screen before starting new chat",
|
SubTitle: "Show a mask splash screen before starting new chat",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Otomatik tamamlamayı devre dışı bırak",
|
Title: "Otomatik tamamlamayı devre dışı bırak",
|
||||||
|
@ -85,9 +85,11 @@ const tw: PartialLocaleType = {
|
|||||||
SubTitle: "在预览气泡中预览 Markdown 内容",
|
SubTitle: "在预览气泡中预览 Markdown 内容",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "面具启动页",
|
Title: "面具启动页",
|
||||||
SubTitle: "新建聊天时,展示面具启动页",
|
SubTitle: "新建聊天时,展示面具启动页",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "停用提示詞自動補齊",
|
Title: "停用提示詞自動補齊",
|
||||||
|
@ -87,9 +87,11 @@ const vi: PartialLocaleType = {
|
|||||||
SubTitle: "Xem trước nội dung markdown bằng bong bóng",
|
SubTitle: "Xem trước nội dung markdown bằng bong bóng",
|
||||||
},
|
},
|
||||||
Mask: {
|
Mask: {
|
||||||
|
Splash: {
|
||||||
Title: "Mask Splash Screen",
|
Title: "Mask Splash Screen",
|
||||||
SubTitle: "Chớp màn hình khi bắt đầu cuộc trò chuyện mới",
|
SubTitle: "Chớp màn hình khi bắt đầu cuộc trò chuyện mới",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
Prompt: {
|
Prompt: {
|
||||||
Disable: {
|
Disable: {
|
||||||
Title: "Vô hiệu hóa chức năng tự động hoàn thành",
|
Title: "Vô hiệu hóa chức năng tự động hoàn thành",
|
||||||
|
@ -420,7 +420,7 @@ export const useChatStore = create<ChatStore>()(
|
|||||||
modelConfig.sendMemory &&
|
modelConfig.sendMemory &&
|
||||||
session.memoryPrompt &&
|
session.memoryPrompt &&
|
||||||
session.memoryPrompt.length > 0 &&
|
session.memoryPrompt.length > 0 &&
|
||||||
session.lastSummarizeIndex <= clearContextIndex;
|
session.lastSummarizeIndex > clearContextIndex;
|
||||||
const longTermMemoryPrompts = shouldSendLongTermMemory
|
const longTermMemoryPrompts = shouldSendLongTermMemory
|
||||||
? [get().getMemoryPrompt()]
|
? [get().getMemoryPrompt()]
|
||||||
: [];
|
: [];
|
||||||
|
@ -32,6 +32,7 @@ export const DEFAULT_CONFIG = {
|
|||||||
disablePromptHint: false,
|
disablePromptHint: false,
|
||||||
|
|
||||||
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
dontShowMaskSplashScreen: false, // dont show splash screen when create chat
|
||||||
|
hideBuiltinMasks: false, // dont add builtin masks
|
||||||
|
|
||||||
models: DEFAULT_MODELS as any as LLMModel[],
|
models: DEFAULT_MODELS as any as LLMModel[],
|
||||||
|
|
||||||
@ -136,9 +137,9 @@ export const useAppConfig = create<ChatConfigStore>()(
|
|||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Config,
|
name: StoreKey.Config,
|
||||||
version: 3.3,
|
version: 3.4,
|
||||||
migrate(persistedState, version) {
|
migrate(persistedState, version) {
|
||||||
if (version === 3.3) return persistedState as any;
|
if (version === 3.4) return persistedState as any;
|
||||||
|
|
||||||
const state = persistedState as ChatConfig;
|
const state = persistedState as ChatConfig;
|
||||||
state.modelConfig.sendMemory = true;
|
state.modelConfig.sendMemory = true;
|
||||||
@ -148,6 +149,7 @@ export const useAppConfig = create<ChatConfigStore>()(
|
|||||||
state.modelConfig.top_p = 1;
|
state.modelConfig.top_p = 1;
|
||||||
state.modelConfig.template = DEFAULT_INPUT_TEMPLATE;
|
state.modelConfig.template = DEFAULT_INPUT_TEMPLATE;
|
||||||
state.dontShowMaskSplashScreen = false;
|
state.dontShowMaskSplashScreen = false;
|
||||||
|
state.hideBuiltinMasks = false;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
@ -90,6 +90,7 @@ export const useMaskStore = create<MaskStore>()(
|
|||||||
(a, b) => b.id - a.id,
|
(a, b) => b.id - a.id,
|
||||||
);
|
);
|
||||||
const config = useAppConfig.getState();
|
const config = useAppConfig.getState();
|
||||||
|
if (config.hideBuiltinMasks) return userMasks;
|
||||||
const buildinMasks = BUILTIN_MASKS.map(
|
const buildinMasks = BUILTIN_MASKS.map(
|
||||||
(m) =>
|
(m) =>
|
||||||
({
|
({
|
||||||
|
Loading…
Reference in New Issue
Block a user