diff options
| author | Gabriel Schneider <[email protected]> | 2026-03-27 14:27:19 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-03-27 15:16:44 -0300 |
| commit | eeb2d880626c72b1f94e6d84838d8a0342bb99c7 (patch) | |
| tree | 510a7030fcbf29f49c6b313fe9602648de928e4f /build | |
| parent | 5d935263f828f8be1037484f22f9181aa9f734c5 (diff) | |
| download | 0x4200.cafe-eeb2d880626c72b1f94e6d84838d8a0342bb99c7.tar.gz 0x4200.cafe-eeb2d880626c72b1f94e6d84838d8a0342bb99c7.zip | |
Added jj metadata to page build, orchestrated with nushell and mise
Diffstat (limited to 'build')
| -rw-r--r-- | build/jj-log.nu | 34 | ||||
| -rw-r--r-- | build/jj-log.tmpl | 1 | ||||
| -rw-r--r-- | build/jj-log.typ | 158 |
3 files changed, 193 insertions, 0 deletions
diff --git a/build/jj-log.nu b/build/jj-log.nu new file mode 100644 index 0000000..7d2aa92 --- /dev/null +++ b/build/jj-log.nu @@ -0,0 +1,34 @@ +#!/usr/bin/env nu + +# Generate JSON from jj log and compile a Typst document +# Usage: nu build/jj-log.nu [--revset '::@'] [--output build/jj-log.pdf] + +def main [ + --revset (-r): string = "::@" # jj revset expression + --output (-o): string = "build/jj-log.pdf" # output PDF path + --json-only (-j) # only generate JSON, skip typst compile +] { + let build_dir = ($env.FILE_PWD? | default ("build" | path expand)) + let tmpl = (open ([$build_dir "jj-log.tmpl"] | path join)) + let json_path = ($output | path parse | update extension "json" | path join) + + let trees_json = "trees/_data/jj-log.json" + mkdir trees/_data + + print $"generating log for revset: ($revset)" + let log_output = (jj log -r $revset --no-graph -T $tmpl) + $log_output | save -f $json_path + $log_output | save -f $trees_json + + let n = (open --raw $json_path | lines | where $it != "" | length) + print $"wrote ($n) entries to ($json_path), ($trees_json)" + + if not $json_only { + let typ_path = ([$build_dir "jj-log.typ"] | path join) + let json_abs = ($json_path | path expand) + let out_abs = ($output | path expand) + print $"compiling ($output)..." + typst compile $typ_path --root / --input $"data=($json_abs)" $out_abs + print $"done → ($output)" + } +} diff --git a/build/jj-log.tmpl b/build/jj-log.tmpl new file mode 100644 index 0000000..2119648 --- /dev/null +++ b/build/jj-log.tmpl @@ -0,0 +1 @@ +"{\"commit\": " ++ json(self) ++ ", \"bookmarks\": " ++ json(local_bookmarks) ++ ", \"empty\": " ++ if(empty, "true", "false") ++ ", \"conflict\": " ++ if(conflict, "true", "false") ++ ", \"root\": " ++ if(root, "true", "false") ++ ", \"short_change\": \"" ++ change_id.shortest() ++ "\", \"short_commit\": \"" ++ commit_id.shortest() ++ "\"}\n" diff --git a/build/jj-log.typ b/build/jj-log.typ new file mode 100644 index 0000000..a1b36a9 --- /dev/null +++ b/build/jj-log.typ @@ -0,0 +1,158 @@ +// jj-log.typ — Typst document that renders jj log JSON output +// +// Usage: +// jj log -r '::@' --no-graph \ +// -T '{"commit": ' ++ json(self) ++ ', "bookmarks": ' ++ json(local_bookmarks) ++ ', "empty": ' ++ if(empty, "true", "false") ++ ', "conflict": ' ++ if(conflict, "true", "false") ++ ', "root": ' ++ if(root, "true", "false") ++ '}\n' \ +// > log.json +// typst compile jj-log.typ --input data=log.json + +#let data-path = sys.inputs.at("data", default: "log.json") +#let raw-text = read(data-path) +#let lines = raw-text.split("\n").filter(l => l.trim() != "") +#let commits = lines.map(l => json(bytes(l))) + +// Filter out root commit +#let commits = commits.filter(c => not c.root) + +// --- Helpers --- + +#let mono(body) = text(font: "DejaVu Sans Mono", size: 0.85em, body) +#let short(hash) = hash.slice(0, 8) +#let tag-pill(name, color: rgb("#4a6fa5")) = { + box( + fill: color.lighten(75%), + stroke: 0.5pt + color, + radius: 3pt, + inset: (x: 4pt, y: 2pt), + text(size: 0.8em, font: "DejaVu Sans Mono", fill: color, weight: "bold", name), + ) +} + +#let format-date(ts) = { + // timestamp is ISO 8601, e.g. "2026-03-27T14:22:35-03:00" + // extract date + time portion + let date-part = ts.slice(0, 10) + let time-part = ts.slice(11, 16) + date-part + " " + time-part +} + +// Collect all bookmarks across commits +#let all-bookmarks = commits.map(c => { + c.bookmarks.map(b => (name: b.name, commit: short(c.commit.commit_id), change: short(c.commit.change_id), desc: c.commit.description.trim())) +}).flatten() + +// --- Page setup --- + +#set page(paper: "a4", margin: 2cm) +#set text(font: "DejaVu Sans", size: 10pt) +#set par(leading: 0.6em) + +// --- Title --- + +#align(center)[ + #text(size: 18pt, weight: "bold")[jj ancestor log] + #v(0.3em) + #text(size: 9pt, fill: gray)[ + #commits.len() commits #h(1em) + #all-bookmarks.len() bookmarks #h(1em) + newest → oldest + ] +] + +#v(1em) + +// --- Bookmarks index --- + +#if all-bookmarks.len() > 0 [ + == Bookmarks + + #for bm in all-bookmarks [ + - #tag-pill(bm.name) #h(0.5em) at #mono(bm.commit) — #emph(bm.desc) + ] + + #v(1em) +] + +// --- Commit log table --- + +== Commit History + +#table( + columns: (auto, auto, 1fr, auto), + align: (center, center, left, right), + stroke: 0.4pt + luma(200), + inset: 6pt, + + // Header + table.header( + text(weight: "bold")[Change], + text(weight: "bold")[Commit], + text(weight: "bold")[Description], + text(weight: "bold")[Date], + ), + + // Rows — newest to oldest (jj log default order with ::@) + ..commits.map(c => { + let bookmarks = c.bookmarks.map(b => tag-pill(b.name)) + let is-empty = c.empty + let has-conflict = c.conflict + let desc = c.commit.description.trim() + if desc == "" { desc = "(no description)" } + + let badges = { + if is-empty { tag-pill("empty", color: rgb("#999")) } + if has-conflict { tag-pill("conflict", color: rgb("#c44")) } + } + + ( + mono(short(c.commit.change_id)), + mono(short(c.commit.commit_id)), + [ + #desc + #if bookmarks.len() > 0 { h(0.5em); bookmarks.join(h(0.3em)) } + #badges + ], + text(size: 0.85em, fill: luma(100), format-date(c.commit.author.timestamp)), + ) + }).flatten(), +) + +// --- Detailed list --- + +#v(1.5em) + +== Commit Details + +#for (i, c) in commits.enumerate() [ + #let desc = c.commit.description.trim() + #if desc == "" { desc = "(no description)" } + #let bookmarks = c.bookmarks.map(b => tag-pill(b.name)) + + #block( + width: 100%, + inset: 8pt, + radius: 4pt, + stroke: 0.3pt + luma(200), + fill: if calc.even(i) { luma(252) } else { white }, + )[ + #grid( + columns: (auto, 1fr), + column-gutter: 1em, + row-gutter: 0.4em, + text(fill: luma(120), size: 0.85em)[change:], mono(short(c.commit.change_id)), + text(fill: luma(120), size: 0.85em)[commit:], mono(short(c.commit.commit_id)), + text(fill: luma(120), size: 0.85em)[author:], [#c.commit.author.name #text(fill: luma(120))[\<#c.commit.author.email\>]], + text(fill: luma(120), size: 0.85em)[date:], format-date(c.commit.author.timestamp), + ..if bookmarks.len() > 0 {( + text(fill: luma(120), size: 0.85em)[bookmarks:], + bookmarks.join(h(0.3em)), + )}, + ) + #v(0.3em) + #text(weight: "bold", size: 1.05em, desc) + #if c.empty [ #text(fill: luma(150), size: 0.85em)[\[empty\]] ] + #if c.conflict [ #text(fill: rgb("#c44"), size: 0.85em)[\[conflict\]] ] + ] + + #v(0.5em) +] |
