feat: refactor select model

This commit is contained in:
butterfly
2024-04-29 16:29:47 +08:00
parent c34b8ab919
commit 8c28c408d8
18 changed files with 501 additions and 239 deletions

View File

@@ -19,15 +19,15 @@ import BreakIcon from "@/app/icons/eraserIcon.svg";
import SettingsIcon from "@/app/icons/configIcon.svg";
import ImageIcon from "@/app/icons/uploadImgIcon.svg";
import AddCircleIcon from "@/app/icons/addCircle.svg";
import BottomArrow from "@/app/icons/downArrowLgIcon.svg";
import Popover from "@/app/components/Popover";
import ModelSelect from "./ModelSelect";
export interface Action {
onClick: () => void;
onClick?: () => void;
text: string;
isShow: boolean;
pcRender?: () => JSX.Element;
render?: (key: string) => JSX.Element;
icon?: JSX.Element;
placement: "left" | "right";
}
@@ -39,7 +39,6 @@ export function ChatActions(props: {
showChatSetting: () => void;
scrollToBottom: () => void;
showPromptHints: () => void;
showModelSelector: (show: boolean) => void;
hitBottom: boolean;
uploading: boolean;
isMobileScreen: boolean;
@@ -101,15 +100,9 @@ export function ChatActions(props: {
placement: "left",
},
{
onClick: () => props.showModelSelector(true),
text: currentModel,
isShow: true,
pcRender: () => (
<div className="flex items-center justify-center gap-1 cursor-pointer rounded-chat-model-select pl-3 pr-2.5 py-2 font-common leading-4 bg-chat-actions-select-model">
{currentModel}
<BottomArrow />
</div>
),
isShow: !props.isMobileScreen,
render: (key: string) => <ModelSelect key={key} />,
placement: "left",
},
{
@@ -182,7 +175,7 @@ export function ChatActions(props: {
icon: <SettingsIcon />,
placement: "right",
},
] as const;
];
if (props.isMobileScreen) {
const content = (
@@ -226,12 +219,8 @@ export function ChatActions(props: {
{actions
.filter((v) => v.placement === "left" && v.isShow)
.map((act, ind) => {
if (act.pcRender) {
return (
<div key={act.text} onClick={act.onClick}>
{act.pcRender()}
</div>
);
if (act.render) {
return act.render(act.text);
}
return (
<Popover

View File

@@ -12,16 +12,10 @@ export interface ChatHeaderProps {
isMobileScreen: boolean;
setIsEditingMessage: (v: boolean) => void;
setShowExport: (v: boolean) => void;
showModelSelector: (v: boolean) => void;
}
export default function ChatHeader(props: ChatHeaderProps) {
const {
isMobileScreen,
setIsEditingMessage,
setShowExport,
showModelSelector,
} = props;
const { isMobileScreen, setIsEditingMessage, setShowExport } = props;
const navigate = useNavigate();
@@ -78,7 +72,7 @@ export default function ChatHeader(props: ChatHeaderProps) {
{isMobileScreen ? (
<div
className="flex items-center gap-1 cursor-pointer"
onClick={() => showModelSelector(true)}
onClick={() => {}}
>
{currentModel}
<BottomArrow />

View File

@@ -35,7 +35,6 @@ export interface ChatInputPanelProps {
setIsLoading: (value: boolean) => void;
showChatSetting: (value: boolean) => void;
_setMsgRenderIndex: (value: number) => void;
showModelSelector: (value: boolean) => void;
setAutoScroll: (value: boolean) => void;
scrollDomToBottom: () => void;
}
@@ -64,7 +63,6 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
_setMsgRenderIndex,
hitBottom,
inputRows,
showModelSelector,
setAutoScroll,
scrollDomToBottom,
} = props;
@@ -226,7 +224,6 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
`}
>
<ChatActions
showModelSelector={showModelSelector}
uploadImage={uploadImage}
setAttachImages={setAttachImages}
setUploading={setUploading}

View File

@@ -0,0 +1,147 @@
import Popover from "@/app/components/Popover";
import React, { useMemo, useRef } from "react";
import useRelativePosition, {
Orientation,
} from "@/app/hooks/useRelativePosition";
import Selected from "@/app/icons/selectedIcon.svg";
import { useChatStore } from "@/app/store/chat";
import { useAllModels } from "@/app/utils/hooks";
import { ModelType, useAppConfig } from "@/app/store/config";
import { showToast } from "@/app/components/ui-lib";
import BottomArrow from "@/app/icons/downArrowLgIcon.svg";
import BottomArrowMobile from "@/app/icons/bottomArrow.svg";
const ModelSelect = () => {
const config = useAppConfig();
const { isMobileScreen } = config;
const chatStore = useChatStore();
const currentModel = chatStore.currentSession().mask.modelConfig.model;
const allModels = useAllModels();
const models = useMemo(() => {
const filteredModels = allModels.filter((m) => m.available);
const defaultModel = filteredModels.find((m) => m.isDefault);
if (defaultModel) {
const arr = [
defaultModel,
...filteredModels.filter((m) => m !== defaultModel),
];
return arr;
} else {
return filteredModels;
}
}, [allModels]);
const rootRef = useRef<HTMLDivElement>(null);
const { position, getRelativePosition } = useRelativePosition({
delay: 0,
});
const contentRef = useMemo<{ current: HTMLDivElement | null }>(() => {
return {
current: null,
};
}, []);
const selectedItemRef = useRef<HTMLDivElement>(null);
const autoScrollToSelectedModal = () => {
window.setTimeout(() => {
const distanceToParent = selectedItemRef.current?.offsetTop || 0;
const childHeight = selectedItemRef.current?.offsetHeight || 0;
const parentHeight = contentRef.current?.offsetHeight || 0;
const distanceToParentCenter =
distanceToParent + childHeight / 2 - parentHeight / 2;
if (distanceToParentCenter > 0 && contentRef.current) {
contentRef.current.scrollTop = distanceToParentCenter;
}
});
};
const content = (
<div
className={` flex flex-col gap-1 overflow-y-auto overflow-x-hidden relative h-[100%]`}
>
{models?.map((o) => (
<div
key={o.displayName}
className={`flex items-center px-3 py-2 gap-3 rounded-action-btn hover:bg-select-option-hovered cursor-pointer`}
onClick={() => {
chatStore.updateCurrentSession((session) => {
session.mask.modelConfig.model = o.name as ModelType;
session.mask.syncGlobalConfig = false;
});
showToast(o.name);
}}
ref={currentModel === o.name ? selectedItemRef : undefined}
>
<div className={`flex-1`}>{o.name}</div>
<div
className={currentModel === o.name ? "opacity-100" : "opacity-0"}
>
<Selected />
</div>
</div>
))}
</div>
);
if (isMobileScreen) {
return (
<Popover
content={content}
trigger="click"
noArrow
placement={
position?.poi.relativePosition[1] !== Orientation.bottom ? "lb" : "lt"
}
popoverClassName="border border-select-popover rounded-lg shadow-select-popover-shadow w-actions-popover bg-select-popover-panel max-h-chat-actions-select-model-popover"
onShow={(e) => {
if (e) {
autoScrollToSelectedModal();
getRelativePosition(rootRef.current!, "");
}
}}
getPopoverPanelRef={(ref) => (contentRef.current = ref.current)}
>
<div className="flex items-center gap-1 cursor-pointer" ref={rootRef}>
{currentModel}
<BottomArrowMobile />
</div>
</Popover>
);
}
return (
<Popover
content={content}
trigger="click"
noArrow
placement={
position?.poi.relativePosition[1] !== Orientation.bottom ? "lb" : "lt"
}
popoverClassName="border border-select-popover rounded-lg shadow-select-popover-shadow w-actions-popover bg-select-popover-panel max-h-chat-actions-select-model-popover"
onShow={(e) => {
if (e) {
autoScrollToSelectedModal();
getRelativePosition(rootRef.current!, "");
}
}}
getPopoverPanelRef={(ref) => (contentRef.current = ref.current)}
>
<div
className="flex items-center justify-center gap-1 cursor-pointer rounded-chat-model-select pl-3 pr-2.5 py-2 font-common leading-4 bg-chat-actions-select-model hover:bg-chat-actions-select-model-hover"
ref={rootRef}
>
<div className="line-clamp-1 max-w-chat-actions-select-model">
{currentModel}
</div>
<BottomArrow />
</div>
</Popover>
);
};
export default ModelSelect;