SourceDialog is a pure Bash TUI dialog library you can source from your own scripts to create interactive terminal forms — no external dependencies (dialog, whiptail, ncurses, etc.).
- Pure Bash — single file, no dependencies, just
sourceit - No ncurses, no
dialog, nowhiptail— uses only Bash builtins and raw ANSI/DEC escape sequences (clears the screen via\e[H\e[2J\e[3J, not/usr/bin/clear) - All standard widgets — canvas, frame, textbox, pushbutton, inputbox, passwordbox, menubox, checklist, radiolist
- Unicode rendering — rounded box-drawing characters, modern markers, scroll indicators (auto-detected, falls back to legacy ACS)
- 256-color support — auto-detected, smoother shadows and custom themes
- Robust terminal handling — traps every fatal signal (
INT,TERM,HUP,QUIT,PIPE) plusEXIT, so the terminal is always restored even when a callback throws or the process is killed mid-dialog - TTY-aware — refuses to enter raw mode if stdin isn't a terminal
(returns
127), so piping into a script never wedges the shell - Customizable — override any
_SD_*color/style variable before callingsd_start - Full API compatibility — drop-in upgrade for the original SourceDialog
- Bash 4.3+
Install by sourcing — either clone the repo or fetch directly via jsDelivr (follows the latest release, HTTPS-served, CDN-backed):
# Latest release (auto-updates):
source <(curl -sSL https://cdn.jsdelivr.net/gh/robindubreuil/sourcedialog@latest/sourcedialog)
# Or pin a specific version for reproducibility:
source <(curl -sSL https://cdn.jsdelivr.net/gh/robindubreuil/sourcedialog@1.1.1/sourcedialog)Or clone and source locally:
source /path/to/sourcedialogThen:
myvar=""
sd_load_canvas name=cv1 x=10 y=2 width=40 height=8 caption=" MY FORM "
sd_load_textbox name=tb1 x=13 y=4 width=34 text="Enter your name:"
sd_load_inputbox name=ib1 x=13 y=6 width=34 varname=myvar
sd_load_pushbutton name=ok x=20 y=8 caption=" OK "
sd_load_pushbutton name=cc x=30 y=8 caption=" Cancel "
sd_ok_push() { return 0; }
sd_cc_push() { return 1; }
sd_start| Function | Description |
|---|---|
sd_load_canvas |
Frame/decoration |
sd_load_frame |
Concave frame (no shadow) |
sd_load_textbox |
Static text |
sd_load_pushbutton |
Button |
sd_load_inputbox |
Text input |
sd_load_passwordbox |
Password input |
sd_load_menubox |
Selection list |
sd_load_checklist |
Multi-select list |
sd_load_radiolist |
Single-select list |
| Function | Description |
|---|---|
sd_start |
Init, draw, read, reset (main entry point) |
sd_init |
Set up terminal (hide cursor, raw mode) |
sd_draw |
Render all registered widgets |
sd_read |
Interactive input loop |
sd_reset |
Restore terminal |
sd_clear |
Unregister all widgets (for multi-page dialogs) |
sd_version |
Print the loaded library version (e.g. 1.1.1) |
The current version is also available as the readonly variable
SD_VERSION immediately after sourcing — useful for feature-detection:
source ./sourcedialog
if [[ ${SD_VERSION:-0} < 1.1 ]]; then
echo "sourcedialog >= 1.1 required (have ${SD_VERSION:-unknown})" >&2
exit 1
fi| Code | Meaning |
|---|---|
0 |
OK / confirm |
1 |
Cancel |
27 |
Escape |
127 |
Not interactive |
254 |
Move backward |
255 |
Move forward |
Set any of these before sd_start to customize appearance:
Colors (values: 0–7 for 8-color, 0–255 for 256-color terminals):
_SD_BG, _SD_FG, _SD_FRAME_BG, _SD_FRAME_HI, _SD_FRAME_LO,
_SD_TEXT_FG, _SD_TEXT_BG, _SD_BTN_FG, _SD_BTN_BG, _SD_BTN_KEY,
_SD_BTN_SEL_FG, _SD_BTN_SEL_BG, _SD_INPUT_FG, _SD_INPUT_BG,
_SD_INPUT_SEL_FG, _SD_INPUT_SEL_BG, _SD_LIST_FG, _SD_LIST_BG,
_SD_LIST_KEY, _SD_LIST_SEL_FG, _SD_LIST_SEL_BG, _SD_SHADOW_BG
Style:
| Variable | Default | Values |
|---|---|---|
_SD_STYLE |
auto |
auto, unicode, legacy |
_SD_CORNER |
rounded |
rounded, square (unicode only) |
_SD_256COLOR |
auto |
auto, yes, no |
| Key | Action |
|---|---|
| Arrow keys | Navigate items (list) |
| Space | Toggle selection (list) |
| Tab / Enter | Move forward |
| Shift+Tab | Move backward |
| Home / End | First / last item |
| Page Up / Down | Scroll page |
| Escape | Cancel |
| Backspace | Delete character (input) |
| Any letter | Jump to matching item (list) |
| Variable | Default | Meaning |
|---|---|---|
SD_FORCE_TTY |
0 |
Treat stdin as a TTY even when it isn't (useful for tests, snapshots, and piping pre-recorded input). When unset/0, sd_init returns 127 for non-TTY stdin. |
SD_DEBUG |
unset | When non-empty, widget read-loops emit their stderr instead of suppressing it. |
TERM |
(inherited) | Selects xterm vs linux-console capability tables. |
LANG, LC_CTYPE, LC_ALL |
(inherited) | Locale, in standard precedence (LC_ALL wins). A UTF-8 locale selects the Unicode renderer; otherwise the legacy ACS renderer is used. |
- Terminal is always restored.
sd_startinstalls traps onINT,TERM,HUP,QUIT,PIPE, andEXIT, captures any prior handlers you had installed, and restores them on return. If a callback callsexit, the cursor/echo/stty state is still cleaned up before the process terminates. - No busy-loop on EOF. If stdin closes mid-dialog, every widget
read-loop returns
1instead of spinning onread. - No glob expansion on quick-jump. Letters typed into a listbox are
matched literally — typing
*no longer jumps to "anything". - Renderer falls back gracefully. Pre-populated values longer than their inputbox are scrolled to their tail instead of overrunning neighbouring widgets.
The widget loaders distinguish forced identity props from soft defaults:
- Forced (
type=, andpassword=yesonsd_load_passwordbox) — cannot be overridden by the caller. A canvas stays a canvas. - Soft (
shadow=,frame=,mark=,multi=) — applied only when the caller did not pass the same prop. Sosd_load_frame name=fr1 shadow=yeshonours yourshadow=yes.
bash examples/demo # multi-page dialog with all widgets
bash examples/example1 # OS chooser
bash examples/example2 # address form
bash examples/example3 # account creationmake test # run the unit suite (419 assertions, no TTY needed)
make lint # shellcheck the library, tests, examples, tutorials
make pty-smoke # real-PTY byte-level smoke test (civis/cnorm/smkx/SGR)
make examples-smoke # run every example/tutorial under SD_FORCE_TTY=1
make check # test + lint (the CI contract)
make all # check + pty-smoke + examples-smokeCI runs the unit suite against Bash 4.3 / 4.4 / 5.0 / 5.1 / 5.2 in Docker, plus a macOS job that verifies the version guard rejects Bash 3.2. See CHANGELOG.md for release history.
Copyright (c) 2025 Robin Dubreuil 63008968+robindubreuil@users.noreply.github.com Licensed under the GNU General Public License v3. See LICENSE.
Inspired by SourceDialog by Antonio Macchi (2008), originally posted to
comp.os.linux.development.apps on 2008-11-07. This is a complete rewrite
aiming to be a drop-in upgrade with full API compatibility.