45 lines
1.6 KiB
TypeScript
45 lines
1.6 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 } from "./types.js";
|
|
|
|
/** A state held shorter than this has no duration fragment. */
|
|
export 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";
|
|
}
|
|
}
|