diff --git a/packages/agent-intelligence/src/services/orchestrator/OrchestrationState.ts b/packages/agent-intelligence/src/services/orchestrator/OrchestrationState.ts index c8734e83..650a4240 100644 --- a/packages/agent-intelligence/src/services/orchestrator/OrchestrationState.ts +++ b/packages/agent-intelligence/src/services/orchestrator/OrchestrationState.ts @@ -70,7 +70,6 @@ export class OrchestrationStateManager { }; const nodes = this.buildDependencyGraph(); - const taskMap = new Map([...this.state.taskQueue, ...this.state.completedTasks].map((t) => [t.id, t])); for (const [taskId, node] of nodes) { if (node.status === 'complete') continue; @@ -86,14 +85,14 @@ export class OrchestrationStateManager { if (pendingDeps.length === 0) { // Check if task has an assignee - const task = taskMap.get(taskId); + const task = this.state.taskQueue.find((t) => t.id === taskId); if (task?.assignee) { plan.ready.push(taskId); } } else { // Check for failed dependencies const failedDeps = pendingDeps.filter((depId) => { - const task = taskMap.get(depId); + const task = this.state.taskQueue.find((t) => t.id === depId); return task?.status === 'cancelled'; }); diff --git a/src/components/code/DiffViewer.tsx b/src/components/code/DiffViewer.tsx index 972919b2..3164f111 100644 --- a/src/components/code/DiffViewer.tsx +++ b/src/components/code/DiffViewer.tsx @@ -5,7 +5,7 @@ * Supports unified and split view modes. */ -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { ChevronDownIcon } from '@/components/icons'; import { Text } from '@/components/ui'; import { organicBorderRadius } from '@/lib/organic-styles'; @@ -95,9 +95,12 @@ export function DiffViewer({ }: Readonly) { const [collapsed, setCollapsed] = useState(false); - const lines = diff || (oldContent && newContent ? parseDiff(oldContent, newContent) : []); - const additions = lines.filter((l) => l.type === 'add').length; - const deletions = lines.filter((l) => l.type === 'remove').length; + const lines = useMemo( + () => diff || (oldContent && newContent ? parseDiff(oldContent, newContent) : []), + [diff, oldContent, newContent] + ); + const additions = useMemo(() => lines.filter((l) => l.type === 'add').length, [lines]); + const deletions = useMemo(() => lines.filter((l) => l.type === 'remove').length, [lines]); const getLineStyle = (type: DiffLine['type']) => { switch (type) { diff --git a/src/components/code/__tests__/DiffViewer.perf.test.tsx b/src/components/code/__tests__/DiffViewer.perf.test.tsx new file mode 100644 index 00000000..ef65bae6 --- /dev/null +++ b/src/components/code/__tests__/DiffViewer.perf.test.tsx @@ -0,0 +1,50 @@ +import { act, fireEvent, render, screen } from '@testing-library/react'; +import { DiffViewer } from '../DiffViewer'; + +// Mock dependencies to isolate DiffViewer performance +vi.mock('@/components/icons', () => ({ + ChevronDownIcon: () => ChevronDown, +})); + +vi.mock('@/components/ui', () => ({ + // biome-ignore lint/suspicious/noExplicitAny: mock + Text: ({ children, ...props }: any) => {children}, +})); + +vi.mock('@/lib/organic-styles', () => ({ + organicBorderRadius: { card: {} }, +})); + +describe('DiffViewer Performance', () => { + it('measures re-render time with large diffs', async () => { + // Generate large content (~5000 lines) to make parsing expensive + const linesCount = 5000; + const oldContent = Array.from({ length: linesCount }, (_, i) => `original line ${i}`).join( + '\n' + ); + const newContent = Array.from({ length: linesCount }, (_, i) => `modified line ${i}`).join( + '\n' + ); + + // Initial Render + const startRender = performance.now(); + render(); + const endRender = performance.now(); + console.log(`[Benchmark] Initial render time: ${(endRender - startRender).toFixed(2)}ms`); + + const button = screen.getByRole('button'); + + // Trigger re-render by collapsing + const startUpdate = performance.now(); + await act(async () => { + fireEvent.click(button); + }); + const endUpdate = performance.now(); + + const updateTime = endUpdate - startUpdate; + console.log(`[Benchmark] Re-render time (collapse): ${updateTime.toFixed(2)}ms`); + + // Sanity check + expect(updateTime).toBeGreaterThan(0); + }, 10000); +});