This commit is contained in:
Joakim
2026-10-12 22:54:45 +02:00
parent 972e941674
commit 4227383e5c
5 changed files with 69 additions and 53 deletions

View File

@@ -1,3 +1,9 @@
## v2 - latest fix
- Fixed `Ctrl+C` exit handling so the Bash wrapper reliably changes to the directory where TUI was closed, including when Bash `set -e` is enabled.
- Action commands are evaluated as complete Bash commands, so `&&` and `||` chains work as they do directly in the terminal.
- Updated the example ZIP action to demonstrate success/failure prompts with `&&` / `||`.
# Changelog # Changelog
## v2.1 ## v2.1

View File

@@ -94,8 +94,7 @@ keys:
actions: actions:
list-zip: list-zip:
command: command:
- unzip -l <path> command: 'unzip -l <path> && read -rp "Enter to continue.." || read -rp "FAILED - Enter to continue..."'
- read -rp "Tryck Enter för att fortsätta..."
move: move:
command: mv -v <path> <dir> command: mv -v <path> <dir>
@@ -165,44 +164,42 @@ not clear the already marked paths.
When an action is triggered, TUI writes the generated commands to a temporary 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 file and exits with a private handoff status. The `tui()` Bash function then
executes them. executes them in the parent Bash process.
For example: 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 ```yaml
actions: actions:
list-zip: 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:
- unzip -l <path> - command-one <path>
- read -rp "Tryck Enter för att fortsätta..." - command-two <path>
``` ```
The flow is: 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.
```text The shell wrapper uses `pushd`/`popd` so action commands run in the directory
TUI shown by TUI without permanently changing the user's Bash directory during
↓ the action.
unzip -l archive.zip
↓
read -rp "Tryck 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
Tryck Enter för att fortsätta...
```
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 ## Security note

View File

@@ -29,21 +29,19 @@ keys:
- "*": move - "*": move
y: y:
file: file:
- "*": delete-file - "*": delete
folder: folder:
- "*": delete-folder - "*": delete
actions: actions:
list-zip: list-zip:
command: unzip -l <path> && read -rp "Enter to continue.." || read -rp "FAILED - Enter to continue..." command: 'unzip -l <path> && read -rp "Enter to continue.." || read -rp "FAILED - Enter to continue..."'
copy: copy:
command: cp -v <path> <dir>copy_<name> command: cp -v <path> <dir>copy_<name>
move: move:
command: mv -v <path> <dir> command: mv -v <path> <dir>
delete-file: delete:
command: rm -v <path> command: rm -v <path>
delete-folder:
command: rmdir -v <path> || read -rp "FAILED - Enter to continue..."
edit: edit:
command: joe <path> command: joe <path>
view: view:

View File

@@ -33,16 +33,26 @@ tui() {
: > "$command_file" : > "$command_file"
: > "$cwd_file" : > "$cwd_file"
# Use an if/else wrapper so this remains safe when the caller has
# `set -e` enabled. A Ctrl+C from the child must still return control
# here so we can cd to the TUI's final directory.
if (( show_all )); then if (( show_all )); then
TUI_COMMAND_FILE="$command_file" \ if TUI_COMMAND_FILE="$command_file" \
TUI_CWD_FILE="$cwd_file" \ TUI_CWD_FILE="$cwd_file" \
python3 "$tui_python" -a "$tui_dir" python3 "$tui_python" -a "$tui_dir"; then
rc=0
else else
TUI_COMMAND_FILE="$command_file" \
TUI_CWD_FILE="$cwd_file" \
python3 "$tui_python" "$tui_dir"
fi
rc=$? rc=$?
fi
else
if TUI_COMMAND_FILE="$command_file" \
TUI_CWD_FILE="$cwd_file" \
python3 "$tui_python" "$tui_dir"; then
rc=0
else
rc=$?
fi
fi
tui_cwd="$(<"$cwd_file")" tui_cwd="$(<"$cwd_file")"
@@ -56,16 +66,18 @@ tui() {
return 1 return 1
fi fi
# A normal TUI exit means the user wants to leave TUI. Change the # Any exit other than SHELL_HANDOFF means TUI is finished. Always
# parent Bash shell to the directory where TUI was actually closed. # change the parent Bash shell to the directory where TUI was
# actually closed. This includes Ctrl+C (status 130).
if (( rc != 42 )); then if (( rc != 42 )); then
if (( rc == 0 )); then
cd "$tui_cwd" || { cd "$tui_cwd" || {
rm -f "$command_file" "$cwd_file" rm -f "$command_file" "$cwd_file"
return 1 return 1
} }
fi
rm -f "$command_file" "$cwd_file" rm -f "$command_file" "$cwd_file"
if (( rc == 130 )); then
return 0
fi
return "$rc" return "$rc"
fi fi

View File

@@ -607,7 +607,10 @@ def main():
try: try:
result = curses.wrapper(run, args.directory, args.all) result = curses.wrapper(run, args.directory, args.all)
except KeyboardInterrupt: except KeyboardInterrupt:
return 0 # Keep a distinct status so the Bash wrapper can reliably distinguish
# Ctrl+C from a normal action handoff. The current directory has
# already been written by run()'s finally block.
return 130
return SHELL_HANDOFF if result == SHELL_HANDOFF else 0 return SHELL_HANDOFF if result == SHELL_HANDOFF else 0