Skip to content

Rip out Bonsplit and replace it with a direct AppKit split host #2289

@lawrencecchen

Description

@lawrencecchen

The current split path has too many geometry owners.

  • SwiftUI owns pane anchors and sidebar, tab bar, and titlebar layout.
  • GhosttyTerminalView is only a placeholder anchor.
  • The real terminal view is reparented through TerminalWindowPortal.
  • WindowTerminalPortal often reconciles geometry on a later main-queue turn.

That is why sidebar, titlebar, and tab bar changes can land a frame before the terminal. We can still reproduce this after targeted fixes.

This issue is not the broader paper-window-manager work in #1221. The goal here is narrower: keep today's split semantics, but replace Bonsplit and the portal-heavy split hosting path with a direct AppKit host, using the same basic ownership model that feels good in NiriCanvasView.

References:

  • Sources/NiriCanvasView.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/TerminalWindowPortal.swift

Scope:

  • Rip out bonsplit for workspace pane layout.
  • Reimplement the current split tree behavior in AppKit.
  • Keep sidebar, workspaces, tabs-in-pane, browser panels, markdown panels, session restore, and keyboard shortcuts.

Core architecture:

  • Workspace stays the source of truth for pane membership, selected panel, focused pane, split tree, and persistence.
  • Add an AppKit WorkspaceSplitHostView or equivalent that owns the visible split tree directly.
  • Each pane gets a real NSView container. The active panel view is mounted directly inside that container.
  • For terminals, mount TerminalSurface.hostedView directly in the pane container, like NiriCanvasView does.
  • Do not use GhosttyTerminalView as a placeholder anchor for the steady-state workspace split path.
  • Remove TerminalWindowPortal from the hot path for workspace terminal panes. If some overlay still needs a portal, keep it out of geometry ownership.
  • Preserve Ghostty's native macOS renderer path. Do not introduce MTKView, custom display links, or extra draw loops as part of this refactor.

Performance requirements:

  • One layout owner per pane. Chrome and terminal geometry must update in the same AppKit layout pass.
  • No steady-state geometry reconciliation that waits for an extra DispatchQueue.main.async.
  • No terminal reparenting during normal split open, close, resize, sidebar toggle, or workspace switch.
  • No sleep or timer based synchronization in app or runtime code.
  • Keep the upstream Ghostty IOSurface presentation path intact.
  • Steady-state CPU and GPU cost must be no worse than current normal rendering.

Acceptance:

  • Cmd+B never shows sidebar, titlebar, or tab bar ahead of the terminal.
  • Split divider drag never shows stale gutters or stale-width terminal frames.
  • Opening or closing a split is visually atomic.
  • Workspace switching does not show old terminal frames on the new workspace.
  • Focus, IME, selection, drag and drop, copy mode, and search overlay continue to work.
  • Session persistence round-trips the new split tree.
  • Browser and markdown panels keep working inside the same split engine.

Implementation notes:

  • Start from the direct-hosting pattern in NiriCanvasView, but implement the existing Bonsplit tree semantics instead of the scrollable canvas.
  • Upstream Ghostty on macOS already has a good renderer and presentation path. The bigger problem here is view ownership and geometry timing, not switching Metal APIs.
  • The refactor should reduce, not increase, the number of abstraction layers between split geometry and the terminal view.
  • Land this behind a temporary hidden flag if needed, but the target should be full replacement, not a permanent second split engine.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions