Skip to content

Instantly share code, notes, and snippets.

@maikelthedev
Created January 7, 2026 08:30
Show Gist options
  • Select an option

  • Save maikelthedev/9695bcec08f85e36a613c7bcbdc3cd09 to your computer and use it in GitHub Desktop.

Select an option

Save maikelthedev/9695bcec08f85e36a613c7bcbdc3cd09 to your computer and use it in GitHub Desktop.
#!/usr/bin/env fish
# Get today's date in YYYYMMDD format
set today (date +%Y%m%d)
set vault_path "$HOME/Documents/Maikel's Vault"
set journal_file "$vault_path/Journal/daily/$today.md"
# Function to get current time in HH:MM:SS format
function get_current_time
date +%H:%M:%S
end
# Function to calculate time difference in seconds
function calculate_time_diff
set start_time $argv[1]
set end_time $argv[2]
# Validate time format (should be HH:MM or HH:MM:SS)
if not echo "$start_time" | grep -qE '^[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$'
echo "0"
return 1
end
if not echo "$end_time" | grep -qE '^[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$'
echo "0"
return 1
end
# Parse time components (handle both HH:MM and HH:MM:SS)
set start_hour (echo $start_time | cut -d: -f1)
set start_min (echo $start_time | cut -d: -f2)
set start_sec (echo $start_time | cut -d: -f3)
if test -z "$start_sec"
set start_sec 0
end
set end_hour (echo $end_time | cut -d: -f1)
set end_min (echo $end_time | cut -d: -f2)
set end_sec (echo $end_time | cut -d: -f3)
if test -z "$end_sec"
set end_sec 0
end
# Calculate total seconds since midnight
set start_total (math "$start_hour * 3600 + $start_min * 60 + $start_sec")
set end_total (math "$end_hour * 3600 + $end_min * 60 + $end_sec")
# Calculate difference
set diff (math "$end_total - $start_total")
echo $diff
return 0
end
# Function to format time difference as hours, minutes, and seconds
function format_time_diff
set total_seconds $argv[1]
# Validate that total_seconds is a number
if not echo "$total_seconds" | grep -qE '^-?[0-9]+$'
echo "0s"
return 1
end
# Handle negative time (shouldn't happen, but be safe)
if test $total_seconds -lt 0
set total_seconds 0
end
# Calculate hours, minutes, and seconds
set hours (math "floor($total_seconds / 3600)")
set remaining (math "$total_seconds % 3600")
set mins (math "floor($remaining / 60)")
set secs (math "$remaining % 60")
# Format output
if test $hours -gt 0
echo "$hours"h" $mins"m" $secs"s
else if test $mins -gt 0
echo "$mins"m" $secs"s
else
echo "$secs"s
end
return 0
end
# Function to find the current active task (marked with [/])
function find_current_task
set found_task 0
set task_line ""
set task_indent ""
set in_section 0
# Read the file and find the task with [/]
while read -l line
# Check if we're in the "What do I want from today?" section
if echo "$line" | grep -q "What do I want from today"
set in_section 1
continue
end
if test $in_section -eq 1
# Check if we hit another section (starts with # and not empty line)
if echo "$line" | grep -q '^#'
set in_section 0
continue
end
# Skip empty lines
if test -z "$line"
continue
end
# Check if this line has [/] - use grep to be more reliable
if echo "$line" | grep -q '\[/\]'
set found_task 1
set task_line "$line"
# Extract indentation (tabs or spaces) - everything before the dash
set task_indent (echo "$line" | sed -E 's/^(\s*).*/\1/')
break
end
end
end < "$journal_file"
if test $found_task -eq 1
echo "$task_line|$task_indent"
return 0
else
return 1
end
end
# Function to get all incomplete tasks (marked with [ ])
# Returns: task_names|task_lines (both separated by |, tasks separated by ||)
function get_incomplete_tasks
set in_section 0
set tasks ""
set task_lines ""
# Read the file and find all tasks with [ ]
while read -l line
# Check if we're in the "What do I want from today?" section
if echo "$line" | grep -q "What do I want from today"
set in_section 1
continue
end
if test $in_section -eq 1
# Check if we hit another section (starts with #)
if echo "$line" | grep -q '^#'
set in_section 0
continue
end
# Skip empty lines
if test -z "$line"
continue
end
# Check if this line has [ ] (incomplete task, not [x] or [/])
# Only match top-level tasks (no leading tabs, only spaces allowed)
if echo "$line" | grep -qE '^[ \t]*-\s*\[ \]'
# Check if it's a top-level task (starts with dash after optional spaces, no tabs before dash)
# Top-level tasks: start with "- [ ]" with no tabs before the dash
if echo "$line" | grep -qE '^[ ]*-\s*\[ \]'
# Extract task name (remove leading whitespace, dash, and [ ] marker)
set task_name (echo "$line" | sed -E 's/^[ ]*-\s*\[ \]\s*//' | sed -E 's/\s*\([0-9:]+\)\s*$//' | string trim)
if test -n "$task_name"
if test -z "$tasks"
set tasks "$task_name"
set task_lines "$line"
else
set tasks "$tasks||$task_name"
set task_lines "$task_lines||$line"
end
end
end
end
end
end < "$journal_file"
# Return both separated by a special marker
echo "$tasks|||$task_lines"
end
# Function to create a new main task
function create_main_task
set current_time (get_current_time)
# Step 1: Close the previous active task if it exists
set current_task_info (find_current_task)
if test $status -eq 0
# Extract task line and indent
set task_line (echo $current_task_info | cut -d'|' -f1)
set task_indent (echo $current_task_info | cut -d'|' -f2)
# Extract task name - remove leading whitespace, dash, and [/] marker
# Remove timestamp in parentheses if present (handles text after timestamp like emojis)
set task_name (echo "$task_line" | sed -E 's/^\s*-\s*\[\/\]\s*//' | sed -E 's/\s*\([0-9]{1,2}:[0-9]{2}\)[^)]*$//' | string trim)
# Extract start time from the task line (handle both HH:MM and HH:MM:SS)
set start_time (echo "$task_line" | grep -oE '\([0-9]{1,2}:[0-9]{2}(:[0-9]{2})?\)' | sed 's/[()]//g')
# Validate that we got a proper time format (HH:MM or HH:MM:SS)
set time_valid 0
if test -n "$start_time"
if echo "$start_time" | grep -qE '^[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$'
set time_valid 1
end
end
# If no valid start time found, use current time as start
if test $time_valid -eq 0
set start_time $current_time
end
# Calculate time difference
set time_diff_seconds (calculate_time_diff $start_time $current_time)
set time_diff_formatted (format_time_diff $time_diff_seconds)
# Create the closed task line: change [/] to [x] and add end time and duration at the end
set closed_task "$task_indent- [x] $task_name ($start_time - $current_time, $time_diff_formatted)"
# Replace the task line in the file
# Use a more reliable approach: read file, replace line, write back
set temp_file (mktemp)
set replaced 0
while read -l line
# Check if this is the task line we want to replace
set normalized_line (echo "$line" | string trim)
set normalized_task (echo "$task_line" | string trim)
if test "$normalized_line" = "$normalized_task"
# Replace with closed task
echo "$closed_task" >> "$temp_file"
set replaced 1
else
# Keep original line
echo "$line" >> "$temp_file"
end
end < "$journal_file"
if test $replaced -eq 1
mv "$temp_file" "$journal_file"
else
rm "$temp_file"
end
end
# Ask for new task name
set new_task (zenity --entry --title="New Main Task" --text="Enter the new main task:")
if test -z "$new_task"
exit 0
end
# Create new task entry
set new_task_entry "- [/] $new_task ($current_time)"
# Add it to the "What do I want from today?" section
set temp_file (mktemp)
set inserted 0
while read -l line
if echo "$line" | grep -q "What do I want from today"
echo "$line" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
set inserted 1
continue
end
echo "$line" >> "$temp_file"
end < "$journal_file"
if test $inserted -eq 0
# Section doesn't exist, add it
echo "" >> "$temp_file"
echo "# What do I want from today?" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
end
mv "$temp_file" "$journal_file"
end
# Function to add a distraction
function add_distraction
# Get user input via zenity
set distraction (zenity --entry --title="What are you doing?" --text="Enter your distraction")
# Check if user cancelled or entered empty string
if test -z "$distraction"
exit 0
end
# Get current time
set current_time (get_current_time)
# Create the file if it doesn't exist
if not test -f "$journal_file"
set template_file "$vault_path/Templates/Daily Note Template.md"
# Get formatted date for template replacement
set day_name (date +%A)
set day_date (date +%d-%m-%Y)
set formatted_date "$day_name, $day_date"
# Copy template and replace date placeholder
sed "s/{{date: dddd, DD-MM-YYYY}}/$formatted_date/g" "$template_file" > "$journal_file"
end
# Find current task
set current_task_info (find_current_task)
if test $status -ne 0
# No current task, get list of incomplete tasks
set incomplete_data (get_incomplete_tasks)
set incomplete_tasks (echo $incomplete_data | sed 's/|||.*//')
set task_lines (echo $incomplete_data | sed 's/.*|||//')
# Check if there are any incomplete tasks
if test -z "$incomplete_tasks"
# No incomplete tasks, ask user to create one
set task_name (zenity --entry --title="No Active Task" --text="No active or incomplete tasks found. Enter the current main task:")
if test -z "$task_name"
exit 0
end
set current_time_task (get_current_time)
set new_task_entry "- [/] $task_name ($current_time_task)"
# Add task to "What do I want from today?" section
set temp_file (mktemp)
set inserted 0
while read -l line
if echo "$line" | grep -q "What do I want from today"
echo "$line" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
set inserted 1
continue
end
echo "$line" >> "$temp_file"
end < "$journal_file"
if test $inserted -eq 0
echo "" >> "$temp_file"
echo "# What do I want from today?" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
end
mv "$temp_file" "$journal_file"
# Now find it again to get the indent
set current_task_info (find_current_task)
else
# Parse task names and lines
set task_names_array (echo $incomplete_tasks | string split '||')
set task_lines_array (echo $task_lines | string split '||')
# Validate we have tasks
if test (count $task_names_array) -eq 0
set task_name (zenity --entry --title="No Active Task" --text="No incomplete tasks found. Enter the current main task:")
if test -z "$task_name"
exit 0
end
set current_time_task (get_current_time)
set new_task_entry "- [/] $task_name ($current_time_task)"
set temp_file (mktemp)
set inserted 0
while read -l line
if echo "$line" | grep -q "What do I want from today"
echo "$line" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
set inserted 1
continue
end
echo "$line" >> "$temp_file"
end < "$journal_file"
if test $inserted -eq 0
echo "" >> "$temp_file"
echo "# What do I want from today?" >> "$temp_file"
echo "$new_task_entry" >> "$temp_file"
end
mv "$temp_file" "$journal_file"
set current_task_info (find_current_task)
else
# Build zenity list data - just task names, one per line
set temp_list_file (mktemp)
for task_name in $task_names_array
# Each line is just the task name
printf "%s\n" "$task_name" >> "$temp_list_file"
end
# Show selection dialog - zenity reads from stdin
# Single column, just task names
set selected_task_name (cat "$temp_list_file" | zenity --list \
--title="Select Task" \
--text="No active task. Select a task to activate:" \
--column="Task")
rm "$temp_list_file"
if test -z "$selected_task_name"
exit 0
end
# Find the index of the selected task by matching the task name
set selected_index 0
set idx 1
for task_name in $task_names_array
if test "$task_name" = "$selected_task_name"
set selected_index $idx
break
end
set idx (math $idx + 1)
end
# Validate we found a match
if test $selected_index -eq 0
exit 1
end
# Get the selected task line using the index
set selected_line ""
set idx 1
for line in $task_lines_array
if test $idx -eq $selected_index
set selected_line "$line"
break
end
set idx (math $idx + 1)
end
# Validate we found a line
if test -z "$selected_line"
exit 1
end
# Extract task name and indent from selected line
set task_indent (echo "$selected_line" | sed -E 's/^(\s*).*/\1/')
set task_name (echo "$selected_line" | sed -E 's/^\s*-\s*\[ \]\s*//' | sed -E 's/\s*\([0-9:]+\)\s*$//' | string trim)
# Update task from [ ] to [/] and add timestamp
set current_time_task (get_current_time)
set updated_task_line "$task_indent- [/] $task_name ($current_time_task)"
# Replace the task line in the file using exact matching
set temp_file (mktemp)
set replaced 0
while read -l line
# Check if this is the task line we want to replace
set normalized_line (echo "$line" | string trim)
set normalized_selected (echo "$selected_line" | string trim)
if test "$normalized_line" = "$normalized_selected"
# Replace with updated task
echo "$updated_task_line" >> "$temp_file"
set replaced 1
else
# Keep original line
echo "$line" >> "$temp_file"
end
end < "$journal_file"
# Only replace file if we successfully found and replaced the line
if test $replaced -eq 1
mv "$temp_file" "$journal_file"
else
rm "$temp_file"
exit 1
end
# Now find it again to get the indent
set current_task_info (find_current_task)
end
end
end
# Extract task indent and line
set task_indent (echo $current_task_info | cut -d'|' -f2)
set task_line (echo $current_task_info | cut -d'|' -f1)
# If task doesn't have a timestamp, add one
if not echo "$task_line" | grep -q '('
set current_time_task (get_current_time)
# Extract task name without timestamp
set task_name_only (echo "$task_line" | sed -E 's/^\s*-\s*\[\/\]\s*//')
set updated_task_line "$task_indent- [/] $task_name_only ($current_time_task)"
# Update the task in the file
set temp_file (mktemp)
set escaped_old (echo "$task_line" | sed 's/[[\.*^$()+?{|]/\\&/g')
set escaped_new (echo "$updated_task_line" | sed 's/[[\.*^$()+?{|]/\\&/g')
sed "s|$escaped_old|$escaped_new|" "$journal_file" > "$temp_file"
mv "$temp_file" "$journal_file"
# Update task_line to the new version
set task_line "$updated_task_line"
# Re-find the task to get the exact line as it appears in the file
set current_task_info (find_current_task)
if test $status -eq 0
set task_line (echo $current_task_info | cut -d'|' -f1)
set task_indent (echo $current_task_info | cut -d'|' -f2)
end
end
# Create distraction entry as subtask (add one level of indentation with tab)
set distraction_entry "$task_indent - $distraction ($current_time)"
# Insert distraction right after the current task
# Match the exact task line we found
set temp_file (mktemp)
set inserted 0
# Normalize the task_line for comparison (trim whitespace)
set normalized_task (echo "$task_line" | string trim)
while read -l line
# Normalize current line for comparison
set normalized_line (echo "$line" | string trim)
# Write the current line
echo "$line" >> "$temp_file"
# Check if this is the exact task line (after normalization)
if test "$normalized_line" = "$normalized_task"
if test $inserted -eq 0
echo "$distraction_entry" >> "$temp_file"
set inserted 1
end
end
end < "$journal_file"
# If we still haven't inserted, try pattern matching as fallback
if test $inserted -eq 0
set temp_file2 (mktemp)
set in_section 0
set inserted2 0
while read -l line
echo "$line" >> "$temp_file2"
# Track if we are in the "What do I want from today?" section
if echo "$line" | grep -q "What do I want from today"
set in_section 1
end
if test $in_section -eq 1
# Check if we hit another section (starts with #)
if echo "$line" | grep -q '^#'
set in_section 0
end
# Check if this line contains [/] and we haven't inserted yet
if echo "$line" | grep -q '\[/\]'
if test $inserted2 -eq 0
echo "$distraction_entry" >> "$temp_file2"
set inserted2 1
end
end
end
end < "$journal_file"
mv "$temp_file2" "$journal_file"
else
mv "$temp_file" "$journal_file"
end
end
# Function to get the current main task (for command-line use)
function get_current_main_task
# Create the file if it doesn't exist
if not test -f "$journal_file"
echo ""
return 1
end
# Find current task
set current_task_info (find_current_task)
if test $status -eq 0
# Extract task line
set task_line (echo $current_task_info | cut -d'|' -f1)
# Extract task name:
# 1. Remove leading whitespace, dash, and [/] marker
# 2. Remove timestamp in parentheses (HH:MM) if present
# 3. Trim trailing whitespace
set task_name (echo "$task_line" | sed -E 's/^\s*-\s*\[\/\]\s*//' | sed -E 's/\s*\([0-9]{1,2}:[0-9]{2}\)\s*//' | string trim)
echo "$task_name"
return 0
else
echo ""
return 1
end
end
# Function to display current task as a reminder using zenity
# Shows a reminder every 5 minutes
# Usage: show_task_banner
function show_task_banner
# Check if zenity is available
if not command -v zenity > /dev/null
echo "Error: zenity is required for task reminder. Please install zenity."
return 1
end
# Set up signal handler for cleanup
function on_exit --on-signal INT --on-signal TERM
exit
end
# Continuous loop: show reminder every 5 minutes
while true
# Create the file if it doesn't exist
if not test -f "$journal_file"
zenity --info \
--title="Task Reminder" \
--window-icon="๐Ÿ“‹" \
--width=400 \
--height=200 \
--text="<span font='20' weight='bold' foreground='red'>NO ACTIVE TASK</span>"
# Wait 5 minutes before next reminder
sleep 300
continue
end
# Find current task
set current_task_info (find_current_task)
if test $status -eq 0
# Extract task line
set task_line (echo $current_task_info | cut -d'|' -f1)
# Extract task name (remove leading whitespace, dash, and [/] marker, and timestamp)
set task_name (echo "$task_line" | sed -E 's/^\s*-\s*\[\/\]\s*//' | sed -E 's/\s*\([0-9]{1,2}:[0-9]{2}\)[^)]*$//' | string trim)
# Extract start time from the task line (handle both HH:MM and HH:MM:SS)
set start_time (echo "$task_line" | grep -oE '\([0-9]{1,2}:[0-9]{2}(:[0-9]{2})?\)' | sed 's/[()]//g')
# Validate start time
set time_valid 0
if test -n "$start_time"
if echo "$start_time" | grep -qE '^[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$'
set time_valid 1
end
end
# Get current time and calculate duration
set current_time (get_current_time)
# Build the message to display with HTML markup
if test $time_valid -eq 1
# Convert start_time to HH:MM:SS format if needed (for display)
set start_time_display "$start_time"
if echo "$start_time" | grep -qE '^[0-9]{1,2}:[0-9]{2}$'
set start_time_display "$start_time:00"
end
set time_diff_seconds (calculate_time_diff $start_time $current_time)
set time_diff_formatted (format_time_diff $time_diff_seconds)
set message "<span font='14' weight='normal' foreground='gray'>Current Task</span>\n<span font='20' weight='bold'>$task_name</span>\n\n<span font='14' foreground='grey'>Started: $start_time_display</span>\n<span font='14' foreground='red'>Duration: $time_diff_formatted</span>"
else
set message "<span font='14' weight='normal' foreground='gray'>Current Task</span>\n<span font='20' weight='bold'>$task_name</span>\n\n<span font='14'>No start timze recorded</span>"
end
# Show zenity dialog (user clicks OK/Enter to dismiss)
zenity --warning \
--title="Task Reminder" \
--width=400 \
--height=200 \
--text="$message"
else
zenity --info \
--title="Task Reminder" \
--window-icon="๐Ÿ“‹" \
--width=400 \
--height=200 \
--text="<span font='20' weight='bold' foreground='red'>NO ACTIVE TASK</span>"
end
# Wait 5 minutes before next reminder
sleep 300
end
end
# Function to show help information
function show_help
echo "Task and Distraction Tracker"
echo ""
echo "Usage: add-distraction.fish [command]"
echo ""
echo "Commands:"
echo " (no command) Add a distraction to the current active task"
echo " new-task Create a new main task (closes previous active task)"
echo " get-task Get the name of the current active task"
echo " show-task [font] Display current task as a banner (continuous, updates every second)"
echo " Optional font: basic, banner4, colossal, standard, banner-3D, etc. (default: banner-3D)"
echo " help Show this help message"
echo ""
echo "Description:"
echo " This script helps you track tasks and distractions in your daily journal."
echo " Tasks are stored in the 'What do I want from today?' section."
echo ""
echo "Task States:"
echo " [ ] - Incomplete task"
echo " [/] - Active task (only one at a time)"
echo " [x] - Completed task"
echo ""
echo "Examples:"
echo " fish add-distraction.fish # Add a distraction"
echo " fish add-distraction.fish new-task # Create new task"
echo " fish add-distraction.fish get-task # Get current task name"
echo " fish add-distraction.fish show-task # Display task banner (default font)"
echo " fish add-distraction.fish show-task basic # Display task banner with 'basic' font"
end
# Main logic: check command line argument
if test (count $argv) -gt 0
switch $argv[1]
case "new-task"
create_main_task
case "get-task"
get_current_main_task
case "show-task"
# Pass font argument if provided
if test (count $argv) -gt 1
show_task_banner $argv[2]
else
show_task_banner
end
case "help" "--help" "-h"
show_help
case "*"
add_distraction
end
else
# Default behavior: add distraction
add_distraction
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment