Coverage / src/diffusionview.ts 91.5%
65 of 71 lines run, 9 of 9 functions, 21 of 28 branches · run #23 on main · run missed
| 1 | 0× | // Diffusion in the editor, without VS Code (oxygenhub-vscode#5): how a pin |
| 2 | 0× | // between an upstream and its consumer looks. The hub's pill has two halves, |
| 3 | 0× | // the upstream's state and the consumer's; a tree row has one icon, so it |
| 4 | 0× | // shows the more urgent of the two and says both in words. |
| 5 | 1× | |
| 6 | import type { DiffusionHalf, DiffusionPin } from "./hubapi"; | |
| 7 | 1× | |
| 8 | 0× | /** A codicon and a theme colour id. */ |
| 9 | export interface Look { | |
| 10 | icon: string; | |
| 11 | color: string; | |
| 12 | } | |
| 13 | 1× | |
| 14 | 0× | /** Most urgent first: a failure, then what nothing checked, then waiting. */ |
| 15 | 1× | const URGENCY = ["red", "amber", "grey", "green", "off"]; |
| 16 | 1× | |
| 17 | 1× | /** The tone a row shows: the more urgent half, ignoring a consumer that does not follow. */ |
| 18 | 1× | export function tone(pin: DiffusionPin): string { |
| 19 | 4× | const tones = [pin.upstream_state.tone, pin.consumer_state.tone].filter((t) => t !== "off"); |
| 20 | 4× | return tones.sort((a, b) => rank(a) - rank(b))[0] ?? "off"; |
| 21 | 4× | } |
| 22 | 1× | |
| 23 | 4× | function rank(t: string): number { |
| 24 | 4× | const i = URGENCY.indexOf(t); |
| 25 | 4× | return i < 0 ? URGENCY.length : i; |
| 26 | 3× | } |
| 27 | 3× | |
| 28 | 3× | export function look(t: string): Look { |
| 29 | 3× | switch (t) { |
| 30 | 3× | case "green": |
| 31 | 3× | return { icon: "pass-filled", color: "testing.iconPassed" }; |
| 32 | 3× | case "red": |
| 33 | 3× | return { icon: "error", color: "testing.iconFailed" }; |
| 34 | 3× | case "amber": |
| 35 | 3× | return { icon: "warning", color: "list.warningForeground" }; |
| 36 | 3× | case "grey": |
| 37 | 3× | return { icon: "clock", color: "testing.iconQueued" }; |
| 38 | 3× | default: |
| 39 | 3× | return { icon: "circle-outline", color: "disabledForeground" }; |
| 40 | 3× | } |
| 41 | 3× | } |
| 42 | 1× | |
| 43 | 1× | /** The row's name: the repository on the other end of the pin. */ |
| 44 | 1× | export function pinLabel(pin: DiffusionPin, side: "takes" | "gives"): string { |
| 45 | 2× | return side === "takes" ? pin.upstream : pin.consumer; |
| 46 | 3× | } |
| 47 | 3× | |
| 48 | 3× | /** "untested │ passes · 2 behind · bump #304", as the hub's pill reads. */ |
| 49 | 3× | export function pinDescription(pin: DiffusionPin): string { |
| 50 | 3× | const parts = [`${pin.upstream_state.word} │ ${pin.consumer_state.word}`]; |
| 51 | 3× | if (pin.behind !== null && pin.behind !== undefined && pin.behind > 0) { |
| 52 | 1× | parts.push(`${pin.behind} behind`); |
| 53 | 1× | } |
| 54 | 3× | if (pin.bump !== null && pin.bump !== undefined) { |
| 55 | 1× | parts.push(`bump #${pin.bump}`); |
| 56 | 1× | } |
| 57 | 3× | return parts.join(" · "); |
| 58 | 3× | } |
| 59 | 1× | |
| 60 | 1× | const FOLLOW: Record<string, string> = { off: "does not follow", green: "follows when the upstream is green", any: "follows every move" }; |
| 61 | 1× | |
| 62 | 1× | /** The tooltip, in Markdown: both halves' sentences, then the pin. */ |
| 63 | 1× | export function pinTooltip(pin: DiffusionPin): string { |
| 64 | 1× | const half = (who: string, h: DiffusionHalf) => `**${who}: ${h.word}.** ${h.title}`; |
| 65 | 1× | const run = pin.run !== null && pin.run !== undefined ? ` · run #${pin.run}${pin.run_status ? ` ${pin.run_status}` : ""}` : ""; |
| 66 | 1× | return [ |
| 67 | 1× | half(pin.upstream, pin.upstream_state), |
| 68 | 1× | half(pin.consumer, pin.consumer_state), |
| 69 | 1× | `Pinned at \`${pin.path}\` on \`${pin.bookmark}\`; ${pin.consumer} ${FOLLOW[pin.follow] ?? pin.follow}.${pin.bump !== null && pin.bump !== undefined ? ` Open bump #${pin.bump}${run}.` : ""}`, |
| 70 | 1× | ].join("\n\n"); |
| 71 | 1× | } |