#sqlite #editor #ratatui #pretty-print #sql #csv #turso #browsing #query-plan

app tursotui

A keyboard-driven terminal UI for browsing, querying, and administering Turso and SQLite databases

3 releases (breaking)

Uses new Rust 2024

0.7.0 Apr 9, 2026
0.6.0 Mar 26, 2026
0.1.0 Mar 24, 2026

#101 in Database interfaces

MIT license

6.5MB
30K SLoC

tursotui logo

A keyboard-driven terminal UI for browsing, querying, and administering Turso and SQLite databases.
Built with Rust, ratatui, and vim-inspired navigation.

License: MIT Rust: 1.85+ Turso-first

Table of Contents

Demo

tursotui demo video
Click the image to watch the demo on YouTube

Screenshots

Schema browser, SQL editor, and results table
Schema browser with row counts, SQL editor with syntax highlighting, and sortable results table

DML Preview popup showing generated SQL statements
DML Preview — review generated INSERT, UPDATE, and DELETE statements before committing

Bookmarks overlay with saved queries and SQL preview
Saved Bookmarks — name, search, and recall frequently-used queries

ER Diagram with table relationships and FK edges
ER Diagram — visual entity-relationship view with FK edges, PK/FK markers, and cycle detection

Features

Multi-Database Tabs — open multiple databases simultaneously with a tab bar. Switch between them with Ctrl+PgDn/Ctrl+PgUp, open new databases with Ctrl+O file picker, close with Ctrl+W. Each database has independent schema, editor, and results state.

Schema Browser — color-coded tree view of tables, views, indexes, triggers, and columns with inline search filtering, async row counts, and DDL viewing. Each entity type has a distinct color for quick visual scanning.

SQL Editor — syntax-highlighted editor with undo/redo, text selection, auto-save, active line highlighting, and statement-at-cursor execution. Supports parameterized queries with bound parameters.

Parameterized Queries — when the editor detects SQL parameters (?1, :name, $name), a parameter bar appears below the editor. Tab between fields, type values, use Ctrl+N for NULL. Parameters are bound safely via the database driver — no string interpolation. Values persist across restarts and are logged in query history.

Schema-Aware Autocomplete — context-sensitive completions for table names, columns, SQL keywords, and qualified references with alias resolution.

Results Table — sortable, resizable columns with alternating row colors, cell/row clipboard copy, JSON pretty-printing, and configurable NULL display. Indexed columns are marked with a · indicator in the header for quick identification of indexed vs. non-indexed columns.

Index Awareness — columns that are the leading key of a database index are marked with · in the results header. When filtering on a non-indexed column (via the w WHERE filter) on a table with >1,000 rows, a brief hint warns that the query may be slow.

Inline Data Editor — edit table data directly in the results view. Add, modify, and delete rows with full change tracking. Preview generated DML (INSERT/UPDATE/DELETE) before committing. Transactional submission with automatic rollback on failure.

Foreign Key Navigation — follow FK references from any cell to the referenced row. Breadcrumb trail with back-navigation to retrace your path through related tables.

Record Detail — vertical key-value view for inspecting a single row across all columns, with JSON syntax coloring for structured values.

ER Diagram — visual entity-relationship diagram built from foreign key definitions. Grid layout with box-drawing borders, PK/FK markers, relationship edges, cycle detection with dashed lines, and adjustable spacing.

Go to Object — fuzzy search across all open databases (Ctrl+P). Instantly navigate to any table, view, index, trigger, or column with ranked results.

Enhanced EXPLAIN View — bytecode table and query plan tree, toggled with a single key. Query plan lines are color-coded by scan type (red for full table scans, yellow for temp B-trees, green for index seeks). Warnings section highlights performance issues and suggests CREATE INDEX statements based on WHERE/ORDER BY columns. Press Enter on a suggestion to send it to the editor, or y to copy to clipboard.

Data Profiling — press 5 to open the Profile tab, then Enter to generate. Two-column layout: left shows columns with colored completeness indicators ( green = no nulls, yellow = <50% nulls, red = ≥50% nulls, dim = all null), right shows per-column statistics including null count, distinct count, uniqueness ratio, min/max, and for numeric columns: avg, sum, stddev (Turso). Text columns show length statistics. Top-5 value frequency with bar chart visualization (hidden for high-cardinality columns with >50 distinct values). Automatic sampling for tables over 10,000 rows (configurable via [profile] sample_threshold in config). Profile auto-invalidates on DML operations — stale indicator (*) appears on the tab. Press r to refresh, Ctrl+Up/Down to scroll stats.

Schema Diffing — press F7 to compare schemas between two open databases. Visual diff overlay shows added (+), removed (-), modified (~), and identical (=) objects with color-coded status icons. Expand modified tables to see column-level diffs including type changes. Copy DDL or auto-generated migration SQL (ALTER TABLE for added columns, 12-step rebuild guidance for type changes) to clipboard. Toggle visibility of identical objects with i.

Slow Query Tracking — execution time in the status bar is color-coded against a configurable threshold ([performance] slow_query_ms in config, default 500ms): yellow for slow, red for very slow. Query history overlay adds s to filter slow queries only and S to sort by execution time descending. Slow entries are marked with a icon.

Export — save results as CSV, JSON, or SQL INSERT statements to file or clipboard. Quick TSV copy with a shortcut.

Saved Bookmarks — name and save frequently-used queries (F3). Database-scoped with search, rename, delete, and one-key recall or execute. Backed by SQLite for persistence across sessions.

Quick Table Filter — press w on the results panel to type a WHERE clause and instantly filter table data without touching the editor. Two-phase dismiss (defocus then clear).

DDL Viewer — press Shift+D on any schema object to view its full CREATE statement with syntax highlighting, scrolling, and clipboard copy.

Query History — SQLite-backed per-database history with search, recall, re-execute, auto-prune, slow-query filtering, and execution time sorting.

Admin Tab — database info (file stats, WAL status, journal mode), PRAGMA dashboard with inline editing, WAL checkpoint, and integrity checks.

Mouse Support — click-to-focus panels, click-to-select rows and columns, click-to-expand schema tree nodes, mousewheel scroll on all panels, drag panel borders to resize, click tab bars to switch. Toggle with F8. Hold Shift+Click for native terminal text selection when mouse mode is enabled.

Theming — Catppuccin Mocha (dark) and Catppuccin Latte (light) themes with rounded borders, toggled at runtime.

Turso Compatibility

tursotui is Turso-first — all features are tested and designed to work within Turso's compatibility surface. It also works with standard SQLite databases.

Turso-aware highlights:

  • Schema introspection uses Turso-compatible PRAGMAs (table_info, index_list, etc.)
  • Foreign key metadata parsed from CREATE TABLE SQL (works around Turso's missing PRAGMA foreign_key_list)
  • Syntax highlighting and autocomplete include Turso-specific functions: UUID (uuid4(), uuid7()), vector search (vector_distance_cos(), vector_top_k()), FTS (fts_match(), fts_score()), time functions, regexp, and more
  • DB Info panel recognizes MVCC journal mode with Turso-specific labeling
  • Data editor generates FK-aware DML ordering (parent inserts before child, child deletes before parent)

Known Turso limitations (not tursotui bugs — these are upstream gaps):

  • No RIGHT JOIN, CROSS JOIN, or ranking window functions (ROW_NUMBER, RANK, etc.)
  • No SAVEPOINT/RELEASE, VACUUM, REINDEX, GENERATED columns, or recursive CTEs
  • Views and Triggers are experimental features requiring enablement flags
  • FTS uses different syntax than SQLite FTS5 (fts_match() instead of MATCH)

See COMPAT.md for the full Turso compatibility matrix.

Installation

From source

Requires Rust (edition 2024, Rust 1.85+).

git clone https://github.com/mikeleppane/tursotui.git
cd tursotui
cargo build --release

The binary is at target/release/tursotui.

Usage

# Open a Turso/SQLite database file
tursotui mydb.db

# Open an in-memory database
tursotui

# Open multiple databases in tabs
tursotui db1.db db2.db

Keybindings

Global

Key Action
Ctrl+Q Quit
Ctrl+Tab Cycle focus between panels
Ctrl+B Toggle schema sidebar
Alt+1 / Alt+2 Switch Query / Admin tab
Ctrl+T Toggle dark/light theme
F1 / ? Help overlay
Ctrl+O Open database file
Ctrl+P Go to Object (fuzzy search)
Ctrl+PgDn / Ctrl+PgUp Next / previous database tab
Ctrl+W Close current database tab
Ctrl+Left / Ctrl+Right Resize sidebar (narrower / wider)
Ctrl+Up / Ctrl+Down Resize editor (shorter / taller)
F3 Bookmarks overlay
Ctrl+Shift+E Export results
Ctrl+Shift+C Quick copy results (TSV)
F8 Toggle mouse mode

Query Editor

Key Action
F5 / Ctrl+Enter Execute query
Ctrl+Shift+Enter Execute selection or statement at cursor
Ctrl+Space Trigger autocomplete
Tab Accept completion
Ctrl+Z / Ctrl+Y Undo / Redo
Ctrl+L Clear buffer
Ctrl+H Query history
Shift+Arrow Extend selection
Ctrl+Shift+A Select all

Schema Explorer

Key Action
j / k Navigate up/down
Enter / Space / l Expand / collapse
h Collapse / go to parent
o Query table (SELECT *)
Shift+D View DDL (CREATE statement)
/ Filter by name

Results Table

Key Action
j / k Navigate rows
h / l Navigate columns
g / G First / last row
s Cycle sort on column
< / > Shrink / grow column
w WHERE filter bar
y Copy cell
Y Copy row

Data Editor (when results are editable)

Key Action
e / F2 Edit current cell
Enter Confirm cell edit
Esc Cancel cell edit
Ctrl+N Set cell to NULL
Ctrl+Enter / F10 Confirm modal edit
a Add new row
d Toggle delete mark
c Clone row
u / U Revert cell / row
Ctrl+U Revert all changes
Ctrl+D Preview DML
Ctrl+S Submit changes
f Follow FK reference
Alt+Left FK back-navigation

Bottom Panels

Key Action
1 / 2 / 3 / 4 / 5 Results / Explain / Detail / ER Diagram / Profile
Tab (Explain) Toggle Bytecode / Query Plan
Enter (Explain) Generate EXPLAIN
Tab (ER Diagram) Cycle focus between tables
Enter (ER Diagram) Expand / collapse table columns
h/j/k/l (ER Diagram) Pan viewport
+ / - (ER Diagram) Adjust spacing
c (ER Diagram) Toggle compact mode
o (ER Diagram) Query focused table

Admin Tab

Key Action
r Refresh
c WAL checkpoint
i Integrity check
Enter (Pragmas) Edit selected pragma

Press Esc in any panel to release focus.

Configuration

Config file location: ~/.config/tursotui/config.toml

[editor]
tab_size = 4
autocomplete = true
autocomplete_min_chars = 1

[results]
max_column_width = 40
null_display = "NULL"

[history]
max_entries = 5000

[theme]
mode = "dark"    # "dark" or "light"

[mouse]
mouse_mode = true  # enable mouse capture (click, scroll, drag)

Architecture

tursotui uses a unidirectional data flow architecture inspired by Elm/Redux. Components emit Actions, AppState processes state changes, and results route back through a two-phase dispatch.

System Overview

graph TB
    subgraph Terminal
        CT[Crossterm Events<br><i>key, mouse, resize</i>]
    end

    subgraph EventLoop["Event Loop (main.rs)"]
        DRAIN[Drain async channel]
        POLL[Poll crossterm — 16ms]
        ROUTE[Route to focused component]
        GLOBAL[Global key fallback]
        RENDER[Render frame]
    end

    subgraph State["Application State (app.rs)"]
        AS[AppState]
        AC[Action enum<br><i>50+ variants</i>]
        SC[SchemaCache]
        DC[DatabaseContext<br><i>per-tab state</i>]
    end

    subgraph Components["Components (Component trait)"]
        direction LR
        SE[Schema<br>Explorer]
        QE[Query<br>Editor]
        RT[Results<br>Table]
        DE[Data<br>Editor]
        EV[Explain<br>View]
        RD[Record<br>Detail]
        ER[ER<br>Diagram]
        AI[Admin<br>Info]
        PD[Pragma<br>Dashboard]
    end

    subgraph Overlays
        direction LR
        HLP[Help]
        HST[History]
        BK[Bookmarks]
        EXP[Export]
        DML[DML Preview]
        FP[File Picker]
        GTO[Go to Object]
        DDL[DDL Viewer]
    end

    subgraph Async["Async I/O (tokio)"]
        DH[DatabaseHandle<br><i>Arc&lt;Database&gt;</i>]
        SPAWN["tokio::spawn<br><i>fresh connection per query</i>"]
        MPSC[mpsc channel]
    end

    subgraph Persistence
        CFG[Config<br><i>TOML</i>]
        HDB[History DB<br><i>SQLite</i>]
        BUF[Editor Buffer<br><i>auto-save</i>]
    end

    CT --> POLL
    DRAIN --> ROUTE
    POLL --> ROUTE
    ROUTE --> Components
    ROUTE --> Overlays
    Components -->|emit Action| AC
    Overlays -->|emit Action| AC
    AC -->|"phase 1: state"| AS
    AC -->|"phase 2: dispatch"| Components
    AC -->|"phase 2: I/O"| DH
    DH --> SPAWN
    SPAWN --> MPSC
    MPSC --> DRAIN
    AS --- SC
    AS --- DC
    RENDER --> Components
    RENDER --> Overlays
    CFG -.-> AS
    HDB -.-> HST
    BUF -.-> QE

    ROUTE -.->|unhandled| GLOBAL

Event Loop & Data Flow

sequenceDiagram
    participant T as Terminal
    participant EL as Event Loop
    participant C as Component
    participant AS as AppState
    participant DB as DatabaseHandle
    participant TK as tokio task

    Note over EL: Each frame (~60fps)

    EL->>EL: Drain async results (try_recv)
    EL->>T: Poll crossterm (16ms timeout)
    T-->>EL: KeyEvent

    EL->>C: handle_key(event)
    C-->>EL: Action

    rect rgb(40, 40, 60)
        Note over EL,AS: Two-phase dispatch
        EL->>AS: update(action) — state mutations
        EL->>C: dispatch — route to components & I/O
    end

    alt Database action (e.g. ExecuteQuery)
        EL->>DB: execute_query(sql)
        DB->>TK: tokio::spawn with fresh connection
        TK-->>EL: QueryResult via mpsc
        EL->>AS: update(QueryCompleted)
        EL->>C: dispatch(QueryCompleted)
    end

    EL->>C: render(frame, area)

UI Layout

┌──────────────────────────────────────────────────────────────────┐
│  Tab Bar: [db1.sqlite] [db2.sqlite] [:memory:]                  │
├────────────────┬─────────────────────────────────────────────────┤
│                │                                                 │
│  Schema        │  Query Editor                                   │
│  Explorer      │  ┌─────────────────────────────────────────┐   │
│                │  │ SELECT * FROM users                      │   │
│  ▸ Tables (5)  │  │ WHERE active = 1;                        │   │
│    users       │  └─────────────────────────────────────────┘   │
│    orders      ├─────────────────────────────────────────────────┤
│    products    │                                                 │
│  ▸ Views (2)   │  Results Table / Explain / Detail / ER Diagram  │
│  ▸ Indexes (3) │  ┌────────┬──────────┬─────────┬────────────┐  │
│                │  │ id     │ name     │ email   │ active     │  │
│                │  ├────────┼──────────┼─────────┼────────────┤  │
│                │  │ 1      │ Alice    │ a@b.com │ 1          │  │
│                │  │ 2      │ Bob      │ b@c.com │ 1          │  │
│                │  └────────┴──────────┴─────────┴────────────┘  │
├────────────────┴─────────────────────────────────────────────────┤
│  Status: users │ 2 rows │ 4 cols                        F1 Help │
└──────────────────────────────────────────────────────────────────┘

Key Design Decisions

  • Unidirectional data flow — components emit Actions, AppState processes state changes, results route back to components via two-phase dispatch.
  • Async queriestokio::spawn with fresh connections per query, results delivered via mpsc channel. No shared connection state between tasks.
  • Component trait — each panel implements handle_key, update, render with consistent panel_block / overlay_block helpers for styled borders.
  • Catppuccin theme system — full Mocha (dark) and Latte (light) palettes with semantic color roles for schema types, editor highlighting, and data editing states.
  • Transactional data editing — change log with one-entry-per-PK invariant, DML generation, and FK-aware statement ordering for safe transactional submission.
  • No unsafe code#[forbid(unsafe_code)] enforced project-wide.

Tech Stack

Crate Purpose
turso Database engine (libSQL/SQLite)
ratatui Terminal UI framework
tokio Async runtime
clap CLI argument parsing
arboard Clipboard access
unicode-width Display-column width measurement
serde / toml Configuration serialization
serde_json JSON detection and pretty-printing
dirs Platform config/data directories

License

MIT

Acknowledgments

Contact

Author: Mikko Leppänen Email: mleppan23@gmail.com GitHub: @mikeleppane

Dependencies

~40–68MB
~1M SLoC