perf(renderer): share retained TabBar projections (#10094)

This commit is contained in:
Neil 2026-07-22 22:17:53 -07:00 committed by GitHub
parent 09756dfaff
commit c445f26541
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 341 additions and 59 deletions

View File

@ -330,6 +330,14 @@ describe('TabBar context menu wiring', () => {
vi.unstubAllGlobals()
})
it('wires the shared agent projection selector into the production TabBar', async () => {
const { selectTabBarAgentProjections } = await import('./tab-agent-types-by-tab-id')
await renderTabBar({ tabs: [], editorFiles: [], browserTabs: [], tabBarOrder: [] })
expect(useAppStoreMock).toHaveBeenCalledWith(selectTabBarAgentProjections)
})
it('counts every tab kind for SortableTab.tabCount', async () => {
// Why: Close Others used to pass tabCount=tabs.length, where tabs is just the
// terminal list. With one terminal + any number of editor/browser tabs, the

View File

@ -78,10 +78,7 @@ import { useTabStripDragScrollHandlers } from './tab-strip-drag-scroll'
import { shouldShowWindowsShellMenu } from './windows-shell-menu-visibility'
import { canToggleNativeChat } from '../native-chat/native-chat-availability'
import { isNativeChatTranscriptLocalReadable } from '@/lib/native-chat-transcript-readability'
import {
selectNativeChatTabWideFallbackUnsafeTabsById,
selectTabAgentTypesByTabId
} from './tab-agent-types-by-tab-id'
import { selectTabBarAgentProjections } from './tab-agent-types-by-tab-id'
import { resolveCommittedTitleAgentType } from '@/lib/pane-agent-evidence'
const isWindows = navigator.userAgent.includes('Windows')
@ -419,16 +416,9 @@ function TabBarInner({
// Why: tab-wide launch/title hints are safe only before split; gate the view-mode toggle to the active leaf's agent.
const toggleTabViewMode = useAppStore((s) => s.toggleTabViewMode)
// Why: agentStatusByPaneKey churns on every status flip; project {tabId:agentType} to re-render only on agent identity change.
const tabAgentTypesByTabId = useAppStore(
useShallow((s) =>
selectTabAgentTypesByTabId(s.agentStatusByPaneKey ?? {}, s.terminalLayoutsByTabId)
)
)
const nativeChatTabWideFallbackUnsafeTabsById = useAppStore(
useShallow((s) => selectNativeChatTabWideFallbackUnsafeTabsById(s.terminalLayoutsByTabId))
)
const nativeChatEnabled = useAppStore((s) => s.settings?.experimentalNativeChat === true)
// Why: every retained TabBar observes the same hot maps; one feature-gated selector shares their projections.
const { nativeChatEnabled, tabAgentTypesByTabId, nativeChatTabWideFallbackUnsafeTabsById } =
useAppStore(useShallow(selectTabBarAgentProjections))
const nativeChatTranscriptIsLocalReadable = useAppStore((s) =>
isNativeChatTranscriptLocalReadable(getConnectionIdFromState(s, worktreeId))
)

View File

@ -1,9 +1,11 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import { shallow } from 'zustand/shallow'
import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
import type { TerminalLayoutSnapshot } from '../../../../shared/types'
import { findTabAgentEntry } from '../native-chat/native-chat-tab-agent-entry'
import {
createTabBarAgentProjectionSelector,
selectTabBarAgentProjections,
selectNativeChatTabWideFallbackUnsafeTabsById,
selectTabAgentTypesByTabId
} from './tab-agent-types-by-tab-id'
@ -205,4 +207,174 @@ describe('selectTabAgentTypesByTabId', () => {
it('ignores malformed pane keys with no tab id', () => {
expect(selectTabAgentTypesByTabId({ ':leaf-a': entry({ agentType: 'claude' }) })).toEqual({})
})
it('shares one pair of global scans across retained TabBar consumers', () => {
const onStatusEntryVisited = vi.fn()
const onAgentTypeLayoutVisited = vi.fn()
const onUnsafeLayoutVisited = vi.fn()
const select = createTabBarAgentProjectionSelector({
onStatusEntryVisited,
onAgentTypeLayoutVisited,
onUnsafeLayoutVisited
})
const statuses = {
'tab-1:leaf-a': entry({ agentType: 'claude' }),
'tab-2:leaf-a': entry({ agentType: 'codex' }),
'tab-3:leaf-a': entry({ agentType: 'grok' })
}
const layouts = {
'tab-1': splitLayout('leaf-a'),
'tab-2': splitLayout('leaf-a')
}
for (let consumer = 0; consumer < 100; consumer++) {
select({
settings: { experimentalNativeChat: true },
agentStatusByPaneKey: statuses,
terminalLayoutsByTabId: layouts
})
}
expect(onStatusEntryVisited).toHaveBeenCalledTimes(3)
expect(onAgentTypeLayoutVisited).toHaveBeenCalledTimes(2)
expect(onUnsafeLayoutVisited).toHaveBeenCalledTimes(2)
})
it('reuses outputs and invalidates only the projection whose input changed', () => {
const onStatusEntryVisited = vi.fn()
const onAgentTypeLayoutVisited = vi.fn()
const onUnsafeLayoutVisited = vi.fn()
const select = createTabBarAgentProjectionSelector({
onStatusEntryVisited,
onAgentTypeLayoutVisited,
onUnsafeLayoutVisited
})
const split = { 'tab-1': splitLayout('leaf-a') }
const working = {
'tab-1:leaf-a': entry({ agentType: 'claude', state: 'working' }),
'tab-1:leaf-b': entry({ agentType: 'codex', state: 'working' })
}
const first = select({
settings: { experimentalNativeChat: true },
agentStatusByPaneKey: working,
terminalLayoutsByTabId: split
})
const done = {
'tab-1:leaf-a': entry({ agentType: 'claude', state: 'done' }),
'tab-1:leaf-b': entry({ agentType: 'codex', state: 'done' })
}
const afterStatus = select({
settings: { experimentalNativeChat: true },
agentStatusByPaneKey: done,
terminalLayoutsByTabId: split
})
expect(afterStatus).toBe(first)
expect(onStatusEntryVisited).toHaveBeenCalledTimes(4)
expect(onAgentTypeLayoutVisited).toHaveBeenCalledTimes(2)
expect(onUnsafeLayoutVisited).toHaveBeenCalledTimes(1)
const singleLeaf = {
'tab-1': {
root: { type: 'leaf' as const, leafId: 'leaf-b' },
activeLeafId: 'leaf-b',
expandedLeafId: null
}
}
const afterLayout = select({
settings: { experimentalNativeChat: true },
agentStatusByPaneKey: done,
terminalLayoutsByTabId: singleLeaf
})
expect(afterLayout.tabAgentTypesByTabId).toEqual({ 'tab-1': 'codex' })
expect(afterLayout.tabAgentTypesByTabId).not.toBe(first.tabAgentTypesByTabId)
expect(afterLayout.nativeChatTabWideFallbackUnsafeTabsById).toEqual({})
expect(afterLayout.nativeChatTabWideFallbackUnsafeTabsById).not.toBe(
first.nativeChatTabWideFallbackUnsafeTabsById
)
expect(onStatusEntryVisited).toHaveBeenCalledTimes(6)
expect(onAgentTypeLayoutVisited).toHaveBeenCalledTimes(3)
expect(onUnsafeLayoutVisited).toHaveBeenCalledTimes(2)
})
it('normalizes missing maps to shared empty inputs', () => {
const select = createTabBarAgentProjectionSelector()
const first = select({ settings: { experimentalNativeChat: true } })
expect(select({ settings: { experimentalNativeChat: true } })).toBe(first)
})
it('releases enabled inputs on disable and rescans them after re-enabling', () => {
const onStatusEntryVisited = vi.fn()
const onAgentTypeLayoutVisited = vi.fn()
const onUnsafeLayoutVisited = vi.fn()
const select = createTabBarAgentProjectionSelector({
onStatusEntryVisited,
onAgentTypeLayoutVisited,
onUnsafeLayoutVisited
})
const state = {
settings: { experimentalNativeChat: true },
agentStatusByPaneKey: { 'tab-1:leaf-a': entry({ agentType: 'claude' }) },
terminalLayoutsByTabId: { 'tab-1': splitLayout('leaf-a') }
}
const first = select(state)
select({ ...state, settings: { experimentalNativeChat: false } })
const afterReenable = select(state)
expect(afterReenable).not.toBe(first)
expect(onStatusEntryVisited).toHaveBeenCalledTimes(2)
expect(onAgentTypeLayoutVisited).toHaveBeenCalledTimes(2)
expect(onUnsafeLayoutVisited).toHaveBeenCalledTimes(2)
})
it('production selector skips all map scans while native chat is disabled', () => {
let statusEnumerations = 0
let layoutEnumerations = 0
const statuses = new Proxy(
{ 'tab-1:leaf-a': entry({ agentType: 'claude' }) },
{
ownKeys(target) {
statusEnumerations++
return Reflect.ownKeys(target)
}
}
)
const layouts = new Proxy(
{ 'tab-1': splitLayout('leaf-a') },
{
ownKeys(target) {
layoutEnumerations++
return Reflect.ownKeys(target)
}
}
)
const disabledState = {
settings: { experimentalNativeChat: false },
agentStatusByPaneKey: statuses,
terminalLayoutsByTabId: layouts
}
const disabled = selectTabBarAgentProjections(disabledState)
for (let consumer = 0; consumer < 100; consumer++) {
expect(selectTabBarAgentProjections(disabledState)).toBe(disabled)
}
expect(statusEnumerations).toBe(0)
expect(layoutEnumerations).toBe(0)
const enabledState = {
...disabledState,
settings: { experimentalNativeChat: true }
}
const enabled = selectTabBarAgentProjections(enabledState)
for (let consumer = 0; consumer < 100; consumer++) {
expect(selectTabBarAgentProjections(enabledState)).toBe(enabled)
}
expect(statusEnumerations).toBe(1)
expect(layoutEnumerations).toBe(2)
})
})

View File

@ -5,6 +5,102 @@ import {
resolveNativeChatActiveLayoutLeafId
} from '../native-chat/native-chat-leaf-routing'
type TabBarAgentProjectionSelectorDependencies = {
onStatusEntryVisited?: (paneKey: string) => void
onAgentTypeLayoutVisited?: (tabId: string) => void
onUnsafeLayoutVisited?: (tabId: string) => void
}
export type TabBarAgentProjectionState = {
agentStatusByPaneKey?: Record<string, AgentStatusEntry>
terminalLayoutsByTabId?: Record<string, TerminalLayoutSnapshot>
settings?: { experimentalNativeChat?: boolean } | null
}
export type TabBarAgentProjections = {
nativeChatEnabled: boolean
tabAgentTypesByTabId: Record<string, AgentType>
nativeChatTabWideFallbackUnsafeTabsById: Record<string, true>
}
const EMPTY_AGENT_STATUS_BY_PANE_KEY: Record<string, AgentStatusEntry> = Object.freeze({})
const EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID: Record<string, TerminalLayoutSnapshot> = Object.freeze({})
const EMPTY_TAB_AGENT_TYPES_BY_TAB_ID: Record<string, AgentType> = Object.freeze({})
const EMPTY_UNSAFE_TABS_BY_ID: Record<string, true> = Object.freeze({})
const DISABLED_TAB_BAR_AGENT_PROJECTIONS: TabBarAgentProjections = Object.freeze({
nativeChatEnabled: false,
tabAgentTypesByTabId: EMPTY_TAB_AGENT_TYPES_BY_TAB_ID,
nativeChatTabWideFallbackUnsafeTabsById: EMPTY_UNSAFE_TABS_BY_ID
})
function reuseRecordIfEqual<T>(
previous: Record<string, T> | undefined,
next: Record<string, T>
): Record<string, T> {
if (!previous) {
return next
}
const nextKeys = Object.keys(next)
if (Object.keys(previous).length !== nextKeys.length) {
return next
}
return nextKeys.every((key) => previous[key] === next[key]) ? previous : next
}
function projectTabAgentTypesByTabId(
agentStatusByPaneKey: Record<string, AgentStatusEntry>,
terminalLayoutsByTabId: Record<string, TerminalLayoutSnapshot>,
dependencies?: TabBarAgentProjectionSelectorDependencies
): Record<string, AgentType> {
const byTabId: Record<string, AgentType> = {}
const claimed = new Set<string>()
for (const [tabId, layout] of Object.entries(terminalLayoutsByTabId)) {
dependencies?.onAgentTypeLayoutVisited?.(tabId)
if (!layout.root && !layout.activeLeafId) {
continue
}
claimed.add(tabId)
const activeLeafId = resolveNativeChatActiveLayoutLeafId(layout)
if (!activeLeafId) {
continue
}
const entry = agentStatusByPaneKey[`${tabId}:${activeLeafId}`]
if (entry?.agentType != null) {
byTabId[tabId] = entry.agentType
}
}
for (const [paneKey, entry] of Object.entries(agentStatusByPaneKey)) {
dependencies?.onStatusEntryVisited?.(paneKey)
const colon = paneKey.indexOf(':')
if (colon <= 0) {
continue
}
const tabId = paneKey.slice(0, colon)
if (claimed.has(tabId)) {
continue
}
claimed.add(tabId)
if (entry.agentType != null) {
byTabId[tabId] = entry.agentType
}
}
return byTabId
}
function projectNativeChatTabWideFallbackUnsafeTabsById(
terminalLayoutsByTabId: Record<string, TerminalLayoutSnapshot>,
dependencies?: TabBarAgentProjectionSelectorDependencies
): Record<string, true> {
const unsafeTabs: Record<string, true> = {}
for (const [tabId, layout] of Object.entries(terminalLayoutsByTabId)) {
dependencies?.onUnsafeLayoutVisited?.(tabId)
if (!isNativeChatTabWideFallbackSafe(layout)) {
unsafeTabs[tabId] = true
}
}
return unsafeTabs
}
/**
* Project `agentStatusByPaneKey` down to the stable `{ terminalTabId: agentType }`
* the tab strip actually reads (to gate the native-chat view-mode toggle).
@ -26,53 +122,69 @@ export function selectTabAgentTypesByTabId(
agentStatusByPaneKey: Record<string, AgentStatusEntry>,
terminalLayoutsByTabId: Record<string, TerminalLayoutSnapshot> = {}
): Record<string, AgentType> {
const byTabId: Record<string, AgentType> = {}
const claimed = new Set<string>()
// Why: the tab action opens chat on the active split leaf, so that leaf's
// identity must outrank object insertion order from unrelated siblings.
for (const [tabId, layout] of Object.entries(terminalLayoutsByTabId)) {
// A rootless snapshot with no active leaf is hydration absence, not a
// topology decision; preserve the legacy tab lookup until a leaf exists.
if (!layout.root && !layout.activeLeafId) {
continue
}
claimed.add(tabId)
const activeLeafId = resolveNativeChatActiveLayoutLeafId(layout)
if (!activeLeafId) {
continue
}
const entry = agentStatusByPaneKey[`${tabId}:${activeLeafId}`]
if (entry?.agentType != null) {
byTabId[tabId] = entry.agentType
}
}
for (const [paneKey, entry] of Object.entries(agentStatusByPaneKey)) {
const colon = paneKey.indexOf(':')
if (colon <= 0) {
continue
}
const tabId = paneKey.slice(0, colon)
if (claimed.has(tabId)) {
continue
}
claimed.add(tabId)
if (entry.agentType != null) {
byTabId[tabId] = entry.agentType
}
}
return byTabId
return projectTabAgentTypesByTabId(agentStatusByPaneKey, terminalLayoutsByTabId)
}
export function selectNativeChatTabWideFallbackUnsafeTabsById(
terminalLayoutsByTabId: Record<string, TerminalLayoutSnapshot> = {}
): Record<string, true> {
// Why: legacy and hydrating store shapes may not expose layout state yet;
// absence carries no unsafe split evidence and must not crash tab rendering.
const unsafeTabs: Record<string, true> = {}
for (const [tabId, layout] of Object.entries(terminalLayoutsByTabId)) {
if (!isNativeChatTabWideFallbackSafe(layout)) {
unsafeTabs[tabId] = true
}
}
return unsafeTabs
return projectNativeChatTabWideFallbackUnsafeTabsById(terminalLayoutsByTabId)
}
export function createTabBarAgentProjectionSelector(
dependencies?: TabBarAgentProjectionSelectorDependencies
): (state: TabBarAgentProjectionState) => TabBarAgentProjections {
let cachedAgentStatusByPaneKey: Record<string, AgentStatusEntry> | null = null
let cachedAgentTypeLayoutsByTabId: Record<string, TerminalLayoutSnapshot> | null = null
let cachedAgentTypesByTabId = EMPTY_TAB_AGENT_TYPES_BY_TAB_ID
let cachedUnsafeLayoutsByTabId: Record<string, TerminalLayoutSnapshot> | null = null
let cachedUnsafeTabsById = EMPTY_UNSAFE_TABS_BY_ID
let cachedEnabledResult: TabBarAgentProjections | null = null
return (state) => {
if (state.settings?.experimentalNativeChat !== true) {
if (cachedEnabledResult) {
cachedAgentStatusByPaneKey = null
cachedAgentTypeLayoutsByTabId = null
cachedAgentTypesByTabId = EMPTY_TAB_AGENT_TYPES_BY_TAB_ID
cachedUnsafeLayoutsByTabId = null
cachedUnsafeTabsById = EMPTY_UNSAFE_TABS_BY_ID
cachedEnabledResult = null
}
return DISABLED_TAB_BAR_AGENT_PROJECTIONS
}
const statuses = state.agentStatusByPaneKey ?? EMPTY_AGENT_STATUS_BY_PANE_KEY
const layouts = state.terminalLayoutsByTabId ?? EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID
if (statuses !== cachedAgentStatusByPaneKey || layouts !== cachedAgentTypeLayoutsByTabId) {
cachedAgentTypesByTabId = reuseRecordIfEqual(
cachedAgentTypesByTabId,
projectTabAgentTypesByTabId(statuses, layouts, dependencies)
)
cachedAgentStatusByPaneKey = statuses
cachedAgentTypeLayoutsByTabId = layouts
}
if (layouts !== cachedUnsafeLayoutsByTabId) {
cachedUnsafeTabsById = reuseRecordIfEqual(
cachedUnsafeTabsById,
projectNativeChatTabWideFallbackUnsafeTabsById(layouts, dependencies)
)
cachedUnsafeLayoutsByTabId = layouts
}
if (
cachedEnabledResult?.tabAgentTypesByTabId === cachedAgentTypesByTabId &&
cachedEnabledResult.nativeChatTabWideFallbackUnsafeTabsById === cachedUnsafeTabsById
) {
return cachedEnabledResult
}
cachedEnabledResult = {
nativeChatEnabled: true,
tabAgentTypesByTabId: cachedAgentTypesByTabId,
nativeChatTabWideFallbackUnsafeTabsById: cachedUnsafeTabsById
}
return cachedEnabledResult
}
}
// Why: every retained TabBar requests the same global projection tuple.
export const selectTabBarAgentProjections = createTabBarAgentProjectionSelector()