From f566f8d9d0b2a213492e18a963b81a2469572b53 Mon Sep 17 00:00:00 2001 From: "shutkin.k" Date: Sat, 24 May 2025 09:36:35 +0300 Subject: [PATCH] Add checklist.sh scrpt --- checklist.sh | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100755 checklist.sh diff --git a/checklist.sh b/checklist.sh new file mode 100755 index 0000000..abd77f1 --- /dev/null +++ b/checklist.sh @@ -0,0 +1,211 @@ +#!/bin/bash + +# Check if file argument is provided +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +FILE="$1" + +# Check if file exists +if [ ! -f "$FILE" ]; then + echo "Error: File '$FILE' not found" + exit 1 +fi + +# Terminal control functions +hide_cursor() { + printf '\033[?25l' +} + +show_cursor() { + printf '\033[?25h' +} + +move_cursor() { + printf '\033[%d;%dH' "$1" "$2" +} + +clear_screen() { + printf '\033[2J' + printf '\033[H' +} + +cleanup() { + show_cursor + clear_screen + echo "Goodbye!" + exit 0 +} + +# Trap to ensure cursor is restored on exit +trap cleanup EXIT INT TERM + +# Hide cursor at start +hide_cursor + +# Parse the file and store tasks +declare -a categories=() +declare -A tasks=() +declare -A checked=() +declare -a display_items=() # Store display items in order +current_category="" +task_counter=0 + +while IFS= read -r line; do + if [[ $line =~ ^@(.+)$ ]]; then + current_category="${BASH_REMATCH[1]}" + categories+=("$current_category") + display_items+=("CATEGORY:$current_category") + elif [[ -n "$line" && -n "$current_category" ]]; then + task_key="${current_category}_${task_counter}" + tasks["$task_key"]="$line" + checked["$task_key"]=false + display_items+=("TASK:$task_key") + ((task_counter++)) + fi +done < "$FILE" + +# Current selection +current_selection=0 +total_items=${#display_items[@]} +header_lines=3 + +# Initial display +initial_display() { + clear_screen + echo "Checklist - Use ↑/↓ to navigate, SPACE to toggle, q to quit" + echo "File: $FILE" + echo "==================================================" + + for i in "${!display_items[@]}"; do + item="${display_items[$i]}" + if [[ $item =~ ^CATEGORY:(.+)$ ]]; then + category="${BASH_REMATCH[1]}" + echo "@$category" + elif [[ $item =~ ^TASK:(.+)$ ]]; then + task_key="${BASH_REMATCH[1]}" + checkbox="[ ]" + if [ "${checked[$task_key]}" = true ]; then + checkbox="[✓]" + fi + echo " $checkbox ${tasks[$task_key]}" + fi + echo # Empty line after each item + done +} + +# Update single line +update_line() { + local line_num=$((header_lines + 1 + ($1 * 2))) # *2 because of empty lines + move_cursor $line_num 1 + printf '\033[K' # Clear line + + item="${display_items[$1]}" + if [[ $item =~ ^CATEGORY:(.+)$ ]]; then + category="${BASH_REMATCH[1]}" + if [ $1 -eq $current_selection ]; then + printf '\033[7m@%s\033[0m' "$category" + else + printf '@%s' "$category" + fi + elif [[ $item =~ ^TASK:(.+)$ ]]; then + task_key="${BASH_REMATCH[1]}" + checkbox="[ ]" + if [ "${checked[$task_key]}" = true ]; then + checkbox="[✓]" + fi + task_line=" $checkbox ${tasks[$task_key]}" + if [ $1 -eq $current_selection ]; then + printf '\033[7m%s\033[0m' "$task_line" + else + printf '%s' "$task_line" + fi + fi +} + +# Update only the checkbox part of a task line +update_checkbox() { + local line_num=$((header_lines + 1 + ($1 * 2))) + local task_key="${BASH_REMATCH[1]}" + + item="${display_items[$1]}" + if [[ $item =~ ^TASK:(.+)$ ]]; then + task_key="${BASH_REMATCH[1]}" + checkbox="[ ]" + if [ "${checked[$task_key]}" = true ]; then + checkbox="[✓]" + fi + + # Move to checkbox position (column 3) and update just the checkbox + move_cursor $line_num 3 + if [ $1 -eq $current_selection ]; then + printf '\033[7m%s\033[0m' "$checkbox" + else + printf '%s' "$checkbox" + fi + fi +} + +# Get the task key at current selection +get_current_task_key() { + item="${display_items[$current_selection]}" + if [[ $item =~ ^TASK:(.+)$ ]]; then + echo "${BASH_REMATCH[1]}" + else + echo "" + fi +} + +# Show initial display +initial_display + +# Highlight first item +update_line 0 + +# Main loop +prev_selection=0 +while true; do + # Read single character + IFS= read -rsn1 input + + # Handle different input cases + if [[ $input == $'\x1b' ]]; then + # ESC sequence - read next two characters + IFS= read -rsn2 input + case $input in + '[A') # Up arrow + if [ $current_selection -gt 0 ]; then + prev_selection=$current_selection + ((current_selection--)) + update_line $prev_selection + update_line $current_selection + fi + ;; + '[B') # Down arrow + if [ $current_selection -lt $((total_items - 1)) ]; then + prev_selection=$current_selection + ((current_selection++)) + update_line $prev_selection + update_line $current_selection + fi + ;; + esac + elif [[ $input == ' ' ]]; then + # Space bar - toggle checkbox + task_key=$(get_current_task_key) + if [ -n "$task_key" ]; then + if [ "${checked[$task_key]}" = true ]; then + checked["$task_key"]=false + else + checked["$task_key"]=true + fi + # Only update the checkbox, not the entire line + update_checkbox $current_selection + fi + elif [[ $input == 'q' ]] || [[ $input == 'Q' ]]; then + # Quit - cleanup will be called by trap + exit 0 + fi +done