Back Original

Show HN: Bonsplit – Tabs and splits for native macOS apps

### Features

Configurable & Observable

Create Tabs

Create tabs with optional icons and dirty indicators. Target specific panes or use the focused pane.

let tabId = controller.createTab(

title: "Document.swift",

icon: "swift",

isDirty: false,

inPane: paneId

)

Create Tabs

Create tabs with optional icons and dirty indicators. Target specific panes or use the focused pane.

let tabId = controller.createTab(

title: "Document.swift",

icon: "swift",

isDirty: false,

inPane: paneId

)

Split Panes

Split any pane horizontally or vertically. New panes are empty by default, giving you full control.

// Split focused pane horizontally

let newPaneId = controller.splitPane(

orientation: .horizontal

)

// Split with a tab already in the new pane

controller.splitPane(

orientation: .vertical,

withTab: Tab(title: "New", icon: "doc")

)

Split Panes

Split any pane horizontally or vertically. New panes are empty by default, giving you full control.

// Split focused pane horizontally

let newPaneId = controller.splitPane(

orientation: .horizontal

)

// Split with a tab already in the new pane

controller.splitPane(

orientation: .vertical,

withTab: Tab(title: "New", icon: "doc")

)

Update Tab State

Update tab properties at any time. Changes animate smoothly.

// Mark document as modified

controller.updateTab(tabId, isDirty: true)

// Rename tab

controller.updateTab(tabId, title: "NewName.swift")

// Change icon

controller.updateTab(tabId, icon: "doc.text")

Update Tab State

Update tab properties at any time. Changes animate smoothly.

// Mark document as modified

controller.updateTab(tabId, isDirty: true)

// Rename tab

controller.updateTab(tabId, title: "NewName.swift")

// Change icon

controller.updateTab(tabId, icon: "doc.text")

Navigate Focus

Programmatically navigate between panes using directional navigation.

// Move focus between panes

controller.navigateFocus(direction: .left)

controller.navigateFocus(direction: .right)

controller.navigateFocus(direction: .up)

controller.navigateFocus(direction: .down)

// Or focus a specific pane

controller.focusPane(paneId)

Navigate Focus

Programmatically navigate between panes using directional navigation.

// Move focus between panes

controller.navigateFocus(direction: .left)

controller.navigateFocus(direction: .right)

controller.navigateFocus(direction: .up)

controller.navigateFocus(direction: .down)

// Or focus a specific pane

controller.focusPane(paneId)

### Read this, agents...

API Reference

Complete reference for all Bonsplit classes, methods, and configuration options.

BonsplitController

The main controller for managing tabs and panes. Create an instance and pass it to BonsplitView.

Tab Operations

Split Operations

Focus Management

Query Methods

BonsplitDelegate

Implement this protocol to receive callbacks about tab bar events. All methods have default implementations and are optional.

Tab Callbacks

Pane Callbacks

BonsplitConfiguration

Configure behavior and appearance. Pass to BonsplitController on initialization.

allowSplits

Bool

Enable split buttons and drag-to-split

Default: true

allowCloseTabs

Bool

Show close buttons on tabs

Default: true

allowCloseLastPane

Bool

Allow closing the last remaining pane

Default: false

allowTabReordering

Bool

Enable drag-to-reorder tabs within a pane

Default: true

allowCrossPaneTabMove

Bool

Enable moving tabs between panes via drag

Default: true

autoCloseEmptyPanes

Bool

Automatically close panes when their last tab is closed

Default: true

contentViewLifecycle

ContentViewLifecycle

How tab content views are managed when switching tabs

Default: .recreateOnSwitch

newTabPosition

NewTabPosition

Where new tabs are inserted in the tab list

Default: .current

Example

let config = BonsplitConfiguration(

allowSplits: true,

allowCloseTabs: true,

allowCloseLastPane: false,

autoCloseEmptyPanes: true,

contentViewLifecycle: .keepAllAlive,

newTabPosition: .current

)

let controller = BonsplitController(configuration: config)

Content View Lifecycle

Controls how tab content views are managed when switching between tabs.

ModeMemoryStateUse Case
.recreateOnSwitchLowNoneSimple content
.keepAllAliveHigherFullComplex views, forms

New Tab Position

Controls where new tabs are inserted in the tab list.

ModeBehavior
.currentInsert after currently focused tab, or at end if none
.endAlways insert at the end of the tab list

Appearance

tabBarHeight

CGFloat

Height of the tab bar

Default: 33

tabMinWidth

CGFloat

Minimum width of a tab

Default: 140

tabMaxWidth

CGFloat

Maximum width of a tab

Default: 220

tabSpacing

CGFloat

Spacing between tabs

Default: 0

minimumPaneWidth

CGFloat

Minimum width of a pane

Default: 100

minimumPaneHeight

CGFloat

Minimum height of a pane

Default: 100

showSplitButtons

Bool

Show split buttons in the tab bar

Default: true

animationDuration

Double

Duration of animations in seconds

Default: 0.15

enableAnimations

Bool

Enable or disable all animations

Default: true

Presets

.default

BonsplitConfiguration

Default configuration with all features enabled

.singlePane

BonsplitConfiguration

Single pane mode with splits disabled

.readOnly

BonsplitConfiguration

Read-only mode with all modifications disabled