agent-ecosystem/src/renderer/components/layout/PaneResizeHandle.tsx
Artem Rootman da58917032
perf: offload heavy I/O to worker thread, reduce renderer re-renders
Main process — worker thread for team data:
- New team-data-worker thread handles getTeamData and findLogsForTask,
  isolating heavy file I/O (scanning 300+ subagent JSONL files) from
  Electron's main event loop. getTeamData dropped from ~2000ms on the
  main thread to ~110ms via the worker.
- Worker-side dedup and 10s result cache for findLogsForTask prevents
  redundant scans when the same task is queried multiple times.
- Discovery cache TTL raised from 5s to 30s — avoids re-scanning the
  entire project directory on every call.
- Message cap at 200 in TeamDataService to keep IPC payloads under 1MB
  (was sending 2200+ messages / ~3MB, stalling Chromium IPC serialization).
- IPC handlers fall back to main-thread execution if the worker is
  unavailable (graceful degradation).

Renderer — useShallow and memoization (55 files):
- Added useShallow to store selectors across 55 renderer files. Batched
  individual useStore() calls (e.g. 17 calls in ExtensionStoreView,
  10 in ConnectionSection) into single useShallow selectors, cutting
  unnecessary re-render checks on every store update.
- MemberLogsTab: three 5-second polling intervals now pause when the
  parent tab is hidden (display:none). Previously 5 hidden tabs × 3
  intervals = 15 polling timers firing continuously.
- KanbanColumn wrapped in React.memo to skip re-renders when props
  haven't changed.
- MemberList: memoized activeMembers/removedMembers/colorMap; replaced
  O(n×m) per-member task scan with a pre-computed reviewer map.
- Bounded timer Maps in store initialization to prevent unbounded growth
  of debounce/throttle tracking maps during long sessions.
2026-04-05 16:21:05 +00:00

85 lines
2.8 KiB
TypeScript

/**
* PaneResizeHandle - Draggable divider between adjacent panes.
* Uses the same mouse-event pattern as Sidebar.tsx for resize.
*/
import { useCallback, useEffect, useState } from 'react';
import { useStore } from '@renderer/store';
import { useShallow } from 'zustand/react/shallow';
interface PaneResizeHandleProps {
leftPaneId: string;
rightPaneId: string;
}
export const PaneResizeHandle = ({ leftPaneId }: PaneResizeHandleProps): React.JSX.Element => {
const [isResizing, setIsResizing] = useState(false);
const resizePanes = useStore((s) => s.resizePanes);
const paneLayout = useStore(useShallow((s) => s.paneLayout));
const handleMouseMove = useCallback(
(e: MouseEvent) => {
if (!isResizing) return;
// Calculate the new width fraction based on mouse position relative to container
const container = document.getElementById('pane-container');
if (!container) return;
const containerRect = container.getBoundingClientRect();
const relativeX = e.clientX - containerRect.left;
const newFraction = relativeX / containerRect.width;
// Calculate the cumulative width of all panes before the left pane
const leftPaneIndex = paneLayout.panes.findIndex((p) => p.id === leftPaneId);
if (leftPaneIndex === -1) return;
let cumulativeWidth = 0;
for (let i = 0; i < leftPaneIndex; i++) {
cumulativeWidth += paneLayout.panes[i].widthFraction;
}
const leftPaneNewWidth = newFraction - cumulativeWidth;
resizePanes(leftPaneId, leftPaneNewWidth);
},
[isResizing, leftPaneId, paneLayout.panes, resizePanes]
);
const handleMouseUp = useCallback(() => {
setIsResizing(false);
}, []);
useEffect(() => {
if (isResizing) {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
}
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
}, [isResizing, handleMouseMove, handleMouseUp]);
const handleMouseDown = (e: React.MouseEvent): void => {
e.preventDefault();
setIsResizing(true);
};
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions -- resize handle requires mouse interaction
<div
className={`flex w-1 shrink-0 cursor-col-resize items-center justify-center transition-colors hover:bg-blue-500/50 ${
isResizing ? 'bg-blue-500/50' : ''
}`}
style={{
backgroundColor: isResizing ? undefined : 'var(--color-border)',
}}
onMouseDown={handleMouseDown}
/>
);
};