mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
New UI: transfer App Drawer lists into the Plugged
This commit is contained in:
@@ -23,7 +23,7 @@ import { useOptimaLayout, usePluggableOptimaLayout } from '~/common/layout/optim
|
||||
import { useUXLabsStore } from '~/common/state/store-ux-labs';
|
||||
|
||||
import type { ComposerOutputMultiPart } from './components/composer/composer.types';
|
||||
import { ChatDrawerItemsMemo } from './components/applayout/ChatDrawerItems';
|
||||
import { ChatDrawerContentMemo } from './components/applayout/ChatDrawerItems';
|
||||
import { ChatDropdowns } from './components/applayout/ChatDropdowns';
|
||||
import { ChatMenuItems } from './components/applayout/ChatMenuItems';
|
||||
import { ChatMessageList } from './components/ChatMessageList';
|
||||
@@ -384,9 +384,9 @@ export function AppChat() {
|
||||
[focusedConversationId, isSplitPane, toggleSplitPane],
|
||||
);
|
||||
|
||||
const drawerItems = React.useMemo(
|
||||
const drawerContent = React.useMemo(
|
||||
() => (
|
||||
<ChatDrawerItemsMemo
|
||||
<ChatDrawerContentMemo
|
||||
activeConversationId={focusedConversationId}
|
||||
disableNewButton={isFocusedChatEmpty}
|
||||
onConversationActivate={setFocusedConversationId}
|
||||
@@ -422,7 +422,7 @@ export function AppChat() {
|
||||
? useFolderStore.getState().folders.find(folder => folder.id === selectedFolderId)?.conversationIds.length || 0
|
||||
: conversations.length;
|
||||
|
||||
usePluggableOptimaLayout(drawerItems, centerItems, menuItems, 'AppChat');
|
||||
usePluggableOptimaLayout(drawerContent, centerItems, menuItems, 'AppChat');
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { Box, ListDivider, ListItemDecorator, MenuItem, Typography } from '@mui/joy';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
|
||||
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||
|
||||
import { DConversationId, useChatStore, useConversationsByFolder } from '~/common/state/store-chats';
|
||||
import { DConversationId, useConversationsByFolder } from '~/common/state/store-chats';
|
||||
import { OpenAIIcon } from '~/common/components/icons/OpenAIIcon';
|
||||
import { PageDrawerList, PageDrawerTallItemSx } from '~/common/layout/optima/components/PageDrawerList';
|
||||
import { useFolderStore } from '~/common/state/store-folders';
|
||||
import { useOptimaDrawers } from '~/common/layout/optima/useOptimaDrawers';
|
||||
import { useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
import { useUXLabsStore } from '~/common/state/store-ux-labs';
|
||||
import { useFolderStore } from '~/common/state/store-folders';
|
||||
|
||||
import { ChatNavigationItemMemo } from './ChatNavigationItem';
|
||||
import { ChatFolderList } from './folder/ChatFolderList';
|
||||
|
||||
// type ListGrouping = 'off' | 'persona';
|
||||
|
||||
export const ChatDrawerItemsMemo = React.memo(ChatDrawerItems);
|
||||
export const ChatDrawerContentMemo = React.memo(ChatDrawerItems);
|
||||
|
||||
function ChatDrawerItems(props: {
|
||||
activeConversationId: DConversationId | null,
|
||||
@@ -102,67 +102,72 @@ function ChatDrawerItems(props: {
|
||||
{/*</ListItem>*/}
|
||||
|
||||
<ChatFolderList
|
||||
onFolderSelect={handleFolderSelect}
|
||||
folders={useFolderStore((state) => state.folders)}
|
||||
selectedFolderId={selectedFolderId}
|
||||
conversationsByFolder={conversations}
|
||||
/>
|
||||
<ListDivider sx={{ mb: 0 }} />
|
||||
onFolderSelect={handleFolderSelect}
|
||||
folders={useFolderStore((state) => state.folders)}
|
||||
selectedFolderId={selectedFolderId}
|
||||
conversationsByFolder={conversations}
|
||||
/>
|
||||
|
||||
<MenuItem disabled={props.disableNewButton} onClick={handleButtonNew}>
|
||||
<ListItemDecorator><AddIcon /></ListItemDecorator>
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'space-between', gap: 1 }}>
|
||||
New
|
||||
{/*<KeyStroke combo='Ctrl + Alt + N' />*/}
|
||||
<ListDivider />
|
||||
|
||||
<PageDrawerList noTopPadding noBottomPadding tallRows>
|
||||
|
||||
<MenuItem disabled={props.disableNewButton} onClick={handleButtonNew} sx={PageDrawerTallItemSx}>
|
||||
<ListItemDecorator><AddIcon /></ListItemDecorator>
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'space-between', gap: 1 }}>
|
||||
New
|
||||
{/*<KeyStroke combo='Ctrl + Alt + N' />*/}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
|
||||
<ListDivider sx={{ mt: 0 }} />
|
||||
|
||||
<Box sx={{ flex: 1, overflowY: 'auto' }}>
|
||||
{/*<ListItem sticky sx={{ justifyContent: 'space-between', boxShadow: 'sm' }}>*/}
|
||||
{/* <Typography level='body-sm'>*/}
|
||||
{/* Conversations*/}
|
||||
{/* </Typography>*/}
|
||||
{/* <ToggleButtonGroup variant='soft' size='sm' value={grouping} onChange={(_event, newValue) => newValue && setGrouping(newValue)}>*/}
|
||||
{/* <IconButton value='off'>*/}
|
||||
{/* <AccessTimeIcon />*/}
|
||||
{/* </IconButton>*/}
|
||||
{/* <IconButton value='persona'>*/}
|
||||
{/* <PersonIcon />*/}
|
||||
{/* </IconButton>*/}
|
||||
{/* </ToggleButtonGroup>*/}
|
||||
{/*</ListItem>*/}
|
||||
|
||||
{conversations.map(conversation =>
|
||||
<ChatNavigationItemMemo
|
||||
key={'nav-' + conversation.id}
|
||||
conversation={conversation}
|
||||
isActive={conversation.id === props.activeConversationId}
|
||||
isLonely={singleChat}
|
||||
maxChatMessages={(labsEnhancedUI || softMaxReached) ? maxChatMessages : 0}
|
||||
showSymbols={showSymbols}
|
||||
onConversationActivate={handleConversationActivate}
|
||||
onConversationDelete={handleConversationDelete}
|
||||
/>)}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
|
||||
<ListDivider sx={{ mb: 0 }} />
|
||||
<ListDivider sx={{ mt: 0 }} />
|
||||
|
||||
<Box sx={{ flex: 1, overflowY: 'auto' }}>
|
||||
{/*<ListItem sticky sx={{ justifyContent: 'space-between', boxShadow: 'sm' }}>*/}
|
||||
{/* <Typography level='body-sm'>*/}
|
||||
{/* Conversations*/}
|
||||
{/* </Typography>*/}
|
||||
{/* <ToggleButtonGroup variant='soft' size='sm' value={grouping} onChange={(_event, newValue) => newValue && setGrouping(newValue)}>*/}
|
||||
{/* <IconButton value='off'>*/}
|
||||
{/* <AccessTimeIcon />*/}
|
||||
{/* </IconButton>*/}
|
||||
{/* <IconButton value='persona'>*/}
|
||||
{/* <PersonIcon />*/}
|
||||
{/* </IconButton>*/}
|
||||
{/* </ToggleButtonGroup>*/}
|
||||
{/*</ListItem>*/}
|
||||
<MenuItem onClick={props.onConversationImportDialog}>
|
||||
<ListItemDecorator>
|
||||
<FileUploadIcon />
|
||||
</ListItemDecorator>
|
||||
Import chats
|
||||
<OpenAIIcon sx={{ fontSize: 'xl', ml: 'auto' }} />
|
||||
</MenuItem>
|
||||
|
||||
{conversations.map(conversation =>
|
||||
<ChatNavigationItemMemo
|
||||
key={'nav-' + conversation.id}
|
||||
conversation={conversation}
|
||||
isActive={conversation.id === props.activeConversationId}
|
||||
isLonely={singleChat}
|
||||
maxChatMessages={(labsEnhancedUI || softMaxReached) ? maxChatMessages : 0}
|
||||
showSymbols={showSymbols}
|
||||
onConversationActivate={handleConversationActivate}
|
||||
onConversationDelete={handleConversationDelete}
|
||||
/>)}
|
||||
</Box>
|
||||
<MenuItem disabled={!hasChats} onClick={() => props.onConversationsDeleteAll(selectedFolderId)}>
|
||||
<ListItemDecorator><DeleteOutlineIcon /></ListItemDecorator>
|
||||
<Typography>
|
||||
Delete {totalConversations >= 2 ? `all ${totalConversations} chats` : 'chat'}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
|
||||
<ListDivider sx={{ mt: 0 }} />
|
||||
|
||||
<MenuItem onClick={props.onConversationImportDialog}>
|
||||
<ListItemDecorator>
|
||||
<FileUploadIcon />
|
||||
</ListItemDecorator>
|
||||
Import chats
|
||||
<OpenAIIcon sx={{ fontSize: 'xl', ml: 'auto' }} />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem disabled={!hasChats} onClick={() => props.onConversationsDeleteAll(selectedFolderId)}>
|
||||
<ListItemDecorator><DeleteOutlineIcon /></ListItemDecorator>
|
||||
<Typography>
|
||||
Delete {totalConversations >= 2 ? `all ${totalConversations} chats` : 'chat'}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
</PageDrawerList>
|
||||
|
||||
</>;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import { conversationTitle } from '~/common/state/store-chats';
|
||||
import { themeBgAppDarker } from '~/common/app.theme';
|
||||
import { usePluggableOptimaLayout } from '~/common/layout/optima/useOptimaLayout';
|
||||
|
||||
import { AppChatLinkDrawerItems } from './AppChatLinkDrawerItems';
|
||||
import { AppChatLinkDrawerContent } from './AppChatLinkDrawerContent';
|
||||
import { AppChatLinkMenuItems } from './AppChatLinkMenuItems';
|
||||
import { ViewChatLink } from './ViewChatLink';
|
||||
|
||||
@@ -84,9 +84,9 @@ export function AppChatLink(props: { linkId: string }) {
|
||||
|
||||
// pluggable UI
|
||||
|
||||
const drawerItems = React.useMemo(() => <AppChatLinkDrawerItems />, []);
|
||||
const drawerContent = React.useMemo(() => <AppChatLinkDrawerContent />, []);
|
||||
const menuItems = React.useMemo(() => <AppChatLinkMenuItems />, []);
|
||||
usePluggableOptimaLayout(hasLinkItems ? drawerItems : null, null, menuItems, 'AppChatLink');
|
||||
usePluggableOptimaLayout(hasLinkItems ? drawerContent : null, null, menuItems, 'AppChatLink');
|
||||
|
||||
|
||||
const pageTitle = (data?.conversation && conversationTitle(data.conversation)) || 'Chat Link';
|
||||
|
||||
+4
-3
@@ -10,13 +10,14 @@ import { Brand } from '~/common/app.config';
|
||||
import { Link } from '~/common/components/Link';
|
||||
import { getChatLinkRelativePath, ROUTE_INDEX } from '~/common/app.routes';
|
||||
import { useOptimaDrawers } from '~/common/layout/optima/useOptimaDrawers';
|
||||
import { PageDrawerList } from '~/common/layout/optima/components/PageDrawerList';
|
||||
|
||||
|
||||
/**
|
||||
* Drawer Items are all the links already shared, for quick access.
|
||||
* This is stores in the Trade Store (local storage).
|
||||
*/
|
||||
export function AppChatLinkDrawerItems() {
|
||||
export function AppChatLinkDrawerContent() {
|
||||
|
||||
// external state
|
||||
const { closeDrawerOnMobile } = useOptimaDrawers();
|
||||
@@ -25,7 +26,7 @@ export function AppChatLinkDrawerItems() {
|
||||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
||||
const notEmpty = chatLinkItems.length > 0;
|
||||
|
||||
return <>
|
||||
return <PageDrawerList>
|
||||
|
||||
<MenuItem
|
||||
onClick={closeDrawerOnMobile}
|
||||
@@ -64,6 +65,6 @@ export function AppChatLinkDrawerItems() {
|
||||
|
||||
))}
|
||||
</Box>}
|
||||
</>;
|
||||
</PageDrawerList>;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { IconButton, MenuList, Sheet, Typography } from '@mui/joy';
|
||||
import { IconButton, Sheet, Typography } from '@mui/joy';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
import type { NavItemApp } from '~/common/app.nav';
|
||||
@@ -39,25 +39,8 @@ export function PageDrawer(props: {
|
||||
</IconButton>
|
||||
</Sheet>
|
||||
|
||||
{/* Pluggable content (Pane) */}
|
||||
<MenuList
|
||||
variant='plain'
|
||||
// variant={props.variant} color={props.color}
|
||||
// onKeyDown={handleListKeyDown}
|
||||
sx={{
|
||||
'--Icon-fontSize': 'var(--joy-fontSize-xl2)',
|
||||
'--ListItem-minHeight': /*props.dense*/ false ? '2.5rem' : '3rem',
|
||||
'--ListItemDecorator-size': '2.75rem', // icon width
|
||||
backgroundColor: 'background.popup',
|
||||
boxShadow: 'md',
|
||||
border: 'none',
|
||||
// ...(props.maxHeightGapPx !== undefined ? { maxHeight: `calc(100dvh - ${props.maxHeightGapPx}px)`, overflowY: 'auto' } : {}),
|
||||
// ...(props.noTopPadding ? { pt: 0 } : {}),
|
||||
// ...(props.noBottomPadding ? { pb: 0 } : {}),
|
||||
// ...(props.sx || {}),
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</MenuList>
|
||||
{/* Pluggable Drawer Content */}
|
||||
{props.children}
|
||||
|
||||
</>;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { ColorPaletteProp, MenuList, VariantProp } from '@mui/joy';
|
||||
import { SxProps } from '@mui/joy/styles/types';
|
||||
|
||||
|
||||
export const PageDrawerTallItemSx: SxProps = {
|
||||
'--ListItem-minHeight': '3rem',
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used by pluggable layouts to have a standardized list appearance
|
||||
*/
|
||||
export function PageDrawerList(props: {
|
||||
variant?: VariantProp,
|
||||
color?: ColorPaletteProp,
|
||||
largeIcons?: boolean,
|
||||
tallRows?: boolean,
|
||||
noTopPadding?: boolean,
|
||||
noBottomPadding?: boolean,
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
|
||||
return (
|
||||
<MenuList
|
||||
variant={props.variant}
|
||||
color={props.color}
|
||||
sx={{
|
||||
// size of the list items
|
||||
...props.largeIcons && {
|
||||
'--Icon-fontSize': 'var(--joy-fontSize-xl2)',
|
||||
'--ListItemDecorator-size': '2.75rem', // icon width
|
||||
},
|
||||
...props.tallRows && PageDrawerTallItemSx,
|
||||
|
||||
// style
|
||||
backgroundColor: 'background.popup',
|
||||
border: 'none',
|
||||
boxShadow: 'md',
|
||||
...(!!props.noTopPadding && { pt: 0 }),
|
||||
...(!!props.noBottomPadding && { pb: 0 }),
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</MenuList>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user