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]
|
[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
|
// Navigate to logs panel and select a specific process
|
||||||
const handleViewProcessInPanel = useCallback(
|
const handleViewProcessInPanel = useCallback(
|
||||||
(processId: string) => {
|
(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)
|
// Render layout content (create mode or workspace mode)
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
const allotmentContent = (
|
// Inner Allotment with panes 2-4 (main, changes/logs, git panel)
|
||||||
<Allotment
|
const innerAllotment = (
|
||||||
ref={allotmentRef}
|
<Allotment onDragEnd={handleInnerPaneResize}>
|
||||||
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>
|
|
||||||
|
|
||||||
<Allotment.Pane
|
<Allotment.Pane
|
||||||
visible={isMainPanelVisible}
|
visible={isMainPanelVisible}
|
||||||
priority={LayoutPriority.High}
|
priority={LayoutPriority.High}
|
||||||
@@ -791,30 +787,50 @@ export function WorkspacesLayout() {
|
|||||||
</Allotment>
|
</Allotment>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isCreateMode) {
|
// Wrap inner Allotment with providers
|
||||||
return (
|
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}>
|
||||||
{allotmentContent}
|
{innerAllotment}
|
||||||
</ReviewProvider>
|
</ReviewProvider>
|
||||||
</CreateModeProvider>
|
</CreateModeProvider>
|
||||||
);
|
) : (
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ExecutionProcessesProvider
|
<ExecutionProcessesProvider
|
||||||
key={`${selectedWorkspace?.id}-${selectedSessionId}`}
|
key={`${selectedWorkspace?.id}-${selectedSessionId}`}
|
||||||
attemptId={selectedWorkspace?.id}
|
attemptId={selectedWorkspace?.id}
|
||||||
sessionId={selectedSessionId}
|
sessionId={selectedSessionId}
|
||||||
>
|
>
|
||||||
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
<ReviewProvider attemptId={selectedWorkspace?.id}>
|
||||||
{allotmentContent}
|
{innerAllotment}
|
||||||
</ReviewProvider>
|
</ReviewProvider>
|
||||||
</ExecutionProcessesProvider>
|
</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 (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user