diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 26716150..13105e84 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -61,7 +61,14 @@ import Locale from "../locales";
import { IconButton } from "./button";
import styles from "./chat.module.scss";
-import { ListItem, Modal, showConfirm, showPrompt, showToast } from "./ui-lib";
+import {
+ ListItem,
+ Modal,
+ Selector,
+ showConfirm,
+ showPrompt,
+ showToast,
+} from "./ui-lib";
import { useLocation, useNavigate } from "react-router-dom";
import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
import { Avatar } from "./emoji";
@@ -404,16 +411,11 @@ export function ChatActions(props: {
// switch model
const currentModel = chatStore.currentSession().mask.modelConfig.model;
- function nextModel() {
- const models = config.models.filter((m) => m.available).map((m) => m.name);
- const modelIndex = models.indexOf(currentModel);
- const nextIndex = (modelIndex + 1) % models.length;
- const nextModel = models[nextIndex];
- chatStore.updateCurrentSession((session) => {
- session.mask.modelConfig.model = nextModel as ModelType;
- session.mask.syncGlobalConfig = false;
- });
- }
+ const models = useMemo(
+ () => config.models.filter((m) => m.available).map((m) => m.name),
+ [config.models],
+ );
+ const [showModelSelector, setShowModelSelector] = useState(false);
return (
@@ -485,10 +487,28 @@ export function ChatActions(props: {
/>
setShowModelSelector(true)}
text={currentModel}
icon={}
/>
+
+ {showModelSelector && (
+ ({
+ title: m,
+ value: m,
+ }))}
+ onClose={() => setShowModelSelector(false)}
+ onSelection={(s) => {
+ if (s.length === 0) return;
+ chatStore.updateCurrentSession((session) => {
+ session.mask.modelConfig.model = s[0] as ModelType;
+ session.mask.syncGlobalConfig = false;
+ });
+ showToast(s[0]);
+ }}
+ />
+ )}
);
}
diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss
index 86b467e5..6e8b64e8 100644
--- a/app/components/ui-lib.module.scss
+++ b/app/components/ui-lib.module.scss
@@ -62,6 +62,7 @@
box-shadow: var(--card-shadow);
margin-bottom: 20px;
animation: slide-in ease 0.3s;
+ background: var(--white);
}
.list .list-item:last-child {
@@ -270,3 +271,34 @@
border: 1px solid var(--primary);
}
}
+
+.selector {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ width: 100vw;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &-content {
+ .list {
+ overflow: hidden;
+
+ .list-item {
+ cursor: pointer;
+ background-color: var(--white);
+
+ &:hover {
+ filter: brightness(0.95);
+ }
+
+ &:active {
+ filter: brightness(0.9);
+ }
+ }
+ }
+ }
+}
diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx
index 512044dc..814c0dd1 100644
--- a/app/components/ui-lib.tsx
+++ b/app/components/ui-lib.tsx
@@ -47,9 +47,13 @@ export function ListItem(props: {
children?: JSX.Element | JSX.Element[];
icon?: JSX.Element;
className?: string;
+ onClick?: () => void;
}) {
return (
-
+
{props.icon &&
{props.icon}
}
@@ -432,3 +436,37 @@ export function showImageModal(img: string) {
),
});
}
+
+export function Selector
(props: {
+ items: Array<{
+ title: string;
+ subTitle?: string;
+ value: T;
+ }>;
+ onSelection?: (selection: T[]) => void;
+ onClose?: () => void;
+ multiple?: boolean;
+}) {
+ return (
+
+
+
+ {props.items.map((item, i) => {
+ return (
+ {
+ props.onSelection?.([item.value]);
+ props.onClose?.();
+ }}
+ >
+ );
+ })}
+
+
+
+ );
+}