| 1 | /** |
| 2 | * Cards in chat: how a card's state reads, which of its actions show (its |
| 3 | * links), and when its preview folds. Pure, so it is tested on its own; |
| 4 | * components/chat/card.tsx draws them. |
| 5 | */ |
| 6 | import type { CardAction, MessageCard } from "@g1t/contracts"; |
| 7 | |
| 8 | import type { SessionChip } from "./session-card"; |
| 9 | |
| 10 | /** A card's state chip, as a session's is, outlined only when it is waiting quietly. */ |
| 11 | export type CardChip = { tone: SessionChip["tone"] | "merged"; live: boolean; outline?: boolean }; |
| 12 | |
| 13 | const DRAFT_CHIPS: Record<string, CardChip> = { |
| 14 | // Waiting on someone to file it: lavender, outlined only. |
| 15 | draft: { tone: "accent", live: false, outline: true }, |
| 16 | filed: { tone: "success", live: false }, |
| 17 | discarded: { tone: "neutral", live: false }, |
| 18 | }; |
| 19 | |
| 20 | /** |
| 21 | * How a draft issue's state reads; null for other kinds (a session's chip |
| 22 | * is lib/session-card.ts's, others are guessed from their words). |
| 23 | */ |
| 24 | export function cardChip(card: Pick<MessageCard, "kind" | "state">): CardChip | null { |
| 25 | if (!card.state) return null; |
| 26 | if (card.kind === "draft_issue") return DRAFT_CHIPS[card.state.trim().toLowerCase()] ?? { tone: "neutral", live: false }; |
| 27 | return null; |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * The actions a card shows: its links. The service that answered the |
| 32 | * others (agents) is gone, so an old card's buttons that ran something are |
| 33 | * left out; the card stays to read. |
| 34 | */ |
| 35 | export function shownActions(card: Pick<MessageCard, "actions">): CardAction[] { |
| 36 | return (card.actions ?? []).filter((a) => !!a.href); |
| 37 | } |
| 38 | |
| 39 | /** Whether a body is long enough that its preview starts folded (over about eight lines). */ |
| 40 | export function foldsBody(body: string, lines = 8): boolean { |
| 41 | const rows = body.split("\n").reduce((n, line) => n + Math.max(1, Math.ceil(line.length / 80)), 0); |
| 42 | return rows > lines; |
| 43 | } |