From ba2f4115ceffa45f331e759f406a016776f3e8bb Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Thu, 18 May 2023 19:00:19 -0700 Subject: [PATCH] Cleanup SendMode --- src/apps/chat/Chat.tsx | 14 +++--- src/apps/chat/components/ChatMessageList.tsx | 15 ++++-- .../chat/components/composer/Composer.tsx | 37 ++++----------- .../chat/components/composer/SendModeMenu.tsx | 47 +++++++++++++++++++ .../components/composer/composer.store.ts} | 9 +--- .../PurposeSelector.tsx | 5 +- .../purpose-selector/purposes.store.ts} | 0 src/data.ts | 20 -------- 8 files changed, 76 insertions(+), 71 deletions(-) create mode 100644 src/apps/chat/components/composer/SendModeMenu.tsx rename src/{common/state/store-composer.ts => apps/chat/components/composer/composer.store.ts} (84%) rename src/apps/chat/components/{ => purpose-selector}/PurposeSelector.tsx (98%) rename src/{common/state/store-purposes.ts => apps/chat/components/purpose-selector/purposes.store.ts} (100%) diff --git a/src/apps/chat/Chat.tsx b/src/apps/chat/Chat.tsx index c9b67f8bb..a1876b2c8 100644 --- a/src/apps/chat/Chat.tsx +++ b/src/apps/chat/Chat.tsx @@ -17,7 +17,6 @@ import { conversationToMarkdown } from '~/common/util/conversationToMarkdown'; import { createDMessage, DMessage, restoreConversationFromJson, useChatStore } from '~/common/state/store-chats'; import { extractCommands } from '~/common/util/extractCommands'; import { useApplicationBarStore } from '~/common/layouts/appbar/store-applicationbar'; -import { useComposerStore } from '~/common/state/store-composer'; import { useSettingsStore } from '~/common/state/store-settings'; import { ActionItems } from './components/appbar/ActionItems'; @@ -34,6 +33,8 @@ import { runReActUpdatingState } from './editors/react-tangent'; const SPECIAL_ID_ALL_CHATS = 'all-chats'; +export type SendModeId = 'immediate' | 'react'; + export function Chat() { @@ -48,9 +49,6 @@ export function Chat() { // external state const theme = useTheme(); - const { sendModeId } = useComposerStore(state => ({ - sendModeId: state.sendModeId, - }), shallow); const { activeConversationId, isConversationEmpty, conversationsCount, importConversation, deleteAllConversations, setMessages, systemPurposeId, setAutoTitle } = useChatStore(state => { const conversation = state.conversations.find(conversation => conversation.id === state.activeConversationId); return { @@ -66,7 +64,7 @@ export function Chat() { }, shallow); - const handleExecuteConversation = async (conversationId: string, history: DMessage[]) => { + const handleExecuteConversation = async (sendModeId: SendModeId, conversationId: string, history: DMessage[]) => { const { chatLLMId } = useModelsStore.getState(); if (!conversationId || !chatLLMId) return; @@ -110,10 +108,10 @@ export function Chat() { const _findConversation = (conversationId: string) => conversationId ? useChatStore.getState().conversations.find(c => c.id === conversationId) ?? null : null; - const handleSendUserMessage = async (conversationId: string, userText: string) => { + const handleSendUserMessage = async (sendModeId: SendModeId, conversationId: string, userText: string) => { const conversation = _findConversation(conversationId); if (conversation) - return await handleExecuteConversation(conversationId, [...conversation.messages, createDMessage('user', userText)]); + return await handleExecuteConversation(sendModeId, conversationId, [...conversation.messages, createDMessage('user', userText)]); }; const handleImagineFromText = async (conversationId: string, messageText: string) => { @@ -121,7 +119,7 @@ export function Chat() { if (conversation) { const prompt = await imaginePromptFromText(messageText); if (prompt) - return await handleExecuteConversation(conversationId, [...conversation.messages, createDMessage('user', `${CmdRunProdia[0]} ${prompt}`)]); + return await handleExecuteConversation('immediate', conversationId, [...conversation.messages, createDMessage('user', `${CmdRunProdia[0]} ${prompt}`)]); } }; diff --git a/src/apps/chat/components/ChatMessageList.tsx b/src/apps/chat/components/ChatMessageList.tsx index 6ae679841..c92ad91de 100644 --- a/src/apps/chat/components/ChatMessageList.tsx +++ b/src/apps/chat/components/ChatMessageList.tsx @@ -11,13 +11,20 @@ import { useSettingsStore } from '~/common/state/store-settings'; import { ChatMessage } from './message/ChatMessage'; import { ChatMessageSelectable, MessagesSelectionHeader } from './message/ChatMessageSelectable'; -import { PurposeSelector } from './PurposeSelector'; +import { PurposeSelector } from './purpose-selector/PurposeSelector'; +import { SendModeId } from '../Chat'; /** * A list of ChatMessages */ -export function ChatMessageList(props: { conversationId: string | null, isMessageSelectionMode: boolean, setIsMessageSelectionMode: (isMessageSelectionMode: boolean) => void, onExecuteConversation: (conversationId: string, history: DMessage[]) => void, onImagineFromText: (conversationId: string, userText: string) => void, sx?: SxProps }) { +export function ChatMessageList(props: { + conversationId: string | null, + isMessageSelectionMode: boolean, setIsMessageSelectionMode: (isMessageSelectionMode: boolean) => void, + onExecuteConversation: (sendModeId: SendModeId, conversationId: string, history: DMessage[]) => void, + onImagineFromText: (conversationId: string, userText: string) => void, + sx?: SxProps +}) { // state const [selectedMessages, setSelectedMessages] = React.useState>(new Set()); @@ -44,11 +51,11 @@ export function ChatMessageList(props: { conversationId: string | null, isMessag const handleRestartFromMessage = (messageId: string, offset: number) => { const truncatedHistory = messages.slice(0, messages.findIndex(m => m.id === messageId) + offset + 1); - props.conversationId && props.onExecuteConversation(props.conversationId, truncatedHistory); + props.conversationId && props.onExecuteConversation('immediate', props.conversationId, truncatedHistory); }; const handleRunExample = (text: string) => - props.conversationId && props.onExecuteConversation(props.conversationId, [...messages, createDMessage('user', text)]); + props.conversationId && props.onExecuteConversation('immediate', props.conversationId, [...messages, createDMessage('user', text)]); // hide system messages if the user chooses so diff --git a/src/apps/chat/components/composer/Composer.tsx b/src/apps/chat/components/composer/Composer.tsx index 2f074ec29..09a72ffae 100644 --- a/src/apps/chat/components/composer/Composer.tsx +++ b/src/apps/chat/components/composer/Composer.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { shallow } from 'zustand/shallow'; -import { Box, Button, Card, Grid, IconButton, ListDivider, ListItemDecorator, Menu, MenuItem, Radio, Stack, Textarea, Tooltip, Typography, useTheme } from '@mui/joy'; +import { Box, Button, Card, Grid, IconButton, ListDivider, ListItemDecorator, Menu, MenuItem, Stack, Textarea, Tooltip, Typography, useTheme } from '@mui/joy'; import { ColorPaletteProp, SxProps, VariantProp } from '@mui/joy/styles/types'; import ClearIcon from '@mui/icons-material/Clear'; import ContentPasteGoIcon from '@mui/icons-material/ContentPasteGo'; @@ -20,19 +20,20 @@ import { ContentReducer } from '~/modules/aifn/summarize/ContentReducer'; import { useChatLLM } from '~/modules/llms/store-llms'; import { ConfirmationModal } from '~/common/components/ConfirmationModal'; -import { SendModeId, SendModes } from '../../../../data'; import { countModelTokens } from '~/common/llm-util/token-counter'; import { extractFilePathsWithCommonRadix } from '~/common/util/dropTextUtils'; import { hideOnDesktop, hideOnMobile } from '~/common/theme'; import { htmlTableToMarkdown } from '~/common/util/htmlTableToMarkdown'; import { pdfToText } from '~/common/util/pdfToText'; import { useChatStore } from '~/common/state/store-chats'; -import { useComposerStore } from '~/common/state/store-composer'; import { useSettingsStore } from '~/common/state/store-settings'; import { useSpeechRecognition } from '~/common/components/useSpeechRecognition'; +import { SendModeId } from '../../Chat'; +import { SendModeMenu } from './SendModeMenu'; import { TokenBadge } from './TokenBadge'; import { TokenProgressbar } from './TokenProgressbar'; +import { useComposerStore } from './composer.store'; /// Text template helpers @@ -97,29 +98,6 @@ const MicButton = (props: { variant: VariantProp, color: ColorPaletteProp, onCli ; -const SendModeMenu = (props: { anchorEl: HTMLAnchorElement, sendMode: SendModeId, onSetSendMode: (sendMode: SendModeId) => void, onClose: () => void, }) => - - - Conversation Mode - - - - {Object.entries(SendModes).map(([key, data]) => - props.onSetSendMode(key as SendModeId)}> - - - - {data.label} - {data.description} - - - )} - - ; - - const SentMessagesMenu = (props: { anchorEl: HTMLAnchorElement, onClose: () => void, messages: { date: number; text: string; count: number }[], @@ -163,11 +141,12 @@ const SentMessagesMenu = (props: { export function Composer(props: { conversationId: string | null; messageId: string | null; isDeveloperMode: boolean; - onSendMessage: (conversationId: string, text: string) => void; + onSendMessage: (sendModeId: SendModeId, conversationId: string, text: string) => void; sx?: SxProps; }) { // state const [composeText, setComposeText] = React.useState(''); + const [sendModeId, setSendModeId] = React.useState('immediate'); const [isDragging, setIsDragging] = React.useState(false); const [reducerText, setReducerText] = React.useState(''); const [reducerTextTokens, setReducerTextTokens] = React.useState(0); @@ -179,7 +158,7 @@ export function Composer(props: { // external state const theme = useTheme(); const enterToSend = useSettingsStore(state => state.enterToSend); - const { sendModeId, setSendModeId, sentMessages, appendSentMessage, clearSentMessages } = useComposerStore(); + const { sentMessages, appendSentMessage, clearSentMessages } = useComposerStore(); const { assistantTyping, tokenCount: conversationTokenCount, stopTyping } = useChatStore(state => { const conversation = state.conversations.find(conversation => conversation.id === props.conversationId); return { @@ -204,7 +183,7 @@ export function Composer(props: { const text = (composeText || '').trim(); if (text.length && props.conversationId) { setComposeText(''); - props.onSendMessage(props.conversationId, text); + props.onSendMessage(sendModeId, props.conversationId, text); appendSentMessage(text); } }; diff --git a/src/apps/chat/components/composer/SendModeMenu.tsx b/src/apps/chat/components/composer/SendModeMenu.tsx new file mode 100644 index 000000000..405cb17c7 --- /dev/null +++ b/src/apps/chat/components/composer/SendModeMenu.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; + +import { Box, ListDivider, Menu, MenuItem, Radio, Typography } from '@mui/joy'; + +import { SendModeId } from '../../Chat'; + + +/// SendMode(s) definition + +type SendModeData = { + label: string; + description: string | React.JSX.Element; +} + +const SendModeItems: { [key in SendModeId]: SendModeData } = { + 'immediate': { + label: 'Chat', + description: 'AI-powered direct responses', + }, + 'react': { + label: 'Reason+Act', + description: 'Answer your questions with ReAct and search', + }, +}; + + +export const SendModeMenu = (props: { anchorEl: HTMLAnchorElement, sendMode: SendModeId, onSetSendMode: (sendMode: SendModeId) => void, onClose: () => void, }) => + + + Conversation Mode + + + + {Object.entries(SendModeItems).map(([key, data]) => + props.onSetSendMode(key as SendModeId)}> + + + + {data.label} + {data.description} + + + )} + + ; \ No newline at end of file diff --git a/src/common/state/store-composer.ts b/src/apps/chat/components/composer/composer.store.ts similarity index 84% rename from src/common/state/store-composer.ts rename to src/apps/chat/components/composer/composer.store.ts index 0580dc69f..eb3eb4192 100644 --- a/src/common/state/store-composer.ts +++ b/src/apps/chat/components/composer/composer.store.ts @@ -1,15 +1,11 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; -import { defaultSendModeId, SendModeId } from '../../data'; /// Composer Store interface ComposerStore { - sendModeId: SendModeId; - setSendModeId: (sendMode: SendModeId) => void; - sentMessages: { date: number, text: string, @@ -23,9 +19,6 @@ interface ComposerStore { export const useComposerStore = create()( persist((set, get) => ({ - sendModeId: defaultSendModeId, - setSendModeId: (sendMode: SendModeId) => set({ sendModeId: sendMode }), - sentMessages: [], appendSentMessage: (text: string) => { const date = Date.now(); @@ -61,4 +54,4 @@ export const useComposerStore = create()( return state as ComposerStore; }, }), -); +); \ No newline at end of file diff --git a/src/apps/chat/components/PurposeSelector.tsx b/src/apps/chat/components/purpose-selector/PurposeSelector.tsx similarity index 98% rename from src/apps/chat/components/PurposeSelector.tsx rename to src/apps/chat/components/purpose-selector/PurposeSelector.tsx index 2c10c9728..5704c371e 100644 --- a/src/apps/chat/components/PurposeSelector.tsx +++ b/src/apps/chat/components/purpose-selector/PurposeSelector.tsx @@ -5,11 +5,12 @@ import { Box, Button, Checkbox, Grid, IconButton, Input, Stack, Textarea, Typogr import ClearIcon from '@mui/icons-material/Clear'; import SearchIcon from '@mui/icons-material/Search'; -import { SystemPurposeId, SystemPurposes } from '../../../data'; import { useChatStore } from '~/common/state/store-chats'; -import { usePurposeStore } from '~/common/state/store-purposes'; import { useSettingsStore } from '~/common/state/store-settings'; +import { SystemPurposeId, SystemPurposes } from '../../../../data'; +import { usePurposeStore } from './purposes.store'; + // Constants for tile sizes / grid width - breakpoints need to be computed here to work around // the "flex box cannot shrink over wrapped content" issue diff --git a/src/common/state/store-purposes.ts b/src/apps/chat/components/purpose-selector/purposes.store.ts similarity index 100% rename from src/common/state/store-purposes.ts rename to src/apps/chat/components/purpose-selector/purposes.store.ts diff --git a/src/data.ts b/src/data.ts index e284c4464..d0da66825 100644 --- a/src/data.ts +++ b/src/data.ts @@ -63,23 +63,3 @@ export const SystemPurposes: { [key in SystemPurposeId]: SystemPurposeData } = { symbol: '✨', }, }; - - -export type SendModeId = 'immediate' | 'react'; -export const defaultSendModeId: SendModeId = 'immediate'; - -type SendModeData = { - label: string; - description: string | JSX.Element; -} - -export const SendModes: { [key in SendModeId]: SendModeData } = { - 'immediate': { - label: 'Chat', - description: 'AI-powered direct responses', - }, - 'react': { - label: 'Reason+Act', - description: 'Answer your questions with ReAct and search', - }, -}; \ No newline at end of file