#!/usr/bin/env bash ############################################################################### # dnotes — Browse and create notes in ~/notes/ via dmenu # # Maintainer: Kris Yotam # License: MIT # Created: 2026-02-15 # Updated: 2026-02-24 # # Lists note folders with nerd icons in dmenu. Selecting a folder lists # its .md files for opening in Emacs. A "Create" option at the bottom # prompts for frontmatter and creates a new .md note. # # Usage: # dnotes ############################################################################### NOTES_DIR="$HOME/notes" ############################################################################### # folder icons ############################################################################### declare -A ICONS=( [cards]="󰘸" [index]="󰮮" [jottings]="" [marginalia]="󰆏" [slipbox]="󱉟" ) CREATE_ICON="" DEFAULT_ICON="󰎞" ############################################################################### # helpers ############################################################################### get_icon() { local folder="$1" echo "${ICONS[$folder]:-$DEFAULT_ICON}" } slugify() { echo "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//' } open_in_emacs() { local file="$1" # Check if emacsclient can reach a running GUI Emacs if emacsclient -e "(window-system)" 2>/dev/null | grep -q "x"; then # GUI Emacs running with server — open in vertical split pane (like SPC p n) emacsclient -e "(progn (split-window-right 80) (other-window 1) (find-file \"$file\") (raise-frame) (select-frame-set-input-focus (selected-frame)) (evil-normal-state))" 2>/dev/null else # No GUI Emacs — launch one with the file emacs "$file" & fi } ############################################################################### # create note ############################################################################### create_note() { # Pick folder for new note local MENU="" for folder in slipbox jottings marginalia index cards; do [ -d "$NOTES_DIR/$folder" ] && MENU+="$(get_icon "$folder") $folder"$'\n' done local CHOICE CHOICE=$(printf '%s' "$MENU" | sed '/^$/d' | dmenu -i -l 10 -p "$CREATE_ICON Create in") [ -z "$CHOICE" ] && exit 0 local FOLDER FOLDER=$(echo "$CHOICE" | sed 's/^[^ ]* *//') # Prompt for title local TITLE TITLE=$(printf '' | dmenu -p "󰏫 Title:") TITLE=$(echo "$TITLE" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') [ -z "$TITLE" ] && exit 0 local SLUG SLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/ /-/g; s/[^a-z0-9-]//g; s/--*/-/g; s/^-//; s/-$//') # Prompt for tags (comma-separated) local TAGS_INPUT TAGS_INPUT=$(echo "" | dmenu -p " Tags (comma-separated, e.g. math, calculus):") # Format as YAML list: "math, calculus" → "[math, calculus]" local TAGS="[]" if [ -n "$TAGS_INPUT" ]; then TAGS="[${TAGS_INPUT}]" fi # Prompt for certainty local CERTAINTY CERTAINTY=$(printf 'possible\ncertain\nlikely\nunlikely\nspeculative' | dmenu -i -l 5 -p " Certainty:") [ -z "$CERTAINTY" ] && CERTAINTY="possible" # Prompt for importance (1-10) local IMPORTANCE IMPORTANCE=$(printf '1\n2\n3\n4\n5\n6\n7\n8\n9\n10' | dmenu -i -l 10 -p " Importance:") [ -z "$IMPORTANCE" ] && IMPORTANCE="5" # Prompt for preview local PREVIEW PREVIEW=$(echo "" | dmenu -p " Preview:") local DATE DATE=$(date +%Y-%m-%d) local FILEPATH="$NOTES_DIR/$FOLDER/${SLUG}.md" if [ -f "$FILEPATH" ]; then notify-send "dnotes" "Note already exists: $SLUG" exit 1 fi cat > "$FILEPATH" <