#!/usr/bin/env bash # d3codex - open a local file in the D3Codex web Annotator, full-screen. # # d3codex myprogram.bp # # Source / latest version: https://d3codex.com/d3codex.sh # Annotator: https://d3codex.com/tools/annotate/ # # How it works (and a reference example for the D3Codex hash scheme): the source # is carried in the URL fragment, so nothing is uploaded - the page reads it back # in the browser. The Annotator (https://d3codex.com/tools/annotate/) accepts: # #s= X = base64url (no padding) of the file's UTF-8 bytes. # #z= X = base64url of a zlib DEFLATE stream of the bytes (used when shorter). # &focus=1 opens full-screen Focus mode. # This script builds both forms, picks the shorter, and opens it in the browser. # # Supported on macOS and Linux (and WSL on Windows). On native Windows the opener # step differs - the script falls back to printing the URL for you to open, or adapt # opener() to your browser; everything else is portable. set -euo pipefail ORIGIN="${D3CODEX_ORIGIN:-https://d3codex.com}" # override for local dev, e.g. http://localhost:7101 FOCUS="&focus=1" PRINT_ONLY=0 usage() { cat <<'EOF' d3codex - open a file in the D3Codex Annotator (full-screen Focus mode). d3codex FILE open FILE in the annotator d3codex - read the program from stdin d3codex -p FILE print the URL instead of opening it d3codex --split FILE open in split (edit + preview) view, not Focus The source is carried in the URL hash; nothing is uploaded. Override the site with D3CODEX_ORIGIN (e.g. http://localhost:7101). EOF } while [ "$#" -gt 0 ]; do case "$1" in -p|--print) PRINT_ONLY=1; shift ;; --split) FOCUS=""; shift ;; -h|--help) usage; exit 0 ;; --) shift; break ;; -) break ;; # explicit stdin sentinel -*) echo "d3codex: unknown option: $1" >&2; exit 2 ;; *) break ;; esac done # base64url, no padding. The tr also unwraps base64's 76-column line breaks, so the # output is identical on macOS (BSD base64) and Linux (GNU coreutils). b64url() { base64 | tr '+/' '-_' | tr -d '=\n'; } # zlib-format DEFLATE (RFC 1950), matching the site's CompressionStream('deflate'), # which DecompressionStream('deflate') inflates regardless of compressor or level. # Returns non-zero if neither python3 nor node is available. deflate() { if command -v python3 >/dev/null 2>&1; then python3 -c 'import sys,zlib; sys.stdout.buffer.write(zlib.compress(sys.stdin.buffer.read(),9))' elif command -v node >/dev/null 2>&1; then node -e 'const z=require("zlib"),c=[];process.stdin.on("data",d=>c.push(d)).on("end",()=>process.stdout.write(z.deflateSync(Buffer.concat(c),{level:9})))' else return 1 fi } # Open a URL in the default browser, or print it if no opener exists. opener() { if command -v open >/dev/null 2>&1; then open "$1" elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$1" elif command -v wslview >/dev/null 2>&1; then wslview "$1" else echo "d3codex: no opener found; URL is:" >&2; printf '%s\n' "$1" fi } # Resolve the source to a file. Buffer stdin to a temp file so it can be read # twice (once raw, once compressed); clean it up on exit. FILE="${1:-}" TMP="" trap '[ -n "$TMP" ] && rm -f "$TMP"' EXIT if [ -z "$FILE" ] || [ "$FILE" = "-" ]; then TMP="$(mktemp "${TMPDIR:-/tmp}/d3codex.XXXXXX")" cat > "$TMP" SRCFILE="$TMP"; LABEL="stdin" else [ -f "$FILE" ] || { echo "d3codex: no such file: $FILE" >&2; exit 1; } SRCFILE="$FILE"; LABEL="$FILE" fi [ -s "$SRCFILE" ] || { echo "d3codex: nothing to open (empty input)" >&2; exit 1; } # Build the raw #s= link; switch to the compressed #z= link only if a deflater is # present, exited cleanly, and produced a genuinely shorter fragment. RAW="$(b64url < "$SRCFILE")" KEY=s; FRAG="$RAW" if Z="$(deflate < "$SRCFILE" 2>/dev/null | b64url)"; then [ -n "$Z" ] && [ "${#Z}" -lt "${#RAW}" ] && { KEY=z; FRAG="$Z"; } fi URL="${ORIGIN}/tools/annotate/#${KEY}=${FRAG}${FOCUS}" # Very long programs can exceed the command-line length (ARG_MAX, passing the URL to # the opener) or the browser's address-bar limit; warn but still try. [ "${#URL}" -gt 100000 ] && echo "d3codex: warning: large input; the URL may exceed the browser or command-line limit" >&2 if [ "$PRINT_ONLY" -eq 1 ]; then printf '%s\n' "$URL" else printf 'Opening %s in the D3Codex Annotator...\n' "$LABEL" opener "$URL" fi