mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2026-02-21 22:04:25 +08:00
Compare commits
13 Commits
v2.14.1
...
cec7ca6fd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cec7ca6fd5 | ||
|
|
cf1c8e8f2a | ||
|
|
d948be2372 | ||
|
|
cc28aef625 | ||
|
|
036358de7c | ||
|
|
0958b9ee12 | ||
|
|
aff1d7ecd6 | ||
|
|
42fdbd9bb8 | ||
|
|
034c82e514 | ||
|
|
14ff46b5cd | ||
|
|
c9099ca0a5 | ||
|
|
58b144b345 | ||
|
|
af21c57e77 |
@@ -13,7 +13,7 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
|
||||
|
||||
if (config.disableGPT4) {
|
||||
remoteModelRes.data = remoteModelRes.data.filter(
|
||||
(m) => !m.id.startsWith("gpt-4"),
|
||||
(m) => !m.id.startsWith("gpt-4") || m.id.startsWith("gpt-4o-mini"),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export function IconButton(props: {
|
||||
tabIndex?: number;
|
||||
autoFocus?: boolean;
|
||||
style?: CSSProperties;
|
||||
aria?: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
@@ -34,9 +35,11 @@ export function IconButton(props: {
|
||||
tabIndex={props.tabIndex}
|
||||
autoFocus={props.autoFocus}
|
||||
style={props.style}
|
||||
aria-label={props.aria}
|
||||
>
|
||||
{props.icon && (
|
||||
<div
|
||||
aria-label={props.text || props.title}
|
||||
className={
|
||||
styles["icon-button-icon"] +
|
||||
` ${props.type === "primary" && "no-dark"}`
|
||||
@@ -47,7 +50,12 @@ export function IconButton(props: {
|
||||
)}
|
||||
|
||||
{props.text && (
|
||||
<div className={styles["icon-button-text"]}>{props.text}</div>
|
||||
<div
|
||||
aria-label={props.text || props.title}
|
||||
className={styles["icon-button-text"]}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -231,10 +231,12 @@
|
||||
|
||||
animation: slide-in ease 0.3s;
|
||||
|
||||
$linear: linear-gradient(to right,
|
||||
rgba(0, 0, 0, 0),
|
||||
rgba(0, 0, 0, 1),
|
||||
rgba(0, 0, 0, 0));
|
||||
$linear: linear-gradient(
|
||||
to right,
|
||||
rgba(0, 0, 0, 0),
|
||||
rgba(0, 0, 0, 1),
|
||||
rgba(0, 0, 0, 0)
|
||||
);
|
||||
mask-image: $linear;
|
||||
|
||||
@mixin show {
|
||||
@@ -340,10 +342,14 @@
|
||||
margin: 0 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
|
||||
.chat-input-actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
transition: all ease 0.3s;
|
||||
background-color: #fff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,7 +373,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-user>.chat-message-container {
|
||||
.chat-message-user > .chat-message-container {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
@@ -450,23 +456,27 @@
|
||||
border: rgba($color: #888, $alpha: 0.2) 1px solid;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
$calc-image-width: calc(100vw/3*2/var(--image-count));
|
||||
$calc-image-width: calc(100vw / 3 * 2 / var(--image-count));
|
||||
|
||||
.chat-message-item-image-multi {
|
||||
width: $calc-image-width;
|
||||
height: $calc-image-width;
|
||||
}
|
||||
|
||||
|
||||
.chat-message-item-image {
|
||||
max-width: calc(100vw/3*2);
|
||||
max-width: calc(100vw / 3 * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
$max-image-width: calc(calc(1200px - var(--sidebar-width))/3*2/var(--image-count));
|
||||
$image-width: calc(calc(var(--window-width) - var(--sidebar-width))/3*2/var(--image-count));
|
||||
$max-image-width: calc(
|
||||
calc(1200px - var(--sidebar-width)) / 3 * 2 / var(--image-count)
|
||||
);
|
||||
$image-width: calc(
|
||||
calc(var(--window-width) - var(--sidebar-width)) / 3 * 2 /
|
||||
var(--image-count)
|
||||
);
|
||||
|
||||
.chat-message-item-image-multi {
|
||||
width: $image-width;
|
||||
@@ -476,7 +486,7 @@
|
||||
}
|
||||
|
||||
.chat-message-item-image {
|
||||
max-width: calc(calc(1200px - var(--sidebar-width))/3*2);
|
||||
max-width: calc(calc(1200px - var(--sidebar-width)) / 3 * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +504,7 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.chat-message-user>.chat-message-container>.chat-message-item {
|
||||
.chat-message-user > .chat-message-container > .chat-message-item {
|
||||
background-color: var(--second);
|
||||
|
||||
&:hover {
|
||||
@@ -605,7 +615,8 @@
|
||||
min-height: 68px;
|
||||
}
|
||||
|
||||
.chat-input:focus {}
|
||||
.chat-input:focus {
|
||||
}
|
||||
|
||||
.chat-input-send {
|
||||
background-color: var(--primary);
|
||||
@@ -624,4 +635,4 @@
|
||||
.chat-input-send {
|
||||
bottom: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ import {
|
||||
|
||||
import {
|
||||
copyToClipboard,
|
||||
selectOrCopy,
|
||||
isNotSelectRange,
|
||||
autoGrowTextArea,
|
||||
useMobileScreen,
|
||||
getMessageTextContent,
|
||||
@@ -250,6 +250,108 @@ function useSubmitHandler() {
|
||||
};
|
||||
}
|
||||
|
||||
function ChatMessageActions(props: {
|
||||
index: number;
|
||||
message: ChatMessage;
|
||||
position: { x: number; y: number };
|
||||
isHover: boolean;
|
||||
onUserStop: (messageId: string) => void;
|
||||
onResend: (message: ChatMessage) => void;
|
||||
onDelete: (messageId: string) => void;
|
||||
onPinMessage: (message: ChatMessage) => void;
|
||||
}) {
|
||||
const {
|
||||
index,
|
||||
message,
|
||||
position,
|
||||
isHover,
|
||||
onUserStop,
|
||||
onResend,
|
||||
onDelete,
|
||||
onPinMessage,
|
||||
} = props;
|
||||
|
||||
const actionsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [rect, setRect] = useState<DOMRect>({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
|
||||
const isUserMessage = message.role === "user";
|
||||
|
||||
const [translate, setTranslate] = useState({ x: 0, y: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
if (position.x && position.y && isHover) {
|
||||
const x = position.x - rect.x - (isUserMessage ? rect.width : 0);
|
||||
const y = position.y - rect.y - (isUserMessage ? rect.height : 0);
|
||||
setTranslate({ x, y });
|
||||
} else {
|
||||
setTranslate({ x: 0, y: 0 });
|
||||
}
|
||||
}, [position, isHover]);
|
||||
|
||||
useEffect(() => {
|
||||
const div = actionsRef.current;
|
||||
if (div) {
|
||||
const rect = div.getBoundingClientRect();
|
||||
setRect(rect);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={actionsRef} className={styles["chat-message-actions"]}>
|
||||
<div
|
||||
className={`${styles["chat-input-actions"]}`}
|
||||
style={{
|
||||
transform: `translate3d(${translate.x}px, ${translate.y}px, 0)`,
|
||||
}}
|
||||
>
|
||||
{message.streaming ? (
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Stop}
|
||||
icon={<StopIcon />}
|
||||
onClick={() => onUserStop(message.id ?? index)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Retry}
|
||||
icon={<ResetIcon />}
|
||||
onClick={() => onResend(message)}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Delete}
|
||||
icon={<DeleteIcon />}
|
||||
onClick={() => onDelete(message.id ?? index)}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Pin}
|
||||
icon={<PinIcon />}
|
||||
onClick={() => onPinMessage(message)}
|
||||
/>
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Copy}
|
||||
icon={<CopyIcon />}
|
||||
onClick={() => copyToClipboard(getMessageTextContent(message))}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export type RenderPrompt = Pick<Prompt, "title" | "content">;
|
||||
|
||||
export function PromptHints(props: {
|
||||
@@ -792,6 +894,13 @@ function _Chat() {
|
||||
const [attachImages, setAttachImages] = useState<string[]>([]);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const [isMessageContainerHover, setIsMessageContainerHover] =
|
||||
useState<boolean>(false);
|
||||
const [actionsPosition, setActionsPosition] = useState<{
|
||||
x: number;
|
||||
y: number;
|
||||
}>({ x: 0, y: 0 });
|
||||
|
||||
// prompt hints
|
||||
const promptStore = usePromptStore();
|
||||
const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]);
|
||||
@@ -947,16 +1056,24 @@ function _Chat() {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
const onRightClick = (e: any, message: ChatMessage) => {
|
||||
// copy to clipboard
|
||||
if (selectOrCopy(e.currentTarget, getMessageTextContent(message))) {
|
||||
if (userInput.length === 0) {
|
||||
setUserInput(getMessageTextContent(message));
|
||||
}
|
||||
|
||||
const onRightClick = (e: any) => {
|
||||
// move actions to the right click position
|
||||
if (isNotSelectRange()) {
|
||||
e.preventDefault();
|
||||
|
||||
setActionsPosition({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
});
|
||||
}
|
||||
};
|
||||
// handle mouse hover to reset actions position
|
||||
useEffect(() => {
|
||||
if (!isMessageContainerHover) {
|
||||
setActionsPosition({ x: 0, y: 0 });
|
||||
}
|
||||
}, [isMessageContainerHover]);
|
||||
|
||||
const deleteMessage = (msgId?: string) => {
|
||||
chatStore.updateCurrentSession(
|
||||
@@ -1337,6 +1454,8 @@ function _Chat() {
|
||||
<IconButton
|
||||
icon={<RenameIcon />}
|
||||
bordered
|
||||
title={Locale.Chat.EditMessage.Title}
|
||||
aria={Locale.Chat.EditMessage.Title}
|
||||
onClick={() => setIsEditingMessage(true)}
|
||||
/>
|
||||
</div>
|
||||
@@ -1356,6 +1475,8 @@ function _Chat() {
|
||||
<IconButton
|
||||
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
||||
bordered
|
||||
title={Locale.Chat.Actions.FullScreen}
|
||||
aria={Locale.Chat.Actions.FullScreen}
|
||||
onClick={() => {
|
||||
config.update(
|
||||
(config) => (config.tightBorder = !config.tightBorder),
|
||||
@@ -1401,12 +1522,20 @@ function _Chat() {
|
||||
isUser ? styles["chat-message-user"] : styles["chat-message"]
|
||||
}
|
||||
>
|
||||
<div className={styles["chat-message-container"]}>
|
||||
<div
|
||||
className={styles["chat-message-container"]}
|
||||
onClick={() => {
|
||||
setActionsPosition({ x: 0, y: 0 });
|
||||
}}
|
||||
onMouseEnter={() => setIsMessageContainerHover(true)}
|
||||
onMouseLeave={() => setIsMessageContainerHover(false)}
|
||||
>
|
||||
<div className={styles["chat-message-header"]}>
|
||||
<div className={styles["chat-message-avatar"]}>
|
||||
<div className={styles["chat-message-edit"]}>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
aria={Locale.Chat.Actions.Edit}
|
||||
onClick={async () => {
|
||||
const newMessage = await showPrompt(
|
||||
Locale.Chat.Actions.Edit,
|
||||
@@ -1457,46 +1586,16 @@ function _Chat() {
|
||||
</div>
|
||||
|
||||
{showActions && (
|
||||
<div className={styles["chat-message-actions"]}>
|
||||
<div className={styles["chat-input-actions"]}>
|
||||
{message.streaming ? (
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Stop}
|
||||
icon={<StopIcon />}
|
||||
onClick={() => onUserStop(message.id ?? i)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Retry}
|
||||
icon={<ResetIcon />}
|
||||
onClick={() => onResend(message)}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Delete}
|
||||
icon={<DeleteIcon />}
|
||||
onClick={() => onDelete(message.id ?? i)}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Pin}
|
||||
icon={<PinIcon />}
|
||||
onClick={() => onPinMessage(message)}
|
||||
/>
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Copy}
|
||||
icon={<CopyIcon />}
|
||||
onClick={() =>
|
||||
copyToClipboard(
|
||||
getMessageTextContent(message),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<ChatMessageActions
|
||||
index={i}
|
||||
message={message}
|
||||
position={actionsPosition}
|
||||
isHover={isMessageContainerHover}
|
||||
onUserStop={onUserStop}
|
||||
onResend={onResend}
|
||||
onDelete={onDelete}
|
||||
onPinMessage={onPinMessage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{showTyping && (
|
||||
@@ -1513,7 +1612,7 @@ function _Chat() {
|
||||
message.content.length === 0 &&
|
||||
!isUser
|
||||
}
|
||||
onContextMenu={(e) => onRightClick(e, message)}
|
||||
onContextMenu={(e) => onRightClick(e)}
|
||||
onDoubleClickCapture={() => {
|
||||
if (!isMobileScreen) return;
|
||||
setUserInput(getMessageTextContent(message));
|
||||
|
||||
@@ -9,6 +9,7 @@ interface InputRangeProps {
|
||||
min: string;
|
||||
max: string;
|
||||
step: string;
|
||||
aria: string;
|
||||
}
|
||||
|
||||
export function InputRange({
|
||||
@@ -19,11 +20,13 @@ export function InputRange({
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
aria,
|
||||
}: InputRangeProps) {
|
||||
return (
|
||||
<div className={styles["input-range"] + ` ${className ?? ""}`}>
|
||||
{title || value}
|
||||
<input
|
||||
aria-label={aria}
|
||||
type="range"
|
||||
title={title}
|
||||
value={value}
|
||||
|
||||
@@ -127,6 +127,8 @@ export function MaskConfig(props: {
|
||||
onClose={() => setShowPicker(false)}
|
||||
>
|
||||
<div
|
||||
tabIndex={0}
|
||||
aria-label={Locale.Mask.Config.Avatar}
|
||||
onClick={() => setShowPicker(true)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
@@ -139,6 +141,7 @@ export function MaskConfig(props: {
|
||||
</ListItem>
|
||||
<ListItem title={Locale.Mask.Config.Name}>
|
||||
<input
|
||||
aria-label={Locale.Mask.Config.Name}
|
||||
type="text"
|
||||
value={props.mask.name}
|
||||
onInput={(e) =>
|
||||
@@ -153,6 +156,7 @@ export function MaskConfig(props: {
|
||||
subTitle={Locale.Mask.Config.HideContext.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Mask.Config.HideContext.Title}
|
||||
type="checkbox"
|
||||
checked={props.mask.hideContext}
|
||||
onChange={(e) => {
|
||||
@@ -169,6 +173,7 @@ export function MaskConfig(props: {
|
||||
subTitle={Locale.Mask.Config.Share.SubTitle}
|
||||
>
|
||||
<IconButton
|
||||
aria={Locale.Mask.Config.Share.Title}
|
||||
icon={<CopyIcon />}
|
||||
text={Locale.Mask.Config.Share.Action}
|
||||
onClick={copyMaskLink}
|
||||
@@ -182,6 +187,7 @@ export function MaskConfig(props: {
|
||||
subTitle={Locale.Mask.Config.Sync.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Mask.Config.Sync.Title}
|
||||
type="checkbox"
|
||||
checked={props.mask.syncGlobalConfig}
|
||||
onChange={async (e) => {
|
||||
|
||||
@@ -17,6 +17,7 @@ export function ModelConfigList(props: {
|
||||
<>
|
||||
<ListItem title={Locale.Settings.Model}>
|
||||
<Select
|
||||
aria-label={Locale.Settings.Model}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
const [model, providerName] = e.currentTarget.value.split("@");
|
||||
@@ -40,6 +41,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.Temperature.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.Temperature.Title}
|
||||
value={props.modelConfig.temperature?.toFixed(1)}
|
||||
min="0"
|
||||
max="1" // lets limit it to 0-1
|
||||
@@ -59,6 +61,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.TopP.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.TopP.Title}
|
||||
value={(props.modelConfig.top_p ?? 1).toFixed(1)}
|
||||
min="0"
|
||||
max="1"
|
||||
@@ -78,6 +81,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.MaxTokens.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.MaxTokens.Title}
|
||||
type="number"
|
||||
min={1024}
|
||||
max={512000}
|
||||
@@ -100,6 +104,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.PresencePenalty.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.PresencePenalty.Title}
|
||||
value={props.modelConfig.presence_penalty?.toFixed(1)}
|
||||
min="-2"
|
||||
max="2"
|
||||
@@ -121,6 +126,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.FrequencyPenalty.Title}
|
||||
value={props.modelConfig.frequency_penalty?.toFixed(1)}
|
||||
min="-2"
|
||||
max="2"
|
||||
@@ -142,6 +148,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.InjectSystemPrompts.Title}
|
||||
type="checkbox"
|
||||
checked={props.modelConfig.enableInjectSystemPrompts}
|
||||
onChange={(e) =>
|
||||
@@ -159,6 +166,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.InputTemplate.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.InputTemplate.Title}
|
||||
type="text"
|
||||
value={props.modelConfig.template}
|
||||
onChange={(e) =>
|
||||
@@ -175,6 +183,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.HistoryCount.Title}
|
||||
title={props.modelConfig.historyMessageCount.toString()}
|
||||
value={props.modelConfig.historyMessageCount}
|
||||
min="0"
|
||||
@@ -193,6 +202,7 @@ export function ModelConfigList(props: {
|
||||
subTitle={Locale.Settings.CompressThreshold.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.CompressThreshold.Title}
|
||||
type="number"
|
||||
min={500}
|
||||
max={4000}
|
||||
@@ -208,6 +218,7 @@ export function ModelConfigList(props: {
|
||||
</ListItem>
|
||||
<ListItem title={Locale.Memory.Title} subTitle={Locale.Memory.Send}>
|
||||
<input
|
||||
aria-label={Locale.Memory.Title}
|
||||
type="checkbox"
|
||||
checked={props.modelConfig.sendMemory}
|
||||
onChange={(e) =>
|
||||
|
||||
@@ -192,6 +192,7 @@ export function ControlParam(props: {
|
||||
required={item.required}
|
||||
>
|
||||
<Select
|
||||
aria-label={item.name}
|
||||
value={props.data[item.value]}
|
||||
onChange={(e) => {
|
||||
props.onChange(item.value, e.currentTarget.value);
|
||||
@@ -216,6 +217,7 @@ export function ControlParam(props: {
|
||||
required={item.required}
|
||||
>
|
||||
<input
|
||||
aria-label={item.name}
|
||||
type="number"
|
||||
min={item.min}
|
||||
max={item.max}
|
||||
@@ -235,6 +237,7 @@ export function ControlParam(props: {
|
||||
required={item.required}
|
||||
>
|
||||
<input
|
||||
aria-label={item.name}
|
||||
type="text"
|
||||
value={props.data[item.value]}
|
||||
style={{ maxWidth: "100%", width: "100%" }}
|
||||
|
||||
@@ -133,6 +133,7 @@ export function Sd() {
|
||||
{showMaxIcon && (
|
||||
<div className="window-action-button">
|
||||
<IconButton
|
||||
aria={Locale.Chat.Actions.FullScreen}
|
||||
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
|
||||
bordered
|
||||
onClick={() => {
|
||||
|
||||
@@ -246,6 +246,7 @@ function DangerItems() {
|
||||
subTitle={Locale.Settings.Danger.Reset.SubTitle}
|
||||
>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Danger.Reset.Title}
|
||||
text={Locale.Settings.Danger.Reset.Action}
|
||||
onClick={async () => {
|
||||
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
|
||||
@@ -260,6 +261,7 @@ function DangerItems() {
|
||||
subTitle={Locale.Settings.Danger.Clear.SubTitle}
|
||||
>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Danger.Clear.Title}
|
||||
text={Locale.Settings.Danger.Clear.Action}
|
||||
onClick={async () => {
|
||||
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
|
||||
@@ -513,6 +515,7 @@ function SyncItems() {
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Sync.CloudState + Locale.UI.Config}
|
||||
icon={<ConfigIcon />}
|
||||
text={Locale.UI.Config}
|
||||
onClick={() => {
|
||||
@@ -543,6 +546,7 @@ function SyncItems() {
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Sync.LocalState + Locale.UI.Export}
|
||||
icon={<UploadIcon />}
|
||||
text={Locale.UI.Export}
|
||||
onClick={() => {
|
||||
@@ -550,6 +554,7 @@ function SyncItems() {
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Sync.LocalState + Locale.UI.Import}
|
||||
icon={<DownloadIcon />}
|
||||
text={Locale.UI.Import}
|
||||
onClick={() => {
|
||||
@@ -687,6 +692,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.CustomEndpoint.Title}
|
||||
type="checkbox"
|
||||
checked={accessStore.useCustomConfig}
|
||||
onChange={(e) =>
|
||||
@@ -706,6 +712,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.OpenAI.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.openaiUrl}
|
||||
placeholder={OPENAI_BASE_URL}
|
||||
@@ -721,6 +728,8 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria={Locale.Settings.ShowPassword}
|
||||
aria-label={Locale.Settings.Access.OpenAI.ApiKey.Title}
|
||||
value={accessStore.openaiApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||
@@ -744,6 +753,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Azure.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.azureUrl}
|
||||
placeholder={Azure.ExampleEndpoint}
|
||||
@@ -759,6 +769,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Azure.ApiKey.Title}
|
||||
value={accessStore.azureApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Azure.ApiKey.Placeholder}
|
||||
@@ -774,6 +785,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Azure.ApiVerion.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Azure.ApiVerion.Title}
|
||||
type="text"
|
||||
value={accessStore.azureApiVersion}
|
||||
placeholder="2023-08-01-preview"
|
||||
@@ -798,6 +810,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Google.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.googleUrl}
|
||||
placeholder={Google.ExampleEndpoint}
|
||||
@@ -813,6 +826,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Google.ApiKey.Title}
|
||||
value={accessStore.googleApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||
@@ -828,6 +842,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Google.ApiVersion.Title}
|
||||
type="text"
|
||||
value={accessStore.googleApiVersion}
|
||||
placeholder="2023-08-01-preview"
|
||||
@@ -843,6 +858,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle}
|
||||
>
|
||||
<Select
|
||||
aria-label={Locale.Settings.Access.Google.GoogleSafetySettings.Title}
|
||||
value={accessStore.googleSafetySettings}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
@@ -873,6 +889,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Anthropic.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.anthropicUrl}
|
||||
placeholder={Anthropic.ExampleEndpoint}
|
||||
@@ -888,6 +905,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Anthropic.ApiKey.Title}
|
||||
value={accessStore.anthropicApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder}
|
||||
@@ -903,6 +921,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Anthropic.ApiVerion.Title}
|
||||
type="text"
|
||||
value={accessStore.anthropicApiVersion}
|
||||
placeholder={Anthropic.Vision}
|
||||
@@ -924,6 +943,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Baidu.Endpoint.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Baidu.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.baiduUrl}
|
||||
placeholder={Baidu.ExampleEndpoint}
|
||||
@@ -939,6 +959,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Baidu.ApiKey.Title}
|
||||
value={accessStore.baiduApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Baidu.ApiKey.Placeholder}
|
||||
@@ -954,6 +975,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Baidu.SecretKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Baidu.SecretKey.Title}
|
||||
value={accessStore.baiduSecretKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Baidu.SecretKey.Placeholder}
|
||||
@@ -975,6 +997,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Tencent.Endpoint.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Tencent.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.tencentUrl}
|
||||
placeholder={Tencent.ExampleEndpoint}
|
||||
@@ -990,6 +1013,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Tencent.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Tencent.ApiKey.Title}
|
||||
value={accessStore.tencentSecretId}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Tencent.ApiKey.Placeholder}
|
||||
@@ -1005,6 +1029,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Tencent.SecretKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Tencent.SecretKey.Title}
|
||||
value={accessStore.tencentSecretKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Tencent.SecretKey.Placeholder}
|
||||
@@ -1029,6 +1054,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.ByteDance.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.bytedanceUrl}
|
||||
placeholder={ByteDance.ExampleEndpoint}
|
||||
@@ -1044,6 +1070,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.ByteDance.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.ByteDance.ApiKey.Title}
|
||||
value={accessStore.bytedanceApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.ByteDance.ApiKey.Placeholder}
|
||||
@@ -1068,6 +1095,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Alibaba.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.alibabaUrl}
|
||||
placeholder={Alibaba.ExampleEndpoint}
|
||||
@@ -1083,6 +1111,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Alibaba.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Alibaba.ApiKey.Title}
|
||||
value={accessStore.alibabaApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Alibaba.ApiKey.Placeholder}
|
||||
@@ -1107,6 +1136,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Moonshot.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.moonshotUrl}
|
||||
placeholder={Moonshot.ExampleEndpoint}
|
||||
@@ -1122,6 +1152,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Moonshot.ApiKey.Title}
|
||||
value={accessStore.moonshotApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder}
|
||||
@@ -1146,6 +1177,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Stability.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.stabilityUrl}
|
||||
placeholder={Stability.ExampleEndpoint}
|
||||
@@ -1161,6 +1193,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Stability.ApiKey.Title}
|
||||
value={accessStore.stabilityApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder}
|
||||
@@ -1184,6 +1217,7 @@ export function Settings() {
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.Iflytek.Endpoint.Title}
|
||||
type="text"
|
||||
value={accessStore.iflytekUrl}
|
||||
placeholder={Iflytek.ExampleEndpoint}
|
||||
@@ -1199,6 +1233,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Iflytek.ApiKey.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Iflytek.ApiKey.Title}
|
||||
value={accessStore.iflytekApiKey}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Iflytek.ApiKey.Placeholder}
|
||||
@@ -1215,6 +1250,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Iflytek.ApiSecret.SubTitle}
|
||||
>
|
||||
<PasswordInput
|
||||
aria-label={Locale.Settings.Access.Iflytek.ApiSecret.Title}
|
||||
value={accessStore.iflytekApiSecret}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Access.Iflytek.ApiSecret.Placeholder}
|
||||
@@ -1244,6 +1280,7 @@ export function Settings() {
|
||||
<div className="window-action-button"></div>
|
||||
<div className="window-action-button">
|
||||
<IconButton
|
||||
aria={Locale.UI.Close}
|
||||
icon={<CloseIcon />}
|
||||
onClick={() => navigate(Path.Home)}
|
||||
bordered
|
||||
@@ -1267,6 +1304,8 @@ export function Settings() {
|
||||
open={showEmojiPicker}
|
||||
>
|
||||
<div
|
||||
aria-label={Locale.Settings.Avatar}
|
||||
tabIndex={0}
|
||||
className={styles.avatar}
|
||||
onClick={() => {
|
||||
setShowEmojiPicker(!showEmojiPicker);
|
||||
@@ -1304,6 +1343,7 @@ export function Settings() {
|
||||
|
||||
<ListItem title={Locale.Settings.SendKey}>
|
||||
<Select
|
||||
aria-label={Locale.Settings.SendKey}
|
||||
value={config.submitKey}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
@@ -1322,6 +1362,7 @@ export function Settings() {
|
||||
|
||||
<ListItem title={Locale.Settings.Theme}>
|
||||
<Select
|
||||
aria-label={Locale.Settings.Theme}
|
||||
value={config.theme}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
@@ -1339,6 +1380,7 @@ export function Settings() {
|
||||
|
||||
<ListItem title={Locale.Settings.Lang.Name}>
|
||||
<Select
|
||||
aria-label={Locale.Settings.Lang.Name}
|
||||
value={getLang()}
|
||||
onChange={(e) => {
|
||||
changeLang(e.target.value as any);
|
||||
@@ -1357,6 +1399,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.FontSize.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
aria={Locale.Settings.FontSize.Title}
|
||||
title={`${config.fontSize ?? 14}px`}
|
||||
value={config.fontSize}
|
||||
min="12"
|
||||
@@ -1376,6 +1419,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.FontFamily.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.FontFamily.Title}
|
||||
type="text"
|
||||
value={config.fontFamily}
|
||||
placeholder={Locale.Settings.FontFamily.Placeholder}
|
||||
@@ -1392,6 +1436,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.AutoGenerateTitle.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.AutoGenerateTitle.Title}
|
||||
type="checkbox"
|
||||
checked={config.enableAutoGenerateTitle}
|
||||
onChange={(e) =>
|
||||
@@ -1408,6 +1453,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.SendPreviewBubble.Title}
|
||||
type="checkbox"
|
||||
checked={config.sendPreviewBubble}
|
||||
onChange={(e) =>
|
||||
@@ -1428,6 +1474,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Mask.Splash.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Mask.Splash.Title}
|
||||
type="checkbox"
|
||||
checked={!config.dontShowMaskSplashScreen}
|
||||
onChange={(e) =>
|
||||
@@ -1445,6 +1492,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Mask.Builtin.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Mask.Builtin.Title}
|
||||
type="checkbox"
|
||||
checked={config.hideBuiltinMasks}
|
||||
onChange={(e) =>
|
||||
@@ -1463,6 +1511,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Prompt.Disable.Title}
|
||||
type="checkbox"
|
||||
checked={config.disablePromptHint}
|
||||
onChange={(e) =>
|
||||
@@ -1482,6 +1531,7 @@ export function Settings() {
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
aria={Locale.Settings.Prompt.List + Locale.Settings.Prompt.Edit}
|
||||
icon={<EditIcon />}
|
||||
text={Locale.Settings.Prompt.Edit}
|
||||
onClick={() => setShowPromptModal(true)}
|
||||
@@ -1503,6 +1553,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.Provider.SubTitle}
|
||||
>
|
||||
<Select
|
||||
aria-label={Locale.Settings.Access.Provider.Title}
|
||||
value={accessStore.provider}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
@@ -1567,6 +1618,7 @@ export function Settings() {
|
||||
subTitle={Locale.Settings.Access.CustomModel.SubTitle}
|
||||
>
|
||||
<input
|
||||
aria-label={Locale.Settings.Access.CustomModel.Title}
|
||||
type="text"
|
||||
value={config.customModels}
|
||||
placeholder="model1,model2,model3"
|
||||
|
||||
@@ -297,12 +297,20 @@ export function SideBar(props: { className?: string }) {
|
||||
</div>
|
||||
<div className={styles["sidebar-action"]}>
|
||||
<Link to={Path.Settings}>
|
||||
<IconButton icon={<SettingsIcon />} shadow />
|
||||
<IconButton
|
||||
aria={Locale.Settings.Title}
|
||||
icon={<SettingsIcon />}
|
||||
shadow
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles["sidebar-action"]}>
|
||||
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
|
||||
<IconButton icon={<GithubIcon />} shadow />
|
||||
<IconButton
|
||||
aria={Locale.Export.MessageFromChatGPT}
|
||||
icon={<GithubIcon />}
|
||||
shadow
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -265,9 +265,10 @@ export function Input(props: InputProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||
export function PasswordInput(
|
||||
props: HTMLProps<HTMLInputElement> & { aria?: string },
|
||||
) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
function changeVisibility() {
|
||||
setVisible(!visible);
|
||||
}
|
||||
@@ -275,6 +276,7 @@ export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||
return (
|
||||
<div className={"password-input-container"}>
|
||||
<IconButton
|
||||
aria={props.aria}
|
||||
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||
onClick={changeVisibility}
|
||||
className={"password-eye"}
|
||||
|
||||
@@ -119,10 +119,16 @@ export const getServerSideConfig = () => {
|
||||
|
||||
if (disableGPT4) {
|
||||
if (customModels) customModels += ",";
|
||||
customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4"))
|
||||
customModels += DEFAULT_MODELS.filter(
|
||||
(m) => m.name.startsWith("gpt-4") && !m.name.startsWith("gpt-4o-mini"),
|
||||
)
|
||||
.map((m) => "-" + m.name)
|
||||
.join(",");
|
||||
if (defaultModel.startsWith("gpt-4")) defaultModel = "";
|
||||
if (
|
||||
defaultModel.startsWith("gpt-4") &&
|
||||
!defaultModel.startsWith("gpt-4o-mini")
|
||||
)
|
||||
defaultModel = "";
|
||||
}
|
||||
|
||||
const isStability = !!process.env.STABILITY_API_KEY;
|
||||
|
||||
@@ -243,6 +243,7 @@ export const KnowledgeCutOffDate: Record<string, string> = {
|
||||
"gpt-4-turbo-preview": "2023-12",
|
||||
"gpt-4o": "2023-10",
|
||||
"gpt-4o-2024-05-13": "2023-10",
|
||||
"gpt-4o-2024-08-06": "2023-10",
|
||||
"gpt-4o-mini": "2023-10",
|
||||
"gpt-4o-mini-2024-07-18": "2023-10",
|
||||
"gpt-4-vision-preview": "2023-04",
|
||||
@@ -264,6 +265,7 @@ const openaiModels = [
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-4o",
|
||||
"gpt-4o-2024-05-13",
|
||||
"gpt-4o-2024-08-06",
|
||||
"gpt-4o-mini",
|
||||
"gpt-4o-mini-2024-07-18",
|
||||
"gpt-4-vision-preview",
|
||||
|
||||
@@ -42,6 +42,7 @@ const cn = {
|
||||
PinToastAction: "查看",
|
||||
Delete: "删除",
|
||||
Edit: "编辑",
|
||||
FullScreen: "全屏",
|
||||
},
|
||||
Commands: {
|
||||
new: "新建聊天",
|
||||
@@ -132,6 +133,7 @@ const cn = {
|
||||
Settings: {
|
||||
Title: "设置",
|
||||
SubTitle: "所有设置选项",
|
||||
ShowPassword: "显示密码",
|
||||
|
||||
Danger: {
|
||||
Reset: {
|
||||
|
||||
@@ -44,6 +44,7 @@ const en: LocaleType = {
|
||||
PinToastAction: "View",
|
||||
Delete: "Delete",
|
||||
Edit: "Edit",
|
||||
FullScreen: "FullScreen",
|
||||
},
|
||||
Commands: {
|
||||
new: "Start a new chat",
|
||||
@@ -135,6 +136,7 @@ const en: LocaleType = {
|
||||
Settings: {
|
||||
Title: "Settings",
|
||||
SubTitle: "All Settings",
|
||||
ShowPassword: "ShowPassword",
|
||||
Danger: {
|
||||
Reset: {
|
||||
Title: "Reset All Settings",
|
||||
|
||||
@@ -145,15 +145,13 @@ export function isFirefox() {
|
||||
);
|
||||
}
|
||||
|
||||
export function selectOrCopy(el: HTMLElement, content: string) {
|
||||
export function isNotSelectRange() {
|
||||
const currentSelection = window.getSelection();
|
||||
|
||||
if (currentSelection?.type === "Range") {
|
||||
return false;
|
||||
}
|
||||
|
||||
copyToClipboard(content);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user