HASH 849542ce02a9
DATE 2026-07-18
SUBJECT web: git header becomes navigation, clickable paths, recent commit bar, prod
repo root env
FILES 14 CHANGED HASH 849542ce02a9
DATE 2026-07-18
SUBJECT web: git header becomes navigation,
clickable paths, recent commit bar,
prod repo root env
FILES 14 CHANGED ┌─ .../.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json ── [ COPY ] ─┐
│ diff --git a/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/re │
│ sults.json b/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/re │
│ sults.json │
│ new file mode 100644 │
│ index 0000000..7c4663b │
│ --- /dev/null │
│ +++ b/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.j │
│ son │
│ @@ -0,0 +1 @@ │
│ +{"version":"4.1.10","results":[[":src/components/GitPath/GitPath.test.ts",{"durat │
│ ion":0,"failed":true}]]} │
│ \ No newline at end of file │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...95601890afd80709/results.json ── [ COPY ] ─┐
│ diff --git a/node_modules/.vite/vitest/da39a │
│ 3ee5e6b4b0d3255bfef95601890afd80709/results. │
│ json b/node_modules/.vite/vitest/da39a3ee5e6 │
│ b4b0d3255bfef95601890afd80709/results.json │
│ new file mode 100644 │
│ index 0000000..7c4663b │
│ --- /dev/null │
│ +++ b/node_modules/.vite/vitest/da39a3ee5e6b │
│ 4b0d3255bfef95601890afd80709/results.json │
│ @@ -0,0 +1 @@ │
│ +{"version":"4.1.10","results":[[":src/compo │
│ nents/GitPath/GitPath.test.ts",{"duration":0 │
│ ,"failed":true}]]} │
│ \ No newline at end of file │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitHeader/GitHeader.astro ────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitHeader/GitHeader.astro b/ooknet-desig │
│ n/src/components/GitHeader/GitHeader.astro │
│ new file mode 100644 │
│ index 0000000..801f06e │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader/GitHeader.astro │
│ @@ -0,0 +1,20 @@ │
│ +--- │
│ +import Pre from "../Pre/Pre.astro"; │
│ +import Frame from "../Frame/Frame.astro"; │
│ +import { rule } from "../../lib/ascii"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from "../../lib/config"; │
│ +import { GIT_LEFT, REPO_KEY, headerGap, type Props } from "./GitHeader"; │
│ +import "./GitHeader.scss"; │
│ + │
│ +const { name, slug } = Astro.props as Props; │
│ +const variants: [string, number][] = [ │
│ + ["frame-wide", FRAME_W], │
│ + ["frame-narrow", MOBILE_FRAME_W], │
│ +]; │
│ +--- │
│ +<nav class="git-header" aria-label="Repository"> │
│ + {variants.map(([cls, w]) => ( │
│ + <Pre class={cls}><a class="git-link strong" href="/git/">{GIT_LEFT}</a>{heade │
│ rGap(name, w)}{REPO_KEY}<a class="git-link" href={`/git/${slug}/`}>{name.toUpperCa │
│ se()}</a></Pre> │
│ + ))} │
│ + <Frame build={(w) => rule(w)} /> │
│ +</nav> │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...nts/GitHeader/GitHeader.astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tHeader/GitHeader.astro b/ooknet-design/src/ │
│ components/GitHeader/GitHeader.astro │
│ new file mode 100644 │
│ index 0000000..801f06e │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader │
│ /GitHeader.astro │
│ @@ -0,0 +1,20 @@ │
│ +--- │
│ +import Pre from "../Pre/Pre.astro"; │
│ +import Frame from "../Frame/Frame.astro"; │
│ +import { rule } from "../../lib/ascii"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from ".. │
│ /../lib/config"; │
│ +import { GIT_LEFT, REPO_KEY, headerGap, typ │
│ e Props } from "./GitHeader"; │
│ +import "./GitHeader.scss"; │
│ + │
│ +const { name, slug } = Astro.props as Props │
│ ; │
│ +const variants: [string, number][] = [ │
│ + ["frame-wide", FRAME_W], │
│ + ["frame-narrow", MOBILE_FRAME_W], │
│ +]; │
│ +--- │
│ +<nav class="git-header" aria-label="Reposit │
│ ory"> │
│ + {variants.map(([cls, w]) => ( │
│ + <Pre class={cls}><a class="git-link str │
│ ong" href="/git/">{GIT_LEFT}</a>{headerGap(n │
│ ame, w)}{REPO_KEY}<a class="git-link" href={ │
│ `/git/${slug}/`}>{name.toUpperCase()}</a></P │
│ re> │
│ + ))} │
│ + <Frame build={(w) => rule(w)} /> │
│ +</nav> │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitHeader/GitHeader.scss ─────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitHeader/GitHeader.scss b/ooknet-design │
│ /src/components/GitHeader/GitHeader.scss │
│ new file mode 100644 │
│ index 0000000..48a5c73 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader/GitHeader.scss │
│ @@ -0,0 +1,22 @@ │
│ +.git-header { │
│ + display: block; │
│ +} │
│ + │
│ +.git-link { │
│ + color: var(--ink); │
│ + text-decoration: none; │
│ + │
│ + &.strong { │
│ + font-weight: 600; │
│ + } │
│ + │
│ + &:hover { │
│ + text-decoration: underline; │
│ + text-underline-offset: 3px; │
│ + } │
│ + │
│ + &:focus-visible { │
│ + outline: 1px dashed var(--ink); │
│ + outline-offset: 1px; │
│ + } │
│ +} │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...ents/GitHeader/GitHeader.scss ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tHeader/GitHeader.scss b/ooknet-design/src/c │
│ omponents/GitHeader/GitHeader.scss │
│ new file mode 100644 │
│ index 0000000..48a5c73 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader │
│ /GitHeader.scss │
│ @@ -0,0 +1,22 @@ │
│ +.git-header { │
│ + display: block; │
│ +} │
│ + │
│ +.git-link { │
│ + color: var(--ink); │
│ + text-decoration: none; │
│ + │
│ + &.strong { │
│ + font-weight: 600; │
│ + } │
│ + │
│ + &:hover { │
│ + text-decoration: underline; │
│ + text-underline-offset: 3px; │
│ + } │
│ + │
│ + &:focus-visible { │
│ + outline: 1px dashed var(--ink); │
│ + outline-offset: 1px; │
│ + } │
│ +} │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitHeader/GitHeader.ts ───────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitHeader/GitHeader.ts b/ooknet-design/s │
│ rc/components/GitHeader/GitHeader.ts │
│ new file mode 100644 │
│ index 0000000..2249c1d │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader/GitHeader.ts │
│ @@ -0,0 +1,19 @@ │
│ +// Git navigation header: OOKNET GIT on the left links to the index, │
│ +// the repo name on the right links to the repo home. Layout math only; │
│ +// the anchors live in the astro file. │
│ +import { len } from "../../lib/ascii"; │
│ + │
│ +export interface Props { │
│ + /** Display name, e.g. "ooknet.org". */ │
│ + name: string; │
│ + slug: string; │
│ +} │
│ + │
│ +export const GIT_LEFT = "OOKNET GIT"; │
│ +export const REPO_KEY = "REPO: "; │
│ + │
│ +export function headerGap(name: string, width: number): string { │
│ + return " ".repeat( │
│ + Math.max(1, width - len(GIT_LEFT) - len(REPO_KEY) - len(name.toUpperCase())), │
│ + ); │
│ +} │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...onents/GitHeader/GitHeader.ts ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tHeader/GitHeader.ts b/ooknet-design/src/com │
│ ponents/GitHeader/GitHeader.ts │
│ new file mode 100644 │
│ index 0000000..2249c1d │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitHeader │
│ /GitHeader.ts │
│ @@ -0,0 +1,19 @@ │
│ +// Git navigation header: OOKNET GIT on the │
│ left links to the index, │
│ +// the repo name on the right links to the │
│ repo home. Layout math only; │
│ +// the anchors live in the astro file. │
│ +import { len } from "../../lib/ascii"; │
│ + │
│ +export interface Props { │
│ + /** Display name, e.g. "ooknet.org". */ │
│ + name: string; │
│ + slug: string; │
│ +} │
│ + │
│ +export const GIT_LEFT = "OOKNET GIT"; │
│ +export const REPO_KEY = "REPO: "; │
│ + │
│ +export function headerGap(name: string, wid │
│ th: number): string { │
│ + return " ".repeat( │
│ + Math.max(1, width - len(GIT_LEFT) - len │
│ (REPO_KEY) - len(name.toUpperCase())), │
│ + ); │
│ +} │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitPath/GitPath.astro ────────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitPath/GitPath.astro b/ooknet-design/sr │
│ c/components/GitPath/GitPath.astro │
│ new file mode 100644 │
│ index 0000000..f53c769 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/GitPath.astro │
│ @@ -0,0 +1,34 @@ │
│ +--- │
│ +import Pre from "../Pre/Pre.astro"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from "../../lib/config"; │
│ +import { layoutPath, pathSegs, type PathSeg, type Props } from "./GitPath"; │
│ +import "../GitHeader/GitHeader.scss"; │
│ + │
│ +const { repo, branch, path, linkLeaf = false } = Astro.props as Props; │
│ +const segs = pathSegs(repo, branch, path, linkLeaf); │
│ +const variants: [string, PathSeg[][]][] = [ │
│ + ["frame-wide", layoutPath(segs, FRAME_W)], │
│ + ["frame-narrow", layoutPath(segs, MOBILE_FRAME_W)], │
│ +]; │
│ +--- │
│ +<nav class="git-path" aria-label="Path"> │
│ + {variants.map(([cls, rows]) => │
│ + rows.map((row, i) => ( │
│ + <Pre class={cls}>{i > 0 ? " " : ""}{row.map((s, j) => ( │
│ + <Fragment>{j > 0 || i > 0 ? " / " : ""}{s.href │
│ + ? <a class="git-link" href={s.href}>{s.label}</a> │
│ + : <span class="git-path-leaf">{s.label}</span>}</Fragment> │
│ + ))}</Pre> │
│ + )) │
│ + )} │
│ +</nav> │
│ + │
│ +<style lang="scss"> │
│ + .git-path { │
│ + display: block; │
│ + │
│ + .git-path-leaf { │
│ + font-weight: 600; │
│ + } │
│ + } │
│ +</style> │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...ponents/GitPath/GitPath.astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tPath/GitPath.astro b/ooknet-design/src/comp │
│ onents/GitPath/GitPath.astro │
│ new file mode 100644 │
│ index 0000000..f53c769 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/G │
│ itPath.astro │
│ @@ -0,0 +1,34 @@ │
│ +--- │
│ +import Pre from "../Pre/Pre.astro"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from ".. │
│ /../lib/config"; │
│ +import { layoutPath, pathSegs, type PathSeg │
│ , type Props } from "./GitPath"; │
│ +import "../GitHeader/GitHeader.scss"; │
│ + │
│ +const { repo, branch, path, linkLeaf = fals │
│ e } = Astro.props as Props; │
│ +const segs = pathSegs(repo, branch, path, l │
│ inkLeaf); │
│ +const variants: [string, PathSeg[][]][] = [ │
│ + ["frame-wide", layoutPath(segs, FRAME_W)] │
│ , │
│ + ["frame-narrow", layoutPath(segs, MOBILE_ │
│ FRAME_W)], │
│ +]; │
│ +--- │
│ +<nav class="git-path" aria-label="Path"> │
│ + {variants.map(([cls, rows]) => │
│ + rows.map((row, i) => ( │
│ + <Pre class={cls}>{i > 0 ? " " : ""}{ │
│ row.map((s, j) => ( │
│ + <Fragment>{j > 0 || i > 0 ? " / " : │
│ ""}{s.href │
│ + ? <a class="git-link" href={s.hre │
│ f}>{s.label}</a> │
│ + : <span class="git-path-leaf">{s. │
│ label}</span>}</Fragment> │
│ + ))}</Pre> │
│ + )) │
│ + )} │
│ +</nav> │
│ + │
│ +<style lang="scss"> │
│ + .git-path { │
│ + display: block; │
│ + │
│ + .git-path-leaf { │
│ + font-weight: 600; │
│ + } │
│ + } │
│ +</style> │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitPath/GitPath.test.ts ──────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitPath/GitPath.test.ts b/ooknet-design/ │
│ src/components/GitPath/GitPath.test.ts │
│ new file mode 100644 │
│ index 0000000..ca68143 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/GitPath.test.ts │
│ @@ -0,0 +1,29 @@ │
│ +import { describe, expect, it } from "vitest"; │
│ +import { layoutPath, pathSegs } from "./GitPath"; │
│ + │
│ +const repo = { name: "ooknet.org", slug: "ooknet-org" }; │
│ + │
│ +describe("pathSegs", () => { │
│ + it("links every segment but the leaf", () => { │
│ + const segs = pathSegs(repo, "master", "src/lib/git.ts"); │
│ + expect(segs.map((s) => s.label)).toEqual(["ooknet.org", "src", "lib", "git.ts │
│ "]); │
│ + expect(segs[0].href).toBe("/git/ooknet-org/"); │
│ + expect(segs[1].href).toBe("/git/ooknet-org/tree/master/src/"); │
│ + expect(segs[2].href).toBe("/git/ooknet-org/tree/master/src/lib/"); │
│ + expect(segs[3].href).toBeUndefined(); │
│ + }); │
│ + │
│ + it("links the leaf on tree pages", () => { │
│ + const segs = pathSegs(repo, "master", "src/lib", true); │
│ + expect(segs[2].href).toBe("/git/ooknet-org/tree/master/src/lib/"); │
│ + }); │
│ +}); │
│ + │
│ +describe("layoutPath", () => { │
│ + it("wraps long paths at segment boundaries", () => { │
│ + const segs = pathSegs(repo, "master", "a-long-directory/another-long-one/deep │
│ er/file.ts"); │
│ + const rows = layoutPath(segs, 30); │
│ + expect(rows.length).toBeGreaterThan(1); │
│ + expect(rows.flat().map((s) => s.label)).toEqual(segs.map((s) => s.label)); │
│ + }); │
│ +}); │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...nents/GitPath/GitPath.test.ts ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tPath/GitPath.test.ts b/ooknet-design/src/co │
│ mponents/GitPath/GitPath.test.ts │
│ new file mode 100644 │
│ index 0000000..ca68143 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/G │
│ itPath.test.ts │
│ @@ -0,0 +1,29 @@ │
│ +import { describe, expect, it } from "vites │
│ t"; │
│ +import { layoutPath, pathSegs } from "./Git │
│ Path"; │
│ + │
│ +const repo = { name: "ooknet.org", slug: "o │
│ oknet-org" }; │
│ + │
│ +describe("pathSegs", () => { │
│ + it("links every segment but the leaf", () │
│ => { │
│ + const segs = pathSegs(repo, "master", " │
│ src/lib/git.ts"); │
│ + expect(segs.map((s) => s.label)).toEqua │
│ l(["ooknet.org", "src", "lib", "git.ts"]); │
│ + expect(segs[0].href).toBe("/git/ooknet- │
│ org/"); │
│ + expect(segs[1].href).toBe("/git/ooknet- │
│ org/tree/master/src/"); │
│ + expect(segs[2].href).toBe("/git/ooknet- │
│ org/tree/master/src/lib/"); │
│ + expect(segs[3].href).toBeUndefined(); │
│ + }); │
│ + │
│ + it("links the leaf on tree pages", () => │
│ { │
│ + const segs = pathSegs(repo, "master", " │
│ src/lib", true); │
│ + expect(segs[2].href).toBe("/git/ooknet- │
│ org/tree/master/src/lib/"); │
│ + }); │
│ +}); │
│ + │
│ +describe("layoutPath", () => { │
│ + it("wraps long paths at segment boundarie │
│ s", () => { │
│ + const segs = pathSegs(repo, "master", " │
│ a-long-directory/another-long-one/deeper/fil │
│ e.ts"); │
│ + const rows = layoutPath(segs, 30); │
│ + expect(rows.length).toBeGreaterThan(1); │
│ + expect(rows.flat().map((s) => s.label)) │
│ .toEqual(segs.map((s) => s.label)); │
│ + }); │
│ +}); │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/components/GitPath/GitPath.ts ───────────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/GitPath/GitPath.ts b/ooknet-design/src/c │
│ omponents/GitPath/GitPath.ts │
│ new file mode 100644 │
│ index 0000000..5547942 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/GitPath.ts │
│ @@ -0,0 +1,56 @@ │
│ +// Clickable file path, github-style: repo / dir / dir / leaf. Every │
│ +// segment but the last links to its tree page; long paths wrap at │
│ +// segment boundaries. │
│ +import { checkGrid, len } from "../../lib/ascii"; │
│ + │
│ +export interface PathSeg { │
│ + label: string; │
│ + href?: string; │
│ +} │
│ + │
│ +export interface Props { │
│ + repo: { name: string; slug: string }; │
│ + branch: string; │
│ + /** Path within the repo; the leaf gets no link. */ │
│ + path: string; │
│ + /** Link the leaf too (tree pages: current dir is the leaf). */ │
│ + linkLeaf?: boolean; │
│ +} │
│ + │
│ +export function pathSegs( │
│ + repo: { name: string; slug: string }, │
│ + branch: string, │
│ + path: string, │
│ + linkLeaf = false, │
│ +): PathSeg[] { │
│ + const segs: PathSeg[] = [{ label: repo.name, href: `/git/${repo.slug}/` }]; │
│ + const parts = path.split("/").filter(Boolean); │
│ + parts.forEach((part, i) => { │
│ + const isLeaf = i === parts.length - 1; │
│ + const sub = parts.slice(0, i + 1).join("/"); │
│ + segs.push({ │
│ + label: part, │
│ + href: !isLeaf || linkLeaf ? `/git/${repo.slug}/tree/${branch}/${sub}/` : un │
│ defined, │
│ + }); │
│ + }); │
│ + if (!linkLeaf && segs.length > 1) delete segs[segs.length - 1].href; │
│ + return segs; │
│ +} │
│ + │
│ +/** Rows of segments fitting `width`; each costs " / " after the first. */ │
│ +export function layoutPath(segs: PathSeg[], width: number): PathSeg[][] { │
│ + const rows: PathSeg[][] = [[]]; │
│ + let used = 0; │
│ + for (const s of segs) { │
│ + checkGrid(s.label, width, "git path segment"); │
│ + const w = len(s.label) + (rows[rows.length - 1].length ? 3 : 0); │
│ + if (rows[rows.length - 1].length && used + w > width) { │
│ + rows.push([s]); │
│ + used = len(s.label); │
│ + } else { │
│ + rows[rows.length - 1].push(s); │
│ + used += w; │
│ + } │
│ + } │
│ + return rows; │
│ +} │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...components/GitPath/GitPath.ts ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/components/Gi │
│ tPath/GitPath.ts b/ooknet-design/src/compone │
│ nts/GitPath/GitPath.ts │
│ new file mode 100644 │
│ index 0000000..5547942 │
│ --- /dev/null │
│ +++ b/ooknet-design/src/components/GitPath/G │
│ itPath.ts │
│ @@ -0,0 +1,56 @@ │
│ +// Clickable file path, github-style: repo │
│ / dir / dir / leaf. Every │
│ +// segment but the last links to its tree p │
│ age; long paths wrap at │
│ +// segment boundaries. │
│ +import { checkGrid, len } from "../../lib/a │
│ scii"; │
│ + │
│ +export interface PathSeg { │
│ + label: string; │
│ + href?: string; │
│ +} │
│ + │
│ +export interface Props { │
│ + repo: { name: string; slug: string }; │
│ + branch: string; │
│ + /** Path within the repo; the leaf gets n │
│ o link. */ │
│ + path: string; │
│ + /** Link the leaf too (tree pages: curren │
│ t dir is the leaf). */ │
│ + linkLeaf?: boolean; │
│ +} │
│ + │
│ +export function pathSegs( │
│ + repo: { name: string; slug: string }, │
│ + branch: string, │
│ + path: string, │
│ + linkLeaf = false, │
│ +): PathSeg[] { │
│ + const segs: PathSeg[] = [{ label: repo.na │
│ me, href: `/git/${repo.slug}/` }]; │
│ + const parts = path.split("/").filter(Bool │
│ ean); │
│ + parts.forEach((part, i) => { │
│ + const isLeaf = i === parts.length - 1; │
│ + const sub = parts.slice(0, i + 1).join( │
│ "/"); │
│ + segs.push({ │
│ + label: part, │
│ + href: !isLeaf || linkLeaf ? `/git/${r │
│ epo.slug}/tree/${branch}/${sub}/` : undefine │
│ d, │
│ + }); │
│ + }); │
│ + if (!linkLeaf && segs.length > 1) delete │
│ segs[segs.length - 1].href; │
│ + return segs; │
│ +} │
│ + │
│ +/** Rows of segments fitting `width`; each │
│ costs " / " after the first. */ │
│ +export function layoutPath(segs: PathSeg[], │
│ width: number): PathSeg[][] { │
│ + const rows: PathSeg[][] = [[]]; │
│ + let used = 0; │
│ + for (const s of segs) { │
│ + checkGrid(s.label, width, "git path seg │
│ ment"); │
│ + const w = len(s.label) + (rows[rows.len │
│ gth - 1].length ? 3 : 0); │
│ + if (rows[rows.length - 1].length && use │
│ d + w > width) { │
│ + rows.push([s]); │
│ + used = len(s.label); │
│ + } else { │
│ + rows[rows.length - 1].push(s); │
│ + used += w; │
│ + } │
│ + } │
│ + return rows; │
│ +} │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/git.config.ts ───────────────────────────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/git.config.ts b/ooknet-design/src/git.config.ts │
│ index e9795bd..e5811a7 100644 │
│ --- a/ooknet-design/src/git.config.ts │
│ +++ b/ooknet-design/src/git.config.ts │
│ @@ -1,14 +1,18 @@ │
│ import type { GitRepo } from "./lib/git"; │
│ │
│ -// Repos rendered by /git/. Paths resolve from ooknet-design/ at build │
│ -// time; missing paths are skipped so any machine can build the site. │
│ -// On ooknode these point at forgejo's bare repositories │
│ -// (.../forgejo/data/forgejo-repositories/<owner>/<repo>.git). │
│ +// Repos rendered by /git/. In production the builder sets │
│ +// OOKNET_GIT_ROOT and paths resolve to bare mirrors │
│ +// ($OOKNET_GIT_ROOT/<slug>.git); on dev machines the fallback paths │
│ +// point at local checkouts. Missing paths are skipped either way, so │
│ +// any machine builds. See .internal/deploy-plan.md. │
│ +const ROOT = process.env.OOKNET_GIT_ROOT; │
│ +const at = (slug: string, dev: string) => (ROOT ? `${ROOT}/${slug}.git` : dev); │
│ + │
│ export const REPOS: GitRepo[] = [ │
│ { │
│ slug: "ooknet-org", │
│ name: "ooknet.org", │
│ - path: "..", │
│ + path: at("ooknet-org", ".."), │
│ desc: "personal website - a text-mode knowledge base and the ascii design sys │
│ tem that renders it", │
│ clone: "https://git.ooknet.org/ooks/ooknet.org.git", │
│ }, │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ooknet-design/src/git.config.ts ─── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/git.config.ts │
│ b/ooknet-design/src/git.config.ts │
│ index e9795bd..e5811a7 100644 │
│ --- a/ooknet-design/src/git.config.ts │
│ +++ b/ooknet-design/src/git.config.ts │
│ @@ -1,14 +1,18 @@ │
│ import type { GitRepo } from "./lib/git"; │
│ │
│ -// Repos rendered by /git/. Paths resolve f │
│ rom ooknet-design/ at build │
│ -// time; missing paths are skipped so any m │
│ achine can build the site. │
│ -// On ooknode these point at forgejo's bare │
│ repositories │
│ -// (.../forgejo/data/forgejo-repositories/< │
│ owner>/<repo>.git). │
│ +// Repos rendered by /git/. In production t │
│ he builder sets │
│ +// OOKNET_GIT_ROOT and paths resolve to bar │
│ e mirrors │
│ +// ($OOKNET_GIT_ROOT/<slug>.git); on dev ma │
│ chines the fallback paths │
│ +// point at local checkouts. Missing paths │
│ are skipped either way, so │
│ +// any machine builds. See .internal/deploy │
│ -plan.md. │
│ +const ROOT = process.env.OOKNET_GIT_ROOT; │
│ +const at = (slug: string, dev: string) => ( │
│ ROOT ? `${ROOT}/${slug}.git` : dev); │
│ + │
│ export const REPOS: GitRepo[] = [ │
│ { │
│ slug: "ooknet-org", │
│ name: "ooknet.org", │
│ - path: "..", │
│ + path: at("ooknet-org", ".."), │
│ desc: "personal website - a text-mode k │
│ nowledge base and the ascii design system th │
│ at renders it", │
│ clone: "https://git.ooknet.org/ooks/ook │
│ net.org.git", │
│ }, │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/blob/[...rest].astro ───────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/blob/[...rest].astro b/ooknet-desi │
│ gn/src/pages/git/[repo]/blob/[...rest].astro │
│ index 2c3a758..34d8762 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/blob/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/blob/[...rest].astro │
│ @@ -1,9 +1,9 @@ │
│ --- │
│ import ArticleLayout from "../../../../layouts/ArticleLayout/ArticleLayout.astro" │
│ ; │
│ import TopHeader from "../../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../components/ArticleHeader/ArticleHeader.ast │
│ ro"; │
│ +import GitHeader from "../../../../components/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components/GitPath/GitPath.astro"; │
│ import Pre from "../../../../components/Pre/Pre.astro"; │
│ -import KeyValue from "../../../../components/KeyValue/KeyValue.astro"; │
│ import CodeFrame from "../../../../components/CodeFrame/CodeFrame.astro"; │
│ import Button from "../../../../components/Button/Button.astro"; │
│ import ArticleFooter from "../../../../components/ArticleFooter/ArticleFooter.ast │
│ ro"; │
│ @@ -26,23 +26,14 @@ const { repo, branch, file } = Astro.props as { repo: GitRepo; │
│ branch: string; f │
│ const blob = readBlob(repo.path, file, branch); │
│ const name = file.split("/").pop()!; │
│ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : ""; │
│ -const chunk = (s: string, n: number) => { │
│ - const cps = [...s]; │
│ - return Array.from({ length: Math.ceil(cps.length / n) }, (_, i) => │
│ - cps.slice(i * n, (i + 1) * n).join("")); │
│ -}; │
│ -const pathRows = chunk(toGrid(file), 38); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - ${name}`} description={`${file} in ${ │
│ repo.name}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`REPO: ${repo.name.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ <Pre>{" "}</Pre> │
│ - <KeyValue entries={[ │
│ - ["FILE", pathRows[0]], │
│ - ...pathRows.slice(1).map((c) => ["", c] as [string, string]), │
│ - ["BRANCH", branch], │
│ - ["LINES", blob.binary ? "BINARY" : `${blob.lines}`], │
│ - ]} /> │
│ + <GitPath repo={{ name: repo.name, slug: repo.slug }} branch={branch} path={file │
│ } /> │
│ + <Pre>{" "}</Pre> │
│ + <Pre>{branch} @ {blob.binary ? "BINARY" : `${blob.lines} LINES`}</Pre> │
│ <Pre>{" "}</Pre> │
│ <Pre><Button label="HISTORY" href={`/git/${repo.slug}/history/${branch}/${file} │
│ /`} />{" "}<Button label="UP" href={dir ? `/git/${repo.slug}/tree/${branch}/${dir │
│ }/` : `/git/${repo.slug}/`} /></Pre> │
│ <Pre>{" "}</Pre> │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...t/[repo]/blob/[...rest].astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/blob/[...rest].astro b/ooknet-design/src │
│ /pages/git/[repo]/blob/[...rest].astro │
│ index 2c3a758..34d8762 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/blo │
│ b/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/blo │
│ b/[...rest].astro │
│ @@ -1,9 +1,9 @@ │
│ --- │
│ import ArticleLayout from "../../../../layo │
│ uts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../../componen │
│ ts/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../comp │
│ onents/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../../componen │
│ ts/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components │
│ /GitPath/GitPath.astro"; │
│ import Pre from "../../../../components/Pre │
│ /Pre.astro"; │
│ -import KeyValue from "../../../../component │
│ s/KeyValue/KeyValue.astro"; │
│ import CodeFrame from "../../../../componen │
│ ts/CodeFrame/CodeFrame.astro"; │
│ import Button from "../../../../components/ │
│ Button/Button.astro"; │
│ import ArticleFooter from "../../../../comp │
│ onents/ArticleFooter/ArticleFooter.astro"; │
│ @@ -26,23 +26,14 @@ const { repo, branch, fi │
│ le } = Astro.props as { repo: GitRepo; branc │
│ h: string; f │
│ const blob = readBlob(repo.path, file, bran │
│ ch); │
│ const name = file.split("/").pop()!; │
│ const dir = file.includes("/") ? file.slice │
│ (0, file.lastIndexOf("/")) : ""; │
│ -const chunk = (s: string, n: number) => { │
│ - const cps = [...s]; │
│ - return Array.from({ length: Math.ceil(cps │
│ .length / n) }, (_, i) => │
│ - cps.slice(i * n, (i + 1) * n).join("")) │
│ ; │
│ -}; │
│ -const pathRows = chunk(toGrid(file), 38); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - │
│ ${name}`} description={`${file} in ${repo.n │
│ ame}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ REPO: ${repo.name.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ <Pre>{" "}</Pre> │
│ - <KeyValue entries={[ │
│ - ["FILE", pathRows[0]], │
│ - ...pathRows.slice(1).map((c) => ["", c] │
│ as [string, string]), │
│ - ["BRANCH", branch], │
│ - ["LINES", blob.binary ? "BINARY" : `${b │
│ lob.lines}`], │
│ - ]} /> │
│ + <GitPath repo={{ name: repo.name, slug: r │
│ epo.slug }} branch={branch} path={file} /> │
│ + <Pre>{" "}</Pre> │
│ + <Pre>{branch} @ {blob.binary ? "BINARY" : │
│ `${blob.lines} LINES`}</Pre> │
│ <Pre>{" "}</Pre> │
│ <Pre><Button label="HISTORY" href={`/git/ │
│ ${repo.slug}/history/${branch}/${file}/`} /> │
│ {" "}<Button label="UP" href={dir ? `/git/$ │
│ {repo.slug}/tree/${branch}/${dir}/` : `/git/ │
│ ${repo.slug}/`} /></Pre> │
│ <Pre>{" "}</Pre> │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/commit/[hash].astro ────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/commit/[hash].astro b/ooknet-desig │
│ n/src/pages/git/[repo]/commit/[hash].astro │
│ index a262295..cf95bad 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/commit/[hash].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/commit/[hash].astro │
│ @@ -1,7 +1,7 @@ │
│ --- │
│ import ArticleLayout from "../../../../layouts/ArticleLayout/ArticleLayout.astro" │
│ ; │
│ import TopHeader from "../../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../components/ArticleHeader/ArticleHeader.ast │
│ ro"; │
│ +import GitHeader from "../../../../components/GitHeader/GitHeader.astro"; │
│ import Pre from "../../../../components/Pre/Pre.astro"; │
│ import KeyValue from "../../../../components/KeyValue/KeyValue.astro"; │
│ import CodeFrame from "../../../../components/CodeFrame/CodeFrame.astro"; │
│ @@ -27,7 +27,7 @@ const branch = defaultBranch(repo.path); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} @ ${hash}`} description={commit.subject │
│ }> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`COMMIT: ${hash!.toUpperCase()}`} date= │
│ {commit.date} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ <Pre>{" "}</Pre> │
│ <KeyValue entries={[ │
│ ["HASH", commit.hash.slice(0, 12)], │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...it/[repo]/commit/[hash].astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/commit/[hash].astro b/ooknet-design/src/ │
│ pages/git/[repo]/commit/[hash].astro │
│ index a262295..cf95bad 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/com │
│ mit/[hash].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/com │
│ mit/[hash].astro │
│ @@ -1,7 +1,7 @@ │
│ --- │
│ import ArticleLayout from "../../../../layo │
│ uts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../../componen │
│ ts/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../comp │
│ onents/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../../componen │
│ ts/GitHeader/GitHeader.astro"; │
│ import Pre from "../../../../components/Pre │
│ /Pre.astro"; │
│ import KeyValue from "../../../../component │
│ s/KeyValue/KeyValue.astro"; │
│ import CodeFrame from "../../../../componen │
│ ts/CodeFrame/CodeFrame.astro"; │
│ @@ -27,7 +27,7 @@ const branch = defaultBran │
│ ch(repo.path); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} @ │
│ ${hash}`} description={commit.subject}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ COMMIT: ${hash!.toUpperCase()}`} date={commi │
│ t.date} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ <Pre>{" "}</Pre> │
│ <KeyValue entries={[ │
│ ["HASH", commit.hash.slice(0, 12)], │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/history/[...rest].astro ────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/history/[...rest].astro b/ooknet-d │
│ esign/src/pages/git/[repo]/history/[...rest].astro │
│ index b303792..28fc3cf 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/history/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/history/[...rest].astro │
│ @@ -1,13 +1,12 @@ │
│ --- │
│ import ArticleLayout from "../../../../layouts/ArticleLayout/ArticleLayout.astro" │
│ ; │
│ import TopHeader from "../../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../components/ArticleHeader/ArticleHeader.ast │
│ ro"; │
│ +import GitHeader from "../../../../components/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components/GitPath/GitPath.astro"; │
│ import Pre from "../../../../components/Pre/Pre.astro"; │
│ -import KeyValue from "../../../../components/KeyValue/KeyValue.astro"; │
│ import Timeline from "../../../../components/Timeline/Timeline.astro"; │
│ import Button from "../../../../components/Button/Button.astro"; │
│ import ArticleFooter from "../../../../components/ArticleFooter/ArticleFooter.ast │
│ ro"; │
│ -import { toGrid } from "../../../../lib/ascii"; │
│ import { REPOS } from "../../../../git.config"; │
│ import { repoAvailable, branches, repoFiles, pathLog, type GitRepo } from "../../ │
│ ../../lib/git"; │
│ │
│ @@ -24,22 +23,14 @@ export function getStaticPaths() { │
│ │
│ const { repo, branch, file } = Astro.props as { repo: GitRepo; branch: string; fi │
│ le: string }; │
│ const log = pathLog(repo.path, file, 200, `/git/${repo.slug}/`, branch); │
│ -const chunk = (s: string, n: number) => { │
│ - const cps = [...s]; │
│ - return Array.from({ length: Math.ceil(cps.length / n) }, (_, i) => │
│ - cps.slice(i * n, (i + 1) * n).join("")); │
│ -}; │
│ -const pathRows = chunk(toGrid(file), 38); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - history of ${file}`} description={`Co │
│ mmits touching ${file} in ${repo.name}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`HISTORY @ ${branch.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ <Pre>{" "}</Pre> │
│ - <KeyValue entries={[ │
│ - ["FILE", pathRows[0]], │
│ - ...pathRows.slice(1).map((c) => ["", c] as [string, string]), │
│ - ["COMMITS", `${log.length}`], │
│ - ]} /> │
│ + <GitPath repo={{ name: repo.name, slug: repo.slug }} branch={branch} path={file │
│ } /> │
│ + <Pre>{" "}</Pre> │
│ + <Pre>HISTORY @ {branch} - {log.length} COMMITS</Pre> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
│ <Pre>{" "}</Pre> │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...repo]/history/[...rest].astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/history/[...rest].astro b/ooknet-design/ │
│ src/pages/git/[repo]/history/[...rest].astro │
│ index b303792..28fc3cf 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/his │
│ tory/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/his │
│ tory/[...rest].astro │
│ @@ -1,13 +1,12 @@ │
│ --- │
│ import ArticleLayout from "../../../../layo │
│ uts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../../componen │
│ ts/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../comp │
│ onents/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../../componen │
│ ts/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components │
│ /GitPath/GitPath.astro"; │
│ import Pre from "../../../../components/Pre │
│ /Pre.astro"; │
│ -import KeyValue from "../../../../component │
│ s/KeyValue/KeyValue.astro"; │
│ import Timeline from "../../../../component │
│ s/Timeline/Timeline.astro"; │
│ import Button from "../../../../components/ │
│ Button/Button.astro"; │
│ import ArticleFooter from "../../../../comp │
│ onents/ArticleFooter/ArticleFooter.astro"; │
│ -import { toGrid } from "../../../../lib/asc │
│ ii"; │
│ import { REPOS } from "../../../../git.conf │
│ ig"; │
│ import { repoAvailable, branches, repoFiles │
│ , pathLog, type GitRepo } from "../../../../ │
│ lib/git"; │
│ │
│ @@ -24,22 +23,14 @@ export function getStati │
│ cPaths() { │
│ │
│ const { repo, branch, file } = Astro.props │
│ as { repo: GitRepo; branch: string; file: st │
│ ring }; │
│ const log = pathLog(repo.path, file, 200, ` │
│ /git/${repo.slug}/`, branch); │
│ -const chunk = (s: string, n: number) => { │
│ - const cps = [...s]; │
│ - return Array.from({ length: Math.ceil(cps │
│ .length / n) }, (_, i) => │
│ - cps.slice(i * n, (i + 1) * n).join("")) │
│ ; │
│ -}; │
│ -const pathRows = chunk(toGrid(file), 38); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - │
│ history of ${file}`} description={`Commits │
│ touching ${file} in ${repo.name}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ HISTORY @ ${branch.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ <Pre>{" "}</Pre> │
│ - <KeyValue entries={[ │
│ - ["FILE", pathRows[0]], │
│ - ...pathRows.slice(1).map((c) => ["", c] │
│ as [string, string]), │
│ - ["COMMITS", `${log.length}`], │
│ - ]} /> │
│ + <GitPath repo={{ name: repo.name, slug: r │
│ epo.slug }} branch={branch} path={file} /> │
│ + <Pre>{" "}</Pre> │
│ + <Pre>HISTORY @ {branch} - {log.length} CO │
│ MMITS</Pre> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
│ <Pre>{" "}</Pre> │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/index.astro ────────────────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/index.astro b/ooknet-design/src/pa │
│ ges/git/[repo]/index.astro │
│ index 469f7e3..84a8dbe 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/index.astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/index.astro │
│ @@ -1,17 +1,17 @@ │
│ --- │
│ import ArticleLayout from "../../../layouts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../components/ArticleHeader/ArticleHeader.astro" │
│ ; │
│ +import GitHeader from "../../../components/GitHeader/GitHeader.astro"; │
│ import Frame from "../../../components/Frame/Frame.astro"; │
│ import Pre from "../../../components/Pre/Pre.astro"; │
│ -import ArticleMeta from "../../../components/ArticleMeta/ArticleMeta.astro"; │
│ import Menu from "../../../components/Menu/Menu.astro"; │
│ import GitListing from "../../../components/GitListing/GitListing.astro"; │
│ import Timeline from "../../../components/Timeline/Timeline.astro"; │
│ import Button from "../../../components/Button/Button.astro"; │
│ import Prose from "../../../components/Prose/Prose.astro"; │
│ import ArticleFooter from "../../../components/ArticleFooter/ArticleFooter.astro" │
│ ; │
│ -import { row2, ruleD } from "../../../lib/ascii"; │
│ +import { len, row2, ruleD } from "../../../lib/ascii"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from "../../../lib/config"; │
│ import { renderMarkdown } from "../../../lib/markdown"; │
│ import { REPOS } from "../../../git.config"; │
│ import { repoAvailable, repoInfo, branches, dirEntries, repoLog, readReadme, type │
│ GitRepo } from "../../../lib/git"; │
│ @@ -34,25 +34,47 @@ const readmeHtml = readme ? renderMarkdown(readme) : null; │
│ │
│ const section = (title: string, right: string) => (w: number) => │
│ [row2(title, right, w), ruleD(w)].join("\n"); │
│ + │
│ +// recent-commit bar: [hash] subject ... date │
│ +const recentRow = (w: number) => { │
│ + const hash = `[${info.lastHash}]`; │
│ + const avail = w - len(hash) - 1 - 12; │
│ + const subject = │
│ + len(info.lastSubject) > avail │
│ + ? [...info.lastSubject].slice(0, avail - 3).join("") + "..." │
│ + : info.lastSubject; │
│ + const gap = " ".repeat(Math.max(2, w - len(hash) - 1 - len(subject) - len(info. │
│ lastDate))); │
│ + return { hash, subject, gap }; │
│ +}; │
│ +const commitsLabel = `COMMITS: ${info.commits}`; │
│ +const variants: [string, number][] = [ │
│ + ["frame-wide", FRAME_W], │
│ + ["frame-narrow", MOBILE_FRAME_W], │
│ +]; │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name}`} description={repo.desc}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`REPO: ${repo.name.toUpperCase()}`} dat │
│ e={info.lastDate} /> │
│ - <Pre>{" "}</Pre> │
│ - <ArticleMeta entries={[ │
│ - { key: "COMMITS", value: `${info.commits}`, href: `/git/${repo.slug}/log/${br │
│ anch}/` }, │
│ - { key: "RECENT", value: `${info.lastHash} ${info.lastSubject}`, href: `/git/$ │
│ {repo.slug}/commit/${info.lastHash}/` }, │
│ - ...(repo.clone ? [{ key: "CLONE", value: repo.clone.replace(/^https?:\/\//, " │
│ ") }] : []), │
│ - ]} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ + {repo.clone && <Pre>CLONE {repo.clone.replace(/^https?:\/\//, "")}</Pre>} │
│ <Pre>{" "}</Pre> │
│ │
│ - <Frame build={section("FILES", `${entries.length} ENTRIES`)} /> │
│ + {variants.map(([cls, w]) => ( │
│ + <Pre class={cls}>FILES{" ".repeat(Math.max(1, w - 5 - len(commitsLabel)))}<a │
│ class="git-link" href={`/git/${repo.slug}/log/${branch}/`}>{commitsLabel}</a></Pre │
│ > │
│ + ))} │
│ + <Frame build={(w) => ruleD(w)} /> │
│ <Pre>{" "}</Pre> │
│ <Pre><Menu label={`BRANCH: ${branch}`} items={allBranches.map((b) => ({ │
│ label: b, │
│ href: b === branch ? `/git/${repo.slug}/` : `/git/${repo.slug}/tree/${b}/`, │
│ }))} /></Pre> │
│ <Pre>{" "}</Pre> │
│ + {variants.map(([cls, w]) => { │
│ + const r = recentRow(w); │
│ + return ( │
│ + <Pre class={cls}><a class="git-link" href={`/git/${repo.slug}/commit/${info │
│ .lastHash}/`}>{r.hash}</a>{" "}{r.subject}{r.gap}{info.lastDate}</Pre> │
│ + ); │
│ + })} │
│ + <Pre>{" "}</Pre> │
│ <GitListing entries={entries} │
│ treeBase={`/git/${repo.slug}/tree/${branch}/`} │
│ blobBase={`/git/${repo.slug}/blob/${branch}/`} │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ .../pages/git/[repo]/index.astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/index.astro b/ooknet-design/src/pages/gi │
│ t/[repo]/index.astro │
│ index 469f7e3..84a8dbe 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/ind │
│ ex.astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/ind │
│ ex.astro │
│ @@ -1,17 +1,17 @@ │
│ --- │
│ import ArticleLayout from "../../../layouts │
│ /ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../components/ │
│ TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../compone │
│ nts/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../components/ │
│ GitHeader/GitHeader.astro"; │
│ import Frame from "../../../components/Fram │
│ e/Frame.astro"; │
│ import Pre from "../../../components/Pre/Pr │
│ e.astro"; │
│ -import ArticleMeta from "../../../component │
│ s/ArticleMeta/ArticleMeta.astro"; │
│ import Menu from "../../../components/Menu/ │
│ Menu.astro"; │
│ import GitListing from "../../../components │
│ /GitListing/GitListing.astro"; │
│ import Timeline from "../../../components/T │
│ imeline/Timeline.astro"; │
│ import Button from "../../../components/But │
│ ton/Button.astro"; │
│ import Prose from "../../../components/Pros │
│ e/Prose.astro"; │
│ import ArticleFooter from "../../../compone │
│ nts/ArticleFooter/ArticleFooter.astro"; │
│ -import { row2, ruleD } from "../../../lib/a │
│ scii"; │
│ +import { len, row2, ruleD } from "../../../ │
│ lib/ascii"; │
│ +import { FRAME_W, MOBILE_FRAME_W } from ".. │
│ /../../lib/config"; │
│ import { renderMarkdown } from "../../../li │
│ b/markdown"; │
│ import { REPOS } from "../../../git.config" │
│ ; │
│ import { repoAvailable, repoInfo, branches, │
│ dirEntries, repoLog, readReadme, type GitRe │
│ po } from "../../../lib/git"; │
│ @@ -34,25 +34,47 @@ const readmeHtml = readm │
│ e ? renderMarkdown(readme) : null; │
│ │
│ const section = (title: string, right: stri │
│ ng) => (w: number) => │
│ [row2(title, right, w), ruleD(w)].join("\ │
│ n"); │
│ + │
│ +// recent-commit bar: [hash] subject ... da │
│ te │
│ +const recentRow = (w: number) => { │
│ + const hash = `[${info.lastHash}]`; │
│ + const avail = w - len(hash) - 1 - 12; │
│ + const subject = │
│ + len(info.lastSubject) > avail │
│ + ? [...info.lastSubject].slice(0, avai │
│ l - 3).join("") + "..." │
│ + : info.lastSubject; │
│ + const gap = " ".repeat(Math.max(2, w - le │
│ n(hash) - 1 - len(subject) - len(info.lastDa │
│ te))); │
│ + return { hash, subject, gap }; │
│ +}; │
│ +const commitsLabel = `COMMITS: ${info.commi │
│ ts}`; │
│ +const variants: [string, number][] = [ │
│ + ["frame-wide", FRAME_W], │
│ + ["frame-narrow", MOBILE_FRAME_W], │
│ +]; │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name}`} │
│ description={repo.desc}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ REPO: ${repo.name.toUpperCase()}`} date={inf │
│ o.lastDate} /> │
│ - <Pre>{" "}</Pre> │
│ - <ArticleMeta entries={[ │
│ - { key: "COMMITS", value: `${info.commit │
│ s}`, href: `/git/${repo.slug}/log/${branch}/ │
│ ` }, │
│ - { key: "RECENT", value: `${info.lastHas │
│ h} ${info.lastSubject}`, href: `/git/${repo. │
│ slug}/commit/${info.lastHash}/` }, │
│ - ...(repo.clone ? [{ key: "CLONE", value │
│ : repo.clone.replace(/^https?:\/\//, "") }] │
│ : []), │
│ - ]} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ + {repo.clone && <Pre>CLONE {repo.clone.re │
│ place(/^https?:\/\//, "")}</Pre>} │
│ <Pre>{" "}</Pre> │
│ │
│ - <Frame build={section("FILES", `${entries │
│ .length} ENTRIES`)} /> │
│ + {variants.map(([cls, w]) => ( │
│ + <Pre class={cls}>FILES{" ".repeat(Math. │
│ max(1, w - 5 - len(commitsLabel)))}<a class= │
│ "git-link" href={`/git/${repo.slug}/log/${br │
│ anch}/`}>{commitsLabel}</a></Pre> │
│ + ))} │
│ + <Frame build={(w) => ruleD(w)} /> │
│ <Pre>{" "}</Pre> │
│ <Pre><Menu label={`BRANCH: ${branch}`} it │
│ ems={allBranches.map((b) => ({ │
│ label: b, │
│ href: b === branch ? `/git/${repo.slug} │
│ /` : `/git/${repo.slug}/tree/${b}/`, │
│ }))} /></Pre> │
│ <Pre>{" "}</Pre> │
│ + {variants.map(([cls, w]) => { │
│ + const r = recentRow(w); │
│ + return ( │
│ + <Pre class={cls}><a class="git-link" │
│ href={`/git/${repo.slug}/commit/${info.lastH │
│ ash}/`}>{r.hash}</a>{" "}{r.subject}{r.gap}{ │
│ info.lastDate}</Pre> │
│ + ); │
│ + })} │
│ + <Pre>{" "}</Pre> │
│ <GitListing entries={entries} │
│ treeBase={`/git/${repo.slug}/tree/${bra │
│ nch}/`} │
│ blobBase={`/git/${repo.slug}/blob/${bra │
│ nch}/`} │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/log/[...branch].astro ──────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/log/[...branch].astro b/ooknet-des │
│ ign/src/pages/git/[repo]/log/[...branch].astro │
│ index 9453807..4e3363c 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/log/[...branch].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/log/[...branch].astro │
│ @@ -1,11 +1,13 @@ │
│ --- │
│ import ArticleLayout from "../../../../layouts/ArticleLayout/ArticleLayout.astro" │
│ ; │
│ import TopHeader from "../../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../components/ArticleHeader/ArticleHeader.ast │
│ ro"; │
│ +import GitHeader from "../../../../components/GitHeader/GitHeader.astro"; │
│ +import Frame from "../../../../components/Frame/Frame.astro"; │
│ import Pre from "../../../../components/Pre/Pre.astro"; │
│ import Timeline from "../../../../components/Timeline/Timeline.astro"; │
│ import Button from "../../../../components/Button/Button.astro"; │
│ import ArticleFooter from "../../../../components/ArticleFooter/ArticleFooter.ast │
│ ro"; │
│ +import { row2, ruleD } from "../../../../lib/ascii"; │
│ import { REPOS } from "../../../../git.config"; │
│ import { repoAvailable, branches, defaultBranch, repoInfo, repoLog, type GitRepo │
│ } from "../../../../lib/git"; │
│ │
│ @@ -26,7 +28,9 @@ const backHref = isDefault ? `/git/${repo.slug}/` : `/git/${repo │
│ .slug}/tree/${br │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - log @ ${branch}`} description={`Commi │
│ t log for ${repo.name} @ ${branch}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`LOG: ${repo.name.toUpperCase()} @ ${br │
│ anch.toUpperCase()}`} date={info.lastDate} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ + <Pre>{" "}</Pre> │
│ + <Frame build={(w) => [row2(`LOG @ ${branch}`, `${info.commits} COMMITS`, w), ru │
│ leD(w)].join("\n")} /> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
│ <Pre>{" "}</Pre> │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ .../[repo]/log/[...branch].astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/log/[...branch].astro b/ooknet-design/sr │
│ c/pages/git/[repo]/log/[...branch].astro │
│ index 9453807..4e3363c 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/log │
│ /[...branch].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/log │
│ /[...branch].astro │
│ @@ -1,11 +1,13 @@ │
│ --- │
│ import ArticleLayout from "../../../../layo │
│ uts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../../componen │
│ ts/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../comp │
│ onents/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../../componen │
│ ts/GitHeader/GitHeader.astro"; │
│ +import Frame from "../../../../components/F │
│ rame/Frame.astro"; │
│ import Pre from "../../../../components/Pre │
│ /Pre.astro"; │
│ import Timeline from "../../../../component │
│ s/Timeline/Timeline.astro"; │
│ import Button from "../../../../components/ │
│ Button/Button.astro"; │
│ import ArticleFooter from "../../../../comp │
│ onents/ArticleFooter/ArticleFooter.astro"; │
│ +import { row2, ruleD } from "../../../../li │
│ b/ascii"; │
│ import { REPOS } from "../../../../git.conf │
│ ig"; │
│ import { repoAvailable, branches, defaultBr │
│ anch, repoInfo, repoLog, type GitRepo } from │
│ "../../../../lib/git"; │
│ │
│ @@ -26,7 +28,9 @@ const backHref = isDefault │
│ ? `/git/${repo.slug}/` : `/git/${repo.slug} │
│ /tree/${br │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - │
│ log @ ${branch}`} description={`Commit log │
│ for ${repo.name} @ ${branch}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ LOG: ${repo.name.toUpperCase()} @ ${branch.t │
│ oUpperCase()}`} date={info.lastDate} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ + <Pre>{" "}</Pre> │
│ + <Frame build={(w) => [row2(`LOG @ ${branc │
│ h}`, `${info.commits} COMMITS`, w), ruleD(w) │
│ ].join("\n")} /> │
│ <Pre>{" "}</Pre> │
│ <Timeline entries={log} /> │
│ <Pre>{" "}</Pre> │
└──────────────────────────────────────────────┘ ┌─ ooknet-design/src/pages/git/[repo]/tree/[...rest].astro ───────────────── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[repo]/tree/[...rest].astro b/ooknet-desi │
│ gn/src/pages/git/[repo]/tree/[...rest].astro │
│ index 570a4ff..64f5007 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/tree/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/tree/[...rest].astro │
│ @@ -5,13 +5,14 @@ │
│ // is no ambiguity to parse. │
│ import ArticleLayout from "../../../../layouts/ArticleLayout/ArticleLayout.astro" │
│ ; │
│ import TopHeader from "../../../../components/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../components/ArticleHeader/ArticleHeader.ast │
│ ro"; │
│ +import GitHeader from "../../../../components/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components/GitPath/GitPath.astro"; │
│ import Frame from "../../../../components/Frame/Frame.astro"; │
│ import Pre from "../../../../components/Pre/Pre.astro"; │
│ import GitListing from "../../../../components/GitListing/GitListing.astro"; │
│ import Button from "../../../../components/Button/Button.astro"; │
│ import ArticleFooter from "../../../../components/ArticleFooter/ArticleFooter.ast │
│ ro"; │
│ -import { row2, ruleD, toGrid } from "../../../../lib/ascii"; │
│ +import { row2, ruleD } from "../../../../lib/ascii"; │
│ import { REPOS } from "../../../../git.config"; │
│ import { repoAvailable, branches, defaultBranch, repoFiles, repoDirs, dirEntries, │
│ type GitRepo } from "../../../../lib/git"; │
│ │
│ @@ -39,16 +40,16 @@ const upHref = dir │
│ : `/git/${repo.slug}/tree/${branch}/` │
│ : `/git/${repo.slug}/`; │
│ │
│ -const where = `/${toGrid(dir)}`; │
│ -const section = (w: number) => │
│ - [row2(`FILES @ ${branch}`, where.length > 30 ? "..." + where.slice(-27) : where │
│ , w), ruleD(w)].join("\n"); │
│ +const section = (w: number) => [row2(`FILES @ ${branch}`, "", w), ruleD(w)].join( │
│ "\n"); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - ${dir || branch}`} description={`${di │
│ r || branch} in ${repo.name}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={`REPO: ${repo.name.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.slug} /> │
│ <Pre>{" "}</Pre> │
│ <Frame build={section} /> │
│ <Pre>{" "}</Pre> │
│ + <GitPath repo={{ name: repo.name, slug: repo.slug }} branch={branch} path={dir} │
│ linkLeaf /> │
│ + <Pre>{" "}</Pre> │
│ <Pre><Button label="UP" href={upHref} />{" "}<Button label="LOG" href={`/git/$ │
│ {repo.slug}/log/${branch}/`} /></Pre> │
│ <Pre>{" "}</Pre> │
│ <GitListing entries={entries} │
└────────────────────────────────────────────────────────────────────────────────────┘ ┌─ ...t/[repo]/tree/[...rest].astro ── [ COPY ] ─┐
│ diff --git a/ooknet-design/src/pages/git/[re │
│ po]/tree/[...rest].astro b/ooknet-design/src │
│ /pages/git/[repo]/tree/[...rest].astro │
│ index 570a4ff..64f5007 100644 │
│ --- a/ooknet-design/src/pages/git/[repo]/tre │
│ e/[...rest].astro │
│ +++ b/ooknet-design/src/pages/git/[repo]/tre │
│ e/[...rest].astro │
│ @@ -5,13 +5,14 @@ │
│ // is no ambiguity to parse. │
│ import ArticleLayout from "../../../../layo │
│ uts/ArticleLayout/ArticleLayout.astro"; │
│ import TopHeader from "../../../../componen │
│ ts/TopHeader/TopHeader.astro"; │
│ -import ArticleHeader from "../../../../comp │
│ onents/ArticleHeader/ArticleHeader.astro"; │
│ +import GitHeader from "../../../../componen │
│ ts/GitHeader/GitHeader.astro"; │
│ +import GitPath from "../../../../components │
│ /GitPath/GitPath.astro"; │
│ import Frame from "../../../../components/F │
│ rame/Frame.astro"; │
│ import Pre from "../../../../components/Pre │
│ /Pre.astro"; │
│ import GitListing from "../../../../compone │
│ nts/GitListing/GitListing.astro"; │
│ import Button from "../../../../components/ │
│ Button/Button.astro"; │
│ import ArticleFooter from "../../../../comp │
│ onents/ArticleFooter/ArticleFooter.astro"; │
│ -import { row2, ruleD, toGrid } from "../../ │
│ ../../lib/ascii"; │
│ +import { row2, ruleD } from "../../../../li │
│ b/ascii"; │
│ import { REPOS } from "../../../../git.conf │
│ ig"; │
│ import { repoAvailable, branches, defaultBr │
│ anch, repoFiles, repoDirs, dirEntries, type │
│ GitRepo } from "../../../../lib/git"; │
│ │
│ @@ -39,16 +40,16 @@ const upHref = dir │
│ : `/git/${repo.slug}/tree/${branch}/` │
│ : `/git/${repo.slug}/`; │
│ │
│ -const where = `/${toGrid(dir)}`; │
│ -const section = (w: number) => │
│ - [row2(`FILES @ ${branch}`, where.length > │
│ 30 ? "..." + where.slice(-27) : where, w), │
│ ruleD(w)].join("\n"); │
│ +const section = (w: number) => [row2(`FILES │
│ @ ${branch}`, "", w), ruleD(w)].join("\n"); │
│ --- │
│ <ArticleLayout title={`GIT - ${repo.name} - │
│ ${dir || branch}`} description={`${dir || b │
│ ranch} in ${repo.name}.`}> │
│ <TopHeader /> │
│ - <ArticleHeader left="OOKNET GIT" right={` │
│ REPO: ${repo.name.toUpperCase()}`} /> │
│ + <GitHeader name={repo.name} slug={repo.sl │
│ ug} /> │
│ <Pre>{" "}</Pre> │
│ <Frame build={section} /> │
│ <Pre>{" "}</Pre> │
│ + <GitPath repo={{ name: repo.name, slug: r │
│ epo.slug }} branch={branch} path={dir} linkL │
│ eaf /> │
│ + <Pre>{" "}</Pre> │
│ <Pre><Button label="UP" href={upHref} />{ │
│ " "}<Button label="LOG" href={`/git/${repo. │
│ slug}/log/${branch}/`} /></Pre> │
│ <Pre>{" "}</Pre> │
│ <GitListing entries={entries} │
└──────────────────────────────────────────────┘ [ BACK TO LOG ] ──────────────────────────────────────────────────────────────────────────────────────
OOKNET ────────────────────────────────────────────────
OOKNET