diff --git a/src/apps/chat/components/applayout/ChatDropdowns.tsx b/src/apps/chat/components/applayout/ChatDropdowns.tsx index 22b2d614f..676caa4df 100644 --- a/src/apps/chat/components/applayout/ChatDropdowns.tsx +++ b/src/apps/chat/components/applayout/ChatDropdowns.tsx @@ -1,93 +1,24 @@ import * as React from 'react'; -import { shallow } from 'zustand/shallow'; -import { ListItemButton, ListItemDecorator, Typography } from '@mui/joy'; -import BuildCircleIcon from '@mui/icons-material/BuildCircle'; -import SettingsIcon from '@mui/icons-material/Settings'; - -import { DLLMId, DModelSourceId } from '~/modules/llms/llm.types'; -import { SystemPurposeId, SystemPurposes } from '../../../../data'; -import { useModelsStore } from '~/modules/llms/store-llms'; - -import { AppBarDropdown, DropdownItems } from '~/common/layout/AppBarDropdown'; -import { useChatStore } from '~/common/state/store-chats'; -import { useUIPreferencesStore, useUIStateStore } from '~/common/state/store-ui'; +import { useChatLLMDropdown } from './useLLMDropdown'; +import { usePersonaIdDropdown } from './usePersonaDropdown'; export function ChatDropdowns(props: { conversationId: string | null }) { - // external state - const { chatLLMId, setChatLLMId, llms } = useModelsStore(state => ({ - chatLLMId: state.chatLLMId, - setChatLLMId: state.setChatLLMId, - llms: state.llms, - }), shallow); - - const { zenMode } = useUIPreferencesStore(state => ({ zenMode: state.zenMode }), shallow); - const { systemPurposeId, setSystemPurposeId } = useChatStore(state => { - const conversation = state.conversations.find(conversation => conversation.id === props.conversationId); - return { - systemPurposeId: conversation?.systemPurposeId ?? null, - setSystemPurposeId: state.setSystemPurposeId, - }; - }, shallow); - const { openLLMOptions, openModelsSetup } = useUIStateStore(state => ({ - openLLMOptions: state.openLLMOptions, openModelsSetup: state.openModelsSetup, - }), shallow); - - const handleChatModelChange = (event: any, value: DLLMId | null) => - value && props.conversationId && setChatLLMId(value); - - const handleSystemPurposeChange = (event: any, value: SystemPurposeId | null) => - value && props.conversationId && setSystemPurposeId(props.conversationId, value); - - const handleOpenLLMOptions = () => chatLLMId && openLLMOptions(chatLLMId); - - // build model menu items, filtering-out hidden models, and add Source separators - const llmItems: DropdownItems = {}; - let prevSourceId: DModelSourceId | null = null; - for (const llm of llms) { - if (!llm.hidden || llm.id === chatLLMId) { - if (!prevSourceId || llm.sId !== prevSourceId) { - if (prevSourceId) - llmItems[`sep-${llm.id}`] = { type: 'separator', title: llm.sId }; - prevSourceId = llm.sId; - } - llmItems[llm.id] = { title: llm.label }; - } - } + // state + const { chatLLMDropdown } = useChatLLMDropdown(); + const { personaDropdown } = usePersonaIdDropdown(props.conversationId); return <> {/* Model selector */} - - - {chatLLMId && ( - - Options - - )} - - - Models - - - } - /> + {chatLLMDropdown} {/* Persona selector */} - {systemPurposeId && ( - - )} + {personaDropdown} ; } diff --git a/src/apps/chat/components/applayout/useLLMDropdown.tsx b/src/apps/chat/components/applayout/useLLMDropdown.tsx new file mode 100644 index 000000000..cc468d658 --- /dev/null +++ b/src/apps/chat/components/applayout/useLLMDropdown.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import { shallow } from 'zustand/shallow'; + +import { ListItemButton, ListItemDecorator } from '@mui/joy'; +import BuildCircleIcon from '@mui/icons-material/BuildCircle'; +import SettingsIcon from '@mui/icons-material/Settings'; + +import { DLLM, DLLMId, DModelSourceId } from '~/modules/llms/llm.types'; +import { useModelsStore } from '~/modules/llms/store-llms'; + +import { AppBarDropdown, DropdownItems } from '~/common/layout/AppBarDropdown'; +import { useUIStateStore } from '~/common/state/store-ui'; + + +function AppBarLLMDropdown(props: { + llms: DLLM[], + llmId: DLLMId | null, + setLlmId: (llmId: DLLMId | null) => void, + placeholder?: string, +}) { + + // build model menu items, filtering-out hidden models, and add Source separators + const llmItems: DropdownItems = {}; + let prevSourceId: DModelSourceId | null = null; + for (const llm of props.llms) { + if (!llm.hidden || llm.id === props.llmId) { + if (!prevSourceId || llm.sId !== prevSourceId) { + if (prevSourceId) + llmItems[`sep-${llm.id}`] = { type: 'separator', title: llm.sId }; + prevSourceId = llm.sId; + } + llmItems[llm.id] = { title: llm.label }; + } + } + + const handleChatLLMChange = (_event: any, value: DLLMId | null) => value && props.setLlmId(value); + + const handleOpenLLMOptions = () => props.llmId && useUIStateStore.getState().openLLMOptions(props.llmId); + + const handleOpenModelsSetup = () => useUIStateStore.getState().openModelsSetup(); + + return ( + + + {props.llmId && ( + + + Options + + )} + + + + Models + + + } + /> + ); +} + +export function useChatLLMDropdown() { + // external state + const { llms, chatLLMId, setChatLLMId } = useModelsStore(state => ({ + llms: state.llms, + chatLLMId: state.chatLLMId, + setChatLLMId: state.setChatLLMId, + }), shallow); + + const chatLLMDropdown = React.useMemo( + () => , + [llms, chatLLMId, setChatLLMId], + ); + + return { chatLLMId, chatLLMDropdown }; +} + +export function useTempLLMDropdown(props: { initialLlmId: DLLMId | null }) { + // local state + const [llmId, setLlmId] = React.useState(props.initialLlmId); + + // external state + const llms = useModelsStore(state => state.llms, shallow); + + const chatLLMDropdown = React.useMemo( + () => , + [llms, llmId, setLlmId], + ); + + return { llmId, chatLLMDropdown }; +} \ No newline at end of file diff --git a/src/apps/chat/components/applayout/usePersonaDropdown.tsx b/src/apps/chat/components/applayout/usePersonaDropdown.tsx new file mode 100644 index 000000000..11050bef7 --- /dev/null +++ b/src/apps/chat/components/applayout/usePersonaDropdown.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { shallow } from 'zustand/shallow'; + +import { SystemPurposeId, SystemPurposes } from '../../../../data'; + +import { AppBarDropdown } from '~/common/layout/AppBarDropdown'; +import { useChatStore } from '~/common/state/store-chats'; +import { useUIPreferencesStore } from '~/common/state/store-ui'; + + +function AppBarPersonaDropdown(props: { + systemPurposeId: SystemPurposeId | null, + setSystemPurposeId: (systemPurposeId: SystemPurposeId | null) => void, +}) { + + // external state + const { zenMode } = useUIPreferencesStore(state => ({ + zenMode: state.zenMode, + }), shallow); + + const handleSystemPurposeChange = (_event: any, value: SystemPurposeId | null) => props.setSystemPurposeId(value); + + return ( + + ); + +} + +export function usePersonaIdDropdown(conversationId: string | null) { + // external state + const { systemPurposeId } = useChatStore(state => { + const conversation = state.conversations.find(conversation => conversation.id === conversationId); + return { + systemPurposeId: conversation?.systemPurposeId ?? null, + }; + }, shallow); + + const personaDropdown = React.useMemo(() => + systemPurposeId ? { + if (conversationId && systemPurposeId) + useChatStore.getState().setSystemPurposeId(conversationId, systemPurposeId); + }} + /> : null, + [conversationId, systemPurposeId], + ); + + return { personaDropdown }; +} \ No newline at end of file