From 30dfcd3f79d93cc7a512b1b5a3a1059a17f215cf Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 12:35:07 +0200 Subject: [PATCH 01/13] update documentation --- skills/terminal-ui-polisher/SKILL.md | 2 + .../assets/box-templates.md | 96 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 skills/terminal-ui-polisher/assets/box-templates.md diff --git a/skills/terminal-ui-polisher/SKILL.md b/skills/terminal-ui-polisher/SKILL.md index 11a79d1..9c37f15 100644 --- a/skills/terminal-ui-polisher/SKILL.md +++ b/skills/terminal-ui-polisher/SKILL.md @@ -160,6 +160,7 @@ menu family. Reference files: +- `skills/terminal-ui-polisher/assets/box-templates.md` contains reusable box templates; use it before writing manual box math. - `terminal/menus/mq-hal-menu.sh` is the clearest submenu pattern. - `terminal/menus/mq-performance-menu.sh` shows status-driven panels. - `terminal/menus/mq-main-menu.sh` shows the main command surface. @@ -189,6 +190,7 @@ Layout rules: - Text must fit at 60 columns because `surface_terminal_width` clamps there. - If a dependency is missing, render a panel explaining the missing binary and the exact check command. - Menus should work sourced from mqlaunch and directly as scripts. +- For legacy launchers with local frame helpers, copy the width math from `assets/box-templates.md`; do not draw nested two-column boxes. When reviewing a proposed menu, reject it if it: diff --git a/skills/terminal-ui-polisher/assets/box-templates.md b/skills/terminal-ui-polisher/assets/box-templates.md new file mode 100644 index 0000000..b9c1d6e --- /dev/null +++ b/skills/terminal-ui-polisher/assets/box-templates.md @@ -0,0 +1,96 @@ +# Terminal Box Templates + +Use these templates when creating or repairing mqlaunch terminal boxes. + +The goal is to avoid one-off width math. Prefer shared `surface_*` helpers for +new menus. Use the `frame_*` template only when maintaining legacy launchers +that already own their own frame helpers, such as `terminal/launchers/gitlaunch.sh`. + +## mqlaunch Surface Menu + +Use this for new menu modules under `terminal/menus/`. + +```bash +render_example_panel() { + local width panel_color + width="$(surface_terminal_width)" + panel_color="$(surface_panel_color)" + + surface_panel_header "Example" "Mode" "$width" "$panel_color" + surface_row "SECTION" "$width" "$panel_color" + surface_split_row "1. First action" "2. Second action" "$width" "$panel_color" + surface_split_row "3. Third action" "4. Fourth action" "$width" "$panel_color" + surface_row "" "$width" "$panel_color" + surface_split_row "b. Back" "x. Exit launcher" "$width" "$panel_color" + surface_row "" "$width" "$panel_color" + surface_row "Status: ready" "$width" "$panel_color" + surface_bottom "$width" "$panel_color" +} +``` + +Rules: + +- Use `surface_terminal_width`; never hardcode box width. +- Use `surface_split_row`; do not draw a manual center divider. +- Keep section labels short uppercase nouns. +- Keep every option label short enough to fit at the 60-column clamp. +- Keep border color from `surface_panel_color`. + +## Legacy Frame Helpers + +Use this only for standalone launchers that cannot easily source `mq-ui.sh`. +This layout keeps every row exactly the same visible width as the top and bottom +borders. + +```bash +UI_WIDTH=88 +UI_INNER=$((UI_WIDTH - 4)) + +frame_two_col() { + local left right left_width right_width + left_width=$((UI_INNER / 2)) + right_width=$((UI_INNER - left_width - 1)) + left=$(truncate_text "$1" "$left_width") + right=$(truncate_text "$2" "$right_width") + printf "%b│%b %-${left_width}s %-${right_width}s %b│%b\n" \ + "$C_BORDER" "$C_RESET" "$left" "$right" "$C_BORDER" "$C_RESET" +} + +fallback_status_row() { + local label="$1" + local value="$2" + local color="${3:-}" + local label_width=8 + local value_width + update_ui_width + value_width=$((UI_INNER - 10)) + (( value_width < 1 )) && value_width=1 + value=$(truncate_text "$value" "$value_width") + printf "%b│%b %b%-${label_width}s%b: %b%-${value_width}s%b %b│%b\n" \ + "$C_BORDER" "$C_RESET" "$C_TITLE" "$label" "$C_RESET" "$color" "$value" "$C_RESET" "$C_BORDER" "$C_RESET" +} +``` + +Rules: + +- `UI_INNER` is `UI_WIDTH - 4` because each row has border, space, text, space, border. +- Two-column rows should contain one outer box, not two nested boxes. +- `left_width + 1 + right_width` must equal `UI_INNER`. +- For status rows, `label_width + colon + space + value_width` must equal `UI_INNER`. +- If the prompt accepts `b`, the helper text must say so. +- In non-interactive tests, EOF should exit/back instead of looping on `Invalid`. + +## Visual Target + +```text +┌─ Gitlaunch ──────────────────────────────────────────────────────────────────┐ +│ Host: Zephyr User: mansys Repo: macos-scripts Branch: main │ +│ Git: Clean Staged: 0 Unstaged: 0 Untracked: 0 │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ 1. Git status 2. Pull │ +│ 3. Suggest commit 4. Safe push │ +│ 5. Open repo 6. Dev mode │ +│ 7. Switch repo 8. Auto commit + push │ +│ 9. Recent log b. Back │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` From 70f407a56eeeef723c2124c7c382e4738942ff46 Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 12:43:27 +0200 Subject: [PATCH 02/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 133532d..d20f86c 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -556,22 +556,11 @@ function prompt_choice() { printf "%bgitlaunch > %b\n" "$C_TITLE" "$C_RESET" printf "%b%s%b\n" "$C_BORDER" "$prompt_sep" "$C_RESET" printf "%b>> press 1-9 or b%b\n" "$C_DIM" "$C_RESET" - if [[ -t 0 && -t 1 ]]; then - printf "\033[3A" - printf "%bgitlaunch > %b" "$C_TITLE" "$C_RESET" - fi - input="" - if [[ -t 0 ]]; then - read -rsk 1 input || input="b" - else - IFS= read -r input || input="b" - fi + IFS= read -r input || input="b" + input="${input[1,1]}" printf "%s\n" "$input" - if [[ -t 0 && -t 1 ]]; then - printf "\033[2B" - fi choice="$input" } From ef8859e54936b87cf204e86aee8c5c92498435fc Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 12:50:19 +0200 Subject: [PATCH 03/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 45 ++++++++++++--------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index d20f86c..e39fa27 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -63,24 +63,16 @@ UNTRACKED_COUNT=0 # ASCII ART # ------------------------ function render_ascii() { - local pulse line - local -a pulses dark_lines amber_lines - - dark_lines=( - " ▄████ ██▓▄▄▄█████▓" - " ██▒ ▀█▒▓██▒▓ ██▒ ▓▒" - " ▒██░▄▄▄░▒██▒▒ ▓██░ ▒░" - " ░▓█ ██▓░██░░ ▓██▓ ░ " - " ░▒▓███▀▒░██░ ▒██▒ ░ " - " ░▒ ▒ ░▓ ▒ ░░ " - ) - amber_lines=( - " ██▓ ▄▄▄ █ ██ ███▄ █ ▄████▄ ██░ ██ " - "▓██▒ ▒████▄ ██ ▓██▒ ██ ▀█ █ ▒██▀ ▀█ ▓██░ ██▒" - "▒██░ ▒██ ▀█▄ ▓██ ▒██░▓██ ▀█ ██▒▒▓█ ▄ ▒██▀▀██░" - "▒██░ ░██▄▄▄▄██ ▓▓█ ░██░▓██▒ ▐▌██▒▒▓▓▄ ▄██▒░▓█ ░██ " - "░██████▒▓█ ▓██▒▒▒█████▓ ▒██░ ▓██░▒ ▓███▀ ░░▓█▒░██▓" - "░ ▒░▓ ░▒▒ ▓▒█░░▒▓▒ ▒ ▒ ░ ▒░ ▒ ▒ ░ ░▒ ▒ ░ ▒ ░░▒░▒" + local line + local -a pulses figure + + figure=( + " ████████ " + " ██ ██ " + " ████████ " + " ████████████ " + " ██ ██ " + " ████ ████ " ) if [[ "$_BANNER_SHOWN" -eq 0 ]]; then @@ -95,21 +87,16 @@ function render_ascii() { sleep 0.05 done printf "\033[2K" - printf "%b" "$C_TITLE" - for line in "${dark_lines[@]}"; do - printf '%s\n' "$line" - sleep 0.03 - done printf "%b" "$C_AMBER" - for line in "${amber_lines[@]}"; do - printf '%s\n' "$line" - sleep 0.03 + for line in "${figure[@]}"; do + printf ' %s\n' "$line" + sleep 0.06 done else - printf "%b" "$C_TITLE" - for line in "${dark_lines[@]}"; do printf '%s\n' "$line"; done printf "%b" "$C_AMBER" - for line in "${amber_lines[@]}"; do printf '%s\n' "$line"; done + for line in "${figure[@]}"; do + printf ' %s\n' "$line" + done fi printf "%b" "$C_RESET" From 01495b19bef5a2f87c1683cd4deb324d23bf00d6 Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 12:52:27 +0200 Subject: [PATCH 04/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index e39fa27..3485ad9 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -540,14 +540,14 @@ function prompt_choice() { prompt_sep="$(repeat_char "─" "$UI_WIDTH")" printf "%b%s%b\n" "$C_BORDER" "$prompt_sep" "$C_RESET" - printf "%bgitlaunch > %b\n" "$C_TITLE" "$C_RESET" - printf "%b%s%b\n" "$C_BORDER" "$prompt_sep" "$C_RESET" - printf "%b>> press 1-9 or b%b\n" "$C_DIM" "$C_RESET" + printf "%bgitlaunch%b %b[1-9,b]%b > " "$C_TITLE" "$C_RESET" "$C_DIM" "$C_RESET" input="" IFS= read -r input || input="b" input="${input[1,1]}" - printf "%s\n" "$input" + if [[ ! -t 0 ]]; then + printf "%s\n" "$input" + fi choice="$input" } From 98a6f8a79c5c3ba11856ba28fbf62920cc1cac23 Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 13:15:37 +0200 Subject: [PATCH 05/13] Fix gitlaunch prompt to match mqlaunch menu style Co-Authored-By: Claude Sonnet 4.6 --- terminal/launchers/gitlaunch.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 3485ad9..f080149 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -535,20 +535,27 @@ function render_next_action() { # Prompts for choice with script-level validation. function prompt_choice() { - local prompt_sep input + local sep input update_ui_width - prompt_sep="$(repeat_char "─" "$UI_WIDTH")" + sep="$(repeat_char "─" "$UI_WIDTH")" + + printf "\n%b%s%b\n" "$C_BORDER" "$sep" "$C_RESET" + printf "%bgitlaunch > %b\n" "$C_TITLE" "$C_RESET" + printf "%b%s%b\n" "$C_BORDER" "$sep" "$C_RESET" + printf "%b>> press 1-9 or b%b\n" "$C_DIM" "$C_RESET" + if [[ -t 0 && -t 1 ]]; then + printf "\033[3A\r" + printf "%bgitlaunch > %b" "$C_TITLE" "$C_RESET" + fi - printf "%b%s%b\n" "$C_BORDER" "$prompt_sep" "$C_RESET" - printf "%bgitlaunch%b %b[1-9,b]%b > " "$C_TITLE" "$C_RESET" "$C_DIM" "$C_RESET" input="" IFS= read -r input || input="b" - input="${input[1,1]}" - if [[ ! -t 0 ]]; then - printf "%s\n" "$input" + if [[ -t 0 && -t 1 ]]; then + printf "\033[2B\r\n" fi + input="${input[1,1]}" choice="$input" } From 25aa458c16fd3b21ec0df18274ad60afb9753faa Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 13:31:55 +0200 Subject: [PATCH 06/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index f080149..f82c113 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -63,8 +63,8 @@ UNTRACKED_COUNT=0 # ASCII ART # ------------------------ function render_ascii() { - local line - local -a pulses figure + local i + local -a pulses figure labels figure=( " ████████ " @@ -74,6 +74,14 @@ function render_ascii() { " ██ ██ " " ████ ████ " ) + labels=( + "" + " Gitlaunch" + "" + "" + "" + "" + ) if [[ "$_BANNER_SHOWN" -eq 0 ]]; then _BANNER_SHOWN=1 @@ -87,15 +95,13 @@ function render_ascii() { sleep 0.05 done printf "\033[2K" - printf "%b" "$C_AMBER" - for line in "${figure[@]}"; do - printf ' %s\n' "$line" + for (( i=1; i<=${#figure[@]}; i++ )); do + printf "%b %s%b%s%b\n" "$C_AMBER" "${figure[$i]}" "$C_TITLE" "${labels[$i]}" "$C_RESET" sleep 0.06 done else - printf "%b" "$C_AMBER" - for line in "${figure[@]}"; do - printf ' %s\n' "$line" + for (( i=1; i<=${#figure[@]}; i++ )); do + printf "%b %s%b%s%b\n" "$C_AMBER" "${figure[$i]}" "$C_TITLE" "${labels[$i]}" "$C_RESET" done fi From 1dcd350d1488afbd123970cbba986f64593a63b9 Mon Sep 17 00:00:00 2001 From: McAmner Date: Wed, 3 Jun 2026 13:56:32 +0200 Subject: [PATCH 07/13] Add pixel figure, fix prompt style, fix post-commit menu flow Co-Authored-By: Claude Sonnet 4.6 --- terminal/launchers/gitlaunch.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index f82c113..f551e7e 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -893,16 +893,22 @@ fi # ------------------------ trap 'printf "%b" "$C_RESET"' EXIT +_MENU_ONLY=0 + while true; do detect_repo clear_screen - status_check - next_action - render_next_action - if use_gum_menu && [ -n "$(git status --porcelain)" ]; then - analyze_diff + if [[ "$_MENU_ONLY" -eq 0 ]]; then + status_check + next_action + render_next_action + + if use_gum_menu && [ -n "$(git status --porcelain)" ]; then + analyze_diff + fi fi + _MENU_ONLY=0 echo "" render_menu @@ -920,7 +926,7 @@ while true; do ;; 3) run_ai_commit - continue + _MENU_ONLY=1 ;; 4) safe_push From 85c749cd153e5911b518dcc543956363cb66113c Mon Sep 17 00:00:00 2001 From: McAmner Date: Thu, 4 Jun 2026 20:08:54 +0200 Subject: [PATCH 08/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index f551e7e..d934c82 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -855,7 +855,6 @@ function run_ai_commit() { if [[ "$proceed" != "y" ]]; then echo "❌ Commit cancelled" - pause_git_menu return 0 fi @@ -863,9 +862,6 @@ function run_ai_commit() { if git commit -m "$SUGGESTED"; then pr_aware_push "$SUGGESTED" fi - - pause_git_menu - return 0 } # ------------------------ @@ -927,6 +923,7 @@ while true; do 3) run_ai_commit _MENU_ONLY=1 + pause_git_menu ;; 4) safe_push From 703003f0276a145eb324e11c4b90579814d9c2b4 Mon Sep 17 00:00:00 2001 From: McAmner Date: Thu, 4 Jun 2026 20:23:26 +0200 Subject: [PATCH 09/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index d934c82..25b4a17 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -768,13 +768,17 @@ function pr_aware_push() { return $? fi + local tmpfile + tmpfile="$(mktemp)" + if [[ "${#push_args[@]}" -gt 0 ]]; then - output=$(git push "${push_args[@]}" 2>&1) + git push "${push_args[@]}" 2>&1 | tee "$tmpfile"; status=${pipestatus[1]} else - output=$(git push 2>&1) + git push 2>&1 | tee "$tmpfile"; status=${pipestatus[1]} fi - status=$? - echo "$output" + + output="$(cat "$tmpfile")" + rm -f "$tmpfile" if [[ "$status" -ne 0 ]] && echo "$output" | grep -E "GH013|Changes must be made through a pull request" >/dev/null; then create_pr_branch_for_push "$branch" "$commit_message" From 50889aa5bdbc8e463f641935e6ee8ff9cd3f6889 Mon Sep 17 00:00:00 2001 From: McAmner Date: Thu, 4 Jun 2026 20:28:32 +0200 Subject: [PATCH 10/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 25b4a17..8656a61 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -893,22 +893,17 @@ fi # ------------------------ trap 'printf "%b" "$C_RESET"' EXIT -_MENU_ONLY=0 - while true; do detect_repo clear_screen - if [[ "$_MENU_ONLY" -eq 0 ]]; then - status_check - next_action - render_next_action + status_check + next_action + render_next_action - if use_gum_menu && [ -n "$(git status --porcelain)" ]; then - analyze_diff - fi + if use_gum_menu && [ -n "$(git status --porcelain)" ]; then + analyze_diff fi - _MENU_ONLY=0 echo "" render_menu @@ -926,7 +921,6 @@ while true; do ;; 3) run_ai_commit - _MENU_ONLY=1 pause_git_menu ;; 4) From 1d5fe8fed48540f89e6103846e1b11247d05a136 Mon Sep 17 00:00:00 2001 From: McAmner Date: Thu, 4 Jun 2026 20:40:13 +0200 Subject: [PATCH 11/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 8656a61..8d7aabd 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -568,7 +568,7 @@ function prompt_choice() { # Pauses inside gitlaunch without changing menu level. function pause_git_menu() { local pause_reply - + stty sane 2>/dev/null || true printf "%bPress Enter to return to Git menu...%b" "$C_DIM" "$C_RESET" read pause_reply } @@ -768,17 +768,13 @@ function pr_aware_push() { return $? fi - local tmpfile - tmpfile="$(mktemp)" - if [[ "${#push_args[@]}" -gt 0 ]]; then - git push "${push_args[@]}" 2>&1 | tee "$tmpfile"; status=${pipestatus[1]} + output=$(git push "${push_args[@]}" 2>&1) else - git push 2>&1 | tee "$tmpfile"; status=${pipestatus[1]} + output=$(git push 2>&1) fi - - output="$(cat "$tmpfile")" - rm -f "$tmpfile" + status=$? + echo "$output" if [[ "$status" -ne 0 ]] && echo "$output" | grep -E "GH013|Changes must be made through a pull request" >/dev/null; then create_pr_branch_for_push "$branch" "$commit_message" From 58231d9a79957e9365fc3b8de78aea61f250535d Mon Sep 17 00:00:00 2001 From: McAmner Date: Thu, 4 Jun 2026 21:04:56 +0200 Subject: [PATCH 12/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 8d7aabd..53a1c32 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -555,7 +555,7 @@ function prompt_choice() { fi input="" - IFS= read -r input || input="b" + IFS= read -r input /dev/null || true printf "%bPress Enter to return to Git menu...%b" "$C_DIM" "$C_RESET" - read pause_reply + read pause_reply Date: Thu, 4 Jun 2026 23:06:22 +0200 Subject: [PATCH 13/13] update shell scripts --- terminal/launchers/gitlaunch.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/terminal/launchers/gitlaunch.sh b/terminal/launchers/gitlaunch.sh index 53a1c32..891e5d0 100755 --- a/terminal/launchers/gitlaunch.sh +++ b/terminal/launchers/gitlaunch.sh @@ -516,7 +516,7 @@ function render_menu() { frame_row "Git: $git_state Staged: $STAGED_COUNT Unstaged: $UNSTAGED_COUNT Untracked: $UNTRACKED_COUNT" frame_mid frame_two_col "1. Git status" "2. Pull" - frame_two_col "3. Suggest commit" "4. Safe push" + frame_two_col "3. Commit with suggested message" "4. Safe push" frame_two_col "5. Open repo" "6. Dev mode" frame_two_col "7. Switch repo" "8. Auto commit + push" frame_two_col "9. Recent log" "b. Back" @@ -555,7 +555,7 @@ function prompt_choice() { fi input="" - IFS= read -r input /dev/null || true - printf "%bPress Enter to return to Git menu...%b" "$C_DIM" "$C_RESET" - read pause_reply