o-family

o-family ochazuke

ochazuke — tea poured over rice

A modal TUI framework for Go, built on Bubble Tea v2: Vim-style modes and an extensible command model, so every action in your app can be bound to a key, run from the palette or scripted from the shell.

libraryv0.55.0pre-1.0Go 1.26 or newer

go run ./examples/csvrec
The CSV example: moving through 3,000 rows, a palette command on a whole column, a filter, a sort and an edit with undo

The CSV example: moving through 3,000 rows, a palette command on a whole column, a filter, a sort and an edit with undo

overview

What it is

Ochazuke is a modal TUI framework for Go. It builds on Charmbracelet's Bubble Tea v2 (with Lip Gloss v2 and Bubbles v2) and adds what a keyboard-first app needs: Vim-style modes and editing, pages and a stack of modals that keep their state, lists, tables and trees, a command palette, forms and a Settings page, all styled out of the box.

Underneath is a generic command model. Every action is a named command with typed arguments, and the same command runs from a key, the palette, the command line or the Go API. Keys, settings and commands are data: users rebind and extend them in JSON files, and apps and extensions add their own without forking the framework. Every command lands in the action history, so undo and dot-repeat work across the whole app.

install

go get
go get github.com/unixsuperhero/ochazuke@latest
demo
go run ./cmd/ochazuke run   # from a checkout
bindings.jsonjs
[
  { noun: "directory", mode: "normal", keys: "U",
    request: { verb: "undo", noun: "input", args: { steps: 3 } } },
  { noun: "editor", mode: "insert", keys: "esc", request: null },   // unbind a default
  { instance: "notes", mode: "normal", keys: "g s",
    request: { verb: "summarize", noun: "workspace" } }
]

features

What it does

  1. 01 / 12 · modal editing

    Vim-style modes

    Normal, insert, visual and any mode your app registers. Counts, operators and key sequences are pending modes of their own, and the mode badge and cursor shape always show where you are.

  2. 02 / 12 · modal editing

    Vim's editing grammar

    Each input owns its editing state and a branching undo tree. Yanks, registers and macros are shared, so a yank from notes pastes into a path field or a modal editor.

  3. 03 / 12 · modal editing

    A stack of stateful modals

    Modals are kept, not rebuilt. Each one holds its mode, focused field, drafts, cursor, selection and scroll, so closing a modal gives you back exactly what you left.

  4. 04 / 12 · layout and components

    Pages that remember

    Pages keep their focus, mode, drafts, undo history and scroll across navigation. Partial-screen modals leave the screen behind them visible, and themes (adaptive, dark, light, mono or your own) restyle everything at once.

  5. 05 / 12 · layout and components

    Lists, tables and trees

    Lists with marks, a / filter and visual selection; tables with a cell cursor, pinned and resizable columns; editable outline trees. A command on a marked set runs once per row, with one confirm.

  6. 06 / 12 · layout and components

    A command palette and forms

    : opens a command line with completion that takes either word order (edit task or task edit). A command missing an argument opens a form with a field per parameter, and choices and paths come from type-to-filter pickers.

  7. 07 / 12 · layout and components

    Settings and a Settings page

    Apps declare typed settings with defaults and sections; a generated Settings page edits them, and config export writes a full reference.

  8. 08 / 12 · extensibility

    A command model

    Every action is a named command with typed, validated arguments. A key, the palette, the CLI and the Go API all run the same command the same way, so nothing is reachable from only one of them.

  9. 09 / 12 · extensibility

    Keys and settings are data

    Default keys are JSON rows. Your bindings.json rebinds, adds or unbinds them per component and mode, with sequences, guards and timed ambiguous keys (g, g g, g g g). The footer hints come from the same rows, so they never name an unbound key.

  10. 10 / 12 · extensibility

    Your own commands

    actions.json adds commands that run external programs. They are ordinary commands: bindable, in the palette, on the CLI and in ? help.

  11. 11 / 12 · extensibility

    Live CLI control and extensions

    A local Unix socket drives a running app from another terminal: ochazuke page open, input undo, state get. Any tool that answers catalog mounts into a host app as pages, keys and palette commands, and catalog inspect browses all of it as HTML.

  12. 12 / 12 · testing

    Stories

    UI behaviour is tested with monogatari stories: JSON keystroke scripts played headless against the app's real model, with expectations on what the screen shows. Ochazuke's own stories run in make test.