mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-11 20:43:42 +08:00
Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web
This commit is contained in:
@@ -26,7 +26,7 @@ export function ChatItem(props: {
|
||||
count: number;
|
||||
time: string;
|
||||
selected: boolean;
|
||||
id: number;
|
||||
id: string;
|
||||
index: number;
|
||||
narrow?: boolean;
|
||||
mask: Mask;
|
||||
|
@@ -240,24 +240,39 @@
|
||||
&:last-child {
|
||||
animation: slide-in ease 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.chat-message-actions {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
max-width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.chat-message-action-date {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-user {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.chat-message-header {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-header {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.chat-message-actions {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
transition: all ease 0.3s;
|
||||
transform: scale(0.9) translateY(5px);
|
||||
margin: 0 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.chat-input-actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-container {
|
||||
@@ -270,6 +285,12 @@
|
||||
.chat-message-edit {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.chat-message-actions {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +299,6 @@
|
||||
}
|
||||
|
||||
.chat-message-avatar {
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
|
||||
.chat-message-edit {
|
||||
@@ -318,27 +338,6 @@
|
||||
border: var(--border-in-light);
|
||||
position: relative;
|
||||
transition: all ease 0.3s;
|
||||
|
||||
.chat-message-actions {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
transition: all ease 0.3s 0.15s;
|
||||
transform: translateX(-5px) scale(0.9) translateY(30px);
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
max-width: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
|
||||
.chat-input-actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chat-message-action-date {
|
||||
|
@@ -221,9 +221,11 @@ function useSubmitHandler() {
|
||||
};
|
||||
}
|
||||
|
||||
export type RenderPompt = Pick<Prompt, "title" | "content">;
|
||||
|
||||
export function PromptHints(props: {
|
||||
prompts: Prompt[];
|
||||
onPromptSelect: (prompt: Prompt) => void;
|
||||
prompts: RenderPompt[];
|
||||
onPromptSelect: (prompt: RenderPompt) => void;
|
||||
}) {
|
||||
const noPrompts = props.prompts.length === 0;
|
||||
const [selectIndex, setSelectIndex] = useState(0);
|
||||
@@ -412,8 +414,12 @@ export function ChatActions(props: {
|
||||
// switch model
|
||||
const currentModel = chatStore.currentSession().mask.modelConfig.model;
|
||||
const models = useMemo(
|
||||
() => config.models.filter((m) => m.available).map((m) => m.name),
|
||||
[config.models],
|
||||
() =>
|
||||
config
|
||||
.allModels()
|
||||
.filter((m) => m.available)
|
||||
.map((m) => m.name),
|
||||
[config],
|
||||
);
|
||||
const [showModelSelector, setShowModelSelector] = useState(false);
|
||||
|
||||
@@ -542,7 +548,7 @@ export function Chat() {
|
||||
|
||||
// prompt hints
|
||||
const promptStore = usePromptStore();
|
||||
const [promptHints, setPromptHints] = useState<Prompt[]>([]);
|
||||
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
|
||||
const onSearch = useDebouncedCallback(
|
||||
(text: string) => {
|
||||
const matchedPrompts = promptStore.search(text);
|
||||
@@ -624,7 +630,7 @@ export function Chat() {
|
||||
setAutoScroll(true);
|
||||
};
|
||||
|
||||
const onPromptSelect = (prompt: Prompt) => {
|
||||
const onPromptSelect = (prompt: RenderPompt) => {
|
||||
setTimeout(() => {
|
||||
setPromptHints([]);
|
||||
|
||||
@@ -642,8 +648,8 @@ export function Chat() {
|
||||
};
|
||||
|
||||
// stop response
|
||||
const onUserStop = (messageId: number) => {
|
||||
ChatControllerPool.stop(sessionIndex, messageId);
|
||||
const onUserStop = (messageId: string) => {
|
||||
ChatControllerPool.stop(session.id, messageId);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -703,54 +709,51 @@ export function Chat() {
|
||||
}
|
||||
};
|
||||
|
||||
const findLastUserIndex = (messageId: number) => {
|
||||
const findLastUserIndex = (messageId: string) => {
|
||||
// find last user input message and resend
|
||||
let lastUserMessageIndex: number | null = null;
|
||||
for (let i = 0; i < session.messages.length; i += 1) {
|
||||
const message = session.messages[i];
|
||||
if (message.id === messageId) {
|
||||
break;
|
||||
}
|
||||
if (message.role === "user") {
|
||||
lastUserMessageIndex = i;
|
||||
}
|
||||
if (message.id === messageId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastUserMessageIndex;
|
||||
};
|
||||
|
||||
const deleteMessage = (userIndex: number) => {
|
||||
chatStore.updateCurrentSession((session) =>
|
||||
session.messages.splice(userIndex, 2),
|
||||
const deleteMessage = (msgId?: string) => {
|
||||
chatStore.updateCurrentSession(
|
||||
(session) =>
|
||||
(session.messages = session.messages.filter((m) => m.id !== msgId)),
|
||||
);
|
||||
};
|
||||
|
||||
const onDelete = (botMessageId: number) => {
|
||||
const userIndex = findLastUserIndex(botMessageId);
|
||||
if (userIndex === null) return;
|
||||
deleteMessage(userIndex);
|
||||
const onDelete = (msgId: string) => {
|
||||
deleteMessage(msgId);
|
||||
};
|
||||
|
||||
const onResend = (botMessageId: number) => {
|
||||
// find last user input message and resend
|
||||
const userIndex = findLastUserIndex(botMessageId);
|
||||
if (userIndex === null) return;
|
||||
const onResend = (message: ChatMessage) => {
|
||||
let content = message.content;
|
||||
|
||||
if (message.role === "assistant" && message.id) {
|
||||
const userIndex = findLastUserIndex(message.id);
|
||||
if (userIndex) {
|
||||
content = session.messages.at(userIndex)?.content ?? content;
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const content = session.messages[userIndex].content;
|
||||
deleteMessage(userIndex);
|
||||
chatStore.onUserInput(content).then(() => setIsLoading(false));
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
const onPinMessage = (botMessage: ChatMessage) => {
|
||||
if (!botMessage.id) return;
|
||||
const userMessageIndex = findLastUserIndex(botMessage.id);
|
||||
if (userMessageIndex === null) return;
|
||||
|
||||
const userMessage = session.messages[userMessageIndex];
|
||||
const onPinMessage = (message: ChatMessage) => {
|
||||
chatStore.updateCurrentSession((session) =>
|
||||
session.mask.context.push(userMessage, botMessage),
|
||||
session.mask.context.push(message),
|
||||
);
|
||||
|
||||
showToast(Locale.Chat.Actions.PinToastContent, {
|
||||
@@ -923,11 +926,11 @@ export function Chat() {
|
||||
>
|
||||
{messages.map((message, i) => {
|
||||
const isUser = message.role === "user";
|
||||
const isContext = i < context.length;
|
||||
const showActions =
|
||||
!isUser &&
|
||||
i > 0 &&
|
||||
!(message.preview || message.content.length === 0) &&
|
||||
i >= context.length; // do not show actions for context prompts
|
||||
!isContext;
|
||||
const showTyping = message.preview || message.streaming;
|
||||
|
||||
const shouldShowClearContextDivider = i === clearContextIndex - 1;
|
||||
@@ -941,64 +944,38 @@ export function Chat() {
|
||||
}
|
||||
>
|
||||
<div className={styles["chat-message-container"]}>
|
||||
<div className={styles["chat-message-avatar"]}>
|
||||
<div className={styles["chat-message-edit"]}>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
onClick={async () => {
|
||||
const newMessage = await showPrompt(
|
||||
Locale.Chat.Actions.Edit,
|
||||
message.content,
|
||||
10,
|
||||
);
|
||||
chatStore.updateCurrentSession((session) => {
|
||||
const m = session.messages.find(
|
||||
(m) => m.id === message.id,
|
||||
<div className={styles["chat-message-header"]}>
|
||||
<div className={styles["chat-message-avatar"]}>
|
||||
<div className={styles["chat-message-edit"]}>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
onClick={async () => {
|
||||
const newMessage = await showPrompt(
|
||||
Locale.Chat.Actions.Edit,
|
||||
message.content,
|
||||
10,
|
||||
);
|
||||
if (m) {
|
||||
m.content = newMessage;
|
||||
}
|
||||
});
|
||||
}}
|
||||
></IconButton>
|
||||
chatStore.updateCurrentSession((session) => {
|
||||
const m = session.messages.find(
|
||||
(m) => m.id === message.id,
|
||||
);
|
||||
if (m) {
|
||||
m.content = newMessage;
|
||||
}
|
||||
});
|
||||
}}
|
||||
></IconButton>
|
||||
</div>
|
||||
{isUser ? (
|
||||
<Avatar avatar={config.avatar} />
|
||||
) : (
|
||||
<MaskAvatar mask={session.mask} />
|
||||
)}
|
||||
</div>
|
||||
{isUser ? (
|
||||
<Avatar avatar={config.avatar} />
|
||||
) : (
|
||||
<MaskAvatar mask={session.mask} />
|
||||
)}
|
||||
</div>
|
||||
{showTyping && (
|
||||
<div className={styles["chat-message-status"]}>
|
||||
{Locale.Chat.Typing}
|
||||
</div>
|
||||
)}
|
||||
<div className={styles["chat-message-item"]}>
|
||||
<Markdown
|
||||
content={message.content}
|
||||
loading={
|
||||
(message.preview || message.content.length === 0) &&
|
||||
!isUser
|
||||
}
|
||||
onContextMenu={(e) => onRightClick(e, message)}
|
||||
onDoubleClickCapture={() => {
|
||||
if (!isMobileScreen) return;
|
||||
setUserInput(message.content);
|
||||
}}
|
||||
fontSize={fontSize}
|
||||
parentRef={scrollRef}
|
||||
defaultShow={i >= messages.length - 10}
|
||||
/>
|
||||
|
||||
{showActions && (
|
||||
<div className={styles["chat-message-actions"]}>
|
||||
<div
|
||||
className={styles["chat-input-actions"]}
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginBottom: 0,
|
||||
}}
|
||||
>
|
||||
<div className={styles["chat-input-actions"]}>
|
||||
{message.streaming ? (
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Stop}
|
||||
@@ -1010,7 +987,7 @@ export function Chat() {
|
||||
<ChatAction
|
||||
text={Locale.Chat.Actions.Retry}
|
||||
icon={<ResetIcon />}
|
||||
onClick={() => onResend(message.id ?? i)}
|
||||
onClick={() => onResend(message)}
|
||||
/>
|
||||
|
||||
<ChatAction
|
||||
@@ -1035,12 +1012,34 @@ export function Chat() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showActions && (
|
||||
<div className={styles["chat-message-action-date"]}>
|
||||
{message.date.toLocaleString()}
|
||||
{showTyping && (
|
||||
<div className={styles["chat-message-status"]}>
|
||||
{Locale.Chat.Typing}
|
||||
</div>
|
||||
)}
|
||||
<div className={styles["chat-message-item"]}>
|
||||
<Markdown
|
||||
content={message.content}
|
||||
loading={
|
||||
(message.preview || message.content.length === 0) &&
|
||||
!isUser
|
||||
}
|
||||
onContextMenu={(e) => onRightClick(e, message)}
|
||||
onDoubleClickCapture={() => {
|
||||
if (!isMobileScreen) return;
|
||||
setUserInput(message.content);
|
||||
}}
|
||||
fontSize={fontSize}
|
||||
parentRef={scrollRef}
|
||||
defaultShow={i >= messages.length - 10}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles["chat-message-action-date"]}>
|
||||
{isContext
|
||||
? Locale.Chat.IsContext
|
||||
: message.date.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{shouldShowClearContextDivider && <ClearContextDivider />}
|
||||
|
@@ -8,7 +8,6 @@ import {
|
||||
Modal,
|
||||
Select,
|
||||
showImageModal,
|
||||
showModal,
|
||||
showToast,
|
||||
} from "./ui-lib";
|
||||
import { IconButton } from "./button";
|
||||
@@ -149,7 +148,7 @@ export function MessageExporter() {
|
||||
if (exportConfig.includeContext) {
|
||||
ret.push(...session.mask.context);
|
||||
}
|
||||
ret.push(...session.messages.filter((m, i) => selection.has(m.id ?? i)));
|
||||
ret.push(...session.messages.filter((m, i) => selection.has(m.id)));
|
||||
return ret;
|
||||
}, [
|
||||
exportConfig.includeContext,
|
||||
@@ -244,9 +243,10 @@ export function RenderExport(props: {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderMsgs = messages.map((v) => {
|
||||
const renderMsgs = messages.map((v, i) => {
|
||||
const [_, role] = v.id.split(":");
|
||||
return {
|
||||
id: i.toString(),
|
||||
role: role as any,
|
||||
content: v.innerHTML,
|
||||
date: "",
|
||||
|
@@ -13,7 +13,13 @@ import EyeIcon from "../icons/eye.svg";
|
||||
import CopyIcon from "../icons/copy.svg";
|
||||
|
||||
import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
|
||||
import { ChatMessage, ModelConfig, useAppConfig, useChatStore } from "../store";
|
||||
import {
|
||||
ChatMessage,
|
||||
createMessage,
|
||||
ModelConfig,
|
||||
useAppConfig,
|
||||
useChatStore,
|
||||
} from "../store";
|
||||
import { ROLES } from "../client/api";
|
||||
import {
|
||||
Input,
|
||||
@@ -35,6 +41,7 @@ import { Updater } from "../typing";
|
||||
import { ModelConfigList } from "./model-config";
|
||||
import { FileName, Path } from "../constant";
|
||||
import { BUILTIN_MASK_STORE } from "../masks";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export function MaskAvatar(props: { mask: Mask }) {
|
||||
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
|
||||
@@ -279,11 +286,13 @@ export function ContextPrompts(props: {
|
||||
bordered
|
||||
className={chatStyle["context-prompt-button"]}
|
||||
onClick={() =>
|
||||
addContextPrompt({
|
||||
role: "user",
|
||||
content: "",
|
||||
date: "",
|
||||
})
|
||||
addContextPrompt(
|
||||
createMessage({
|
||||
role: "user",
|
||||
content: "",
|
||||
date: "",
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -319,7 +328,7 @@ export function MaskPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const [editingMaskId, setEditingMaskId] = useState<number | undefined>();
|
||||
const [editingMaskId, setEditingMaskId] = useState<string | undefined>();
|
||||
const editingMask =
|
||||
maskStore.get(editingMaskId) ?? BUILTIN_MASK_STORE.get(editingMaskId);
|
||||
const closeMaskModal = () => setEditingMaskId(undefined);
|
||||
|
@@ -51,9 +51,9 @@ function useShiftRange() {
|
||||
}
|
||||
|
||||
export function useMessageSelector() {
|
||||
const [selection, setSelection] = useState(new Set<number>());
|
||||
const updateSelection: Updater<Set<number>> = (updater) => {
|
||||
const newSelection = new Set<number>(selection);
|
||||
const [selection, setSelection] = useState(new Set<string>());
|
||||
const updateSelection: Updater<Set<string>> = (updater) => {
|
||||
const newSelection = new Set<string>(selection);
|
||||
updater(newSelection);
|
||||
setSelection(newSelection);
|
||||
};
|
||||
@@ -65,8 +65,8 @@ export function useMessageSelector() {
|
||||
}
|
||||
|
||||
export function MessageSelector(props: {
|
||||
selection: Set<number>;
|
||||
updateSelection: Updater<Set<number>>;
|
||||
selection: Set<string>;
|
||||
updateSelection: Updater<Set<string>>;
|
||||
defaultSelectAll?: boolean;
|
||||
onSelected?: (messages: ChatMessage[]) => void;
|
||||
}) {
|
||||
@@ -83,12 +83,12 @@ export function MessageSelector(props: {
|
||||
const config = useAppConfig();
|
||||
|
||||
const [searchInput, setSearchInput] = useState("");
|
||||
const [searchIds, setSearchIds] = useState(new Set<number>());
|
||||
const isInSearchResult = (id: number) => {
|
||||
const [searchIds, setSearchIds] = useState(new Set<string>());
|
||||
const isInSearchResult = (id: string) => {
|
||||
return searchInput.length === 0 || searchIds.has(id);
|
||||
};
|
||||
const doSearch = (text: string) => {
|
||||
const searchResults = new Set<number>();
|
||||
const searchResults = new Set<string>();
|
||||
if (text.length > 0) {
|
||||
messages.forEach((m) =>
|
||||
m.content.includes(text) ? searchResults.add(m.id!) : null,
|
||||
|
@@ -24,8 +24,8 @@ export function ModelConfigList(props: {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{config.models.map((v) => (
|
||||
<option value={v.name} key={v.name} disabled={!v.available}>
|
||||
{config.allModels().map((v, i) => (
|
||||
<option value={v.name} key={i} disabled={!v.available}>
|
||||
{v.name}
|
||||
</option>
|
||||
))}
|
||||
|
@@ -103,8 +103,7 @@ export function NewChat() {
|
||||
useCommand({
|
||||
mask: (id) => {
|
||||
try {
|
||||
const intId = parseInt(id);
|
||||
const mask = maskStore.get(intId) ?? BUILTIN_MASK_STORE.get(intId);
|
||||
const mask = maskStore.get(id) ?? BUILTIN_MASK_STORE.get(id);
|
||||
startChat(mask ?? undefined);
|
||||
} catch {
|
||||
console.error("[New Chat] failed to create chat from mask id=", id);
|
||||
|
@@ -48,8 +48,9 @@ import { useNavigate } from "react-router-dom";
|
||||
import { Avatar, AvatarPicker } from "./emoji";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { useSyncStore } from "../store/sync";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
function EditPromptModal(props: { id: number; onClose: () => void }) {
|
||||
function EditPromptModal(props: { id: string; onClose: () => void }) {
|
||||
const promptStore = usePromptStore();
|
||||
const prompt = promptStore.get(props.id);
|
||||
|
||||
@@ -107,7 +108,7 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
||||
const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
|
||||
const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
|
||||
|
||||
const [editingPromptId, setEditingPromptId] = useState<number>();
|
||||
const [editingPromptId, setEditingPromptId] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (searchInput.length > 0) {
|
||||
@@ -128,6 +129,8 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
||||
key="add"
|
||||
onClick={() =>
|
||||
promptStore.add({
|
||||
id: nanoid(),
|
||||
createdAt: Date.now(),
|
||||
title: "Empty Prompt",
|
||||
content: "Empty Prompt Content",
|
||||
})
|
||||
@@ -315,7 +318,6 @@ export function Settings() {
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||
const config = useAppConfig();
|
||||
const updateConfig = config.update;
|
||||
const chatStore = useChatStore();
|
||||
|
||||
const updateStore = useUpdateStore();
|
||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||
@@ -579,6 +581,38 @@ export function Settings() {
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.Disable.Title}
|
||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.disablePromptHint}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.disablePromptHint = e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.List}
|
||||
subTitle={Locale.Settings.Prompt.ListCount(
|
||||
builtinCount,
|
||||
customCount,
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
text={Locale.Settings.Prompt.Edit}
|
||||
onClick={() => setShowPromptModal(true)}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
{showAccessCode ? (
|
||||
<ListItem
|
||||
@@ -654,38 +688,22 @@ export function Settings() {
|
||||
)}
|
||||
</ListItem>
|
||||
) : null}
|
||||
</List>
|
||||
|
||||
<List>
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.Disable.Title}
|
||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||
title={Locale.Settings.CustomModel.Title}
|
||||
subTitle={Locale.Settings.CustomModel.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.disablePromptHint}
|
||||
type="text"
|
||||
value={config.customModels}
|
||||
placeholder="model1,model2,model3"
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.disablePromptHint = e.currentTarget.checked),
|
||||
config.update(
|
||||
(config) => (config.customModels = e.currentTarget.value),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.List}
|
||||
subTitle={Locale.Settings.Prompt.ListCount(
|
||||
builtinCount,
|
||||
customCount,
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
text={Locale.Settings.Prompt.Edit}
|
||||
onClick={() => setShowPromptModal(true)}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<SyncItems />
|
||||
|
@@ -286,7 +286,9 @@
|
||||
|
||||
&-content {
|
||||
.list {
|
||||
overflow: hidden;
|
||||
max-height: 90vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.list-item {
|
||||
cursor: pointer;
|
||||
|
Reference in New Issue
Block a user