Prevent WorkspacesSidebar re-render on workspace switch (Vibe Kanban) (#1973)
* The refactoring is complete. Here's a summary of what was changed:
## Summary
**File modified:** `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`
**Change:** Restructured `renderContent()` so that `WorkspacesSidebar` is rendered **outside** the `ExecutionProcessesProvider`.
### Before:
```tsx
<ExecutionProcessesProvider key={`${selectedWorkspace?.id}-${selectedSessionId}`}>
<ReviewProvider>
<Allotment>
<Allotment.Pane>{renderSidebar()}</Allotment.Pane> // Inside provider
<Allotment.Pane>...</Allotment.Pane>
<Allotment.Pane>...</Allotment.Pane>
<Allotment.Pane>...</Allotment.Pane>
</Allotment>
</ReviewProvider>
</ExecutionProcessesProvider>
```
### After:
```tsx
<Allotment>
<Allotment.Pane>{renderSidebar()}</Allotment.Pane> // OUTSIDE provider - won't remount
<ExecutionProcessesProvider key={`${selectedWorkspace?.id}-${selectedSessionId}`}>
<ReviewProvider>
<>
<Allotment.Pane>...</Allotment.Pane>
<Allotment.Pane>...</Allotment.Pane>
<Allotment.Pane>...</Allotment.Pane>
</>
</ReviewProvider>
</ExecutionProcessesProvider>
</Allotment>
```
**Result:** When switching workspaces, the `ExecutionProcessesProvider`'s key changes, which unmounts/remounts only panes 2-4. The sidebar pane stays mounted and won't re-render unnecessarily.
* The implementation is complete. Here's a summary of what was changed:
## Summary
**File modified:** `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`
### Changes:
1. **Created nested Allotments structure:**
- **Outer Allotment** (2 panes): Sidebar + Content container
- **Inner Allotment** (3 panes): Main + Changes/Logs + Git Panel
2. **Separated resize handlers:**
- `handleOuterPaneResize`: Handles sidebar width only
- `handleInnerPaneResize`: Handles main, changes/logs, and git panel widths
3. **Removed old `handlePaneResize`** which was designed for the flat 4-pane structure
### Result:
```
Outer Allotment
├── Sidebar Pane (OUTSIDE providers - won't remount)
└── Content Pane
└── ExecutionProcessesProvider (keyed)
└── ReviewProvider
└── Inner Allotment
├── Main Pane
├── Changes/Logs Pane
└── Git Panel Pane
```
Now when switching workspaces:
- The `ExecutionProcessesProvider` with its key will remount the inner Allotment (ensuring clean state)
- The sidebar stays mounted because it's outside the keyed provider
- Layout looks the same as before (sidebar on left, 3 panes on right)
This commit is contained in:
committed by
GitHub
parent
2c474d689c
commit
572e9706e5
@@ -495,26 +495,6 @@ export function WorkspacesLayout() {
|
||||
[setFileTreeHeight]
|
||||
);
|
||||
|
||||
// Handle pane resize end
|
||||
const handlePaneResize = useCallback(
|
||||
(sizes: number[]) => {
|
||||
// sizes[0] = sidebar, sizes[1] = main, sizes[2] = changes/logs panel, sizes[3] = git panel
|
||||
if (sizes[0] !== undefined) setSidebarWidth(sizes[0]);
|
||||
if (sizes[3] !== undefined) setGitPanelWidth(sizes[3]);
|
||||
|
||||
const total = sizes.reduce((sum, s) => sum + (s ?? 0), 0);
|
||||
if (total > 0) {
|
||||
// Store changes/logs panel as percentage of TOTAL container width
|
||||
const centerPaneWidth = sizes[2];
|
||||
if (centerPaneWidth !== undefined) {
|
||||
const percent = Math.round((centerPaneWidth / total) * 100);
|
||||
setChangesPanelWidth(`${percent}%`);
|
||||
}
|
||||
}
|
||||
},
|
||||
[setSidebarWidth, setGitPanelWidth, setChangesPanelWidth]
|
||||
);
|
||||
|
||||
// Navigate to logs panel and select a specific process
|
||||
const handleViewProcessInPanel = useCallback(
|
||||
(processId: string) => {
|
||||
@@ -698,23 +678,39 @@ 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)
|
||||
const renderContent = () => {
|
||||
const allotmentContent = (
|
||||
<Allotment
|
||||
ref={allotmentRef}
|
||||
className="flex-1 min-h-0"
|
||||
onDragEnd={handlePaneResize}
|
||||
>
|
||||
<Allotment.Pane
|
||||
minSize={300}
|
||||
preferredSize={sidebarWidth}
|
||||
maxSize={600}
|
||||
visible={isSidebarVisible}
|
||||
>
|
||||
<div className="h-full overflow-hidden">{renderSidebar()}</div>
|
||||
</Allotment.Pane>
|
||||
|
||||
// Inner Allotment with panes 2-4 (main, changes/logs, git panel)
|
||||
const innerAllotment = (
|
||||
<Allotment onDragEnd={handleInnerPaneResize}>
|
||||
<Allotment.Pane
|
||||
visible={isMainPanelVisible}
|
||||
priority={LayoutPriority.High}
|
||||
@@ -791,30 +787,50 @@ export function WorkspacesLayout() {
|
||||
</Allotment>
|
||||
);
|
||||
|
||||
if (isCreateMode) {
|
||||
return (
|
||||
<CreateModeProvider
|
||||
initialProjectId={lastWorkspaceTask?.project_id}
|
||||
initialRepos={lastWorkspaceRepos}
|
||||
>
|
||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||
{allotmentContent}
|
||||
</ReviewProvider>
|
||||
</CreateModeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
// Wrap inner Allotment with providers
|
||||
const wrappedInnerContent = isCreateMode ? (
|
||||
<CreateModeProvider
|
||||
initialProjectId={lastWorkspaceTask?.project_id}
|
||||
initialRepos={lastWorkspaceRepos}
|
||||
>
|
||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||
{innerAllotment}
|
||||
</ReviewProvider>
|
||||
</CreateModeProvider>
|
||||
) : (
|
||||
<ExecutionProcessesProvider
|
||||
key={`${selectedWorkspace?.id}-${selectedSessionId}`}
|
||||
attemptId={selectedWorkspace?.id}
|
||||
sessionId={selectedSessionId}
|
||||
>
|
||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||
{allotmentContent}
|
||||
{innerAllotment}
|
||||
</ReviewProvider>
|
||||
</ExecutionProcessesProvider>
|
||||
);
|
||||
|
||||
return (
|
||||
<Allotment
|
||||
ref={allotmentRef}
|
||||
className="flex-1 min-h-0"
|
||||
onDragEnd={handleOuterPaneResize}
|
||||
>
|
||||
{/* Sidebar pane - OUTSIDE providers, won't remount on workspace switch */}
|
||||
<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 */}
|
||||
<Allotment.Pane priority={LayoutPriority.High}>
|
||||
{wrappedInnerContent}
|
||||
</Allotment.Pane>
|
||||
</Allotment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user