https://github.com/mikeleppane/tursotui
A keyboard-driven terminal UI for browsing, querying, and administering Turso and SQLite databases — built with Rust, ratatui, and vim-inspired navigation.
https://github.com/mikeleppane/tursotui
cli crossterm database ratatui rust sqlite terminal terminal-ui tui tui-app turso tursodb vim
Last synced: 3 months ago
JSON representation
A keyboard-driven terminal UI for browsing, querying, and administering Turso and SQLite databases — built with Rust, ratatui, and vim-inspired navigation.
- Host: GitHub
- URL: https://github.com/mikeleppane/tursotui
- Owner: mikeleppane
- License: mit
- Created: 2026-03-20T15:08:48.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-01T06:10:34.000Z (3 months ago)
- Last Synced: 2026-04-01T08:54:29.292Z (3 months ago)
- Topics: cli, crossterm, database, ratatui, rust, sqlite, terminal, terminal-ui, tui, tui-app, turso, tursodb, vim
- Language: Rust
- Homepage:
- Size: 6.21 MB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
A keyboard-driven terminal UI for browsing, querying, and administering Turso and SQLite databases.
Built with Rust, ratatui, and vim-inspired navigation.
## Table of Contents
- [Demo](#demo)
- [Screenshots](#screenshots)
- [Features](#features)
- [Turso Compatibility](#turso-compatibility)
- [Installation](#installation)
- [Usage](#usage)
- [Keybindings](#keybindings)
- [Configuration](#configuration)
- [Architecture](#architecture)
- [Tech Stack](#tech-stack)
- [License](#license)
- [Acknowledgments](#acknowledgments)
- [Contact](#contact)
## Demo
Click the image to watch the demo on YouTube
## Screenshots
Schema browser with row counts, SQL editor with syntax highlighting, and sortable results table
DML Preview — review generated INSERT, UPDATE, and DELETE statements before committing
Saved Bookmarks — name, search, and recall frequently-used queries
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.
**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 uses `PRAGMA defer_foreign_keys` inside transactions for safe FK handling
**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](https://github.com/tursodatabase/turso/blob/main/COMPAT.md) for the full Turso compatibility matrix.
## Installation
### From source
Requires [Rust](https://rustup.rs/) (edition 2024, Rust 1.85+).
```sh
git clone https://github.com/mikeleppane/tursotui.git
cd tursotui
cargo build --release
```
The binary is at `target/release/tursotui`.
## Usage
```sh
# 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) |
### 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` | Results / Explain / Detail / ER Diagram |
| `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`
```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"
```
## Architecture
tursotui uses a **unidirectional data flow** architecture inspired by Elm/Redux. Components emit `Action`s, `AppState` processes state changes, and results route back through a two-phase dispatch.
### System Overview
```mermaid
graph TB
subgraph Terminal
CT[Crossterm Events
key, mouse, resize]
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
50+ variants]
SC[SchemaCache]
DC[DatabaseContext
per-tab state]
end
subgraph Components["Components (Component trait)"]
direction LR
SE[Schema
Explorer]
QE[Query
Editor]
RT[Results
Table]
DE[Data
Editor]
EV[Explain
View]
RD[Record
Detail]
ER[ER
Diagram]
AI[Admin
Info]
PD[Pragma
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
Arc<Database>]
SPAWN["tokio::spawn
fresh connection per query"]
MPSC[mpsc channel]
end
subgraph Persistence
CFG[Config
TOML]
HDB[History DB
SQLite]
BUF[Editor Buffer
auto-save]
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
```mermaid
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
```text
┌──────────────────────────────────────────────────────────────────┐
│ 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 `Action`s, `AppState` processes state changes, results route back to components via two-phase dispatch.
- **Async queries** — `tokio::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 `PRAGMA defer_foreign_keys` for safe FK handling.
- **No unsafe code** — `#[forbid(unsafe_code)]` enforced project-wide.
## Tech Stack
| Crate | Purpose |
| ------- | --------- |
| [turso](https://crates.io/crates/turso) | Database engine (libSQL/SQLite) |
| [ratatui](https://crates.io/crates/ratatui) | Terminal UI framework |
| [tokio](https://crates.io/crates/tokio) | Async runtime |
| [clap](https://crates.io/crates/clap) | CLI argument parsing |
| [arboard](https://crates.io/crates/arboard) | Clipboard access |
| [unicode-width](https://crates.io/crates/unicode-width) | Display-column width measurement |
| [serde](https://crates.io/crates/serde) / [toml](https://crates.io/crates/toml) | Configuration serialization |
| [serde_json](https://crates.io/crates/serde_json) | JSON detection and pretty-printing |
| [dirs](https://crates.io/crates/dirs) | Platform config/data directories |
## License
MIT
## Acknowledgments
- Built with [Rust](https://www.rust-lang.org/)
- TUI powered by [ratatui](https://ratatui.rs)
- Cross-platform terminal handling by [crossterm](https://github.com/crossterm-rs/crossterm)
- Theme palette by [Catppuccin](https://github.com/catppuccin/catppuccin)
## Contact
**Author:** Mikko Leppänen
**Email:**
**GitHub:** [@mikeleppane](https://github.com/mikeleppane)