Contents

wadetregaskis/tuikit

[!IMPORTANT]

What is this?

TUIkit lets you build TUI apps using the same declarative syntax you already know from SwiftUI. Define your UI with View, compose views with VStack, HStack, and ZStack, style text with modifiers like .bold() and .foregroundColor(.red), and run it all in your terminal.

import TUIkit

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var count = 0
    
    var body: some View {
        VStack(spacing: 1) {
            Text("Hello, TUIkit!")
                .bold()
                .foregroundStyle(.cyan)
            
            Text("Count: \(count)")
            
            Button("Increment") {
                count += 1
            }
        }
        .statusBarItems {
            StatusBarItem(shortcut: "q", label: "quit")
        }
    }
}

Features

Core

  • View protocol: the core building block, mirroring SwiftUI's View
  • @ViewBuilder: result builder for declarative view composition
  • @State: reactive state management with automatic re-rendering
  • @Environment: dependency injection for theme, focus manager, status bar
  • App protocol: app lifecycle with signal handling and run loop

Views & Components

  • Primitive views: Text, EmptyView, Spacer, Divider, Image (ASCII art rendering with five character sets — fine-block (the default, near-square subpixels), coarse-block, shape-vector, ASCII and braille — and multiple color modes, async loading)
  • Layout containers: VStack, HStack, ZStack (with .zIndex for draw order and overlay layers for floating content), LazyVStack, LazyHStack with alignment and spacing
  • Interactive: Button, ButtonRow, Toggle (default, checkbox, switch styles), Menu, Picker (menu and inline styles), TextField, SecureField, Slider, Stepper, RadioButtonGroup with keyboard navigation
  • Data views: List, Table, Section, ForEach, NavigationSplitView, ContentUnavailableView
  • Containers: Alert, Dialog, Panel, Box, Card
  • Feedback: ProgressView (5 bar styles), Spinner (animated)
  • StatusBar: context-sensitive keyboard shortcuts with .compact and .bordered styles

Styling

  • Text styling: bold, italic, underline, strikethrough, dim, blink, inverted
  • Full color support: ANSI colors, 256-color palette, 24-bit RGB, hex values, HSL
  • Theming: 6 predefined palettes (Green, Amber, Red, Violet, Blue, White)
  • Border styles: line, rounded, doubleLine, heavy, none
  • List styles: PlainListStyle, InsetGroupedListStyle with alternating rows
  • Badges: .badge() modifier for counts and labels on list rows

Notifications

  • Toast-style notifications: transient alerts via .notificationHost() modifier

Internationalization (i18n)

  • 5 languages built-in: English, German, French, Italian, Spanish
  • Type-safe string constants: Compile-time verified LocalizationKey enum
  • Persistent language selection: Automatic storage with XDG paths
  • Fallback chain: Current language → English → key itself
  • Thread-safe operations: Safe language switching at runtime

Advanced

  • Lifecycle modifiers: .onAppear(), .onDisappear(), .task()
  • Key handling: .onKeyPress() with modifier keys (ctrl, alt, shift) and function keys F1–F12
  • Storage: @AppStorage with JSON file backend (XDG paths) and UserDefaults backend
  • Preferences: bottom-up data flow with PreferenceKey
  • Focus system: Tab/Shift+Tab navigation, .focusSection() for grouped areas
  • Render caching: .equatable() for subtree memoization

Run the Example App

swift run TUIkitExample

Press q or ESC to exit.

Installation

Quick Start with CLI

Install the tuikit command and create a new project:

curl -fsSL https://raw.githubusercontent.com/wadetregaskis/TUIkit/main/project-template/install.sh | bash
tuikit init MyApp
cd MyApp && swift run

See project-template/README.md for more options (SQLite, Swift Testing).

Manual Setup

Add TUIkit to your Package.swift:

dependencies: [
    .package(url: "https://github.com/wadetregaskis/TUIkit.git", branch: "main")
]

Then add it to your target:

.target(
    name: "YourApp",
    dependencies: ["TUIkit"]
)

Tip: import TUIkit re-exports all sub-modules. For finer control you can import individual modules: TUIkitCore, TUIkitStyling, TUIkitView, or TUIkitImage.

Theming

TUIkit includes predefined palettes inspired by classic terminals:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .palette(SystemPalette(.green))  // Classic green terminal
    }
}

Available palettes (all via SystemPalette):

  • .green: Classic P1 phosphor CRT (default)
  • .amber: P3 phosphor monochrome
  • .red: IBM 3279 plasma
  • .violet: Retro sci-fi terminal
  • .blue: VFD/LCD displays
  • .white: DEC VT100/VT220 (P4 phosphor)

Internationalization

TUIkit includes comprehensive i18n support with 5 languages and type-safe string constants:

import TUIkit

struct MyView: View {
    var body: some View {
        VStack {
            // Type-safe localized strings
            Text(localized: LocalizationKey.Button.ok)
            LocalizedString(LocalizationKey.Error.notFound)

            // Switch language at runtime
            Button("Deutsch") {
                AppState.shared.setLanguage(.german)
            }
        }
    }
}

Supported languages: English, Deutsch, Français, Italiano, Español

For complete documentation, see Localization Guide in the DocC documentation.

Architecture

  • Modular package: 5 Swift modules + 1 C target (see Project Structure below)
  • No singletons for state: All state flows through the Environment system
  • Pure ANSI rendering: No ncurses or other C dependencies
  • Linux compatible: Works on macOS and Linux (XDG paths supported)
  • Value types: Views are structs, just like SwiftUI

Project Structure

Sources/
├── CSTBImage/            C bindings for stb_image (PNG/JPEG decoding)
├── TUIkitCore/           Primitives, key events, frame buffer, concurrency helpers
├── TUIkitStyling/        Color, theme palettes, border styles
├── TUIkitView/           View protocol, ViewBuilder, State, Environment, Renderable
├── TUIkitImage/          ASCII art converter, image loading (depends on CSTBImage)
├── TUIkit/               Main module: App, Views, Modifiers, Focus, StatusBar, Notification
   ├── App/              App, Scene, WindowGroup
   ├── Environment/      Environment keys, service configuration
   ├── Focus/            Focus system and keyboard navigation
   ├── Localization/     i18n service, type-safe keys, translation files (5 languages)
   ├── Modifiers/        Border, Frame, Padding, Overlay, Lifecycle, KeyPress
   ├── Notification/     Toast-style notification system
   ├── Rendering/        Terminal, ANSIRenderer, ViewRenderer
   ├── StatusBar/        Context-sensitive keyboard shortcuts
   └── Views/            Text, Stacks, Button, TextField, Slider, List, Image, ...
└── TUIkitExample/        Example app (executable target)

Tests/
└── TUIkitTests/          Over a thousand tests across over a hundred test files (including i18n consistency & localization tests)

Tools/
├── EmojiBugScanner/      Probes Terminal.app for emoji cursor-advance quirks
└── EmojiBenchmark/       Benchmarks emoji classification strategies

Requirements

  • Swift 6.2+
  • macOS 14+ or Linux

Developer Notes

  • Tests use Swift Testing (@Test, #expect): run with swift test
  • Most tests run in parallel (some mutate global state and so must be serialised, but they're a tiny subset) and executing them takes only a few seconds.
  • The Terminal class handles raw mode and cursor control via POSIX termios

License

This repository has been published under the MIT license.

Package Metadata

Repository: wadetregaskis/tuikit

Default branch: main

README: README.md