Compare commits

...

13 Commits

Author SHA1 Message Date
Nacho.L
cec7ca6fd5 Enhance message context menu 2024-08-09 14:52:33 +08:00
Dogtiti
cf1c8e8f2a Merge pull request #5233 from ConnectAI-E/feature/bangpt
Feature/bangpt
2024-08-08 17:27:50 +08:00
lyf
d948be2372 Merge branch 'feature/bangpt' of ssh://github.com/ConnectAI-E/ChatGPT-Next-Web into feature/bangpt 2024-08-08 16:25:49 +08:00
lyf
cc28aef625 add gpt-4o-2024-08-06 2024-08-08 16:23:40 +08:00
Dogtiti
036358de7c Merge pull request #5219 from ConnectAI-E/feature/access
修复ChatGPTNextWeb页面的无障碍问题
2024-08-08 13:59:16 +08:00
lyf
0958b9ee12 tsxiugai 2024-08-08 13:08:56 +08:00
Dogtiti
aff1d7ecd6 Merge pull request #5223 from 122cygf/main
修复拼写错误
2024-08-07 21:29:43 +08:00
lzz
42fdbd9bb8 Fix spelling errors 2024-08-07 17:22:32 +08:00
lyf
034c82e514 修改设置的无障碍 2024-08-07 13:39:23 +08:00
lyf
14ff46b5cd 解决按钮无障碍 2024-08-07 13:01:08 +08:00
lyf
c9099ca0a5 无障碍按钮和链接 2024-08-07 10:55:02 +08:00
Dogtiti
58b144b345 Merge pull request #5218 from ConnectAI-E/feature/bangpt
解决禁止gpt4时禁止gtp4o-mini问题
2024-08-07 10:45:38 +08:00
lyf
af21c57e77 ban gpt4 2024-08-07 10:15:39 +08:00
17 changed files with 289 additions and 75 deletions

View File

@@ -13,7 +13,7 @@ function getModels(remoteModelRes: OpenAIListModelResponse) {
if (config.disableGPT4) { if (config.disableGPT4) {
remoteModelRes.data = remoteModelRes.data.filter( remoteModelRes.data = remoteModelRes.data.filter(
(m) => !m.id.startsWith("gpt-4"), (m) => !m.id.startsWith("gpt-4") || m.id.startsWith("gpt-4o-mini"),
); );
} }

View File

@@ -18,6 +18,7 @@ export function IconButton(props: {
tabIndex?: number; tabIndex?: number;
autoFocus?: boolean; autoFocus?: boolean;
style?: CSSProperties; style?: CSSProperties;
aria?: string;
}) { }) {
return ( return (
<button <button
@@ -34,9 +35,11 @@ export function IconButton(props: {
tabIndex={props.tabIndex} tabIndex={props.tabIndex}
autoFocus={props.autoFocus} autoFocus={props.autoFocus}
style={props.style} style={props.style}
aria-label={props.aria}
> >
{props.icon && ( {props.icon && (
<div <div
aria-label={props.text || props.title}
className={ className={
styles["icon-button-icon"] + styles["icon-button-icon"] +
` ${props.type === "primary" && "no-dark"}` ` ${props.type === "primary" && "no-dark"}`
@@ -47,7 +50,12 @@ export function IconButton(props: {
)} )}
{props.text && ( {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> </button>
); );

View File

@@ -231,10 +231,12 @@
animation: slide-in ease 0.3s; animation: slide-in ease 0.3s;
$linear: linear-gradient(to right, $linear: linear-gradient(
rgba(0, 0, 0, 0), to right,
rgba(0, 0, 0, 1), rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0)); rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0)
);
mask-image: $linear; mask-image: $linear;
@mixin show { @mixin show {
@@ -340,10 +342,14 @@
margin: 0 10px; margin: 0 10px;
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
z-index: 1;
.chat-input-actions { .chat-input-actions {
display: flex; display: flex;
flex-wrap: nowrap; 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; align-items: flex-end;
} }
@@ -450,23 +456,27 @@
border: rgba($color: #888, $alpha: 0.2) 1px solid; border: rgba($color: #888, $alpha: 0.2) 1px solid;
} }
@media only screen and (max-width: 600px) { @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 { .chat-message-item-image-multi {
width: $calc-image-width; width: $calc-image-width;
height: $calc-image-width; height: $calc-image-width;
} }
.chat-message-item-image { .chat-message-item-image {
max-width: calc(100vw/3*2); max-width: calc(100vw / 3 * 2);
} }
} }
@media screen and (min-width: 600px) { @media screen and (min-width: 600px) {
$max-image-width: calc(calc(1200px - var(--sidebar-width))/3*2/var(--image-count)); $max-image-width: calc(
$image-width: calc(calc(var(--window-width) - var(--sidebar-width))/3*2/var(--image-count)); 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 { .chat-message-item-image-multi {
width: $image-width; width: $image-width;
@@ -476,7 +486,7 @@
} }
.chat-message-item-image { .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; 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); background-color: var(--second);
&:hover { &:hover {
@@ -605,7 +615,8 @@
min-height: 68px; min-height: 68px;
} }
.chat-input:focus {} .chat-input:focus {
}
.chat-input-send { .chat-input-send {
background-color: var(--primary); background-color: var(--primary);
@@ -624,4 +635,4 @@
.chat-input-send { .chat-input-send {
bottom: 30px; bottom: 30px;
} }
} }

View File

@@ -55,7 +55,7 @@ import {
import { import {
copyToClipboard, copyToClipboard,
selectOrCopy, isNotSelectRange,
autoGrowTextArea, autoGrowTextArea,
useMobileScreen, useMobileScreen,
getMessageTextContent, 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 type RenderPrompt = Pick<Prompt, "title" | "content">;
export function PromptHints(props: { export function PromptHints(props: {
@@ -792,6 +894,13 @@ function _Chat() {
const [attachImages, setAttachImages] = useState<string[]>([]); const [attachImages, setAttachImages] = useState<string[]>([]);
const [uploading, setUploading] = useState(false); 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 // prompt hints
const promptStore = usePromptStore(); const promptStore = usePromptStore();
const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]); const [promptHints, setPromptHints] = useState<RenderPrompt[]>([]);
@@ -947,16 +1056,24 @@ function _Chat() {
e.preventDefault(); 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(); 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) => { const deleteMessage = (msgId?: string) => {
chatStore.updateCurrentSession( chatStore.updateCurrentSession(
@@ -1337,6 +1454,8 @@ function _Chat() {
<IconButton <IconButton
icon={<RenameIcon />} icon={<RenameIcon />}
bordered bordered
title={Locale.Chat.EditMessage.Title}
aria={Locale.Chat.EditMessage.Title}
onClick={() => setIsEditingMessage(true)} onClick={() => setIsEditingMessage(true)}
/> />
</div> </div>
@@ -1356,6 +1475,8 @@ function _Chat() {
<IconButton <IconButton
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />} icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
bordered bordered
title={Locale.Chat.Actions.FullScreen}
aria={Locale.Chat.Actions.FullScreen}
onClick={() => { onClick={() => {
config.update( config.update(
(config) => (config.tightBorder = !config.tightBorder), (config) => (config.tightBorder = !config.tightBorder),
@@ -1401,12 +1522,20 @@ function _Chat() {
isUser ? styles["chat-message-user"] : styles["chat-message"] 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-header"]}>
<div className={styles["chat-message-avatar"]}> <div className={styles["chat-message-avatar"]}>
<div className={styles["chat-message-edit"]}> <div className={styles["chat-message-edit"]}>
<IconButton <IconButton
icon={<EditIcon />} icon={<EditIcon />}
aria={Locale.Chat.Actions.Edit}
onClick={async () => { onClick={async () => {
const newMessage = await showPrompt( const newMessage = await showPrompt(
Locale.Chat.Actions.Edit, Locale.Chat.Actions.Edit,
@@ -1457,46 +1586,16 @@ function _Chat() {
</div> </div>
{showActions && ( {showActions && (
<div className={styles["chat-message-actions"]}> <ChatMessageActions
<div className={styles["chat-input-actions"]}> index={i}
{message.streaming ? ( message={message}
<ChatAction position={actionsPosition}
text={Locale.Chat.Actions.Stop} isHover={isMessageContainerHover}
icon={<StopIcon />} onUserStop={onUserStop}
onClick={() => onUserStop(message.id ?? i)} onResend={onResend}
/> onDelete={onDelete}
) : ( onPinMessage={onPinMessage}
<> />
<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>
)} )}
</div> </div>
{showTyping && ( {showTyping && (
@@ -1513,7 +1612,7 @@ function _Chat() {
message.content.length === 0 && message.content.length === 0 &&
!isUser !isUser
} }
onContextMenu={(e) => onRightClick(e, message)} onContextMenu={(e) => onRightClick(e)}
onDoubleClickCapture={() => { onDoubleClickCapture={() => {
if (!isMobileScreen) return; if (!isMobileScreen) return;
setUserInput(getMessageTextContent(message)); setUserInput(getMessageTextContent(message));

View File

@@ -9,6 +9,7 @@ interface InputRangeProps {
min: string; min: string;
max: string; max: string;
step: string; step: string;
aria: string;
} }
export function InputRange({ export function InputRange({
@@ -19,11 +20,13 @@ export function InputRange({
min, min,
max, max,
step, step,
aria,
}: InputRangeProps) { }: InputRangeProps) {
return ( return (
<div className={styles["input-range"] + ` ${className ?? ""}`}> <div className={styles["input-range"] + ` ${className ?? ""}`}>
{title || value} {title || value}
<input <input
aria-label={aria}
type="range" type="range"
title={title} title={title}
value={value} value={value}

View File

@@ -127,6 +127,8 @@ export function MaskConfig(props: {
onClose={() => setShowPicker(false)} onClose={() => setShowPicker(false)}
> >
<div <div
tabIndex={0}
aria-label={Locale.Mask.Config.Avatar}
onClick={() => setShowPicker(true)} onClick={() => setShowPicker(true)}
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
> >
@@ -139,6 +141,7 @@ export function MaskConfig(props: {
</ListItem> </ListItem>
<ListItem title={Locale.Mask.Config.Name}> <ListItem title={Locale.Mask.Config.Name}>
<input <input
aria-label={Locale.Mask.Config.Name}
type="text" type="text"
value={props.mask.name} value={props.mask.name}
onInput={(e) => onInput={(e) =>
@@ -153,6 +156,7 @@ export function MaskConfig(props: {
subTitle={Locale.Mask.Config.HideContext.SubTitle} subTitle={Locale.Mask.Config.HideContext.SubTitle}
> >
<input <input
aria-label={Locale.Mask.Config.HideContext.Title}
type="checkbox" type="checkbox"
checked={props.mask.hideContext} checked={props.mask.hideContext}
onChange={(e) => { onChange={(e) => {
@@ -169,6 +173,7 @@ export function MaskConfig(props: {
subTitle={Locale.Mask.Config.Share.SubTitle} subTitle={Locale.Mask.Config.Share.SubTitle}
> >
<IconButton <IconButton
aria={Locale.Mask.Config.Share.Title}
icon={<CopyIcon />} icon={<CopyIcon />}
text={Locale.Mask.Config.Share.Action} text={Locale.Mask.Config.Share.Action}
onClick={copyMaskLink} onClick={copyMaskLink}
@@ -182,6 +187,7 @@ export function MaskConfig(props: {
subTitle={Locale.Mask.Config.Sync.SubTitle} subTitle={Locale.Mask.Config.Sync.SubTitle}
> >
<input <input
aria-label={Locale.Mask.Config.Sync.Title}
type="checkbox" type="checkbox"
checked={props.mask.syncGlobalConfig} checked={props.mask.syncGlobalConfig}
onChange={async (e) => { onChange={async (e) => {

View File

@@ -17,6 +17,7 @@ export function ModelConfigList(props: {
<> <>
<ListItem title={Locale.Settings.Model}> <ListItem title={Locale.Settings.Model}>
<Select <Select
aria-label={Locale.Settings.Model}
value={value} value={value}
onChange={(e) => { onChange={(e) => {
const [model, providerName] = e.currentTarget.value.split("@"); const [model, providerName] = e.currentTarget.value.split("@");
@@ -40,6 +41,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.Temperature.SubTitle} subTitle={Locale.Settings.Temperature.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.Temperature.Title}
value={props.modelConfig.temperature?.toFixed(1)} value={props.modelConfig.temperature?.toFixed(1)}
min="0" min="0"
max="1" // lets limit it to 0-1 max="1" // lets limit it to 0-1
@@ -59,6 +61,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.TopP.SubTitle} subTitle={Locale.Settings.TopP.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.TopP.Title}
value={(props.modelConfig.top_p ?? 1).toFixed(1)} value={(props.modelConfig.top_p ?? 1).toFixed(1)}
min="0" min="0"
max="1" max="1"
@@ -78,6 +81,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.MaxTokens.SubTitle} subTitle={Locale.Settings.MaxTokens.SubTitle}
> >
<input <input
aria-label={Locale.Settings.MaxTokens.Title}
type="number" type="number"
min={1024} min={1024}
max={512000} max={512000}
@@ -100,6 +104,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.PresencePenalty.SubTitle} subTitle={Locale.Settings.PresencePenalty.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.PresencePenalty.Title}
value={props.modelConfig.presence_penalty?.toFixed(1)} value={props.modelConfig.presence_penalty?.toFixed(1)}
min="-2" min="-2"
max="2" max="2"
@@ -121,6 +126,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.FrequencyPenalty.SubTitle} subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.FrequencyPenalty.Title}
value={props.modelConfig.frequency_penalty?.toFixed(1)} value={props.modelConfig.frequency_penalty?.toFixed(1)}
min="-2" min="-2"
max="2" max="2"
@@ -142,6 +148,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle} subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
> >
<input <input
aria-label={Locale.Settings.InjectSystemPrompts.Title}
type="checkbox" type="checkbox"
checked={props.modelConfig.enableInjectSystemPrompts} checked={props.modelConfig.enableInjectSystemPrompts}
onChange={(e) => onChange={(e) =>
@@ -159,6 +166,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.InputTemplate.SubTitle} subTitle={Locale.Settings.InputTemplate.SubTitle}
> >
<input <input
aria-label={Locale.Settings.InputTemplate.Title}
type="text" type="text"
value={props.modelConfig.template} value={props.modelConfig.template}
onChange={(e) => onChange={(e) =>
@@ -175,6 +183,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.HistoryCount.SubTitle} subTitle={Locale.Settings.HistoryCount.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.HistoryCount.Title}
title={props.modelConfig.historyMessageCount.toString()} title={props.modelConfig.historyMessageCount.toString()}
value={props.modelConfig.historyMessageCount} value={props.modelConfig.historyMessageCount}
min="0" min="0"
@@ -193,6 +202,7 @@ export function ModelConfigList(props: {
subTitle={Locale.Settings.CompressThreshold.SubTitle} subTitle={Locale.Settings.CompressThreshold.SubTitle}
> >
<input <input
aria-label={Locale.Settings.CompressThreshold.Title}
type="number" type="number"
min={500} min={500}
max={4000} max={4000}
@@ -208,6 +218,7 @@ export function ModelConfigList(props: {
</ListItem> </ListItem>
<ListItem title={Locale.Memory.Title} subTitle={Locale.Memory.Send}> <ListItem title={Locale.Memory.Title} subTitle={Locale.Memory.Send}>
<input <input
aria-label={Locale.Memory.Title}
type="checkbox" type="checkbox"
checked={props.modelConfig.sendMemory} checked={props.modelConfig.sendMemory}
onChange={(e) => onChange={(e) =>

View File

@@ -192,6 +192,7 @@ export function ControlParam(props: {
required={item.required} required={item.required}
> >
<Select <Select
aria-label={item.name}
value={props.data[item.value]} value={props.data[item.value]}
onChange={(e) => { onChange={(e) => {
props.onChange(item.value, e.currentTarget.value); props.onChange(item.value, e.currentTarget.value);
@@ -216,6 +217,7 @@ export function ControlParam(props: {
required={item.required} required={item.required}
> >
<input <input
aria-label={item.name}
type="number" type="number"
min={item.min} min={item.min}
max={item.max} max={item.max}
@@ -235,6 +237,7 @@ export function ControlParam(props: {
required={item.required} required={item.required}
> >
<input <input
aria-label={item.name}
type="text" type="text"
value={props.data[item.value]} value={props.data[item.value]}
style={{ maxWidth: "100%", width: "100%" }} style={{ maxWidth: "100%", width: "100%" }}

View File

@@ -133,6 +133,7 @@ export function Sd() {
{showMaxIcon && ( {showMaxIcon && (
<div className="window-action-button"> <div className="window-action-button">
<IconButton <IconButton
aria={Locale.Chat.Actions.FullScreen}
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />} icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
bordered bordered
onClick={() => { onClick={() => {

View File

@@ -246,6 +246,7 @@ function DangerItems() {
subTitle={Locale.Settings.Danger.Reset.SubTitle} subTitle={Locale.Settings.Danger.Reset.SubTitle}
> >
<IconButton <IconButton
aria={Locale.Settings.Danger.Reset.Title}
text={Locale.Settings.Danger.Reset.Action} text={Locale.Settings.Danger.Reset.Action}
onClick={async () => { onClick={async () => {
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) { if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
@@ -260,6 +261,7 @@ function DangerItems() {
subTitle={Locale.Settings.Danger.Clear.SubTitle} subTitle={Locale.Settings.Danger.Clear.SubTitle}
> >
<IconButton <IconButton
aria={Locale.Settings.Danger.Clear.Title}
text={Locale.Settings.Danger.Clear.Action} text={Locale.Settings.Danger.Clear.Action}
onClick={async () => { onClick={async () => {
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) { if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
@@ -513,6 +515,7 @@ function SyncItems() {
> >
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<IconButton <IconButton
aria={Locale.Settings.Sync.CloudState + Locale.UI.Config}
icon={<ConfigIcon />} icon={<ConfigIcon />}
text={Locale.UI.Config} text={Locale.UI.Config}
onClick={() => { onClick={() => {
@@ -543,6 +546,7 @@ function SyncItems() {
> >
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<IconButton <IconButton
aria={Locale.Settings.Sync.LocalState + Locale.UI.Export}
icon={<UploadIcon />} icon={<UploadIcon />}
text={Locale.UI.Export} text={Locale.UI.Export}
onClick={() => { onClick={() => {
@@ -550,6 +554,7 @@ function SyncItems() {
}} }}
/> />
<IconButton <IconButton
aria={Locale.Settings.Sync.LocalState + Locale.UI.Import}
icon={<DownloadIcon />} icon={<DownloadIcon />}
text={Locale.UI.Import} text={Locale.UI.Import}
onClick={() => { onClick={() => {
@@ -687,6 +692,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle} subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.CustomEndpoint.Title}
type="checkbox" type="checkbox"
checked={accessStore.useCustomConfig} checked={accessStore.useCustomConfig}
onChange={(e) => onChange={(e) =>
@@ -706,6 +712,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle} subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.OpenAI.Endpoint.Title}
type="text" type="text"
value={accessStore.openaiUrl} value={accessStore.openaiUrl}
placeholder={OPENAI_BASE_URL} placeholder={OPENAI_BASE_URL}
@@ -721,6 +728,8 @@ export function Settings() {
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle} subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria={Locale.Settings.ShowPassword}
aria-label={Locale.Settings.Access.OpenAI.ApiKey.Title}
value={accessStore.openaiApiKey} value={accessStore.openaiApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder} placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
@@ -744,6 +753,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Azure.Endpoint.Title}
type="text" type="text"
value={accessStore.azureUrl} value={accessStore.azureUrl}
placeholder={Azure.ExampleEndpoint} placeholder={Azure.ExampleEndpoint}
@@ -759,6 +769,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Azure.ApiKey.Title}
value={accessStore.azureApiKey} value={accessStore.azureApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Azure.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Azure.ApiKey.Placeholder}
@@ -774,6 +785,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Azure.ApiVerion.SubTitle} subTitle={Locale.Settings.Access.Azure.ApiVerion.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.Azure.ApiVerion.Title}
type="text" type="text"
value={accessStore.azureApiVersion} value={accessStore.azureApiVersion}
placeholder="2023-08-01-preview" placeholder="2023-08-01-preview"
@@ -798,6 +810,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Google.Endpoint.Title}
type="text" type="text"
value={accessStore.googleUrl} value={accessStore.googleUrl}
placeholder={Google.ExampleEndpoint} placeholder={Google.ExampleEndpoint}
@@ -813,6 +826,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Google.ApiKey.Title}
value={accessStore.googleApiKey} value={accessStore.googleApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
@@ -828,6 +842,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle} subTitle={Locale.Settings.Access.Google.ApiVersion.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.Google.ApiVersion.Title}
type="text" type="text"
value={accessStore.googleApiVersion} value={accessStore.googleApiVersion}
placeholder="2023-08-01-preview" placeholder="2023-08-01-preview"
@@ -843,6 +858,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle} subTitle={Locale.Settings.Access.Google.GoogleSafetySettings.SubTitle}
> >
<Select <Select
aria-label={Locale.Settings.Access.Google.GoogleSafetySettings.Title}
value={accessStore.googleSafetySettings} value={accessStore.googleSafetySettings}
onChange={(e) => { onChange={(e) => {
accessStore.update( accessStore.update(
@@ -873,6 +889,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Anthropic.Endpoint.Title}
type="text" type="text"
value={accessStore.anthropicUrl} value={accessStore.anthropicUrl}
placeholder={Anthropic.ExampleEndpoint} placeholder={Anthropic.ExampleEndpoint}
@@ -888,6 +905,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Anthropic.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Anthropic.ApiKey.Title}
value={accessStore.anthropicApiKey} value={accessStore.anthropicApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Anthropic.ApiKey.Placeholder}
@@ -903,6 +921,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle} subTitle={Locale.Settings.Access.Anthropic.ApiVerion.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.Anthropic.ApiVerion.Title}
type="text" type="text"
value={accessStore.anthropicApiVersion} value={accessStore.anthropicApiVersion}
placeholder={Anthropic.Vision} placeholder={Anthropic.Vision}
@@ -924,6 +943,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Baidu.Endpoint.SubTitle} subTitle={Locale.Settings.Access.Baidu.Endpoint.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.Baidu.Endpoint.Title}
type="text" type="text"
value={accessStore.baiduUrl} value={accessStore.baiduUrl}
placeholder={Baidu.ExampleEndpoint} placeholder={Baidu.ExampleEndpoint}
@@ -939,6 +959,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Baidu.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Baidu.ApiKey.Title}
value={accessStore.baiduApiKey} value={accessStore.baiduApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Baidu.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Baidu.ApiKey.Placeholder}
@@ -954,6 +975,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Baidu.SecretKey.SubTitle} subTitle={Locale.Settings.Access.Baidu.SecretKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Baidu.SecretKey.Title}
value={accessStore.baiduSecretKey} value={accessStore.baiduSecretKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Baidu.SecretKey.Placeholder} placeholder={Locale.Settings.Access.Baidu.SecretKey.Placeholder}
@@ -975,6 +997,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Tencent.Endpoint.SubTitle} subTitle={Locale.Settings.Access.Tencent.Endpoint.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.Tencent.Endpoint.Title}
type="text" type="text"
value={accessStore.tencentUrl} value={accessStore.tencentUrl}
placeholder={Tencent.ExampleEndpoint} placeholder={Tencent.ExampleEndpoint}
@@ -990,6 +1013,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Tencent.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Tencent.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Tencent.ApiKey.Title}
value={accessStore.tencentSecretId} value={accessStore.tencentSecretId}
type="text" type="text"
placeholder={Locale.Settings.Access.Tencent.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Tencent.ApiKey.Placeholder}
@@ -1005,6 +1029,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Tencent.SecretKey.SubTitle} subTitle={Locale.Settings.Access.Tencent.SecretKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Tencent.SecretKey.Title}
value={accessStore.tencentSecretKey} value={accessStore.tencentSecretKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Tencent.SecretKey.Placeholder} placeholder={Locale.Settings.Access.Tencent.SecretKey.Placeholder}
@@ -1029,6 +1054,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.ByteDance.Endpoint.Title}
type="text" type="text"
value={accessStore.bytedanceUrl} value={accessStore.bytedanceUrl}
placeholder={ByteDance.ExampleEndpoint} placeholder={ByteDance.ExampleEndpoint}
@@ -1044,6 +1070,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.ByteDance.ApiKey.SubTitle} subTitle={Locale.Settings.Access.ByteDance.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.ByteDance.ApiKey.Title}
value={accessStore.bytedanceApiKey} value={accessStore.bytedanceApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.ByteDance.ApiKey.Placeholder} placeholder={Locale.Settings.Access.ByteDance.ApiKey.Placeholder}
@@ -1068,6 +1095,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Alibaba.Endpoint.Title}
type="text" type="text"
value={accessStore.alibabaUrl} value={accessStore.alibabaUrl}
placeholder={Alibaba.ExampleEndpoint} placeholder={Alibaba.ExampleEndpoint}
@@ -1083,6 +1111,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Alibaba.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Alibaba.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Alibaba.ApiKey.Title}
value={accessStore.alibabaApiKey} value={accessStore.alibabaApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Alibaba.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Alibaba.ApiKey.Placeholder}
@@ -1107,6 +1136,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Moonshot.Endpoint.Title}
type="text" type="text"
value={accessStore.moonshotUrl} value={accessStore.moonshotUrl}
placeholder={Moonshot.ExampleEndpoint} placeholder={Moonshot.ExampleEndpoint}
@@ -1122,6 +1152,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Moonshot.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Moonshot.ApiKey.Title}
value={accessStore.moonshotApiKey} value={accessStore.moonshotApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Moonshot.ApiKey.Placeholder}
@@ -1146,6 +1177,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Stability.Endpoint.Title}
type="text" type="text"
value={accessStore.stabilityUrl} value={accessStore.stabilityUrl}
placeholder={Stability.ExampleEndpoint} placeholder={Stability.ExampleEndpoint}
@@ -1161,6 +1193,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Stability.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Stability.ApiKey.Title}
value={accessStore.stabilityApiKey} value={accessStore.stabilityApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Stability.ApiKey.Placeholder}
@@ -1184,6 +1217,7 @@ export function Settings() {
} }
> >
<input <input
aria-label={Locale.Settings.Access.Iflytek.Endpoint.Title}
type="text" type="text"
value={accessStore.iflytekUrl} value={accessStore.iflytekUrl}
placeholder={Iflytek.ExampleEndpoint} placeholder={Iflytek.ExampleEndpoint}
@@ -1199,6 +1233,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Iflytek.ApiKey.SubTitle} subTitle={Locale.Settings.Access.Iflytek.ApiKey.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Iflytek.ApiKey.Title}
value={accessStore.iflytekApiKey} value={accessStore.iflytekApiKey}
type="text" type="text"
placeholder={Locale.Settings.Access.Iflytek.ApiKey.Placeholder} placeholder={Locale.Settings.Access.Iflytek.ApiKey.Placeholder}
@@ -1215,6 +1250,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Iflytek.ApiSecret.SubTitle} subTitle={Locale.Settings.Access.Iflytek.ApiSecret.SubTitle}
> >
<PasswordInput <PasswordInput
aria-label={Locale.Settings.Access.Iflytek.ApiSecret.Title}
value={accessStore.iflytekApiSecret} value={accessStore.iflytekApiSecret}
type="text" type="text"
placeholder={Locale.Settings.Access.Iflytek.ApiSecret.Placeholder} 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"></div>
<div className="window-action-button"> <div className="window-action-button">
<IconButton <IconButton
aria={Locale.UI.Close}
icon={<CloseIcon />} icon={<CloseIcon />}
onClick={() => navigate(Path.Home)} onClick={() => navigate(Path.Home)}
bordered bordered
@@ -1267,6 +1304,8 @@ export function Settings() {
open={showEmojiPicker} open={showEmojiPicker}
> >
<div <div
aria-label={Locale.Settings.Avatar}
tabIndex={0}
className={styles.avatar} className={styles.avatar}
onClick={() => { onClick={() => {
setShowEmojiPicker(!showEmojiPicker); setShowEmojiPicker(!showEmojiPicker);
@@ -1304,6 +1343,7 @@ export function Settings() {
<ListItem title={Locale.Settings.SendKey}> <ListItem title={Locale.Settings.SendKey}>
<Select <Select
aria-label={Locale.Settings.SendKey}
value={config.submitKey} value={config.submitKey}
onChange={(e) => { onChange={(e) => {
updateConfig( updateConfig(
@@ -1322,6 +1362,7 @@ export function Settings() {
<ListItem title={Locale.Settings.Theme}> <ListItem title={Locale.Settings.Theme}>
<Select <Select
aria-label={Locale.Settings.Theme}
value={config.theme} value={config.theme}
onChange={(e) => { onChange={(e) => {
updateConfig( updateConfig(
@@ -1339,6 +1380,7 @@ export function Settings() {
<ListItem title={Locale.Settings.Lang.Name}> <ListItem title={Locale.Settings.Lang.Name}>
<Select <Select
aria-label={Locale.Settings.Lang.Name}
value={getLang()} value={getLang()}
onChange={(e) => { onChange={(e) => {
changeLang(e.target.value as any); changeLang(e.target.value as any);
@@ -1357,6 +1399,7 @@ export function Settings() {
subTitle={Locale.Settings.FontSize.SubTitle} subTitle={Locale.Settings.FontSize.SubTitle}
> >
<InputRange <InputRange
aria={Locale.Settings.FontSize.Title}
title={`${config.fontSize ?? 14}px`} title={`${config.fontSize ?? 14}px`}
value={config.fontSize} value={config.fontSize}
min="12" min="12"
@@ -1376,6 +1419,7 @@ export function Settings() {
subTitle={Locale.Settings.FontFamily.SubTitle} subTitle={Locale.Settings.FontFamily.SubTitle}
> >
<input <input
aria-label={Locale.Settings.FontFamily.Title}
type="text" type="text"
value={config.fontFamily} value={config.fontFamily}
placeholder={Locale.Settings.FontFamily.Placeholder} placeholder={Locale.Settings.FontFamily.Placeholder}
@@ -1392,6 +1436,7 @@ export function Settings() {
subTitle={Locale.Settings.AutoGenerateTitle.SubTitle} subTitle={Locale.Settings.AutoGenerateTitle.SubTitle}
> >
<input <input
aria-label={Locale.Settings.AutoGenerateTitle.Title}
type="checkbox" type="checkbox"
checked={config.enableAutoGenerateTitle} checked={config.enableAutoGenerateTitle}
onChange={(e) => onChange={(e) =>
@@ -1408,6 +1453,7 @@ export function Settings() {
subTitle={Locale.Settings.SendPreviewBubble.SubTitle} subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
> >
<input <input
aria-label={Locale.Settings.SendPreviewBubble.Title}
type="checkbox" type="checkbox"
checked={config.sendPreviewBubble} checked={config.sendPreviewBubble}
onChange={(e) => onChange={(e) =>
@@ -1428,6 +1474,7 @@ export function Settings() {
subTitle={Locale.Settings.Mask.Splash.SubTitle} subTitle={Locale.Settings.Mask.Splash.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Mask.Splash.Title}
type="checkbox" type="checkbox"
checked={!config.dontShowMaskSplashScreen} checked={!config.dontShowMaskSplashScreen}
onChange={(e) => onChange={(e) =>
@@ -1445,6 +1492,7 @@ export function Settings() {
subTitle={Locale.Settings.Mask.Builtin.SubTitle} subTitle={Locale.Settings.Mask.Builtin.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Mask.Builtin.Title}
type="checkbox" type="checkbox"
checked={config.hideBuiltinMasks} checked={config.hideBuiltinMasks}
onChange={(e) => onChange={(e) =>
@@ -1463,6 +1511,7 @@ export function Settings() {
subTitle={Locale.Settings.Prompt.Disable.SubTitle} subTitle={Locale.Settings.Prompt.Disable.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Prompt.Disable.Title}
type="checkbox" type="checkbox"
checked={config.disablePromptHint} checked={config.disablePromptHint}
onChange={(e) => onChange={(e) =>
@@ -1482,6 +1531,7 @@ export function Settings() {
)} )}
> >
<IconButton <IconButton
aria={Locale.Settings.Prompt.List + Locale.Settings.Prompt.Edit}
icon={<EditIcon />} icon={<EditIcon />}
text={Locale.Settings.Prompt.Edit} text={Locale.Settings.Prompt.Edit}
onClick={() => setShowPromptModal(true)} onClick={() => setShowPromptModal(true)}
@@ -1503,6 +1553,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.Provider.SubTitle} subTitle={Locale.Settings.Access.Provider.SubTitle}
> >
<Select <Select
aria-label={Locale.Settings.Access.Provider.Title}
value={accessStore.provider} value={accessStore.provider}
onChange={(e) => { onChange={(e) => {
accessStore.update( accessStore.update(
@@ -1567,6 +1618,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.CustomModel.SubTitle} subTitle={Locale.Settings.Access.CustomModel.SubTitle}
> >
<input <input
aria-label={Locale.Settings.Access.CustomModel.Title}
type="text" type="text"
value={config.customModels} value={config.customModels}
placeholder="model1,model2,model3" placeholder="model1,model2,model3"

View File

@@ -297,12 +297,20 @@ export function SideBar(props: { className?: string }) {
</div> </div>
<div className={styles["sidebar-action"]}> <div className={styles["sidebar-action"]}>
<Link to={Path.Settings}> <Link to={Path.Settings}>
<IconButton icon={<SettingsIcon />} shadow /> <IconButton
aria={Locale.Settings.Title}
icon={<SettingsIcon />}
shadow
/>
</Link> </Link>
</div> </div>
<div className={styles["sidebar-action"]}> <div className={styles["sidebar-action"]}>
<a href={REPO_URL} target="_blank" rel="noopener noreferrer"> <a href={REPO_URL} target="_blank" rel="noopener noreferrer">
<IconButton icon={<GithubIcon />} shadow /> <IconButton
aria={Locale.Export.MessageFromChatGPT}
icon={<GithubIcon />}
shadow
/>
</a> </a>
</div> </div>
</> </>

View File

@@ -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); const [visible, setVisible] = useState(false);
function changeVisibility() { function changeVisibility() {
setVisible(!visible); setVisible(!visible);
} }
@@ -275,6 +276,7 @@ export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
return ( return (
<div className={"password-input-container"}> <div className={"password-input-container"}>
<IconButton <IconButton
aria={props.aria}
icon={visible ? <EyeIcon /> : <EyeOffIcon />} icon={visible ? <EyeIcon /> : <EyeOffIcon />}
onClick={changeVisibility} onClick={changeVisibility}
className={"password-eye"} className={"password-eye"}

View File

@@ -119,10 +119,16 @@ export const getServerSideConfig = () => {
if (disableGPT4) { if (disableGPT4) {
if (customModels) customModels += ","; 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) .map((m) => "-" + m.name)
.join(","); .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; const isStability = !!process.env.STABILITY_API_KEY;

View File

@@ -243,6 +243,7 @@ export const KnowledgeCutOffDate: Record<string, string> = {
"gpt-4-turbo-preview": "2023-12", "gpt-4-turbo-preview": "2023-12",
"gpt-4o": "2023-10", "gpt-4o": "2023-10",
"gpt-4o-2024-05-13": "2023-10", "gpt-4o-2024-05-13": "2023-10",
"gpt-4o-2024-08-06": "2023-10",
"gpt-4o-mini": "2023-10", "gpt-4o-mini": "2023-10",
"gpt-4o-mini-2024-07-18": "2023-10", "gpt-4o-mini-2024-07-18": "2023-10",
"gpt-4-vision-preview": "2023-04", "gpt-4-vision-preview": "2023-04",
@@ -264,6 +265,7 @@ const openaiModels = [
"gpt-4-turbo-preview", "gpt-4-turbo-preview",
"gpt-4o", "gpt-4o",
"gpt-4o-2024-05-13", "gpt-4o-2024-05-13",
"gpt-4o-2024-08-06",
"gpt-4o-mini", "gpt-4o-mini",
"gpt-4o-mini-2024-07-18", "gpt-4o-mini-2024-07-18",
"gpt-4-vision-preview", "gpt-4-vision-preview",

View File

@@ -42,6 +42,7 @@ const cn = {
PinToastAction: "查看", PinToastAction: "查看",
Delete: "删除", Delete: "删除",
Edit: "编辑", Edit: "编辑",
FullScreen: "全屏",
}, },
Commands: { Commands: {
new: "新建聊天", new: "新建聊天",
@@ -132,6 +133,7 @@ const cn = {
Settings: { Settings: {
Title: "设置", Title: "设置",
SubTitle: "所有设置选项", SubTitle: "所有设置选项",
ShowPassword: "显示密码",
Danger: { Danger: {
Reset: { Reset: {

View File

@@ -44,6 +44,7 @@ const en: LocaleType = {
PinToastAction: "View", PinToastAction: "View",
Delete: "Delete", Delete: "Delete",
Edit: "Edit", Edit: "Edit",
FullScreen: "FullScreen",
}, },
Commands: { Commands: {
new: "Start a new chat", new: "Start a new chat",
@@ -135,6 +136,7 @@ const en: LocaleType = {
Settings: { Settings: {
Title: "Settings", Title: "Settings",
SubTitle: "All Settings", SubTitle: "All Settings",
ShowPassword: "ShowPassword",
Danger: { Danger: {
Reset: { Reset: {
Title: "Reset All Settings", Title: "Reset All Settings",

View File

@@ -145,15 +145,13 @@ export function isFirefox() {
); );
} }
export function selectOrCopy(el: HTMLElement, content: string) { export function isNotSelectRange() {
const currentSelection = window.getSelection(); const currentSelection = window.getSelection();
if (currentSelection?.type === "Range") { if (currentSelection?.type === "Range") {
return false; return false;
} }
copyToClipboard(content);
return true; return true;
} }