Ports catalog.ts's tree renderer to components under src/components/sidebar/, following docs/SIDEBAR.md: two-line cards behind a 5px activity rail, adaptive meta line (duration, detail, program, diff stat), collapsible folders, j/k walk, inline rename and a right-click menu. The nav walk reads a pure model (model.ts) rather than the DOM, so the rule that an expanded folder header is skipped and a collapsed one is not is testable without a render. Rail motion needs @keyframes, so it stays in sidebar.css rather than the Tailwind config. Closes #244
48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
// docs/SIDEBAR.md: a 5px bar pinned to the row's own left edge, square
|
|
// corners, full card height. It paints for every card, Idle included, and it
|
|
// carries the accessible name of the state so status is never colour-only.
|
|
|
|
import { activityColorSlug } from "../../activity.js";
|
|
import type { ActivityState } from "../../types.js";
|
|
|
|
export const STATE_WORDS: Record<string, string> = {
|
|
active: "Active",
|
|
idle: "Idle",
|
|
"awaiting-input": "Awaiting input",
|
|
error: "Error",
|
|
};
|
|
|
|
/** Rail fill per state, and the motion the state earns. */
|
|
const RAIL_CLASS: Record<string, string> = {
|
|
active: "bg-live",
|
|
idle: "bg-border-strong",
|
|
"awaiting-input": "bg-warn rail-blink",
|
|
error: "bg-danger rail-flash",
|
|
};
|
|
|
|
/** Duration text inherits the rail's colour (docs/SIDEBAR.md). */
|
|
export const DURATION_CLASS: Record<string, string> = {
|
|
active: "text-live",
|
|
idle: "text-muted",
|
|
"awaiting-input": "text-warn",
|
|
error: "text-danger",
|
|
};
|
|
|
|
export function stateWordFor(activity: ActivityState | string | undefined): string {
|
|
return STATE_WORDS[activityColorSlug(activity ?? "Idle")] ?? STATE_WORDS.idle;
|
|
}
|
|
|
|
export function ActivityRail({ activity }: { activity: ActivityState | string | undefined }) {
|
|
const slug = activityColorSlug(activity ?? "Idle");
|
|
const word = STATE_WORDS[slug] ?? STATE_WORDS.idle;
|
|
return (
|
|
<span
|
|
className={`absolute left-0 top-0 bottom-0 w-[5px] ${RAIL_CLASS[slug] ?? RAIL_CLASS.idle}`}
|
|
data-activity={slug}
|
|
role="img"
|
|
title={word}
|
|
aria-label={word}
|
|
/>
|
|
);
|
|
}
|