Skip to content

fissible/shellframe

Repository files navigation

shellframe — Bash TUI Library

A composable, full-featured terminal UI library for bash. Designed to be sourced by other tools — each widget gathers input from a human and returns clean data to the caller.

Requirements: bash 3.2+ (macOS default), a VT100-compatible terminal.


Design goals

  • LEGO composability — small, single-purpose components that snap together. Source only what you need; widgets never implicitly depend on each other.
  • Full UI lifecycle — covers input gathering, selection, feedback, and output formatting. Every widget maps to a clear data shape (string, index, flag set).
  • Two abstraction levels — use widgets directly for simple one-off interactions (shellframe_confirm, shellframe_alert), or declare a multi-screen application with shellframe_app: define screens as function triples and let the runtime own the session loop, widget dispatch, and transitions.
  • Two audiences — human-friendly keyboard behavior with discoverable footer hints; developer-friendly exit codes, namespaced globals, and stdout/tty split so widgets work correctly inside $() command substitution.
  • Self-configuring — on first load, shellframe detects the bash version and terminal capabilities, writes feature flags to .toolrc.local, and reads them back on subsequent runs so code paths are chosen once per machine.
  • Cross-version tested — a Docker-based test matrix runs the suite against bash 3.2, 4.4, and 5.x to catch portability regressions before they ship.

Quick start

Single widget — call a widget directly and read its exit code:

source /path/to/shellframe/shellframe.sh

shellframe_confirm "Deploy to production?" "  api-server  restart" "  cache       flush"
(( $? == 0 )) && deploy || echo "Cancelled."

Multi-screen application — declare screens as function triples, let shellframe_app drive the loop:

source /path/to/shellframe/shellframe.sh

_app_ROOT_type()    { printf 'confirm'; }
_app_ROOT_render()  { _SHELLFRAME_APP_QUESTION="Continue?"; }
_app_ROOT_yes()     { _SHELLFRAME_APP_NEXT="DONE"; }
_app_ROOT_no()      { _SHELLFRAME_APP_NEXT="__QUIT__"; }

_app_DONE_type()    { printf 'alert'; }
_app_DONE_render()  { _SHELLFRAME_APP_TITLE="All done."; }
_app_DONE_dismiss() { _SHELLFRAME_APP_NEXT="__QUIT__"; }

shellframe_app "_app" "ROOT"

See examples/list-select.sh for a complete working interactive list selector.


API

Module Provides
src/screen.sh Alternate screen, cursor, raw mode, stty, framebuffer (shellframe_fb_*, shellframe_screen_flush)
src/input.sh shellframe_read_key, SHELLFRAME_KEY_* constants (arrows, Tab, Shift-Tab, Ctrl, F1–F12, modifier+arrow, mouse SGR)
src/draw.sh shellframe_pad_left, color constants
src/clip.sh shellframe_str_clip, shellframe_str_clip_ellipsis, shellframe_str_pad, shellframe_str_len — ANSI-aware
src/selection.sh Cursor + multi-select state model (shellframe_sel_*)
src/keymap.sh Key name lookup (shellframe_keyname), named action keymaps (shellframe_keymap_bind/lookup)
src/cursor.sh Text cursor model — insert, delete, move, word-jump, kill ops (shellframe_cur_*)
src/text.sh v2 text primitive — render, align (left/center/right), word-wrap, size
src/scroll.sh V+H scroll state — move, resize, ensure_row/col, multi-context
src/panel.sh v2 bordered box — single/double/rounded/none styles, title, focus highlight
src/split.sh v2 horizontal/vertical split container — fixed or proportional panes
src/hitbox.sh Widget hit-test registry — shellframe_widget_register/at/clear for mouse routing
src/diff.sh Diff parsing — unified diff → structured line objects
src/shell.sh shellframe_shell — v2 composable multi-pane runtime (region layout, Tab focus, key/mouse dispatch)
src/sheet.sh Sheet navigation primitive — partial overlay with frozen back-strip, wizard transitions, shellframe_sheet_push/pop
src/app.sh shellframe_app — declarative multi-screen FSM runtime (v1 widgets)
src/widgets/tab-bar.sh v2 horizontal tab bar — reverse-video active tab, left/right arrow nav
src/widgets/input-field.sh v2 single-line text input — cursor.sh backed, all edit keys, placeholder, mask mode
src/widgets/list.sh v2 scrollable selectable list — selection.sh + scroll.sh, optional multiselect
src/widgets/modal.sh v2 modal/dialog overlay — centered panel, message, optional input field, button row
src/widgets/tree.sh v2 scrollable tree — expand/collapse, keyboard navigation, pre-order parallel arrays
src/widgets/editor.sh v2 multiline text editor — soft word-wrap, no-wrap mode, bracketed paste, click-to-position
src/widgets/grid.sh v2 data grid — sticky header, column separators, PK boundary marker, V+H scroll
src/widgets/menu-bar.sh v2 horizontal menu bar — dropdown + submenu, SHELLFRAME_MENUBAR_RESULT
src/widgets/form.sh v2 multi-field form — Tab traversal, scroll, shellframe_form_render/on_key/values
src/widgets/toast.sh Flash/toast overlay — TTL-based auto-dismiss, theme-overridable colors
src/widgets/autocomplete.sh Autocomplete overlay for input-field and editor — provider callback, auto/tab trigger
src/widgets/scrollbar.sh Proportional scrollbar — auto-hide when content fits, / track
src/widgets/context-menu.sh Floating context menu — border, keyboard nav, scroll, auto-positioning
src/widgets/diff-view.sh Side-by-side diff viewer — unified diff input, syntax highlighting, line-level navigation
src/widgets/action-list.sh v1 full-screen interactive action list
src/widgets/table.sh v1 full-page navigable table with headers, page chrome, scroll
src/widgets/confirm.sh v1 modal yes/no dialog
src/widgets/alert.sh v1 modal informational dismiss dialog

Full API reference


Going deeper

  • Widget showcase — visual gallery: ASCII art + code for every widget
  • API reference — every function, global, and callback signature
  • TUI skeletons — copy-paste starting points for apps and custom widgets
  • Hard-won lessons — 9 bash TUI pitfalls and how to avoid them
  • CLAUDE.md — development guidelines and coding conventions

File layout

shellframe/
├── shellframe.sh          # entry point — source this
├── src/
│   ├── screen.sh          # alternate screen, cursor, stty, framebuffer
│   ├── input.sh           # key reading + SHELLFRAME_KEY_* constants + mouse SGR
│   ├── draw.sh            # shellframe_pad_left, color constants
│   ├── clip.sh            # ANSI-aware string measurement and clipping
│   ├── selection.sh       # cursor + multi-select state model (shellframe_sel_*)
│   ├── keymap.sh          # key name lookup + named action keymaps
│   ├── cursor.sh          # text cursor model for input fields (shellframe_cur_*)
│   ├── text.sh            # v2 text primitive — render, align, wrap
│   ├── scroll.sh          # v2 scroll state — V+H, ensure_row/col
│   ├── panel.sh           # v2 bordered box — styles, title, focus
│   ├── split.sh           # v2 split container — horizontal/vertical panes
│   ├── hitbox.sh          # widget hit-test registry for mouse routing
│   ├── diff.sh            # unified diff parser
│   ├── shell.sh           # shellframe_shell — v2 composable multi-pane runtime
│   ├── sheet.sh           # sheet navigation — partial overlay, wizard transitions
│   ├── app.sh             # shellframe_app — declarative screen FSM runtime (v1)
│   └── widgets/
│       ├── tab-bar.sh     # v2 horizontal tab bar
│       ├── input-field.sh # v2 single-line text input
│       ├── list.sh        # v2 scrollable selectable list
│       ├── modal.sh       # v2 modal/dialog overlay
│       ├── tree.sh        # v2 scrollable tree with expand/collapse
│       ├── editor.sh      # v2 multiline text editor (wrap + no-wrap)
│       ├── grid.sh        # v2 data grid with sticky header + V/H scroll
│       ├── menu-bar.sh    # v2 horizontal menu bar + dropdown + submenu
│       ├── form.sh        # v2 multi-field form with Tab traversal
│       ├── toast.sh       # flash/toast overlay with TTL auto-dismiss
│       ├── autocomplete.sh # autocomplete overlay for input-field + editor
│       ├── scrollbar.sh   # proportional scrollbar widget
│       ├── context-menu.sh # floating context menu with auto-positioning
│       ├── diff-view.sh   # side-by-side diff viewer
│       ├── action-list.sh # v1 interactive action-list widget
│       ├── table.sh       # v1 full-page navigable table widget
│       ├── confirm.sh     # v1 modal yes/no confirmation dialog
│       └── alert.sh       # v1 modal informational dialog (dismiss-only)
├── docs/
│   ├── api.md             # API reference
│   ├── showcase.md        # visual gallery: ASCII art + code for every widget
│   ├── skeletons.md       # copy-paste TUI skeletons
│   └── hard-won-lessons.md # bash TUI pitfalls
├── examples/
│   ├── list-select.sh     # single-select list demo
│   ├── action-list.sh     # action-list widget demo
│   ├── confirm.sh         # confirm modal demo
│   ├── alert.sh           # alert modal demo
│   ├── modal.sh           # modal prompt demo
│   ├── autocomplete.sh    # autocomplete overlay demo
│   └── sheet.sh           # two-step wizard using sheet navigation
└── tests/
    ├── docker/            # cross-version portability matrix (bash 3.2, 4.4, 5.x)
    ├── unit/
    └── integration/

Portability

The key known portability difference is bash 3.2 (macOS default): no decimal -t timeouts, no {varname} fd allocation, and subtly different read behavior. See Hard-won lessons for details.

To test against multiple bash versions locally:

bash tests/docker/run-matrix.sh             # bash 3.2, 4.4, 5.x
bash tests/docker/run-matrix.sh --no-cache  # force clean rebuild

About

Bash TUI primitives: alternate screen, raw input, ANSI-aware column padding

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages