perf: handle large workspace space size scales

This commit is contained in:
Neil
2026-05-30 07:20:02 -07:00
parent bddf05b949
commit 17ee56862e
3 changed files with 51 additions and 3 deletions
@@ -66,6 +66,8 @@ import { buildTreemapLayout, type TreemapRect } from './workspace-space-layout'
import {
filterWorkspaceSpaceRows,
countWorkspaceSpaceActiveAgents,
getLargestWorkspaceSpaceItemSize,
getLargestWorkspaceSpaceRowSize,
getSelectedDeletableWorkspaceIds,
getVisibleDeletableWorkspaceIds,
getWorkspaceSpaceGitStatusRefreshCandidates,
@@ -831,7 +833,7 @@ function BreakdownList({
)
}
const maxChildSize = Math.max(...worktree.topLevelItems.map((item) => item.sizeBytes), 0)
const maxChildSize = getLargestWorkspaceSpaceItemSize(worktree.topLevelItems)
const topLevelItemCount = worktree.topLevelItems.length + worktree.omittedTopLevelItemCount
return (
<div className="min-h-72 rounded-lg border border-border/70 bg-background/35">
@@ -1276,7 +1278,7 @@ export function WorkspaceSpaceManagerPanel(): React.JSX.Element {
const zoomedWorktree =
sourceRows.find((row) => row.worktreeId === treemapZoomWorktreeId && row.status === 'ok') ??
null
const maxSize = Math.max(...rows.map((row) => row.sizeBytes), 0)
const maxSize = getLargestWorkspaceSpaceRowSize(rows)
const selectedDeletableIds = useMemo(
() => getSelectedDeletableWorkspaceIds(rows, selectedIds, isWorktreeUnavailableForDelete),
[isWorktreeUnavailableForDelete, rows, selectedIds]
@@ -3,6 +3,8 @@ import type { WorkspaceSpaceWorktree } from '../../../../shared/workspace-space-
import {
countWorkspaceSpaceActiveAgents,
filterWorkspaceSpaceRows,
getLargestWorkspaceSpaceItemSize,
getLargestWorkspaceSpaceRowSize,
getSelectedDeletableWorkspaceIds,
getVisibleDeletableWorkspaceIds,
getWorkspaceSpaceGitStatusRefreshCandidates,
@@ -99,6 +101,23 @@ describe('workspace space presentation helpers', () => {
expect(filterWorkspaceSpaceRows(rows, '', true).map((item) => item.worktreeId)).toEqual(['a'])
})
it('finds largest sizes without spreading large workspace arrays', () => {
const rows = Array.from({ length: 130_000 }, (_, index) =>
row({ worktreeId: `wt-${index}`, sizeBytes: index === 87_654 ? 999_999 : index })
)
const items = Array.from({ length: 130_000 }, (_, index) => ({
name: `item-${index}`,
path: `/repo/item-${index}`,
kind: 'directory' as const,
sizeBytes: index === 12_345 ? 888_888 : index
}))
expect(getLargestWorkspaceSpaceRowSize(rows)).toBe(999_999)
expect(getLargestWorkspaceSpaceItemSize(items)).toBe(888_888)
expect(getLargestWorkspaceSpaceRowSize([])).toBe(0)
expect(getLargestWorkspaceSpaceItemSize([])).toBe(0)
})
it('returns only selected worktrees that can be deleted', () => {
const rows = [
row({ worktreeId: 'ok', canDelete: true, status: 'ok' }),
@@ -7,7 +7,10 @@ import {
} from '../../../../shared/agent-status-types'
import { parsePaneKey } from '../../../../shared/stable-pane-id'
import type { TerminalTab } from '../../../../shared/types'
import type { WorkspaceSpaceWorktree } from '../../../../shared/workspace-space-types'
import type {
WorkspaceSpaceItem,
WorkspaceSpaceWorktree
} from '../../../../shared/workspace-space-types'
export type WorkspaceSpaceSortKey = 'size' | 'name' | 'repo' | 'activity'
export type WorkspaceSpaceSortDirection = 'asc' | 'desc'
@@ -140,6 +143,30 @@ export function getWorkspaceSpaceSearchText(worktree: WorkspaceSpaceWorktree): s
.toLowerCase()
}
export function getLargestWorkspaceSpaceItemSize(
items: readonly Pick<WorkspaceSpaceItem, 'sizeBytes'>[]
): number {
let maxSize = 0
for (const item of items) {
if (item.sizeBytes > maxSize) {
maxSize = item.sizeBytes
}
}
return maxSize
}
export function getLargestWorkspaceSpaceRowSize(
rows: readonly Pick<WorkspaceSpaceWorktree, 'sizeBytes'>[]
): number {
let maxSize = 0
for (const row of rows) {
if (row.sizeBytes > maxSize) {
maxSize = row.sizeBytes
}
}
return maxSize
}
function compareRows(
left: WorkspaceSpaceWorktree,
right: WorkspaceSpaceWorktree,