Skip to content

fix(ios): show a sidebar toggle on the unfolded iPhone Duo in portrait - #20133

Merged
juliusmarminge merged 3 commits into
pingdotgg:mainfrom
PixPMusic:pixpmusic/ios-duo-sidebar-toggle
Oct 16, 2026
Merged

juliusmarminge merged 3 commits into
pingdotgg:mainfrom
PixPMusic:pixpmusic/ios-duo-sidebar-toggle

Conversation

@PixPMusic

@PixPMusic PixPMusic commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On the iPhone Duo unfolded in portrait, an open thread fills the screen and the thread list is hidden. The only way back to the list is an edge swipe, and nothing in the header hints at it. iPad shows a sidebar toggle in the same situation. Julius agreed in Discord that there should be a sidebar button there, top left.

Cause

Unfolded in portrait, the inner display (669×951pt, regular width) reports a hinge but no side bar and no fold division. The Duo rule in deriveLayout keys off those two signals, so this posture fell through to the iPad floor of 720pt and went compact. UIKit still kept the split expanded, so it showed the thread alone. With a compact layout the thread header never got the split view's sidebar toggle.

Fix

  • deriveLayout also treats a display with a hinge as Duo geometry. Unfolded portrait now takes the same split path as iPad: the list sits beside the thread, and the thread header gets the sidebar toggle (Maximize content / Show thread sidebar).
  • In that split, the Threads and Pull requests tab buttons sit at the leading edge of the thread list's bottom toolbar, with compose at the trailing edge, so they hide when the list collapses. Without this, UIKit's phone tab bar spread across both panes, its pill centered near the divider and its compose button under the thread, duplicating the header's. UIKit can't leading-align its floating tab bar, so it stays hidden in this posture. The Duo's side rail still carries UIKit's tabs when it has one, and iPad keeps its segmented picker, which now shares the tab selection logic.

Unfolded landscape already took the Duo path through its side rail, and the folded front screen reports a compact size class, so neither changes.

Verification

  • Device metrics from the iOS 27.1 Duo simulator, read from useNativeLayoutMetrics:

    Posture Size Size class Side bar Layout before → after
    Unfolded, portrait 669×951 regular none compact → split
    Unfolded, landscape 951×669 regular right split → split
    Folded, portrait 466×678 compact right compact → compact
    Folded, landscape 678×466 compact right compact → compact
  • vp test run apps/mobile/src/lib/reserved-regions.test.ts apps/mobile/src/lib/layout.test.ts: 49 passed. New cases use the measured unfolded-portrait metrics (split with the hinge, compact without it) and the folded metrics in both orientations.

  • vp run typecheck in apps/mobile passes. vp lint and vp fmt --check on the changed files are clean.

  • On the Duo, unfolded in portrait, with a synthetic showcase environment: the toggle hides and shows the list. The toolbar tabs switch between Threads and Pull requests, including from an open thread (which closes it), and the selected tab is highlighted. Collapsing the list hides the tabs, leaving compose only in the thread header. In unfolded landscape, the side rail and the list beside the thread look the same as on main.

  • On an iPad simulator, the sidebar's segmented picker still switches tabs after the shared selection hook refactor.

  • Not checked: the folded front screen visually. The simulator stream goes black while folded, so the folded rows above come from the metrics log only.

Before / After

iPhone Duo, unfolded, portrait.

Before After
Thread Before: the thread fills the screen with no sidebar control in the header After: the list is collapsed and a sidebar button sits at the top left
Thread with list — After: the thread list beside the thread, with tab buttons and compose at the bottom of the list
Home Before: Home with the phone tab bar along the bottom After: Home with tab buttons at the bottom left of the list and New Task in the empty pane

Model: Claude Opus 5.5 | Harness: Claude Code in T3 Code

Unfolded in portrait, the Duo's inner display reports a hinge but no
side bar or fold division, so deriveLayout skipped the Duo rule and
applied the iPad width floor (720pt) to its 669pt window. The layout
went compact while UIKit kept the split expanded: the thread filled the
screen with no way back to the list except an edge swipe.

Treat any hinged display as Duo geometry, so unfolded portrait takes
the split path iPad uses: the list beside the thread, the sidebar toggle
in the thread header, and the Threads/Pull requests picker in the
sidebar instead of the phone tab bar. Landscape keeps its side rail and
the folded screen stays compact.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Oct 16, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 16, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 50ae77d

Macroscope's review found this PR approvable — This is a focused iOS Duo portrait layout fix with localized workspace toolbar changes and regression coverage for the affected device metrics. Existing iPad, folded Duo, Android, and side-rail paths remain accounted for, with no schema, infrastructure, security, or static-analysis changes.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 21d4a3b2-12bb-491f-bd25-2c7beb25c1bb
📥 Commits

Reviewing files that changed from the base of the PR and between c8bbdcc and 50ae77d.

📒 Files selected for processing: 5
  • apps/mobile/src/features/workspace/WorkspaceRootScreen.tsx
  • apps/mobile/src/features/workspace/WorkspaceSidebarTabs.ios.tsx
  • apps/mobile/src/features/workspace/WorkspaceToolbar.ios.tsx
  • apps/mobile/src/features/workspace/useSystemWorkspaceTabBar.ts
  • apps/mobile/src/lib/layout.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Duo layout detection now considers hinge metadata. iOS sidebar tabs can use picker or toolbar placement based on layout metrics. Tab selection is shared between the sidebar picker and toolbar controls.

Changes: Duo display layout

Layer / File(s) Summary
Hinge geometry and layout tests
apps/mobile/src/lib/layout.ts, apps/mobile/src/lib/reserved-regions.test.ts
deriveLayout now treats reported hinge metadata as Duo geometry. Tests cover unfolded displays with and without hinge metadata, and folded front-screen layouts.

iOS workspace tabs

Layer / File(s) Summary
Sidebar-tab placement and picker layout
apps/mobile/src/features/workspace/useSystemWorkspaceTabBar.ts, apps/mobile/src/features/workspace/WorkspaceRootScreen.tsx
The sidebar-tab hook returns "picker", "toolbar", or null based on platform and layout metrics. The workspace root applies picker layout behavior only in picker mode.
Tab selection and toolbar controls
apps/mobile/src/features/workspace/useSelectWorkspaceTab.ts, apps/mobile/src/features/workspace/WorkspaceSidebarTabs.ios.tsx, apps/mobile/src/features/workspace/WorkspaceToolbar.ios.tsx
A shared hook handles tab selection, keyboard dismissal, and navigation-stack popping. The sidebar picker uses the hook. When tabs are assigned to the toolbar, it displays Threads and Pull requests buttons with selection-specific tint.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant WorkspaceToolbar
  participant useSelectWorkspaceTab
  participant Keyboard
  participant Navigation
  participant WorkspaceState
  User->>WorkspaceToolbar: Select Threads or Pull requests
  WorkspaceToolbar->>useSelectWorkspaceTab: Select requested tab
  useSelectWorkspaceTab->>Keyboard: Dismiss keyboard
  useSelectWorkspaceTab->>Navigation: Pop to top when navigation index is truthy
  useSelectWorkspaceTab->>WorkspaceState: Dispatch tab selection
Loading

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 50ae7

The unfolded portrait Duo routes workspace tabs to the thread-list toolbar. No merge-blocking issue was identified; merge after normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main iOS Duo portrait-layout fix.
Description check Passed The description explains the problem, cause, fix, affected layouts, verification results, screenshots, and known limitation. It does not include a dedicated Scope and approval section or a link to the…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

The previous commit gave the unfolded portrait Duo iPad's segmented
picker. Keep tab buttons instead: Threads and Pull requests sit at the
leading edge of the thread list's bottom toolbar, with compose at the
trailing edge, so they hide with the list. UIKit's own tab bar cannot be
leading-aligned, so it stays hidden in this posture. iPad keeps its
picker, which now shares the tab selection logic.
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 16, 2026 03:44

Dismissing prior approval to re-evaluate c8bbdcc

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Oct 16, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Oct 16, 2026
# Conflicts:
#	apps/mobile/src/features/workspace/WorkspaceRootScreen.tsx
#	apps/mobile/src/features/workspace/useSystemWorkspaceTabBar.ts
@macroscopeapp
macroscopeapp Bot dismissed their stale review October 16, 2026 05:59

Dismissing prior approval to re-evaluate 50ae77d

@juliusmarminge
juliusmarminge merged commit d35882e into pingdotgg:main Oct 16, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 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.

2 participants