From 041dd5f05b1b1c236c45c7d0bee080b78442bdb4 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 17 Aug 2026 13:57:55 -0700 Subject: [PATCH] fix(native-chat): bring the structured core branch back under max-lines (#15168) Static analysis was red on the branch itself, which is why every stacked PR inherited three failing checks. Two files were over their caps, both from this branch's own growth: - `use-app-startup-hydration.ts` (307/300): the startup store subscription moves to `use-app-startup-actions.ts`. It is a separate concern from the boot chain and was the only reason the file needed `useShallow`. - the mobile session route (5036/5015): the structured chat view was wired inline with eighteen props. It moves to `MobileStructuredSessionPane`, so the route passes one entry object. No per-file cap was raised and no rule was disabled. Verified: repo-wide oxlint clean (0 errors, 15765 files), web typecheck, mobile typecheck, and the startup source-slice suite (25 tests). --- .../app/h/[hostId]/session/[worktreeId].tsx | 32 ++----------- .../session/MobileStructuredSessionPane.tsx | 46 +++++++++++++++++++ .../src/app-shell/use-app-startup-actions.ts | 39 ++++++++++++++++ .../app-shell/use-app-startup-hydration.ts | 36 +-------------- 4 files changed, 89 insertions(+), 64 deletions(-) create mode 100644 mobile/src/session/MobileStructuredSessionPane.tsx create mode 100644 src/renderer/src/app-shell/use-app-startup-actions.ts diff --git a/mobile/app/h/[hostId]/session/[worktreeId].tsx b/mobile/app/h/[hostId]/session/[worktreeId].tsx index 6e1673d2bb2..988e652a059 100644 --- a/mobile/app/h/[hostId]/session/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/session/[worktreeId].tsx @@ -228,7 +228,7 @@ import { } from '../../../../src/dictation/mobile-dictation-setup' import { TerminalPaneView } from '../../../../src/session/TerminalPaneView' import { MobileNativeChatOverlay } from '../../../../src/session/MobileNativeChatOverlay' -import { MobileStructuredAgentSessionView } from '../../../../src/session/MobileStructuredAgentSessionView' +import { MobileStructuredSessionPane } from '../../../../src/session/MobileStructuredSessionPane' import { MobileStructuredSessionCreateError } from '../../../../src/session/MobileStructuredSessionCreateError' import { useMobileStructuredSessionEntry } from '../../../../src/session/use-mobile-structured-session-entry' import { showMobileStructuredChatChoice } from '../../../../src/session/mobile-structured-session-create' @@ -4684,36 +4684,10 @@ export default function SessionScreen() { )} ) : activeStructuredTab ? ( - { - const accepted = await structuredSessionEntry.writes.send(text, [ - ...restored, - ...structuredSessionEntry.attachments.attachments - ]) - if (accepted) { - structuredSessionEntry.attachments.clear() - } - return accepted - }} - onTakeQueuedForEdit={structuredSessionEntry.writes.takeQueuedForEdit} - onRetry={structuredSessionEntry.writes.retry} - onRespondToPrompt={structuredSessionEntry.writes.respondToPrompt} - sessionOptions={structuredSessionEntry.sessionOptions} - attachments={structuredSessionEntry.attachments.attachments} - isAttaching={structuredSessionEntry.attachments.attaching} - onAttachImage={() => void structuredSessionEntry.attachments.attach('library')} - onRemoveAttachment={structuredSessionEntry.attachments.remove} - onCancel={structuredSessionEntry.writes.cancel} /> ) : activePendingTerminalTab ? ( diff --git a/mobile/src/session/MobileStructuredSessionPane.tsx b/mobile/src/session/MobileStructuredSessionPane.tsx new file mode 100644 index 00000000000..837b7ee5301 --- /dev/null +++ b/mobile/src/session/MobileStructuredSessionPane.tsx @@ -0,0 +1,46 @@ +// Wires one structured agent session entry into the chat view. Kept out of the +// session route so the route threads one object instead of eighteen props. + +import { MobileStructuredAgentSessionView } from './MobileStructuredAgentSessionView' +import type { useMobileStructuredSessionEntry } from './use-mobile-structured-session-entry' + +type StructuredSessionEntry = ReturnType + +export function MobileStructuredSessionPane({ + entry, + onOpenFile +}: { + entry: StructuredSessionEntry + onOpenFile: (path: string, line?: number) => void +}) { + const { session, writes, attachments, sessionOptions } = entry + return ( + { + const accepted = await writes.send(text, [...restored, ...attachments.attachments]) + if (accepted) { + attachments.clear() + } + return accepted + }} + onTakeQueuedForEdit={writes.takeQueuedForEdit} + onRetry={writes.retry} + onRespondToPrompt={writes.respondToPrompt} + sessionOptions={sessionOptions} + attachments={attachments.attachments} + isAttaching={attachments.attaching} + onAttachImage={() => void attachments.attach('library')} + onRemoveAttachment={attachments.remove} + onCancel={writes.cancel} + /> + ) +} diff --git a/src/renderer/src/app-shell/use-app-startup-actions.ts b/src/renderer/src/app-shell/use-app-startup-actions.ts new file mode 100644 index 00000000000..df7cec71ab1 --- /dev/null +++ b/src/renderer/src/app-shell/use-app-startup-actions.ts @@ -0,0 +1,39 @@ +// The renderer boot chain's store subscription, kept apart from the chain itself +// so one useShallow equality check covers every startup action. + +import { useShallow } from 'zustand/react/shallow' +import { useAppStore } from '../store' + +export function useStartupActions() { + // Why: consolidate action refs into one useShallow subscription so React runs one equality check per store mutation instead of one per action. + return useAppStore( + useShallow((s) => ({ + fetchReposForAllHosts: s.fetchReposForAllHosts, + awaitLocalRepoCatalogSettlement: s.awaitLocalRepoCatalogSettlement, + fetchProjectGroupsForAllHosts: s.fetchProjectGroupsForAllHosts, + fetchFolderWorkspacesForAllHosts: s.fetchFolderWorkspacesForAllHosts, + fetchAllWorktrees: s.fetchAllWorktrees, + fetchWorktrees: s.fetchWorktrees, + fetchWorktreeLineage: s.fetchWorktreeLineage, + fetchOrcaProfiles: s.fetchOrcaProfiles, + fetchSettings: s.fetchSettings, + awaitOwnerWorktreeVisibilityDefaultsHydration: + s.awaitOwnerWorktreeVisibilityDefaultsHydration, + fetchKeybindings: s.fetchKeybindings, + initGitHubCache: s.initGitHubCache, + hydrateWorkspaceSession: s.hydrateWorkspaceSession, + hydrateTabsSession: s.hydrateTabsSession, + hydrateEditorSession: s.hydrateEditorSession, + hydrateBrowserSession: s.hydrateBrowserSession, + fetchBrowserSessionProfiles: s.fetchBrowserSessionProfiles, + reconnectPersistedTerminals: s.reconnectPersistedTerminals, + setTerminalStartupRestorationReady: s.setTerminalStartupRestorationReady, + setDeferredSshReconnectTargets: s.setDeferredSshReconnectTargets, + setSshConnectionState: s.setSshConnectionState, + hydratePersistedUI: s.hydratePersistedUI, + setHydrationSucceeded: s.setHydrationSucceeded, + pruneLastVisitedTimestamps: s.pruneLastVisitedTimestamps, + seedActiveWorktreeLastVisitedIfMissing: s.seedActiveWorktreeLastVisitedIfMissing + })) + ) +} diff --git a/src/renderer/src/app-shell/use-app-startup-hydration.ts b/src/renderer/src/app-shell/use-app-startup-hydration.ts index 637eece13ff..091039fb793 100644 --- a/src/renderer/src/app-shell/use-app-startup-hydration.ts +++ b/src/renderer/src/app-shell/use-app-startup-hydration.ts @@ -1,8 +1,8 @@ import { useEffect, useRef } from 'react' -import { useShallow } from 'zustand/react/shallow' import { syncZoomCSSVar } from '@/lib/ui-zoom' import { installCodexDetachedPaneRestartExecutor } from '@/components/terminal-pane/codex-detached-pane-restart-scheduler' import { useAppStore } from '../store' +import { useStartupActions } from './use-app-startup-actions' import { WORKTREE_REFRESH_CONCURRENCY } from '../store/slices/worktrees' import { sweepRestoredCodexPanesForStaleAccounts } from '../lib/codex-stale-pane-sweep' import { fetchWorkspaceSessionWithRuntimeHostOwners } from '../lib/workspace-session-host-persistence' @@ -46,40 +46,6 @@ async function listRuntimeSessionHostIdsForStartup(): Promise } } -function useStartupActions() { - // Why: consolidate action refs into one useShallow subscription so React runs one equality check per store mutation instead of one per action. - return useAppStore( - useShallow((s) => ({ - fetchReposForAllHosts: s.fetchReposForAllHosts, - awaitLocalRepoCatalogSettlement: s.awaitLocalRepoCatalogSettlement, - fetchProjectGroupsForAllHosts: s.fetchProjectGroupsForAllHosts, - fetchFolderWorkspacesForAllHosts: s.fetchFolderWorkspacesForAllHosts, - fetchAllWorktrees: s.fetchAllWorktrees, - fetchWorktrees: s.fetchWorktrees, - fetchWorktreeLineage: s.fetchWorktreeLineage, - fetchOrcaProfiles: s.fetchOrcaProfiles, - fetchSettings: s.fetchSettings, - awaitOwnerWorktreeVisibilityDefaultsHydration: - s.awaitOwnerWorktreeVisibilityDefaultsHydration, - fetchKeybindings: s.fetchKeybindings, - initGitHubCache: s.initGitHubCache, - hydrateWorkspaceSession: s.hydrateWorkspaceSession, - hydrateTabsSession: s.hydrateTabsSession, - hydrateEditorSession: s.hydrateEditorSession, - hydrateBrowserSession: s.hydrateBrowserSession, - fetchBrowserSessionProfiles: s.fetchBrowserSessionProfiles, - reconnectPersistedTerminals: s.reconnectPersistedTerminals, - setTerminalStartupRestorationReady: s.setTerminalStartupRestorationReady, - setDeferredSshReconnectTargets: s.setDeferredSshReconnectTargets, - setSshConnectionState: s.setSshConnectionState, - hydratePersistedUI: s.hydratePersistedUI, - setHydrationSucceeded: s.setHydrationSucceeded, - pruneLastVisitedTimestamps: s.pruneLastVisitedTimestamps, - seedActiveWorktreeLastVisitedIfMissing: s.seedActiveWorktreeLastVisitedIfMissing - })) - ) -} - /** * Runs the renderer's one-shot boot chain: settings, persisted UI, the local repo catalog, * the workspace session, SSH reconnect, and terminal restoration — then unlocks the session