#!/usr/bin/env bash set -eo pipefail : "${NOTES_DIR:=$HOME/notes}" tmp="$(mktemp --suffix=.html)" # ---------------------------- # HTML escape # ---------------------------- escape_html() { sed \ -e 's/&/\&/g' \ -e 's//\>/g' \ -e 's/"/\"/g' } # ---------------------------- # title = full filename # ---------------------------- get_title() { basename "$1" \ | sed -E ' s/\.[^.]+$//; s/^[0-9]{4}-[0-9]{2}-[0-9]{2}-// ' } linkify() { sed -E ' s/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/\1<\/a>/g s#(https?://[^[:space:]]+)#\1#g ' } # ---------------------------- # preview = first N lines # ---------------------------- get_preview() { python3 - "$1" <<'PY' import sys import re import html path = sys.argv[1] n = 3 with open(path, "r", encoding="utf-8", errors="ignore") as f: lines = f.readlines() # ---------------------------- # strip YAML frontmatter # ---------------------------- if lines and lines[0].strip() == "---": i = 1 while i < len(lines) and lines[i].strip() != "---": i += 1 if i < len(lines): lines = lines[i+1:] # ---------------------------- # collect first N non-empty lines # ---------------------------- out = [] for line in lines: line = line.strip() if not line: continue out.append(line) if len(out) >= n: break text = " ".join(out) # ---------------------------- # escape HTML FIRST # ---------------------------- text = html.escape(text) # ---------------------------- # markdown links # ---------------------------- placeholders = [] def md_replace(match): label = match.group(1) url = match.group(2) token = f"@@LINK{len(placeholders)}@@" placeholders.append( f'{label}' ) return token text = re.sub( r'\[([^\]]+)\]\((https?://[^)]+)\)', md_replace, text ) # ---------------------------- # raw URLs # ---------------------------- text = re.sub( r'(?{m.group(0)}', text ) # ---------------------------- # restore markdown links # ---------------------------- for i, link in enumerate(placeholders): text = text.replace(f"@@LINK{i}@@", link) print(text) PY } # ---------------------------- # open file (WSL2 + Linux + macOS) # ---------------------------- open_file() { local file="$1" if command -v wslpath >/dev/null 2>&1 && command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$(wslpath -w "$file")" >/dev/null 2>&1 & elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$file" >/dev/null 2>&1 & elif command -v open >/dev/null 2>&1; then open "$file" >/dev/null 2>&1 & else echo "No opener found: $file" fi } # ---------------------------- # HTML header # ---------------------------- cat > "$tmp" <<'HTML' noteweb
HTML # ---------------------------- # main loop (safe, no subshell issues) # ---------------------------- while IFS= read -r file; do [ -z "$file" ] && continue title="$(get_title "$file")" preview="$(get_preview "$file")" mtime="$(date -r "$file" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "")" # escape file path for JS file_escaped="${file//\\/\\\\}" file_escaped="${file_escaped//\"/\\\"}" cat >> "$tmp" <
$title
$preview
$mtime
EOF done < <( find "$NOTES_DIR" \ -type f \ \( -name "*.md" -o -name "*.txt" \) \ -not -path "*/.trash/*" \ | sort -r ) # ---------------------------- # footer # ---------------------------- cat >> "$tmp" <<'HTML' HTML # ---------------------------- # open result # ---------------------------- open_file "$tmp"