Replace allotment with react-resizable-panels and simplify layout (#2029)
* merge * switch out panel lib * fmt * remove allotment dep * bump lock * The position of the react resizable panel is lost when the page is reloaded in (vibe-kanban 6ac32e23) `WorkspacesLayout.tsx` , but should be stored in `useUiPreferencesStore.ts` `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` `vibe-kanban/frontend/src/stores/useUiPreferencesStore.ts` * remove redundant type * add dep --------- Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
This commit is contained in:
@@ -50,7 +50,6 @@
|
|||||||
"@tanstack/react-query": "^5.85.5",
|
"@tanstack/react-query": "^5.85.5",
|
||||||
"@uiw/react-codemirror": "^4.25.1",
|
"@uiw/react-codemirror": "^4.25.1",
|
||||||
"@virtuoso.dev/message-list": "^1.13.3",
|
"@virtuoso.dev/message-list": "^1.13.3",
|
||||||
"allotment": "^1.20.5",
|
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"click-to-react-component": "^1.1.2",
|
"click-to-react-component": "^1.1.2",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { Group, Layout, Panel, Separator } from 'react-resizable-panels';
|
||||||
import { Allotment, LayoutPriority, type AllotmentHandle } from 'allotment';
|
|
||||||
import 'allotment/dist/style.css';
|
|
||||||
import { useWorkspaceContext } from '@/contexts/WorkspaceContext';
|
import { useWorkspaceContext } from '@/contexts/WorkspaceContext';
|
||||||
import { useActions } from '@/contexts/ActionsContext';
|
import { useActions } from '@/contexts/ActionsContext';
|
||||||
import { ExecutionProcessesProvider } from '@/contexts/ExecutionProcessesContext';
|
import { ExecutionProcessesProvider } from '@/contexts/ExecutionProcessesContext';
|
||||||
@@ -39,9 +37,9 @@ import { useTask } from '@/hooks/useTask';
|
|||||||
import { useAttemptRepo } from '@/hooks/useAttemptRepo';
|
import { useAttemptRepo } from '@/hooks/useAttemptRepo';
|
||||||
import { useBranchStatus } from '@/hooks/useBranchStatus';
|
import { useBranchStatus } from '@/hooks/useBranchStatus';
|
||||||
import {
|
import {
|
||||||
usePaneSize,
|
|
||||||
useExpandedAll,
|
|
||||||
PERSIST_KEYS,
|
PERSIST_KEYS,
|
||||||
|
useExpandedAll,
|
||||||
|
usePaneSize,
|
||||||
} from '@/stores/useUiPreferencesStore';
|
} from '@/stores/useUiPreferencesStore';
|
||||||
import {
|
import {
|
||||||
useLayoutStore,
|
useLayoutStore,
|
||||||
@@ -53,6 +51,7 @@ import { useCommandBarShortcut } from '@/hooks/useCommandBarShortcut';
|
|||||||
import { Actions } from '@/components/ui-new/actions';
|
import { Actions } from '@/components/ui-new/actions';
|
||||||
import type { RepoAction } from '@/components/ui-new/primitives/RepoCard';
|
import type { RepoAction } from '@/components/ui-new/primitives/RepoCard';
|
||||||
import type { Workspace, RepoWithTargetBranch, Merge } from 'shared/types';
|
import type { Workspace, RepoWithTargetBranch, Merge } from 'shared/types';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
// Container component for GitPanel that uses hooks requiring GitOperationsProvider
|
// Container component for GitPanel that uses hooks requiring GitOperationsProvider
|
||||||
interface GitPanelContainerProps {
|
interface GitPanelContainerProps {
|
||||||
@@ -300,9 +299,29 @@ export function WorkspacesLayout() {
|
|||||||
setMainPanelVisible,
|
setMainPanelVisible,
|
||||||
} = useLayoutStore();
|
} = useLayoutStore();
|
||||||
|
|
||||||
// Derived state: right main panel (Changes/Logs/Preview) is visible
|
const [rightMainPanelSize, setRightMainPanelSize] = usePaneSize(
|
||||||
|
PERSIST_KEYS.rightMainPanel,
|
||||||
|
50
|
||||||
|
);
|
||||||
const isRightMainPanelVisible = useIsRightMainPanelVisible();
|
const isRightMainPanelVisible = useIsRightMainPanelVisible();
|
||||||
|
|
||||||
|
const defaultLayout = (): Layout => {
|
||||||
|
let layout = { 'left-main': 50, 'right-main': 50 };
|
||||||
|
if (typeof rightMainPanelSize === 'number') {
|
||||||
|
layout = {
|
||||||
|
'left-main': 100 - rightMainPanelSize,
|
||||||
|
'right-main': rightMainPanelSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLayoutChange = (layout: Layout) => {
|
||||||
|
if (isRightMainPanelVisible) {
|
||||||
|
setRightMainPanelSize(layout['right-main']);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// === Auto-show Workspaces Guide on first visit ===
|
// === Auto-show Workspaces Guide on first visit ===
|
||||||
const WORKSPACES_GUIDE_ID = 'workspaces-guide';
|
const WORKSPACES_GUIDE_ID = 'workspaces-guide';
|
||||||
const {
|
const {
|
||||||
@@ -493,17 +512,6 @@ export function WorkspacesLayout() {
|
|||||||
);
|
);
|
||||||
}, [logMatchIndices.length]);
|
}, [logMatchIndices.length]);
|
||||||
|
|
||||||
// Ref to Allotment for programmatic control
|
|
||||||
const allotmentRef = useRef<AllotmentHandle>(null);
|
|
||||||
|
|
||||||
// Reset Allotment sizes when right main panel becomes visible
|
|
||||||
// This re-applies preferredSize percentages based on current window size
|
|
||||||
useEffect(() => {
|
|
||||||
if (isRightMainPanelVisible && allotmentRef.current) {
|
|
||||||
allotmentRef.current.reset();
|
|
||||||
}
|
|
||||||
}, [isRightMainPanelVisible]);
|
|
||||||
|
|
||||||
// Reset changes and logs mode when entering create mode
|
// Reset changes and logs mode when entering create mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isCreateMode) {
|
if (isCreateMode) {
|
||||||
@@ -535,32 +543,6 @@ export function WorkspacesLayout() {
|
|||||||
// Expanded state for file tree selection
|
// Expanded state for file tree selection
|
||||||
const { setExpanded } = useExpandedAll();
|
const { setExpanded } = useExpandedAll();
|
||||||
|
|
||||||
// Persisted pane sizes
|
|
||||||
const [sidebarWidth, setSidebarWidth] = usePaneSize(
|
|
||||||
PERSIST_KEYS.sidebarWidth,
|
|
||||||
300
|
|
||||||
);
|
|
||||||
const [gitPanelWidth, setGitPanelWidth] = usePaneSize(
|
|
||||||
PERSIST_KEYS.gitPanelWidth,
|
|
||||||
300
|
|
||||||
);
|
|
||||||
const [changesPanelWidth, setChangesPanelWidth] = usePaneSize(
|
|
||||||
PERSIST_KEYS.changesPanelWidth,
|
|
||||||
'40%'
|
|
||||||
);
|
|
||||||
const [fileTreeHeight, setFileTreeHeight] = usePaneSize(
|
|
||||||
PERSIST_KEYS.fileTreeHeight,
|
|
||||||
'70%'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle file tree resize (vertical split within git panel)
|
|
||||||
const handleFileTreeResize = useCallback(
|
|
||||||
(sizes: number[]) => {
|
|
||||||
if (sizes[0] !== undefined) setFileTreeHeight(sizes[0]);
|
|
||||||
},
|
|
||||||
[setFileTreeHeight]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Navigate to logs panel and select a specific process
|
// Navigate to logs panel and select a specific process
|
||||||
const handleViewProcessInPanel = useCallback(
|
const handleViewProcessInPanel = useCallback(
|
||||||
(processId: string) => {
|
(processId: string) => {
|
||||||
@@ -635,8 +617,8 @@ export function WorkspacesLayout() {
|
|||||||
if (isChangesMode) {
|
if (isChangesMode) {
|
||||||
// In changes mode, split git panel vertically: file tree on top, git on bottom
|
// In changes mode, split git panel vertically: file tree on top, git on bottom
|
||||||
return (
|
return (
|
||||||
<Allotment vertical onDragEnd={handleFileTreeResize} proportionalLayout>
|
<div className="flex flex-col h-full">
|
||||||
<Allotment.Pane minSize={200} preferredSize={fileTreeHeight}>
|
<div className="flex-[7] min-h-0 overflow-hidden">
|
||||||
<FileTreeContainer
|
<FileTreeContainer
|
||||||
key={selectedWorkspace?.id}
|
key={selectedWorkspace?.id}
|
||||||
workspaceId={selectedWorkspace?.id}
|
workspaceId={selectedWorkspace?.id}
|
||||||
@@ -649,16 +631,16 @@ export function WorkspacesLayout() {
|
|||||||
setExpanded(`diff:${path}`, true);
|
setExpanded(`diff:${path}`, true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
<Allotment.Pane minSize={200}>
|
<div className="flex-[3] min-h-0 overflow-hidden">
|
||||||
<GitPanelContainer
|
<GitPanelContainer
|
||||||
selectedWorkspace={selectedWorkspace}
|
selectedWorkspace={selectedWorkspace}
|
||||||
repos={repos}
|
repos={repos}
|
||||||
repoInfos={repoInfos}
|
repoInfos={repoInfos}
|
||||||
onBranchNameChange={handleBranchNameChange}
|
onBranchNameChange={handleBranchNameChange}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
</Allotment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,8 +652,8 @@ export function WorkspacesLayout() {
|
|||||||
? logsPanelContent.processId
|
? logsPanelContent.processId
|
||||||
: null;
|
: null;
|
||||||
return (
|
return (
|
||||||
<Allotment vertical onDragEnd={handleFileTreeResize} proportionalLayout>
|
<div className="flex flex-col h-full">
|
||||||
<Allotment.Pane minSize={200} preferredSize={fileTreeHeight}>
|
<div className="flex-[7] min-h-0 overflow-hidden">
|
||||||
<ProcessListContainer
|
<ProcessListContainer
|
||||||
selectedProcessId={selectedProcessId}
|
selectedProcessId={selectedProcessId}
|
||||||
onSelectProcess={handleViewProcessInPanel}
|
onSelectProcess={handleViewProcessInPanel}
|
||||||
@@ -683,38 +665,38 @@ export function WorkspacesLayout() {
|
|||||||
onPrevMatch={handleLogPrevMatch}
|
onPrevMatch={handleLogPrevMatch}
|
||||||
onNextMatch={handleLogNextMatch}
|
onNextMatch={handleLogNextMatch}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
<Allotment.Pane minSize={200}>
|
<div className="flex-[3] min-h-0 overflow-hidden">
|
||||||
<GitPanelContainer
|
<GitPanelContainer
|
||||||
selectedWorkspace={selectedWorkspace}
|
selectedWorkspace={selectedWorkspace}
|
||||||
repos={repos}
|
repos={repos}
|
||||||
repoInfos={repoInfos}
|
repoInfos={repoInfos}
|
||||||
onBranchNameChange={handleBranchNameChange}
|
onBranchNameChange={handleBranchNameChange}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
</Allotment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPreviewMode) {
|
if (isPreviewMode) {
|
||||||
// In preview mode, split git panel vertically: preview controls on top, git on bottom
|
// In preview mode, split git panel vertically: preview controls on top, git on bottom
|
||||||
return (
|
return (
|
||||||
<Allotment vertical onDragEnd={handleFileTreeResize} proportionalLayout>
|
<div className="flex flex-col h-full">
|
||||||
<Allotment.Pane minSize={200} preferredSize={fileTreeHeight}>
|
<div className="flex-[7] min-h-0 overflow-hidden">
|
||||||
<PreviewControlsContainer
|
<PreviewControlsContainer
|
||||||
attemptId={selectedWorkspace?.id}
|
attemptId={selectedWorkspace?.id}
|
||||||
onViewProcessInPanel={handleViewProcessInPanel}
|
onViewProcessInPanel={handleViewProcessInPanel}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
<Allotment.Pane minSize={200}>
|
<div className="flex-[3] min-h-0 overflow-hidden">
|
||||||
<GitPanelContainer
|
<GitPanelContainer
|
||||||
selectedWorkspace={selectedWorkspace}
|
selectedWorkspace={selectedWorkspace}
|
||||||
repos={repos}
|
repos={repos}
|
||||||
repoInfos={repoInfos}
|
repoInfos={repoInfos}
|
||||||
onBranchNameChange={handleBranchNameChange}
|
onBranchNameChange={handleBranchNameChange}
|
||||||
/>
|
/>
|
||||||
</Allotment.Pane>
|
</div>
|
||||||
</Allotment>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,123 +726,119 @@ export function WorkspacesLayout() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle inner pane resize (main, changes/logs, git panel)
|
|
||||||
const handleInnerPaneResize = useCallback(
|
|
||||||
(sizes: number[]) => {
|
|
||||||
// sizes[0] = main (no persistence needed, uses LayoutPriority.High)
|
|
||||||
// sizes[1] = changes/logs panel
|
|
||||||
// sizes[2] = git panel
|
|
||||||
if (sizes[2] !== undefined) setGitPanelWidth(sizes[2]);
|
|
||||||
|
|
||||||
const total = sizes.reduce((sum, s) => sum + (s ?? 0), 0);
|
|
||||||
if (total > 0) {
|
|
||||||
const centerPaneWidth = sizes[1];
|
|
||||||
if (centerPaneWidth !== undefined) {
|
|
||||||
const percent = Math.round((centerPaneWidth / total) * 100);
|
|
||||||
setChangesPanelWidth(`${percent}%`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setGitPanelWidth, setChangesPanelWidth]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle outer pane resize (sidebar only)
|
|
||||||
const handleOuterPaneResize = useCallback(
|
|
||||||
(sizes: number[]) => {
|
|
||||||
if (sizes[0] !== undefined) setSidebarWidth(sizes[0]);
|
|
||||||
},
|
|
||||||
[setSidebarWidth]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Render layout content (create mode or workspace mode)
|
// Render layout content (create mode or workspace mode)
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
// Inner Allotment with panes 2-4 (main, changes/logs, git panel)
|
// Main panel content
|
||||||
const innerAllotment = (
|
const mainPanelContent = isCreateMode ? (
|
||||||
<Allotment onDragEnd={handleInnerPaneResize}>
|
<CreateChatBoxContainer />
|
||||||
<Allotment.Pane
|
) : (
|
||||||
visible={isMainPanelVisible}
|
<FileNavigationProvider
|
||||||
priority={LayoutPriority.High}
|
viewFileInChanges={handleViewFileInChanges}
|
||||||
minSize={300}
|
diffPaths={diffPaths}
|
||||||
|
>
|
||||||
|
<LogNavigationProvider
|
||||||
|
viewProcessInPanel={handleViewProcessInPanel}
|
||||||
|
viewToolContentInPanel={handleViewToolContentInPanel}
|
||||||
>
|
>
|
||||||
<div className="h-full overflow-hidden">
|
<WorkspacesMainContainer
|
||||||
{isCreateMode ? (
|
selectedWorkspace={selectedWorkspace ?? null}
|
||||||
<CreateChatBoxContainer />
|
selectedSession={selectedSession}
|
||||||
) : (
|
sessions={sessions}
|
||||||
<FileNavigationProvider
|
onSelectSession={selectSession}
|
||||||
viewFileInChanges={handleViewFileInChanges}
|
isLoading={isLoading}
|
||||||
diffPaths={diffPaths}
|
isNewSessionMode={isNewSessionMode}
|
||||||
>
|
onStartNewSession={startNewSession}
|
||||||
<LogNavigationProvider
|
onViewCode={handleToggleChangesMode}
|
||||||
viewProcessInPanel={handleViewProcessInPanel}
|
diffStats={diffStats}
|
||||||
viewToolContentInPanel={handleViewToolContentInPanel}
|
/>
|
||||||
>
|
</LogNavigationProvider>
|
||||||
<WorkspacesMainContainer
|
</FileNavigationProvider>
|
||||||
selectedWorkspace={selectedWorkspace ?? null}
|
|
||||||
selectedSession={selectedSession}
|
|
||||||
sessions={sessions}
|
|
||||||
onSelectSession={selectSession}
|
|
||||||
isLoading={isLoading}
|
|
||||||
isNewSessionMode={isNewSessionMode}
|
|
||||||
onStartNewSession={startNewSession}
|
|
||||||
onViewCode={handleToggleChangesMode}
|
|
||||||
diffStats={diffStats}
|
|
||||||
/>
|
|
||||||
</LogNavigationProvider>
|
|
||||||
</FileNavigationProvider>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Allotment.Pane>
|
|
||||||
|
|
||||||
<Allotment.Pane
|
|
||||||
minSize={300}
|
|
||||||
preferredSize={changesPanelWidth}
|
|
||||||
visible={isRightMainPanelVisible}
|
|
||||||
>
|
|
||||||
<div className="h-full overflow-hidden">
|
|
||||||
{isChangesMode && (
|
|
||||||
<ChangesPanelContainer
|
|
||||||
diffs={realDiffs}
|
|
||||||
selectedFilePath={selectedFilePath}
|
|
||||||
onFileInViewChange={setFileInView}
|
|
||||||
projectId={selectedWorkspaceTask?.project_id}
|
|
||||||
attemptId={selectedWorkspace?.id}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isLogsMode && (
|
|
||||||
<LogsContentContainer
|
|
||||||
content={logsPanelContent}
|
|
||||||
searchQuery={logSearchQuery}
|
|
||||||
currentMatchIndex={logCurrentMatchIdx}
|
|
||||||
onMatchIndicesChange={setLogMatchIndices}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isPreviewMode && (
|
|
||||||
<PreviewBrowserContainer attemptId={selectedWorkspace?.id} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Allotment.Pane>
|
|
||||||
|
|
||||||
<Allotment.Pane
|
|
||||||
minSize={300}
|
|
||||||
preferredSize={gitPanelWidth}
|
|
||||||
maxSize={600}
|
|
||||||
visible={isGitPanelVisible}
|
|
||||||
>
|
|
||||||
<div className="h-full overflow-hidden">
|
|
||||||
{renderRightPanelContent()}
|
|
||||||
</div>
|
|
||||||
</Allotment.Pane>
|
|
||||||
</Allotment>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wrap inner Allotment with providers
|
// Right main panel content (Changes/Logs/Preview)
|
||||||
|
const rightMainPanelContent = (
|
||||||
|
<>
|
||||||
|
{isChangesMode && (
|
||||||
|
<ChangesPanelContainer
|
||||||
|
diffs={realDiffs}
|
||||||
|
selectedFilePath={selectedFilePath}
|
||||||
|
onFileInViewChange={setFileInView}
|
||||||
|
projectId={selectedWorkspaceTask?.project_id}
|
||||||
|
attemptId={selectedWorkspace?.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isLogsMode && (
|
||||||
|
<LogsContentContainer
|
||||||
|
content={logsPanelContent}
|
||||||
|
searchQuery={logSearchQuery}
|
||||||
|
currentMatchIndex={logCurrentMatchIdx}
|
||||||
|
onMatchIndicesChange={setLogMatchIndices}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isPreviewMode && (
|
||||||
|
<PreviewBrowserContainer attemptId={selectedWorkspace?.id} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Inner layout with main, changes/logs, git panel
|
||||||
|
const innerLayout = (
|
||||||
|
<div className="flex h-full">
|
||||||
|
{/* Resizable area for main + right panels */}
|
||||||
|
<Group
|
||||||
|
orientation="horizontal"
|
||||||
|
className="flex-1 min-w-0 h-full"
|
||||||
|
defaultLayout={defaultLayout()}
|
||||||
|
onLayoutChange={onLayoutChange}
|
||||||
|
>
|
||||||
|
{/* Main panel (chat area) */}
|
||||||
|
{isMainPanelVisible && (
|
||||||
|
<Panel
|
||||||
|
id="left-main"
|
||||||
|
minSize={20}
|
||||||
|
className="min-w-0 h-full overflow-hidden"
|
||||||
|
>
|
||||||
|
{mainPanelContent}
|
||||||
|
</Panel>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Resize handle between main and right panels */}
|
||||||
|
{isMainPanelVisible && isRightMainPanelVisible && (
|
||||||
|
<Separator
|
||||||
|
id="main-separator"
|
||||||
|
className="w-1 bg-transparent hover:bg-brand/50 transition-colors cursor-col-resize"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Right main panel (Changes/Logs/Preview) */}
|
||||||
|
{isRightMainPanelVisible && (
|
||||||
|
<Panel
|
||||||
|
id="right-main"
|
||||||
|
minSize={20}
|
||||||
|
className="min-w-0 h-full overflow-hidden"
|
||||||
|
>
|
||||||
|
{rightMainPanelContent}
|
||||||
|
</Panel>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Git panel (right sidebar) - fixed width, not resizable */}
|
||||||
|
{isGitPanelVisible && (
|
||||||
|
<div className="w-[300px] shrink-0 h-full overflow-hidden">
|
||||||
|
{renderRightPanelContent()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wrap inner layout with providers
|
||||||
const wrappedInnerContent = isCreateMode ? (
|
const wrappedInnerContent = isCreateMode ? (
|
||||||
<CreateModeProvider
|
<CreateModeProvider
|
||||||
initialProjectId={lastWorkspaceTask?.project_id}
|
initialProjectId={lastWorkspaceTask?.project_id}
|
||||||
initialRepos={lastWorkspaceRepos}
|
initialRepos={lastWorkspaceRepos}
|
||||||
>
|
>
|
||||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||||
{innerAllotment}
|
{innerLayout}
|
||||||
</ReviewProvider>
|
</ReviewProvider>
|
||||||
</CreateModeProvider>
|
</CreateModeProvider>
|
||||||
) : (
|
) : (
|
||||||
@@ -870,32 +848,23 @@ export function WorkspacesLayout() {
|
|||||||
sessionId={selectedSessionId}
|
sessionId={selectedSessionId}
|
||||||
>
|
>
|
||||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||||
{innerAllotment}
|
{innerLayout}
|
||||||
</ReviewProvider>
|
</ReviewProvider>
|
||||||
</ExecutionProcessesProvider>
|
</ExecutionProcessesProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Allotment
|
<div className="flex flex-1 min-h-0">
|
||||||
ref={allotmentRef}
|
{/* Sidebar - OUTSIDE providers, won't remount on workspace switch */}
|
||||||
className="flex-1 min-h-0"
|
{isSidebarVisible && (
|
||||||
onDragEnd={handleOuterPaneResize}
|
<div className="w-[300px] shrink-0 h-full overflow-hidden">
|
||||||
>
|
{renderSidebar()}
|
||||||
{/* Sidebar pane - OUTSIDE providers, won't remount on workspace switch */}
|
</div>
|
||||||
<Allotment.Pane
|
)}
|
||||||
minSize={300}
|
|
||||||
preferredSize={sidebarWidth}
|
|
||||||
maxSize={600}
|
|
||||||
visible={isSidebarVisible}
|
|
||||||
>
|
|
||||||
<div className="h-full overflow-hidden">{renderSidebar()}</div>
|
|
||||||
</Allotment.Pane>
|
|
||||||
|
|
||||||
{/* Container for provider-wrapped inner content */}
|
{/* Container for provider-wrapped inner content */}
|
||||||
<Allotment.Pane priority={LayoutPriority.High}>
|
<div className="flex-1 min-w-0 h-full">{wrappedInnerContent}</div>
|
||||||
{wrappedInnerContent}
|
</div>
|
||||||
</Allotment.Pane>
|
|
||||||
</Allotment>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,8 @@ export const PERSIST_KEYS = {
|
|||||||
contextBarPosition: 'context-bar-position',
|
contextBarPosition: 'context-bar-position',
|
||||||
// GitHub comments toggle
|
// GitHub comments toggle
|
||||||
showGitHubComments: 'show-github-comments',
|
showGitHubComments: 'show-github-comments',
|
||||||
// Pane sizes
|
// Panel sizes
|
||||||
sidebarWidth: 'workspaces-sidebar-width',
|
rightMainPanel: 'right-main-panel',
|
||||||
gitPanelWidth: 'workspaces-git-panel-width',
|
|
||||||
changesPanelWidth: 'workspaces-changes-panel-width',
|
|
||||||
fileTreeHeight: 'workspaces-file-tree-height',
|
|
||||||
// Dynamic keys (use helper functions)
|
// Dynamic keys (use helper functions)
|
||||||
repoCard: (repoId: string) => `repo-card-${repoId}` as const,
|
repoCard: (repoId: string) => `repo-card-${repoId}` as const,
|
||||||
} as const;
|
} as const;
|
||||||
@@ -53,10 +50,7 @@ export type PersistKey =
|
|||||||
| typeof PERSIST_KEYS.changesSection
|
| typeof PERSIST_KEYS.changesSection
|
||||||
| typeof PERSIST_KEYS.devServerSection
|
| typeof PERSIST_KEYS.devServerSection
|
||||||
| typeof PERSIST_KEYS.showGitHubComments
|
| typeof PERSIST_KEYS.showGitHubComments
|
||||||
| typeof PERSIST_KEYS.sidebarWidth
|
| typeof PERSIST_KEYS.rightMainPanel
|
||||||
| typeof PERSIST_KEYS.gitPanelWidth
|
|
||||||
| typeof PERSIST_KEYS.changesPanelWidth
|
|
||||||
| typeof PERSIST_KEYS.fileTreeHeight
|
|
||||||
| `repo-card-${string}`
|
| `repo-card-${string}`
|
||||||
| `diff:${string}`
|
| `diff:${string}`
|
||||||
| `edit:${string}`
|
| `edit:${string}`
|
||||||
|
|||||||
51
pnpm-lock.yaml
generated
51
pnpm-lock.yaml
generated
@@ -122,9 +122,6 @@ importers:
|
|||||||
'@virtuoso.dev/message-list':
|
'@virtuoso.dev/message-list':
|
||||||
specifier: ^1.13.3
|
specifier: ^1.13.3
|
||||||
version: 1.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.13.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
allotment:
|
|
||||||
specifier: ^1.20.5
|
|
||||||
version: 1.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -2121,12 +2118,6 @@ packages:
|
|||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||||
|
|
||||||
allotment@1.20.5:
|
|
||||||
resolution: {integrity: sha512-7i4NT7ieXEyAd5lBrXmE7WHz/e7hRuo97+j+TwrPE85ha6kyFURoc76nom0dWSZ1pTKVEAMJy/+f3/Isfu/41A==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
|
|
||||||
ansi-regex@5.0.1:
|
ansi-regex@5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2222,9 +2213,6 @@ packages:
|
|||||||
class-variance-authority@0.7.1:
|
class-variance-authority@0.7.1:
|
||||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||||
|
|
||||||
classnames@2.5.1:
|
|
||||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
|
||||||
|
|
||||||
click-to-react-component@1.1.2:
|
click-to-react-component@1.1.2:
|
||||||
resolution: {integrity: sha512-8e9xU2MTubMwrtqu66/FtVHnv4TD94svOwMLRhza54OsmZqwMsLkscnl6ecJ3GgJ8Rk74jbLHCxpoSaZrdClGw==}
|
resolution: {integrity: sha512-8e9xU2MTubMwrtqu66/FtVHnv4TD94svOwMLRhza54OsmZqwMsLkscnl6ecJ3GgJ8Rk74jbLHCxpoSaZrdClGw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2512,9 +2500,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
eventemitter3@5.0.1:
|
|
||||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
|
||||||
|
|
||||||
fancy-ansi@0.1.3:
|
fancy-ansi@0.1.3:
|
||||||
resolution: {integrity: sha512-tRQVTo5jjdSIiydqgzIIEZpKddzSsfGLsSVt6vWdjVm7fbvDTiQkyoPu6Z3dIPlAM4OZk0jP5jmTCX4G8WGgBw==}
|
resolution: {integrity: sha512-tRQVTo5jjdSIiydqgzIIEZpKddzSsfGLsSVt6vWdjVm7fbvDTiQkyoPu6Z3dIPlAM4OZk0jP5jmTCX4G8WGgBw==}
|
||||||
|
|
||||||
@@ -2825,12 +2810,6 @@ packages:
|
|||||||
lodash-es@4.17.21:
|
lodash-es@4.17.21:
|
||||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||||
|
|
||||||
lodash.clamp@4.0.3:
|
|
||||||
resolution: {integrity: sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==}
|
|
||||||
|
|
||||||
lodash.debounce@4.0.8:
|
|
||||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
@@ -3513,12 +3492,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
usehooks-ts@3.1.1:
|
|
||||||
resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==}
|
|
||||||
engines: {node: '>=16.15.0'}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
|
||||||
|
|
||||||
util-deprecate@1.0.2:
|
util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||||
|
|
||||||
@@ -5613,17 +5586,6 @@ snapshots:
|
|||||||
json-schema-traverse: 1.0.0
|
json-schema-traverse: 1.0.0
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
allotment@1.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
|
||||||
dependencies:
|
|
||||||
classnames: 2.5.1
|
|
||||||
eventemitter3: 5.0.1
|
|
||||||
fast-deep-equal: 3.1.3
|
|
||||||
lodash.clamp: 4.0.3
|
|
||||||
lodash.debounce: 4.0.8
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
usehooks-ts: 3.1.1(react@18.3.1)
|
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-regex@6.1.0: {}
|
ansi-regex@6.1.0: {}
|
||||||
@@ -5718,8 +5680,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
||||||
classnames@2.5.1: {}
|
|
||||||
|
|
||||||
click-to-react-component@1.1.2(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
click-to-react-component@1.1.2(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/react-dom-interactions': 0.3.1(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@floating-ui/react-dom-interactions': 0.3.1(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@@ -6064,8 +6024,6 @@ snapshots:
|
|||||||
|
|
||||||
esutils@2.0.3: {}
|
esutils@2.0.3: {}
|
||||||
|
|
||||||
eventemitter3@5.0.1: {}
|
|
||||||
|
|
||||||
fancy-ansi@0.1.3:
|
fancy-ansi@0.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
@@ -6344,10 +6302,6 @@ snapshots:
|
|||||||
|
|
||||||
lodash-es@4.17.21: {}
|
lodash-es@4.17.21: {}
|
||||||
|
|
||||||
lodash.clamp@4.0.3: {}
|
|
||||||
|
|
||||||
lodash.debounce@4.0.8: {}
|
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
@@ -6960,11 +6914,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
usehooks-ts@3.1.1(react@18.3.1):
|
|
||||||
dependencies:
|
|
||||||
lodash.debounce: 4.0.8
|
|
||||||
react: 18.3.1
|
|
||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
uuid@13.0.0: {}
|
uuid@13.0.0: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user