Skip to content

photon-hq/SwiftTUI

 
 

Repository files navigation

SwiftTUI

swift 5.6 platform macos platform linux

An innovative, exceptionally simple way to build text-based user interfaces.

SwiftTUI brings SwiftUI to the terminal. It provides an API similar to SwiftUI to build terminal applications with a text-based user interface.

What is working

Many features from SwiftUI are already working:

✓ Property wrappers @State, @Binding, @Environment and @ObservedObject
✓ Stacks, .frame(), .padding(), GeometryReader, @ViewBuilder, ForEach, Group
✓ Structural identity like in SwiftUI
✓ Scrollable lists with ScrollView
Button, TextField and moving focus with the arrow keys
Color with ANSI, xterm and TrueColor support
Text with bold, italic, underscore and strikethrough variants
.onAppear(), .border(), .foregroundColor(), .backgroundColor
.buttonHighlightStyle() for customizing button focus appearance
.onKeyPress() for detecting keyboard input
onInterrupt for handling Ctrl+C
✓ Modifiers applied to all views in a collection like in SwiftUI

Getting started

To use SwiftTUI, you need to add the SwiftTUI package dependency. Import SwiftTUI in your files, and write your views like SwiftUI views with the supported features. Then, start the terminal application using one of your views as the root view. This is the simplest SwiftTUI app you can write:

import SwiftTUI

struct MyTerminalView: View {
  var body: some View {
    Text("Hello, world!")
  }
}

Application(rootView: MyTerminalView()).start()

To run your app, change to your package's directory and run it from the terminal:

swift run

For more, and to see the supported functionality, check out the documentation.

Handling Ctrl+C (Interrupt Signal)

By default, pressing Ctrl+C will quit the application. You can override this behavior using the onInterrupt callback:

let app = Application(rootView: MyView())

app.onInterrupt = {
    // Return .quit to exit, or .none to ignore
    return .none  // Ignore Ctrl+C
}

app.start()

InterruptResult Options

Result Description
.quit Exit the application
.none Ignore the interrupt and continue running

Example: Confirmation Before Quit

import SwiftTUI

struct MyView: View {
    @State var showQuitConfirmation = false
    
    var body: some View {
        VStack {
            Text("Press Ctrl+C to quit")
            if showQuitConfirmation {
                Text("Are you sure? Press 'y' to confirm, 'n' to cancel")
            }
        }
    }
}

let app = Application(rootView: MyView())

app.onInterrupt = {
    print("Ctrl+C pressed!")
    // Add your confirmation logic here
    return .none  // Don't quit immediately
}

app.start()

Keyboard Input

SwiftTUI provides the .onKeyPress() modifier to detect keyboard input outside of text fields.

Detect a Specific Key

VStack {
    Text("Press 'q' to quit")
    Button("Click Me") { }
}
.onKeyPress("q") {
    print("Q was pressed!")
    exit(0)
}

Detect Any Key

VStack {
    Text("Press any key")
}
.onKeyPress { char in
    print("Key pressed: \(char)")
    return true  // Return true if handled, false to propagate
}

Multiple Key Handlers

VStack {
    Text("Press 'q' to quit, 'r' to refresh")
}
.onKeyPress("q") {
    exit(0)
}
.onKeyPress("r") {
    refresh()
}

Button Highlight Styles

SwiftTUI provides customizable button highlight styles. By default, buttons invert their colors when focused. You can customize this behavior using the .buttonHighlightStyle() modifier.

Available Styles

Style Description
.inverted Default behavior - swaps foreground and background colors
.textColor(Color) Only changes text color when focused (no background change)
.none No visual change when focused

Usage Examples

Change text color only (no background):

Button("Click Me") {
    print("Pressed!")
}
.buttonHighlightStyle(.textColor(.cyan))

No highlight effect:

Button("Click Me") {
    print("Pressed!")
}
.buttonHighlightStyle(.none)

Apply to multiple buttons:

VStack {
    Button("Button 1") { }
    Button("Button 2") { }
    Button("Button 3") { }
}
.buttonHighlightStyle(.textColor(.green))

Different styles for different buttons:

VStack {
    Button("Cyan Highlight") { }
        .buttonHighlightStyle(.textColor(.cyan))
    
    Button("No Highlight") { }
        .buttonHighlightStyle(.none)
    
    Button("Default Inverted") { }
        // Uses default .inverted style
}

Examples

These example projects are included in the repository.

This is a very simple to-do list application. Use the arrow keys to move around. To complete a to-do item, select it, and then press the enter key or space bar. To add a new to-do item, move to the text field, enter a description, and press the enter key to add it to the list. Completed items are automatically removed from the list after half a second.

This is a flag editor, which you will agree to if you come from a country which has a simple flag consisting of colors stacked horizontally or vertically. Select one of the colors of the flag to change it. Use the options on the right to change the number of colors or the flag orientation.

Showcase

Are you working on a project that's using SwiftTUI? Get in touch with me if you'd like to have it featured here.

soundcld

This is a TUI application for SoundCloud. It's not (yet) available publicly.

More

See a screen recording of SwiftTUI in action on Reddit.

Learn how the diffing works on my blog.

Documentation

You can find generated documentation here.

Contributing

This is an open-source project, contributions are welcome! The goal of SwiftTUI is to resemble SwiftUI when it comes to both API and inner workings, unless those don't make sense for terminal applications. Features that SwiftUI lacks but that would be useful for terminal applications might better live in a seperate project.

About

SwiftUI for terminal applications

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%