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/MouseEventvalues. - 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
ncursesdevelopment headers installed (sudo apt install libncursesw5-devon Debian/Ubuntu,brew install ncurseson 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 buildIf 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 DashboardDemoPass --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 --previewThis outputs an ASCII capture suitable for CI logs and documentation.
- Screen & Layout: The
Screenscene 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
TerminalAppconformer. 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 spawnTasks and post updates via async sequences. - Capabilities: Inspect
context.capabilitiesorcontext.paletteto 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 worksBefore submitting patches:
- Confirm
swift testpasses on Linux and macOS when possible. - Exercise the Dashboard demo in an interactive terminal to verify real rendering.
- Update DocC tutorials and
CHANGELOG.mdentries for user-facing behavior. - When cutting a release, duplicate
.github/RELEASE_TEMPLATE.mdinto 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.