SwiftCursesKit is a Swift-native wrapper around ncurses that lets you build rich, windowed dashboards directly in the terminal. It combines ncurses’ battle-tested capabilities (cursor control, colors, mouse input, panels) with ergonomic Swift APIs, async event loops, and declarative layout primitives.
- Features
- Supported Platforms & Capabilities
- Installing SwiftCursesKit
- Bootstrapping an App
- Resource & Input Model
- Development & Testing Workflow
- Troubleshooting
- Limitations & Roadmap
- Project Layout
- Safe resource management – Scene and widget wrappers manage ncurses windows and color pairs, automatically initializing and tearing down resources.
- Declarative layout – Compose split views, stacks, gauges, tables, and log panes with familiar Swift builders.
- Async input handling – Translate keystrokes, mouse events, and timers into structured
KeyEvent
/MouseEvent
values. - Terminal-aware rendering – Capability detection negotiates wide characters, color pairs, mouse reporting, and resizing at runtime.
- Examples included – Sample dashboards demonstrate live metrics, log streaming, and modal dialogs.
SwiftCursesKit targets Swift 6.1+ with wide-character ncurses (ncursesw
). Tested hosts include:
Platform | Swift Version | ncurses Package | Notes |
---|---|---|---|
Ubuntu 22.04 | 6.1 | libncursesw5-dev |
Requires UTF-8 locale, e.g. LANG=en_US.UTF-8 . |
Debian 12 | 6.1 | libncursesw6-dev |
Ensure /usr/lib contains wide-character libraries. |
macOS 13+ | 6.1 (Xcode 15 toolchain) | brew install ncurses |
Export PKG_CONFIG_PATH=/opt/homebrew/opt/ncurses/lib/pkgconfig . |
Terminal emulators: 256-color support is auto-detected; fallback rendering reduces gradients when limited colors are available.
- Swift 6.1 or newer
ncurses
development headers installed (sudo apt install libncursesw5-dev
on Debian/Ubuntu,brew install ncurses
on macOS)
// swift-tools-version: 6.1
import PackageDescription
let package = Package(
name: "MyTerminalApp",
dependencies: [
.package(url: "https://github.com/your-org/SwiftCursesKit.git", from: "0.1.0")
],
targets: [
.executableTarget(
name: "MyTerminalApp",
dependencies: [
.product(name: "SwiftCursesKit", package: "SwiftCursesKit")
]
)
]
)
Fetch dependencies and ensure the ncurses headers are discoverable:
swift package update
swift build
If the build cannot locate ncurses, set PKG_CONFIG_PATH
(macOS/Homebrew) or install the *-dev
package for your distribution.
Use TerminalApp
to describe your dashboard and respond to runtime events. The example below mirrors the Examples/DashboardDemo
executable:
import SwiftCursesKit
@main
struct DemoDashboard: TerminalApp {
var cpuUsage: Double = 0.25
var memoryUsage: Double = 0.24
var logBuffer: [String] = []
var body: some Scene {
Screen {
VStack(spacing: 1) {
Title("SwiftCursesKit Demo")
HStack {
Gauge(title: "CPU", value: cpuUsage)
Gauge(title: "Memory", value: memoryUsage)
}
LogView(lines: logBuffer)
StatusBar(items: [
.label("q: quit"),
.label("f: toggle fullscreen")
])
}
.padding(1)
}
}
mutating func onEvent(_ event: Event, context: AppContext) async {
switch event {
case .key(.character("q")):
await context.quit()
case .tick:
cpuUsage = min(1.0, cpuUsage + 0.05)
memoryUsage = max(0.0, memoryUsage - 0.02)
logBuffer.append("Tick \(logBuffer.count + 1) processed")
default:
break
}
}
}
Run the demo from the repository root:
swift run DashboardDemo
Pass --help
to inspect runtime options (tick frequency, preview mode, headless snapshot output, etc.).
To render a static snapshot without attaching to the terminal, run:
swift run DashboardDemo --ticks 8 --log-lines 5 --preview
This outputs an ASCII capture suitable for CI logs and documentation.
- Screen & Layout: The
Screen
scene owns the root ncurses window. ComposeVStack
,HStack
,Split
, and widget scenes (Gauge
,LogView
,StatusBar
) to create panes. - State Management: Store application state as mutable properties on your
TerminalApp
conformer. Mutations triggered insideTerminalApp/onEvent(_:context:)
will be reflected during the next render pass. - Event Handling: Implement
onEvent(_:context:)
to react toEvent.tick
, key presses, mouse gestures, and resize notifications. Long-running work can spawnTask
s and post updates via async sequences. - Capabilities: Inspect
context.capabilities
orcontext.palette
to determine if colors, mouse reporting, or resize events are active. Provide fallbacks for monochrome terminals and disable unsupported gestures gracefully.
SwiftCursesKit’s CI gates mirror the recommended local workflow:
swift build # Validate module interfaces and linking
swift test # Execute unit/integration coverage
swift-format --configuration .swift-format.json --in-place .
swift run DashboardDemo --preview # Render a headless snapshot to ensure ncurses IO works
Before submitting patches:
- Confirm
swift test
passes on Linux and macOS when possible. - Exercise the Dashboard demo in an interactive terminal to verify real rendering.
- Update DocC tutorials and
CHANGELOG.md
entries for user-facing behavior. - When cutting a release, duplicate
.github/RELEASE_TEMPLATE.md
into the GitHub release form and summarize highlights consistently.
Symptom | Suggested Fix |
---|---|
pkg-config cannot find ncurses |
Ensure pkg-config --libs ncursesw succeeds. On macOS set PKG_CONFIG_PATH=/opt/homebrew/opt/ncurses/lib/pkgconfig . |
Garbled characters or missing borders | Confirm the terminal is running in UTF-8 and that wide-character ncurses (ncursesw ) is installed. |
Mouse input ignored | Some terminals (e.g., tmux panes without set -g mouse on ) suppress mouse events. Toggle mouse support or handle .mouseUnavailable capability. |
Snapshot rendering differs from live view | Preview mode limits color depth to avoid escape codes. Launch the example app interactively to confirm gradients and animations. |
If issues persist, file a GitHub issue with your platform, Swift version, terminal emulator, and reproduction steps.
- Inline text editing widgets are experimental and may change.
- Accessibility hints (screen readers) are not yet exposed.
- Windows assume a monospaced font; ambiguous-width glyphs may cause misalignment.
- Additional tutorials for multi-window composition and async streams are in progress.
.
├── Package.swift
├── Sources
│ ├── SwiftCursesKit # Public Swift API
│ └── CNCursesSupport # C shims and unsafe interop
├── Examples
│ └── DashboardDemo # Reference terminal dashboard
└── Tests
└── SwiftCursesKitTests # Unit and integration tests
Refer to AGENTS.md
for detailed contribution standards and safety requirements.
The repository includes a shared .swift-format.json
profile. Apply it before committing to
keep whitespace and line wrapping consistent across all targets:
swift-format --configuration .swift-format.json --in-place .
Before submitting a pull request:
- Verify that examples render correctly in a real terminal session.
- Update DocC tutorials or inline documentation for new features.
- Add regression tests for bug fixes and new widgets.
We welcome issues, feature ideas, and pull requests! Please read AGENTS.md
for style, testing, and review expectations before contributing. Open a discussion if you plan to propose major architectural changes such as alternate renderers or event loops.
SwiftCursesKit is released under the MIT License. See LICENSE
for details.