jacquardSnapshot

← snapshot

7548 bytes
"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>
  );
}