From 41bfeb0dca64d7f999d7194d3078bdd8991c1c07 Mon Sep 17 00:00:00 2001 From: m4air Date: Mon, 7 Sep 2026 19:50:03 -0700 Subject: [PATCH] perf: measure sidebar row geometry once before sorting delete anchors --- .../sidebar/worktree-context-menu-policy.ts | 7 +++--- .../worktree-delete-position-scaling.test.ts | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/renderer/src/components/sidebar/worktree-delete-position-scaling.test.ts diff --git a/src/renderer/src/components/sidebar/worktree-context-menu-policy.ts b/src/renderer/src/components/sidebar/worktree-context-menu-policy.ts index 2d8aaa2aa20..bbe73b69435 100644 --- a/src/renderer/src/components/sidebar/worktree-context-menu-policy.ts +++ b/src/renderer/src/components/sidebar/worktree-context-menu-policy.ts @@ -171,9 +171,10 @@ export function preserveDeleteSiblingPosition(scope: HTMLElement | null): () => if (!(sidebar instanceof HTMLElement) || !(row instanceof HTMLElement)) { return () => {} } - const rows = Array.from( - sidebar.querySelectorAll('[data-worktree-virtual-row]') - ).sort((a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top) + const rows = Array.from(sidebar.querySelectorAll('[data-worktree-virtual-row]')) + .map((element) => ({ element, top: element.getBoundingClientRect().top })) + .sort((a, b) => a.top - b.top) + .map(({ element }) => element) const rowIndex = rows.indexOf(row) const anchorRow = rows[rowIndex + 1] ?? rows[rowIndex - 1] ?? null const anchorKey = anchorRow?.getAttribute('data-worktree-virtual-row-key') diff --git a/src/renderer/src/components/sidebar/worktree-delete-position-scaling.test.ts b/src/renderer/src/components/sidebar/worktree-delete-position-scaling.test.ts new file mode 100644 index 00000000000..65a78065177 --- /dev/null +++ b/src/renderer/src/components/sidebar/worktree-delete-position-scaling.test.ts @@ -0,0 +1,22 @@ +// @vitest-environment happy-dom +import { expect, it } from 'vitest' +import { preserveDeleteSiblingPosition } from './worktree-context-menu-policy' + +it('measures each mounted sidebar row once when choosing a delete-position anchor', () => { + const sidebar = document.createElement('div') + sidebar.setAttribute('data-worktree-sidebar', '') + let measurements = 0 + const rows = Array.from({ length: 200 }, (_, index) => { + const row = document.createElement('div') + row.setAttribute('data-worktree-virtual-row', '') + row.setAttribute('data-worktree-virtual-row-key', String(index)) + row.getBoundingClientRect = () => { + measurements += 1 + return { top: (index * 73) % 200 } as DOMRect + } + sidebar.append(row) + return row + }) + expect(typeof preserveDeleteSiblingPosition(rows[100])).toBe('function') + expect(measurements).toBeLessThanOrEqual(201) +})