Skip to main content

Overview

TUIOS can be embedded as a library in your Go applications. The pkg/tuios package provides a clean API to integrate the terminal window manager into Bubble Tea apps, web terminals, SSH servers, and more.
The library API is stable and designed for embedding. It exposes only the public interface from pkg/tuios/tuios.go.

Installation

go get github.com/Gaurav-Gosain/tuios/pkg/tuios
Import:
import "github.com/Gaurav-Gosain/tuios/pkg/tuios"

Basic Usage

Minimal Example

package main

import (
	"log"
	"os"

	tea "charm.land/bubbletea/v2"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func main() {
	// Create TUIOS model with defaults
	model := tuios.New()

	// Create Bubble Tea program
	p := tea.NewProgram(
		model,
		tuios.ProgramOptions()...,
	)

	// Run the TUI
	if _, err := p.Run(); err != nil {
		log.Fatal(err)
	}
}
This gives you a full TUIOS window manager in ~10 lines of code.

Configuration Options

Using Functional Options

Customize TUIOS behavior with options:
model := tuios.New(
	tuios.WithTheme("dracula"),
	tuios.WithShowKeys(true),
	tuios.WithAnimations(false),
	tuios.WithWorkspaces(9),
	tuios.WithBorderStyle("rounded"),
	tuios.WithDockbarPosition("top"),
)

Available Options

Theme

tuios.WithTheme("dracula")
tuios.WithTheme("nord")
tuios.WithTheme("tokyonight")
tuios.WithTheme("")  // Use terminal colors
Leave theme empty to use the user’s terminal color scheme.

ShowKeys Overlay

tuios.WithShowKeys(true)   // Display pressed keys
tuios.WithShowKeys(false)  // Hide overlay

Animations

tuios.WithAnimations(true)   // Smooth transitions
tuios.WithAnimations(false)  // Instant snapping

ASCII-Only Mode

tuios.WithASCIIOnly(true)   // No Nerd Font icons
tuios.WithASCIIOnly(false)  // Use icons (default)

Workspaces

tuios.WithWorkspaces(9)  // 1-9 workspaces
tuios.WithWorkspaces(5)  // Limit to 5
Valid range: 1-9.

Border Style

tuios.WithBorderStyle("rounded")
tuios.WithBorderStyle("normal")
tuios.WithBorderStyle("thick")
tuios.WithBorderStyle("double")
tuios.WithBorderStyle("hidden")
tuios.WithBorderStyle("block")
tuios.WithBorderStyle("ascii")

Dockbar Position

tuios.WithDockbarPosition("bottom")
tuios.WithDockbarPosition("top")
tuios.WithDockbarPosition("hidden")

Window Buttons

tuios.WithHideWindowButtons(true)   // No minimize/maximize/close
tuios.WithHideWindowButtons(false)  // Show buttons

Scrollback Buffer

tuios.WithScrollbackLines(10000)  // Default
tuios.WithScrollbackLines(50000)  // Large buffer
Valid range: 100 - 1,000,000 lines.

Initial Size

tuios.WithSize(120, 40)  // 120 cols x 40 rows
Leave at 0 to auto-detect from terminal.

SSH Mode

tuios.WithSSHMode(true)   // Running over SSH
tuios.WithSSHMode(false)  // Local terminal

Custom User Config

import "github.com/Gaurav-Gosain/tuios/internal/config"

userConfig, _ := config.LoadUserConfig()
tuios.WithUserConfig(userConfig)

Using with sip (Web Terminal)

sip serves Bubble Tea apps as web applications. TUIOS integrates seamlessly.

Basic Web Terminal

package main

import (
	"context"
	"log"

	tea "charm.land/bubbletea/v2"
	"github.com/charmbracelet/sip"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func main() {
	server := sip.NewServer(sip.DefaultConfig())

	server.Serve(context.Background(), func(sess sip.Session) (tea.Model, []tea.ProgramOption) {
		// Create TUIOS for this web session
		model := tuios.NewForPTY(
			sess.Pty(),
			tuios.WithTheme("dracula"),
		)

		return model, tuios.ProgramOptions()
	})

	log.Println("Web terminal running at http://localhost:8080")
	if err := server.Start(":8080"); err != nil {
		log.Fatal(err)
	}
}
Now users can access TUIOS in their browser at http://localhost:8080.

Advanced Web Setup

package main

import (
	"context"
	"log"
	"os"

	tea "charm.land/bubbletea/v2"
	"github.com/charmbracelet/sip"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func main() {
	cfg := sip.DefaultConfig()
	cfg.Host = "0.0.0.0"
	cfg.Port = 3000
	cfg.KeyPath = "server.key"
	cfg.CertPath = "server.crt"

	server := sip.NewServer(cfg)

	server.Serve(context.Background(), func(sess sip.Session) (tea.Model, []tea.ProgramOption) {
		// PTY provides terminal dimensions
		pty := sess.Pty()

		// Create TUIOS with web-specific settings
		model := tuios.NewForPTY(
			pty,
			tuios.WithTheme(""),  // Use browser terminal colors
			tuios.WithAnimations(true),
			tuios.WithScrollbackLines(20000),
			tuios.WithASCIIOnly(false),
		)

		opts := tuios.ProgramOptions()
		opts = append(opts, tea.WithFilter(tuios.FilterMouseMotion))

		return model, opts
	})

	log.Printf("TUIOS web terminal: https://0.0.0.0:%d", cfg.Port)
	if err := server.Start(); err != nil {
		log.Fatal(err)
	}
}

SSH Server Integration

Embed TUIOS in an SSH server using Wish.

Basic SSH Server

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	tea "charm.land/bubbletea/v2"
	"charm.land/wish/v2"
	"charm.land/wish/v2/bubbletea"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func main() {
	server, err := wish.NewServer(
		wish.WithAddress(":2222"),
		wish.WithHostKeyPath(".ssh/tuios_host_key"),
		wish.WithMiddleware(
			bubbletea.Middleware(teaHandler),
		),
	)
	if err != nil {
		log.Fatal(err)
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, os.Interrupt, syscall.SIGTERM)

	log.Printf("TUIOS SSH server listening on :2222")
	log.Println("Connect with: ssh localhost -p 2222")

	go func() {
		if err := server.ListenAndServe(); err != nil {
			log.Fatal(err)
		}
	}()

	<-done
	log.Println("Shutting down...")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	server.Shutdown(ctx)
}

func teaHandler(sess wish.Session) (tea.Model, []tea.ProgramOption) {
	pty := sess.Pty()

	model := tuios.NewForPTY(
		pty,
		tuios.WithSSHMode(true),
		tuios.WithTheme("dracula"),
	)

	return model, tuios.ProgramOptions()
}
Users can now SSH in and get a TUIOS session:
ssh localhost -p 2222

Advanced API

PTY Interface

The NewForPTY function accepts any type implementing:
type PTY interface {
	Width() int
	Height() int
}
This works with:
  • sip.Session.Pty()
  • wish.Session.Pty()
  • Custom PTY implementations

Program Options

ProgramOptions() returns recommended tea.ProgramOption values:
opts := tuios.ProgramOptions()
// Returns: []tea.ProgramOption{
//     tea.WithFPS(60),
// }

p := tea.NewProgram(model, opts...)
You can add your own:
opts := tuios.ProgramOptions()
opts = append(opts,
	tea.WithFilter(tuios.FilterMouseMotion),
	tea.WithoutSignalHandler(),
)

p := tea.NewProgram(model, opts...)

Mouse Motion Filtering

FilterMouseMotion reduces CPU usage by filtering redundant mouse events:
p := tea.NewProgram(
	model,
	tea.WithFilter(tuios.FilterMouseMotion),
)
It passes through mouse motion only during:
  • Window dragging
  • Window resizing
  • Scrollback selection
  • Terminal mode mouse interactions (e.g., vim)
All other mouse motion is filtered, reducing CPU load by 40-60%.

Mode Constants

Access TUIOS modes:
import "github.com/Gaurav-Gosain/tuios/pkg/tuios"

switch model.Mode {
case tuios.WindowManagementMode:
	fmt.Println("In window mode")
case tuios.TerminalMode:
	fmt.Println("In terminal mode")
}

Config Loading

Load and apply user configuration:
// Load user's config file
userConfig, err := tuios.Config.LoadUserConfig()
if err != nil {
	userConfig = tuios.Config.DefaultConfig()
}

// Get config file path
configPath, _ := tuios.Config.GetConfigPath()
fmt.Println("Config:", configPath)

// Create TUIOS with config
model := tuios.New(
	tuios.WithUserConfig(userConfig),
)

Real-World Examples

Custom Wrapper CLI

// cmd/myapp/main.go
package main

import (
	"fmt"
	"log"
	"os"

	tea "charm.land/bubbletea/v2"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
	"github.com/spf13/cobra"
)

func main() {
	var theme string
	var workspaces int

	rootCmd := &cobra.Command{
		Use:   "myapp",
		Short: "My custom TUIOS wrapper",
		RunE: func(cmd *cobra.Command, args []string) error {
			model := tuios.New(
				tuios.WithTheme(theme),
				tuios.WithWorkspaces(workspaces),
				tuios.WithBorderStyle("thick"),
				tuios.WithDockbarPosition("top"),
			)

			p := tea.NewProgram(model, tuios.ProgramOptions()...)
			_, err := p.Run()
			return err
		},
	}

	rootCmd.Flags().StringVar(&theme, "theme", "dracula", "Color theme")
	rootCmd.Flags().IntVar(&workspaces, "workspaces", 9, "Number of workspaces")

	if err := rootCmd.Execute(); err != nil {
		log.Fatal(err)
	}
}

Development Container

Embed TUIOS in a devcontainer:
// internal/devenv/tuios.go
package devenv

import (
	tea "charm.land/bubbletea/v2"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func StartDevEnvironment() error {
	model := tuios.New(
		tuios.WithTheme("tokyonight"),
		tuios.WithWorkspaces(5),
		tuios.WithScrollbackLines(50000),
	)

	// Start with dev-specific settings
	p := tea.NewProgram(
		model,
		tuios.ProgramOptions()...,
	)

	_, err := p.Run()
	return err
}

Multi-User Server

package main

import (
	"context"
	"fmt"
	"log"

	tea "charm.land/bubbletea/v2"
	"charm.land/wish/v2"
	"charm.land/wish/v2/bubbletea"
	"github.com/Gaurav-Gosain/tuios/pkg/tuios"
)

func main() {
	server, _ := wish.NewServer(
		wish.WithAddress(":2222"),
		wish.WithMiddleware(
			bubbletea.Middleware(func(sess wish.Session) (tea.Model, []tea.ProgramOption) {
				// Per-user theme from environment
				username := sess.User()
				theme := getUserTheme(username)

				model := tuios.NewForPTY(
					sess.Pty(),
					tuios.WithTheme(theme),
					tuios.WithSSHMode(true),
				)

				return model, tuios.ProgramOptions()
			}),
		),
	)

	log.Println("Multi-user TUIOS server started")
	server.ListenAndServe()
}

func getUserTheme(username string) string {
	// Look up user preference from DB/config
	themes := map[string]string{
		"alice": "nord",
		"bob":   "dracula",
	}
	return themes[username]
}

Package Structure

pkg/tuios/
  ├── tuios.go          # Public API
  └── ...

internal/
  ├── app/              # Core window manager (not exported)
  ├── config/           # Configuration (partial export)
  ├── terminal/         # Terminal windows (not exported)
  └── ...
Only pkg/tuios is part of the stable API. Do not import from internal/ in your applications.

Type Reference

Model

type Model = app.OS
The main TUIOS model implementing tea.Model.

Mode

type Mode = app.Mode

const (
	WindowManagementMode Mode = app.WindowManagementMode
	TerminalMode         Mode = app.TerminalMode
)

Options

type Options struct {
	Theme             string
	ShowKeys          bool
	Animations        bool
	ASCIIOnly         bool
	Workspaces        int
	BorderStyle       string
	DockbarPosition   string
	HideWindowButtons bool
	ScrollbackLines   int
	Width             int
	Height            int
	SSHMode           bool
	UserConfig        *config.UserConfig
}

Option Function

type Option func(*Options)
Functional option for configuring TUIOS.

Best Practices

1. Always Use ProgramOptions

// Good
p := tea.NewProgram(model, tuios.ProgramOptions()...)

// Bad - missing FPS config
p := tea.NewProgram(model)

2. Filter Mouse Motion

p := tea.NewProgram(
	model,
	tea.WithFilter(tuios.FilterMouseMotion),  // Reduces CPU usage
)

3. Handle Errors from Config Loading

userConfig, err := tuios.Config.LoadUserConfig()
if err != nil {
	// Use defaults, don't fail
	userConfig = tuios.Config.DefaultConfig()
}

4. Match Theme to Environment

// For web/SSH: let users use their terminal colors
tuios.WithTheme("")  

// For local CLI: use a specific theme
tuios.WithTheme("dracula")

5. Adjust Scrollback for Use Case

// Development (lots of output)
tuios.WithScrollbackLines(50000)

// Production (memory constrained)
tuios.WithScrollbackLines(5000)

Troubleshooting

Terminal Size Detection

If running in an environment where size detection fails:
model := tuios.New(
	tuios.WithSize(120, 40),  // Explicit fallback
)

Theme Not Applying

Ensure theme initialization happens before program start:
import "github.com/Gaurav-Gosain/tuios/internal/theme"

// Initialize theme system (happens automatically in New())
theme.Initialize("dracula")

Mouse Events Not Working

Ensure mouse is enabled in your program:
p := tea.NewProgram(
	model,
	tea.WithMouseCellMotion(),  // Bubble Tea mouse support
)

Migration from CLI to Library

If you’re used to the TUIOS CLI and want to embed it: CLI:
tuios --theme dracula --workspaces 5
Library:
model := tuios.New(
	tuios.WithTheme("dracula"),
	tuios.WithWorkspaces(5),
)

p := tea.NewProgram(model, tuios.ProgramOptions()...)
p.Run()
CLI flags → Options:
  • --theme <name>WithTheme(name)
  • --ascii-onlyWithASCIIOnly(true)
  • --border-style <style>WithBorderStyle(style)
  • --dockbar-position <pos>WithDockbarPosition(pos)
  • --hide-window-buttonsWithHideWindowButtons(true)
  • --scrollback-lines <n>WithScrollbackLines(n)
  • --show-keysWithShowKeys(true)
  • --no-animationsWithAnimations(false)

See Also

Bubble Tea

TUI framework documentation

sip

Serve Bubble Tea apps as web apps

Wish

SSH server middleware

Configuration

Config file reference