#!/usr/bin/env bash # Helper for the sid.sqlite-viewer bar widget. # open-db launch an empty DB Browser for SQLite # open-db open one database in the viewer # open-db --list print recent database files as JSON for the panel set -euo pipefail VIEWER=sqlitebrowser ensure_viewer() { command -v "$VIEWER" >/dev/null 2>&1 && return 0 omarchy-notification-send "DB Browser for SQLite is not installed — run: omarchy pkg add sqlitebrowser" 2>/dev/null || echo "open-db: $VIEWER not found; install it with: omarchy pkg add sqlitebrowser" >&2 exit 1 } launch() { ensure_viewer setsid uwsm app -- "$VIEWER" "$@" >/dev/null 2>&1 & } # Hard bounds so opening the panel can never trigger unbounded work, however # large the home directory: the walk itself is killed after MAX_WALK_SECONDS # (depth and result caps alone cannot bound traversal when few files match), # traversal stops at MAX_DEPTH, and at most MAX_CANDIDATES files are stat'ed # and sorted. A timed-out walk yields whatever it found so far. MAX_WALK_SECONDS=3 MAX_DEPTH=8 MAX_CANDIDATES=400 # Recent database files under $HOME, skipping heavy tool caches and browser # profiles. fd when available (Omarchy installs it by default but does not # depend on it), plain find otherwise. Dot-named files (macOS ._* stubs) are # skipped while hidden directories are still descended into. find_candidates() { if command -v fd >/dev/null 2>&1; then timeout -k 1 "$MAX_WALK_SECONDS" \ fd --absolute-path --type f --hidden \ --max-depth "$MAX_DEPTH" \ -e db -e sqlite -e sqlite3 -e db3 \ --changed-within 60d \ -E node_modules -E .git -E .cache -E .cargo -E .rustup \ -E .local/share/Trash -E .mozilla -E .thunderbird -E '.local/share/*webkit*' \ -E .config/chromium -E '*chrome-profile*' \ '^[^.]' "$HOME" -0 2>/dev/null || true else timeout -k 1 "$MAX_WALK_SECONDS" \ find "$HOME" -maxdepth "$MAX_DEPTH" \ \( -name node_modules -o -name .git -o -name .cache -o -name .cargo \ -o -name .rustup -o -name .mozilla -o -name .thunderbird \ -o -name '*chrome-profile*' -o -path "$HOME/.local/share/Trash" \ -o -path "$HOME/.config/chromium" \) -prune -o \ -type f ! -name '.*' -mtime -60 \ \( -name '*.db' -o -name '*.sqlite' -o -name '*.sqlite3' -o -name '*.db3' \) \ -print0 2>/dev/null || true fi } # Newest first, with project files (visible paths) ranked ahead of app-state # databases living under dot-directories, which churn constantly and would # otherwise crowd the list. The candidate stream is truncated to # MAX_CANDIDATES *before* any stat/sort work happens, so the expensive part # of the pipeline is bounded regardless of how many files match. scan() { find_candidates | head -z -n "$MAX_CANDIDATES" | xargs -0 -r stat -c $'%Y\t%s\t%n' 2>/dev/null | awk -F'\t' -v home="$HOME/" '{ rank = (substr($3, length(home) + 1) ~ /^\./) ? 1 : 0 print rank "\t" $0 }' | sort -t$'\t' -k1,1n -k2,2rn | head -14 | cut -f2- } case "${1:-}" in "") launch ;; --list) scan | jq -Rs --arg home "$HOME" ' split("\n") | map(select(length > 0) | split("\t") | { mtime: (.[0] | tonumber), size: (.[1] | tonumber), path: .[2], name: (.[2] | split("/") | last), dir: (.[2] | split("/")[:-1] | join("/") | sub("^\($home)"; "~")) })' ;; *) launch "$1" ;; esac