Skip to content

Commit 6d7c3b4

Browse files
authored
Fix diff refresh loop and idle work
Stabilize diff refreshes, preserve visible diff content during soft refreshes, and reduce inactive resource/browser work.
1 parent d07be21 commit 6d7c3b4

7 files changed

Lines changed: 295 additions & 224 deletions

File tree

frontend/src/components/SessionView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ export const SessionView = memo(() => {
973973
.filter(p => !defaultTerminalPanel || p.id !== defaultTerminalPanel.id)
974974
.map(panel => {
975975
const isActive = panel.id === currentActivePanel.id;
976-
const shouldKeepAlive = ['terminal', 'browser'].includes(panel.type);
976+
const shouldKeepAlive = ['terminal'].includes(panel.type);
977977
if (!isActive && !shouldKeepAlive) return null;
978978
return (
979979
<div
@@ -1116,7 +1116,7 @@ export const SessionView = memo(() => {
11161116
.filter(p => !defaultTerminalPanel || p.id !== defaultTerminalPanel.id)
11171117
.map(panel => {
11181118
const isActive = panel.id === currentActivePanel.id;
1119-
const shouldKeepAlive = ['terminal', 'browser'].includes(panel.type);
1119+
const shouldKeepAlive = ['terminal'].includes(panel.type);
11201120
if (!isActive && !shouldKeepAlive) return null;
11211121
return (
11221122
<div

frontend/src/components/panels/PanelTabBar.tsx

Lines changed: 95 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
9090
const resourceChipRef = useRef<HTMLButtonElement>(null);
9191
const resourcePopoverRef = useRef<HTMLDivElement>(null);
9292
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set(['pane-app']));
93-
const { snapshot, startActive, stopActive, refresh } = useResourceMonitor();
93+
const { snapshot, isLoading: resourceLoading, startActive, stopActive, refresh } = useResourceMonitor();
9494
const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
9595

9696
const getPanelActivityStatus = usePanelStore(s => s.getPanelActivityStatus);
@@ -150,12 +150,16 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
150150

151151
// Resource monitor handlers
152152
const toggleResourcePopover = useCallback(() => {
153-
setShowResourcePopover(prev => {
154-
if (!prev) startActive();
155-
else stopActive();
156-
return !prev;
157-
});
158-
}, [startActive, stopActive]);
153+
if (showResourcePopover) {
154+
setShowResourcePopover(false);
155+
stopActive();
156+
return;
157+
}
158+
159+
setShowResourcePopover(true);
160+
void refresh();
161+
startActive();
162+
}, [showResourcePopover, refresh, startActive, stopActive]);
159163

160164
// Popover positioning
161165
useEffect(() => {
@@ -230,7 +234,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
230234
});
231235
}, []);
232236

233-
const handleRefresh = useCallback(() => { refresh(); }, [refresh]);
237+
const handleRefresh = useCallback(() => { void refresh(); }, [refresh]);
234238

235239
const electronTotalCpu = useMemo(() =>
236240
snapshot?.electronProcesses.reduce((sum, p) => sum + p.cpuPercent, 0) ?? 0
@@ -910,7 +914,7 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
910914
</div>
911915

912916
{/* Resource monitor popover */}
913-
{showResourcePopover && snapshot && createPortal(
917+
{showResourcePopover && createPortal(
914918
<div
915919
ref={resourcePopoverRef}
916920
className="bg-surface-primary border border-border-subtle/60 rounded-lg shadow-dropdown-elevated backdrop-blur-sm animate-dropdown-enter overflow-hidden w-[320px]"
@@ -924,90 +928,99 @@ export const PanelTabBar: React.FC<PanelTabBarProps> = memo(({
924928
<button
925929
onClick={handleRefresh}
926930
className="p-1 rounded text-text-tertiary hover:text-text-primary hover:bg-surface-hover transition-colors"
931+
disabled={resourceLoading}
927932
>
928-
<RefreshCw className="w-3.5 h-3.5" />
933+
<RefreshCw className={cn("w-3.5 h-3.5", resourceLoading && "animate-spin")} />
929934
</button>
930935
</div>
931936

932-
{/* Summary */}
933-
<div className="flex items-center gap-4 px-3 py-2 border-b border-border-secondary">
934-
<span className="text-sm text-text-secondary">
935-
CPU <strong className="text-text-primary">{snapshot.cpuReady ? `${snapshot.totalCpuPercent.toFixed(1)}%` : '—'}</strong>
936-
</span>
937-
<span className="text-sm text-text-secondary">
938-
Memory <strong className="text-text-primary">{formatMemory(snapshot.totalMemoryMB)}</strong>
939-
</span>
940-
</div>
937+
{!snapshot ? (
938+
<div className="px-3 py-4 text-sm text-text-secondary">
939+
{resourceLoading ? 'Loading resource usage...' : 'No resource snapshot yet.'}
940+
</div>
941+
) : (
942+
<>
943+
{/* Summary */}
944+
<div className="flex items-center gap-4 px-3 py-2 border-b border-border-secondary">
945+
<span className="text-sm text-text-secondary">
946+
CPU <strong className="text-text-primary">{snapshot.cpuReady ? `${snapshot.totalCpuPercent.toFixed(1)}%` : '—'}</strong>
947+
</span>
948+
<span className="text-sm text-text-secondary">
949+
Memory <strong className="text-text-primary">{formatMemory(snapshot.totalMemoryMB)}</strong>
950+
</span>
951+
</div>
941952

942-
{/* Scrollable content */}
943-
<div className="max-h-[400px] overflow-y-auto">
944-
{/* Pane App section */}
945-
<div className="border-b border-border-secondary">
946-
<button
947-
onClick={() => toggleSection('pane-app')}
948-
className="flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
949-
>
950-
<div className="flex items-center gap-1.5">
951-
{expandedSections.has('pane-app')
952-
? <ChevronDown className="w-3 h-3 text-text-quaternary" />
953-
: <ChevronRight className="w-3 h-3 text-text-quaternary" />}
954-
<span className="text-sm font-medium text-text-primary">Pane App</span>
955-
</div>
956-
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono">
957-
<span>{snapshot.cpuReady ? `${electronTotalCpu.toFixed(1)}%` : '—'}</span>
958-
<span>{formatMemory(electronTotalMem)}</span>
959-
</div>
960-
</button>
961-
{expandedSections.has('pane-app') && snapshot.electronProcesses.map(p => (
962-
<div key={p.pid} className="flex items-center justify-between px-3 py-1 pl-8">
963-
<span className="text-xs text-text-secondary">{p.label}</span>
964-
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono">
965-
<span>{snapshot.cpuReady ? `${p.cpuPercent.toFixed(1)}%` : '—'}</span>
966-
<span>{formatMemory(p.memoryMB)}</span>
967-
</div>
953+
{/* Scrollable content */}
954+
<div className="max-h-[400px] overflow-y-auto">
955+
{/* Pane App section */}
956+
<div className="border-b border-border-secondary">
957+
<button
958+
onClick={() => toggleSection('pane-app')}
959+
className="flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
960+
>
961+
<div className="flex items-center gap-1.5">
962+
{expandedSections.has('pane-app')
963+
? <ChevronDown className="w-3 h-3 text-text-quaternary" />
964+
: <ChevronRight className="w-3 h-3 text-text-quaternary" />}
965+
<span className="text-sm font-medium text-text-primary">Pane App</span>
966+
</div>
967+
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono">
968+
<span>{snapshot.cpuReady ? `${electronTotalCpu.toFixed(1)}%` : '—'}</span>
969+
<span>{formatMemory(electronTotalMem)}</span>
970+
</div>
971+
</button>
972+
{expandedSections.has('pane-app') && snapshot.electronProcesses.map(p => (
973+
<div key={p.pid} className="flex items-center justify-between px-3 py-1 pl-8">
974+
<span className="text-xs text-text-secondary">{p.label}</span>
975+
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono">
976+
<span>{snapshot.cpuReady ? `${p.cpuPercent.toFixed(1)}%` : '—'}</span>
977+
<span>{formatMemory(p.memoryMB)}</span>
978+
</div>
979+
</div>
980+
))}
968981
</div>
969-
))}
970-
</div>
971982

972-
{/* Per-session sections */}
973-
{snapshot.sessions.map(sess => (
974-
<div
975-
key={sess.sessionId}
976-
className={cn(
977-
"border-b border-border-secondary",
978-
sess.sessionId === session?.id && "bg-interactive/5"
979-
)}
980-
>
981-
<button
982-
onClick={() => toggleSection(sess.sessionId)}
983-
className="flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
984-
>
985-
<div className="flex items-center gap-1.5 min-w-0">
986-
{expandedSections.has(sess.sessionId)
987-
? <ChevronDown className="w-3 h-3 text-text-quaternary flex-shrink-0" />
988-
: <ChevronRight className="w-3 h-3 text-text-quaternary flex-shrink-0" />}
989-
{sess.sessionId === session?.id && (
990-
<div className="w-1.5 h-1.5 rounded-full bg-interactive flex-shrink-0" />
983+
{/* Per-session sections */}
984+
{snapshot.sessions.map(sess => (
985+
<div
986+
key={sess.sessionId}
987+
className={cn(
988+
"border-b border-border-secondary",
989+
sess.sessionId === session?.id && "bg-interactive/5"
991990
)}
992-
<span className="text-sm font-medium text-text-primary truncate">{sess.sessionName}</span>
993-
</div>
994-
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2">
995-
<span>{snapshot.cpuReady ? `${sess.totalCpuPercent.toFixed(1)}%` : '—'}</span>
996-
<span>{formatMemory(sess.totalMemoryMB)}</span>
997-
</div>
998-
</button>
999-
{expandedSections.has(sess.sessionId) && sess.children.map(child => (
1000-
<div key={child.pid} className="flex items-center justify-between px-3 py-1 pl-8">
1001-
<span className="text-xs text-text-secondary truncate">{child.name}</span>
1002-
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2">
1003-
<span>{snapshot.cpuReady ? `${child.cpuPercent.toFixed(1)}%` : '—'}</span>
1004-
<span>{formatMemory(child.memoryMB)}</span>
1005-
</div>
991+
>
992+
<button
993+
onClick={() => toggleSection(sess.sessionId)}
994+
className="flex items-center justify-between w-full px-3 py-1.5 hover:bg-surface-hover transition-colors"
995+
>
996+
<div className="flex items-center gap-1.5 min-w-0">
997+
{expandedSections.has(sess.sessionId)
998+
? <ChevronDown className="w-3 h-3 text-text-quaternary flex-shrink-0" />
999+
: <ChevronRight className="w-3 h-3 text-text-quaternary flex-shrink-0" />}
1000+
{sess.sessionId === session?.id && (
1001+
<div className="w-1.5 h-1.5 rounded-full bg-interactive flex-shrink-0" />
1002+
)}
1003+
<span className="text-sm font-medium text-text-primary truncate">{sess.sessionName}</span>
1004+
</div>
1005+
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2">
1006+
<span>{snapshot.cpuReady ? `${sess.totalCpuPercent.toFixed(1)}%` : '—'}</span>
1007+
<span>{formatMemory(sess.totalMemoryMB)}</span>
1008+
</div>
1009+
</button>
1010+
{expandedSections.has(sess.sessionId) && sess.children.map(child => (
1011+
<div key={child.pid} className="flex items-center justify-between px-3 py-1 pl-8">
1012+
<span className="text-xs text-text-secondary truncate">{child.name}</span>
1013+
<div className="flex items-center gap-3 text-xs text-text-tertiary font-mono flex-shrink-0 ml-2">
1014+
<span>{snapshot.cpuReady ? `${child.cpuPercent.toFixed(1)}%` : '—'}</span>
1015+
<span>{formatMemory(child.memoryMB)}</span>
1016+
</div>
1017+
</div>
1018+
))}
10061019
</div>
10071020
))}
10081021
</div>
1009-
))}
1010-
</div>
1022+
</>
1023+
)}
10111024
</div>,
10121025
document.body
10131026
)}

0 commit comments

Comments
 (0)