Skip to content

Fix capped session pane interaction paths - #8250

Merged
lawrencecchen merged 8 commits into
mainfrom
fix-session-content-width-autoreview
Jul 16, 2026
Merged

lawrencecchen merged 8 commits into
mainfrom
fix-session-content-width-autoreview

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #8222.

Preserves full-pane terminal overlays and Canvas width caps, keeps terminal gutter focus, scrolling, and context menus on existing interaction paths, and keeps agent-chat gutter scrolling active. Splits shared geometry and gutter-host types into policy-compliant files.

Verification:

  • canonical autoreview clean
  • CmuxSettings: 258 tests passed
  • CmuxSettingsUI: 112 tests passed
  • tagged macOS cloud build passed on the reviewed implementation

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes interaction gaps around capped session content so clicking, scrolling, and context menus work in terminal and agent chat panes. Keeps full‑pane overlays and Canvas width caps while retargeting gutter events to the right surface and coalescing agent gutter scrolls.

  • Bug Fixes

    • Routes gutter scrolling in agent chat panes to the transcript and coalesces/clamps deltas for smoother scroll.
    • Clicking gutters focuses the webview; right‑click shows a pane‑chrome menu without sending terminal events.
    • Preserves terminal focus, first‑click, and scrolling from empty space around capped content; agent web content respects max width and alignment.
  • Refactors

    • Extracts SessionContentWidthPresentation for shared width/alignment logic across SwiftUI and AppKit hosts.
    • Moves the terminal gutter host into TerminalPaneBackgroundView and wires it into the Ghostty surface scroll view.

Written for commit 005ca30. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added persisted controls to limit agent session content width and choose horizontal alignment.
  • Bug Fixes
    • Improved terminal background interactions by forwarding focus, context menus, and scroll/pointer events to the correct terminal views.
    • Corrected hosted session web view sizing and layout to respect the selected content width presentation.
  • Usability
    • Refined right-click and pointer interaction behavior so context menus and first-interaction handling work more consistently.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes refactor terminal background event forwarding and context-menu construction, and add persisted width/alignment presentation state for sizing and interacting with hosted agent-session web views.

Changes

Terminal interaction handling

Layer / File(s) Summary
Context-menu creation refactor
Sources/GhosttyTerminalView.swift
menu(for:) uses shared context-menu construction, while pane menus can omit synthesized terminal pointer events.
Background pane event forwarding
Sources/GhosttyTerminalView.swift, Sources/TerminalPaneBackgroundView.swift, cmux.xcodeproj/project.pbxproj
Background views forward terminal pointer, context-menu, focus, and scroll interactions through direct view references, with the new source included in the project.

Session content presentation

Layer / File(s) Summary
Presentation model and panel wiring
Sources/Panels/SessionContentWidthPresentation.swift, Sources/Panels/SessionContentWidthModifier.swift, Sources/Panels/AgentSessionPanelView.swift, cmux.xcodeproj/project.pbxproj
Persisted width and alignment settings are converted into SessionContentWidthPresentation and passed to the web renderer.
Hosted web-view layout and input handling
Sources/Panels/AgentSessionWebRenderer.swift, Sources/Panels/AgentSessionWebHostView.swift
The host sizes its web view using the presentation’s content frame and forwards pointer and scroll events to the hosted web view.

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

Sequence Diagram(s)

sequenceDiagram
  participant AgentSessionPanelView
  participant AgentSessionWebRenderer
  participant AgentSessionWebHostView
  AgentSessionPanelView->>AgentSessionWebRenderer: provide sessionContentWidthPresentation
  AgentSessionWebRenderer->>AgentSessionWebHostView: update presentation
  AgentSessionWebHostView->>AgentSessionWebHostView: compute contentFrame and layout hosted web view
  AgentSessionWebHostView->>AgentSessionWebHostView: forward pointer and scroll events
Loading

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Package Boundaries ❌ Error SessionContentWidthPresentation is pure, testable geometry reused by Canvas, Panels, Ghostty, and tests, but it was added under app-target Sources. Move SessionContentWidthPresentation into CmuxSettings (or a tiny CmuxSessionContentLayout package) and expose a value-only API there.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description covers summary and testing, but it misses the required Demo Video, Review Trigger block, and Checklist sections. Add the missing template sections, especially a demo video for the UI behavior change, the review-trigger snippet, and the checklist.
✅ Passed checks (22 passed)
Check name Status Explanation
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.
Cmux Swift Actor Isolation ✅ Passed The new shared presentation type and host/view updates stay in main/UI code paths; no new background access or mutable Sendable reference types were introduced.
Cmux Swift Blocking Runtime ✅ Passed The only runtime hunk replaces scroll-event retargeting with JS evaluation; no new waits, sleeps, syncs, semaphores, or locks were introduced.
Cmux Browser Automation Off-Main ✅ Passed PR only changes AgentSession/AppKit views; no browser.* socket commands, worker routing, or policy coverage touched, and the lone JS call is non-waiting on @MainActor.
Cmux Expensive Synchronous Load ✅ Passed PASS: The patch only rewires AgentSessionWebHostView scroll handling/layout; no RestorableAgentSessionIndex.load(), JSONL parsing, or other expensive agent-history load was added.
Cmux Cache Substitution Correctness ✅ Passed PASS: the new presentation is a UI/layout value with a .disabled cold default and SwiftUI/AppStorage-driven updates; no persistence/history/snapshot path trusts it.
Cmux No Hacky Sleeps ✅ Passed PASS: The diff only touches a Swift file; this rule applies to non-Swift runtime/script code, and no TS/JS/shell delay code was introduced.
Cmux Algorithmic Complexity ✅ Passed Touched paths only add constant-time geometry/menu forwarding and property plumbing; no new scans, sorts, or batch rescans of scalable collections.
Cmux Swift Concurrency ✅ Passed The diff only adds an AppKit/OS callback boundary (WKWebView.evaluateJavaScript) and leaves the existing Task unchanged; no new DispatchQueue, Combine, or uncontrolled fire-and-forget patterns appear.
Cmux Swift @Concurrent ✅ Passed The only diff is a synchronous scrollWheel retargeting to WebKit JS; no new @concurrent, nonisolated async, or UI-heavy async helpers were introduced.
Cmux Swiftpm Lockfiles ✅ Passed Diff only adds Swift source files and build-file refs; no Package.resolved, .gitignore, or SwiftPM package-reference changes appear.
Cmux Swift Logging ✅ Passed The only changed Swift file adds scroll/focus handling and sizing; the diff contains no print/debugPrint/dump/NSLog/Logger usage.
Cmux User-Facing Error Privacy ✅ Passed The touched files only add layout/interaction code and generic menu labels; no user-facing error, alert, API, or recovery copy exposes vendor/internal details.
Cmux Full Internationalization ✅ Passed The patch only adds layout/event plumbing and file wiring; it adds no user-facing Swift text and no xcstrings, web/messages, or routing locale changes.
Cmux Swiftui State Layout ✅ Passed Only the AppKit bridge scrollWheel logic changed; no new SwiftUI state, GeometryReader measurement, lazy-row store refs, or render-time state mutation was introduced.
Cmux Architecture Rethink ✅ Passed PASS: Changes use shared value snapshots and platform bridge views with clear owners; no new polling, locks, duplicate wiring, or split lifecycle ownership introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No new standalone NSWindow/NSPanel/WindowGroup code or cmux.* window identifiers were added; changes are limited to views and web/terminal hosts.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/config files; no logs, temp dirs, build outputs, or other artifact paths are present.
Cmux No Test Or Debug Seam In Production Source ✅ Passed Touched production files add layout helpers and a background view, but no #if DEBUG or *ForTesting seam was introduced; new setters have production callers.
Cmux No Ambient Global State ✅ Passed HEAD diff only changes AgentSessionWebHostView.scrollWheel; it adds no new top-level funcs, globals, namespaces, or singletons.
Title check ✅ Passed The title clearly matches the main change: fixing interaction paths for capped session panes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-session-content-width-autoreview

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes interaction gaps introduced by capped-width session panes by retargeting gutter events to the correct terminal and agent-chat surfaces. It also splits the shared SessionContentWidthPresentation type into its own AppKit-importable file so width/alignment geometry can be enforced directly on the WKWebView frame rather than relying on a SwiftUI layout modifier.

  • Terminal gutter (TerminalPaneBackgroundView): focus, scroll, right-click, and context-menu events are now forwarded to GhosttyNSView/GhosttyScrollView via direct weak references; paneContextMenu skips sending out-of-bounds mouse events to Ghostty.
  • Agent gutter (AgentSessionWebHostView): scroll events are coalesced with Task.yield(), clamped, and forwarded to the WKWebView via scrollBy JS injection; mouseDown focuses the web view and acceptsFirstMouse delegates to it.
  • Width capping: AgentSessionPanelView drops the SwiftUI .sessionContentWidth() modifier and instead passes a SessionContentWidthPresentation straight to the AppKit host, which sizes the WKWebView frame directly via contentFrame(in:).

Confidence Score: 5/5

Safe to merge — the changes are a focused interaction-routing fix with no regressions identified and a passing test suite.

All actor-isolation boundaries are respected (the whole web host is @mainactor; the JS completion handler re-enters via Task { @mainactor in }). Scroll coalescing uses Task.yield() rather than a fixed sleep. Weak references are used correctly throughout. The paneContextMenu method is a real production entrypoint, not a test seam. No expensive synchronous loads, no ambient global state, and no allocating formatting on hot paths were introduced.

No files require special attention.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Extracts makeContextMenu as a private helper and adds paneContextMenu for background-view right-clicks that skip sending out-of-bounds pointer events to Ghostty; moves TerminalPaneBackgroundView to its own file.
Sources/TerminalPaneBackgroundView.swift New dedicated file for the background gutter view; delegates focus, scroll, right-click, and context-menu events to the real terminal surface/scroll views via weak references.
Sources/Panels/AgentSessionWebHostView.swift Adds gutter scroll coalescing via Task.yield + JavaScript scrollBy injection, acceptsFirstMouse forwarding, and mouseDown focus routing; wires sessionContentWidthPresentation so the hosted web view is sized to the capped frame rather than the full pane.
Sources/Panels/SessionContentWidthPresentation.swift Extracts the shared SessionContentWidthPresentation struct into its own file with a CoreGraphics import so it can be used from AppKit code without pulling in SwiftUI.
Sources/Panels/SessionContentWidthModifier.swift Now only contains the SwiftUI-specific private extension (swiftUIAlignment) and SessionContentWidthModifier; struct body moved to the new dedicated file.
Sources/Panels/AgentSessionPanelView.swift Removes the .sessionContentWidth() SwiftUI modifier and replaces it with @AppStorage reads that build a SessionContentWidthPresentation forwarded to the AppKit web host directly.
Sources/Panels/AgentSessionWebRenderer.swift Threads the new sessionContentWidthPresentation prop through the NSViewRepresentable to the AppKit host view on each update cycle.
cmux.xcodeproj/project.pbxproj Registers the two new Swift source files (SessionContentWidthPresentation.swift and TerminalPaneBackgroundView.swift) in the Xcode project build phase and file reference list.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Gutter as TerminalPaneBackgroundView (gutter)
    participant TermSurface as GhosttyNSView (terminal)
    participant ScrollView as GhosttyScrollView

    Note over Gutter,ScrollView: Terminal pane – capped width interaction paths

    Gutter->>TermSurface: mouseDown → focusFromPointerDown()
    Gutter->>ScrollView: scrollWheel → scrollWheel(event)
    Gutter->>TermSurface: rightMouseDown → focusFromPointerDown() + super
    Gutter->>TermSurface: menu(for:) → paneContextMenu(for:)
    Note over TermSurface: paneContextMenu skips ghostty_surface_mouse_pos/button

    participant AgentHost as AgentSessionWebHostView (gutter)
    participant WebView as WKWebView (agent chat)
    participant JS as DOM .agent-thread

    Note over AgentHost,JS: Agent pane – capped width interaction paths

    AgentHost->>WebView: mouseDown → makeFirstResponder(webView)
    AgentHost->>AgentHost: scrollWheel → accumulate pendingScrollDelta (clamped)
    AgentHost->>AgentHost: scheduleScrollFlush (Task.yield coalesce)
    AgentHost->>JS: evaluateJavaScript scrollBy(−Δx, −Δy)
    JS-->>AgentHost: "completion → isScrollJavaScriptInFlight=false → scheduleScrollFlush"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Gutter as TerminalPaneBackgroundView (gutter)
    participant TermSurface as GhosttyNSView (terminal)
    participant ScrollView as GhosttyScrollView

    Note over Gutter,ScrollView: Terminal pane – capped width interaction paths

    Gutter->>TermSurface: mouseDown → focusFromPointerDown()
    Gutter->>ScrollView: scrollWheel → scrollWheel(event)
    Gutter->>TermSurface: rightMouseDown → focusFromPointerDown() + super
    Gutter->>TermSurface: menu(for:) → paneContextMenu(for:)
    Note over TermSurface: paneContextMenu skips ghostty_surface_mouse_pos/button

    participant AgentHost as AgentSessionWebHostView (gutter)
    participant WebView as WKWebView (agent chat)
    participant JS as DOM .agent-thread

    Note over AgentHost,JS: Agent pane – capped width interaction paths

    AgentHost->>WebView: mouseDown → makeFirstResponder(webView)
    AgentHost->>AgentHost: scrollWheel → accumulate pendingScrollDelta (clamped)
    AgentHost->>AgentHost: scheduleScrollFlush (Task.yield coalesce)
    AgentHost->>JS: evaluateJavaScript scrollBy(−Δx, −Δy)
    JS-->>AgentHost: "completion → isScrollJavaScriptInFlight=false → scheduleScrollFlush"
Loading

Reviews (4): Last reviewed commit: "Preserve capped gutter fallback interact..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c23676f07

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GhosttyTerminalView.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Panels/AgentSessionWebHostView.swift`:
- Around line 39-43: Update mouseDown(with:) in AgentSessionWebHostView so that
when hostedWebView cannot be cast to AgentSessionWebView, it calls
super.mouseDown(with: event) before returning. Preserve the existing pointer
callback and first-responder behavior for successful casts.
- Around line 45-47: Update AgentSessionWebHostView.scrollWheel(with:) with a
re-entrancy guard around forwarding events to hostedWebView. Forward only when
the view is not already forwarding and the subview exists; otherwise call
super.scrollWheel(with:) to prevent recursive forwarding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 04b79205-19d8-4ef7-9eb2-8f2543a38b7b

📥 Commits

Reviewing files that changed from the base of the PR and between 14effdc and 3c23676.

📒 Files selected for processing (8)
  • Sources/GhosttyTerminalView.swift
  • Sources/Panels/AgentSessionPanelView.swift
  • Sources/Panels/AgentSessionWebHostView.swift
  • Sources/Panels/AgentSessionWebRenderer.swift
  • Sources/Panels/SessionContentWidthModifier.swift
  • Sources/Panels/SessionContentWidthPresentation.swift
  • Sources/TerminalPaneBackgroundView.swift
  • cmux.xcodeproj/project.pbxproj

Comment thread Sources/Panels/AgentSessionWebHostView.swift
Comment thread Sources/Panels/AgentSessionWebHostView.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Panels/AgentSessionWebHostView.swift`:
- Around line 46-51: Update the scroll-wheel handling around hostedWebView and
delta validation so both early-return paths call super.scrollWheel(with: event)
before returning. Preserve the existing hosted-web-view handling for valid,
finite deltas.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b05666a8-99a2-4264-9ac6-ecc09e723fd6

📥 Commits

Reviewing files that changed from the base of the PR and between 3c23676 and 4046775.

📒 Files selected for processing (1)
  • Sources/Panels/AgentSessionWebHostView.swift

Comment thread Sources/Panels/AgentSessionWebHostView.swift Outdated
@lawrencecchen
lawrencecchen merged commit ebee659 into main Jul 16, 2026
8 checks passed
@lawrencecchen
lawrencecchen deleted the fix-session-content-width-autoreview branch July 16, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant