57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
// docs/SIDEBAR.md: the session card's meta line — activity colour mapping and
|
|
// the shared duration format. Both clients compute it identically; the TUI
|
|
// mirrors this in crates/maestro-tui.
|
|
|
|
import type { ActivityState, IsoTimestamp, Session } from "./types.js";
|
|
import { sessionBlockers } from "./types.js";
|
|
|
|
/** A state held shorter than this keeps the card to one line (contract rule 1). */
|
|
const META_LINE_MIN_HOLD_MS = 30_000;
|
|
|
|
// Payloads predating #102 default `activity_since` to the Unix epoch; anything
|
|
// implausibly old is treated as "never observed" rather than a huge duration.
|
|
const PLAUSIBLE_EPOCH_MS = Date.UTC(2010, 0, 1);
|
|
|
|
export function activityHeldMs(since: IsoTimestamp | undefined, now: number): number | null {
|
|
if (!since) return null;
|
|
const t = Date.parse(since);
|
|
if (Number.isNaN(t) || t < PLAUSIBLE_EPOCH_MS) return null;
|
|
return Math.max(0, now - t);
|
|
}
|
|
|
|
/** `<60s` → whole seconds, `<60m` → whole minutes, else whole hours. Floored
|
|
* everywhere so the counter never shows a duration the state hasn't reached. */
|
|
export function activityDurationLabel(heldMs: number): string {
|
|
const s = Math.floor(heldMs / 1000);
|
|
if (s < 60) return `${s}s`;
|
|
const m = Math.floor(s / 60);
|
|
if (m < 60) return `${m}m`;
|
|
return `${Math.floor(m / 60)}h`;
|
|
}
|
|
|
|
/** CSS hook for the state's colour (`styles.css` maps slugs to tokens). An
|
|
* unknown variant degrades to idle so rendering never fails (Precedence). */
|
|
export function activityColorSlug(state: ActivityState | string): string {
|
|
switch (state) {
|
|
case "Active":
|
|
return "active";
|
|
case "AwaitingInput":
|
|
return "awaiting-input";
|
|
case "Error":
|
|
return "error";
|
|
default:
|
|
return "idle";
|
|
}
|
|
}
|
|
|
|
/** Line 2 renders only when at least one contract trigger holds. */
|
|
export function showsMetaLine(s: Session, now: number = Date.now()): boolean {
|
|
const held = activityHeldMs(s.activity_since, now);
|
|
return (
|
|
(held !== null && held >= META_LINE_MIN_HOLD_MS) ||
|
|
sessionBlockers(s) !== null ||
|
|
s.status === "Dead" ||
|
|
s.activity === "Error"
|
|
);
|
|
}
|