@kud/gh-ink
v0.76.0
Published
Ink components for rendering GitHub PR review comments and health — controlled, presentation-only, built on @kud/ink-ui and fed by @kud/gh.
Maintainers
Readme
@kud/gh-ink
Controlled Ink components for rendering
GitHub PR domain objects in the terminal. Presentation-first: data comes in as
props (the consuming surface owns the fetch), mutations run against
@kud/gh. Built on @kud/ink-ui.
Consumed by the standalone gh-pr-* CLIs and by cockpit — one component, many
surfaces.
Install
npm install @kud/gh-ink @kud/ghink and react are peer dependencies.
Exports
| Export | What |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| CommentsPanel (CommentsPanelProps) | Selectable review-thread + conversation panel — resolve (x), reply (r), show/hide resolved (R). |
| healthDisplay / healthGlyph / healthColor | Map a @kud/gh Health token → glyph + @kud/ink-ui colour. Glyph distinguishes (colourblind-safe); colour reinforces. |
| healthLegend | Ordered [Health, label] pairs for a help legend. |
| renderMarkdown | GitHub-flavoured markdown → styled terminal lines. |
Design
Every component is controlled — the parent owns loading and passes data in — so
the same panel drops into a full-screen CLI or a single pane of a dashboard. The
core (@kud/gh) decides the semantic token; this layer maps it to a colour. Same
seam as @kud/jenkins → @kud/jenkins-ink.
Inbox extensions
A host contributes verbs and screens to the inbox through InboxExtension:
| Field | What |
| ----------- | ----------------------------------------------------------------------------------------------------------- |
| key | The keypress that opens it, matched after the inbox's own bindings, so an extension never shadows them. |
| scope | "item" acts on the selected row and earns a place in its action menu; "global" does not. |
| menuGroup | "act" (does something to the row) or "open" (opens something); absent or "other" joins the quiet end. |
| hint | Short footer label. The action menu capitalises it for the row ("submit" → "Submit"); the legend keeps title. |
| icon | Nerd-font glyph for the menu row, as a "\uF4FA" escape. Shown only in nerd icon mode (see below). |
| body | (onExit, target) => ReactNode — rendered inside the inbox frame; claims header/footer via useChrome. |
Nerd-font icons render only when the host calls setIconMode("nerd") from
@kud/ink-ui before the first render. In the default text mode the menu's icon
column is dropped entirely — no stand-in, nothing to align — so a terminal
without a Nerd Font loses nothing but decoration. The built-ins use these
codepoints; sibling host verbs should match them rather than invent neighbours:
| Verb | Codepoint | | ----------------------------- | --------- | | Submit / Resubmit | U+F4FA / U+F46A | | Land | U+F419 | | Open PR / in browser / ticket | U+F440 / U+F465 / U+F41B | | Switch here / tab / pane | U+EBCB / U+EAE4 / U+EB56 | | Copy URL / repo / branch | U+F44C / U+F401 / U+F418 | | Unsubscribe / Mute / Remove reviewer | U+F478 / U+F466 / U+F468 | | Close PR / and delete / issue | U+F4DC / U+F48E / U+F41D |
Inbox focus slot and tab icons
App draws one standing row directly above the tab strip for whatever the
reader should look at next. The fetch result carries it as
focus?: FocusSlot | null: absent means the host has no focus feature and no
row is drawn; null means nothing to point at and draws the dim empty line.
It rides the fetch (and the cache beside the rows) because it goes stale the
same way they do.
| Prop / field | What |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| focusLabel | What to call the row (default focus). Passing this or focusEmpty reserves the row from first paint. |
| focusEmpty | The empty sentence, drawn dim after the label when focus comes back null. |
| FocusSlot | label, ref (a ticket key, or a PR-style #427), title, reason, optional marker (the host's priority glyph), mark/markColor/refColor overrides, and target: { tab, url }. |
| g | Jumps to focus.target: switches to that tab and lands the cursor on that row, opening a collapsed tail it is folded into. Listed in the ? legend only while a focus is on screen. |
A row whose url matches the target draws the mark in a fixed gutter between the tree glyph and the marker cell; every other row holds two blanks there, so the mark moves nothing sideways. Without a focus the rows draw what they always drew.
Tabs take an optional Section.icon, passed through to ink-ui's
TabItem.icon: one glyph naming the tab beside its label, which is also what
an inactive tab folds down to when the strip does not fit its width.
Development
npm run typecheck
npm run test
npm run buildLicence
MIT © Erwann Mest
