From 0a07f2a4471365d45ffa2e57875051060c428bbb Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Sat, 19 Oct 2024 03:12:08 -0700 Subject: [PATCH] Ephemerals: improve layout --- src/apps/chat/components/ChatMessageList.tsx | 1 - src/apps/chat/components/Ephemerals.tsx | 170 +++++++++--------- .../chat-overlay/ConversationHandler.ts | 15 +- .../store-perchat_ephemerals_slice.ts | 65 ++++--- 4 files changed, 124 insertions(+), 127 deletions(-) diff --git a/src/apps/chat/components/ChatMessageList.tsx b/src/apps/chat/components/ChatMessageList.tsx index 9cf24d27f..534ee7e49 100644 --- a/src/apps/chat/components/ChatMessageList.tsx +++ b/src/apps/chat/components/ChatMessageList.tsx @@ -374,7 +374,6 @@ export function ChatMessageList(props: { sx={{ mt: 'auto', overflowY: 'auto', - minHeight: 64, }} /> )} diff --git a/src/apps/chat/components/Ephemerals.tsx b/src/apps/chat/components/Ephemerals.tsx index 3bfb26f6a..aab8460f4 100644 --- a/src/apps/chat/components/Ephemerals.tsx +++ b/src/apps/chat/components/Ephemerals.tsx @@ -3,7 +3,8 @@ import * as React from 'react'; import type { SxProps } from '@mui/joy/styles/types'; import { Box, Grid, IconButton, Sheet, styled, Typography } from '@mui/joy'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; -import PushPinIcon from '@mui/icons-material/PushPin'; +import MaximizeIcon from '@mui/icons-material/Maximize'; +import MinimizeIcon from '@mui/icons-material/Minimize'; import VerticalSplitIcon from '@mui/icons-material/VerticalSplit'; import VerticalSplitOutlinedIcon from '@mui/icons-material/VerticalSplitOutlined'; @@ -111,107 +112,112 @@ function EphemeralItem(props: { const { ephemeral, conversationHandler } = props; + // Event handlers const handleDelete = React.useCallback(() => { conversationHandler.overlayActions.ephemeralsDelete(ephemeral.id); }, [conversationHandler, ephemeral.id]); - const handleTogglePinned = React.useCallback(() => { - conversationHandler.overlayActions.ephemeralsTogglePinned(ephemeral.id); + const handleToggleMinimized = React.useCallback(() => { + conversationHandler.overlayActions.ephemeralsToggleMinimized(ephemeral.id); }, [conversationHandler, ephemeral.id]); const handleToggleShowState = React.useCallback(() => { - conversationHandler.overlayActions.ephemeralsToggleShowState(ephemeral.id); + conversationHandler.overlayActions.ephemeralsToggleShowStatePane(ephemeral.id); }, [conversationHandler, ephemeral.id]); - const showStatePane = ephemeral.showState && !!ephemeral.state; - return - - {/* Title */} - {ephemeral.title && ( - - {ephemeral.title} Internal Monologue - - )} - - {/* Vertical | split */} - - - {/* Left pane (log) */} - - {/* New renderer, with */} - - - - - - {/* Right pane (state) */} - {showStatePane && ( - - - - )} - - - - - {/* Buttons */} - - {/* Pin */} - *': { transition: 'transform 0.2s' }, - }} - > - - + {/* Top Line - Title and Buttons */} + - {/* Show State */} - - {ephemeral.showState ? : } - + + {ephemeral.title} Internal Monologue + - {/* Close */} - - - + {/* Show State */} + {!ephemeral.minimized && ( + + {ephemeral.showStatePane ? : } + + )} + + {/* Minimize/Expand Button */} + + {ephemeral.minimized ? : } + + + {/* Close */} + + + + + + + {/* Content */} + {!ephemeral.minimized && + + {/* Content Grid */} + + + {/* Left pane (log) */} + + {/* New renderer, with */} + + + + + + {/* Right pane (state) */} + {showStatePane && ( + + + + )} + + + } - - ; + ); } diff --git a/src/common/chat-overlay/ConversationHandler.ts b/src/common/chat-overlay/ConversationHandler.ts index 2a39275fc..f4f18e765 100644 --- a/src/common/chat-overlay/ConversationHandler.ts +++ b/src/common/chat-overlay/ConversationHandler.ts @@ -14,14 +14,10 @@ import { getChatLLMId } from '~/common/stores/llms/store-llms'; import { getChatAutoAI } from '../../apps/chat/store-app-chat'; -import { createDEphemeral } from './store-perchat_ephemerals_slice'; +import { createDEphemeral, EPHEMERALS_DEFAULT_TIMEOUT } from './store-perchat_ephemerals_slice'; import { createPerChatVanillaStore } from './store-perchat_vanilla'; -// configuration -const EPHEMERAL_DELETION_DELAY = 5 * 1000; - - /** * ConversationHandler is a class to overlay state onto a conversation. * It is a singleton per conversationId. @@ -250,16 +246,15 @@ export class ConversationHandler { // Ephemerals createEphemeralHandler(title: string, initialText: string) { - const { ephemeralsAppend, ephemeralsUpdate, ephemeralsDelete, ephemeralsIsPinned } = this.overlayActions; + const { ephemeralsAppend, ephemeralsUpdate, ephemeralsDelete, getEphemeral } = this.overlayActions; // create and append const ephemeral = createDEphemeral(title, initialText); const eId = ephemeral.id; ephemeralsAppend(ephemeral); - // delete if not pinned - const deleteIfNotPinned = () => { - if (!ephemeralsIsPinned(eId)) + const deleteIfMinimized = () => { + if (getEphemeral(eId)?.minimized) ephemeralsDelete(eId); }; @@ -269,7 +264,7 @@ export class ConversationHandler { updateState: (state: object) => ephemeralsUpdate(eId, { state }), markAsDone: () => { ephemeralsUpdate(eId, { done: true }); - setTimeout(deleteIfNotPinned, EPHEMERAL_DELETION_DELAY); + setTimeout(deleteIfMinimized, EPHEMERALS_DEFAULT_TIMEOUT); }, }; } diff --git a/src/common/chat-overlay/store-perchat_ephemerals_slice.ts b/src/common/chat-overlay/store-perchat_ephemerals_slice.ts index 9b4c55862..2aea4f48c 100644 --- a/src/common/chat-overlay/store-perchat_ephemerals_slice.ts +++ b/src/common/chat-overlay/store-perchat_ephemerals_slice.ts @@ -3,6 +3,11 @@ import type { StateCreator } from 'zustand/vanilla'; import { agiUuid } from '~/common/util/idUtils'; +// configuration +export const EPHEMERALS_DEFAULT_TIMEOUT = 6000; +const EPHEMERALS_DEFAULT_MINIMIZED = true; + + /** * DEphemeral: For ReAct sidebars, displayed under the chat */ @@ -11,9 +16,9 @@ export interface DEphemeral { title: string; text: string; state: object; - done: boolean; - pinned: boolean; - showState: boolean; + done: boolean; // is complete, shall close after timeout + minimized: boolean; // collapsed to a single line + showStatePane: boolean; // show the state object } type DEphemeralId = string; @@ -25,16 +30,16 @@ export function createDEphemeral(title: string, initialText: string): DEphemeral text: initialText, state: {}, done: false, - pinned: lastEphemeralPinned, - showState: lastEphemeralShowState, + minimized: lastMinimized, + showStatePane: lastShowStatePane, }; } /// Ephemerals Overlay Store /// -let lastEphemeralPinned = false; -let lastEphemeralShowState = false; +let lastMinimized = EPHEMERALS_DEFAULT_MINIMIZED; +let lastShowStatePane = false; interface EphemeralsOverlayState { @@ -48,11 +53,10 @@ export interface EphemeralsOverlayStore extends EphemeralsOverlayState { ephemeralsDelete: (ephemeralId: DEphemeralId) => void; ephemeralsUpdate: (ephemeralId: DEphemeralId, update: Partial) => void; - ephemeralsIsPinned: (ephemeralId: DEphemeralId) => boolean; - ephemeralsTogglePinned: (ephemeralId: DEphemeralId) => void; + ephemeralsToggleMinimized: (ephemeralId: DEphemeralId) => void; + ephemeralsToggleShowStatePane: (ephemeralId: DEphemeralId) => void; - ephemeralsIsShowState: (ephemeralId: DEphemeralId) => boolean; - ephemeralsToggleShowState: (ephemeralId: DEphemeralId) => void; + getEphemeral: (ephemeralId: DEphemeralId) => Readonly | undefined; } @@ -75,10 +79,10 @@ export const createEphemeralsOverlayStoreSlice: StateCreator _set((state) => { - if (update.pinned !== undefined) - lastEphemeralPinned = update.pinned; - if (update.showState !== undefined) - lastEphemeralShowState = update.showState; + if (update.minimized !== undefined) + lastMinimized = update.minimized; + if (update.showStatePane !== undefined) + lastShowStatePane = update.showStatePane; return { ephemerals: state.ephemerals.map((e) => e.id === ephemeralId @@ -88,28 +92,21 @@ export const createEphemeralsOverlayStoreSlice: StateCreator - _get().ephemerals.find((e) => e.id === ephemeralId)?.pinned || false, - - ephemeralsTogglePinned: (ephemeralId) => { - const { ephemerals, ephemeralsDelete, ephemeralsUpdate } = _get(); - const ephemeral = ephemerals.find((e) => e.id === ephemeralId); - if (ephemeral) { - if (ephemeral.pinned && ephemeral.done) - ephemeralsDelete(ephemeralId); - else - ephemeralsUpdate(ephemeralId, { pinned: !ephemeral.pinned }); - } - }, - - ephemeralsIsShowState: (ephemeralId) => - _get().ephemerals.find((e) => e.id === ephemeralId)?.showState || false, - - ephemeralsToggleShowState: (ephemeralId) => { + ephemeralsToggleMinimized: (ephemeralId) => { const { ephemerals, ephemeralsUpdate } = _get(); const ephemeral = ephemerals.find((e) => e.id === ephemeralId); if (ephemeral) - ephemeralsUpdate(ephemeralId, { showState: !ephemeral.showState }); + ephemeralsUpdate(ephemeralId, { minimized: !ephemeral.minimized }); }, + ephemeralsToggleShowStatePane: (ephemeralId) => { + const { ephemerals, ephemeralsUpdate } = _get(); + const ephemeral = ephemerals.find((e) => e.id === ephemeralId); + if (ephemeral) + ephemeralsUpdate(ephemeralId, { showStatePane: !ephemeral.showStatePane }); + }, + + getEphemeral: (ephemeralId) => + _get().ephemerals.find((e) => e.id === ephemeralId), + });