Skip to content

SwiftNCurses Framework API Reference

This is the API reference for SwiftNCurses, Substation's terminal UI framework. For building your first terminal UI, start with the integration guide. This document covers the complete SwiftNCurses API including components, styling, and rendering.

Package Overview

SwiftNCurses provides a declarative, component-based terminal UI framework with:

  • SwiftUI-like syntax with result builders for familiar development experience
  • Cross-platform rendering using NCurses abstraction layer
  • High-performance rendering with render buffering and virtual scrolling
  • Component-based architecture with composable, reusable components
  • Semantic color system based on GitHub Primer design system
  • MainActor isolation for thread-safe terminal operations

Core Types

Geometry Types

/// Type-safe position representation
public struct Position: Equatable, Hashable, Sendable {
    public let row: Int32
    public let col: Int32

    /// Convenience properties for x/y access
    public var x: Int32 { col }
    public var y: Int32 { row }

    public init(row: Int32, col: Int32)
    public init(x: Int32, y: Int32)

    public static let zero = Position(row: 0, col: 0)

    public func offset(by offset: Position) -> Position
}

/// Alias for familiar Point terminology
public typealias Point = Position

/// Type-safe size representation
public struct Size: Equatable, Hashable, Sendable {
    public let width: Int32
    public let height: Int32

    public init(width: Int32, height: Int32)

    public static let zero = Size(width: 0, height: 0)
    public var area: Int32 { width * height }
}

/// Type-safe rectangle representation
public struct Rect: Equatable, Hashable, Sendable {
    public let origin: Position
    public let size: Size

    public init(origin: Position, size: Size)
    public init(x: Int32, y: Int32, width: Int32, height: Int32)

    public static let zero = Rect(origin: .zero, size: .zero)

    public var minX: Int32
    public var minY: Int32
    public var maxX: Int32
    public var maxY: Int32

    public func contains(_ position: Position) -> Bool
    public func inset(by insets: EdgeInsets) -> Rect
}

/// Edge insets for padding and margins
public struct EdgeInsets: Equatable, Hashable, Sendable {
    public let top: Int32
    public let leading: Int32
    public let bottom: Int32
    public let trailing: Int32

    public init(top: Int32, leading: Int32, bottom: Int32, trailing: Int32)
    public init(all: Int32)

    public static let zero = EdgeInsets(all: 0)
}

Color System

SwiftNCurses uses a semantic color system inspired by GitHub's Primer Design System for professional, accessible terminal UIs.

/// Semantic color types for consistent theming
public enum Color: CaseIterable, Sendable {
    case primary      // Main text/content (Blue)
    case secondary    // Supporting text (Cyan)
    case accent       // Highlights/selections (White on Blue)
    case success      // Positive states (Green)
    case warning      // Caution states (Yellow)
    case error        // Error states (Red)
    case info         // Information/neutral (White)
    case background   // Background color
    case border       // Borders and separators (Dark Gray)
    case muted        // Subdued text (Magenta)
    case emphasis     // Strong emphasis (Black on White)

    /// Maps semantic colors to ncurses color pair indices
    public var colorPairIndex: Int32
}

/// Text attributes for styling
public struct TextAttributes: OptionSet, Hashable, Sendable {
    public static let normal    = TextAttributes([])
    public static let bold      = TextAttributes(rawValue: 1 << 0)
    public static let dim       = TextAttributes(rawValue: 1 << 1)
    public static let reverse   = TextAttributes(rawValue: 1 << 2)
    public static let underline = TextAttributes(rawValue: 1 << 3)
    public static let blink     = TextAttributes(rawValue: 1 << 4)
}

/// Complete text styling configuration
public struct TextStyle: Hashable, Sendable {
    public let color: Color
    public let attributes: TextAttributes

    public init(color: Color, attributes: TextAttributes = .normal)

    // Predefined styles
    public static let primary: TextStyle
    public static let secondary: TextStyle
    public static let accent: TextStyle
    public static let success: TextStyle
    public static let warning: TextStyle
    public static let error: TextStyle
    public static let info: TextStyle
    public static let border: TextStyle
    public static let muted: TextStyle
    public static let emphasis: TextStyle

    // Enhanced styles with attributes
    public static let primaryBold: TextStyle
    public static let accentBold: TextStyle
    public static let errorBold: TextStyle
    public static let emphasisBold: TextStyle
    public static let mutedDim: TextStyle

    // Style modifiers
    public func bold() -> TextStyle
    public func dim() -> TextStyle
    public func reverse() -> TextStyle

    /// Automatically choose style based on status string
    public static func forStatus(_ status: String?) -> TextStyle
}

Example:

// Using predefined styles
let title = Text("Welcome").styled(.primaryBold)
let error = Text("Error!").styled(.error)
let muted = Text("Optional").styled(.mutedDim)

// Using style modifiers
let highlighted = Text("Important").primary().bold()

// Automatic status styling
let status = TextStyle.forStatus("active")  // Returns .success
let failed = TextStyle.forStatus("error")   // Returns .error

SwiftNCurses Main Interface

The SwiftNCurses struct provides the primary interface for terminal operations.

Terminal Initialization

public struct SwiftNCurses {
    /// Initialize SwiftNCurses with ncurses
    @MainActor public static func initialize(colorScheme: ColorScheme? = nil)

    /// Initialize terminal colors and cursor settings
    @MainActor public static func initializeTerminal(colorScheme: ColorScheme? = nil) -> Bool

    /// Initialize terminal screen (replaces initscr())
    @MainActor public static func initializeScreen() -> WindowHandle?

    /// Complete terminal initialization sequence
    /// Returns (screen, rows, cols, success)
    @MainActor public static func initializeTerminalSession() -> (
        screen: WindowHandle?,
        rows: Int32,
        cols: Int32,
        success: Bool
    )

    /// Cleanup terminal (replaces endwin())
    @MainActor public static func cleanupTerminal()
}

Example:

// Standard initialization pattern
let (screen, rows, cols, success) = SwiftNCurses.initializeTerminalSession()
guard success, let screen = screen else {
    print("Failed to initialize terminal")
    return
}
defer { SwiftNCurses.cleanupTerminal() }

print("Terminal initialized: \(cols)x\(rows)")

Surface Management

extension SwiftNCurses {
    /// Create a drawing surface from an ncurses window
    @MainActor public static func surface(from window: OpaquePointer?) -> CursesSurface

    /// Create a drawing surface from a WindowHandle
    @MainActor public static func surface(from window: WindowHandle) -> CursesSurface

    /// Render a component to a surface
    @MainActor public static func render(
        _ component: any Component,
        on surface: any Surface,
        in rect: Rect? = nil
    ) async
}

Example:

let surface = SwiftNCurses.surface(from: screen)

// Render a component
await SwiftNCurses.render(
    Text("Hello, World!").primary(),
    on: surface,
    in: Rect(x: 0, y: 0, width: 20, height: 1)
)

Screen Operations

extension SwiftNCurses {
    /// Clear entire screen
    @MainActor public static func clearScreen(_ window: WindowHandle)

    /// Refresh screen (standard refresh)
    @MainActor public static func refreshScreen(_ window: WindowHandle)

    /// Batched screen update - preferred over refresh()
    /// Updates virtual screen then flushes to terminal in one syscall
    @MainActor public static func batchedRefresh(_ window: WindowHandle)

    /// Update virtual screen without flushing (for batching)
    @MainActor public static func wnoutrefresh(_ window: WindowHandle)

    /// Flush all pending screen updates to terminal
    @MainActor public static func doupdate()

    /// Clear to end of line from current position
    @MainActor public static func clearToEndOfLine(_ window: WindowHandle)

    /// Move cursor to position
    @MainActor public static func moveCursor(_ window: WindowHandle, to position: Point)

    /// Get maximum Y coordinate
    @MainActor public static func getMaxY(_ window: WindowHandle) -> Int32

    /// Get maximum X coordinate
    @MainActor public static func getMaxX(_ window: WindowHandle) -> Int32
}

Example:

// Clear and redraw
SwiftNCurses.clearScreen(screen)

// Render multiple components
await SwiftNCurses.render(header, on: surface, in: headerBounds)
await SwiftNCurses.render(content, on: surface, in: contentBounds)
await SwiftNCurses.render(footer, on: surface, in: footerBounds)

// Batched refresh (preferred - reduces syscalls)
SwiftNCurses.batchedRefresh(screen)

Input Handling

extension SwiftNCurses {
    /// Get character input
    @MainActor public static func getInput(_ window: WindowHandle) -> Int32

    /// Set input delay mode
    @MainActor public static func setInputDelay(_ window: WindowHandle, enabled: Bool)

    /// Wait for any input
    @MainActor public static func waitForInput(_ window: WindowHandle)
}

Styled Text Drawing

extension SwiftNCurses {
    /// Draw styled text at position using color pair
    @MainActor public static func drawStyledText(
        _ window: WindowHandle,
        at position: Position,
        text: String,
        colorPair: Int32
    )

    /// Draw styled text using semantic color
    @MainActor public static func drawStyledText(
        _ window: WindowHandle,
        at position: Position,
        text: String,
        color: Color
    )

    /// Draw styled text using TextStyle
    @MainActor public static func drawStyledText(
        _ window: WindowHandle,
        at position: Position,
        text: String,
        style: TextStyle
    )

    // Semantic color helpers
    @MainActor public static func primaryColor() -> Int32
    @MainActor public static func secondaryColor() -> Int32
    @MainActor public static func accentColor() -> Int32
    @MainActor public static func warningColor() -> Int32
    @MainActor public static func successColor() -> Int32
    @MainActor public static func infoColor() -> Int32
    @MainActor public static func errorColor() -> Int32
    @MainActor public static func borderColor() -> Int32
    @MainActor public static func mutedColor() -> Int32
    @MainActor public static func emphasisColor() -> Int32
}

Timer Management

extension SwiftNCurses {
    /// Create a repeating timer that works in async contexts
    @MainActor public static func createRepeatingTimer(
        interval: TimeInterval,
        tolerance: TimeInterval = 0.1,
        action: @escaping () -> Void
    ) -> Task<Void, Never>

    /// Create a one-shot timer that works in async contexts
    @MainActor public static func createOneShotTimer(
        delay: TimeInterval,
        action: @escaping () -> Void
    ) -> Task<Void, Never>
}

Example:

// Repeating timer for auto-refresh
let refreshTimer = SwiftNCurses.createRepeatingTimer(interval: 60.0) {
    Task { await refreshData() }
}

// Cancel timer when done
refreshTimer.cancel()

Component System

Component Protocol

All UI elements conform to the Component protocol:

/// Base protocol for all UI components
public protocol Component: Sendable {
    /// Render the component on a surface within the given bounds
    @MainActor func render(in context: DrawingContext) async

    /// Calculate the preferred size for this component
    var intrinsicSize: Size { get }
}

extension Component {
    /// Default intrinsic size for components that don't specify one
    public var intrinsicSize: Size {
        return Size(width: 0, height: 1)
    }

    /// Render with automatic context creation
    @MainActor public func render(on surface: any Surface, in rect: Rect) async

    /// Add padding around this component
    public func padding(_ edges: EdgeInsets) -> Padded
    public func padding(_ amount: Int32) -> Padded
}

DrawingContext

/// Provides a drawing context for components with surface and bounds information
public struct DrawingContext: Sendable {
    public let surface: any Surface
    public let bounds: Rect

    public init(surface: any Surface, bounds: Rect)

    /// Create a sub-context with adjusted bounds
    public func subContext(rect: Rect) -> DrawingContext

    /// Check if a position is within the context bounds
    public func contains(_ position: Position) -> Bool

    /// Get the absolute position for a relative position within this context
    public func absolutePosition(for relativePosition: Position) -> Position

    /// Draw text at a position with style
    @MainActor public func draw(at position: Position, text: String, style: TextStyle? = nil) async

    /// Draw a border around the context bounds with optional title
    @MainActor public func drawBorder(style: TextStyle = .border, title: String? = nil) async
}

Surface Protocol

/// Abstract drawing surface for rendering components
public protocol Surface: Sendable {
    /// Draw text at a specific position with optional styling
    @MainActor func draw(at position: Position, text: String, style: TextStyle?) async

    /// Move cursor to position without drawing
    @MainActor func move(to position: Position)

    /// Clear a rectangular area
    @MainActor func clear(rect: Rect)

    /// Clear to end of line from current position
    @MainActor func clearToEndOfLine()

    /// Draw a single character at position
    @MainActor func draw(at position: Position, character: Character, style: TextStyle?) async

    /// Get the size of the surface
    @MainActor var size: Size { get }

    /// Create a drawing context for the given bounds
    @MainActor func context(for bounds: Rect) -> DrawingContext

    /// Get string input from user at position
    @MainActor func getStringInput(at position: Position, prompt: String, maxLength: Int) -> String?

    /// Get character input from user
    @MainActor func getCharacterInput() -> Character?
}

extension Surface {
    /// Draw text with default primary style
    @MainActor public func draw(at position: Position, text: String) async

    /// Fill a rectangle with a character
    @MainActor public func fill(rect: Rect, character: Character = " ", style: TextStyle? = nil) async

    /// Draw a horizontal line
    @MainActor public func drawHorizontalLine(
        at row: Int32,
        from startCol: Int32,
        to endCol: Int32,
        character: Character = "-",
        style: TextStyle? = nil
    ) async

    /// Draw a vertical line
    @MainActor public func drawVerticalLine(
        at col: Int32,
        from startRow: Int32,
        to endRow: Int32,
        character: Character = "|",
        style: TextStyle? = nil
    ) async
}

CursesSurface

/// Concrete implementation of Surface using ncurses
@MainActor public class CursesSurface: Surface {
    public init(window: OpaquePointer?, colorScheme: ColorScheme? = nil, enableBuffering: Bool = true)

    public var size: Size

    /// Flush the render buffer to screen
    public func flushBuffer()

    /// Enable or disable render buffering
    public func setBufferingEnabled(_ enabled: Bool)

    /// Mark all buffer cells as dirty for full redraw
    public func markBufferDirty()
}

UI Components

Text Components

/// Basic text rendering component
public struct Text: Component {
    public init(_ content: String, style: TextStyle = .primary)

    // Style modifiers (chainable)
    public func primary() -> Text
    public func secondary() -> Text
    public func accent() -> Text
    public func success() -> Text
    public func warning() -> Text
    public func error() -> Text
    public func info() -> Text
    public func muted() -> Text
    public func emphasis() -> Text
    public func bold() -> Text
    public func dim() -> Text
    public func reverse() -> Text
    public func styled(_ newStyle: TextStyle) -> Text

    // Quick constructors
    public static func primary(_ content: String) -> Text
    public static func accent(_ content: String) -> Text
}

/// Text component that automatically styles based on status
public struct StatusText: Component {
    public init(_ status: String, customStyle: TextStyle? = nil)
}

/// Text component with automatic formatting and truncation
public struct FormattedText: Component {
    public enum TextAlignment: Sendable {
        case leading
        case center
        case trailing
    }

    public init(
        _ content: String,
        style: TextStyle = .primary,
        maxWidth: Int32? = nil,
        alignment: TextAlignment = .leading
    )
}

/// Text with automatic padding for table-like layouts
public struct PaddedText: Component {
    public init(
        _ content: String,
        width: Int32,
        style: TextStyle = .primary,
        alignment: FormattedText.TextAlignment = .leading
    )
}

/// Text component that handles line breaks and wrapping
public struct MultilineText: Component {
    public init(_ content: String, style: TextStyle = .primary, maxWidth: Int32)
}

Example:

// Simple text
let text = Text("Hello, World!")

// Styled text with chaining
let styled = Text("Error!")
    .error()
    .bold()

// Status-aware text
let status = StatusText("ACTIVE")  // Automatically uses .success style

// Formatted text with alignment
let centered = FormattedText("Title", maxWidth: 40, alignment: .center)

// Multiline text with wrapping
let description = MultilineText(longText, maxWidth: 60)

Layout Components

/// Vertical stack container
public struct VStack: Component {
    public init(spacing: Int32 = 0, @ComponentBuilder content: () -> [any Component])
    public init(spacing: Int32 = 0, children: [any Component])
}

/// Horizontal stack container
public struct HStack: Component {
    public init(spacing: Int32 = 1, @ComponentBuilder content: () -> [any Component])
    public init(spacing: Int32 = 1, children: [any Component])
}

/// Component that applies padding to child components
public struct Padded: Component {
    public init(_ child: any Component, padding: EdgeInsets)
    public init(_ child: any Component, padding: Int32)
}

/// Component that renders nothing (useful for conditional rendering)
public struct EmptyComponent: Component {
    public init()
}

Example:

// Vertical stack with spacing
let vstack = VStack(spacing: 1) {
    Text("Header").primaryBold()
    Text("Content").primary()
    Text("Footer").muted()
}

// Horizontal stack
let hstack = HStack(spacing: 2) {
    StatusIcon(status: "active")
    Text("Server Name")
    Text("10.0.0.1").muted()
}

// Padded content
let padded = Text("Padded Content").padding(2)

ComponentBuilder

SwiftNCurses provides a result builder for declarative component composition:

@resultBuilder
public struct ComponentBuilder {
    public static func buildBlock(_ components: any Component...) -> [any Component]
    public static func buildBlock(_ component: any Component) -> any Component
    public static func buildOptional(_ component: (any Component)?) -> any Component
    public static func buildEither(first component: any Component) -> any Component
    public static func buildEither(second component: any Component) -> any Component
    public static func buildArray(_ components: [any Component]) -> [any Component]
}

Example:

// Conditional rendering
let content = VStack {
    Text("Title")
    if showDetails {
        Text("Details")
    }
    if isError {
        Text("Error").error()
    } else {
        Text("OK").success()
    }
}

List Components

/// High-level list component with headers, scrolling, and selection
public struct ListView<Item: Sendable>: Component {
    public struct ListConfiguration: Sendable {
        public init(
            showHeaders: Bool = true,
            showBorder: Bool = true,
            title: String? = nil,
            headerStyle: TextStyle = .accent,
            separatorStyle: TextStyle = .secondary,
            selectionStyle: TextStyle = .primary,
            maxVisibleItems: Int? = nil
        )
    }

    public init(
        items: [Item],
        selectedIndex: Int? = nil,
        scrollOffset: Int = 0,
        configuration: ListConfiguration = .standard(),
        @ComponentBuilder rowRenderer: @escaping @Sendable (Item, Bool, Int) -> any Component
    )
}

/// Component that handles row selection styling
public struct SelectableRow: Component {
    public init(_ child: any Component, isSelected: Bool, style: TextStyle = .primary)
}

/// Standard list item with icon, text, and optional status
public struct ListItem: Component {
    public init(
        icon: (any Component)? = nil,
        text: String,
        status: (any Component)? = nil,
        style: TextStyle = .primary
    )
}

/// List item with fixed-width columns for table-like display
public struct TableListItem: Component {
    public struct TableColumn: Sendable {
        public init(content: any Component, width: Int32, alignment: FormattedText.TextAlignment = .leading)
        public init(text: String, width: Int32, style: TextStyle = .primary, alignment: FormattedText.TextAlignment = .leading)
    }

    public init(columns: [TableColumn])
}

Example:

struct Server {
    let name: String
    let status: String
    let ip: String
}

let servers: [Server] = [...]

let list = ListView(
    items: servers,
    selectedIndex: selectedIndex,
    scrollOffset: scrollOffset,
    configuration: ListView.ListConfiguration(
        title: "Servers",
        maxVisibleItems: 20
    )
) { server, isSelected, index in
    TableListItem(columns: [
        .init(text: server.name, width: 20),
        .init(content: StatusText(server.status), width: 10),
        .init(text: server.ip, width: 15, style: .muted)
    ])
}

Virtual Scrolling

For large datasets, use VirtualScrollView for optimal performance:

/// High-performance virtual scrolling component for large datasets
public struct VirtualScrollView<Item: Sendable, ItemView: Component>: Component {
    public init(
        items: [Item],
        itemHeight: Int32 = 1,
        scrollOffset: Int = 0,
        selectedIndex: Int? = nil,
        renderItem: @escaping @Sendable (Item, Bool) -> ItemView
    )
}

/// Controller for managing virtual scrolling state and behavior
@MainActor
public final class VirtualListController {
    public var scrollOffset: Int
    public var selectedIndex: Int

    // Navigation methods
    public func moveUp()
    public func moveDown()
    public func pageUp(visibleItems: Int)
    public func pageDown(visibleItems: Int, totalItems: Int)
    public func moveToTop()
    public func moveToBottom(totalItems: Int)

    // Scroll management
    public func ensureVisible(index: Int, visibleItems: Int)
    public func adjustScroll(forTotalItems totalItems: Int, visibleItems: Int)
}

Example:

// For lists with 1000+ items
let virtualList = VirtualScrollView(
    items: largeDataset,
    itemHeight: 1,
    scrollOffset: controller.scrollOffset,
    selectedIndex: controller.selectedIndex
) { item, isSelected in
    TableListItem(columns: [
        .init(text: item.name, width: 30),
        .init(text: item.value, width: 20)
    ])
}

await SwiftNCurses.render(virtualList, on: surface, in: contentBounds)

Status and Border Components

/// Status indicator component
public struct StatusIcon: Component {
    public init(
        status: String?,
        activeStates: [String] = ["active", "available"],
        errorStates: [String] = ["error", "fault"]
    )
}

/// Border component
public struct Border: Component {
    public init(
        title: String? = nil,
        style: TextStyle = .border,
        @ComponentBuilder content: () -> any Component
    )
}

/// Separator line component
public struct Separator: Component {
    public enum Direction: Sendable {
        case horizontal
        case vertical
    }

    public init(direction: Direction, style: TextStyle = .border)
}

Input Handling

KeyEvent Enum

/// Enhanced key events
public enum KeyEvent {
    case character(Character)
    case arrowUp, arrowDown, arrowLeft, arrowRight
    case pageUp, pageDown
    case home, end
    case enter, escape, space, backspace
    case functionKey(Int)
    case unknown(Int32)

    /// Check if this is a movement key
    public var isMovement: Bool

    /// Check if this is a navigation key
    public var isNavigation: Bool
}

EnhancedInputManager

/// Enhanced input manager for improved user experience
@MainActor
public class EnhancedInputManager {
    public init()

    /// Get the next key with enhanced processing
    public func getNextKey() -> KeyEvent?
}

Input Loop Pattern

let inputManager = EnhancedInputManager()
var running = true

while running {
    let key = SwiftNCurses.getInput(screen)

    switch key {
    case Int32(KEY_UP):
        controller.moveUp()

    case Int32(KEY_DOWN):
        controller.moveDown()

    case Int32(KEY_PPAGE):  // Page Up
        controller.pageUp(visibleItems: visibleCount)

    case Int32(KEY_NPAGE):  // Page Down
        controller.pageDown(visibleItems: visibleCount, totalItems: items.count)

    case 10, 13:  // Enter
        handleSelection()

    case 27:  // Escape
        handleBack()

    case Int32(Character("q").asciiValue!):
        running = false

    default:
        if key >= 32 && key <= 126 {
            handleCharacter(Character(UnicodeScalar(Int(key))!))
        }
    }

    // Re-render
    await renderScreen()
    SwiftNCurses.batchedRefresh(screen)
}

Animation Support

/// Simple animation support for enhanced UX
@MainActor
public class AnimationManager {
    public init()

    /// Start a simple fade animation
    public func startFadeIn(id: String, duration: TimeInterval = 0.3)

    /// Start a slide animation
    public func startSlideIn(id: String, direction: Animation.Direction, duration: TimeInterval = 0.2)

    /// Get the current progress of an animation (0.0 to 1.0)
    public func getProgress(for id: String) -> Double

    /// Check if an animation is active
    public func isAnimating(_ id: String) -> Bool
}

public struct Animation: Sendable {
    public enum AnimationType: Sendable {
        case fadeIn
        case slide(Direction)
    }

    public enum Direction: Sendable {
        case left, right, up, down
    }
}

Performance Optimization

Render Buffering

SwiftNCurses uses a render buffer to minimize terminal I/O:

// Buffering is enabled by default
let surface = CursesSurface(window: screen.pointer, enableBuffering: true)

// Render multiple components (buffered)
await component1.render(on: surface, in: bounds1)
await component2.render(on: surface, in: bounds2)
await component3.render(on: surface, in: bounds3)

// Flush buffer to terminal (single I/O operation)
surface.flushBuffer()

Batched Screen Updates

// PREFERRED: Batched refresh reduces syscalls
SwiftNCurses.batchedRefresh(screen)

// Alternative: Manual batching for complex updates
SwiftNCurses.wnoutrefresh(screen)  // Update virtual screen
// ... more wnoutrefresh calls ...
SwiftNCurses.doupdate()  // Single flush to terminal

Virtual Scrolling Best Practices

// For large datasets (100+ items), always use VirtualScrollView
if items.count > 100 {
    // Only renders visible items
    let virtualList = VirtualScrollView(
        items: items,
        scrollOffset: offset,
        selectedIndex: selected
    ) { item, isSelected in
        renderRow(item, isSelected)
    }
}

Memory Management

// Clear screen when switching views
SwiftNCurses.clearScreen(screen)

// Always cleanup on exit
defer { SwiftNCurses.cleanupTerminal() }

// Mark buffer dirty for full redraw after resize
surface.markBufferDirty()

Best Practices

1. Initialize Once, Cleanup Always

let (screen, rows, cols, success) = SwiftNCurses.initializeTerminalSession()
guard success, let screen = screen else { return }
defer { SwiftNCurses.cleanupTerminal() }

2. Use Semantic Colors

// GOOD: Semantic colors
Text("Error").styled(.error)
Text("Success").styled(.success)

// AVOID: Direct color codes
// wattron(window, COLOR_PAIR(6))  // Magic numbers

3. Batch Screen Updates

// GOOD: Single refresh after all rendering
await component1.render(on: surface, in: bounds1)
await component2.render(on: surface, in: bounds2)
SwiftNCurses.batchedRefresh(screen)

// AVOID: Refresh after each component

4. Use Components, Not Direct Drawing

// GOOD: Component-based
let header = VStack {
    Text("Title").primaryBold()
    Separator(direction: .horizontal)
}
await SwiftNCurses.render(header, on: surface, in: headerBounds)

// AVOID: Direct ncurses calls
// wmove(window, 0, 0)
// waddstr(window, "Title")

5. Handle Terminal Resize

let newRows = SwiftNCurses.getMaxY(screen)
let newCols = SwiftNCurses.getMaxX(screen)

if newRows != currentRows || newCols != currentCols {
    currentRows = newRows
    currentCols = newCols
    surface.markBufferDirty()
    recalculateLayout()
}

Migration from Direct NCurses

Before (Direct NCurses)

initscr();
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
wattron(stdscr, COLOR_PAIR(1));
mvwprintw(stdscr, 0, 0, "Error!");
wattroff(stdscr, COLOR_PAIR(1));
refresh();
endwin();

After (SwiftNCurses)

let (screen, _, _, success) = SwiftNCurses.initializeTerminalSession()
guard success, let screen = screen else { return }
defer { SwiftNCurses.cleanupTerminal() }

let surface = SwiftNCurses.surface(from: screen)
await SwiftNCurses.render(
    Text("Error!").error(),
    on: surface,
    in: Rect(x: 0, y: 0, width: 10, height: 1)
)
SwiftNCurses.batchedRefresh(screen)

See Also: