perf(renderer): bail out of identity-equal terminal layout and cache-timer writes

setCacheTimerStartedAt and setTabLayout spread a fresh object and returned it
unconditionally, so every redundant call published a new AppState and ran every
zustand subscriber's selector across all mounted panes. Both have a real
redundant cadence: parked-terminal-byte-watcher writes a null cache timer on
each agent working/exit/stale-title transition, and TerminalPane re-persists an
identical layout on pane-title churn.

Extract the existing terminalLayoutEqual comparator out of web-session-tabs-sync
into a shared module and use it to gate the layout write, and dedupe the
remote-runtime layout IPC against the last snapshot pushed per tab.
This commit is contained in:
Brennan Benson
2026-08-03 21:12:17 -07:00
parent 9a97e737f5
commit 5d4e6b9511
7 changed files with 498 additions and 81 deletions
@@ -136,8 +136,7 @@ import {
} from '@/runtime/runtime-terminal-inspection'
import {
clearWebRuntimeTerminalBuffer,
closeWebRuntimeTerminal,
updateWebRuntimePaneLayout
closeWebRuntimeTerminal
} from '@/runtime/web-runtime-session'
import {
armPrimarySelectionNativePasteSuppression,
@@ -158,6 +157,10 @@ import {
planTerminalLiveLayoutInsertions
} from './terminal-live-layout-reconciliation'
import type { TerminalQuickCommand, TerminalQuickCommandScope } from '../../../../shared/types'
import {
createRemotePaneLayoutPusher,
type RemotePaneLayoutPusher
} from './remote-pane-layout-push'
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
import { isRuntimeOwnedSshTargetId } from '../../../../shared/execution-host'
import { getRepoIdFromWorktreeId } from '../../../../shared/worktree-id'
@@ -512,6 +515,8 @@ function TerminalPane(
paneTitlesRef.current = paneTitles
const removedTitleLeafIdsRef = useRef<Set<string>>(new Set())
const clearedScrollbackLeafIdsRef = useRef<Set<string>>(new Set())
const remotePaneLayoutPusherRef = useRef<RemotePaneLayoutPusher | null>(null)
remotePaneLayoutPusherRef.current ??= createRemotePaneLayoutPusher()
const [paneTitleOverlayRects, setPaneTitleOverlayRects] = useState<
Record<number, PaneTitleOverlayRect>
>({})
@@ -1057,19 +1062,21 @@ function TerminalPane(
(ptyId) => typeof ptyId === 'string' && isRemoteRuntimePtyId(ptyId)
)
if (hasRemotePane) {
void updateWebRuntimePaneLayout({
worktreeId,
tabId,
root: layout.root,
expandedLeafId: layout.expandedLeafId,
...(layout.titlesByLeafId ? { titlesByLeafId: layout.titlesByLeafId } : {})
})
remotePaneLayoutPusherRef.current?.push({ worktreeId, tabId, layout })
}
for (const leafId of currentLeafIds) {
clearedScrollbackLeafIds.delete(leafId)
}
}, [tabId, setTabLayout, worktreeId])
// Why: the dedupe cache mirrors what one host holds for one tab, so drop it when either changes or the pane unmounts.
useEffect(() => {
const pusher = remotePaneLayoutPusherRef.current
return () => {
pusher?.reset()
}
}, [tabId, worktreeId])
const clearPaneScrollback = useCallback(
(pane: ManagedPane): void => {
clearedScrollbackLeafIdsRef.current.add(pane.leafId)
@@ -0,0 +1,135 @@
/**
* Perf regression: layout persists fire on pane-title churn, and every one of
* them used to push a remote-runtime IPC round trip regardless of whether the
* host-visible layout had moved. Counting invocations at the mocked IPC boundary
* pins the before/after: 100 persists with an unchanged layout cost 100 pushes
* before the dedupe and 1 after.
*/
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { TerminalLayoutSnapshot } from '../../../../shared/types'
const updateWebRuntimePaneLayout = vi.fn()
vi.mock('@/runtime/web-runtime-session', () => ({
updateWebRuntimePaneLayout: (...args: unknown[]) => updateWebRuntimePaneLayout(...args)
}))
const { createRemotePaneLayoutPusher } = await import('./remote-pane-layout-push')
const PERSISTS = 100
function makeLayout(overrides: Partial<TerminalLayoutSnapshot> = {}): TerminalLayoutSnapshot {
return {
root: {
type: 'split',
direction: 'vertical',
ratio: 0.5,
first: { type: 'leaf', leafId: 'leaf-a' },
second: { type: 'leaf', leafId: 'leaf-b' }
},
activeLeafId: 'leaf-a',
expandedLeafId: null,
ptyIdsByLeafId: { 'leaf-a': 'remote:pty-a', 'leaf-b': 'remote:pty-b' },
titlesByLeafId: { 'leaf-a': 'build' },
...overrides
}
}
describe('createRemotePaneLayoutPusher', () => {
beforeEach(() => {
updateWebRuntimePaneLayout.mockClear()
})
it('pushes once across 100 persists of an unchanged layout', () => {
const pusher = createRemotePaneLayoutPusher()
for (let i = 0; i < PERSISTS; i += 1) {
// Fresh object each time: persistLayoutSnapshot re-serializes on every call.
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
}
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(1)
})
it('sends the same payload the un-deduped path sent', () => {
const pusher = createRemotePaneLayoutPusher()
const layout = makeLayout()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout })
expect(updateWebRuntimePaneLayout).toHaveBeenCalledWith({
worktreeId: 'wt-1',
tabId: 'tab-1',
root: layout.root,
expandedLeafId: layout.expandedLeafId,
titlesByLeafId: layout.titlesByLeafId
})
})
it('omits titlesByLeafId when the layout carries no titles', () => {
const pusher = createRemotePaneLayoutPusher()
pusher.push({
worktreeId: 'wt-1',
tabId: 'tab-1',
layout: makeLayout({ titlesByLeafId: undefined })
})
expect(updateWebRuntimePaneLayout.mock.calls[0][0]).not.toHaveProperty('titlesByLeafId')
})
it('pushes again for every host-visible change', () => {
const pusher = createRemotePaneLayoutPusher()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
pusher.push({
worktreeId: 'wt-1',
tabId: 'tab-1',
layout: makeLayout({ expandedLeafId: 'leaf-a' })
})
pusher.push({
worktreeId: 'wt-1',
tabId: 'tab-1',
layout: makeLayout({
expandedLeafId: 'leaf-a',
titlesByLeafId: { 'leaf-a': 'test' }
})
})
pusher.push({
worktreeId: 'wt-1',
tabId: 'tab-1',
layout: makeLayout({
expandedLeafId: 'leaf-a',
titlesByLeafId: { 'leaf-a': 'test' },
root: {
type: 'split',
direction: 'vertical',
ratio: 0.7,
first: { type: 'leaf', leafId: 'leaf-a' },
second: { type: 'leaf', leafId: 'leaf-b' }
}
})
})
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(4)
})
it('pushes again when a remote pane swaps its pty', () => {
// ptyIdsByLeafId is not in the payload, but a swap means a different host session.
const pusher = createRemotePaneLayoutPusher()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
pusher.push({
worktreeId: 'wt-1',
tabId: 'tab-1',
layout: makeLayout({ ptyIdsByLeafId: { 'leaf-a': 'remote:pty-c', 'leaf-b': 'remote:pty-b' } })
})
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(2)
})
it('does not carry a cached layout across tabs', () => {
const pusher = createRemotePaneLayoutPusher()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-2', layout: makeLayout() })
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(3)
})
it('re-pushes after reset so a remount re-establishes host geometry', () => {
const pusher = createRemotePaneLayoutPusher()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
pusher.reset()
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(2)
})
})
@@ -0,0 +1,35 @@
import type { TerminalLayoutSnapshot } from '../../../../shared/types'
import { terminalLayoutEqual } from '@/lib/terminal-layout-equality'
import { updateWebRuntimePaneLayout } from '@/runtime/web-runtime-session'
export type RemotePaneLayoutPusher = {
push: (input: { worktreeId: string; tabId: string; layout: TerminalLayoutSnapshot }) => void
reset: () => void
}
/**
* Pane geometry is host-authoritative for remote tabs, so persists must push it — but
* persists also fire on pane-title churn, which leaves the host-visible layout untouched.
* Dedupe against the last push so unchanged layouts cost no remote round trip.
*/
export function createRemotePaneLayoutPusher(): RemotePaneLayoutPusher {
let lastPushed: { tabId: string; snapshot: TerminalLayoutSnapshot } | null = null
return {
push: ({ worktreeId, tabId, layout }) => {
if (lastPushed?.tabId === tabId && terminalLayoutEqual(lastPushed.snapshot, layout)) {
return
}
lastPushed = { tabId, snapshot: layout }
void updateWebRuntimePaneLayout({
worktreeId,
tabId,
root: layout.root,
expandedLeafId: layout.expandedLeafId,
...(layout.titlesByLeafId ? { titlesByLeafId: layout.titlesByLeafId } : {})
})
},
reset: () => {
lastPushed = null
}
}
}
@@ -0,0 +1,55 @@
import type { TerminalLayoutSnapshot, TerminalPaneLayoutNode } from '../../../shared/types'
function sameStringRecord(
a: Readonly<Record<string, string>> | undefined,
b: Readonly<Record<string, string>> | undefined
): boolean {
const left = a ?? {}
const right = b ?? {}
const leftKeys = Object.keys(left)
const rightKeys = Object.keys(right)
return (
leftKeys.length === rightKeys.length &&
leftKeys.every(
(key) => Object.prototype.hasOwnProperty.call(right, key) && left[key] === right[key]
)
)
}
export function terminalLayoutNodeEqual(
a: TerminalPaneLayoutNode | null | undefined,
b: TerminalPaneLayoutNode | null | undefined
): boolean {
if (!a || !b) {
return !a && !b
}
if (a.type !== b.type) {
return false
}
if (a.type === 'leaf') {
return b.type === 'leaf' && a.leafId === b.leafId
}
return (
b.type === 'split' &&
a.direction === b.direction &&
a.ratio === b.ratio &&
terminalLayoutNodeEqual(a.first, b.first) &&
terminalLayoutNodeEqual(a.second, b.second)
)
}
/** Structural equality over every persisted layout field; drives store/IPC write bailouts. */
export function terminalLayoutEqual(
a: TerminalLayoutSnapshot | undefined,
b: TerminalLayoutSnapshot
): boolean {
return (
terminalLayoutNodeEqual(a?.root, b.root) &&
(a?.activeLeafId ?? null) === b.activeLeafId &&
(a?.expandedLeafId ?? null) === b.expandedLeafId &&
sameStringRecord(a?.ptyIdsByLeafId, b.ptyIdsByLeafId) &&
sameStringRecord(a?.buffersByLeafId, b.buffersByLeafId) &&
sameStringRecord(a?.scrollbackRefsByLeafId, b.scrollbackRefsByLeafId) &&
sameStringRecord(a?.titlesByLeafId, b.titlesByLeafId)
)
}
@@ -25,13 +25,13 @@ import type {
TabGroup,
TabGroupLayoutNode,
TerminalLayoutSnapshot,
TerminalPaneLayoutNode,
TerminalTab
} from '../../../shared/types'
import type { OpenFile } from '../store/slices/editor'
import { isTerminalLeafId, makePaneKey, parsePaneKey } from '../../../shared/stable-pane-id'
import { getRemoteRuntimePtyEnvironmentId, toRemoteRuntimePtyId } from './runtime-terminal-stream'
import { sanitizeTerminalLayoutPaneTitlesForLabels } from '@/lib/terminal-pane-title-sanitization'
import { terminalLayoutEqual } from '@/lib/terminal-layout-equality'
import { normalizeTerminalLayoutPtyOwnership } from '@/components/terminal-pane/terminal-layout-pty-ownership'
import { getExplicitRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner'
import { getReachableRuntimeSessionMirrorTargets } from '@/lib/runtime-session-mirror-targets'
@@ -1424,59 +1424,6 @@ function isMirroredCommandCodeTurnBump(
)
}
function sameStringRecord(
a: Readonly<Record<string, string>> | undefined,
b: Readonly<Record<string, string>> | undefined
): boolean {
const left = a ?? {}
const right = b ?? {}
const leftKeys = Object.keys(left)
const rightKeys = Object.keys(right)
return (
leftKeys.length === rightKeys.length &&
leftKeys.every(
(key) => Object.prototype.hasOwnProperty.call(right, key) && left[key] === right[key]
)
)
}
function terminalLayoutNodeEqual(
a: TerminalPaneLayoutNode | null | undefined,
b: TerminalPaneLayoutNode | null | undefined
): boolean {
if (!a || !b) {
return !a && !b
}
if (a.type !== b.type) {
return false
}
if (a.type === 'leaf') {
return b.type === 'leaf' && a.leafId === b.leafId
}
return (
b.type === 'split' &&
a.direction === b.direction &&
a.ratio === b.ratio &&
terminalLayoutNodeEqual(a.first, b.first) &&
terminalLayoutNodeEqual(a.second, b.second)
)
}
function terminalLayoutEqual(
a: TerminalLayoutSnapshot | undefined,
b: TerminalLayoutSnapshot
): boolean {
return (
terminalLayoutNodeEqual(a?.root, b.root) &&
(a?.activeLeafId ?? null) === b.activeLeafId &&
(a?.expandedLeafId ?? null) === b.expandedLeafId &&
sameStringRecord(a?.ptyIdsByLeafId, b.ptyIdsByLeafId) &&
sameStringRecord(a?.buffersByLeafId, b.buffersByLeafId) &&
sameStringRecord(a?.scrollbackRefsByLeafId, b.scrollbackRefsByLeafId) &&
sameStringRecord(a?.titlesByLeafId, b.titlesByLeafId)
)
}
function sanitizeRecentTabIds(recent: string[] | undefined, tabOrder: string[]): string[] {
if (!recent || recent.length === 0) {
return []
@@ -0,0 +1,227 @@
/**
* Perf regression: setCacheTimerStartedAt and setTabLayout must not publish
* state when the write is a no-op.
*
* Both actions previously spread a fresh object and returned it unconditionally,
* so every redundant call produced a new AppState object and woke EVERY zustand
* subscriber — which, with per-pane selectors across all mounted panes, is an
* O(panes) sweep per write. The cadence is real: parked-terminal-byte-watcher
* writes a null cache timer on every agent working/exit/stale-title transition,
* and TerminalPane re-persists its layout on pane-title churn.
*
* These tests count subscriber wakeups on a real store; the pre-fix numbers are
* 1_000 (one per call), the post-fix numbers are 0.
*/
import { describe, expect, it } from 'vitest'
import type { TerminalLayoutSnapshot } from '../../../../shared/types'
import { createTestStore } from './store-test-helpers'
const REPEATS = 1_000
function makeLayout(overrides: Partial<TerminalLayoutSnapshot> = {}): TerminalLayoutSnapshot {
return {
root: {
type: 'split',
direction: 'vertical',
ratio: 0.5,
first: { type: 'leaf', leafId: 'leaf-a' },
second: { type: 'leaf', leafId: 'leaf-b' }
},
activeLeafId: 'leaf-a',
expandedLeafId: null,
ptyIdsByLeafId: { 'leaf-a': 'pty-a', 'leaf-b': 'pty-b' },
titlesByLeafId: { 'leaf-a': 'build' },
...overrides
}
}
describe('setCacheTimerStartedAt identity bailout', () => {
it('wakes zero subscribers across 1,000 null-over-null writes', () => {
const store = createTestStore()
const paneKey = 'tab-1:leaf-a'
store.getState().setCacheTimerStartedAt(paneKey, null)
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
for (let i = 0; i < REPEATS; i += 1) {
store.getState().setCacheTimerStartedAt(paneKey, null)
}
unsubscribe()
expect(wakeups).toBe(0)
expect(store.getState().cacheTimerByKey[paneKey]).toBeNull()
})
it('wakes zero subscribers across 1,000 repeats of the same timestamp', () => {
const store = createTestStore()
const paneKey = 'tab-1:leaf-a'
store.getState().setCacheTimerStartedAt(paneKey, 1_700_000_000_000)
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
for (let i = 0; i < REPEATS; i += 1) {
store.getState().setCacheTimerStartedAt(paneKey, 1_700_000_000_000)
}
unsubscribe()
expect(wakeups).toBe(0)
})
it('still publishes when the timestamp actually changes', () => {
const store = createTestStore()
const paneKey = 'tab-1:leaf-a'
store.getState().setCacheTimerStartedAt(paneKey, null)
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
store.getState().setCacheTimerStartedAt(paneKey, 123)
store.getState().setCacheTimerStartedAt(paneKey, null)
unsubscribe()
expect(wakeups).toBe(2)
expect(store.getState().cacheTimerByKey[paneKey]).toBeNull()
})
it('records the first write for a key even when the value is null', () => {
const store = createTestStore()
const paneKey = 'tab-1:leaf-a'
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
store.getState().setCacheTimerStartedAt(paneKey, null)
unsubscribe()
expect(wakeups).toBe(1)
expect(paneKey in store.getState().cacheTimerByKey).toBe(true)
})
it('still clears a stale :seed sentinel when the pane value is unchanged', () => {
// Without this carve-out the bailout would strand the sentinel and leave a phantom timer.
const store = createTestStore()
const paneKey = 'tab-1:leaf-a'
store.getState().setCacheTimerStartedAt(paneKey, null)
store.setState((s) => ({ cacheTimerByKey: { ...s.cacheTimerByKey, 'tab-1:seed': 42 } }))
store.getState().setCacheTimerStartedAt(paneKey, null)
expect('tab-1:seed' in store.getState().cacheTimerByKey).toBe(false)
})
})
describe('setTabLayout identity bailout', () => {
it('wakes zero subscribers across 1,000 structurally identical snapshots', () => {
const store = createTestStore()
store.getState().setTabLayout('tab-1', makeLayout())
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
for (let i = 0; i < REPEATS; i += 1) {
// Fresh object each iteration: this is what persistLayoutSnapshot produces.
store.getState().setTabLayout('tab-1', makeLayout())
}
unsubscribe()
expect(wakeups).toBe(0)
})
it('keeps the stored snapshot reference stable when nothing changed', () => {
const store = createTestStore()
store.getState().setTabLayout('tab-1', makeLayout())
const first = store.getState().terminalLayoutsByTabId['tab-1']
store.getState().setTabLayout('tab-1', makeLayout())
expect(store.getState().terminalLayoutsByTabId['tab-1']).toBe(first)
})
it('publishes when any tracked field changes', () => {
const store = createTestStore()
store.getState().setTabLayout('tab-1', makeLayout())
const mutations: Partial<TerminalLayoutSnapshot>[] = [
{ activeLeafId: 'leaf-b' },
{ expandedLeafId: 'leaf-a' },
{ titlesByLeafId: { 'leaf-a': 'test' } },
{ ptyIdsByLeafId: { 'leaf-a': 'pty-a', 'leaf-b': 'pty-c' } },
{ buffersByLeafId: { 'leaf-a': 'scrollback' } },
{ scrollbackRefsByLeafId: { 'leaf-a': 'ref-1' } },
{
root: {
type: 'split',
direction: 'horizontal',
ratio: 0.5,
first: { type: 'leaf', leafId: 'leaf-a' },
second: { type: 'leaf', leafId: 'leaf-b' }
}
},
{
root: {
type: 'split',
direction: 'vertical',
ratio: 0.7,
first: { type: 'leaf', leafId: 'leaf-a' },
second: { type: 'leaf', leafId: 'leaf-b' }
}
}
]
for (const mutation of mutations) {
store.getState().setTabLayout('tab-1', makeLayout())
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
store.getState().setTabLayout('tab-1', makeLayout(mutation))
unsubscribe()
expect(wakeups, `expected a publish for ${JSON.stringify(mutation)}`).toBe(1)
}
})
it('does not fire pane-ownership transfers for a bailed-out identical layout', () => {
// A duplicate-pty layout normalizes to a transfer; replaying the already-normalized
// snapshot must be inert, since normalization then finds nothing to move.
const store = createTestStore()
const duplicate = makeLayout({
ptyIdsByLeafId: { 'leaf-a': 'pty-a', 'leaf-b': 'pty-a' }
})
store.getState().setTabLayout('tab-1', duplicate)
const normalized = store.getState().terminalLayoutsByTabId['tab-1']
expect(normalized.ptyIdsByLeafId).not.toEqual(duplicate.ptyIdsByLeafId)
store.getState().markTerminalPaneUnread('tab-1:leaf-a')
const beforeUnread = { ...store.getState().unreadTerminalPanes }
for (let i = 0; i < REPEATS; i += 1) {
store.getState().setTabLayout('tab-1', { ...normalized })
}
expect(store.getState().terminalLayoutsByTabId['tab-1']).toBe(normalized)
expect(store.getState().unreadTerminalPanes).toEqual(beforeUnread)
})
it('still deletes the layout on a clearing call, and bails when already absent', () => {
const store = createTestStore()
store.getState().setTabLayout('tab-1', makeLayout())
store.getState().setTabLayout('tab-1', null)
expect('tab-1' in store.getState().terminalLayoutsByTabId).toBe(false)
let wakeups = 0
const unsubscribe = store.subscribe(() => {
wakeups += 1
})
store.getState().setTabLayout('tab-1', null)
unsubscribe()
expect(wakeups).toBe(0)
})
})
+29 -18
View File
@@ -54,6 +54,7 @@ import type { AgentStartedTelemetry } from '../../lib/worktree-activation'
import { scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
import { forgetAgentHibernationTabOutput } from '@/lib/agent-hibernation-output-activity'
import { forgetForegroundTerminalTabs } from '@/lib/foreground-terminal-tabs'
import { terminalLayoutEqual } from '@/lib/terminal-layout-equality'
import { forgetAgentStartupDeliveriesForTabs } from '@/lib/agent-startup-delivery-guards'
import { clearTransientTerminalState, emptyLayoutSnapshot } from './terminal-helpers'
import {
@@ -1266,15 +1267,18 @@ export const createTerminalSlice: StateCreator<AppState, [], [], TerminalSlice>
setCacheTimerStartedAt: (key, ts) => {
set((s) => {
const next = { ...s.cacheTimerByKey, [key]: ts }
// Why: a real pane write clears any ':seed' sentinel from seedCacheTimersForIdleTabs, avoiding phantom timers when the seed key doesn't match the real pane.
const colonIdx = key.indexOf(':')
if (colonIdx !== -1) {
const tabId = key.slice(0, colonIdx)
const suffix = key.slice(colonIdx + 1)
if (suffix !== 'seed') {
delete next[`${tabId}:seed`]
}
const suffix = colonIdx === -1 ? null : key.slice(colonIdx + 1)
const seedKey = colonIdx !== -1 && suffix !== 'seed' ? `${key.slice(0, colonIdx)}:seed` : null
const hasStaleSeed = seedKey !== null && seedKey in s.cacheTimerByKey
// Why: parked-pane watchers replay null-over-null on every working/exit transition; each redundant write runs every subscriber's selector.
if (s.cacheTimerByKey[key] === ts && !hasStaleSeed) {
return s
}
const next = { ...s.cacheTimerByKey, [key]: ts }
if (seedKey !== null) {
delete next[seedKey]
}
return { cacheTimerByKey: next }
})
@@ -3649,20 +3653,27 @@ export const createTerminalSlice: StateCreator<AppState, [], [], TerminalSlice>
setTabLayout: (tabId, layout) => {
let ownershipTransfers: ReturnType<typeof resolveTerminalLayoutPtyOwnershipTransfers> = []
set((s) => {
const next = { ...s.terminalLayoutsByTabId }
if (layout) {
const normalized = normalizeTerminalLayoutPtyOwnership(layout)
next[tabId] = normalized.snapshot
if (normalized.changed) {
ownershipTransfers = resolveTerminalLayoutPtyOwnershipTransfers(
layout,
normalized.snapshot
)
if (!layout) {
if (!(tabId in s.terminalLayoutsByTabId)) {
return s
}
} else {
const next = { ...s.terminalLayoutsByTabId }
delete next[tabId]
return { terminalLayoutsByTabId: next }
}
const normalized = normalizeTerminalLayoutPtyOwnership(layout)
// Resolved before the bailout: normalization can transfer pane ownership even when the stored snapshot is untouched.
if (normalized.changed) {
ownershipTransfers = resolveTerminalLayoutPtyOwnershipTransfers(layout, normalized.snapshot)
}
// Why: pane-title churn re-persists structurally identical snapshots; bailing keeps every pane selector asleep.
const existing = s.terminalLayoutsByTabId[tabId]
if (existing && terminalLayoutEqual(existing, normalized.snapshot)) {
return s
}
return {
terminalLayoutsByTabId: { ...s.terminalLayoutsByTabId, [tabId]: normalized.snapshot }
}
return { terminalLayoutsByTabId: next }
})
transferNormalizedTerminalLayoutPtyOwnership(get(), tabId, ownershipTransfers)
},