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