Repository navigation
fix(mobile): iOS 18 iPad sidebar and thread feed start below the header - #32936
Conversation
Before iOS 26 the native bars are opaque, but the column still lays content out under them. The iPad thread sidebar turned UIKit's automatic inset off there, and ThreadFeed always got a top inset of 0. So the first rows and the "Load earlier activity" button sat behind the bar, where they could not be tapped. - The sidebar list uses UIKit's automatic content inset on every iOS version, as Home does, so it clears the bar and its stacked search field. - When UIKit doesn't inset the feed, ThreadFeed sizes its top spacer, anchors and indicator insets from the column's measured top safe area (useContentTopInset). That value is 0 on Android and on iOS 26 it stays 0 because UIKit owns the inset there.
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This corrects sidebar and thread-feed spacing beneath iOS headers and aligns header appearance with scrolling. The changes are limited to layout behavior, with no persistent-data or external-system effects. No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
Handoff from the issue-watch investigation of #32884. I did not reproduce this on a device: the iOS/Android pools were empty and shiv was unreachable during my run. No app build, native interaction, screenshot or recording was produced, and no format/lint/typecheck or repository test suite ran. Source investigation at A local alternative fix, |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
Dismissing prior approval to re-evaluate eb39186
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate f76e21b
**v0.1.9-nightly.20261110.4491** · 40 changes from 9 contributors - **Shared OpenCode goals.** OpenCode goals stay in sync across clients. - **OpenCode checklists.** See OpenCode todo updates as checklists. - **Drag threads into the composer.** Drag threads from the command palette into the composer as context without navigating away. - **More usage limits.** See Muse Code limits and xAI and Antigravity accounts managed through CLIProxyAPI in Usage → Limits. - **Open apps from links.** Open apps from Markdown links and see their icons. - **Checkpoint new files.** Checkpoints retain new files when ignore files contain blank lines. - **Open cited files.** Open more file links from chat, including citations with line ranges. ### Plus 30 more bug fixes and performance improvements. <details> <summary>Full changelog</summary> ## What's Changed * fix(mobile): iOS 18 iPad sidebar and thread feed start below the header by @juliusmarminge in pingdotgg/t3code#32936 * fix(web): Tab reaches the add-project and new-project submit buttons by @juliusmarminge in pingdotgg/t3code#32605 * fix(web): screen readers hear the add-project submit shortcut by @juliusmarminge in pingdotgg/t3code#32576 * fix(server): Antigravity limits read the quota host the account is metered on by @juliusmarminge in pingdotgg/t3code#32425 * fix(web): the thread menu works for an older settled thread the sidebar hasn't loaded by @juliusmarminge in pingdotgg/t3code#32507 * fix(opencode): show questions asked while OpenCode wakes a session on its own by @juliusmarminge in pingdotgg/t3code#32434 * fix(server): blank .gitignore lines no longer make checkpoints drop new files by @juliusmarminge in pingdotgg/t3code#32374 * test(web): minimap next-turn test no longer flakes after a resize by @t3dotgg in pingdotgg/t3code#33964 * feat(opencode): mirror native goals across clients by @Yash-Singh1 in pingdotgg/t3code#33758 * feat(web): bindable shortcut to regenerate a thread's title by @sharanry in pingdotgg/t3code#33987 * fix(server): linked projects keep their repository identity while it refreshes by @juliusmarminge in pingdotgg/t3code#32359 * fix(mobile): tool call commands are highlighted and monospaced like on web by @juliusmarminge in pingdotgg/t3code#33957 * fix: Claude monitor wakes no longer fire a completion alert per line by @juliusmarminge in pingdotgg/t3code#32520 * fix(server): removed thread worktrees keep their project's skills in the picker by @juliusmarminge in pingdotgg/t3code#32471 * fix(web): drag command palette threads into the composer by @Yash-Singh1 in pingdotgg/t3code#33990 * fix(shared): show zero seconds in minute durations by @Yash-Singh1 in pingdotgg/t3code#33994 * fix(web): closing the right panel returns focus to where it was by @juliusmarminge in pingdotgg/t3code#32569 * fix(server): say why threads without a project are unavailable by @juliusmarminge in pingdotgg/t3code#32342 * fix: chat file links open the file agents cite by @juliusmarminge in pingdotgg/t3code#33943 * fix(clients): scroll truncated command output notice with logs by @Yash-Singh1 in pingdotgg/t3code#34011 * fix(web): show app bundle icons and open apps from markdown links by @Yash-Singh1 in pingdotgg/t3code#33950 * feat(opencode): project native todo updates into checklists by @Yash-Singh1 in pingdotgg/t3code#34000 * fix(antigravity): recognize Gemini 4 Carbon and its quota by @DrSeedon in pingdotgg/t3code#33991 * perf(client): reopening a finished command row no longer refetches its output by @juliusmarminge in pingdotgg/t3code#34002 * perf(server): user-only message reads use the user-message index for ordinals by @juliusmarminge in pingdotgg/t3code#33009 * fix(web): keep HTML previews anchored when accordions expand by @Yash-Singh1 in pingdotgg/t3code#34003 * fix(web): preserve search result titles in drag previews by @Yash-Singh1 in pingdotgg/t3code#34017 * fix: file links with line ranges open at their first line by @juliusmarminge in pingdotgg/t3code#34006 * fix(codex): show image generation tool calls by @Yash-Singh1 in pingdotgg/t3code#34020 * fix(pi): a steer Pi has not read yet shows as waiting, not steered by @juliusmarminge in pingdotgg/t3code#33982 * fix(server): provider switches hand off started cancelled runs by @juliusmarminge in pingdotgg/t3code#34016 * fix(web,mobile): replace sparkles icons and ban them with a lint rule by @flamboh in pingdotgg/t3code#25422 * fix(github): a proxy's 403 with Retry-After is no longer reported as a rate limit by @juliusmarminge in pingdotgg/t3code#33676 * fix(web): sending from an empty citation comment clears the composer by @juliusmarminge in pingdotgg/t3code#33440 * fix(server): text generation fallback respects project provider settings by @Muzammil-Bit in pingdotgg/t3code#33741 * feat(usage): show Muse Code limits, and xAI and Antigravity accounts from a CLIProxyAPI hub by @StiensWout in pingdotgg/t3code#33800 * fix(web): preserve previews during attachment preparation by @Yash-Singh1 in pingdotgg/t3code#33949 * fix(desktop): input during window teardown no longer throws in the main process by @juliusmarminge in pingdotgg/t3code#33620 * fix(web): sent messages keep the numbers written on list items by @tsapa44 in pingdotgg/t3code#33820 * fix(server): quitting stops an agent-device daemon left by an earlier server by @juliusmarminge in pingdotgg/t3code#33587 ## New Contributors * @sharanry made their first contribution in pingdotgg/t3code#33987 * @tsapa44 made their first contribution in pingdotgg/t3code#33820 **Full Changelog**: pingdotgg/t3code@v0.1.9-nightly.20261110.4478...v0.1.9-nightly.20261110.4491 </details> Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.1.9-nightly.20261110.4491
… (Checking unused code) Failing check: Checking unused code **What failed:** "Checking unused code". Knip reported one unused file: `apps/mobile/src/features/layout/workspace/workspace-list-insets.ts`. **Why:** Before the merge, Loom used this file through its `WORKSPACE_LIST_SCROLL_INSETS` export in `ThreadNavigationSidebar.tsx`. Upstream's pingdotgg#32936 (80e6261) removed that export and its import. All that's left in the file is `useWorkspaceListEmptyTopInset`, and upstream calls it only from the early-return empty state in `HomeScreen.tsx`. Loom's `project-navigation` seam replaces that early return with empty states rendered inside the list. The list already sits below the glass bar through `contentTopInset`, so the fork has no caller for the hook and doesn't need one. Pre-merge, the hook was already an unused export, but knip's export pass doesn't cover `apps/mobile`. Nothing flagged it until it became the file's only export. **What I changed:** Deleting upstream's file isn't allowed, and adding the inset to the in-list empty state would offset it twice. Instead I added a marked seam to `knip.jsonc`: an `ignore` entry for that file in the `apps/mobile` workspace, marked `// fork: ext-lint`. This copies the existing `apps/web` thread-inspector ignore. I bumped the `knip.jsonc ext-lint` row in `docs/fork/seams.tsv` from 7 to 8 and added a sentence to the knip note in FORK.md. `vp run knip:check:all` now passes (3/3), and `check_seams` exits 0.
Problem
On iPadOS / iOS before 26 (no Liquid Glass, so the bars are solid), two lists start underneath their column's header. The native column lays content out under the bar, but these lists add no top inset there:
The same happens to the top of an ordinary thread feed, to the empty-thread placeholder, and to the first message sent into an empty thread, on iPad and iPhone.
Change
ThreadNavigationSidebarnow uses UIKit's automatic content inset (contentInsetAdjustmentBehavior="automatic", with indicator insets) on every iOS version. Before, it was only on when glass was supported. UIKit then clears the bar and its stacked Search field. On iOS 26 the props are the same as before. TheWORKSPACE_LIST_SCROLL_INSETSconstant only existed to turn this off on iOS < 26, so it is removed.ThreadFeedno longer takes acontentTopInsetprop. Its only caller always passed0.contentInset(iOS < 26), its top inset isuseContentTopInset(), the column's measured top safe area. That is the bar height where the column lays content under the bar, and 0 on Android.contentInsetof the bar height, it stays 0.useContentTopInsetnow says it applies to solid bars too.Scope and approval
Fixes #32884
Fixes #22543
Triaged via the automated issue flow. https://git.cafe/pingdotgg/t3code/issues/22543 reports the sidebar half on its own.
Verification
vp run check:changedpasses on the head08e1fb258a(format, lint, knip, typecheck, react compiler, tests beside the changed files).ca796d5211(N1:80e1e4d14d). After iseb39186ba6,f76e21be6c(comment-only on top), or4980e33628, as each row says; the head08e1fb258aonly merges main with noapps/mobilechanges. Both sides share the native fingerprint and Legend List 3.7.2. Main merged sinceeb39186ba6changes only file preview, asset loading and the Android rail width (0 off Android) underapps/mobile. The changed inset paths are identical apart from comments.ca796d5211→eb39186ba6ca796d5211→eb39186ba6ca796d5211→eb39186ba6ca796d5211→eb39186ba6ca796d5211→4980e33628ca796d5211→eb39186ba6ca796d5211→f76e21be6cca796d5211→eb39186ba6ca796d5211→f76e21be6cca796d5211→f76e21be6c80e1e4d14d→4980e3362880e1e4d14d→4980e33628ca796d5211→4980e33628Recordings:
Evidence write-ups:
Seed and driver scripts: seed-rpc.mjs, driver.md, capture.py, seed.py, recapture-seed.mjs, repair-comparisons.py, repair-four-comparisons.py.
What else changed in the captures: only the moved content above. Inside each crop or mask, the following still differ:
The recordings also carry touch markers and grey boxes over model labels. In the I1 before recording the inspector is 266 pt wide; after, it is 260 pt. Run 3's Search recordings show a Working counter only on the after side.
Not checked:
useContentTopInsetreturns 0 there, and the sidebar change is in the iOS-only branch.Verification log
4980e33628, then08e1fb258a), with no conflicts.4980e33628both reviewers returned MERGE with no findings.4980e33628. Its two passes found clock, timestamp and parent-pane noise in S2, F2, F3, F5, F8, I2, I3 and N1, and a before/before G1 pair. Those comparisons were rebuilt as above. The auditor rebuilt all ten independently with zero pixel delta and returned sufficient.scrollToIndexwithviewPosition: 0.2), so it is out of scope here.AI-assisted; verified with the tests and tools listed above.