Skip to content

Add adaptive split-view layout for iPad/mobile workspace - #3514

Merged
juliusmarminge merged 81 commits into
mainfrom
t3code/ipad-responsive-mobile-layout
Jul 3, 2026
Merged

Add adaptive split-view layout for iPad/mobile workspace#3514
juliusmarminge merged 81 commits into
mainfrom
t3code/ipad-responsive-mobile-layout

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Add an adaptive workspace shell that switches between compact and split-view layouts based on available screen size.
  • Introduce a persistent thread sidebar for wider screens, with search, quick settings, and new-task actions.
  • Update thread and home routes to respect split-view behavior and hide redundant drawer/back navigation when the sidebar is present.
  • Refactor thread navigation grouping into shared logic and add coverage for layout and search filtering behavior.
  • Allow the mobile app to rotate beyond portrait so tablets and foldable-sized windows can use the split layout.

Testing

  • vp check
  • vp run typecheck
  • vp test
  • Added/updated tests for layout breakpoints and thread navigation grouping behavior
  • Not run: native/mobile device UI verification

Note

High Risk
Full navigation stack replacement and iOS scene/signing/deep-link changes affect every route and auth handoff; native diff payload bridging and review scroll/selection logic are complex and performance-sensitive.

Overview
Replaces expo-router with a custom React Navigation root (App.tsx, Stack.tsx): flat native-stack routes (thread paths no longer nested), sheet overlays that do not drive workspace layout, glass/solid header presets, and deep linking via createStaticNavigation. Entry moves from expo-router/entry to registerRootComponent(App) with react-native-screens synchronous updates enabled for nested FormSheet stacks.

iOS build and auth plumbing gains pinned appleTeamId, associatedDomains / relyingParty for Clerk (clerk.t3.codes), a SceneDelegate config plugin, and a CocoaPods UUID cache repair in post_install. expo-router is removed from dependencies; @react-navigation/* and @expo/metro-runtime are added.

New t3-native-controls module exposes UIKit header buttons and a T3KeyboardCommands view (⌘N, search, back, files/terminal/review, sidebar) with first-responder reclaim after text fields dismiss.

Review diff native surface moves large rowsJson / token payloads off Fabric props into async view methods with JS retry; Swift decodes JSON off the main thread, adds pull-to-refresh, scrollToFile / scrollToTop, visible-file events (suppressed during programmatic scroll), and contentResetKey to drop stale token patches.

Terminal and composer get hardware-key encoding (iOS UIKeyCommand, Android Ctrl+A–Z), focusRequest, and ⌘↩ submit on the native composer editor.

Typography bumps the global @theme scale and makes markdown/code/heading sizes scale from base body size (including optional headingFontSizes).

Files and archive UI shifts toward virtualized lists (LegendList for archive), shared swipe helpers, native mail-style search toolbars, optimistic file-tree selection, and file tree as a top-level FlatList with automatic content inset for glass headers.

Reviewed by Cursor Bugbot for commit d4c2133. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add adaptive split-view layout with inspector panes, appearance settings, and hardware keyboard support for iPad

  • Replaces expo-router with React Navigation throughout the mobile app, migrating all screens to named routes and StaticScreenProps; the app entrypoint now bootstraps via a local App component and RootStack config
  • Introduces an adaptive split-view workspace layout (AdaptiveWorkspaceLayout) that renders a resizable inspector pane alongside the main content on wide screens, with WorkspacePaneDivider for live resizing
  • Adds a ThreadNavigationSidebar for iPad-wide layouts and a ThreadInspectorContentStack that keeps Files/Git panes mounted after first activation to reduce UIKit teardown cost
  • Adds appearance preferences (base font size, code font size, terminal font size, word-break) with a new SettingsAppearanceRouteScreen, live CSS variable injection via applyTextScaleVariables, and per-component scaled typography via useScaledTextRole and useAppearanceCodeSurface
  • Adds hardware keyboard command handling via T3KeyboardCommandsView (iOS native) and HardwareKeyboardCommandProvider, enabling shortcuts for new task, back, files, terminal, review, and focus search
  • Moves large diff JSON payloads (rowsJson, tokensJson, tokensPatchJson) from Fabric props to async functions on T3ReviewDiffModule, adds pull-to-refresh, programmatic scrollToFile/scrollToTop, and onVisibleFileChange events to the native diff surface
  • Adds preloadWorkspaceFileContents to pre-fetch and syntax-highlight files before navigation, and useReviewDiffPrewarming to progressively pre-parse non-selected diff sections during idle time
  • Risk: enabling synchronousScreenUpdatesEnabled on react-native-screens changes iOS FormSheet nested stack sizing behavior; split-view layout now requires both width and height thresholds to activate, changing which devices see the split layout

Macroscope summarized d4c2133.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 07cb4200-4bb8-4304-9a21-d53a68dec0b6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/ipad-responsive-mobile-layout

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jun 23, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Sidebar shows archived threads
    • Added .filter((thread) => thread.archivedAt === null) in buildThreadNavigationGroups before sorting, matching the same filtering logic used by buildHomeThreadGroups.
  • ✅ Fixed: Split feed uses window width
    • Changed viewportWidth initial state to 0 when layoutVariant === "split" so the first render doesn't use the full window width, and added viewportWidth to LegendList's extraData so rows repaint after onLayout corrects it.
  • ✅ Fixed: Split view hides archive
    • Added useThreadListActions hook and a long-press handler with Archive/Delete options to ThreadNavigationSidebar thread rows, restoring thread management actions in split view.

Create PR

Or push these changes by commenting:

@cursor push b06fdc835f
Preview (b06fdc835f)
diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx
--- a/apps/mobile/src/features/threads/ThreadFeed.tsx
+++ b/apps/mobile/src/features/threads/ThreadFeed.tsx
@@ -1134,7 +1134,9 @@
   const initialScrollReadyRef = useRef(false);
   const lastContentHeightRef = useRef(0);
   const { width: windowWidth } = useWindowDimensions();
-  const [viewportWidth, setViewportWidth] = useState(windowWidth);
+  const [viewportWidth, setViewportWidth] = useState(() =>
+    props.layoutVariant === "split" ? 0 : windowWidth,
+  );
   const [interactionState, setInteractionState] = useState<{
     readonly copiedRowId: string | null;
     readonly expandedWorkGroups: Record<string, boolean>;
@@ -1206,6 +1208,7 @@
       markdownStyles,
       reviewCommentColors,
       userBubbleColor,
+      viewportWidth,
     }),
     [
       copiedRowId,
@@ -1215,6 +1218,7 @@
       markdownStyles,
       reviewCommentColors,
       userBubbleColor,
+      viewportWidth,
     ],
   );
   const presentedFeed = useMemo(

diff --git a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
--- a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
+++ b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
@@ -1,7 +1,7 @@
 import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
 import { SymbolView } from "expo-symbols";
-import { useMemo, useState } from "react";
-import { Pressable, ScrollView, StyleSheet, TextInput, View } from "react-native";
+import { useCallback, useMemo, useState } from "react";
+import { Alert, Pressable, ScrollView, StyleSheet, TextInput, View } from "react-native";
 import { useSafeAreaInsets } from "react-native-safe-area-context";
 
 import { AppText as Text } from "../../components/AppText";
@@ -10,6 +10,7 @@
 import { relativeTime } from "../../lib/time";
 import { useThemeColor } from "../../lib/useThemeColor";
 import { useProjects, useThreadShells } from "../../state/entities";
+import { useThreadListActions } from "../home/useThreadListActions";
 import { buildThreadNavigationGroups } from "./thread-navigation-groups";
 import { threadStatusTone } from "./threadPresentation";
 
@@ -24,11 +25,23 @@
   const projects = useProjects();
   const threads = useThreadShells();
   const [searchQuery, setSearchQuery] = useState("");
+  const { archiveThread, confirmDeleteThread } = useThreadListActions();
   const groups = useMemo(
     () => buildThreadNavigationGroups({ projects, threads, searchQuery }),
     [projects, searchQuery, threads],
   );
 
+  const handleThreadLongPress = useCallback(
+    (thread: EnvironmentThreadShell) => {
+      Alert.alert(thread.title, undefined, [
+        { text: "Cancel", style: "cancel" },
+        { text: "Archive", onPress: () => archiveThread(thread) },
+        { text: "Delete", style: "destructive", onPress: () => confirmDeleteThread(thread) },
+      ]);
+    },
+    [archiveThread, confirmDeleteThread],
+  );
+
   const backgroundColor = useThemeColor("--color-drawer");
   const borderColor = useThemeColor("--color-border");
   const foregroundColor = useThemeColor("--color-foreground");
@@ -131,6 +144,7 @@
                       accessibilityLabel={thread.title}
                       accessibilityRole="button"
                       accessibilityState={{ selected }}
+                      onLongPress={() => handleThreadLongPress(thread)}
                       onPress={() => props.onSelectThread(thread)}
                       style={({ pressed }) => [
                         styles.threadRow,

diff --git a/apps/mobile/src/features/threads/thread-navigation-groups.ts b/apps/mobile/src/features/threads/thread-navigation-groups.ts
--- a/apps/mobile/src/features/threads/thread-navigation-groups.ts
+++ b/apps/mobile/src/features/threads/thread-navigation-groups.ts
@@ -33,7 +33,9 @@
 
   return groupProjectsByRepository(input).flatMap((group) => {
     const threads = Arr.sort(
-      group.projects.flatMap((projectGroup) => projectGroup.threads),
+      group.projects
+        .flatMap((projectGroup) => projectGroup.threads)
+        .filter((thread) => thread.archivedAt === null),
       threadActivityOrder,
     );
     const title = group.projects[0]?.project.title ?? group.title;

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/src/features/threads/thread-navigation-groups.ts
Comment thread apps/mobile/src/features/threads/ThreadFeed.tsx Outdated
Comment thread apps/mobile/src/screens/index.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

54 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Jun 24, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 5 potential issues.

There are 8 total unresolved issues (including 3 from previous reviews).

Autofix Details

Bugbot Autofix prepared fixes for all 5 issues found in the latest run.

  • ✅ Fixed: Stale tokens after file switch
    • Added contentView.tokensByRowId = [:] in setContentResetKey to clear token state when content resets, matching the pattern already used by setTokensResetKey.
  • ✅ Fixed: All files selection reverts
    • Added an isAllFilesSelectedRef that suppresses onVisibleFileChange scroll-sync events when the user has explicitly selected "All files", preventing the sidebar from reverting to a specific file after the programmatic scroll-to-top animation completes.
  • ✅ Fixed: Thread feed reveals before scroll
    • Added setRevealedThreadId(null) to the threadId change effect so returning to a previously revealed thread correctly hides the feed until the scroll-to-end sequence completes.
  • ✅ Fixed: File tree stuck selection highlight
    • Added setPendingSelection(null) when controlledSelectedPath changes and a 1-second timeout fallback in handleSelectFile to clear optimistic state when the controlled path never catches up.
  • ✅ Fixed: Native payload retry mismatch
    • Changed the hardcoded 'T3ReviewDiffView' string in isPendingNativeViewRegistration to use the NATIVE_REVIEW_DIFF_MODULE_NAME constant ('T3ReviewDiffSurface') so the retry path correctly matches the registered module name.

Create PR

Or push these changes by commenting:

@cursor push 032c0d2718
Preview (032c0d2718)
diff --git a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
--- a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
+++ b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@@ -554,6 +554,7 @@
     lastVisibleFileId = nil
     pendingScrollFileId = nil
     isProgrammaticScrollActive = false
+    contentView.tokensByRowId = [:]
     scrollView.setContentOffset(.zero, animated: false)
     updateViewportFrame()
     applyInitialRowIndexIfNeeded()

diff --git a/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts b/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
--- a/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
+++ b/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
@@ -168,7 +168,8 @@
 
 function isPendingNativeViewRegistration(error: unknown): boolean {
   return (
-    error instanceof Error && error.message.includes("Unable to find the 'T3ReviewDiffView' view")
+    error instanceof Error &&
+    error.message.includes(`Unable to find the '${NATIVE_REVIEW_DIFF_MODULE_NAME}' view`)
   );
 }
 

diff --git a/apps/mobile/src/features/files/FileTreeBrowser.tsx b/apps/mobile/src/features/files/FileTreeBrowser.tsx
--- a/apps/mobile/src/features/files/FileTreeBrowser.tsx
+++ b/apps/mobile/src/features/files/FileTreeBrowser.tsx
@@ -148,6 +148,7 @@
   }, [defaultExpanded]);
 
   useEffect(() => {
+    setPendingSelection(null);
     if (!controlledSelectedPath) {
       return;
     }
@@ -175,12 +176,25 @@
       return next;
     });
   }, []);
+  const pendingSelectionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
   const handleSelectFile = useCallback(
     (path: string) => {
+      if (pendingSelectionTimerRef.current !== null) {
+        clearTimeout(pendingSelectionTimerRef.current);
+      }
       setPendingSelection({
         path,
         selectedPathAtPress: controlledSelectedPathRef.current,
       });
+      pendingSelectionTimerRef.current = setTimeout(() => {
+        pendingSelectionTimerRef.current = null;
+        setPendingSelection((current) =>
+          current?.path === path &&
+          current.selectedPathAtPress === controlledSelectedPathRef.current
+            ? null
+            : current,
+        );
+      }, 1000);
       onSelectFile(path);
     },
     [onSelectFile],

diff --git a/apps/mobile/src/features/review/ReviewSheet.tsx b/apps/mobile/src/features/review/ReviewSheet.tsx
--- a/apps/mobile/src/features/review/ReviewSheet.tsx
+++ b/apps/mobile/src/features/review/ReviewSheet.tsx
@@ -465,8 +465,10 @@
     canHighlight: parsedDiff.kind === "files",
   });
 
+  const isAllFilesSelectedRef = useRef(false);
   const handleSelectFile = useCallback(
     (fileId: string | null) => {
+      isAllFilesSelectedRef.current = fileId === null;
       commentSelection.clearSelection();
       if (fileId !== null && collapsedFileIds.includes(fileId)) {
         toggleExpandedFile(fileId);
@@ -484,7 +486,7 @@
   const handleVisibleFileChange = useCallback(
     (event: NativeSyntheticEvent<{ readonly fileId?: string }>) => {
       const { fileId } = event.nativeEvent;
-      if (!fileId) {
+      if (!fileId || isAllFilesSelectedRef.current) {
         return;
       }
       reviewFileNavigatorRef.current?.setVisibleFile(fileId);

diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx
--- a/apps/mobile/src/features/threads/ThreadFeed.tsx
+++ b/apps/mobile/src/features/threads/ThreadFeed.tsx
@@ -1322,6 +1322,7 @@
       cancelAnimationFrame(revealSettleFrameRef.current);
       revealSettleFrameRef.current = null;
     }
+    setRevealedThreadId(null);
     initialScrollReadyRef.current = false;
     isNearEndRef.current = true;
     lastContentHeightRef.current = 0;

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/src/features/review/ReviewSheet.tsx
Comment thread apps/mobile/src/features/threads/ThreadFeed.tsx Outdated
Comment thread apps/mobile/src/features/files/FileTreeBrowser.tsx
Comment thread apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

There are 4 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Selection timeout reverts highlight
    • The timeout handler now checks whether controlledSelectedPathRef.current has caught up to the tapped path before clearing pending selection, preventing the highlight from reverting during slow navigation.
  • ✅ Fixed: Archive hides active thread
    • Added an onCompleted callback to useThreadListActions and an onThreadRemoved prop to the sidebar that triggers router.back() when the currently-selected thread is archived or deleted.
  • ✅ Fixed: Review navigator stale after reset
    • Added an explicit onVisibleFileChange emission with NSNull() in setContentResetKey so React always receives the 'All files' sync after a content reset, regardless of the lastVisibleFileId guard.

Create PR

Or push these changes by commenting:

@cursor push 058e075f4c
Preview (058e075f4c)
diff --git a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
--- a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
+++ b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@@ -557,6 +557,7 @@
     pendingScrollFileId = nil
     isProgrammaticScrollActive = false
     scrollView.setContentOffset(.zero, animated: false)
+    onVisibleFileChange(["fileId": NSNull()])
     updateViewportFrame()
     applyInitialRowIndexIfNeeded()
   }

diff --git a/apps/mobile/src/features/files/FileTreeBrowser.tsx b/apps/mobile/src/features/files/FileTreeBrowser.tsx
--- a/apps/mobile/src/features/files/FileTreeBrowser.tsx
+++ b/apps/mobile/src/features/files/FileTreeBrowser.tsx
@@ -197,7 +197,11 @@
       });
       pendingSelectionTimeoutRef.current = setTimeout(() => {
         pendingSelectionTimeoutRef.current = null;
-        setPendingSelection((current) => (current?.path === path ? null : current));
+        setPendingSelection((current) => {
+          if (current?.path !== path) return current;
+          if (controlledSelectedPathRef.current === path) return null;
+          return current;
+        });
       }, OPTIMISTIC_SELECTION_TIMEOUT_MS);
       onSelectFile(path);
     },

diff --git a/apps/mobile/src/features/home/useThreadListActions.ts b/apps/mobile/src/features/home/useThreadListActions.ts
--- a/apps/mobile/src/features/home/useThreadListActions.ts
+++ b/apps/mobile/src/features/home/useThreadListActions.ts
@@ -99,11 +99,19 @@
   );
 }
 
-export function useThreadListActions(): {
+export function useThreadListActions(
+  onCompleted?: (action: ThreadListAction, thread: EnvironmentThreadShell) => void,
+): {
   readonly archiveThread: (thread: EnvironmentThreadShell) => void;
   readonly confirmDeleteThread: (thread: EnvironmentThreadShell) => void;
 } {
-  const executeAction = useThreadActionExecutor();
+  const handleCompleted = useCallback(
+    (action: ThreadListAction, thread: EnvironmentThreadShell) => {
+      onCompleted?.(action, thread);
+    },
+    [onCompleted],
+  );
+  const executeAction = useThreadActionExecutor(onCompleted ? handleCompleted : undefined);
 
   const archiveThread = useCallback(
     (thread: EnvironmentThreadShell) => {

diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
--- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
+++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
@@ -238,6 +238,7 @@
               onOpenSettings={() => router.push("/settings")}
               onSelectThread={handleSelectThread}
               onStartNewTask={() => router.push("/new")}
+              onThreadRemoved={() => router.back()}
             />
           </Animated.View>
         ) : null}

diff --git a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
--- a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
+++ b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
@@ -23,13 +23,24 @@
   readonly onOpenSettings: () => void;
   readonly onSelectThread: (thread: EnvironmentThreadShell) => void;
   readonly onStartNewTask: () => void;
+  readonly onThreadRemoved?: (thread: EnvironmentThreadShell) => void;
 }) {
   const insets = useSafeAreaInsets();
   const projects = useProjects();
   const threads = useThreadShells();
   const [searchQuery, setSearchQuery] = useState("");
   const openSwipeableRef = useRef<SwipeableMethods | null>(null);
-  const { archiveThread, confirmDeleteThread } = useThreadListActions();
+  const { onThreadRemoved, selectedThreadKey } = props;
+  const handleThreadActionCompleted = useCallback(
+    (_action: unknown, thread: EnvironmentThreadShell) => {
+      const threadKey = scopedThreadKey(thread.environmentId, thread.id);
+      if (threadKey === selectedThreadKey) {
+        onThreadRemoved?.(thread);
+      }
+    },
+    [onThreadRemoved, selectedThreadKey],
+  );
+  const { archiveThread, confirmDeleteThread } = useThreadListActions(handleThreadActionCompleted);
   const groups = useMemo(
     () => buildThreadNavigationGroups({ projects, threads, searchQuery }),
     [projects, searchQuery, threads],

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/src/features/files/FileTreeBrowser.tsx
Comment thread apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx Outdated
Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@juliusmarminge
juliusmarminge force-pushed the t3code/ipad-responsive-mobile-layout branch from fcaaab4 to 678ebfa Compare June 26, 2026 05:43

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 5 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 5 issues found in the latest run.

  • ✅ Fixed: Content reset keeps stale rows
    • Added rows clearing (rows=[], contentView.rows=[]) and rowsDecodeGeneration increment to setContentResetKey, plus replaced updateViewportFrame with updateContentMetrics to properly recalculate layout after clearing.
  • ✅ Fixed: Native payload dispatch gives up
    • Increased max retry attempts from 4 to 12 with exponential backoff using setTimeout after the initial 4 requestAnimationFrame attempts, and added a console.error when all retries are exhausted.
  • ✅ Fixed: Invalid rows JSON aborts silently
    • Added DispatchQueue.main.async blocks in the UTF-8 guard failure path for both setRowsJson and setTokensJson that clear state and emit debug errors, matching the existing catch-block error handling pattern.
  • ✅ Fixed: Inspector file pick always pushes
    • Updated handleSelectInspectorFile to use resolveFileSelectionNavigationAction and router.replace when fileInspector.supported is true, consistent with ThreadFilesRouteScreen behavior.
  • ✅ Fixed: Sidebar ignores environment filter
    • Added optional environmentId parameter to buildThreadNavigationGroups with project and thread filtering, exposed it via ThreadNavigationSidebar prop, and passed the current thread's environmentId from AdaptiveWorkspaceLayout.

Create PR

Or push these changes by commenting:

@cursor push 8aa37e0484
Preview (8aa37e0484)
diff --git a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
--- a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
+++ b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@@ -420,6 +420,18 @@
 
     payloadDecodeQueue.async { [weak self] in
       guard let data = rowsJson.data(using: .utf8) else {
+        DispatchQueue.main.async { [weak self] in
+          guard let self, generation == self.rowsDecodeGeneration else {
+            return
+          }
+          self.rows = []
+          self.contentView.rows = []
+          self.hasAppliedInitialRowIndex = false
+          self.lastVisibleFileId = nil
+          self.pendingScrollFileId = nil
+          self.updateContentMetrics()
+          self.emitDebug("rows-decode-failed", ["error": "invalid utf8"])
+        }
         return
       }
 
@@ -464,6 +476,13 @@
 
     payloadDecodeQueue.async { [weak self] in
       guard let data = tokensJson.data(using: .utf8) else {
+        DispatchQueue.main.async { [weak self] in
+          guard let self, generation == self.tokensDecodeGeneration else {
+            return
+          }
+          self.contentView.tokensByRowId = [:]
+          self.emitDebug("tokens-decode-failed", ["error": "invalid utf8"])
+        }
         return
       }
 
@@ -550,14 +569,17 @@
     }
 
     self.contentResetKey = contentResetKey
+    rowsDecodeGeneration += 1
     tokensDecodeGeneration += 1
+    rows = []
+    contentView.rows = []
     contentView.tokensByRowId = [:]
     hasAppliedInitialRowIndex = false
     lastVisibleFileId = nil
     pendingScrollFileId = nil
     isProgrammaticScrollActive = false
     scrollView.setContentOffset(.zero, animated: false)
-    updateViewportFrame()
+    updateContentMetrics()
     applyInitialRowIndexIfNeeded()
   }
 

diff --git a/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts b/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
--- a/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
+++ b/apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
@@ -185,7 +185,9 @@
 
     let cancelled = false;
     let frame: number | null = null;
+    let timer: ReturnType<typeof setTimeout> | null = null;
     let attempts = 0;
+    const MAX_ATTEMPTS = 12;
 
     const dispatch = () => {
       if (cancelled) {
@@ -195,15 +197,22 @@
       const view = nativeRef.current;
       const command = view?.[method];
       if (!view || !command) {
-        if (attempts < 4) {
+        if (attempts < MAX_ATTEMPTS) {
           attempts += 1;
-          frame = requestAnimationFrame(dispatch);
+          if (attempts <= 4) {
+            frame = requestAnimationFrame(dispatch);
+          } else {
+            const delay = Math.min(50 * 2 ** (attempts - 5), 1000);
+            timer = setTimeout(dispatch, delay);
+          }
+        } else {
+          console.error(`[native-review-diff] ${method} gave up after ${MAX_ATTEMPTS} attempts`);
         }
         return;
       }
 
       void command.call(view, payload).catch((error: unknown) => {
-        if (!cancelled && attempts < 4 && isPendingNativeViewRegistration(error)) {
+        if (!cancelled && attempts < MAX_ATTEMPTS && isPendingNativeViewRegistration(error)) {
           attempts += 1;
           frame = requestAnimationFrame(dispatch);
           return;
@@ -221,6 +230,9 @@
       if (frame !== null) {
         cancelAnimationFrame(frame);
       }
+      if (timer !== null) {
+        clearTimeout(timer);
+      }
     };
   }, [method, nativeRef, payload]);
 }

diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
--- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
+++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
@@ -223,6 +223,9 @@
             <ThreadNavigationSidebar
               width={layout.listPaneWidth}
               selectedThreadKey={selectedThreadKey}
+              selectedEnvironmentId={
+                environmentId !== null ? EnvironmentId.make(environmentId) : null
+              }
               onOpenSettings={handleOpenSettings}
               onSelectThread={handleSelectThread}
               onStartNewTask={handleStartNewTask}

diff --git a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
--- a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
+++ b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
@@ -1,4 +1,5 @@
 import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
+import type { EnvironmentId } from "@t3tools/contracts";
 import { SymbolView } from "expo-symbols";
 import { memo, useCallback, useMemo, useRef, useState } from "react";
 import type { ColorValue } from "react-native";
@@ -107,6 +108,7 @@
 export function ThreadNavigationSidebar(props: {
   readonly width: number;
   readonly selectedThreadKey: string | null;
+  readonly selectedEnvironmentId?: EnvironmentId | null;
   readonly onOpenSettings: () => void;
   readonly onSelectThread: (thread: EnvironmentThreadShell) => void;
   readonly onStartNewTask: () => void;
@@ -118,8 +120,14 @@
   const openSwipeableRef = useRef<SwipeableMethods | null>(null);
   const { archiveThread, confirmDeleteThread } = useThreadListActions();
   const groups = useMemo(
-    () => buildThreadNavigationGroups({ projects, threads, searchQuery }),
-    [projects, searchQuery, threads],
+    () =>
+      buildThreadNavigationGroups({
+        projects,
+        threads,
+        searchQuery,
+        environmentId: props.selectedEnvironmentId,
+      }),
+    [projects, props.selectedEnvironmentId, searchQuery, threads],
   );
 
   const backgroundColor = useThemeColor("--color-drawer");

diff --git a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
--- a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
+++ b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
@@ -20,6 +20,7 @@
   buildThreadTerminalNavigation,
 } from "../../lib/routes";
 import { scopedThreadKey } from "../../lib/scopedEntities";
+import { resolveFileSelectionNavigationAction } from "../../lib/adaptive-navigation";
 import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
 import { connectionTone } from "../connection/connectionTone";
 import { nativeTopScrollEdgeEffect } from "../../lib/native-scroll-edge-effect";
@@ -382,9 +383,17 @@
       if (selectedThread === null) {
         return;
       }
-      router.push(buildThreadFilesNavigation(selectedThread, path));
+      const destination = buildThreadFilesNavigation(selectedThread, path);
+      const navigationAction = resolveFileSelectionNavigationAction({
+        hasPersistentFileInspector: fileInspector.supported,
+      });
+      if (navigationAction === "replace") {
+        router.replace(destination);
+        return;
+      }
+      router.push(destination);
     },
-    [router, selectedThread],
+    [fileInspector.supported, router, selectedThread],
   );
   const GitInspector = useCallback(
     () => <GitOverviewSheet headerInset={headerHeight} presentation="inspector" />,

diff --git a/apps/mobile/src/features/threads/thread-navigation-groups.ts b/apps/mobile/src/features/threads/thread-navigation-groups.ts
--- a/apps/mobile/src/features/threads/thread-navigation-groups.ts
+++ b/apps/mobile/src/features/threads/thread-navigation-groups.ts
@@ -2,6 +2,7 @@
   EnvironmentProject,
   EnvironmentThreadShell,
 } from "@t3tools/client-runtime/state/shell";
+import type { EnvironmentId } from "@t3tools/contracts";
 import * as Arr from "effect/Array";
 import * as Order from "effect/Order";
 
@@ -28,37 +29,49 @@
   readonly projects: ReadonlyArray<EnvironmentProject>;
   readonly threads: ReadonlyArray<EnvironmentThreadShell>;
   readonly searchQuery?: string;
+  readonly environmentId?: EnvironmentId | null;
 }): ReadonlyArray<ThreadNavigationGroup> {
   const query = input.searchQuery?.trim().toLocaleLowerCase() ?? "";
-  const activeThreads = input.threads.filter((thread) => thread.archivedAt === null);
+  const environmentId = input.environmentId ?? null;
+  const activeThreads = input.threads.filter(
+    (thread) =>
+      thread.archivedAt === null &&
+      (environmentId === null || thread.environmentId === environmentId),
+  );
+  const filteredProjects =
+    environmentId === null
+      ? input.projects
+      : input.projects.filter((project) => project.environmentId === environmentId);
 
-  return groupProjectsByRepository({ ...input, threads: activeThreads }).flatMap((group) => {
-    const threads = Arr.sort(
-      group.projects.flatMap((projectGroup) => projectGroup.threads),
-      threadActivityOrder,
-    );
-    const title = group.projects[0]?.project.title ?? group.title;
-    const groupMatches =
-      query.length === 0 ||
-      title.toLocaleLowerCase().includes(query) ||
-      group.title.toLocaleLowerCase().includes(query) ||
-      group.projects.some((projectGroup) =>
-        projectGroup.project.title.toLocaleLowerCase().includes(query),
+  return groupProjectsByRepository({ projects: filteredProjects, threads: activeThreads }).flatMap(
+    (group) => {
+      const threads = Arr.sort(
+        group.projects.flatMap((projectGroup) => projectGroup.threads),
+        threadActivityOrder,
       );
-    const matchingThreads = groupMatches
-      ? threads
-      : threads.filter((thread) => thread.title.toLocaleLowerCase().includes(query));
+      const title = group.projects[0]?.project.title ?? group.title;
+      const groupMatches =
+        query.length === 0 ||
+        title.toLocaleLowerCase().includes(query) ||
+        group.title.toLocaleLowerCase().includes(query) ||
+        group.projects.some((projectGroup) =>
+          projectGroup.project.title.toLocaleLowerCase().includes(query),
+        );
+      const matchingThreads = groupMatches
+        ? threads
+        : threads.filter((thread) => thread.title.toLocaleLowerCase().includes(query));
 
-    if (query.length > 0 && matchingThreads.length === 0) {
-      return [];
-    }
+      if (query.length > 0 && matchingThreads.length === 0) {
+        return [];
+      }
 
-    return [
-      {
-        key: group.key,
-        title,
-        threads: matchingThreads,
-      },
-    ];
-  });
+      return [
+        {
+          key: group.key,
+          title,
+          threads: matchingThreads,
+        },
+      ];
+    },
+  );
 }

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts
Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx
Comment thread apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
) : null}
</View>
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium threads/ThreadFeed.tsx:825

The renderFeedEntry function no longer handles the queued-message entry type, so queued outbox messages are dropped from the thread feed and users lose the pending bubble text/attachment preview. They can only see a count in the composer, hiding what is actually waiting to send. If this removal is intentional, consider documenting the rationale; otherwise restore the queued-message branch to preserve the pending message UI.

Also found in 3 other location(s)

apps/mobile/src/lib/threadActivity.ts:1253

buildThreadFeed no longer merges locally queued outbox messages into the feed, even though useThreadComposerState still enqueues them and ThreadDetailScreen.handleSendMessage() immediately anchors to the returned messageId. After sending, the just-composed user message is absent from selectedThreadFeed until the backend echoes it back, so the conversation can appear to drop the user's message and the post-send scroll-to-anchor never runs on slow/offline connections.

apps/mobile/src/state/use-thread-composer-state.ts:93

useThreadComposerState no longer passes queued outbox messages into buildThreadFeed, so a newly queued send disappears from the chat transcript until the backend later echoes it back. Before this change buildThreadFeed emitted queued-message entries and ThreadFeed rendered them; now pressing send clears the draft and only increments queueCount, leaving no optimistic message bubble for queued/offline sends.

apps/mobile/src/features/threads/ThreadRouteScreen.tsx:652

ThreadRouteContent passes composer.selectedThreadFeed into ThreadDetailScreen, but the refactored composer state no longer merges queued outbox messages into that feed. After onSendMessage queues a message and clears the draft, the new MessageId never appears in selectedThreadFeed until the server snapshot catches up, so newly sent/offline messages disappear from the conversation UI even though they are still queued for delivery.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/threads/ThreadFeed.tsx around line 825:

The `renderFeedEntry` function no longer handles the `queued-message` entry type, so queued outbox messages are dropped from the thread feed and users lose the pending bubble text/attachment preview. They can only see a count in the composer, hiding what is actually waiting to send. If this removal is intentional, consider documenting the rationale; otherwise restore the `queued-message` branch to preserve the pending message UI.

Also found in 3 other location(s):
- apps/mobile/src/lib/threadActivity.ts:1253 -- `buildThreadFeed` no longer merges locally queued outbox messages into the feed, even though `useThreadComposerState` still enqueues them and `ThreadDetailScreen.handleSendMessage()` immediately anchors to the returned `messageId`. After sending, the just-composed user message is absent from `selectedThreadFeed` until the backend echoes it back, so the conversation can appear to drop the user's message and the post-send scroll-to-anchor never runs on slow/offline connections.
- apps/mobile/src/state/use-thread-composer-state.ts:93 -- `useThreadComposerState` no longer passes queued outbox messages into `buildThreadFeed`, so a newly queued send disappears from the chat transcript until the backend later echoes it back. Before this change `buildThreadFeed` emitted `queued-message` entries and `ThreadFeed` rendered them; now pressing send clears the draft and only increments `queueCount`, leaving no optimistic message bubble for queued/offline sends.
- apps/mobile/src/features/threads/ThreadRouteScreen.tsx:652 -- `ThreadRouteContent` passes `composer.selectedThreadFeed` into `ThreadDetailScreen`, but the refactored composer state no longer merges queued outbox messages into that feed. After `onSendMessage` queues a message and clears the draft, the new `MessageId` never appears in `selectedThreadFeed` until the server snapshot catches up, so newly sent/offline messages disappear from the conversation UI even though they are still queued for delivery.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High

When timelineEntries transitions from empty to non-empty after mount, the component stays scrolled at the top instead of jumping to the live edge. The removed previousRowCountRef effect handled this case in commit 33dadb5a; with only initialScrollAtEnd remaining, users see stale/blank content and the scroll-to-bottom pill shows wrong state until manual scroll.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/chat/MessagesTimeline.tsx around line 286:

When `timelineEntries` transitions from empty to non-empty after mount, the component stays scrolled at the top instead of jumping to the live edge. The removed `previousRowCountRef` effect handled this case in commit `33dadb5a`; with only `initialScrollAtEnd` remaining, users see stale/blank content and the scroll-to-bottom pill shows wrong state until manual scroll.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/src/app/new/_layout.tsx Outdated
Comment thread apps/mobile/src/screens/index.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx
@juliusmarminge
juliusmarminge force-pushed the t3code/ipad-responsive-mobile-layout branch from 678ebfa to babbf90 Compare June 27, 2026 00:25

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale token patches after reset
    • Added tokensDecodeGeneration capture and check in setTokensPatchJson to reject patches decoded after a content reset has bumped the generation counter, matching the same guard pattern already used in setTokensJson.

Create PR

Or push these changes by commenting:

@cursor push 925701f905
Preview (925701f905)
diff --git a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
--- a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
+++ b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@@ -492,6 +492,8 @@
   }
 
   func setTokensPatchJson(_ tokensPatchJson: String) {
+    let generation = tokensDecodeGeneration
+
     payloadDecodeQueue.async { [weak self] in
       guard let data = tokensPatchJson.data(using: .utf8) else {
         return
@@ -500,7 +502,7 @@
       do {
         let patch = try JSONDecoder().decode(ReviewDiffNativeTokenPatch.self, from: data)
         DispatchQueue.main.async { [weak self] in
-          guard let self else {
+          guard let self, generation == self.tokensDecodeGeneration else {
             return
           }
           // A highlighter request from the previous file can finish after the view has

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/plugins/withIosSceneLifecycle.cjs
Comment thread apps/mobile/src/app/_layout.tsx Outdated
Comment thread apps/mobile/plugins/withIosSceneLifecycle.cjs
Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for 3 of the 4 issues found in the latest run.

  • ✅ Fixed: Cmd+F focuses sidebar not composer
    • Suppressed focusSearch key commands (Cmd+F, Cmd+K) in T3KeyboardCommandsView when a UITextField or UITextView is the first responder, preventing the sidebar search from stealing focus from active text inputs.
  • ✅ Fixed: Inspector width ignores sidebar
    • Added an optional sidebarWidth parameter to deriveFileInspectorPaneLayout and passed the sidebar width from both callers, so the inspector constrains its width against the space remaining after the sidebar rather than the full viewport.
  • ✅ Fixed: Keyboard files bypass inspector
    • Registered a files keyboard command handler in ThreadRouteContent that opens the inspector pane via showAuxiliaryPane when the file inspector is supported in split view, falling through to the default router.push behavior only when it is not.

Create PR

Or push these changes by commenting:

@cursor push 07a67ed044
Preview (07a67ed044)
diff --git a/apps/mobile/modules/t3-native-controls/ios/T3KeyboardCommandsModule.swift b/apps/mobile/modules/t3-native-controls/ios/T3KeyboardCommandsModule.swift
--- a/apps/mobile/modules/t3-native-controls/ios/T3KeyboardCommandsModule.swift
+++ b/apps/mobile/modules/t3-native-controls/ios/T3KeyboardCommandsModule.swift
@@ -21,10 +21,13 @@
   public override var canBecomeFirstResponder: Bool { true }
 
   public override var keyCommands: [UIKeyCommand]? {
-    [
+    let responder = window?.t3FirstResponder
+    let textInputActive = responder is UITextField || responder is UITextView
+
+    return [
       enabledCommand("newTask", input: "n", modifiers: .command, action: #selector(newTask), title: "New Task"),
-      enabledCommand("focusSearch", input: "f", modifiers: .command, action: #selector(focusSearch), title: "Find"),
-      enabledCommand("focusSearch", input: "k", modifiers: .command, action: #selector(focusSearch), title: "Focus Search"),
+      textInputActive ? nil : enabledCommand("focusSearch", input: "f", modifiers: .command, action: #selector(focusSearch), title: "Find"),
+      textInputActive ? nil : enabledCommand("focusSearch", input: "k", modifiers: .command, action: #selector(focusSearch), title: "Focus Search"),
       enabledCommand("back", input: "[", modifiers: .command, action: #selector(goBack), title: "Back"),
       enabledCommand("files", input: "f", modifiers: [.command, .shift], action: #selector(openFiles), title: "Open Files"),
       enabledCommand("terminal", input: "t", modifiers: [.command, .shift], action: #selector(openTerminal), title: "Open Terminal"),

diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
--- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
+++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
@@ -131,6 +131,7 @@
         layout,
         viewportWidth: width,
         preferredWidth: fileInspectorPreferredWidth ?? undefined,
+        sidebarWidth: layout.listPaneWidth ?? 0,
       }),
     [fileInspectorPreferredWidth, layout, width],
   );

diff --git a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
--- a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
+++ b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx
@@ -60,6 +60,7 @@
   useAdaptiveWorkspaceLayout,
   useAdaptiveWorkspacePaneRole,
 } from "../layout/AdaptiveWorkspaceLayout";
+import { useHardwareKeyboardCommand } from "../keyboard/hardwareKeyboardCommands";
 import { WorkspaceSidebarToolbar } from "../layout/workspace-sidebar-toolbar";
 import { ThreadFileNavigatorPane } from "../files/thread-file-navigator-pane";
 import {
@@ -377,6 +378,14 @@
     }
     action.toggleAuxiliaryPane();
   }, []);
+  const handleFilesKeyboardCommand = useCallback(() => {
+    if (fileInspector.supported && selectedThreadCwd !== null) {
+      handleOpenFilesInspector();
+      return true;
+    }
+    return false;
+  }, [fileInspector.supported, handleOpenFilesInspector, selectedThreadCwd]);
+  useHardwareKeyboardCommand("files", handleFilesKeyboardCommand);
   const handleSelectInspectorFile = useCallback(
     (path: string) => {
       if (selectedThread === null) {

diff --git a/apps/mobile/src/lib/layout.test.ts b/apps/mobile/src/lib/layout.test.ts
--- a/apps/mobile/src/lib/layout.test.ts
+++ b/apps/mobile/src/lib/layout.test.ts
@@ -164,7 +164,7 @@
       contentPaneWidth: 1_024,
       supportsAuxiliaryPane: true,
       auxiliaryPaneVisible: true,
-      auxiliaryPaneWidth: 287,
+      auxiliaryPaneWidth: 260,
     });
   });
 
@@ -185,7 +185,7 @@
       contentPaneWidth: 986,
       supportsAuxiliaryPane: true,
       auxiliaryPaneVisible: true,
-      auxiliaryPaneWidth: 320,
+      auxiliaryPaneWidth: 276,
     });
   });
 

diff --git a/apps/mobile/src/lib/layout.ts b/apps/mobile/src/lib/layout.ts
--- a/apps/mobile/src/lib/layout.ts
+++ b/apps/mobile/src/lib/layout.ts
@@ -100,6 +100,7 @@
       layout: input.layout,
       viewportWidth,
       preferredWidth: input.auxiliaryPanePreferredWidth,
+      sidebarWidth: preferredPrimarySidebarWidth,
     });
     const auxiliaryPaneVisible = fileInspector.supported && input.auxiliaryPanePreferredVisible;
     const primarySidebarSuppressedByAuxiliary =
@@ -151,10 +152,13 @@
   readonly layout: Layout;
   readonly viewportWidth: number;
   readonly preferredWidth?: number;
+  readonly sidebarWidth?: number;
 }): FileInspectorPaneLayout {
   const viewportWidth = Math.max(0, input.viewportWidth);
+  const sidebarWidth = input.sidebarWidth ?? 0;
   const supported =
     input.layout.usesSplitView && viewportWidth >= FILE_INSPECTOR_MIN_VIEWPORT_WIDTH;
+  const availableWidth = Math.max(0, viewportWidth - sidebarWidth);
 
   return {
     supported,
@@ -163,11 +167,11 @@
           preferredWidth:
             input.preferredWidth ??
             clamp(
-              Math.round(viewportWidth * 0.28),
+              Math.round(availableWidth * 0.28),
               AUXILIARY_PANE_MIN_WIDTH,
               AUXILIARY_PANE_DEFAULT_MAX_WIDTH,
             ),
-          availableWidth: viewportWidth,
+          availableWidth,
         })
       : null,
   };

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx
Comment thread apps/mobile/src/lib/layout.ts
Comment thread apps/mobile/src/features/layout/adaptive-inspector-layout.tsx Outdated
Comment thread apps/mobile/src/features/home/workspace-connection-status.ts
Comment thread apps/mobile/src/features/layout/workspace-pane-divider.tsx
Comment thread apps/mobile/src/lib/layout.ts
Comment thread apps/mobile/src/features/threads/sidebar-filter-button.ios.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadFeed.tsx Outdated
Comment thread apps/mobile/src/features/terminal/NativeTerminalSurface.tsx
<NativeTerminalSurfaceView
appearanceScheme={appearanceScheme}
backgroundColor={theme.background}
focusRequest={props.keyboardFocusRequest ?? 0}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High terminal/NativeTerminalSurface.tsx:218

On Android, focusRequest has no effect because T3TerminalModule does not declare a @ReactProp(name = "focusRequest") handler. The JS side increments keyboardFocusRequest and forwards it via focusRequest, but the native Android view ignores the prop, so once the soft keyboard is dismissed it cannot be reopened. If Android support for this flow is intended, the native module needs to handle focusRequest and call requestFocus()/show the soft input accordingly.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/terminal/NativeTerminalSurface.tsx around line 218:

On Android, `focusRequest` has no effect because `T3TerminalModule` does not declare a `@ReactProp(name = "focusRequest")` handler. The JS side increments `keyboardFocusRequest` and forwards it via `focusRequest`, but the native Android view ignores the prop, so once the soft keyboard is dismissed it cannot be reopened. If Android support for this flow is intended, the native module needs to handle `focusRequest` and call `requestFocus()`/show the soft input accordingly.

Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment thread apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
Comment thread apps/mobile/src/features/home/workspace-connection-status.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

There are 6 total unresolved issues (including 2 from previous reviews).

Autofix Details

Bugbot Autofix prepared fixes for all 4 issues found in the latest run.

  • ✅ Fixed: Files shortcut no-op split view
    • Added a guard to only intercept the keyboard shortcut when auxiliaryPaneRole is already 'inspector', otherwise falling through to route navigation which properly initializes inspector content and role.
  • ✅ Fixed: Connection banner misleading label
    • Added a 'Connection error' label case in workspaceConnectionStatusLabel when state.connectionError is non-null, so partial failures don't show misleading 'Not connected' text.
  • ✅ Fixed: VoiceOver widens pane incorrectly
    • Removed the resizeDirection multiplier from accessibility increment/decrement actions so they always widen and narrow respectively regardless of divider position, matching the fixed labels.
  • ✅ Fixed: Header blur ignores scroll position
    • Added a listScrollOffsetRef to track actual scroll position and use it in reportInitialHeaderMaterialVisibility when available, preventing content size changes from incorrectly enabling header material when user is at the top.

Create PR

Or push these changes by commenting:

@cursor push b75ca20d39
Preview (b75ca20d39)
diff --git a/apps/mobile/src/features/home/workspace-connection-status.ts b/apps/mobile/src/features/home/workspace-connection-status.ts
--- a/apps/mobile/src/features/home/workspace-connection-status.ts
+++ b/apps/mobile/src/features/home/workspace-connection-status.ts
@@ -17,5 +17,6 @@
   if (state.connectingEnvironments.length > 1) {
     return `Reconnecting ${state.connectingEnvironments.length} environments`;
   }
+  if (state.connectionError !== null) return "Connection error";
   return "Not connected";
 }

diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
--- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
+++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
@@ -199,9 +199,12 @@
     if (!layout.usesSplitView || !fileInspector.supported || !parseActiveThreadPath(pathname)) {
       return false;
     }
+    if (auxiliaryPaneRole !== "inspector") {
+      return false;
+    }
     showAuxiliaryPane("inspector");
     return true;
-  }, [fileInspector.supported, layout.usesSplitView, pathname, showAuxiliaryPane]);
+  }, [auxiliaryPaneRole, fileInspector.supported, layout.usesSplitView, pathname, showAuxiliaryPane]);
   useHardwareKeyboardCommand("files", handleOpenFilesCommand);
   const toggleAuxiliaryPane = useCallback(() => {
     if (auxiliaryPaneRole === "inspector") {

diff --git a/apps/mobile/src/features/layout/workspace-pane-divider.tsx b/apps/mobile/src/features/layout/workspace-pane-divider.tsx
--- a/apps/mobile/src/features/layout/workspace-pane-divider.tsx
+++ b/apps/mobile/src/features/layout/workspace-pane-divider.tsx
@@ -59,9 +59,9 @@
   const handleAccessibilityAction = (event: AccessibilityActionEvent) => {
     props.onResizeStart?.();
     if (event.nativeEvent.actionName === "increment") {
-      props.onResizeBy(ACCESSIBILITY_RESIZE_STEP * props.resizeDirection);
+      props.onResizeBy(ACCESSIBILITY_RESIZE_STEP);
     } else if (event.nativeEvent.actionName === "decrement") {
-      props.onResizeBy(-ACCESSIBILITY_RESIZE_STEP * props.resizeDirection);
+      props.onResizeBy(-ACCESSIBILITY_RESIZE_STEP);
     }
     props.onResizeEnd?.();
   };

diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx
--- a/apps/mobile/src/features/threads/ThreadFeed.tsx
+++ b/apps/mobile/src/features/threads/ThreadFeed.tsx
@@ -1128,6 +1128,7 @@
   const foldSettleSecondFrameRef = useRef<number | null>(null);
   const disclosureAnchorKeyRef = useRef<string | null>(null);
   const headerMaterialVisibleRef = useRef(false);
+  const listScrollOffsetRef = useRef<number | null>(null);
   const listContentHeightRef = useRef(0);
   const listViewportHeightRef = useRef(0);
   const previousLatestTurnRef = useRef(props.latestTurn);
@@ -1230,11 +1231,16 @@
   );
   const handleScroll = useCallback(
     (event: NativeSyntheticEvent<NativeScrollEvent>) => {
+      listScrollOffsetRef.current = event.nativeEvent.contentOffset.y;
       reportHeaderMaterialVisibility(event.nativeEvent.contentOffset.y + topContentInset > 6);
     },
     [reportHeaderMaterialVisibility, topContentInset],
   );
   const reportInitialHeaderMaterialVisibility = useCallback(() => {
+    if (listScrollOffsetRef.current !== null) {
+      reportHeaderMaterialVisibility(listScrollOffsetRef.current + topContentInset > 6);
+      return;
+    }
     const topInsetContribution = props.usesAutomaticContentInsets ? topContentInset : 0;
     reportHeaderMaterialVisibility(
       listContentHeightRef.current - listViewportHeightRef.current + topInsetContribution > 6,
@@ -1265,6 +1271,7 @@
 
   useEffect(() => {
     listContentHeightRef.current = 0;
+    listScrollOffsetRef.current = null;
     reportHeaderMaterialVisibility(false);
   }, [props.threadId, reportHeaderMaterialVisibility]);

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
Comment thread apps/mobile/src/features/home/workspace-connection-status.ts
Comment thread apps/mobile/src/features/layout/workspace-pane-divider.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadFeed.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Send guard cleared mid-flight
    • Added a generation counter (sendGenerationRef) that increments on thread switch; the finally block in handleSend now only clears sendInFlightRef when the generation matches, preventing a stale finally from clearing the guard for a different thread's in-flight send.

Create PR

Or push these changes by commenting:

@cursor push 67763defc2
Preview (67763defc2)
diff --git a/apps/mobile/src/features/threads/ThreadComposer.tsx b/apps/mobile/src/features/threads/ThreadComposer.tsx
--- a/apps/mobile/src/features/threads/ThreadComposer.tsx
+++ b/apps/mobile/src/features/threads/ThreadComposer.tsx
@@ -210,6 +210,7 @@
   const [isFocused, setIsFocused] = useState(false);
   const wasExpandedBeforePreviewRef = useRef(false);
   const sendInFlightRef = useRef(false);
+  const sendGenerationRef = useRef(0);
   const { onExpandedChange } = props;
 
   const [previewImageUri, setPreviewImageUri] = useState<string | null>(null);
@@ -218,6 +219,7 @@
   const canSend = hasContent;
 
   useEffect(() => {
+    sendGenerationRef.current++;
     sendInFlightRef.current = false;
   }, [props.selectedThread.id]);
 
@@ -456,10 +458,13 @@
   const handleSend = useCallback(async () => {
     if (!canSend || sendInFlightRef.current) return;
     sendInFlightRef.current = true;
+    const generation = sendGenerationRef.current;
     try {
       await onSendMessage();
     } finally {
-      sendInFlightRef.current = false;
+      if (sendGenerationRef.current === generation) {
+        sendInFlightRef.current = false;
+      }
     }
   }, [canSend, onSendMessage]);
   const handleCommandSelect = useCallback(

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/src/features/threads/ThreadComposer.tsx Outdated
Comment thread apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx
renderInspector={renderInspector}
/>
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split file route hides preview

High Severity

Moving the fileInspector.supported check to occur earlier causes ThreadRouteScreen to return prematurely. This prevents file content from rendering in the main pane and bypasses crucial checks for cwd or invalid paths, leading to an empty inspector and chat instead of the expected file view or error state.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5efa2df. Configure here.

Comment thread apps/mobile/src/app/_layout.tsx
Comment thread apps/mobile/src/app/new/_layout.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadRouteScreen.tsx Outdated
Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/src/features/threads/ThreadComposer.tsx Outdated
Comment thread apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx Outdated
Comment thread apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx
Comment thread apps/mobile/src/features/files/ThreadFilesRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 7 total unresolved issues (including 5 from previous reviews).

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Stale scroll size after reset
    • Added updateContentMetrics() call in setContentResetKey to sync scrollView.contentSize with the cleared rows, replacing the insufficient updateViewportFrame() call.
  • ✅ Fixed: Files shortcut drops open file
    • Restored handleOpenFilesCommand to call showAuxiliaryPane("inspector") instead of router.replace to the files index route, preserving the current file and properly toggling the inspector pane.

Create PR

Or push these changes by commenting:

@cursor push 4a3f5a2cac
Preview (4a3f5a2cac)
diff --git a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
--- a/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
+++ b/apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
@@ -565,7 +565,7 @@
     pendingScrollFileId = nil
     isProgrammaticScrollActive = false
     scrollView.setContentOffset(.zero, animated: false)
-    updateViewportFrame()
+    updateContentMetrics()
     applyInitialRowIndexIfNeeded()
   }
 

diff --git a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
--- a/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
+++ b/apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx
@@ -30,7 +30,7 @@
   type WorkspacePaneLayout,
 } from "../../lib/layout";
 import { resolveThreadSelectionNavigationAction } from "../../lib/adaptive-navigation";
-import { buildThreadFilesNavigation, buildThreadRoutePath } from "../../lib/routes";
+import { buildThreadRoutePath } from "../../lib/routes";
 import { scopedThreadKey } from "../../lib/scopedEntities";
 import {
   parseActiveThreadPath,
@@ -204,13 +204,12 @@
     setSupplementaryPanePreferredVisible(true);
   }, []);
   const handleOpenFilesCommand = useCallback(() => {
-    const activeThread = parseActiveThreadPath(pathname);
-    if (!layout.usesSplitView || !fileInspector.supported || activeThread === null) {
+    if (!layout.usesSplitView || !fileInspector.supported || !parseActiveThreadPath(pathname)) {
       return false;
     }
-    router.replace(buildThreadFilesNavigation(activeThread));
+    showAuxiliaryPane("inspector");
     return true;
-  }, [fileInspector.supported, layout.usesSplitView, pathname, router]);
+  }, [fileInspector.supported, layout.usesSplitView, pathname, showAuxiliaryPane]);
   useHardwareKeyboardCommand("files", handleOpenFilesCommand);
   const toggleAuxiliaryPane = useCallback(() => {
     if (auxiliaryPaneRole === "inspector") {

You can send follow-ups to the cloud agent here.

Comment thread apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift
Comment thread apps/mobile/src/features/layout/AdaptiveWorkspaceLayout.tsx Outdated
Julius Marminge and others added 11 commits July 2, 2026 21:18
- Rework mobile routes around a static native stack layout
- Add iPad-friendly sheet and header behavior across core screens
- Update DPoP hashing and review diff pull-to-refresh support
- Scale text and markdown/code surfaces from appearance settings
- Keep workspace layout stable when overlay sheets are open
- Refresh mobile settings and spacing to fit iPad-style screens
Sidebar (iPad): host the pane in a navigation-inert single-screen native
stack so the column gets a real UINavigationBar — inline leading title,
glass bar-button items (filter menu + settings), pinned UISearchController
search field, native scroll-edge blur. Row actions move from a per-row
@react-native-menu ⋯ button (the component behind Fabric unmount-index
crashes) to a Messages-style context menu on long-press/right-click, with
swipe actions unchanged. Row swipes now fail on vertically-dominant pans
(patched failOffsetY into RNGH ReanimatedSwipeable) so trackpad scrolling
can't open rows.

Thread lists (compact): group collapse + show-more display model
(homeListItems), PR status via use-thread-pr, unified status resolution in
threadPresentation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract the group header (collapse), thread row (status pill, PR badge,
branch subtitle, timestamp), and show-more row into thread-list-items with
compact/sidebar variants, and drive the sidebar from the same homeListItems
display model as Home. The iPad sidebar gains group collapse, show-more
pagination, PR badges, and project favicons; both lists now share the
swipe actions and the long-press/right-click context menu, plus one
homeListItemsAreEqual implementation.

Sidebar rows keep their selected-state bubble and tighter metrics via the
"sidebar" variant; Home keeps its full-bleed separator look via "compact".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Patch @react-native-menu so shouldOpenOnLongPress mode stops the UIButton
contentView from swallowing touches: the button sits full-bounds IN FRONT of
the React children, which made row taps dead when MenuView wraps a row. In
long-press mode the button now passes touches through and the
UIContextMenuInteraction is hosted by the component view instead, so taps
reach the row Pressable while long-press / pointer right-click present the
menu with the row as the zoom preview (requires a native rebuild).

Rows swipes are additionally gated on list scroll activity
(useSwipeableScrollGate, mirroring UIKit's !isDragging && !isDecelerating):
failOffsetY covers the first pan, but trackpad scroll sessions spawn fresh
gesture sessions whose reset translation could re-activate a swipe
mid-scroll. Lists also opt out of item recycling explicitly (rows carry
hover state and vcs subscriptions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
recycleItems={false} was only silencing the LegendList hint — this turns
recycling ON so scrolling reuses mounted rows (each carries a reanimated
swipeable, gesture handlers, a MenuView, and a vcs-status subscription)
instead of unmounting/remounting them, which was the actual jank source.

Recycling safety: row hover state moves to useRecyclingState (auto-resets on
container reuse) and ThreadSwipeable gains a resetKey that snaps a reused
swipeable back to closed so open/mid-drag state can't leak onto another
thread's row. Compact row estimate corrected to 72pt and drawDistance raised
to 500 so fast scrolls hit pre-rendered rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flipping the gate through list extraData + renderItem deps re-rendered every
visible row (hooks, vcs subscriptions and all) exactly at scroll start — the
moment frame budget matters most. As a context value consumed inside
ThreadSwipeable, only the swipeable re-renders and the row's expensive work
is skipped. Also drops searchQuery from the sidebar extraData: rows never
read it, so typing was re-rendering the whole visible list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Move file and review inspectors into dedicated workspace columns
- Smooth pane animations and preserve route context across transitions
- Add native hardware keyboard handling for terminal control keys
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarminge force-pushed the t3code/ipad-responsive-mobile-layout branch from 770c781 to 9f54b06 Compare July 3, 2026 04:19
return (
<>
<Stack.Screen
<NativeStackScreenOptions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium terminal/ThreadTerminalRouteScreen.tsx:856

The new header hard-codes title: "Terminal" and only sets unstable_headerSubtitle to selectedThreadProject?.title on iOS. The previous header rendered the environment label plus project name on the first line and the live terminal cwd/workspace root on a second line, so each terminal session was distinguishable. After this change, Android shows no per-session context at all (the subtitle is gated behind Platform.OS === "ios"), and even on iOS the current working directory is dropped. Different terminal sessions and routes now produce an identical header, making them indistinguishable. Consider restoring the environment label, project name, and live cwd in the header, or document why the per-session context was intentionally removed.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx around line 856:

The new header hard-codes `title: "Terminal"` and only sets `unstable_headerSubtitle` to `selectedThreadProject?.title` on iOS. The previous header rendered the environment label plus project name on the first line and the live terminal `cwd`/workspace root on a second line, so each terminal session was distinguishable. After this change, Android shows no per-session context at all (the subtitle is gated behind `Platform.OS === "ios"`), and even on iOS the current working directory is dropped. Different terminal sessions and routes now produce an identical header, making them indistinguishable. Consider restoring the environment label, project name, and live `cwd` in the header, or document why the per-session context was intentionally removed.

Comment on lines +137 to +139
const onMomentumScrollBegin = useCallback(() => {
clearSettle();
}, [clearSettle]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium home/thread-swipe-actions.tsx:137

A short high-velocity flick can enter momentum before onScroll crosses the 4 px threshold while draggingRef.current is still true. In that case onMomentumScrollBegin cancels the settle timer but leaves gateActive false, so swipeEnabled stays true and rows can be swiped open mid-scroll during the deceleration phase — the exact interaction this hook is meant to block. Consider arming the gate in onMomentumScrollBegin (e.g. when the drag moved vertically) so the deceleration phase is also gated.

  const onMomentumScrollBegin = useCallback(() => {
+    if (gateActiveRef.current === false && draggingRef.current) {
+      update(true);
+    }
    draggingRef.current = false;
    clearSettle();
  }, [clearSettle, update]);
Also found in 1 other location(s)

apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx:472

ArchivedThreadsScreen now passes simultaneousSwipeGesture={archiveScrollGesture} into every ArchivedThreadRow, but the screen never installs useSwipeableScrollGate/SwipeableScrollGateProvider like the other LegendList swipeable screens do. In ThreadSwipeable, the inline comments explain that once the row swipe pan runs simultaneously with the list scroll gesture, trackpad/two-finger scroll sessions with a little horizontal drift can start opening rows mid-scroll unless the gate is active. This regression is introduced here because the archive list enables simultaneous gestures at line 472 without the companion gate, so simply scrolling the archive on iPad/trackpad can trigger swipe actions unintentionally.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/home/thread-swipe-actions.tsx around lines 137-139:

A short high-velocity flick can enter momentum before `onScroll` crosses the 4 px threshold while `draggingRef.current` is still true. In that case `onMomentumScrollBegin` cancels the settle timer but leaves `gateActive` false, so `swipeEnabled` stays true and rows can be swiped open mid-scroll during the deceleration phase — the exact interaction this hook is meant to block. Consider arming the gate in `onMomentumScrollBegin` (e.g. when the drag moved vertically) so the deceleration phase is also gated.

Also found in 1 other location(s):
- apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx:472 -- `ArchivedThreadsScreen` now passes `simultaneousSwipeGesture={archiveScrollGesture}` into every `ArchivedThreadRow`, but the screen never installs `useSwipeableScrollGate`/`SwipeableScrollGateProvider` like the other `LegendList` swipeable screens do. In `ThreadSwipeable`, the inline comments explain that once the row swipe pan runs simultaneously with the list scroll gesture, trackpad/two-finger scroll sessions with a little horizontal drift can start opening rows mid-scroll unless the gate is active. This regression is introduced here because the archive list enables simultaneous gestures at line 472 without the companion gate, so simply scrolling the archive on iPad/trackpad can trigger swipe actions unintentionally.

Comment on lines +243 to +249

if (current === null || current.mode === "route") {
return { routeThreadIdentity, mode: "route" };
}

return { ...current, routeThreadIdentity };
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium threads/ThreadRouteScreen.tsx:243

When ThreadRouteContent switches from a plain chat route to one that provides renderInspector, the effect that updates inspectorSelection preserves an existing non-"route" mode instead of switching to "route". If the Git inspector was open before navigating to a file route, inspectorMode stays "git", so the file route renders with the Git pane still occupying the inspector column instead of the route inspector the new screen registered. Consider resetting to "route" whenever props.renderInspector becomes defined, regardless of the prior mode.

Suggested change
if (current === null || current.mode === "route") {
return { routeThreadIdentity, mode: "route" };
}
return { ...current, routeThreadIdentity };
});
if (current === null || current.mode === "route") {
return { routeThreadIdentity, mode: "route" };
}
return { ...current, routeThreadIdentity };
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/threads/ThreadRouteScreen.tsx around lines 243-249:

When `ThreadRouteContent` switches from a plain chat route to one that provides `renderInspector`, the effect that updates `inspectorSelection` preserves an existing non-`"route"` mode instead of switching to `"route"`. If the Git inspector was open before navigating to a file route, `inspectorMode` stays `"git"`, so the file route renders with the Git pane still occupying the inspector column instead of the route inspector the new screen registered. Consider resetting to `"route"` whenever `props.renderInspector` becomes defined, regardless of the prior mode.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

There are 33 total unresolved issues (including 30 from previous reviews).

Fix All in Cursor

Bugbot Autofix prepared fixes for 2 of the 3 issues found in the latest run.

  • ✅ Fixed: Settings expand no longer works
    • Added a SettingsSheetDetentLayout that reads isExpanded from ClerkSettingsSheetDetentProvider and dynamically updates sheetAllowedDetents via navigation.setOptions(), and added collapse() cleanup to ArchivedThreadsRouteScreen's focus effect.
  • ✅ Fixed: Compact sidebar search diverges
    • Replaced HomeRouteScreen's local searchQuery useState with the shared primarySidebarSearchQuery from the workspace context so both compact and split layouts use the same search state.

Create PR

Or push these changes by commenting:

@cursor push 2d4f46256f
Preview (2d4f46256f)
diff --git a/apps/mobile/src/Stack.tsx b/apps/mobile/src/Stack.tsx
--- a/apps/mobile/src/Stack.tsx
+++ b/apps/mobile/src/Stack.tsx
@@ -10,13 +10,17 @@
   createNativeStackScreen,
   type NativeStackNavigationOptions,
 } from "@react-navigation/native-stack";
+import { useEffect } from "react";
 import { DynamicColorIOS, Platform, Pressable, ScrollView, StyleSheet } from "react-native";
 import { useResolveClassNames } from "uniwind";
 
 import { AppText as Text } from "./components/AppText";
 import { ArchivedThreadsRouteScreen } from "./features/archive/ArchivedThreadsRouteScreen";
 import { useAgentNotificationNavigation } from "./features/agent-awareness/notificationNavigation";
-import { ClerkSettingsSheetDetentProvider } from "./features/cloud/ClerkSettingsSheetDetent";
+import {
+  ClerkSettingsSheetDetentProvider,
+  useClerkSettingsSheetDetent,
+} from "./features/cloud/ClerkSettingsSheetDetent";
 import { ThreadFilesTreeScreen, ThreadFileScreen } from "./features/files/ThreadFilesRouteScreen";
 import { AdaptiveWorkspaceLayout } from "./features/layout/AdaptiveWorkspaceLayout";
 import { HardwareKeyboardCommandProvider } from "./features/keyboard/HardwareKeyboardCommandProvider";
@@ -307,6 +311,19 @@
   );
 }
 
+function SettingsSheetDetentLayout(props: { readonly children: React.ReactNode }) {
+  const { isExpanded } = useClerkSettingsSheetDetent();
+  const navigation = useNavigation();
+
+  useEffect(() => {
+    navigation.setOptions({
+      sheetAllowedDetents: isExpanded ? [0.92] : [0.7, 0.92],
+    });
+  }, [isExpanded, navigation]);
+
+  return <>{props.children}</>;
+}
+
 export const RootStack = createNativeStackNavigator({
   initialRouteName: "Home",
   layout: RootStackLayout,
@@ -404,6 +421,7 @@
     SettingsSheet: createNativeStackScreen({
       screen: SettingsSheetStack,
       linking: "settings",
+      layout: SettingsSheetDetentLayout,
       options: {
         gestureEnabled: true,
         headerShown: false,

diff --git a/apps/mobile/src/features/archive/ArchivedThreadsRouteScreen.tsx b/apps/mobile/src/features/archive/ArchivedThreadsRouteScreen.tsx
--- a/apps/mobile/src/features/archive/ArchivedThreadsRouteScreen.tsx
+++ b/apps/mobile/src/features/archive/ArchivedThreadsRouteScreen.tsx
@@ -18,7 +18,7 @@
 } from "./useArchivedThreadSnapshots";
 
 export function ArchivedThreadsRouteScreen() {
-  const { expand } = useClerkSettingsSheetDetent();
+  const { expand, collapse } = useClerkSettingsSheetDetent();
   const { savedConnectionsById } = useSavedRemoteConnections();
   const [searchQuery, setSearchQuery] = useState("");
   const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<EnvironmentId | null>(null);
@@ -72,7 +72,8 @@
     useCallback(() => {
       expand();
       refresh();
-    }, [expand, refresh]),
+      return () => collapse();
+    }, [collapse, expand, refresh]),
   );
 
   return (

diff --git a/apps/mobile/src/features/home/HomeRouteScreen.tsx b/apps/mobile/src/features/home/HomeRouteScreen.tsx
--- a/apps/mobile/src/features/home/HomeRouteScreen.tsx
+++ b/apps/mobile/src/features/home/HomeRouteScreen.tsx
@@ -1,7 +1,7 @@
 import * as Arr from "effect/Array";
 import * as Order from "effect/Order";
 import { useNavigation } from "@react-navigation/native";
-import { useMemo, useState } from "react";
+import { useMemo } from "react";
 
 import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
 import { useProjects, useThreadShells } from "../../state/entities";
@@ -18,13 +18,16 @@
 /* ─── Route screen ───────────────────────────────────────────────────── */
 
 export function HomeRouteScreen() {
-  const { layout } = useAdaptiveWorkspaceLayout();
+  const {
+    layout,
+    primarySidebarSearchQuery: searchQuery,
+    setPrimarySidebarSearchQuery: setSearchQuery,
+  } = useAdaptiveWorkspaceLayout();
   const projects = useProjects();
   const threads = useThreadShells();
   const { state: catalogState } = useWorkspaceState();
   const { savedConnectionsById } = useSavedRemoteConnections();
   const navigation = useNavigation();
-  const [searchQuery, setSearchQuery] = useState("");
   const { archiveThread, confirmDeleteThread } = useThreadListActions();
   const environments = useMemo(
     () =>

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 9f54b06. Configure here.

Comment thread apps/mobile/src/Stack.tsx
presentation: "formSheet",
sheetAllowedDetents: [0.7, 0.92],
sheetGrabberVisible: true,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings expand no longer works

Medium Severity

The SettingsSheet's sheetAllowedDetents are now fixed in the RootStack configuration. This prevents the ClerkSettingsSheetDetentProvider's expand() function from dynamically adjusting the sheet's height, so flows like archive, auth, and waitlist no longer open at their intended taller detent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f54b06. Configure here.

return;
}
setFileInspectorPreferredVisible(false);
navigation.navigate("Thread", params);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sidebar thread pick stacks routes

High Severity

In split view on the base thread route, sidebar selection is meant to update the open thread in place (set-params), but the handler calls navigation.navigate("Thread", …) instead. Each sidebar pick can push another Thread screen, breaking back navigation and leaving stale routes on the stack.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f54b06. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bugbot Autofix determined this is a false positive.

React Navigation's navigate() on a stack correctly finds an existing screen by name and updates its params in place rather than pushing a duplicate, and Thread has no getId that would cause distinct identity matching.

You can send follow-ups to the cloud agent here.

const { state: catalogState } = useWorkspaceState();
const { savedConnectionsById } = useSavedRemoteConnections();
const navigation = useNavigation();
const [searchQuery, setSearchQuery] = useState("");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compact sidebar search diverges

Low Severity

Compact home keeps thread search in local state, while split layout uses separate primarySidebarSearchQuery in the workspace shell. Resizing into split view (or starting split after searching on compact home) drops the active filter because the two queries are never synchronized.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9f54b06. Configure here.

UIApplication.shared,
continue: userActivity,
restorationHandler: { _ in })
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High plugins/withIosSceneLifecycle.cjs:45

SceneDelegate.scene(_:willConnectTo:options:) forwards connectionOptions.urlContexts and connectionOptions.userActivities to the AppDelegate but drops connectionOptions.notificationResponse. A cold start from a push notification tap delivers the launch payload through that field, so the app never records the initial notification response. Since the app reads Notifications.getLastNotificationResponseAsync() on startup to route notification deep links, taps from a terminated state will stop navigating to the intended thread. Consider forwarding connectionOptions.notificationResponse (e.g., via appDelegate.application(..., didReceiveRemoteNotification:...) or an equivalent AppDelegate hook) so the response is captured on scene connect.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/plugins/withIosSceneLifecycle.cjs around line 45:

`SceneDelegate.scene(_:willConnectTo:options:)` forwards `connectionOptions.urlContexts` and `connectionOptions.userActivities` to the AppDelegate but drops `connectionOptions.notificationResponse`. A cold start from a push notification tap delivers the launch payload through that field, so the app never records the initial notification response. Since the app reads `Notifications.getLastNotificationResponseAsync()` on startup to route notification deep links, taps from a terminated state will stop navigating to the intended thread. Consider forwarding `connectionOptions.notificationResponse` (e.g., via `appDelegate.application(..., didReceiveRemoteNotification:...)` or an equivalent AppDelegate hook) so the response is captured on scene connect.

Comment thread apps/mobile/src/Stack.tsx
Comment on lines +148 to +154
SettingsAuth: createNativeStackScreen({
screen: SettingsAuthRouteScreen,
linking: "auth",
options: {
title: "Sign in",
},
}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium src/Stack.tsx:148

SettingsSheetStack applies GLASS_HEADER_OPTIONS (which sets headerTransparent: true on iOS) to every screen including SettingsAuth. SettingsAuthRouteScreen renders ConfiguredSettingsAuthRouteScreen, a full-screen view with no ScrollView for the glass header to sample — so the auth UI renders underneath the transparent navigation bar on iOS, obscuring the top of the sign-in/account screen. The other settings screens avoid this because they use ScrollView with contentInsetAdjustmentBehavior="automatic". Consider giving SettingsAuth the SHEET_SOLID_HEADER_OPTIONS preset (opaque header) instead of inheriting the glass options.

    SettingsAuth: createNativeStackScreen({
      screen: SettingsAuthRouteScreen,
      linking: "auth",
-      options: {
-        title: "Sign in",
-      },
+      options: {
+        ...SHEET_SOLID_HEADER_OPTIONS,
+        title: "Sign in",
+      },
    }),
Also found in 1 other location(s)

apps/mobile/src/features/home/HomeScreen.tsx:329

The non-iOS no-threads empty state is laid out under the floating custom header. The list path inserts HomeTopContentSpacer with topInset + CUSTOM_HEADER_HEIGHT, but the early if (!hasAnyThreads) return uses only paddingTop: insets.top on Android/other non-iOS platforms. With the HomeHeader toolbar still mounted, the centered EmptyState can be covered by roughly CUSTOM_HEADER_HEIGHT pixels of header chrome.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/Stack.tsx around lines 148-154:

`SettingsSheetStack` applies `GLASS_HEADER_OPTIONS` (which sets `headerTransparent: true` on iOS) to every screen including `SettingsAuth`. `SettingsAuthRouteScreen` renders `ConfiguredSettingsAuthRouteScreen`, a full-screen view with no `ScrollView` for the glass header to sample — so the auth UI renders underneath the transparent navigation bar on iOS, obscuring the top of the sign-in/account screen. The other settings screens avoid this because they use `ScrollView` with `contentInsetAdjustmentBehavior="automatic"`. Consider giving `SettingsAuth` the `SHEET_SOLID_HEADER_OPTIONS` preset (opaque header) instead of inheriting the glass options.

Also found in 1 other location(s):
- apps/mobile/src/features/home/HomeScreen.tsx:329 -- The non-iOS no-threads empty state is laid out under the floating custom header. The list path inserts `HomeTopContentSpacer` with `topInset + CUSTOM_HEADER_HEIGHT`, but the early `if (!hasAnyThreads)` return uses only `paddingTop: insets.top` on Android/other non-iOS platforms. With the `HomeHeader` toolbar still mounted, the centered `EmptyState` can be covered by roughly `CUSTOM_HEADER_HEIGHT` pixels of header chrome.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium

When codeWordBreak is enabled, the FlatList omits getItemLayout, but the scrollToIndex effect still runs for any non-null initialLine. Without getItemLayout, React Native cannot scroll to an index outside the current render window, so opening a file at a distant line with wrapping enabled fails to navigate to the requested line. Consider guarding the effect so it skips scrollToIndex when codeWordBreak is true (or providing an onScrollToIndexFailed handler).

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/files/SourceFileSurface.tsx around line 220:

When `codeWordBreak` is enabled, the `FlatList` omits `getItemLayout`, but the `scrollToIndex` effect still runs for any non-null `initialLine`. Without `getItemLayout`, React Native cannot scroll to an index outside the current render window, so opening a file at a distant line with wrapping enabled fails to navigate to the requested line. Consider guarding the effect so it skips `scrollToIndex` when `codeWordBreak` is true (or providing an `onScrollToIndexFailed` handler).

Comment on lines +57 to +59
if (normalized.headerTintColor !== undefined) {
normalized.headerTintColor = String(normalized.headerTintColor);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium native/StackHeader.tsx:57

normalizeScreenOptions coerces headerTintColor via String(...), but AppNativeStackNavigationOptions.headerTintColor accepts any ColorValue, including PlatformColor and DynamicColorIOS which are opaque native objects. String() converts those to [object Object], so the native dynamic tint color is lost and replaced with a meaningless literal string. Consider passing ColorValue values through unmodified, since NativeStackNavigationOptions.headerTintColor already accepts ColorValue on the native side.

-  if (normalized.headerTintColor !== undefined) {
-    normalized.headerTintColor = String(normalized.headerTintColor);
-  }
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/native/StackHeader.tsx around lines 57-59:

`normalizeScreenOptions` coerces `headerTintColor` via `String(...)`, but `AppNativeStackNavigationOptions.headerTintColor` accepts any `ColorValue`, including `PlatformColor` and `DynamicColorIOS` which are opaque native objects. `String()` converts those to `[object Object]`, so the native dynamic tint color is lost and replaced with a meaningless literal string. Consider passing `ColorValue` values through unmodified, since `NativeStackNavigationOptions.headerTintColor` already accepts `ColorValue` on the native side.

archivedAt: thread.archivedAt,
session: thread.session,
latestUserMessageAt: latestUserMessageAt(thread),
hasPendingApprovals: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium state/use-thread-selection.ts:63

threadDetailToShell hard-codes hasPendingApprovals, hasPendingUserInput, and hasActionableProposedPlan to false, so on the fallback path where the shell snapshot is missing but useEnvironmentThread() provides the full OrchestrationThread, the derived shell silently drops the thread's real pending-approval, pending-input, and plan-ready state. UI driven by useThreadSelection() will present the thread as idle and hide actionable work. If deriving these flags from activities and proposedPlans here is out of scope, consider documenting why the fallback path accepts this data loss.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/state/use-thread-selection.ts around line 63:

`threadDetailToShell` hard-codes `hasPendingApprovals`, `hasPendingUserInput`, and `hasActionableProposedPlan` to `false`, so on the fallback path where the shell snapshot is missing but `useEnvironmentThread()` provides the full `OrchestrationThread`, the derived shell silently drops the thread's real pending-approval, pending-input, and plan-ready state. UI driven by `useThreadSelection()` will present the thread as idle and hide actionable work. If deriving these flags from `activities` and `proposedPlans` here is out of scope, consider documenting why the fallback path accepts this data loss.

<NativeTerminalSurfaceView
appearanceScheme={appearanceScheme}
backgroundColor={theme.background}
focusRequest={props.isRunning ? (props.keyboardFocusRequest ?? 0) : 0}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium terminal/NativeTerminalSurface.tsx:229

focusRequest is set to 0 whenever isRunning is false, so transitioning a running terminal to stopped flips focusRequest from a positive number to 0. On iOS, T3TerminalView.focusRequest fires requestKeyboardFocus() on any value change — including 1 -> 0 — so stopping the terminal triggers a focus request that reopens the software keyboard for a non-running terminal. Consider forwarding props.keyboardFocusRequest ?? 0 directly (or guarding the native side) so the prop only changes on explicit focus requests, not on session state transitions.

Suggested change
focusRequest={props.isRunning ? (props.keyboardFocusRequest ?? 0) : 0}
focusRequest={props.keyboardFocusRequest ?? 0}
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/terminal/NativeTerminalSurface.tsx around line 229:

`focusRequest` is set to `0` whenever `isRunning` is false, so transitioning a running terminal to stopped flips `focusRequest` from a positive number to `0`. On iOS, `T3TerminalView.focusRequest` fires `requestKeyboardFocus()` on any value change — including `1 -> 0` — so stopping the terminal triggers a focus request that reopens the software keyboard for a non-running terminal. Consider forwarding `props.keyboardFocusRequest ?? 0` directly (or guarding the native side) so the prop only changes on explicit focus requests, not on session state transitions.

}

return (
<ArchivedThreadRow

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium archive/ArchivedThreadsScreen.tsx:464

LegendList recycles row components, but ArchivedThreadRow does not pass a resetKey to ThreadSwipeable. When a recycled cell is reused for a different thread, ThreadSwipeable's reset() is never triggered, so the new thread renders in the previous thread's open or mid-swipe state — exposing the wrong swipe actions and allowing accidental delete/unarchive on the wrong item. Consider passing a resetKey prop (e.g., item.key) so the swipeable resets whenever the recycled row changes identity.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx around line 464:

`LegendList` recycles row components, but `ArchivedThreadRow` does not pass a `resetKey` to `ThreadSwipeable`. When a recycled cell is reused for a different thread, `ThreadSwipeable`'s `reset()` is never triggered, so the new thread renders in the previous thread's open or mid-swipe state — exposing the wrong swipe actions and allowing accidental delete/unarchive on the wrong item. Consider passing a `resetKey` prop (e.g., `item.key`) so the swipeable resets whenever the recycled row changes identity.


type CommandHandler = () => boolean | void;

const handlers = new Map<HardwareKeyboardCommand, Set<CommandHandler>>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium keyboard/hardwareKeyboardCommands.ts:15

handlers stores CommandHandler callbacks in a Set, so when two useHardwareKeyboardCommand calls register the same function object for the same command, the second add is a no-op. When either registration unmounts, its cleanup deletes that shared callback, removing the other still-mounted registration as well. This breaks dispatch and ordering for any shared/stable callback reference passed to multiple registrations. Consider keying registrations by a unique token (e.g., a per-registration object) instead of by the handler function itself.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/keyboard/hardwareKeyboardCommands.ts around line 15:

`handlers` stores `CommandHandler` callbacks in a `Set`, so when two `useHardwareKeyboardCommand` calls register the same function object for the same command, the second `add` is a no-op. When either registration unmounts, its cleanup deletes that shared callback, removing the other still-mounted registration as well. This breaks dispatch and ordering for any shared/stable callback reference passed to multiple registrations. Consider keying registrations by a unique token (e.g., a per-registration object) instead of by the handler function itself.

useImperativeHandle(
nativeViewRef,
() => ({
scrollToFile: async (fileId, animated = true) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium diffs/nativeReviewDiffSurface.ts:250

scrollToFile and scrollToTop forward directly to nativeRef.current with no retry, so when Expo has attached the ref but not yet registered the native tag, the awaited call rejects immediately and the requested navigation is lost instead of waiting for registration to complete. Consider applying the same retry loop used by useNativeReviewDiffPayload so the call is retried until the native view is ready.

Also found in 1 other location(s)

apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift:852

applyPendingScrollIfNeeded() clears pendingScrollFileId whenever the requested fileId is absent from the current rows (if !rows.isEmpty { pendingScrollFileId = nil }). After this change, setRowsJson decodes on payloadDecodeQueue, so the view can keep showing the previous section's rows for a while. If scrollToFile(...) is called during that window—e.g. the user taps a file in the new split-view inspector before the new diff finishes decoding—the lookup runs against stale rows, the pending request is discarded, and the view never scrolls to the requested file once the new rows arrive.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/diffs/nativeReviewDiffSurface.ts around line 250:

`scrollToFile` and `scrollToTop` forward directly to `nativeRef.current` with no retry, so when Expo has attached the ref but not yet registered the native tag, the awaited call rejects immediately and the requested navigation is lost instead of waiting for registration to complete. Consider applying the same retry loop used by `useNativeReviewDiffPayload` so the call is retried until the native view is ready.

Also found in 1 other location(s):
- apps/mobile/modules/t3-review-diff/ios/T3ReviewDiffView.swift:852 -- `applyPendingScrollIfNeeded()` clears `pendingScrollFileId` whenever the requested `fileId` is absent from the *current* `rows` (`if !rows.isEmpty { pendingScrollFileId = nil }`). After this change, `setRowsJson` decodes on `payloadDecodeQueue`, so the view can keep showing the previous section's rows for a while. If `scrollToFile(...)` is called during that window—e.g. the user taps a file in the new split-view inspector before the new diff finishes decoding—the lookup runs against stale rows, the pending request is discarded, and the view never scrolls to the requested file once the new rows arrive.


// In split layouts the persistent sidebar IS the thread list — Home becomes
// an empty detail pane so selecting a thread never transitions layouts.
if (layout.usesSplitView) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium home/HomeRouteScreen.tsx:58

When layout.usesSplitView is true, HomeRouteScreen returns WorkspaceEmptyDetail unconditionally, bypassing HomeScreen entirely. On a split-view device with no threads or no ready environment, the user sees the generic Select a thread empty state with a New Task button instead of HomeScreen's contextual empty state (e.g., Add environment when no ready environment exists). This removes the first-run recovery path on tablets and foldables. Consider rendering HomeScreen (or at least its empty-state branch) in the split-view case when there are no threads, so users get the correct onboarding CTA.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/mobile/src/features/home/HomeRouteScreen.tsx around line 58:

When `layout.usesSplitView` is true, `HomeRouteScreen` returns `WorkspaceEmptyDetail` unconditionally, bypassing `HomeScreen` entirely. On a split-view device with no threads or no ready environment, the user sees the generic `Select a thread` empty state with a `New Task` button instead of `HomeScreen`'s contextual empty state (e.g., `Add environment` when no ready environment exists). This removes the first-run recovery path on tablets and foldables. Consider rendering `HomeScreen` (or at least its empty-state branch) in the split-view case when there are no threads, so users get the correct onboarding CTA.

- Adjust react-native-screens patch for toolbar and header sizing
- Update lockfile to use the new patch hash
- Improve sidebar and home list spacing on larger mobile layouts
- Clean up related formatting and workspace catalog quotes
@juliusmarminge
juliusmarminge merged commit 32d17d3 into main Jul 3, 2026
16 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/ipad-responsive-mobile-layout branch July 3, 2026 05:42
wizzoapp Bot pushed a commit to agriffiths-bots/t3code that referenced this pull request Jul 3, 2026
)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
adamfgr pushed a commit to agriffiths-bots/t3code that referenced this pull request Jul 3, 2026
* fix(dev): Fix electron dev launch and add test (pingdotgg#3662)

* Add adaptive split-view layout for iPad/mobile workspace (pingdotgg#3514)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(mobile): compile patched native pods from source on EAS (pingdotgg#3667)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Make the thread composer read as elevated liquid glass (pingdotgg#3668)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(sync): repair upstream sync CI fallout

---------

Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: wizzoapp[bot] <254688279+wizzoapp[bot]@users.noreply.github.com>
1337hero pushed a commit to 1337hero/t3code that referenced this pull request Jul 4, 2026
)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
eyeveil added a commit to eyeveil/t3code that referenced this pull request Jul 9, 2026
Independently ports six low-risk improvements from Julius's Android PR,
adapted to the fork's native expo-symbols icon model (no AppSymbol/Tabler):
gate push/live-activity registration + settings toggles on personal-team
builds that can't receive APNs; render the work-log and project-folder icons
on Android (were bare SF strings, invisible); a copy button on chat code
blocks; a selection haptic on ControlPill press; and flat Android settings
sections with a card opt-back for the appearance pages.
tarik02 referenced this pull request in tarik02-org/t3code Jul 13, 2026
* Add middle-click close for right panel tabs (#3161)

Co-authored-by: Julius Marminge <jmarminge@gmail.com>

* fix: warm WSL before preflight in WSL-only backend mode (#3588)

* Add Claude Sonnet 5 as the default Claude model (#3620)

* Restore the ultrathink frame border effect (#3625)

* fix(dev): Fix electron dev launch and add test (#3662)

* Add adaptive split-view layout for iPad/mobile workspace (#3514)

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(mobile): compile patched native pods from source on EAS (#3667)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Make the thread composer read as elevated liquid glass (#3668)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Upgrade Vite Plus and enable bundled dev opt-in (#3679)

* Surface pending tasks in mobile home and draft flow (#3670)

* fix(mobile): combined test branch — scroll, back-swipe, thread lists, computer switching (#3687)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Add repo-root favicon.svg so t3 code shows its own icon (#3683)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Load thread snapshots over HTTP before live sync (#3719)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Fix mobile legend anchor under automatic iOS insets (#3684)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Improve live activity routing and diagnostics (#3685)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Prevent Add Project sheet from collapsing on relayout (#3759)

* Use variant-specific splash icons in mobile app (#3762)

* Fix Expo widget asset wiring order (#3763)

* Extend Done display to 15 minutes and show up to 5 Live Activity banner rows (#3761)

* Clear VCS presentation state on finish (#3764)

* Lead with the outcome when no agents are active in the Live Activity (#3768)

* Add T3 Connect onboarding for mobile and web (#3765)

* Revert "Add T3 Connect onboarding for mobile and web" (#3776)

* Expose Clerk Google sign-in env vars to Expo (#3772)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Set up Cursor Cloud dev environment (web + Android toolchain) (#3755)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Theo Browne <t3dotgg@users.noreply.github.com>

* Revert "Revert "Add T3 Connect onboarding for mobile and web"" (#3777)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* Use rounded depth logo for production splash screen (#3780)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* fix(release): stage pnpm 11 allowBuilds for desktop installs (#3781)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* Upgrade Clerk toolchain to latest versions (#3785)

* fix(release): bump electron-builder so pnpm 11 deduped deps land in the asar (#3790)

* Fix desktop native optional dependency packaging (#3816)

* [codex] Upgrade Clerk stack (#3821)

Co-authored-by: codex <codex@users.noreply.github.com>

* [codex] Preserve worktree metadata during branch sync (#3822)

Co-authored-by: codex <codex@users.noreply.github.com>

* feat(client): persist offline environment data and mobile preferences (#3795)

Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: codex <codex@users.noreply.github.com>

* [codex] Label max and ultra reasoning (#3824)

Co-authored-by: codex <codex@users.noreply.github.com>

* fix(mobile): embed fonts and render project favicons reliably (#3823)

Co-authored-by: codex <codex@users.noreply.github.com>

* Show compact PR number badges in mobile thread rows (#3827)

Co-authored-by: codex <codex@users.noreply.github.com>

* Expose mobile PR indicator labels to accessibility (#3828)

Co-authored-by: codex <codex@users.noreply.github.com>

* Fix truncated chat error alert layout (#3899)

* fix(marketing): show platform-appropriate commit shortcut on the website (#3644)

* [codex] Add Android mobile support (#3579)

Co-authored-by: Horus Lugo <horusgoul@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Ben Davis <45952064+bmdavis419@users.noreply.github.com>
Co-authored-by: Alex <me@pixp.cc>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>

---------

Co-authored-by: Hugo Blom <6117705+huxcrux@users.noreply.github.com>
Co-authored-by: Julius Marminge <jmarminge@gmail.com>
Co-authored-by: Utkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: Julius Marminge <julius@mac.lan>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Theo Browne <t3dotgg@users.noreply.github.com>
Co-authored-by: Rowan <rowan@cardow.co>
Co-authored-by: Patricio Gómez Meneses <107218376+Prgm-code@users.noreply.github.com>
Co-authored-by: Jake Leventhal <jakeleventhal@me.com>
Co-authored-by: Vedank Purohit <VedankPurohit2@gmail.com>
Co-authored-by: Horus Lugo <horusgoul@gmail.com>
Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Ben Davis <45952064+bmdavis419@users.noreply.github.com>
Co-authored-by: Alex <me@pixp.cc>
@juliusmarminge juliusmarminge mentioned this pull request Jul 20, 2026
3 tasks
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Jul 27, 2026
## What's Changed
* Add middle-click close for right panel tabs by @huxcrux in https://github.com/pingdotgg/t3code/pull/3161
* fix: warm WSL before preflight in WSL-only backend mode by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3588
* Add Claude Sonnet 5 as the default Claude model by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3620
* Restore the ultrathink frame border effect by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3625
* fix(dev): Fix electron dev launch and add test by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3662
* Add adaptive split-view layout for iPad/mobile workspace by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3514
* fix(mobile): compile patched native pods from source on EAS by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3667
* Make the thread composer read as elevated liquid glass by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3668
* Upgrade Vite Plus and enable bundled dev opt-in by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3679
* Surface pending tasks in mobile home and draft flow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3670
* fix(mobile): combined test branch — scroll, back-swipe, thread lists, computer switching by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3687
* Add repo-root favicon.svg so t3 code shows its own icon by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3683
* Load thread snapshots over HTTP before live sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3719
* Fix mobile legend anchor under automatic iOS insets by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3684
* Improve live activity routing and diagnostics by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3685
* Prevent Add Project sheet from collapsing on relayout by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3759
* Use variant-specific splash icons in mobile app by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3762
* Fix Expo widget asset wiring order by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3763
* Extend Done display to 15 minutes and show up to 5 Live Activity banner rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3761
* Clear VCS presentation state on finish by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3764
* Lead with the outcome when no agents are active in the Live Activity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3768
* Add T3 Connect onboarding for mobile and web by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3765
* Revert "Add T3 Connect onboarding for mobile and web" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3776
* Expose Clerk Google sign-in env vars to Expo by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3772
* Set up Cursor Cloud dev environment (web + Android toolchain) by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3755
* Revert "Revert "Add T3 Connect onboarding for mobile and web"" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3777
* Use rounded depth logo for production splash screen by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3780
* fix(release): stage pnpm 11 allowBuilds for desktop installs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3781
* Upgrade Clerk toolchain to latest versions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3785
* fix(release): bump electron-builder so pnpm 11 deduped deps land in the asar by @avocardow in https://github.com/pingdotgg/t3code/pull/3790
* Fix desktop native optional dependency packaging by @Prgm-code in https://github.com/pingdotgg/t3code/pull/3816
* [codex] Upgrade Clerk stack by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3821
* [codex] Preserve worktree metadata during branch sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3822
* feat(client): persist offline environment data and mobile preferences by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3795
* [codex] Label max and ultra reasoning by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3824
* fix(mobile): embed fonts and render project favicons reliably by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3823
* Show compact PR number badges in mobile thread rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3827
* Expose mobile PR indicator labels to accessibility by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3828
* Fix truncated chat error alert layout by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3899
* fix(marketing): show platform-appropriate commit shortcut on the website by @VedankPurohit in https://github.com/pingdotgg/t3code/pull/3644
* [codex] Add Android mobile support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3579
* Use client-side fallbacks for missing project favicons by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3959
* Skip stale working-task notifications by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3961
* Prepare Android beta branding and review diff UI by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3967
* perf(web): duty-cycle status animations and remove fixed noise overlay by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3978
* fix(docs): correct CI task-runner commands in ci.md by @kridaydave in https://github.com/pingdotgg/t3code/pull/3990
* fix(docs): repair broken source links in architecture overview by @kridaydave in https://github.com/pingdotgg/t3code/pull/3991
* fix(docs): replace stale codething-mvp absolute paths with repo-relative links by @kridaydave in https://github.com/pingdotgg/t3code/pull/3992
* docs: Add T3 Code Legal Docs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3972
* Fix Legal modal header crash by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4000
* [codex] Fix onboarding connection status by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4001
* Isolate native diff highlight grammar state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4029
* Fix macOS fullscreen titlebar spacing by @D3OXY in https://github.com/pingdotgg/t3code/pull/4019
* Prevent duplicate project workspace roots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3829
* Normalize over-indented markdown list items by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4020
* Resolve localhost preview URLs for remote environments by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4011
* fix(mobile): Send composer images in upload wire format by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4035
* Fix iOS terminal Enter input encoding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4043
* Add native mobile share target support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4021
* [codex] Expand real-route app store screenshot harness by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4014
* fix(server): use CLAUDE_CONFIG_DIR instead of HOME for Claude instanc… by @dmstoykov in https://github.com/pingdotgg/t3code/pull/4017
* Fix dropped events during initial thread snapshot by @D3OXY in https://github.com/pingdotgg/t3code/pull/4079
* feat: show nightly update changelog tooltip by @HugoVizcainoSantana in https://github.com/pingdotgg/t3code/pull/3832
* fix(git): treat selected commit paths literally by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/3998
* fix(server): stabilize non-repository Git diagnostics by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/4077
* Refresh app icons across release variants by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4080
* Update marketing GitHub star count by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4088
* fix(marketing): correct Cursor icon color by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4090
* Normalize protocol-relative remote host input as https by @kridaydave in https://github.com/pingdotgg/t3code/pull/3971
* fix(cursor): default binary path to cursor-agent (avoid path conflict w/ grok) by @BunnyGamezsc in https://github.com/pingdotgg/t3code/pull/4094
* Fix documented task-runner commands (bun run -> vp) by @kridaydave in https://github.com/pingdotgg/t3code/pull/3965
* Allow preview panel to grow on wide displays by @olivoil in https://github.com/pingdotgg/t3code/pull/4044
* fix: prevent initial right-click from selecting a context menu item by @Fazalkadivar21 in https://github.com/pingdotgg/t3code/pull/3877
* Fix duplicate keybinding rule when replacing with an existing rule by @kridaydave in https://github.com/pingdotgg/t3code/pull/3969
* fix(server): image upload crashed dispatchCommand with a stack overflow by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3952
* Remove unused code parameter from describePreviewError by @kridaydave in https://github.com/pingdotgg/t3code/pull/3970
* [codex] prevent ACP assistant ID collisions after restarts by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3932
* fix(web): inset Windows desktop scrollbars from resize edge by @nateEc in https://github.com/pingdotgg/t3code/pull/4097
* [codex] fix mobile composer Enter behavior by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3930
* feat(server): include runtime model and effort in Codex developer instructions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3948
* fix(ux): spamming cmd + , no longer stack opening settings by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2757
* fix(terminal): strip AppImage runtime env from spawned terminals by @leorivastech in https://github.com/pingdotgg/t3code/pull/3108
* fix(server): thread cwd through Claude capability probe (#2048) by @mvanhorn in https://github.com/pingdotgg/t3code/pull/2124
* [codex] fix: guard invalid web timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3515
* [codex] fix: tolerate invalid latest user message timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3521
* [codex] Fix provider update checks restore defaults by @StiensWout in https://github.com/pingdotgg/t3code/pull/3531
* fix(server): skip undecodable provider runtime rows when listing sessions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3951
* Share MCP OAuth locks across Codex shadow homes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4104
* Preserve T3 Code identity in macOS development launcher by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4102
* fix(web): increase contrast of question option descriptions by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3867
* feat: draft hero landing on the index route by @yordis in https://github.com/pingdotgg/t3code/pull/4055
* feat: file explorer mention actions and zoom-aware context menus by @yordis in https://github.com/pingdotgg/t3code/pull/4054
* fix(mobile): restore iOS home screen branding by @PixPMusic in https://github.com/pingdotgg/t3code/pull/4025
* perf(client): defer active thread cache writes by @Chrrxs in https://github.com/pingdotgg/t3code/pull/4006
* Default diffs to working changes by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3974
* Add Grok to marketing site provider list by @Aditya190803 in https://github.com/pingdotgg/t3code/pull/3484
* Fix reopening existing Diff tab by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3973
* Fix sending messages during active turns by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3919
* [codex] Route OpenCode missing-session errors through Effect by @StiensWout in https://github.com/pingdotgg/t3code/pull/3608
* [fix/feat:ui] Show default option badge by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3232
* [fix/feat:ui] Preserve open-in editor brand colors by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3225
* fix(web): handle macOS Home and End in composer by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2508
* Allow failed remote environments to be removed by @zepi2509 in https://github.com/pingdotgg/t3code/pull/4084
* [codex] canonicalize client timestamps by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4112
* [fix/feat:ui] Make selected menu checks blue by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3234
* fix(desktop): Validate WSL node version against engine range after probe success by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3621
* Refresh splash screen and favicon branding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4120
* Add terminal selection copy action by @tarik02 in https://github.com/pingdotgg/t3code/pull/2904
* Add isolated app testing workflow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4121
* feat(web): themed sidebar header art for nightly and dev builds by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4130
* feat: add headless `t3 connect` setup for SSH hosts by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3749
* Refine T3 Connect authorization surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4159
* fix: increase OpenCode server startup timeout from 5s to 30s by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4132
* fix(shared): delete unused agentAwareness phase predicates by @kridaydave in https://github.com/pingdotgg/t3code/pull/4134
* fix(mobile): Stabilize native stack option updates by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4037
* Make test-t3-app skill discoverable by Claude Code by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4162
* fix(web): improve dev sidebar backdrop contrast & remove version pills by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4166
* Fix draft banner stack overlap by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4164
* Add portable mobile app testing guidance by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4165
* fix(client): use lightweight connection probe by @eeinarsson in https://github.com/pingdotgg/t3code/pull/4137
* fix(server): resolve Claude SDK executable path on Windows npm installs by @nsxdavid in https://github.com/pingdotgg/t3code/pull/3740
* Fix project action preview settings persistence by @keeperxy in https://github.com/pingdotgg/t3code/pull/3842
* fix(desktop): allow clipboard writes in the preview browser by @carlosricojr in https://github.com/pingdotgg/t3code/pull/3889
* fix(web): handle sidebar shortcut before editors by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3921
* fix(server): recognize Bedrock-backed Claude as authenticated by @PieterVanZyl-Dev in https://github.com/pingdotgg/t3code/pull/3931
* Fix incorrect pluralization of “entry” by @McMelonTV in https://github.com/pingdotgg/t3code/pull/3933
* feat(server): title background-task work-log rows with the task name by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3751
* fix: delegate OpenCode session titles to provider by @tris203 in https://github.com/pingdotgg/t3code/pull/3720
* Archive selected threads from the context menu by @theduke in https://github.com/pingdotgg/t3code/pull/3895
* fix(cli): support force removing projects by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3922
* fix: allow sidebar to be shrunk when wider than viewport by @shoaib050326 in https://github.com/pingdotgg/t3code/pull/2456
* fix(codex): show web search query and url in tool call details by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2093
* Add Codex launch arguments setting by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2892
* [orchestration] Clear stale active turn when session becomes inactive by @Andrew-Forster in https://github.com/pingdotgg/t3code/pull/3159
* Regenerate Codex reset credit protocol bindings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4173
* fix(preview): preserve direct localhost navigation by @Chrrxs in https://github.com/pingdotgg/t3code/pull/3939
* Synchronize mobile threads with authoritative shell snapshots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4163
* Gate iOS glass layout on native support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4032
* fix(opencode): resume the OpenCode session on follow-ups instead of starting an empty one by @vdmkotai in https://github.com/pingdotgg/t3code/pull/3617
* fix(server): use CLI for OpenCode health check instead of spawning server by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4153
* fix(web): scope timeline minimap hover target to the side gutter by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3869
* [codex] show complete approval details by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4111
* fix(web): paint text selection over composer chips by @yordis in https://github.com/pingdotgg/t3code/pull/4139
* [codex] preserve custom model slugs by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4168
* fix(web): preview workspace images in the file panel by @Rhiz3K in https://github.com/pingdotgg/t3code/pull/3996
* feat(web): drag files from the explorer into the chat composer by @yordis in https://github.com/pingdotgg/t3code/pull/4140
* fix(desktop): preserve main window bounds by @anirudhsama in https://github.com/pingdotgg/t3code/pull/3851
* perf(orchestration): speed up new-chat propagation and offline catch-up by @RusiruSadathana in https://github.com/pingdotgg/t3code/pull/4177
* Finale: upgrade changed files card to fix various UI issues by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/4113
* Pass CLI OAuth config to hosted web deploy by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4186
* fix(web): always show environment chip for remote projects by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4217
* fix(web): keep composer editable while disconnected by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4241
* fix: better defaults — Claude 1M context, Codex gpt-5.6, worktrees from origin main by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4240
* fix(claude): handle all SDK stream messages; stop spurious work-log warning rows by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4244
* Sidebar v2 beta: flat thread list with a server-backed settled lifecycle by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4026
* fix(settings): validate the add-provider wizard step before advancing (#2813) by @leorivastech in https://github.com/pingdotgg/t3code/pull/3100
* fix(claude): isolate capability probe from user MCP servers by @jbbottoms in https://github.com/pingdotgg/t3code/pull/4015
* Preserve connecting status while a turn starts by @D3OXY in https://github.com/pingdotgg/t3code/pull/4101
* fix(server): stop restoring stale OpenCode models by @nateEc in https://github.com/pingdotgg/t3code/pull/4095
* [codex] keep scoped package references as text by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4167
* fix(web): default provider selection for users without Codex by @mfazekas in https://github.com/pingdotgg/t3code/pull/4117
* Unify temporary worktree branch naming by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4278
* fix(web): use message-square icon for settled icon-less project threads in sidebar v2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4279
* Stabilize sidebar settling animations by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4280
* Restore Copy Link in chat link context menu by @caezium in https://github.com/pingdotgg/t3code/pull/4161
* fix(desktop): handle EPIPE errors on stdout/stderr to prevent crash dialog by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4213
* Preserve draft thread highlighting during promotion by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4283
* Move mobile working timer into the thread timeline by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4285
* Stabilize PR status lookups and provider session lifecycle by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4281
* fix: open command palette instead of custom dialog for new thread picker in SidebarV2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4269
* fix(server): don't drop sticky PR fallback when remote URL can't be resolved by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4289
* feat(web): copy branch name via right-click in the branch selector by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4275
* Add remote server updates and standalone service management by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4286
* Refine light-mode sidebar surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4268
* fix(mobile): don't mark Android VPN/Tailscale as offline when connected by @Wraient in https://github.com/pingdotgg/t3code/pull/3949
* improve and prevent silent thread branch drift and PR fetching by @justsomelegs in https://github.com/pingdotgg/t3code/pull/2284
* Refresh web application surfaces and dark-mode dialogs by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4319
* fix(web): new-thread defaults ignored for remote environments by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4276
* feat: add "Auto" runtime mode — AI-reviewed approvals for Codex and Claude by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4272
* Add shared t3.json project configuration support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4317
* Unify dialog glass and fix composer overlays by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4365
* fix(web): warn before silent Windows updates by @nateEc in https://github.com/pingdotgg/t3code/pull/4350
* [codex] Move project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4313
* [codex] Group project scopes in mobile thread lists by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4314
* [codex] Move mobile project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4315
* [codex] Deduplicate connection failure messaging by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4367
* Restore grouped project filtering in Sidebar V2 by @shivamhwp in https://github.com/pingdotgg/t3code/pull/4282
* [codex] restore Sidebar V2 project actions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4373
* [codex] Group projects in new-thread pickers by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4312
* fix(web): restore dark composer toolbar styling by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4375
* Fix thread tooltip folder icon color by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4383
* fix(server): parse CLI version in update preflight by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4389
* fix(web): sidebar v2 polish — jump hints, working duration, in-flight fade, settled sort by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4274
* Fix logical project grouping labels on mobile by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4391
* Add preview color scheme controls and simplify project grouping by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4385
* fix(cli): publish nightly branded favicons by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4372
* Fix thread loading flash by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4396
* fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4309
* Fix composer context strip alignment and glass shell by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4404
* Polish iOS git progress overlay with glass effects by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4387
* Improve composer glass fallbacks by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4406
* feat(web): collapse large git diffs by default to make chat more readable by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4409
* Stop new threads inheriting checkout/branch from viewed thread by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4411
* fix: tone down branch-mismatch banner by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4416
* fix: Claude Code skills discoverable for the composer $ picker by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4414
* fix(web): keep settled threads reachable when opened directly by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4413
* feat(sidebar-v2): thread snoozing by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4311
* Upgrade Clerk packages and Expo integration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4440
* Increase light-mode contrast for user message bubbles by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4441
* Restore model picker layout and retain iterative test state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4450
* Color settled PR labels on hover by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4451
* [codex] Fix glass hover compositing artifacts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4446
* Add Claude Opus 5 model by @thomaslittle in https://github.com/pingdotgg/t3code/pull/4472
* feat(web): add collapse-all toggle to diff panel by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4475
* feat(web): show fast mode as a bolt instead of a "Normal" label by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4488
* feat(dev): keep worktree dev state isolated on T3 Code dev servers by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4555
* feat(dev): Make t3 code dev instances shareable over Tailscale by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4556
* fix(dev): skip browser-blocked ports by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4608
* fix: cut websocket throughput in half by pruning activity payloads by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4622
* perf(mobile): defer work-log detail serialization by @saphid in https://github.com/pingdotgg/t3code/pull/4607
* test: account for lazy thread feed details by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4628
* feat(relay): limit managed tunnels per user by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4530
* Add managed tunnel limits migration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4635
* Add background preview capture and picture-in-picture support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4397
* feat(web): prompt stash — cmd+S saves the composer to a per-provider queue by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4453
* [codex] Upgrade Effect and Alchemy betas by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4643
* feat: allow new thread creation through project breadcrumbs by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4638
* fix(web): scope PR state to the thread branch by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4460
* Drop redundant Relay user indexes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4648
* feat(connect): release the Cloudflare tunnel when the environment shuts down by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4531
* Fix Relay Worker RuntimeContext wiring by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4653
* Fix live sidebar resize limits and defer Alchemy runtime context by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4655
* fix(web): constrain branch toolbar context by @maxktz in https://github.com/pingdotgg/t3code/pull/4657
* Keep MCP credentials alive across provider turns by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4659
* fix: close actions dropdown when editing by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4660
* fix(preview): stabilize PiP viewport identity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4661
* Add glass styling for thread tooltips and simplify preview tab handling by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4665
* Use tarball archiving for hosted web deploys by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4669
* fix(server): bound editor discovery during config loading by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4291
* Prevent draft thread detail polling before shell registration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4670
* feat: add configurable source control writing settings by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4204
* feat(diff-panel): show total line additions and deletions by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4674
* Clear provider update actions while updating by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4676
* Fix sidebar highlighting for draft threads by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4679
* Use glass surfaces for web toasts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4681
* Show origin ref in branch trigger label by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4680
* fix(mobile): match react version to react-native 0.85.3 vendored renderer (19.2.3) by @KrzysztofMoch in https://github.com/pingdotgg/t3code/pull/4675

## New Contributors
* @avocardow made their first contribution in https://github.com/pingdotgg/t3code/pull/3790
* @Prgm-code made their first contribution in https://github.com/pingdotgg/t3code/pull/3816
* @jakeleventhal made their first contribution in https://github.com/pingdotgg/t3code/pull/3899
* @VedankPurohit made their first contribution in https://github.com/pingdotgg/t3code/pull/3644
* @kridaydave made their first contribution in https://github.com/pingdotgg/t3code/pull/3990
* @dmstoykov made their first contribution in https://github.com/pingdotgg/t3code/pull/4017
* @HugoVizcainoSantana made their first contribution in https://github.com/pingdotgg/t3code/pull/3832
* @EricTsai83 made their first contribution in https://github.com/pingdotgg/t3code/pull/3998
* @AmoonPod made their first contribution in https://github.com/pingdotgg/t3code/pull/4088
* @BunnyGamezsc made their first contribution in https://github.com/pingdotgg/t3code/pull/4094
* @olivoil made their first contribution in https://github.com/pingdotgg/t3code/pull/4044
* @Fazalkadivar21 made their first contribution in https://github.com/pingdotgg/t3code/pull/3877
* @maxwellyoung made their first contribution in https://github.com/pingdotgg/t3code/pull/3932
* @nateEc made their first contribution in https://github.com/pingdotgg/t3code/pull/4097
* @leorivastech made their first contribution in https://github.com/pingdotgg/t3code/pull/3108
* @xxashxx-svg made their first contribution in https://github.com/pingdotgg/t3code/pull/3867
* @yordis made their first contribution in https://github.com/pingdotgg/t3code/pull/4055
* @Chrrxs made their first contribution in https://github.com/pingdotgg/t3code/pull/4006
* @Aditya190803 made their first contribution in https://github.com/pingdotgg/t3code/pull/3484
* @zepi2509 made their first contribution in https://github.com/pingdotgg/t3code/pull/4084
* @eeinarsson made their first contribution in https://github.com/pingdotgg/t3code/pull/4137
* @keeperxy made their first contribution in https://github.com/pingdotgg/t3code/pull/3842
* @carlosricojr made their first contribution in https://github.com/pingdotgg/t3code/pull/3889
* @Bortlesboat made their first contribution in https://github.com/pingdotgg/t3code/pull/3921
* @PieterVanZyl-Dev made their first contribution in https://github.com/pingdotgg/t3code/pull/3931
* @McMelonTV made their first contribution in https://github.com/pingdotgg/t3code/pull/3933
* @tris203 made their first contribution in https://github.com/pingdotgg/t3code/pull/3720
* @theduke made their first contribution in https://github.com/pingdotgg/t3code/pull/3895
* @shoaib050326 made their first contribution in https://github.com/pingdotgg/t3code/pull/2456
* @vdmkotai made their first contribution in https://github.com/pingdotgg/t3code/pull/3617
* @Rhiz3K made their first contribution in https://github.com/pingdotgg/t3code/pull/3996
* @anirudhsama made their first contribution in https://github.com/pingdotgg/t3code/pull/3851
* @RusiruSadathana made their first contribution in https://github.com/pingdotgg/t3code/pull/4177
* @jbbottoms made their first contribution in https://github.com/pingdotgg/t3code/pull/4015
* @mfazekas made their first contribution in https://github.com/pingdotgg/t3code/pull/4117
* @caezium made their first contribution in https://github.com/pingdotgg/t3code/pull/4161
* @Wraient made their first contribution in https://github.com/pingdotgg/t3code/pull/3949
* @thomaslittle made their first contribution in https://github.com/pingdotgg/t3code/pull/4472
* @0x4bs3nt made their first contribution in https://github.com/pingdotgg/t3code/pull/4475
* @saphid made their first contribution in https://github.com/pingdotgg/t3code/pull/4607
* @maxktz made their first contribution in https://github.com/pingdotgg/t3code/pull/4657
* @KrzysztofMoch made their first contribution in https://github.com/pingdotgg/t3code/pull/4675

**Full Changelog**: https://github.com/pingdotgg/t3code/compare/v0.0.28...v0.0.29

## What's Changed
* Add middle-click close for right panel tabs by @huxcrux in https://github.com/pingdotgg/t3code/pull/3161
* fix: warm WSL before preflight in WSL-only backend mode by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3588
* Add Claude Sonnet 5 as the default Claude model by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3620
* Restore the ultrathink frame border effect by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3625
* fix(dev): Fix electron dev launch and add test by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3662
* Add adaptive split-view layout for iPad/mobile workspace by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3514
* fix(mobile): compile patched native pods from source on EAS by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3667
* Make the thread composer read as elevated liquid glass by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3668
* Upgrade Vite Plus and enable bundled dev opt-in by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3679
* Surface pending tasks in mobile home and draft flow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3670
* fix(mobile): combined test branch — scroll, back-swipe, thread lists, computer switching by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3687
* Add repo-root favicon.svg so t3 code shows its own icon by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3683
* Load thread snapshots over HTTP before live sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3719
* Fix mobile legend anchor under automatic iOS insets by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3684
* Improve live activity routing and diagnostics by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3685
* Prevent Add Project sheet from collapsing on relayout by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3759
* Use variant-specific splash icons in mobile app by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3762
* Fix Expo widget asset wiring order by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3763
* Extend Done display to 15 minutes and show up to 5 Live Activity banner rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3761
* Clear VCS presentation state on finish by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3764
* Lead with the outcome when no agents are active in the Live Activity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3768
* Add T3 Connect onboarding for mobile and web by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3765
* Revert "Add T3 Connect onboarding for mobile and web" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3776
* Expose Clerk Google sign-in env vars to Expo by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3772
* Set up Cursor Cloud dev environment (web + Android toolchain) by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3755
* Revert "Revert "Add T3 Connect onboarding for mobile and web"" by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3777
* Use rounded depth logo for production splash screen by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3780
* fix(release): stage pnpm 11 allowBuilds for desktop installs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3781
* Upgrade Clerk toolchain to latest versions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3785
* fix(release): bump electron-builder so pnpm 11 deduped deps land in the asar by @avocardow in https://github.com/pingdotgg/t3code/pull/3790
* Fix desktop native optional dependency packaging by @Prgm-code in https://github.com/pingdotgg/t3code/pull/3816
* [codex] Upgrade Clerk stack by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3821
* [codex] Preserve worktree metadata during branch sync by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3822
* feat(client): persist offline environment data and mobile preferences by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3795
* [codex] Label max and ultra reasoning by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3824
* fix(mobile): embed fonts and render project favicons reliably by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3823
* Show compact PR number badges in mobile thread rows by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3827
* Expose mobile PR indicator labels to accessibility by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3828
* Fix truncated chat error alert layout by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3899
* fix(marketing): show platform-appropriate commit shortcut on the website by @VedankPurohit in https://github.com/pingdotgg/t3code/pull/3644
* [codex] Add Android mobile support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3579
* Use client-side fallbacks for missing project favicons by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3959
* Skip stale working-task notifications by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3961
* Prepare Android beta branding and review diff UI by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3967
* perf(web): duty-cycle status animations and remove fixed noise overlay by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3978
* fix(docs): correct CI task-runner commands in ci.md by @kridaydave in https://github.com/pingdotgg/t3code/pull/3990
* fix(docs): repair broken source links in architecture overview by @kridaydave in https://github.com/pingdotgg/t3code/pull/3991
* fix(docs): replace stale codething-mvp absolute paths with repo-relative links by @kridaydave in https://github.com/pingdotgg/t3code/pull/3992
* docs: Add T3 Code Legal Docs by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3972
* Fix Legal modal header crash by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4000
* [codex] Fix onboarding connection status by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4001
* Isolate native diff highlight grammar state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4029
* Fix macOS fullscreen titlebar spacing by @D3OXY in https://github.com/pingdotgg/t3code/pull/4019
* Prevent duplicate project workspace roots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/3829
* Normalize over-indented markdown list items by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4020
* Resolve localhost preview URLs for remote environments by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4011
* fix(mobile): Send composer images in upload wire format by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4035
* Fix iOS terminal Enter input encoding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4043
* Add native mobile share target support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4021
* [codex] Expand real-route app store screenshot harness by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4014
* fix(server): use CLAUDE_CONFIG_DIR instead of HOME for Claude instanc… by @dmstoykov in https://github.com/pingdotgg/t3code/pull/4017
* Fix dropped events during initial thread snapshot by @D3OXY in https://github.com/pingdotgg/t3code/pull/4079
* feat: show nightly update changelog tooltip by @HugoVizcainoSantana in https://github.com/pingdotgg/t3code/pull/3832
* fix(git): treat selected commit paths literally by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/3998
* fix(server): stabilize non-repository Git diagnostics by @EricTsai83 in https://github.com/pingdotgg/t3code/pull/4077
* Refresh app icons across release variants by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4080
* Update marketing GitHub star count by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4088
* fix(marketing): correct Cursor icon color by @AmoonPod in https://github.com/pingdotgg/t3code/pull/4090
* Normalize protocol-relative remote host input as https by @kridaydave in https://github.com/pingdotgg/t3code/pull/3971
* fix(cursor): default binary path to cursor-agent (avoid path conflict w/ grok) by @BunnyGamezsc in https://github.com/pingdotgg/t3code/pull/4094
* Fix documented task-runner commands (bun run -> vp) by @kridaydave in https://github.com/pingdotgg/t3code/pull/3965
* Allow preview panel to grow on wide displays by @olivoil in https://github.com/pingdotgg/t3code/pull/4044
* fix: prevent initial right-click from selecting a context menu item by @Fazalkadivar21 in https://github.com/pingdotgg/t3code/pull/3877
* Fix duplicate keybinding rule when replacing with an existing rule by @kridaydave in https://github.com/pingdotgg/t3code/pull/3969
* fix(server): image upload crashed dispatchCommand with a stack overflow by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3952
* Remove unused code parameter from describePreviewError by @kridaydave in https://github.com/pingdotgg/t3code/pull/3970
* [codex] prevent ACP assistant ID collisions after restarts by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3932
* fix(web): inset Windows desktop scrollbars from resize edge by @nateEc in https://github.com/pingdotgg/t3code/pull/4097
* [codex] fix mobile composer Enter behavior by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/3930
* feat(server): include runtime model and effort in Codex developer instructions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3948
* fix(ux): spamming cmd + , no longer stack opening settings by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2757
* fix(terminal): strip AppImage runtime env from spawned terminals by @leorivastech in https://github.com/pingdotgg/t3code/pull/3108
* fix(server): thread cwd through Claude capability probe (#2048) by @mvanhorn in https://github.com/pingdotgg/t3code/pull/2124
* [codex] fix: guard invalid web timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3515
* [codex] fix: tolerate invalid latest user message timestamps by @StiensWout in https://github.com/pingdotgg/t3code/pull/3521
* [codex] Fix provider update checks restore defaults by @StiensWout in https://github.com/pingdotgg/t3code/pull/3531
* fix(server): skip undecodable provider runtime rows when listing sessions by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3951
* Share MCP OAuth locks across Codex shadow homes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4104
* Preserve T3 Code identity in macOS development launcher by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4102
* fix(web): increase contrast of question option descriptions by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3867
* feat: draft hero landing on the index route by @yordis in https://github.com/pingdotgg/t3code/pull/4055
* feat: file explorer mention actions and zoom-aware context menus by @yordis in https://github.com/pingdotgg/t3code/pull/4054
* fix(mobile): restore iOS home screen branding by @PixPMusic in https://github.com/pingdotgg/t3code/pull/4025
* perf(client): defer active thread cache writes by @Chrrxs in https://github.com/pingdotgg/t3code/pull/4006
* Default diffs to working changes by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3974
* Add Grok to marketing site provider list by @Aditya190803 in https://github.com/pingdotgg/t3code/pull/3484
* Fix reopening existing Diff tab by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3973
* Fix sending messages during active turns by @jakeleventhal in https://github.com/pingdotgg/t3code/pull/3919
* [codex] Route OpenCode missing-session errors through Effect by @StiensWout in https://github.com/pingdotgg/t3code/pull/3608
* [fix/feat:ui] Show default option badge by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3232
* [fix/feat:ui] Preserve open-in editor brand colors by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3225
* fix(web): handle macOS Home and End in composer by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2508
* Allow failed remote environments to be removed by @zepi2509 in https://github.com/pingdotgg/t3code/pull/4084
* [codex] canonicalize client timestamps by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4112
* [fix/feat:ui] Make selected menu checks blue by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/3234
* fix(desktop): Validate WSL node version against engine range after probe success by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/3621
* Refresh splash screen and favicon branding by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4120
* Add terminal selection copy action by @tarik02 in https://github.com/pingdotgg/t3code/pull/2904
* Add isolated app testing workflow by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4121
* feat(web): themed sidebar header art for nightly and dev builds by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4130
* feat: add headless `t3 connect` setup for SSH hosts by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3749
* Refine T3 Connect authorization surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4159
* fix: increase OpenCode server startup timeout from 5s to 30s by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4132
* fix(shared): delete unused agentAwareness phase predicates by @kridaydave in https://github.com/pingdotgg/t3code/pull/4134
* fix(mobile): Stabilize native stack option updates by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4037
* Make test-t3-app skill discoverable by Claude Code by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4162
* fix(web): improve dev sidebar backdrop contrast & remove version pills by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4166
* Fix draft banner stack overlap by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4164
* Add portable mobile app testing guidance by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4165
* fix(client): use lightweight connection probe by @eeinarsson in https://github.com/pingdotgg/t3code/pull/4137
* fix(server): resolve Claude SDK executable path on Windows npm installs by @nsxdavid in https://github.com/pingdotgg/t3code/pull/3740
* Fix project action preview settings persistence by @keeperxy in https://github.com/pingdotgg/t3code/pull/3842
* fix(desktop): allow clipboard writes in the preview browser by @carlosricojr in https://github.com/pingdotgg/t3code/pull/3889
* fix(web): handle sidebar shortcut before editors by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3921
* fix(server): recognize Bedrock-backed Claude as authenticated by @PieterVanZyl-Dev in https://github.com/pingdotgg/t3code/pull/3931
* Fix incorrect pluralization of “entry” by @McMelonTV in https://github.com/pingdotgg/t3code/pull/3933
* feat(server): title background-task work-log rows with the task name by @t3dotgg in https://github.com/pingdotgg/t3code/pull/3751
* fix: delegate OpenCode session titles to provider by @tris203 in https://github.com/pingdotgg/t3code/pull/3720
* Archive selected threads from the context menu by @theduke in https://github.com/pingdotgg/t3code/pull/3895
* fix(cli): support force removing projects by @Bortlesboat in https://github.com/pingdotgg/t3code/pull/3922
* fix: allow sidebar to be shrunk when wider than viewport by @shoaib050326 in https://github.com/pingdotgg/t3code/pull/2456
* fix(codex): show web search query and url in tool call details by @GuilhermeVieiraDev in https://github.com/pingdotgg/t3code/pull/2093
* Add Codex launch arguments setting by @jamesx0416 in https://github.com/pingdotgg/t3code/pull/2892
* [orchestration] Clear stale active turn when session becomes inactive by @Andrew-Forster in https://github.com/pingdotgg/t3code/pull/3159
* Regenerate Codex reset credit protocol bindings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4173
* fix(preview): preserve direct localhost navigation by @Chrrxs in https://github.com/pingdotgg/t3code/pull/3939
* Synchronize mobile threads with authoritative shell snapshots by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4163
* Gate iOS glass layout on native support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4032
* fix(opencode): resume the OpenCode session on follow-ups instead of starting an empty one by @vdmkotai in https://github.com/pingdotgg/t3code/pull/3617
* fix(server): use CLI for OpenCode health check instead of spawning server by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4153
* fix(web): scope timeline minimap hover target to the side gutter by @xxashxx-svg in https://github.com/pingdotgg/t3code/pull/3869
* [codex] show complete approval details by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4111
* fix(web): paint text selection over composer chips by @yordis in https://github.com/pingdotgg/t3code/pull/4139
* [codex] preserve custom model slugs by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4168
* fix(web): preview workspace images in the file panel by @Rhiz3K in https://github.com/pingdotgg/t3code/pull/3996
* feat(web): drag files from the explorer into the chat composer by @yordis in https://github.com/pingdotgg/t3code/pull/4140
* fix(desktop): preserve main window bounds by @anirudhsama in https://github.com/pingdotgg/t3code/pull/3851
* perf(orchestration): speed up new-chat propagation and offline catch-up by @RusiruSadathana in https://github.com/pingdotgg/t3code/pull/4177
* Finale: upgrade changed files card to fix various UI issues by @sandersonstabo in https://github.com/pingdotgg/t3code/pull/4113
* Pass CLI OAuth config to hosted web deploy by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4186
* fix(web): always show environment chip for remote projects by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4217
* fix(web): keep composer editable while disconnected by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4241
* fix: better defaults — Claude 1M context, Codex gpt-5.6, worktrees from origin main by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4240
* fix(claude): handle all SDK stream messages; stop spurious work-log warning rows by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4244
* Sidebar v2 beta: flat thread list with a server-backed settled lifecycle by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4026
* fix(settings): validate the add-provider wizard step before advancing (#2813) by @leorivastech in https://github.com/pingdotgg/t3code/pull/3100
* fix(claude): isolate capability probe from user MCP servers by @jbbottoms in https://github.com/pingdotgg/t3code/pull/4015
* Preserve connecting status while a turn starts by @D3OXY in https://github.com/pingdotgg/t3code/pull/4101
* fix(server): stop restoring stale OpenCode models by @nateEc in https://github.com/pingdotgg/t3code/pull/4095
* [codex] keep scoped package references as text by @maxwellyoung in https://github.com/pingdotgg/t3code/pull/4167
* fix(web): default provider selection for users without Codex by @mfazekas in https://github.com/pingdotgg/t3code/pull/4117
* Unify temporary worktree branch naming by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4278
* fix(web): use message-square icon for settled icon-less project threads in sidebar v2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4279
* Stabilize sidebar settling animations by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4280
* Restore Copy Link in chat link context menu by @caezium in https://github.com/pingdotgg/t3code/pull/4161
* fix(desktop): handle EPIPE errors on stdout/stderr to prevent crash dialog by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4213
* Preserve draft thread highlighting during promotion by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4283
* Move mobile working timer into the thread timeline by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4285
* Stabilize PR status lookups and provider session lifecycle by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4281
* fix: open command palette instead of custom dialog for new thread picker in SidebarV2 by @UtkarshUsername in https://github.com/pingdotgg/t3code/pull/4269
* fix(server): don't drop sticky PR fallback when remote URL can't be resolved by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4289
* feat(web): copy branch name via right-click in the branch selector by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4275
* Add remote server updates and standalone service management by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4286
* Refine light-mode sidebar surfaces by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4268
* fix(mobile): don't mark Android VPN/Tailscale as offline when connected by @Wraient in https://github.com/pingdotgg/t3code/pull/3949
* improve and prevent silent thread branch drift and PR fetching by @justsomelegs in https://github.com/pingdotgg/t3code/pull/2284
* Refresh web application surfaces and dark-mode dialogs by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4319
* fix(web): new-thread defaults ignored for remote environments by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4276
* feat: add "Auto" runtime mode — AI-reviewed approvals for Codex and Claude by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4272
* Add shared t3.json project configuration support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4317
* Unify dialog glass and fix composer overlays by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4365
* fix(web): warn before silent Windows updates by @nateEc in https://github.com/pingdotgg/t3code/pull/4350
* [codex] Move project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4313
* [codex] Group project scopes in mobile thread lists by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4314
* [codex] Move mobile project grouping to General settings by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4315
* [codex] Deduplicate connection failure messaging by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4367
* Restore grouped project filtering in Sidebar V2 by @shivamhwp in https://github.com/pingdotgg/t3code/pull/4282
* [codex] restore Sidebar V2 project actions by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4373
* [codex] Group projects in new-thread pickers by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4312
* fix(web): restore dark composer toolbar styling by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4375
* Fix thread tooltip folder icon color by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4383
* fix(server): parse CLI version in update preflight by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4389
* fix(web): sidebar v2 polish — jump hints, working duration, in-flight fade, settled sort by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4274
* Fix logical project grouping labels on mobile by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4391
* Add preview color scheme controls and simplify project grouping by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4385
* fix(cli): publish nightly branded favicons by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4372
* Fix thread loading flash by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4396
* fix(client-runtime): keep a warm thread un-settled despite a merged/closed PR by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4309
* Fix composer context strip alignment and glass shell by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4404
* Polish iOS git progress overlay with glass effects by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4387
* Improve composer glass fallbacks by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4406
* feat(web): collapse large git diffs by default to make chat more readable by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4409
* Stop new threads inheriting checkout/branch from viewed thread by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4411
* fix: tone down branch-mismatch banner by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4416
* fix: Claude Code skills discoverable for the composer $ picker by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4414
* fix(web): keep settled threads reachable when opened directly by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4413
* feat(sidebar-v2): thread snoozing by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4311
* Upgrade Clerk packages and Expo integration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4440
* Increase light-mode contrast for user message bubbles by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4441
* Restore model picker layout and retain iterative test state by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4450
* Color settled PR labels on hover by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4451
* [codex] Fix glass hover compositing artifacts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4446
* Add Claude Opus 5 model by @thomaslittle in https://github.com/pingdotgg/t3code/pull/4472
* feat(web): add collapse-all toggle to diff panel by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4475
* feat(web): show fast mode as a bolt instead of a "Normal" label by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4488
* feat(dev): keep worktree dev state isolated on T3 Code dev servers by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4555
* feat(dev): Make t3 code dev instances shareable over Tailscale by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4556
* fix(dev): skip browser-blocked ports by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4608
* fix: cut websocket throughput in half by pruning activity payloads by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4622
* perf(mobile): defer work-log detail serialization by @saphid in https://github.com/pingdotgg/t3code/pull/4607
* test: account for lazy thread feed details by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4628
* feat(relay): limit managed tunnels per user by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4530
* Add managed tunnel limits migration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4635
* Add background preview capture and picture-in-picture support by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4397
* feat(web): prompt stash — cmd+S saves the composer to a per-provider queue by @t3dotgg in https://github.com/pingdotgg/t3code/pull/4453
* [codex] Upgrade Effect and Alchemy betas by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4643
* feat: allow new thread creation through project breadcrumbs by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4638
* fix(web): scope PR state to the thread branch by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4460
* Drop redundant Relay user indexes by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4648
* feat(connect): release the Cloudflare tunnel when the environment shuts down by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4531
* Fix Relay Worker RuntimeContext wiring by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4653
* Fix live sidebar resize limits and defer Alchemy runtime context by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4655
* fix(web): constrain branch toolbar context by @maxktz in https://github.com/pingdotgg/t3code/pull/4657
* Keep MCP credentials alive across provider turns by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4659
* fix: close actions dropdown when editing by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4660
* fix(preview): stabilize PiP viewport identity by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4661
* Add glass styling for thread tooltips and simplify preview tab handling by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4665
* Use tarball archiving for hosted web deploys by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4669
* fix(server): bound editor discovery during config loading by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4291
* Prevent draft thread detail polling before shell registration by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4670
* feat: add configurable source control writing settings by @maria-rcks in https://github.com/pingdotgg/t3code/pull/4204
* feat(diff-panel): show total line additions and deletions by @0x4bs3nt in https://github.com/pingdotgg/t3code/pull/4674
* Clear provider update actions while updating by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4676
* Fix sidebar highlighting for draft threads by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4679
* Use glass surfaces for web toasts by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4681
* Show origin ref in branch trigger label by @juliusmarminge in https://github.com/pingdotgg/t3code/pull/4680
* fix(mobile): match react version to react-native 0.85.3 vendored renderer (19.2.3) by @KrzysztofMoch in https://github.com/pingdotgg/t3code/pull/4675

## New Contributors
* @avocardow made their first contribution in https://github.com/pingdotgg/t3code/pull/3790
* @Prgm-code made their first contribution in https://github.com/pingdotgg/t3code/pull/3816
* @jakeleventhal made their first contribution in https://github.com/pingdotgg/t3code/pull/3899
* @VedankPurohit made their first contribution in https://github.com/pingdotgg/t3code/pull/3644
* @kridaydave made their first contribution in https://github.com/pingdotgg/t3code/pull/3990
* @dmstoykov made their first contribution in https://github.com/pingdotgg/t3code/pull/4017
* @HugoVizcainoSantana made their first contribution in https://github.com/pingdotgg/t3code/pull/3832
* @EricTsai83 made their first contribution in https://github.com/pingdotgg/t3code/pull/3998
* @AmoonPod made their first contribution in https://github.com/pingdotgg/t3code/pull/4088
* @BunnyGamezsc made their first contribution in https://github.com/pingdotgg/t3code/pull/4094
* @olivoil made their first contribution in https://github.com/pingdotgg/t3code/pull/4044
* @Fazalkadivar21 made their first contribution in https://github.com/pingdotgg/t3code/pull/3877
* @maxwellyoung made their first contribution in https://github.com/pingdotgg/t3code/pull/3932
* @nateEc made their first contribution in https://github.com/pingdotgg/t3code/pull/4097
* @leorivastech made their first contribution in https://github.com/pingdotgg/t3code/pull/3108
* @xxashxx-svg made their first contribution in https://github.com/pingdotgg/t3code/pull/3867
* @yordis made their first contribution in https://github.com/pingdotgg/t3code/pull/4055
* @Chrrxs made their first contribution in https://github.com/pingdotgg/t3code/pull/4006
* @Aditya190803 made their first contribution in https://github.com/pingdotgg/t3code/pull/3484
* @zepi2509 made their first contribution …
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant