#!/usr/bin/env bash # dot-watch — auto-rerender a graphviz .dot file to PNG on every save. # # WHY THIS EXISTS # pi-studio renders mermaid natively but has no graphviz/DOT renderer. # Its markdown preview DOES render local image links (.png/.jpg/.gif/.webp), # and the editor offers "refresh from disk". This helper closes the loop: # edit a .dot file -> dot-watch regenerates .png -> hit refresh in # Studio to see the update. Uses mtime polling (no inotify dependency, # which isn't in the trixie-slim base). # # USAGE # dot-watch [layout] [dpi] # layout: dot|neato|fdp|circo|twopi (default: dot) # dpi: output resolution (default: 150) # env: DOT_WATCH_INTERVAL= poll interval (default: 1) # # EXAMPLES # dot-watch /workspace/graph.dot # dot-watch graph.dot neato 200 set -euo pipefail SRC="${1:?usage: dot-watch [layout] [dpi]}" LAYOUT="${2:-dot}" DPI="${3:-150}" [[ -f "$SRC" ]] || { echo "error: no such file: $SRC" >&2; exit 1; } command -v "$LAYOUT" >/dev/null || { echo "error: layout engine '$LAYOUT' not found" >&2; exit 1; } OUT="${SRC%.dot}.png" INTERVAL="${DOT_WATCH_INTERVAL:-1}" # seconds between polls ERRLOG="$(mktemp -t dot-watch.XXXXXX.err)" trap 'rm -f "$ERRLOG"' EXIT render() { if "$LAYOUT" -Tpng -Gdpi="$DPI" "$SRC" -o "$OUT" 2> "$ERRLOG"; then printf '[%s] rendered -> %s\n' "$(date +%H:%M:%S)" "$OUT" else printf '[%s] DOT error:\n' "$(date +%H:%M:%S)" sed 's/^/ /' "$ERRLOG" fi } # portable mtime (GNU stat, fallback to BSD stat) mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null; } echo "watching $SRC ($LAYOUT, ${DPI}dpi) -> $OUT [Ctrl-C to stop]" render last="$(mtime "$SRC")" while true; do sleep "$INTERVAL" [[ -f "$SRC" ]] || continue now="$(mtime "$SRC")" if [[ "$now" != "$last" ]]; then last="$now" render fi done