"use client";
import { useCallback, useEffect, useState } from "react";
import { api, ApiError } from "@/lib/api";
import type { ShelfIndex } from "@/lib/types";
/**
* A shelf of imported repositories, and what it says about who wrote them.
*
* One repo's provenance is trivia. A whole working directory's is a claim
* about how somebody works — and the honest version of that claim has to lead
* with how much of it is unknown. Ninety-odd percent of any real shelf will be
* "a person, assumed", because git records who ran the command and nothing
* else. The bar is drawn so the assumed share is the largest thing on screen,
* not a footnote under a confident number.
*/
export function ShelfView({ defaultPath }: { defaultPath: string }) {
const [path, setPath] = useState(defaultPath);
const [shelf, setShelf] = useState<ShelfIndex | null>(null);
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const [sort, setSort] = useState<"evidence" | "size" | "name">("evidence");
const load = useCallback(async (where: string) => {
setBusy(true);
setError(null);
try {
setShelf(await api.shelf(where));
} catch (e) {
setShelf(null);
setError(e instanceof ApiError ? e.message : "could not read that shelf");
} finally {
setBusy(false);
}
}, []);
useEffect(() => {
void load(defaultPath);
}, [defaultPath, load]);
const p = shelf?.provenance;
const total = p ? p.human + p.agent + p.mixed : 0;
const pct = (n: number) => (total ? Math.round((n / total) * 100) : 0);
const repos = shelf
? [...shelf.repos].sort((a, b) => {
if (sort === "name") return a.slug.localeCompare(b.slug);
if (sort === "size") return b.snapshots - a.snapshots;
const ev = (r: typeof a) => r.provenance.mixed + r.provenance.agent;
return ev(b) - ev(a) || a.slug.localeCompare(b.slug);
})
: [];
return (
<div className="jac-page jac-page--wide">
<p className="jac-eyebrow">The shelf</p>
<h1 className="jac-h1">Everything you have written</h1>
<p className="jac-lede">
A bounded slice of every git repository on a directory, imported with
its provenance guessed from what the commits actually say. The number
that matters is not how much a machine wrote — it is how much nobody
recorded either way.
</p>
<div className="jac-remark-actions" style={{ marginBottom: 18 }}>
<input
className="jac-select"
style={{ minWidth: 300 }}
value={path}
onChange={(e) => setPath(e.target.value)}
/>
<button type="button" className="jac-btn" onClick={() => void load(path)}>
{busy ? "Reading…" : "Read it"}
</button>
</div>
{error ? (
<div className="jac-shelf-empty">
<p className="jac-small jac-error">{error}</p>
<p className="jac-small">
Build one with:{" "}
<code className="jac-mono">
cargo run -p jac-serve -- --import-dir ~/github/one --out ~/jack/one
</code>
</p>
</div>
) : null}
{shelf && p ? (
<>
<div className="jac-shelf-summary">
<div className="jac-shelf-counts">
<span>
<b>{shelf.imported}</b> repos
</span>
<span>
<b>{total}</b> snapshots
</span>
{shelf.failed > 0 ? (
<span className="jac-shelf-failed">
<b>{shelf.failed}</b> could not be read
</span>
) : null}
</div>
{/* Assumed is drawn first and widest: it is the finding. */}
<div className="jac-shelf-bar" aria-hidden="true">
<span
className="jac-shelf-seg"
data-kind="assumed"
style={{ width: `${pct(p.assumed)}%` }}
/>
<span
className="jac-shelf-seg"
data-kind="mixed"
style={{ width: `${pct(p.mixed)}%` }}
/>
<span
className="jac-shelf-seg"
data-kind="agent"
style={{ width: `${pct(p.agent)}%` }}
/>
</div>
<ul className="jac-shelf-legend">
<li data-kind="assumed">
<b>{pct(p.assumed)}%</b> a person, <em>assumed</em> — {p.assumed}{" "}
snapshots where nothing recorded whose hands made the work
</li>
<li data-kind="mixed">
<b>{pct(p.mixed)}%</b> mixed, from evidence — {p.mixed} carry a
co-author trailer naming a model
</li>
<li data-kind="agent">
<b>{pct(p.agent)}%</b> agent, from evidence — {p.agent} were
authored by a bot
</li>
</ul>
<p className="jac-import-honesty">
Only the second and third lines are findings. The first is the
absence of one: git records who ran the command, so a human label
here means nobody wrote anything down, not that a person wrote the
code. Closing that gap takes a person saying so.
</p>
</div>
<div className="jac-seg" style={{ margin: "20px 0 10px" }}>
{(["evidence", "size", "name"] as const).map((s) => (
<button key={s} type="button" data-on={sort === s} onClick={() => setSort(s)}>
{s === "evidence" ? "most evidence" : s === "size" ? "most snapshots" : "name"}
</button>
))}
</div>
<ol className="jac-shelf-list">
{repos.map((r) => {
const rt = r.provenance.human + r.provenance.agent + r.provenance.mixed;
const w = (n: number) => (rt ? (n / rt) * 100 : 0);
return (
<li key={r.slug}>
<span className="jac-shelf-name">{r.slug}</span>
<span className="jac-shelf-mini" aria-hidden="true">
<span data-kind="assumed" style={{ width: `${w(r.provenance.assumed)}%` }} />
<span data-kind="mixed" style={{ width: `${w(r.provenance.mixed)}%` }} />
<span data-kind="agent" style={{ width: `${w(r.provenance.agent)}%` }} />
</span>
<span className="jac-shelf-nums">
{r.provenance.mixed > 0 ? (
<b title="co-authored by a model">{r.provenance.mixed} mixed</b>
) : null}
{r.provenance.agent > 0 ? (
<i title="authored by a bot">{r.provenance.agent} agent</i>
) : null}
<span>{r.snapshots} snaps</span>
</span>
</li>
);
})}
</ol>
{shelf.failures.length > 0 ? (
<details className="jac-shelf-failures">
<summary>{shelf.failures.length} could not be read</summary>
<ul>
{shelf.failures.map((f) => (
<li key={f.repo}>
<code className="jac-mono">{f.repo}</code> — {f.why}
</li>
))}
</ul>
</details>
) : null}
</>
) : null}
</div>
);
}