Files
tui/README.md
Joakim 4227383e5c .tui
2026-10-12 22:54:45 +02:00

233 lines
5.9 KiB
Markdown

# TUI
A small keyboard-driven terminal UI for browsing directories and executing
ordinary shell commands defined in `.tui` files.
The important design choice is that **TUI does not execute actions inside a
Python subprocess**. It hands the generated commands back to the Bash process
that started `tui`. This keeps the commands visible in Bash history and gives
actions the normal shell environment.
## Requirements
- Debian/Linux with Bash
- Python 3
- PyYAML (`python3-yaml` on Debian)
- `curses` from the normal Python installation
Install the Debian dependency if necessary:
```bash
sudo apt install python3-yaml
```
## Installation
Unpack the project and run:
```bash
./install.sh
```
The installer installs TUI under:
```text
~/.local/lib/tui/
```
and adds the shell integration to:
```text
~/.bashrc
```
The integration is therefore loaded automatically for interactive Bash
sessions, including the normal Debian login/terminal startup path.
After installation, start a new terminal/login session, or run:
```bash
source ~/.bashrc
```
Then:
```bash
tui
```
Use `-a` to include hidden files and directories:
```bash
tui -a
```
The installer does not overwrite an existing `~/.tui`.
`-a` / `--all` is a command-line option, so the shell integration preserves it
when the TUI restarts after an action.
## `.tui` configuration
TUI searches for the nearest `.tui` starting in the current directory and
walking upwards, but never above `$HOME`.
A configuration contains `keys` and `actions`:
```yaml
keys:
up: up
down: down
left: parent
right:
folder: enter
file:
- "*.zip": list-zip
ctrl-k:
b: mark-begin
k: mark-end
m:
file:
- "*": move
actions:
list-zip:
command:
command: 'unzip -l <path> && read -rp "Enter to continue.." || read -rp "FAILED - Enter to continue..."'
move:
command: mv -v <path> <dir>
```
Rules are evaluated from top to bottom; the first matching glob wins.
### Substitutions
| Token | Meaning |
|---|---|
| `<name>` | basename of the target |
| `<path>` | full path of the target |
| `<dir>` | directory currently displayed by TUI, with trailing `/` |
## Navigation
The default example configuration uses:
- `↑` / `↓` — move cursor
- `Enter` / `→` — execute the configured action
- `../` — synthetic parent-directory entry at the top of every listing
- `←` — move the cursor to `../`; pressing `←` again enters the parent directory
- `Enter` on `../` — enter the parent directory
- navigation cannot move above the directory containing the applicable `.tui` file
- hidden files and directories are hidden by default; `tui -a` shows them
- file and directory colors follow the usual GNU `ls --color=yes` conventions for common file types
- `Ctrl-K B` — set selection start
- `Ctrl-K K` — set selection end
- `Ctrl-K C` — copy marked files/folders
- `Ctrl-K M` — move marked files
- `Ctrl-K Y` — delete marked files/folders
- `Ctrl-C` — quit
## Marking
Marks are stored as absolute paths and belong to the TUI session, not to the
current directory.
For example:
```text
/home/joakim/notes/
A.txt *
B.txt *
work/
```
Entering `work/` does not clear the marks. Returning to the parent directory
shows `A.txt` and `B.txt` as marked again.
Marks in different directories can coexist. If `A.txt` is marked, then TUI
is moved to `work/`, `Ctrl-K M` operates on the original absolute path while
using the **current directory** as the destination:
```bash
mv -v /home/joakim/notes/A.txt .
```
A mark is removed automatically only when its target no longer exists.
`Ctrl-K B` and `Ctrl-K K` define a contiguous selection in the directory being
viewed. Changing directory clears the temporary selection endpoints but does
not clear the already marked paths.
## Shell integration
When an action is triggered, TUI writes the generated commands to a temporary
file and exits with a private handoff status. The `tui()` Bash function then
executes them in the parent Bash process.
When TUI is closed with `Ctrl+C`, the wrapper changes the parent Bash shell to
the directory where TUI was actually closed.
Action commands are handed to Bash as complete commands, so normal shell
operators such as `&&` and `||` work exactly as they do at the terminal. For
example:
```yaml
actions:
list-zip:
command: 'unzip -l <path> && read -rp "Enter to continue.." || read -rp "FAILED - Enter to continue..."'
```
The complete command is evaluated once. If `unzip` succeeds, the success
`read` is executed. If `unzip` fails, the failure `read` is executed.
For actions containing several separate commands, use a YAML list:
```yaml
actions:
example:
command:
- command-one <path>
- command-two <path>
```
TUI is **not redrawn between commands belonging to the same action**. Each
command is inserted into Bash history. If a separate command fails, the
remaining commands in that action are not executed; TUI prints the exit code
and waits before returning to TUI.
The shell wrapper uses `pushd`/`popd` so action commands run in the directory
shown by TUI without permanently changing the user's Bash directory during
the action.
## Security note
Action strings are intentionally ordinary Bash commands and are executed with
`eval` by the user's Bash shell. `.tui` files should therefore be treated as
executable configuration and only used from directories/configurations you
trust.
## Development
Run a syntax check with:
```bash
python3 -m py_compile tui/tui.py
```
For development without installing:
```bash
source tui/tui-shell.bash
tui
```
The example configuration is in `tui/example.tui`.
### Cursor and block selection
There is an empty cursor position immediately below the last object in a directory. This makes range selection behave like a text editor.
`Ctrl+K K` follows joe-style semantics: the end of the selection is the row immediately above the cursor. Thus, when the cursor is on the empty line below the final object, `Ctrl+K K` includes the final object.