Skip to content

Commit

Permalink
Use react-resizable-panels instead of the flexbox
Browse files Browse the repository at this point in the history
Also Fix #255 due to the large layout restructuring.
  • Loading branch information
enricoros committed Dec 27, 2023
1 parent cdc2de5 commit 8bf90e3
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 51 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"react-dom": "^18.2.0",
"react-katex": "^3.0.1",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^1.0.5",
"react-timeago": "^7.2.0",
"remark-gfm": "^4.0.0",
"superjson": "^2.2.1",
Expand Down
82 changes: 49 additions & 33 deletions src/apps/chat/AppChat.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react';

import { Box } from '@mui/joy';
import ForkRightIcon from '@mui/icons-material/ForkRight';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';

import { Box, useTheme } from '@mui/joy';

import { CmdRunBrowse } from '~/modules/browse/browse.client';
import { CmdRunReact } from '~/modules/aifn/react/react';
Expand Down Expand Up @@ -63,6 +64,8 @@ export function AppChat() {
const composerTextAreaRef = React.useRef<HTMLTextAreaElement>(null);

// external state
const theme = useTheme();

const { openLlmOptions } = useOptimaLayout();

const { chatLLM } = useChatLLM();
Expand All @@ -74,6 +77,7 @@ export function AppChat() {
openConversationInFocusedPane,
openConversationInSplitPane,
setFocusedPaneIndex,
removePaneIndex,
} = usePanesManager();

const {
Expand All @@ -97,10 +101,6 @@ export function AppChat() {

const chatPaneIDs = chatPanes.length > 0 ? chatPanes.map(pane => pane.conversationId) : [null];

const setActivePaneIndex = React.useCallback((idx: number) => {
setFocusedPaneIndex(idx);
}, [setFocusedPaneIndex]);

const setFocusedConversationId = React.useCallback((conversationId: DConversationId | null) => {
conversationId && openConversationInFocusedPane(conversationId);
}, [openConversationInFocusedPane]);
Expand Down Expand Up @@ -382,18 +382,28 @@ export function AppChat() {

return <>

<Box sx={{
flexGrow: 1,
display: 'flex', flexDirection: { xs: 'column', md: 'row' },
overflow: 'clip',
}}>

{chatPaneIDs.map((_conversationId, idx) => (
<Box key={'chat-pane-' + idx} onClick={() => setActivePaneIndex(idx)} sx={{
flexGrow: 1, flexBasis: 1,
display: 'flex', flexDirection: 'column',
overflow: 'clip',
}}>
<PanelGroup direction='horizontal'>

{chatPaneIDs.map((_conversationId, idx, panels) => <React.Fragment key={'chat-pane-' + _conversationId + '-' + panels.length}>

<Panel
id={'chat-pane-' + _conversationId}
order={idx}
collapsible
defaultSize={panels.length > 0 ? Math.round(100 / panels.length) : undefined}
minSize={20}
onClick={() => setFocusedPaneIndex(idx)}
onCollapse={() => setTimeout(() => removePaneIndex(idx), 50)}
style={{
// allows the content to be scrolled (all browsers)
overflowY: 'auto',
// border only for active pane (if two or more panes)
...(chatPaneIDs.length < 2 ? {}
: (_conversationId === focusedConversationId)
? { border: `2px solid ${theme.palette.primary.solidBg}` }
: { border: `2px solid ${theme.palette.background.level1}` }),
}}
>

<ChatMessageList
conversationId={_conversationId}
Expand All @@ -407,34 +417,40 @@ export function AppChat() {
onTextImagine={handleTextImagine}
onTextSpeak={handleTextSpeak}
sx={{
flexGrow: 1,
backgroundColor: themeBgApp,
overflowY: 'auto',
minHeight: 96,
// outline the current focused pane
...(chatPaneIDs.length < 2 ? {}
: (_conversationId === focusedConversationId)
? {
border: '2px solid',
borderColor: 'primary.solidBg',
} : {
padding: '2px',
}),
minHeight: '100%', // ensures filling of the blank space on newer chats
}}
/>

<Ephemerals
conversationId={_conversationId}
sx={{
// TODO: Fixme post panels?
// flexGrow: 0.1,
flexShrink: 0.5,
overflowY: 'auto',
minHeight: 64,
}} />

</Box>
))}
</Box>
</Panel>

{/* Panel Separators & Resizers */}
{idx < panels.length - 1 && (
<PanelResizeHandle>
<Box sx={{
backgroundColor: themeBgApp,
height: '100%',
width: '4px',
'&:hover': {
backgroundColor: 'primary.softActiveBg',
},
}} />
</PanelResizeHandle>
)}

</React.Fragment>)}

</PanelGroup>

<Composer
chatLLM={chatLLM}
Expand Down
31 changes: 15 additions & 16 deletions src/apps/chat/components/usePanesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ interface AppChatPanesStore {
openConversationInSplitPane: (conversationId: DConversationId) => void;
navigateHistoryInFocusedPane: (direction: 'back' | 'forward') => boolean;
setFocusedPaneIndex: (paneIndex: number) => void;
splitChatPane: (numberOfPanes: number) => void;
unsplitChatPane: (paneIndexToKeep: number) => void;
removePaneIndex: (paneIndex: number) => void;
onConversationsChanged: (conversationIds: DConversationId[]) => void;

}
Expand Down Expand Up @@ -169,22 +168,20 @@ const useAppChatPanesStore = create<AppChatPanesStore>()(persist(
};
}),

splitChatPane: (numberOfPanes: number) => {
const { chatPanes, chatPaneFocusIndex } = _get();
const focusedPane = (chatPaneFocusIndex !== null ? chatPanes[chatPaneFocusIndex] : null) ?? createPane();

_set({
chatPanes: Array.from({ length: numberOfPanes }, () => ({ ...focusedPane })),
chatPaneFocusIndex: 0,
});
},
removePaneIndex: (paneIndex: number) =>
_set(state => {
const { chatPanes, chatPaneFocusIndex } = state;
if (paneIndex < 0 || paneIndex >= chatPanes.length)
return state;

unsplitChatPane: (paneIndexToKeep: number) =>
_set(state => ({
chatPanes: [state.chatPanes[paneIndexToKeep] || createPane()],
chatPaneFocusIndex: 0,
})),
const newPanes = chatPanes.toSpliced(paneIndex, 1);

// when a pane is removed, focus the pane 0, or null if no panes remain
return {
chatPanes: newPanes,
chatPaneFocusIndex: newPanes.length ? 0 : null,
};
}),

/**
* This function is vital, as is invoked when the conversationId[] changes in the global chats store.
Expand Down Expand Up @@ -258,6 +255,7 @@ export function usePanesManager() {
onConversationsChanged,
openConversationInFocusedPane,
openConversationInSplitPane,
removePaneIndex,
setFocusedPaneIndex,
} = state;
const focusedConversationId = chatPaneFocusIndex !== null ? chatPanes[chatPaneFocusIndex]?.conversationId ?? null : null;
Expand All @@ -268,6 +266,7 @@ export function usePanesManager() {
onConversationsChanged,
openConversationInFocusedPane,
openConversationInSplitPane,
removePaneIndex,
setFocusedPaneIndex,
};
}, shallow);
Expand Down
47 changes: 45 additions & 2 deletions src/common/layout/optima/OptimaLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,39 @@ import { SettingsModal } from '../../../apps/settings-modal/SettingsModal';
import { ShortcutsModal } from '../../../apps/settings-modal/ShortcutsModal';

import { isPwa } from '~/common/util/pwaUtils';
import { useIsMobile } from '~/common/components/useMatchMedia';
import { useUIPreferencesStore } from '~/common/state/store-ui';

import { AppBar } from './AppBar';
import { NextRouterProgress } from './NextLoadProgress';
import { useOptimaLayout } from './useOptimaLayout';


/*function ResponsiveNavigation() {
return <>
<Drawer
open={false}
variant='solid'
anchor='left'
onClose={() => {
}}
sx={{
'& .MuiDrawer-paper': {
width: 256,
boxSizing: 'border-box',
},
}}
>
<Box sx={{ width: 256, height: '100%' }}>
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', height: '100%' }}>
<Box sx={{ flexGrow: 1 }} />
</Box>
</Box>
</Drawer>
</>;
}*/


/**
* Core layout of big-AGI, used by all the Primary applications therein.
*
Expand All @@ -28,22 +54,34 @@ import { useOptimaLayout } from './useOptimaLayout';
export function OptimaLayout(props: { suspendAutoModelsSetup?: boolean, children: React.ReactNode, }) {

// external state
const isMobile = useIsMobile();

let centerMode = useUIPreferencesStore(state => (isPwa() || isMobile) ? 'full' : state.centerMode);

const {
closePreferences, closeShortcuts,
openShortcuts,
showPreferencesTab, showShortcuts,
} = useOptimaLayout();

const centerMode = useUIPreferencesStore(state => isPwa() ? 'full' : state.centerMode);
return <>

{/*<Box sx={{*/}
{/* display: 'flex', flexDirection: 'row',*/}
{/* maxWidth: '100%', flexWrap: 'nowrap',*/}
{/* // overflowX: 'hidden',*/}
{/* background: 'lime',*/}
{/*}}>*/}

return <>
{/*<Box sx={{ background: 'rgba(100 0 0 / 0.5)' }}>a</Box>*/}

{/*<ResponsiveNavigation />*/}

<Container
disableGutters
maxWidth={centerMode === 'full' ? false : centerMode === 'narrow' ? 'md' : 'xl'}
sx={{
// minWidth: 0,
boxShadow: {
xs: 'none',
md: centerMode === 'narrow' ? 'md' : 'none',
Expand All @@ -60,12 +98,17 @@ export function OptimaLayout(props: { suspendAutoModelsSetup?: boolean, children
zIndex: 20,
}} />

{/* Children must make the assumption they're in a flex-col layout */}
{props.children}

</Box>

</Container>

{/*<Box sx={{ background: 'rgba(100 0 0 / 0.5)' }}>bb</Box>*/}

{/*</Box>*/}


{/* Overlay Settings */}
<SettingsModal open={!!showPreferencesTab} tabIndex={showPreferencesTab} onClose={closePreferences} onOpenShortcuts={openShortcuts} />
Expand Down

0 comments on commit 8bf90e3

Please sign in to comment.