clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

Words, not chips: plain text for kinds, topics and filters

cat committed 0259f3c
Jev's kind of a commit is small muted monospace in its column, and a
repo's topic, the mechanical mark on a diff file, what changed between
two versions of a commit, a comment's outdated or addressed note, and a
range-diff's unchanged or rewritten are words in their colour, without
the pill around them. The shelf filters on the index and owner pages
are plain links, the chosen one bold over a thin accent bar, with their
counts as faint numbers. A review's latest revision (r2) is a plain
link, and "private" a coloured word.

Only a review's status keeps a shape, where it matters: a small square
label, white on its colour, on review pages, in branch and stack tables
and in review lists, where it replaces the round dot. The live status
says what it is in words, without a dot.
commit
0259f3c859c8886e14569ed24a00c874a5fef1ba
parent
28742881e180f6c49287b4be428cdd9441039bb8
author
cat <admin@dev.site>
ci
✓ passed in 78s, signed by cat
kind
refactor risk score 2.1 of 3, Jev's estimate
review
#12 Square, hairline, textured: the site's design pass
ci attestation
forge-attestation 1
type ci
commit 0259f3c859c8886e14569ed24a00c874a5fef1ba
tree 43fef645fa4d5b7857d20a2cb5c0e3a942f67ad6
script .forge/ci 38df37c0d5a6382f60569079a0dd821094e37e4a
result pass
exit 0
started 2026-10-19T01:51:52Z
duration 78s
runner (not shown here; refs/notes/ci has it)
system Darwin 25.5.0 arm64
env host
log sha256:4bc6c1cfb5a7d76e7b5a4ace3158c417aec951a7bdd2d07010c5d230280a711d 253 lines
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|b901d96 running .forge/ci: greet.sh: say what it is
|  | sh: /bin/sh
|  | ok   greet
|b901d96 pass in 0s, signed
|pushed main
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|4f82d04 running .forge/ci: wave at the world
|  | sh: /bin/sh
|  | FAIL greet : got 'hello, world o/', want 'hello, world'
|4f82d04 fail in 1s, signed
|forge: 1 commit(s) did not pass
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|982d757 running .forge/ci: update the test for the wave
|  | sh: /bin/sh
|  | ok   greet
|982d757 pass in 0s, signed
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|warning: You appear to have cloned an empty repository.
|site home: deployed 7e53850 -> /sites/ana/dotfiles/home/
|created ana/notes
|remote: review #1: /ana/notes/reviews/1/
|pushed ana/todo
|landing #1 A todo list: ana/todo -> main
|daf325f running .forge/ci: web, server and Python versions of greet
|  | sh: /bin/sh
|  | ok   greet
|daf325f pass in 0s, signed
|pushed main
|3c2a9cc running .forge/ci: messages: notes for the later regions
|  | sh: /bin/sh
|  | ok   greet
|3c2a9cc pass in 0s, signed
|remote: review #7: /dami/greet/reviews/7/
|pushed dami/regions
|created dami/forge
|warning: You appear to have cloned an empty repository.
|site guide: deployed 314d9be -> /sites/dami/forge/guide/
|(node:64132) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/dotfiles in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.TecxQS/wt/demo/root/.forge/www
|(node:64256) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/notes in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.TecxQS/wt/demo/root/.forge/www
|(node:64397) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/forge in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.TecxQS/wt/demo/root/.forge/www
|(node:64530) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/greet in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.TecxQS/wt/demo/root/.forge/www
|(node:65195) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built - in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.TecxQS/wt/demo/root/.forge/www
|226 of 226 pages render the same without 11ty
|guide: 4 files, links ok
|ok

Signature: good, cat

6 files changed, +36 -42
MREADME.md4+-
Msite/_includes/discussion.njk2+-
Msite/_includes/macros.njk25+++++++----------
Msite/assets/style.css44++++++++++++++----------------
Msite/repo/review-diff.njk1+
Msite/repo/revision.njk2+-
M README.md +2 -2
M site/_includes/discussion.njk +1 -1
M site/_includes/macros.njk +10 -15
@@ -83,11 +83,7 @@
8383 .rv { position: relative; display: grid; grid-template-columns: auto 1fr auto; column-gap: 10px; row-gap: 3px; align-items: baseline; padding: 10px 8px; border-bottom: 1px solid var(--border); }8484 .rv:hover, .rv.sel { background: var(--hover); }8585 .rv.sel { box-shadow: inset 2px 0 0 var(--accent); }86-.rv-mark { grid-row: 1 / 3; align-self: start; width: 9px; height: 9px; margin-top: 6px; background: var(--muted); }87-.rv-mark.open { background: var(--pass); }88-.rv-mark.draft { background: none; box-shadow: inset 0 0 0 2px var(--muted); }89-.rv-mark.merged { background: var(--merged); }90-.rv-mark.closed { background: var(--fail); }86+.rv > .status { grid-row: 1; }9187 .rv-title { min-width: 0; font-weight: 600; overflow-wrap: anywhere; }9288 .rv-title a::after { content: ''; position: absolute; inset: 0; }9389 .rv-side { display: flex; gap: 12px; align-items: baseline; justify-content: flex-end; white-space: nowrap; font: 12.5px var(--mono); }
@@ -99,23 +95,22 @@
9995 .rv-meta .del { color: var(--fail); }10096 .rv-meta a, .rv-side a { position: relative; z-index: 1; }10197 .rv.layer { margin-left: calc(var(--depth) * 22px); border-left: 1px solid var(--border); }102-a.rev { font-size: 0.85em; color: var(--muted); border: 1px solid var(--border); padding: 0 4px; }10398 .appr { white-space: nowrap; font-weight: 600; color: var(--pass); }10499 .appr + .appr { margin-left: 8px; }105100 .appr.stale { color: var(--warn); font-weight: normal; }106101 @media (max-width: 720px) {107102 .rv { grid-template-columns: auto 1fr; }108103 .rv-side { grid-column: 2; justify-content: flex-start; }109- .rv-meta { grid-column: 2; }104+ .rv-meta { grid-column: 1 / 3; }110105 .rv.layer { margin-left: calc(var(--depth) * 12px); }111106 }112107 {% endcss %}113108 {% set last = r.revisions[r.revisions.length - 1] %}114109 <li class="rv{% if depth %} layer{% endif %}"{% if depth %} style="--depth: {{ depth }}"{% endif %}>115-<span class="rv-mark {{ r.status }}" title="{{ r.status }}"></span>110+{{ status(r.status) }}116111 <span class="rv-title"><a href="{{ repo.urls.review(r.id).href }}">{{ r.title }}</a></span>117112 <span class="rv-side">{{ reviewci(r.ci) }}{% if r.approvals.length %}<span>{{ approvals(r) }}</span>{% endif %}</span>118-<span class="rv-meta"><span class="num">#{{ r.id }}</span><span class="mono">{{ r.branch }} &rarr; {{ r.target }}</span>{% if r.author %}<span>{{ r.author }}</span>{% endif %}{% if r.commits.length %}<span>{{ r.commits.length | plural('commit') }}</span>{% endif %}{% if r.combinedStats and (r.combinedStats.add or r.combinedStats.del) %}<span class="num"><span class="add">+{{ r.combinedStats.add }}</span> <span class="del">&minus;{{ r.combinedStats.del }}</span></span>{% endif %}<span class="date">{{ r.updated | ago }}</span>{% if last and last.view %}<a class="rev" href="{{ repo.urls.revision(r.id, last.n).href }}" title="what changed since r{{ last.prev }}">r{{ last.n }}</a>{% endif %}</span>113+<span class="rv-meta"><span class="num">#{{ r.id }}</span><span class="mono">{{ r.branch }} &rarr; {{ r.target }}</span>{% if r.author %}<span>{{ r.author }}</span>{% endif %}{% if r.commits.length %}<span>{{ r.commits.length | plural('commit') }}</span>{% endif %}{% if r.combinedStats and (r.combinedStats.add or r.combinedStats.del) %}<span class="num"><span class="add">+{{ r.combinedStats.add }}</span> <span class="del">&minus;{{ r.combinedStats.del }}</span></span>{% endif %}<span class="date">{{ r.updated | ago }}</span>{% if last and last.view %}<a href="{{ repo.urls.revision(r.id, last.n).href }}" title="what changed since r{{ last.prev }}">r{{ last.n }}</a>{% endif %}</span>119114 </li>120115 {% endmacro %}121116
@@ -139,16 +134,16 @@ a.rev { font-size: 0.85em; color: var(--muted); border: 1px solid var(--border);
139134 {% endmacro %}140135 141136 {# The index and owner pages: repos as rows on shelves by kind (the shelves142- filter), with chips that jump to a shelf. ui.ts makes the chips filters and137+ filter), with links (.chip) that jump to a shelf. ui.ts makes them filters and143138 adds "has a site" and the sort; keys.ts walks the cards with j and k. #}144139 {% macro shelves(repos, owners = false) %}145140 {% css %}146-.shelf-bar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 0 0 4px; }141+.shelf-bar { display: flex; flex-wrap: wrap; gap: 4px 18px; align-items: baseline; margin: 0 0 4px; }147142 .shelf-bar .grow { flex: 1; }148-.chip { font: inherit; font-size: 0.85em; color: var(--muted); background: none; border: 1px solid var(--border); padding: 2px 11px; cursor: pointer; white-space: nowrap; }149-.chip:hover { color: var(--fg); background: var(--hover); text-decoration: none; }150-.chip[aria-pressed="true"] { color: var(--fg); background: var(--hover); border-color: var(--faint); }151-.chip .n { color: var(--faint); margin-left: 4px; font-variant-numeric: tabular-nums; }143+.chip { font: inherit; font-size: 0.9em; letter-spacing: normal; text-transform: none; color: var(--link); background: none; border: 0; padding: 4px 0; cursor: pointer; white-space: nowrap; }144+.chip:hover { color: var(--deep); background: none; text-decoration: none; }145+.chip[aria-pressed="true"] { color: var(--fg); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent); }146+.chip .n { font: 11px var(--mono); color: var(--faint); margin-left: 5px; font-variant-numeric: tabular-nums; }152147 .shelf-head { display: flex; align-items: baseline; gap: 8px; margin: 26px 0 0; padding-bottom: 6px; border-bottom: 1px solid var(--border); scroll-margin-top: 12px; }153148 .shelf-head h2 { margin: 0; padding: 0; border: 0; }154149 .shelf-head .n { font: 600 11px var(--mono); color: var(--faint); }
M site/assets/style.css +21 -23
@@ -127,21 +127,19 @@ details.clone > summary::-webkit-details-marker { display: none; }
127127 details.clone .panel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 10; width: min(440px, 92vw); background: var(--raised); border: 1px solid var(--border); padding: 10px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); }128128 .copyrow { display: flex; gap: 6px; align-items: center; margin: 4px 0; }129129 .clone-note { margin: 6px 0 0; font-size: 0.85em; color: var(--muted); }130-.private-mark { font-size: 0.75em; font-weight: 600; color: var(--warn); border: 1px solid currentColor; padding: 0 7px; vertical-align: 2px; }131-.private-mark:hover { text-decoration: none; }130+.private-mark { font: 600 10.5px var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--warn); vertical-align: 2px; }131+.private-mark:hover { color: var(--warn); }132132 .copyrow label { width: 44px; font: 600 10.5px var(--mono); letter-spacing: 0.08em; color: var(--faint); }133133 .copyrow input { flex: 1; min-width: 0; font: 13px var(--mono); padding: 4px 6px; border: 1px solid var(--border); background: var(--bg); color: var(--fg); }134134 button, .btn { font: inherit; font-size: 0.85em; color: var(--fg); background: var(--subtle); border: 1px solid var(--border); padding: 3px 10px; cursor: pointer; }135135 button:hover, .btn:hover { background: var(--hover); text-decoration: none; }136136 137-/* live status */138-#live { font-size: 0.8em; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40vw; }139-#live span::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 6px; vertical-align: 1px; }140-#live .live-on::before { background: var(--pass); }141-#live .live-off::before { background: var(--faint); }142-#live .live-busy { color: var(--warn); }143-#live .live-busy::before { background: var(--warn); animation: live-pulse 1s ease-in-out infinite alternate; }144-@keyframes live-pulse { to { opacity: 0.2; } }137+/* live status: words, no dot */138+#live { font: 11px var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40vw; }139+#live .live-on { color: var(--pass); }140+#live .live-off { color: var(--faint); }141+#live .live-busy { color: var(--warn); animation: live-pulse 1s ease-in-out infinite alternate; }142+@keyframes live-pulse { to { opacity: 0.4; } }145143 146144 main.wrap { padding-top: 20px; padding-bottom: 40px; }147145 main.wrap > :first-child { margin-top: 0; }
@@ -203,12 +201,13 @@ table.list tbody tr.sel td:first-child { box-shadow: inset 2px 0 0 var(--accent)
203201 .hide-sm { display: none; }204202 }205203 206-/* statuses and CI */207-.status { font-weight: 600; white-space: nowrap; }208-.status.open { color: var(--pass); }209-.status.draft { color: var(--muted); }210-.status.merged { color: var(--merged); }211-.status.closed { color: var(--fail); }204+/* statuses and CI. A review's status is the one filled shape: a small205+ square label, white on the status colour (the same in both modes, so the206+ white stays readable). */207+.status { display: inline-block; font: 600 10px/1.5 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: #fff; background: #5b6169; padding: 0 6px; white-space: nowrap; vertical-align: 1px; }208+.status.open { background: #0e8420; }209+.status.merged { background: #3148f6; }210+.status.closed { background: #c7162b; }212211 .ci { white-space: nowrap; font-weight: 600; }213212 .ci[title] { cursor: help; }214213 .ci.pass { color: var(--pass); }
@@ -371,18 +370,17 @@ details.att pre, details.howto pre { white-space: pre-wrap; overflow-wrap: anywh
371370 .markdown h2 { font-size: 1.25em; }372371 }373372 /* jev: commits and repos */374-.jev-kind, .jev-topic { display: inline-block; padding: 0 6px; border: 1px solid var(--border); font-size: 0.75rem; line-height: 1.5; font-weight: 500; color: var(--muted); white-space: nowrap; vertical-align: 1px; }375-.jev-kind.feat { color: var(--pass); }376-.jev-kind.fix { color: var(--merged); }373+/* What Jev says a commit or a repo is: plain words, quiet. */374+.jev-kind { font: 12px var(--mono); color: var(--muted); white-space: nowrap; }375+.jev-topic { color: var(--muted); white-space: nowrap; }377376 /* Jev's risk score, 0 to 3: a small number, colored once it is moderate. */378-.jev-score { font-size: 0.8rem; font-variant-numeric: tabular-nums; color: var(--faint); white-space: nowrap; }377+.jev-score { font: 12px var(--mono); font-variant-numeric: tabular-nums; color: var(--faint); white-space: nowrap; }379378 .jev-score.moderate { color: var(--warn); }380379 .jev-score.high { color: var(--fail); font-weight: 600; }381380 .jev-mismatch { font-size: 0.85em; color: var(--faint); cursor: help; }382381 .jev-note { font-size: 0.8rem; font-style: italic; color: var(--faint); }383382 /* The log: Jev's type in a narrow column on the left. */384383 table.log th.kind, table.log td.kind { width: 1%; white-space: nowrap; padding-right: 4px; }385-table.log td.kind .jev-kind { vertical-align: 0; }386384 table.log td.risk { width: 1%; }387385 @media (max-width: 720px) {388386 /* Stacked: the type leads the line under the subject. */
@@ -400,8 +398,8 @@ table.log td.risk { width: 1%; }
400398 .notes summary:hover { color: var(--fg); }401399 402400 /* jev: review pages */403-.jev-mech, .jev-change { display: inline-block; padding: 0 6px; border: 1px dashed currentColor; font-size: 0.75rem; line-height: 1.5; font-weight: 500; color: var(--muted); white-space: nowrap; }401+.jev-mech, .jev-change { font: 12px var(--mono); color: var(--muted); white-space: nowrap; }404402 .jev-change.behavior, .jev-sum.behavior { color: var(--warn); }405403 .jev-sum { font-size: 0.9em; color: var(--muted); }406404 summary .jev-sum { margin-left: 6px; font-weight: normal; }407-.comment .tag.jev-addressed { color: var(--pass); border-style: dashed; }405+.comment .tag.jev-addressed { color: var(--pass); }
M site/repo/review-diff.njk +1 -0
M site/repo/revision.njk +1 -1