Styling changes, fixing MCP and default model choices.

This commit is contained in:
DominicJamesWhite 2025-04-09 13:24:34 +02:00
parent b846530531
commit bc36926535
4 changed files with 23 additions and 420 deletions

View File

@ -12,48 +12,23 @@ import React, {
import SendWhiteIcon from "../icons/send-white.svg"; import SendWhiteIcon from "../icons/send-white.svg";
import BrainIcon from "../icons/brain.svg"; import BrainIcon from "../icons/brain.svg";
import RenameIcon from "../icons/rename.svg"; import RenameIcon from "../icons/rename.svg";
import EditIcon from "../icons/rename.svg";
import ExportIcon from "../icons/share.svg";
import ReturnIcon from "../icons/return.svg"; import ReturnIcon from "../icons/return.svg";
import CopyIcon from "../icons/copy.svg"; import CopyIcon from "../icons/copy.svg";
import SpeakIcon from "../icons/speak.svg";
import SpeakStopIcon from "../icons/speak-stop.svg";
import LoadingIcon from "../icons/three-dots.svg"; import LoadingIcon from "../icons/three-dots.svg";
import LoadingButtonIcon from "../icons/loading.svg"; import LoadingButtonIcon from "../icons/loading.svg";
import PromptIcon from "../icons/prompt.svg";
import MaskIcon from "../icons/mask.svg";
import MaxIcon from "../icons/max.svg";
import MinIcon from "../icons/min.svg";
import ResetIcon from "../icons/reload.svg"; import ResetIcon from "../icons/reload.svg";
import ReloadIcon from "../icons/reload.svg";
import BreakIcon from "../icons/break.svg";
import SettingsIcon from "../icons/chat-settings.svg";
import DeleteIcon from "../icons/clear.svg"; import DeleteIcon from "../icons/clear.svg";
import PinIcon from "../icons/pin.svg";
import ConfirmIcon from "../icons/confirm.svg"; import ConfirmIcon from "../icons/confirm.svg";
import CloseIcon from "../icons/close.svg"; import CloseIcon from "../icons/close.svg";
import CancelIcon from "../icons/cancel.svg"; import CancelIcon from "../icons/cancel.svg";
import ImageIcon from "../icons/image.svg";
import LightIcon from "../icons/light.svg";
import DarkIcon from "../icons/dark.svg";
import AutoIcon from "../icons/auto.svg";
import BottomIcon from "../icons/bottom.svg";
import StopIcon from "../icons/pause.svg"; import StopIcon from "../icons/pause.svg";
import RobotIcon from "../icons/robot.svg";
import SizeIcon from "../icons/size.svg";
import QualityIcon from "../icons/hd.svg";
import StyleIcon from "../icons/palette.svg";
import PluginIcon from "../icons/plugin.svg";
import ShortcutkeyIcon from "../icons/shortcutkey.svg";
import McpToolIcon from "../icons/tool.svg"; import McpToolIcon from "../icons/tool.svg";
import HeadphoneIcon from "../icons/headphone.svg";
import { import {
BOT_HELLO, BOT_HELLO,
ChatMessage, ChatMessage,
createMessage, createMessage,
DEFAULT_TOPIC, DEFAULT_TOPIC,
ModelType,
SubmitKey, SubmitKey,
Theme, Theme,
useAccessStore, useAccessStore,
@ -67,14 +42,11 @@ import {
copyToClipboard, copyToClipboard,
getMessageImages, getMessageImages,
getMessageTextContent, getMessageTextContent,
isDalle3,
isVisionModel, isVisionModel,
safeLocalStorage, safeLocalStorage,
getModelSizes, getModelSizes,
supportsCustomSize,
useMobileScreen, useMobileScreen,
selectOrCopy, selectOrCopy,
showPlugins,
} from "../utils"; } from "../utils";
import { uploadImage as uploadImageRemote } from "@/app/utils/chat"; import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
@ -89,15 +61,7 @@ import Locale from "../locales";
import { IconButton } from "./button"; import { IconButton } from "./button";
import styles from "./chat.module.scss"; import styles from "./chat.module.scss";
import { import { List, ListItem, Modal, showConfirm, showToast } from "./ui-lib";
List,
ListItem,
Modal,
Selector,
showConfirm,
showPrompt,
showToast,
} from "./ui-lib";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { import {
CHAT_PAGE_SIZE, CHAT_PAGE_SIZE,
@ -116,12 +80,11 @@ import { prettyObject } from "../utils/format";
import { ExportMessageModal } from "./exporter"; import { ExportMessageModal } from "./exporter";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { useAllModels } from "../utils/hooks"; import { useAllModels } from "../utils/hooks";
import { ClientApi, MultimodalContent } from "../client/api"; import { ClientApi } from "../client/api";
import { createTTSPlayer } from "../utils/audio"; import { createTTSPlayer } from "../utils/audio";
import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts"; import { MsEdgeTTS, OUTPUT_FORMAT } from "../utils/ms_edge_tts";
import { isEmpty } from "lodash-es"; import { isEmpty } from "lodash-es";
import { getModelProvider } from "../utils/model";
import { RealtimeChat } from "@/app/components/realtime-chat"; import { RealtimeChat } from "@/app/components/realtime-chat";
import clsx from "clsx"; import clsx from "clsx";
import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions"; import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions";
@ -598,250 +561,9 @@ export function ChatActions(props: {
return ( return (
<div className={styles["chat-input-actions"]}> <div className={styles["chat-input-actions"]}>
<> {/* Actions removed for simplicity */}
{couldStop && (
<ChatAction
onClick={stopAll}
text={Locale.Chat.InputActions.Stop}
icon={<StopIcon />}
/>
)}
{!props.hitBottom && (
<ChatAction
onClick={props.scrollToBottom}
text={Locale.Chat.InputActions.ToBottom}
icon={<BottomIcon />}
/>
)}
{props.hitBottom && (
<ChatAction
onClick={props.showPromptModal}
text={Locale.Chat.InputActions.Settings}
icon={<SettingsIcon />}
/>
)}
{showUploadImage && (
<ChatAction
onClick={props.uploadImage}
text={Locale.Chat.InputActions.UploadImage}
icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
/>
)}
<ChatAction
onClick={nextTheme}
text={Locale.Chat.InputActions.Theme[theme]}
icon={
<>
{theme === Theme.Auto ? (
<AutoIcon />
) : theme === Theme.Light ? (
<LightIcon />
) : theme === Theme.Dark ? (
<DarkIcon />
) : null}
</>
}
/>
<ChatAction
onClick={props.showPromptHints}
text={Locale.Chat.InputActions.Prompt}
icon={<PromptIcon />}
/>
<ChatAction
onClick={() => {
navigate(Path.Masks);
}}
text={Locale.Chat.InputActions.Masks}
icon={<MaskIcon />}
/>
<ChatAction
text={Locale.Chat.InputActions.Clear}
icon={<BreakIcon />}
onClick={() => {
chatStore.updateTargetSession(session, (session) => {
if (session.clearContextIndex === session.messages.length) {
session.clearContextIndex = undefined;
} else {
session.clearContextIndex = session.messages.length;
session.memoryPrompt = ""; // will clear memory
}
});
}}
/>
<ChatAction
onClick={() => setShowModelSelector(true)}
text={currentModelName}
icon={<RobotIcon />}
/>
{showModelSelector && (
<Selector
defaultSelectedValue={`${currentModel}@${currentProviderName}`}
items={models.map((m) => ({
title: `${m.displayName}${
m?.provider?.providerName
? " (" + m?.provider?.providerName + ")"
: ""
}`,
value: `${m.name}@${m?.provider?.providerName}`,
}))}
onClose={() => setShowModelSelector(false)}
onSelection={(s) => {
if (s.length === 0) return;
const [model, providerName] = getModelProvider(s[0]);
chatStore.updateTargetSession(session, (session) => {
session.mask.modelConfig.model = model as ModelType;
session.mask.modelConfig.providerName =
providerName as ServiceProvider;
session.mask.syncGlobalConfig = false;
});
if (providerName == "ByteDance") {
const selectedModel = models.find(
(m) =>
m.name == model &&
m?.provider?.providerName == providerName,
);
showToast(selectedModel?.displayName ?? "");
} else {
showToast(model);
}
}}
/>
)}
{supportsCustomSize(currentModel) && (
<ChatAction
onClick={() => setShowSizeSelector(true)}
text={currentSize}
icon={<SizeIcon />}
/>
)}
{showSizeSelector && (
<Selector
defaultSelectedValue={currentSize}
items={modelSizes.map((m) => ({
title: m,
value: m,
}))}
onClose={() => setShowSizeSelector(false)}
onSelection={(s) => {
if (s.length === 0) return;
const size = s[0];
chatStore.updateTargetSession(session, (session) => {
session.mask.modelConfig.size = size;
});
showToast(size);
}}
/>
)}
{isDalle3(currentModel) && (
<ChatAction
onClick={() => setShowQualitySelector(true)}
text={currentQuality}
icon={<QualityIcon />}
/>
)}
{showQualitySelector && (
<Selector
defaultSelectedValue={currentQuality}
items={dalle3Qualitys.map((m) => ({
title: m,
value: m,
}))}
onClose={() => setShowQualitySelector(false)}
onSelection={(q) => {
if (q.length === 0) return;
const quality = q[0];
chatStore.updateTargetSession(session, (session) => {
session.mask.modelConfig.quality = quality;
});
showToast(quality);
}}
/>
)}
{isDalle3(currentModel) && (
<ChatAction
onClick={() => setShowStyleSelector(true)}
text={currentStyle}
icon={<StyleIcon />}
/>
)}
{showStyleSelector && (
<Selector
defaultSelectedValue={currentStyle}
items={dalle3Styles.map((m) => ({
title: m,
value: m,
}))}
onClose={() => setShowStyleSelector(false)}
onSelection={(s) => {
if (s.length === 0) return;
const style = s[0];
chatStore.updateTargetSession(session, (session) => {
session.mask.modelConfig.style = style;
});
showToast(style);
}}
/>
)}
{showPlugins(currentProviderName, currentModel) && (
<ChatAction
onClick={() => {
if (pluginStore.getAll().length == 0) {
navigate(Path.Plugins);
} else {
setShowPluginSelector(true);
}
}}
text={Locale.Plugin.Name}
icon={<PluginIcon />}
/>
)}
{showPluginSelector && (
<Selector
multiple
defaultSelectedValue={chatStore.currentSession().mask?.plugin}
items={pluginStore.getAll().map((item) => ({
title: `${item?.title}@${item?.version}`,
value: item?.id,
}))}
onClose={() => setShowPluginSelector(false)}
onSelection={(s) => {
chatStore.updateTargetSession(session, (session) => {
session.mask.plugin = s as string[];
});
}}
/>
)}
{!isMobileScreen && (
<ChatAction
onClick={() => props.setShowShortcutKeyModal(true)}
text={Locale.Chat.ShortcutKey.Title}
icon={<ShortcutkeyIcon />}
/>
)}
{!isMobileScreen && <MCPAction />}
</>
<div className={styles["chat-input-actions-end"]}> <div className={styles["chat-input-actions-end"]}>
{config.realtimeConfig.enable && ( {/* Actions removed for simplicity */}
<ChatAction
onClick={() => props.setShowChatSidePanel(true)}
text={"Realtime Chat"}
icon={<HeadphoneIcon />}
/>
)}
</div> </div>
</div> </div>
); );
@ -1713,17 +1435,7 @@ function _Chat() {
</div> </div>
</div> </div>
<div className="window-actions"> <div className="window-actions">
<div className="window-action-button"> {/* Refresh, Export, Fullscreen buttons removed for simplicity */}
<IconButton
icon={<ReloadIcon />}
bordered
title={Locale.Chat.Actions.RefreshTitle}
onClick={() => {
showToast(Locale.Chat.Actions.RefreshToast);
chatStore.summarizeSession(true, session);
}}
/>
</div>
{!isMobileScreen && ( {!isMobileScreen && (
<div className="window-action-button"> <div className="window-action-button">
<IconButton <IconButton
@ -1735,31 +1447,6 @@ function _Chat() {
/> />
</div> </div>
)} )}
<div className="window-action-button">
<IconButton
icon={<ExportIcon />}
bordered
title={Locale.Chat.Actions.Export}
onClick={() => {
setShowExport(true);
}}
/>
</div>
{showMaxIcon && (
<div className="window-action-button">
<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),
);
}}
/>
</div>
)}
</div> </div>
<PromptToast <PromptToast
@ -1807,47 +1494,7 @@ function _Chat() {
<div className={styles["chat-message-container"]}> <div className={styles["chat-message-container"]}>
<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"]}> {/* Edit button removed for simplicity */}
<IconButton
icon={<EditIcon />}
aria={Locale.Chat.Actions.Edit}
onClick={async () => {
const newMessage = await showPrompt(
Locale.Chat.Actions.Edit,
getMessageTextContent(message),
10,
);
let newContent:
| string
| MultimodalContent[] = newMessage;
const images = getMessageImages(message);
if (images.length > 0) {
newContent = [
{ type: "text", text: newMessage },
];
for (let i = 0; i < images.length; i++) {
newContent.push({
type: "image_url",
image_url: {
url: images[i],
},
});
}
}
chatStore.updateTargetSession(
session,
(session) => {
const m = session.mask.context
.concat(session.messages)
.find((m) => m.id === message.id);
if (m) {
m.content = newContent;
}
},
);
}}
></IconButton>
</div>
{isUser ? ( {isUser ? (
<Avatar avatar={config.avatar} /> <Avatar avatar={config.avatar} />
) : ( ) : (
@ -1885,25 +1532,7 @@ function _Chat() {
/> />
) : ( ) : (
<> <>
<ChatAction {/* Retry, Delete, Pin, Speech actions removed for simplicity */}
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 <ChatAction
text={Locale.Chat.Actions.Copy} text={Locale.Chat.Actions.Copy}
icon={<CopyIcon />} icon={<CopyIcon />}
@ -1913,27 +1542,6 @@ function _Chat() {
) )
} }
/> />
{config.ttsConfig.enable && (
<ChatAction
text={
speechStatus
? Locale.Chat.Actions.StopSpeech
: Locale.Chat.Actions.Speech
}
icon={
speechStatus ? (
<SpeakStopIcon />
) : (
<SpeakIcon />
)
}
onClick={() =>
openaiSpeech(
getMessageTextContent(message),
)
}
/>
)}
</> </>
)} )}
</div> </div>

View File

@ -3,15 +3,10 @@ import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
import styles from "./home.module.scss"; import styles from "./home.module.scss";
import { IconButton } from "./button"; import { IconButton } from "./button";
import SettingsIcon from "../icons/settings.svg";
import GithubIcon from "../icons/github.svg";
import ChatGptIcon from "../icons/chatgpt.svg"; import ChatGptIcon from "../icons/chatgpt.svg";
import AddIcon from "../icons/add.svg"; import AddIcon from "../icons/add.svg";
import DeleteIcon from "../icons/delete.svg"; import DeleteIcon from "../icons/delete.svg";
import MaskIcon from "../icons/mask.svg";
import McpIcon from "../icons/mcp.svg";
import DragIcon from "../icons/drag.svg"; import DragIcon from "../icons/drag.svg";
import DiscoveryIcon from "../icons/discovery.svg";
import Locale from "../locales"; import Locale from "../locales";
@ -23,10 +18,9 @@ import {
MIN_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH, NARROW_SIDEBAR_WIDTH,
Path, Path,
REPO_URL,
} from "../constant"; } from "../constant";
import { Link, useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { isIOS, useMobileScreen } from "../utils"; import { isIOS, useMobileScreen } from "../utils";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { Selector, showConfirm } from "./ui-lib"; import { Selector, showConfirm } from "./ui-lib";
@ -250,13 +244,13 @@ export function SideBar(props: { className?: string }) {
{...props} {...props}
> >
<SideBarHeader <SideBarHeader
title="NextChat" title="Canyon"
subTitle="Build your own AI assistant." subTitle="Alpha 0.7"
logo={<ChatGptIcon />} logo={<ChatGptIcon />}
shouldNarrow={shouldNarrow} shouldNarrow={shouldNarrow}
> >
<div className={styles["sidebar-header-bar"]}> <div className={styles["sidebar-header-bar"]}>
<IconButton {/* <IconButton
icon={<MaskIcon />} icon={<MaskIcon />}
text={shouldNarrow ? undefined : Locale.Mask.Name} text={shouldNarrow ? undefined : Locale.Mask.Name}
className={styles["sidebar-bar-button"]} className={styles["sidebar-bar-button"]}
@ -268,8 +262,8 @@ export function SideBar(props: { className?: string }) {
} }
}} }}
shadow shadow
/> /> */}
{mcpEnabled && ( {/* {mcpEnabled && (
<IconButton <IconButton
icon={<McpIcon />} icon={<McpIcon />}
text={shouldNarrow ? undefined : Locale.Mcp.Name} text={shouldNarrow ? undefined : Locale.Mcp.Name}
@ -279,14 +273,14 @@ export function SideBar(props: { className?: string }) {
}} }}
shadow shadow
/> />
)} )} */}
<IconButton {/* <IconButton
icon={<DiscoveryIcon />} icon={<DiscoveryIcon />}
text={shouldNarrow ? undefined : Locale.Discovery.Name} text={shouldNarrow ? undefined : Locale.Discovery.Name}
className={styles["sidebar-bar-button"]} className={styles["sidebar-bar-button"]}
onClick={() => setshowDiscoverySelector(true)} onClick={() => setshowDiscoverySelector(true)}
shadow shadow
/> /> */}
</div> </div>
{showDiscoverySelector && ( {showDiscoverySelector && (
<Selector <Selector
@ -327,7 +321,7 @@ 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 <IconButton
aria={Locale.Settings.Title} aria={Locale.Settings.Title}
@ -335,8 +329,8 @@ export function SideBar(props: { className?: string }) {
shadow 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 <IconButton
aria={Locale.Export.MessageFromChatGPT} aria={Locale.Export.MessageFromChatGPT}
@ -344,7 +338,7 @@ export function SideBar(props: { className?: string }) {
shadow shadow
/> />
</a> </a>
</div> </div> */}
</> </>
} }
secondaryAction={ secondaryAction={

View File

@ -525,6 +525,7 @@ const googleModels = [
"gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-01-21",
"gemini-2.0-pro-exp", "gemini-2.0-pro-exp",
"gemini-2.0-pro-exp-02-05", "gemini-2.0-pro-exp-02-05",
"gemini-2.5-pro-preview-03-25",
]; ];
const anthropicModels = [ const anthropicModels = [

View File

@ -64,8 +64,8 @@ export const DEFAULT_CONFIG = {
models: DEFAULT_MODELS as any as LLMModel[], models: DEFAULT_MODELS as any as LLMModel[],
modelConfig: { modelConfig: {
model: "gpt-4o-mini" as ModelType, model: "gemini-2.5-pro-preview-03-25" as ModelType,
providerName: "OpenAI" as ServiceProvider, providerName: "Google" as ServiceProvider,
temperature: 0.5, temperature: 0.5,
top_p: 1, top_p: 1,
max_tokens: 4000, max_tokens: 4000,