Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | 5x 5x 12x 9x 12x 9x 5x 1x | /**
* CodeHeaderStatus — presentational status line for the Code context header.
* Contains both the pure computation ({@link describeCodeStatus}) and the
* React component ({@link CodeHeaderStatus}).
*
* @module
*/
import type { CSSProperties, JSX } from "react";
import type { Session } from "../../hooks/types.js";
import { isActiveSession } from "../sessions/sessionsView.js";
import { formatRelativeTime } from "../../utils/time.js";
// ---------------------------------------------------------------------------
// Pure logic
// ---------------------------------------------------------------------------
/** Summary of session activity for the Code context header. */
export interface CodeStatusSummary {
/** Number of currently active (live) sessions. */
activeCount: number;
/** ISO timestamp of the most recently started session, or `undefined` if none. */
lastActivityAt: string | undefined;
}
/** Derive a Code context status summary from the sessions array. */
export function describeCodeStatus(
sessions: readonly Pick<Session, "status" | "endReason" | "startedAt">[],
): CodeStatusSummary {
let activeCount: number = 0;
let lastActivityAt: string | undefined;
for (const s of sessions) {
if (isActiveSession(s)) {
activeCount++;
}
if (!lastActivityAt || s.startedAt > lastActivityAt) {
lastActivityAt = s.startedAt;
}
}
return { activeCount, lastActivityAt };
}
// ---------------------------------------------------------------------------
// Presentational component
// ---------------------------------------------------------------------------
/** Inline styles matching the agent header's HeartbeatStatus pattern. */
const STATUS_ITEM_STYLE: CSSProperties = {
fontSize: "var(--font-size-xs)",
color: "var(--text-tertiary)",
whiteSpace: "nowrap",
};
/** Props for {@link CodeHeaderStatus}. */
export interface CodeHeaderStatusProps {
/** Summary computed by {@link describeCodeStatus}. */
summary: CodeStatusSummary;
}
/** Renders the Code context header status line. Returns nothing when there is no data. */
export function CodeHeaderStatus({ summary }: CodeHeaderStatusProps): JSX.Element | undefined {
const { activeCount, lastActivityAt } = summary;
if (activeCount === 0 && !lastActivityAt) {
return undefined;
}
return (
<div
style={{ display: "flex", alignItems: "center", gap: "var(--space-md)", flexWrap: "wrap" }}
data-testid="code-header-status"
>
{activeCount > 0 && (
<span style={STATUS_ITEM_STYLE} data-testid="code-header-active-sessions">
{activeCount} active {activeCount === 1 ? "session" : "sessions"}
</span>
)}
{lastActivityAt && (
<span style={STATUS_ITEM_STYLE} data-testid="code-header-last-activity">
Last activity {formatRelativeTime(lastActivityAt)}
</span>
)}
</div>
);
}
|