# 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: - unzip -l - read -rp "Press Enter to continue..." move: command: mv -v ``` Rules are evaluated from top to bottom; the first matching glob wins. ### Substitutions | Token | Meaning | |---|---| | `` | basename of the target | | `` | full path of the target | | `` | 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. For example: ```yaml actions: list-zip: command: - unzip -l - read -rp "Press Enter to continue..." ``` The flow is: ```text TUI ↓ unzip -l archive.zip ↓ read -rp "Press Enter..." ↓ TUI ``` TUI is **not redrawn between commands belonging to the same action**. Every command is also inserted into Bash history. If one command fails, the remaining commands in that action are not executed. TUI prints the exit code and waits for: ```text Press Enter to continue... ``` before returning to TUI, so an error cannot disappear immediately. The shell wrapper uses `pushd`/`popd` so the command runs in the directory shown by TUI without permanently changing the user's Bash working directory. ## 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`. ### Version Use `tui -v` or `tui --version` to verify the installed TUI version. ## v3 menu Press `M` to open the action menu. The menu is generated entirely from the current `.tui` file, so a directory can provide its own context-specific operations. The example configuration defines ordinary file operations and Git operations that appear when the current directory is inside a Git worktree. The top-level keys are also configurable in `.tui`: ```yaml keys: a: toggle-all q: quit m: menu ``` Menu entries support: ```yaml menu: - label: "Git: pull" key: p action: git-pull selection: none when: git ``` `selection` can be `auto`, `current`, `marked` or `none`. Available generic `when` conditions include `always`, `git`, `not-git`, `file`, `folder`, `marked`, `multiple`, `exists:`, `missing:`, `glob:`, plus `all`, `any` and `not` expressions. Actions that operate on the current directory rather than a file can use: ```yaml actions: git-pull: target: none command: git pull ``` This keeps the Python code generic: menu presentation, labels, shortcuts, conditions and shell commands belong in `.tui`. ## Floating action menu Press `M` to open the configured action menu. The menu is rendered as a floating overlay over the browser. It is positioned above or below the current cursor when possible, so the selected file remains visible. Menu labels, shortcuts, actions, and context conditions are defined in `.tui`; the Python UI engine does not contain Git-specific menu definitions. All user-visible TUI text is English.