~kris/dots

srice

ref: 6b8b8fffe50a045e2038c0b81fc5c137cdeb32f0 srice/bin/dnotes -rwxr-xr-x 5.5 KiB
6b8b8fff — Kris Yotam add bad-apple and cirno fastfetch presets, update glenda ascii art, update ff function 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env bash
###############################################################################
# dnotes — Browse and create notes in ~/notes/ via dmenu
#
# Maintainer:   Kris Yotam <krisyotam@pm.me>
# 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" <<EOF
---
id: $SLUG
start: $DATE
finish:
certainty: $CERTAINTY
status: seed
importance: $IMPORTANCE
preview: $PREVIEW
tags: $TAGS
---

# $TITLE

EOF

    open_in_emacs "$FILEPATH"
    notify-send "dnotes" "Created: $FOLDER/$SLUG.md"
}

###############################################################################
# main
###############################################################################

# Build folder menu
MENU=""
for folder in slipbox jottings marginalia index cards; do
    [ -d "$NOTES_DIR/$folder" ] && MENU+="$(get_icon "$folder")  $folder"$'\n'
done
MENU+="  create"

# Select folder or create
CHOICE=$(printf '%s' "$MENU" | sed '/^$/d' | dmenu -i -l 10 -p "󰎞  Notes")
[ -z "$CHOICE" ] && exit 0

# Check if Create was selected
SELECTED=$(echo "$CHOICE" | sed 's/^[^ ]* *//')
if [ "$SELECTED" = "create" ]; then
    create_note
    exit 0
fi

FOLDER="$SELECTED"

# List .md files in folder
FILES=$(find "$NOTES_DIR/$FOLDER" -maxdepth 1 -name "*.md" -type f | sort)

if [ -z "$FILES" ]; then
    notify-send "dnotes" "No notes in $FOLDER"
    exit 1
fi

# Build file menu from markdown titles
FILE_MENU=""
while IFS= read -r filepath; do
    filename=$(basename "$filepath" .md)
    # Extract title from # heading
    title=$(grep -m1 '^# ' "$filepath" | sed 's/^# *//')
    [ -z "$title" ] && title="$filename"
    FILE_MENU+="$title  [$filename]"$'\n'
done <<< "$FILES"

# Select file
FILE_CHOICE=$(printf '%s' "$FILE_MENU" | sed '/^$/d' | dmenu -i -l 20 -p "$(get_icon "$FOLDER")  $FOLDER")
[ -z "$FILE_CHOICE" ] && exit 0

# Extract slug from selection
SLUG=$(echo "$FILE_CHOICE" | sed 's/.*\[//; s/\]//')
MD_FILE="$NOTES_DIR/$FOLDER/${SLUG}.md"

if [ -f "$MD_FILE" ]; then
    open_in_emacs "$MD_FILE"
else
    notify-send "dnotes" "File not found: $SLUG.md"
    exit 1
fi