/**
* The header chrome for the browse pages.
*
* The chips the toolbar carries — the git branch, the change totals, the live
* connection state — and the footer's live line. Everything here is pure string
* building over an escaped view model: a branch name, a count, and a status word
* are all text a repository can choose, so each one crosses `escapeHtml` before
* it reaches the page. The page shell and the page bodies stay in `html.ts`.
*/
import { escapeHtml } from "../../../lib/escape.ts";
import { GLYPHS } from "../../../lib/tui/glyphs.ts";
import { liveChip, reloadChip } from "../../../lib/web/index.ts";
import type { GitStatus } from "../git/git.ts";
import type { ChangeIndex } from "../live/live.ts";
/** The header's git chip: the branch, or the detached head; changes are the changes chip's job. */
export function renderGit(git: GitStatus | undefined): string {
if (!git) return "";
const name = git.branch ?? git.head;
if (!name) return "";
const glyph = git.branch ? GLYPHS.branch : GLYPHS.detached;
const aria = git.branch ? `git branch ${git.branch}` : `git detached at ${git.head ?? ""}`;
return `${escapeHtml(glyph)}${escapeHtml(name)}`;
}
/** The live chrome a page renders: how often its stream is sent a frame. */
export interface LivePage {
intervalMs: number;
/** Root-relative folder the page listed; absent on a page with no listing to check. */
dir?: string;
/** Revision of the names that listing drew, which the reload control compares against. */
rev?: string;
}
/**
* The header's live chrome: the connection chip, and the reload control the client reveals
* once the page stops being true — the folder it drew changed on disk, or the stream it reads
* has gone. Both start hidden, so a page with JS off — or with live status disabled — never
* shows a state or a control it cannot keep true.
*
* The chip is not a live region: the header is where a reader looks for the page's state, and
* its tooltip is where the server's own accounting of the stream (`up 2m 14s · 37 requests`)
* is read, since the footer carries the page's facts and nothing else.
*/
export function renderLiveChrome(live: LivePage | undefined): string {
if (!live) return "";
const rev: Record =
live.rev === undefined ? {} : { "data-dir": live.dir ?? "", "data-rev": live.rev };
return (
liveChip({
id: "live-state",
label: "connecting",
title: "Live status from the browse server",
hidden: true,
attrs: { "data-interval": String(live.intervalMs), ...rev },
}) + reloadChip({ title: "This folder changed on disk" })
);
}
/**
* The header's changes chip, linking to the diff overview. With live status on,
* a hidden shell is always rendered so the chip can appear and disappear as the
* working tree changes, without the client inserting elements into the header.
*/
export function renderChanges(summary: ChangeIndex | undefined, live: LivePage | undefined, base: string): string {
const count = summary?.count ?? 0;
if (count <= 0) {
return live
? ``
: "";
}
const label = `${count} change${count === 1 ? "" : "s"}`;
const insertions = summary?.insertions ?? 0;
const deletions = summary?.deletions ?? 0;
const totals =
insertions + deletions > 0
? ` +${insertions} −${deletions}`
: "";
const aria = `${label} against HEAD${insertions + deletions > 0 ? `, ${insertions} insertions and ${deletions} deletions` : ""}`;
return `●${escapeHtml(label)}${totals}`;
}