From 7e518c86e3ae302df70991d4afb89f6568f5de21 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:00:02 -0700 Subject: [PATCH] refactor(mobile): thread inspector mode is derived, not synced ThreadRouteScreen stored the inspector selection together with the thread identity it was picked for, then copied the new identity in after every route change with an effect. Each thread switch therefore rendered once with no inspector mode (deregistering the workspace inspector and letting the auto-close effect see a transient empty state), then rendered again once the effect caught up. Store only the mode the user chose and derive the effective mode in render: "route" applies whenever the route supplies an inspector, a chosen git/files mode carries over, and files drops out when the thread has no working directory. Effects: ThreadRouteScreen.tsx 4 -> 3. Removed the selection sync effect. Kept the auto-close effect: it writes the shared workspace pane preference when this thread has nothing to show, and dropping it would leave the pane preferred-visible, which on non-native split view keeps the primary sidebar suppressed with no inspector beside it. Co-Authored-By: Claude Opus 5.5 --- .../features/threads/ThreadRouteScreen.tsx | 52 +++++-------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx index 0287a6975f7f9..4fe22d6cd5f22 100644 --- a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx +++ b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx @@ -111,11 +111,6 @@ import { import type { ThreadInspectorTarget } from "./thread-inspector-navigation"; import { useSelectWorkspaceDetail } from "../workspace/useSelectWorkspaceDetail"; -interface ThreadInspectorSelection { - readonly routeThreadIdentity: string | null; - readonly mode: ThreadInspectorMode; -} - function InspectorPaneRoleActivation() { useAdaptiveWorkspacePaneRole("inspector"); return null; @@ -435,19 +430,21 @@ function ThreadRouteContent( const threadId = firstRouteParam(params.threadId); const routeThreadIdentity = environmentIdRaw !== null && threadId !== null ? `${environmentIdRaw}:${threadId}` : null; - const [inspectorSelection, setInspectorSelection] = useState( - () => (props.renderInspector ? { routeThreadIdentity, mode: "route" } : null), - ); + // The chosen mode survives thread switches; "route" only applies while the route + // supplies an inspector, which is also the default whenever it does. + const [chosenInspectorMode, setChosenInspectorMode] = useState(null); const previewTarget = props.inspectorPresentation ? undefined : params.inspector; const inspectorMode = (() => { if (previewTarget) return "preview"; - if (inspectorSelection?.routeThreadIdentity === routeThreadIdentity) { - if (inspectorSelection.mode === "files" && selectedThreadCwd === null) { - return null; - } - return inspectorSelection.mode; - } - return null; + const mode = + props.renderInspector === undefined + ? chosenInspectorMode === "route" + ? null + : chosenInspectorMode + : chosenInspectorMode === null || chosenInspectorMode === "route" + ? "route" + : chosenInspectorMode; + return mode === "files" && selectedThreadCwd === null ? null : mode; })(); useEffect(() => { if ( @@ -468,24 +465,6 @@ function ThreadRouteContent( toggleAuxiliaryPane, ]); - useEffect(() => { - // oxlint-disable-next-line react/set-state-in-effect -- Inspector selection follows route ownership but keeps an inspector the user opened. - setInspectorSelection((current) => { - if (props.renderInspector === undefined) { - if (current === null || current.mode === "route") { - return null; - } - return { ...current, routeThreadIdentity }; - } - - if (current === null || current.mode === "route") { - return { routeThreadIdentity, mode: "route" }; - } - - return { ...current, routeThreadIdentity }; - }); - }, [props.renderInspector, routeThreadIdentity]); - const revealPreview = useEffectEvent(() => showAuxiliaryPane("inspector")); useEffect(() => { if (previewTarget) revealPreview(); @@ -552,7 +531,7 @@ function ThreadRouteContent( return; } navigation.setParams({ inspector: undefined }); - setInspectorSelection({ routeThreadIdentity, mode: "git" }); + setChosenInspectorMode("git"); showAuxiliaryPane("inspector"); }; const handleOpenFilesInspector = () => { @@ -567,10 +546,7 @@ function ThreadRouteContent( return; } navigation.setParams({ inspector: undefined }); - setInspectorSelection({ - routeThreadIdentity, - mode: props.renderInspector === undefined ? "files" : "route", - }); + setChosenInspectorMode(props.renderInspector === undefined ? "files" : "route"); showAuxiliaryPane("inspector"); }; const handleToggleInspector = () => {