perf(cmd-j): keep cold palette within frame budget

This commit is contained in:
Jinwoo-H
2026-08-27 04:27:19 -04:00
parent 026389a3bc
commit 9fe918e49d
10 changed files with 646 additions and 67 deletions
@@ -13,6 +13,8 @@ import { StarNagToastHost } from '../components/star-nag/StarNagToastHost'
import { TelemetryFirstLaunchSurface } from '../components/TelemetryFirstLaunchSurface'
import { ZoomOverlay } from '../components/ZoomOverlay'
import { shouldRenderPetOverlay } from '../components/pet/pet-overlay-visibility'
// Why eager: lazy evaluation alone delayed the first global Cmd+J results by ~300 ms.
import WorktreeJumpPalette from '../components/WorktreeJumpPalette'
import { useAppStore } from '../store'
import type { UpdateStatus } from '../../../shared/update-status-types'
import { useLazyModalMounts } from './use-lazy-modal-mounts'
@@ -20,7 +22,6 @@ import type { FloatingWorkspacePanelState } from './use-floating-workspace-panel
import type { OnboardingGate } from './use-onboarding-and-feature-tips'
const QuickOpen = lazy(() => import('../components/QuickOpen'))
const WorktreeJumpPalette = lazy(() => import('../components/WorktreeJumpPalette'))
const WorkspaceCleanupDialog = lazy(
() => import('../components/workspace-cleanup/WorkspaceCleanupDialog')
)
@@ -1,13 +1,5 @@
/* oxlint-disable max-lines */
import React, {
useCallback,
useDeferredValue,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState
} from 'react'
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
@@ -75,11 +67,12 @@ import { activateAndRevealWorktree } from '@/lib/worktree-activation'
import { queueWorkspaceActivationTerminalFocus } from '@/lib/workspace-activation-terminal-focus'
import {
getWorktreePaletteSearchScope,
searchWorktreeDocuments,
type MatchRange,
type PaletteSearchResult
} from '@/lib/worktree-palette-search'
import { buildWorktreePaletteDocuments } from '@/lib/worktree-palette-document'
import { useCooperativeWorktreePaletteSearch } from './cmd-j/use-cooperative-worktree-palette-search'
import { usePaintDeferredValue } from './cmd-j/use-paint-deferred-value'
import {
resolveWorktreeBranchLabel,
resolveWorktreeDisplayName
@@ -826,7 +819,7 @@ function WorktreeJumpPaletteContent({
const settingsSections = useSettingsNavigationMetadata()
const [query, setQuery] = useState('')
const deferredQuery = useDeferredValue(query)
const deferredQuery = usePaintDeferredValue(query)
const liveQueryRef = useRef(query)
liveQueryRef.current = query
// Why a ref: creation must follow an explicit ArrowDown/click, and typing re-arms
@@ -1211,25 +1204,14 @@ function WorktreeJumpPaletteContent({
]
)
const worktreeMatches = useMemo(
() =>
searchWorktreeDocuments({
worktrees: sortedWorktrees,
query: paletteSearchQuery,
documents: worktreeDocuments,
repoMap,
repoMapByHostIdentity: repoByHostIdentity,
checksReviewByWorktree
}),
[
sortedWorktrees,
paletteSearchQuery,
worktreeDocuments,
repoByHostIdentity,
repoMap,
checksReviewByWorktree
]
)
const worktreeMatches = useCooperativeWorktreePaletteSearch({
worktrees: sortedWorktrees,
query: paletteSearchQuery,
documents: worktreeDocuments,
repoMap,
repoMapByHostIdentity: repoByHostIdentity,
checksReviewByWorktree
})
const browserPageEntries = useMemo<SearchableBrowserPage[]>(() => {
if (!paletteStatusInputsActive) {
@@ -0,0 +1,61 @@
import { useEffect, useMemo, useState } from 'react'
import {
searchWorktreeDocuments,
searchWorktreeDocumentsCooperatively,
type PaletteSearchResult,
type WorktreePaletteSearchArgs
} from '@/lib/worktree-palette-search'
import { parseCmdJTaskSourceUrl } from '@/lib/worktree-palette-task-url-match'
const COOPERATIVE_SEARCH_MIN_WORKTREES = 200
type CompletedSearch = {
request: WorktreePaletteSearchArgs
results: PaletteSearchResult[]
}
export function useCooperativeWorktreePaletteSearch(
args: WorktreePaletteSearchArgs
): PaletteSearchResult[] {
const { worktrees, query, documents, repoMap, repoMapByHostIdentity, checksReviewByWorktree } =
args
const request = useMemo<WorktreePaletteSearchArgs>(
() => ({
worktrees,
query,
documents,
repoMap,
repoMapByHostIdentity,
checksReviewByWorktree
}),
[worktrees, query, documents, repoMap, repoMapByHostIdentity, checksReviewByWorktree]
)
const cooperative =
request.worktrees.length >= COOPERATIVE_SEARCH_MIN_WORKTREES &&
request.query.trim().length > 0 &&
parseCmdJTaskSourceUrl(request.query.trim()) === null
const immediateResults = useMemo(
() => (cooperative ? null : searchWorktreeDocuments(request)),
[cooperative, request]
)
const [completed, setCompleted] = useState<CompletedSearch | null>(null)
useEffect(() => {
if (!cooperative) {
return
}
let current = true
void searchWorktreeDocumentsCooperatively(request, {
shouldContinue: () => current
}).then((results) => {
if (current && results) {
setCompleted({ request, results })
}
})
return () => {
current = false
}
}, [cooperative, request])
return immediateResults ?? (completed?.request === request ? completed.results : [])
}
@@ -0,0 +1,26 @@
import { useEffect, useState } from 'react'
import { yieldToEventLoop } from '../../../../shared/event-loop-yield'
export function usePaintDeferredValue<T>(value: T): T {
const [deferred, setDeferred] = useState(value)
useEffect(() => {
if (Object.is(value, deferred)) {
return
}
let current = true
const frameId = requestAnimationFrame(() => {
void yieldToEventLoop().then(() => {
if (current) {
setDeferred(value)
}
})
})
return () => {
current = false
cancelAnimationFrame(frameId)
}
}, [deferred, value])
return deferred
}
@@ -1,4 +1,5 @@
import { normalizePaletteText, type NormalizedText } from './normalized-text'
import { isLetterOnlyWord } from './palette-query'
import { segmentPaletteText, type PaletteAtom, type PaletteWord } from './text-segments'
import type { PaletteMatchQuality } from './match-quality'
@@ -40,11 +41,46 @@ export type PaletteIndexedField = {
text: NormalizedText
atoms: readonly PaletteAtom[]
words: readonly PaletteWord[]
firstAtomByText: ReadonlyMap<string, PaletteAtom>
firstWordByText: ReadonlyMap<string, PaletteWord>
uniqueAtoms: readonly PaletteAtom[]
uniqueWords: readonly PaletteWord[]
wordStarts: ReadonlySet<number>
typoWordsByLength: ReadonlyMap<number, readonly PaletteWord[]>
evidenceId: string | null
identifier: PaletteIdentifierOptions | null
isContainer: boolean
}
function indexFirstByText<T>(entries: readonly T[], textOf: (entry: T) => string): Map<string, T> {
const firstByText = new Map<string, T>()
for (const entry of entries) {
const text = textOf(entry)
if (!firstByText.has(text)) {
firstByText.set(text, entry)
}
}
return firstByText
}
function indexTypoWordsByLength(words: readonly PaletteWord[]): Map<number, PaletteWord[]> {
const byLength = new Map<number, PaletteWord[]>()
const seen = new Set<string>()
for (const word of words) {
if (word.text.length < 4 || !isLetterOnlyWord(word.text) || seen.has(word.text)) {
continue
}
seen.add(word.text)
const bucket = byLength.get(word.text.length)
if (bucket) {
bucket.push(word)
} else {
byLength.set(word.text.length, [word])
}
}
return byLength
}
const IDENTIFIER_PREFIX_KINDS: ReadonlySet<PaletteIdentifierKind> = new Set<PaletteIdentifierKind>([
'port',
'sha',
@@ -121,12 +157,22 @@ export function indexPaletteField(source: PaletteFieldSource): PaletteIndexedFie
}
const text = normalizePaletteText(trimmed)
const segments = segmentPaletteText(text)
const firstAtomByText = indexFirstByText(segments.atoms, (atom) =>
text.normalized.slice(atom.start, atom.end)
)
const firstWordByText = indexFirstByText(segments.words, (word) => word.text)
return {
id: source.id,
profile: source.profile,
text,
atoms: segments.atoms,
words: segments.words,
firstAtomByText,
firstWordByText,
uniqueAtoms: [...firstAtomByText.values()],
uniqueWords: [...firstWordByText.values()],
wordStarts: new Set(segments.words.map((word) => word.start)),
typoWordsByLength: indexTypoWordsByLength(segments.words),
evidenceId: source.evidenceId ?? null,
identifier: source.identifier ?? null,
isContainer: Boolean(source.isContainer)
@@ -4,7 +4,7 @@ import {
paletteProfileAllowedQualities,
type PaletteIndexedField
} from './indexed-field'
import { isLetterOnlyWord, type PaletteQueryToken } from './palette-query'
import type { PaletteQueryToken } from './palette-query'
import type { PaletteMatchQuality } from './match-quality'
import type { PaletteAtom } from './text-segments'
import { isPaletteTypoCandidate, isWithinOnePaletteEdit } from './typo-distance'
@@ -56,7 +56,7 @@ function passesSigilGate(field: PaletteIndexedField, token: PaletteQueryToken):
}
function isWordStart(field: PaletteIndexedField, index: number): boolean {
return field.words.some((word) => word.start === index) || index === 0
return field.wordStarts.has(index) || index === 0
}
function matchAtomComponentRun(atom: PaletteAtom, compact: string): [number, number] | null {
@@ -92,11 +92,11 @@ function matchLiteral(
return { quality: 'field-exact', ranges: toRanges(field, 0, normalized.length) }
}
if (qualities.has('word-exact')) {
const word = field.words.find((entry) => entry.text === text)
const word = field.firstWordByText.get(text)
if (word) {
return { quality: 'word-exact', ranges: toRanges(field, word.start, word.end) }
}
const atom = field.atoms.find((entry) => normalized.slice(entry.start, entry.end) === text)
const atom = field.firstAtomByText.get(text)
if (atom) {
return { quality: 'word-exact', ranges: toRanges(field, atom.start, atom.end) }
}
@@ -105,8 +105,8 @@ function matchLiteral(
return { quality: 'field-prefix', ranges: toRanges(field, 0, text.length) }
}
if (qualities.has('word-prefix')) {
const word = field.words.find((entry) => entry.text.startsWith(text))
const atom = field.atoms.find((entry) => normalized.startsWith(text, entry.start))
const word = field.uniqueWords.find((entry) => entry.text.startsWith(text))
const atom = field.uniqueAtoms.find((entry) => normalized.startsWith(text, entry.start))
const start = word && atom ? Math.min(word.start, atom.start) : (word?.start ?? atom?.start)
if (start !== undefined) {
return { quality: 'word-prefix', ranges: toRanges(field, start, start + text.length) }
@@ -157,12 +157,11 @@ function matchTypo(
if (!qualities.has('typo') || !token.isLetterOnly || !isPaletteTypoCandidate(token.text)) {
return null
}
for (const word of field.words) {
if (!isPaletteTypoCandidate(word.text) || !isLetterOnlyWord(word.text)) {
continue
}
if (isWithinOnePaletteEdit(token.text, word.text)) {
return { quality: 'typo', ranges: toRanges(field, word.start, word.end) }
for (let length = token.text.length - 1; length <= token.text.length + 1; length += 1) {
for (const word of field.typoWordsByLength.get(length) ?? []) {
if (isWithinOnePaletteEdit(token.text, word.text)) {
return { quality: 'typo', ranges: toRanges(field, word.start, word.end) }
}
}
}
return null
@@ -23,3 +23,10 @@ export const PALETTE_MATCH_BUDGET = {
*/
documentPayloadMb: 24
} as const
/** User-visible budgets measured by the built Electron Cmd+J flow. */
export const PALETTE_INTERACTION_BUDGET = {
openHandlerMs: 50,
firstVisibleResultsMs: 100,
maxFrameGapMs: 50
} as const
@@ -1,9 +1,11 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import {
getWorktreePaletteSearchScope,
makeEmptyPaletteSearchResult,
searchWorktreeDocumentsCooperatively,
searchWorktrees
} from './worktree-palette-search'
import { buildWorktreePaletteDocuments } from './worktree-palette-document'
import {
WORKTREE_PALETTE_QUERY_MAX_BYTES,
isWorktreePaletteQueryTooLarge
@@ -545,4 +547,49 @@ describe('worktree-palette-search', () => {
searchWorktrees([worktree], '300', repoMap, { workspacePortsByWorktreeId })
).toHaveLength(1)
})
it('cooperatively preserves complete source ordering', async () => {
const worktrees = Array.from({ length: 20 }, (_, index) =>
makeWorktree({ id: `wt-${index}`, displayName: `Needle ${index}` })
)
const yieldBetweenSlices = vi.fn(async () => {})
const results = await searchWorktreeDocumentsCooperatively(
{
worktrees,
query: 'needle',
documents: buildWorktreePaletteDocuments(worktrees, { repoMap }),
repoMap
},
{ timeSliceMs: 0, yieldBetweenSlices }
)
expect(results?.map((result) => result.worktreeId)).toEqual(
worktrees.map((worktree) => worktree.id)
)
expect(yieldBetweenSlices).toHaveBeenCalled()
})
it('drops a cooperative result when its generation is stale', async () => {
const worktrees = Array.from({ length: 20 }, (_, index) =>
makeWorktree({ id: `wt-${index}`, displayName: `Needle ${index}` })
)
let current = true
const results = await searchWorktreeDocumentsCooperatively(
{
worktrees,
query: 'needle',
documents: buildWorktreePaletteDocuments(worktrees, { repoMap }),
repoMap
},
{
timeSliceMs: 0,
shouldContinue: () => current,
yieldBetweenSlices: async () => {
current = false
}
}
)
expect(results).toBeNull()
})
})
+78 -23
View File
@@ -30,6 +30,7 @@ import {
matchWorktreePaletteTaskUrl,
parseCmdJTaskSourceUrl
} from './worktree-palette-task-url-match'
import { yieldToEventLoop } from '../../../shared/event-loop-yield'
export type { MatchRange }
@@ -162,6 +163,45 @@ export type WorktreePaletteSearchArgs = {
checksReviewByWorktree?: ReadonlyMap<Worktree, HostedReviewInfo | null>
}
async function yieldToPalettePaint(): Promise<void> {
if (typeof requestAnimationFrame !== 'function') {
await yieldToEventLoop()
return
}
await new Promise<void>((resolve) => {
requestAnimationFrame(() => {
void yieldToEventLoop().then(resolve)
})
})
}
function matchPreparedWorktree(
args: WorktreePaletteSearchArgs,
worktree: Worktree,
prepared: Extract<ReturnType<typeof preparePaletteQuery>, { state: 'ready' }>,
taskSourceUrl: ReturnType<typeof parseCmdJTaskSourceUrl>
): PaletteSearchResult | null {
if (taskSourceUrl) {
return matchWorktreePaletteTaskUrl({
worktree,
intent: taskSourceUrl,
repo: resolvePaletteRepoForWorktree(worktree, args.repoMap, args.repoMapByHostIdentity),
review: args.checksReviewByWorktree?.get(worktree)
})
}
const document = args.documents.get(getWorktreeHostIdentity(worktree))
if (!document) {
return null
}
const match = matchPaletteDocument({
document,
tokens: prepared.tokens,
normalizedQuery: prepared.normalized
})
return match ? toWorktreePaletteSearchResult(worktree.id, match, worktree.hostId) : null
}
/** Matches prepared documents; callers memoize `documents` across keystrokes. */
export function searchWorktreeDocuments(args: WorktreePaletteSearchArgs): PaletteSearchResult[] {
const prepared = preparePaletteQuery(args.query)
@@ -177,35 +217,50 @@ export function searchWorktreeDocuments(args: WorktreePaletteSearchArgs): Palett
const taskSourceUrl = parseCmdJTaskSourceUrl(args.query.trim())
const results: PaletteSearchResult[] = []
for (const worktree of args.worktrees) {
if (taskSourceUrl) {
const match = matchWorktreePaletteTaskUrl({
worktree,
intent: taskSourceUrl,
repo: resolvePaletteRepoForWorktree(worktree, args.repoMap, args.repoMapByHostIdentity),
review: args.checksReviewByWorktree?.get(worktree)
})
if (match) {
results.push(match)
}
continue
}
const document = args.documents.get(getWorktreeHostIdentity(worktree))
if (!document) {
continue
}
const match = matchPaletteDocument({
document,
tokens: prepared.tokens,
normalizedQuery: prepared.normalized
})
const match = matchPreparedWorktree(args, worktree, prepared, taskSourceUrl)
if (match) {
results.push(toWorktreePaletteSearchResult(worktree.id, match, worktree.hostId))
results.push(match)
}
}
return results
}
export async function searchWorktreeDocumentsCooperatively(
args: WorktreePaletteSearchArgs,
options: {
shouldContinue?: () => boolean
timeSliceMs?: number
yieldBetweenSlices?: () => Promise<void>
} = {}
): Promise<PaletteSearchResult[] | null> {
const prepared = preparePaletteQuery(args.query)
if (prepared.state !== 'ready' || parseCmdJTaskSourceUrl(args.query.trim())) {
return searchWorktreeDocuments(args)
}
const shouldContinue = options.shouldContinue ?? (() => true)
const timeSliceMs = options.timeSliceMs ?? 8
const yieldBetweenSlices = options.yieldBetweenSlices ?? yieldToPalettePaint
const results: PaletteSearchResult[] = []
let sliceStartedAt = performance.now()
for (const worktree of args.worktrees) {
if (!shouldContinue()) {
return null
}
const match = matchPreparedWorktree(args, worktree, prepared, null)
if (match) {
results.push(match)
}
if (performance.now() - sliceStartedAt < timeSliceMs) {
continue
}
await yieldBetweenSlices()
sliceStartedAt = performance.now()
}
return shouldContinue() ? results : null
}
/** Convenience entry point that prepares documents inline. */
export function searchWorktrees(
worktrees: readonly Worktree[],
@@ -0,0 +1,355 @@
import type { ElectronApplication, Page } from '@stablyai/playwright-test'
import { test, expect } from './helpers/orca-app'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import { PALETTE_INTERACTION_BUDGET } from '../../src/renderer/src/lib/palette-match/palette-match-budget'
const WORKSPACE_COUNT = 800
const {
openHandlerMs: MAX_OPEN_HANDLER_MS,
firstVisibleResultsMs: MAX_FIRST_VISIBLE_MS,
maxFrameGapMs: MAX_FRAME_GAP_MS
} = PALETTE_INTERACTION_BUDGET
const TARGET_QUERY = 'needle 0399'
const TARGET_LOCAL_NAME = 'Needle local 0399'
const TARGET_REMOTE_NAME = 'Needle remote 0399'
type InteractionMetrics = {
firstVisibleMs: number
handlerMs: number | null
maxFrameGapMs: number
visibleTexts: string[]
}
type CmdJPerformanceProbe = {
begin: (expectedTexts: string[]) => void
finish: () => InteractionMetrics | null
stop: () => void
}
type PerformanceProbeWindow = Window & { __cmdJPerformanceProbe?: CmdJPerformanceProbe }
async function seedAccumulatedWorkspaceCatalog(page: Page): Promise<void> {
await page.evaluate(
({ workspaceCount, targetLocalName, targetRemoteName }) => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
const now = Date.now()
const sharedRepoId = 'perf-repo'
const localRepo = {
id: sharedRepoId,
path: '/perf/orca',
displayName: 'acme/orca-local',
badgeColor: '#64748b',
addedAt: now,
kind: 'git' as const,
executionHostId: 'local' as const
}
const remoteRepo = {
...localRepo,
path: '/srv/perf/orca',
displayName: 'acme/orca-remote',
connectionId: 'perf-box',
executionHostId: 'ssh:perf-box' as const
}
const longComment =
'Blocked on the staging relay while the execution host reconnects; review the rollback runbook and deployment evidence before retrying. '.repeat(
6
)
const worktrees = Array.from({ length: workspaceCount }, (_, index) => {
const pairIndex = Math.floor(index / 2)
const suffix = String(pairIndex).padStart(4, '0')
const remote = index % 2 === 1
const hostId = remote ? ('ssh:perf-box' as const) : ('local' as const)
const displayName =
pairIndex === 399
? remote
? targetRemoteName
: targetLocalName
: `Accumulated ${remote ? 'remote' : 'local'} workspace ${suffix}`
return {
id: `${sharedRepoId}::/perf/workspace-${suffix}`,
instanceId: `perf-instance-${index}`,
repoId: sharedRepoId,
projectId: 'perf-project',
hostId,
projectHostSetupId: remote ? 'perf-ssh-setup' : 'perf-local-setup',
path: remote ? `/srv/perf/workspace-${suffix}` : `/perf/workspace-${suffix}`,
head: index.toString(16).padStart(40, 'a'),
branch: `refs/heads/perf/accumulated-${suffix}-${remote ? 'remote' : 'local'}`,
isBare: false,
isMainWorktree: pairIndex === 0,
displayName,
comment: `${longComment} Dataset row ${index}.`,
linkedIssue: 10_000 + index,
linkedPR: 20_000 + index,
linkedLinearIssue: `STA-${30_000 + index}`,
linkedWorkItem: {
provider: index % 4 === 0 ? ('gitlab' as const) : ('linear' as const),
type: index % 4 === 0 ? ('mr' as const) : ('issue' as const),
number: 30_000 + index,
title: `Rework the palette ranking pipeline for accumulated workspace ${index}`,
url:
index % 4 === 0
? `https://gitlab.example/acme/orca/-/merge_requests/${30_000 + index}`
: `https://linear.app/acme/issue/STA-${30_000 + index}`,
linearIdentifier: index % 4 === 0 ? undefined : `STA-${30_000 + index}`
},
automationProvenance: {
kind: 'created-by-automation' as const,
automationId: `perf-auto-${index % 8}`,
automationNameSnapshot: 'Nightly accumulated workspace review',
automationRunId: `perf-run-${index}`,
automationRunTitleSnapshot: `Scan daily sweep ${index}`,
createdAt: now - index * 60_000,
executionTargetType: remote ? ('ssh' as const) : ('local' as const),
executionTargetId: remote ? 'perf-box' : sharedRepoId,
projectId: 'perf-project',
repoId: sharedRepoId,
hostId
},
isArchived: false,
isUnread: index % 7 === 0,
isPinned: index % 19 === 0,
sortOrder: index,
lastActivityAt: now - (workspaceCount - index) * 1_000
}
})
const ports = worktrees.map((worktree, index) => ({
id: `perf-port-${index}`,
bindHost: '127.0.0.1',
connectHost: '127.0.0.1',
port: 3_000 + index,
pid: 40_000 + index,
processName: index % 2 === 0 ? 'node' : 'vite',
protocol: 'http' as const,
kind: 'workspace' as const,
owner: {
worktreeId: worktree.id,
repoId: worktree.repoId,
displayName: worktree.displayName,
path: worktree.path,
confidence: 'cwd' as const
}
}))
store.setState({
repos: [localRepo, remoteRepo],
activeRepoId: sharedRepoId,
worktreesByRepo: { [sharedRepoId]: worktrees },
activeWorktreeId: worktrees[0]!.id,
activeWorkspaceExecutionHostId: 'local',
tabsByWorktree: {},
unifiedTabsByWorktree: {},
browserTabsByWorktree: {},
browserPagesByWorkspace: {},
workspacePortScan: {
key: 'perf-800-workspaces',
result: { platform: 'darwin', scannedAt: now, ports }
},
showSleepingWorkspaces: true,
hideDefaultBranchWorkspace: false,
hideAutomationGeneratedWorkspaces: false,
hideCliCreatedWorkspaces: false,
hideDetachedHeadWorkspaces: false,
hideWorkspacesFromOtherDevices: false,
activeView: 'tasks',
sidebarOpen: false,
rightSidebarOpen: false,
activeModal: undefined
})
},
{
workspaceCount: WORKSPACE_COUNT,
targetLocalName: TARGET_LOCAL_NAME,
targetRemoteName: TARGET_REMOTE_NAME
}
)
}
async function installPerformanceProbe(page: Page): Promise<void> {
await page.evaluate(() => {
const store = window.__store
if (!store) {
throw new Error('window.__store is not available')
}
let actionStartedAt = 0
let expectedTexts: string[] = []
let firstVisibleAt: number | null = null
let completedMetrics: InteractionMetrics | null = null
let handlerMs: number | null = null
let maxFrameGapMs = 0
let previousFrameAt = performance.now()
let visibleTexts: string[] = []
let frameId = 0
const originalOpenModal = store.getState().openModal
store.setState({
openModal: (...args: Parameters<typeof originalOpenModal>) => {
const startedAt = performance.now()
originalOpenModal(...args)
if (args[0] === 'worktree-palette') {
handlerMs = performance.now() - startedAt
}
}
})
const sampleFrame = (now: number): void => {
if (actionStartedAt > 0 && completedMetrics === null) {
maxFrameGapMs = Math.max(maxFrameGapMs, now - previousFrameAt)
const items = [...document.querySelectorAll<HTMLElement>('[cmdk-item]')].filter(
(item) => item.getClientRects().length > 0
)
visibleTexts = items.map((item) => item.textContent?.replace(/\s+/g, ' ').trim() ?? '')
const hasExpectedRows =
items.length > 0 &&
expectedTexts.every((expected) => visibleTexts.some((text) => text.includes(expected)))
if (firstVisibleAt === null && hasExpectedRows) {
firstVisibleAt = now
completedMetrics = {
firstVisibleMs: firstVisibleAt - actionStartedAt,
handlerMs,
maxFrameGapMs,
visibleTexts
}
}
}
previousFrameAt = now
frameId = requestAnimationFrame(sampleFrame)
}
frameId = requestAnimationFrame(sampleFrame)
;(window as PerformanceProbeWindow).__cmdJPerformanceProbe = {
begin: (nextExpectedTexts) => {
actionStartedAt = performance.now()
previousFrameAt = actionStartedAt
expectedTexts = nextExpectedTexts
firstVisibleAt = null
completedMetrics = null
maxFrameGapMs = 0
visibleTexts = []
},
finish: () => completedMetrics,
stop: () => {
cancelAnimationFrame(frameId)
store.setState({ openModal: originalOpenModal })
}
}
})
}
async function beginProbe(page: Page, expectedTexts: string[]): Promise<void> {
await page.evaluate(
(texts) => (window as PerformanceProbeWindow).__cmdJPerformanceProbe?.begin(texts),
expectedTexts
)
}
async function readMetrics(page: Page): Promise<InteractionMetrics | null> {
return page.evaluate(
() => (window as PerformanceProbeWindow).__cmdJPerformanceProbe?.finish() ?? null
)
}
async function togglePaletteFromMain(electronApp: ElectronApplication): Promise<void> {
await electronApp.evaluate(({ BrowserWindow }) => {
const mainWindow = BrowserWindow.getAllWindows().find((window) => !window.isDestroyed())
if (!mainWindow) {
throw new Error('Orca main window is not available')
}
mainWindow.webContents.send('ui:toggleWorktreePalette')
})
}
test.describe('Cmd-J cold accumulated-workspace performance', () => {
test.beforeEach(async ({ orcaPage }) => {
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
})
test('paints complete host-qualified results without dropping a frame', async ({
electronApp,
orcaPage
}, testInfo) => {
const startupTiming = await orcaPage.evaluate(() => ({
rendererReadyMs: performance.now(),
firstContentfulPaintMs:
performance.getEntriesByName('first-contentful-paint')[0]?.startTime ?? null
}))
await seedAccumulatedWorkspaceCatalog(orcaPage)
await installPerformanceProbe(orcaPage)
await orcaPage.evaluate(
() =>
new Promise<void>((resolve) =>
requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
)
)
await beginProbe(orcaPage, [])
await togglePaletteFromMain(electronApp)
const dialog = orcaPage.getByRole('dialog', { name: 'Jump to...' })
await expect(dialog).toBeVisible()
await expect.poll(() => readMetrics(orcaPage), { timeout: 10_000 }).not.toBeNull()
const coldOpen = await readMetrics(orcaPage)
expect(coldOpen).not.toBeNull()
await beginProbe(orcaPage, [
TARGET_REMOTE_NAME,
TARGET_LOCAL_NAME,
`Create worktree "${TARGET_QUERY}"`
])
await dialog.getByPlaceholder(/Search chats, terminals, worktrees/).fill(TARGET_QUERY)
await expect.poll(() => readMetrics(orcaPage), { timeout: 10_000 }).not.toBeNull()
const immediateQuery = await readMetrics(orcaPage)
expect(immediateQuery).not.toBeNull()
const matchingRows = dialog
.locator('[cmdk-item]:has([data-slot="palette-worktree-name"])')
.filter({ hasText: 'Needle' })
await expect(matchingRows).toHaveCount(2)
const matchingTexts = (await matchingRows.allTextContents()).map((text) =>
text.replace(/\s+/g, ' ').trim()
)
expect(matchingTexts[0]).toContain(TARGET_REMOTE_NAME)
expect(matchingTexts[1]).toContain(TARGET_LOCAL_NAME)
await togglePaletteFromMain(electronApp)
await expect(dialog).not.toBeVisible()
await orcaPage.waitForTimeout(400)
await beginProbe(orcaPage, ['Accumulated remote workspace 0398'])
await togglePaletteFromMain(electronApp)
await expect(dialog).toBeVisible()
await expect.poll(() => readMetrics(orcaPage), { timeout: 10_000 }).not.toBeNull()
const warmReopen = await readMetrics(orcaPage)
expect(warmReopen).not.toBeNull()
const report = {
startupTiming,
coldOpen,
immediateQuery,
warmReopen,
workspaceCount: WORKSPACE_COUNT
}
await testInfo.attach('cmd-j-cold-open-metrics.json', {
body: Buffer.from(`${JSON.stringify(report, null, 2)}\n`),
contentType: 'application/json'
})
await testInfo.attach('cmd-j-cold-open-visible.png', {
body: await orcaPage.screenshot(),
contentType: 'image/png'
})
console.log(`[cmd-j-cold-open] ${JSON.stringify(report)}`)
expect(coldOpen!.handlerMs).not.toBeNull()
expect(coldOpen!.handlerMs!).toBeLessThanOrEqual(MAX_OPEN_HANDLER_MS)
expect(coldOpen!.firstVisibleMs).toBeLessThanOrEqual(MAX_FIRST_VISIBLE_MS)
expect(coldOpen!.maxFrameGapMs).toBeLessThanOrEqual(MAX_FRAME_GAP_MS)
expect(immediateQuery!.firstVisibleMs).toBeLessThanOrEqual(MAX_FIRST_VISIBLE_MS)
expect(immediateQuery!.maxFrameGapMs).toBeLessThanOrEqual(MAX_FRAME_GAP_MS)
expect(warmReopen!.firstVisibleMs).toBeLessThanOrEqual(MAX_FIRST_VISIBLE_MS)
expect(warmReopen!.maxFrameGapMs).toBeLessThanOrEqual(MAX_FRAME_GAP_MS)
await orcaPage.evaluate(() => (window as PerformanceProbeWindow).__cmdJPerformanceProbe?.stop())
})
})