@kud/gh-ink 0.76.2 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +58 -24
- package/dist/index.js +230 -96
- package/package.json +10 -10
package/dist/index.d.ts
CHANGED
|
@@ -467,10 +467,17 @@ type StripItem = {
|
|
|
467
467
|
key: string;
|
|
468
468
|
state: StripState;
|
|
469
469
|
/**
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
|
|
470
|
+
* Why the state is what it is, as a short word the host chooses: `slow`,
|
|
471
|
+
* `errors`, `errors · slow`, `alerting`. Drawn after the mark in the mark's
|
|
472
|
+
* colour. Ignored for `ok`; `unknown` draws `no data` itself.
|
|
473
|
+
*/
|
|
474
|
+
cause?: string;
|
|
475
|
+
/**
|
|
476
|
+
* The thing's latest trunk build is red. Independent of `state` and drawn
|
|
477
|
+
* as its own `build ✕` beside it rather than folded in, because the two
|
|
478
|
+
* answer different questions and a reader needs to see when they disagree.
|
|
479
|
+
* Folding them — a red build downgrading a tick to a warning — was ruled
|
|
480
|
+
* against on 2026-10-23: it loses which system is broken.
|
|
474
481
|
*/
|
|
475
482
|
alarm?: boolean;
|
|
476
483
|
/** Where `o` goes when this item is opened from a rail row. Unused here. */
|
|
@@ -480,30 +487,51 @@ type StatusStrip = {
|
|
|
480
487
|
/** What the row is a strip OF, in the host's words: `services`, say. */
|
|
481
488
|
title: string;
|
|
482
489
|
items: StripItem[];
|
|
483
|
-
/** When this snapshot was taken (ms). Drawn as an age at the end of
|
|
490
|
+
/** When this snapshot was taken (ms). Drawn as an age at the end of row 1. */
|
|
484
491
|
at?: number;
|
|
492
|
+
/**
|
|
493
|
+
* Why every item is unknown, when it is one shared reason (`monitoring
|
|
494
|
+
* unreachable`). Turns row 1 into one sentence instead of one `? no data`
|
|
495
|
+
* per item — thirteen identical items say less than the sentence does.
|
|
496
|
+
*/
|
|
497
|
+
note?: string;
|
|
485
498
|
};
|
|
486
499
|
declare const stripGlyph: (state: StripState) => [string, string];
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
*/
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
summary: string;
|
|
500
|
+
/** One run of text in one style — the unit both the budget and the renderer read. */
|
|
501
|
+
type StripSpan = {
|
|
502
|
+
text: string;
|
|
503
|
+
color?: string;
|
|
504
|
+
bold?: boolean;
|
|
505
|
+
dim?: boolean;
|
|
506
|
+
};
|
|
507
|
+
type StripRows = {
|
|
508
|
+
/** Row 1 as plain text, without its title or age. */
|
|
509
|
+
issues: string;
|
|
510
|
+
/** Row 2 as plain text, without its title. */
|
|
511
|
+
success: string;
|
|
512
|
+
/** The age as drawn (`8m ago`), or `""` when it was shed or never stamped. */
|
|
501
513
|
age: string;
|
|
514
|
+
/** The same rows as styled runs — exactly what the renderer draws. */
|
|
515
|
+
spans: {
|
|
516
|
+
issues: StripSpan[];
|
|
517
|
+
success: StripSpan[];
|
|
518
|
+
};
|
|
519
|
+
/** Both titles, padded to one width so the rows' content lines up. */
|
|
520
|
+
titles: [string, string];
|
|
502
521
|
};
|
|
503
522
|
/**
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
523
|
+
* Both rows of the strip at the width they have to fit. Exported so a test —
|
|
524
|
+
* or a host laying out its own row — can ask the same question the renderer
|
|
525
|
+
* asks.
|
|
526
|
+
*/
|
|
527
|
+
declare const stripRows: (strip: StatusStrip, width: number, now?: number) => StripRows;
|
|
528
|
+
declare const ageLabel: (at: number | undefined, now?: number) => string;
|
|
529
|
+
/**
|
|
530
|
+
* A standing two-line row, the same height across loading / absent / ready
|
|
531
|
+
* — two lines and one margin — so the content below it never jumps: the same
|
|
532
|
+
* contract `CiStatusLine` keeps, and drawn in the same place, because a reader
|
|
533
|
+
* learns one row of glances. The host reserves those three rows out of its
|
|
534
|
+
* own budget; a fourth here would clip the frame from the top.
|
|
507
535
|
*/
|
|
508
536
|
declare const StatusStripLine: ({ strip, label, width, now, }: {
|
|
509
537
|
/** `undefined` while the first poll is out; `null` when the host has nothing. */
|
|
@@ -961,7 +989,12 @@ type StripStatusState = {
|
|
|
961
989
|
kind: "ready";
|
|
962
990
|
strip: StatusStrip | null;
|
|
963
991
|
};
|
|
964
|
-
/**
|
|
992
|
+
/**
|
|
993
|
+
* What the strip SAYS, ignoring when it said it. Every field the strip draws
|
|
994
|
+
* is in it — the cause word and the shared note included — or a cause moving
|
|
995
|
+
* from `slow` to `errors` would bail out of the repaint and leave the old
|
|
996
|
+
* word on screen.
|
|
997
|
+
*/
|
|
965
998
|
declare const stripSignature: (strip: StatusStrip | null) => string;
|
|
966
999
|
declare const toCiStatusState: (status: CiStatus | null) => CiStatusState;
|
|
967
1000
|
declare const sameCiStatusState: (a: CiStatusState, b: CiStatusState) => boolean;
|
|
@@ -1137,4 +1170,5 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
1137
1170
|
*/
|
|
1138
1171
|
declare const parsePatterns: (value: string) => string[];
|
|
1139
1172
|
|
|
1140
|
-
export {
|
|
1173
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, DEFAULT_FOCUS_EMPTY, DEFAULT_FOCUS_LABEL, FOCUS_JUMP_KEY, FOCUS_MARK, FocusSlotLine, HealthPanel, LookupMiss, MIN_SIDEBAR_COLS, PrRow, SIDEBAR_COLS, SidePanel, StatusStripLine, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, focusCellFor, focusRefColor, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, isMenuGap, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, menuRowsOf, menuTone, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripRows, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, useChrome, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
|
1174
|
+
export type { Action, ActionGroup, ChromeHints, ChromeSpec, ChromeState, CiStatusState, Command, CommentsPanelProps, DetailContext, ExplainSection, ExtensionTarget, FocusGutter, FocusSlot, HealthPanelProps, InboxBudget, InboxExtension, JiraAvailableTransition, JiraTransition, JiraTransitionsFor, LiveLabel, MenuGap, MenuRow, PrRowProps, Rails, RepoFilter, RowAnnouncement, RowMotion, SettledResult, Sidebar, SidebarRow, StatusStrip, StripItem, StripRows, StripSpan, StripState, StripStatusState, TabForStatus, TrailingColumns, TrailingShed };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React2, { createContext, useState, useRef, useContext, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import { useWindowSize, useInput, Text, Box } from 'ink';
|
|
3
|
-
import { colors, ScrollView, TextInput, Pill, getIconMode, FooterHints, useAppKeys, LoadingScreen, pillWidth, StatusMessage, useFilterMode, useListCursor, CommandPalette, Tabs, ProgressBar } from '@kud/ink-ui';
|
|
3
|
+
import { colors, ScrollView, TextInput, Pill, Spinner, getIconMode, FooterHints, useAppKeys, LoadingScreen, pillWidth, StatusMessage, useFilterMode, useListCursor, CommandPalette, Tabs, ProgressBar } from '@kud/ink-ui';
|
|
4
4
|
import { isPassCheck, isFailCheck, isInconclusiveCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer, parseRef, formatRef, fetchItemNode } from '@kud/gh';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { PIN_MARK, sizeOf, sizePartsOf, depthOf, impliedLabels, labelPriority, checkoutDirs, profileOf, inboxConfig, relativeTime, filterBySearch, filterByRepos, filterByOrigin, refCandidates, resolveRef } from '@kud/gh-workflow';
|
|
@@ -444,7 +444,7 @@ var Summary = ({
|
|
|
444
444
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
445
445
|
/* @__PURE__ */ jsx(Text, { bold: true, children: label.padEnd(10) }),
|
|
446
446
|
/* @__PURE__ */ jsx(Text, { children: icon + " " }),
|
|
447
|
-
parts.map(([text, color], i) => /* @__PURE__ */ jsxs(
|
|
447
|
+
parts.map(([text, color], i) => /* @__PURE__ */ jsxs(React2.Fragment, { children: [
|
|
448
448
|
i > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
449
449
|
/* @__PURE__ */ jsx(Text, { color, children: text })
|
|
450
450
|
] }, text + i))
|
|
@@ -930,7 +930,7 @@ var layoutOf = ({
|
|
|
930
930
|
}
|
|
931
931
|
return `\uF02B ${fitted.join(", ")}`;
|
|
932
932
|
};
|
|
933
|
-
const
|
|
933
|
+
const widthOf3 = () => {
|
|
934
934
|
const block = TRAILING.reduce(
|
|
935
935
|
(w, k) => givingUp[k] || own[k] === 0 ? w : w + 2 + own[k],
|
|
936
936
|
0
|
|
@@ -949,11 +949,11 @@ var layoutOf = ({
|
|
|
949
949
|
() => givingUp.repo = true,
|
|
950
950
|
() => givingUp.age = true
|
|
951
951
|
]) {
|
|
952
|
-
if (cols -
|
|
952
|
+
if (cols - widthOf3() >= MIN_TITLE) break;
|
|
953
953
|
give();
|
|
954
954
|
}
|
|
955
955
|
const labelLabel = labelCell();
|
|
956
|
-
const titleMax = Math.max(1, cols -
|
|
956
|
+
const titleMax = Math.max(1, cols - widthOf3());
|
|
957
957
|
return {
|
|
958
958
|
numStr,
|
|
959
959
|
showAuthor,
|
|
@@ -1081,7 +1081,7 @@ var PrRow = ({
|
|
|
1081
1081
|
/* @__PURE__ */ jsx(Text3, { color: colors.secondary, children: agePair.activity }),
|
|
1082
1082
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` (${agePair.lifetime})` })
|
|
1083
1083
|
] }) : ageLabel2 ? /* @__PURE__ */ jsx(Text3, { color: colors.secondary, children: ageLabel2 }) : null }) : null,
|
|
1084
|
-
backdropped ? null : announcements.map((a) => /* @__PURE__ */ jsxs(
|
|
1084
|
+
backdropped ? null : announcements.map((a) => /* @__PURE__ */ jsxs(React2.Fragment, { children: [
|
|
1085
1085
|
/* @__PURE__ */ jsx(Text3, { children: " " }),
|
|
1086
1086
|
/* @__PURE__ */ jsx(Pill, { color: a.color, children: a.label })
|
|
1087
1087
|
] }, a.label))
|
|
@@ -1100,12 +1100,183 @@ var stripGlyph = (state) => {
|
|
|
1100
1100
|
return ["?", colors.muted];
|
|
1101
1101
|
}
|
|
1102
1102
|
};
|
|
1103
|
-
var
|
|
1103
|
+
var OK = stripGlyph("ok")[0];
|
|
1104
|
+
var WARN = stripGlyph("warn")[0];
|
|
1105
|
+
var FAIL = stripGlyph("fail")[0];
|
|
1106
|
+
var SUCCESS_TITLE = "success";
|
|
1107
|
+
var ITEM_GAP = " ";
|
|
1108
|
+
var dim = (text) => ({ text, dim: true });
|
|
1109
|
+
var widthOf = (spans) => spans.reduce((n, span) => n + span.text.length, 0);
|
|
1110
|
+
var textOf = (spans) => spans.map((span) => span.text).join("");
|
|
1111
|
+
var joinWith = (groups, sep) => groups.flatMap((group, i) => i === 0 ? group : [sep, ...group]);
|
|
1104
1112
|
var SEVERITY = {
|
|
1105
1113
|
fail: 0,
|
|
1106
1114
|
warn: 1,
|
|
1107
|
-
|
|
1108
|
-
|
|
1115
|
+
build: 2,
|
|
1116
|
+
unknown: 3
|
|
1117
|
+
};
|
|
1118
|
+
var rankOf = (item) => {
|
|
1119
|
+
if (item.state === "ok") return item.alarm ? "build" : null;
|
|
1120
|
+
return item.state;
|
|
1121
|
+
};
|
|
1122
|
+
var buildSpans = () => [
|
|
1123
|
+
{ text: "build ", color: colors.error },
|
|
1124
|
+
{ text: FAIL, color: colors.error, bold: true }
|
|
1125
|
+
];
|
|
1126
|
+
var nameSpans = (item) => [
|
|
1127
|
+
{ text: item.key, bold: true },
|
|
1128
|
+
dim(":"),
|
|
1129
|
+
{ text: " " }
|
|
1130
|
+
];
|
|
1131
|
+
var itemSpans = (item) => {
|
|
1132
|
+
const spans = nameSpans(item);
|
|
1133
|
+
switch (item.state) {
|
|
1134
|
+
case "fail":
|
|
1135
|
+
spans.push({ text: FAIL, color: colors.error, bold: true });
|
|
1136
|
+
if (item.cause)
|
|
1137
|
+
spans.push({ text: ` ${item.cause}`, color: colors.error, bold: true });
|
|
1138
|
+
break;
|
|
1139
|
+
case "warn":
|
|
1140
|
+
spans.push({ text: WARN, color: colors.warning, bold: true });
|
|
1141
|
+
if (item.cause)
|
|
1142
|
+
spans.push({ text: ` ${item.cause}`, color: colors.warning });
|
|
1143
|
+
break;
|
|
1144
|
+
case "unknown":
|
|
1145
|
+
spans.push(dim("? no data"));
|
|
1146
|
+
break;
|
|
1147
|
+
case "ok":
|
|
1148
|
+
spans.push({ text: OK, color: colors.success });
|
|
1149
|
+
}
|
|
1150
|
+
if (item.alarm)
|
|
1151
|
+
spans.push({ text: " " }, dim("\xB7"), { text: " " }, ...buildSpans());
|
|
1152
|
+
return spans;
|
|
1153
|
+
};
|
|
1154
|
+
var countSpans = (rank, n) => {
|
|
1155
|
+
switch (rank) {
|
|
1156
|
+
case "fail":
|
|
1157
|
+
return [{ text: `${FAIL} ${n}`, color: colors.error, bold: true }];
|
|
1158
|
+
case "warn":
|
|
1159
|
+
return [{ text: `${WARN} ${n}`, color: colors.warning, bold: true }];
|
|
1160
|
+
case "build":
|
|
1161
|
+
return [...buildSpans(), { text: ` ${n}`, color: colors.error }];
|
|
1162
|
+
case "unknown":
|
|
1163
|
+
return [dim(`? ${n}`)];
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
var foldedIssues = (bad, folded, failsNamed) => {
|
|
1167
|
+
let fails = 0;
|
|
1168
|
+
const named = [];
|
|
1169
|
+
const rest = [];
|
|
1170
|
+
for (const item of bad) {
|
|
1171
|
+
const rank = rankOf(item);
|
|
1172
|
+
const keep = rank === "fail" ? fails++ < failsNamed : !folded.has(rank);
|
|
1173
|
+
(keep ? named : rest).push(item);
|
|
1174
|
+
}
|
|
1175
|
+
const tallies = [
|
|
1176
|
+
["fail", rest.filter((i) => i.state === "fail").length],
|
|
1177
|
+
["warn", rest.filter((i) => i.state === "warn").length],
|
|
1178
|
+
["build", rest.filter((i) => i.alarm).length],
|
|
1179
|
+
["unknown", rest.filter((i) => i.state === "unknown").length]
|
|
1180
|
+
];
|
|
1181
|
+
return joinWith(
|
|
1182
|
+
[
|
|
1183
|
+
...named.map(itemSpans),
|
|
1184
|
+
...tallies.filter(([, n]) => n > 0).map(([rank, n]) => countSpans(rank, n))
|
|
1185
|
+
],
|
|
1186
|
+
{ text: ITEM_GAP }
|
|
1187
|
+
);
|
|
1188
|
+
};
|
|
1189
|
+
var issueCandidates = (strip) => {
|
|
1190
|
+
const { items, note } = strip;
|
|
1191
|
+
if (note !== void 0) {
|
|
1192
|
+
const sentence = dim(`? no data for all ${items.length} \xB7 ${note}`);
|
|
1193
|
+
const builds = items.filter((i) => i.alarm);
|
|
1194
|
+
const named = joinWith(
|
|
1195
|
+
[[sentence], ...builds.map((i) => [...nameSpans(i), ...buildSpans()])],
|
|
1196
|
+
{ text: ITEM_GAP }
|
|
1197
|
+
);
|
|
1198
|
+
const counted = joinWith(
|
|
1199
|
+
[
|
|
1200
|
+
[sentence],
|
|
1201
|
+
...builds.length ? [countSpans("build", builds.length)] : []
|
|
1202
|
+
],
|
|
1203
|
+
{ text: ITEM_GAP }
|
|
1204
|
+
);
|
|
1205
|
+
return [
|
|
1206
|
+
{ spans: named, age: true },
|
|
1207
|
+
{ spans: counted, age: true },
|
|
1208
|
+
{ spans: counted, age: false }
|
|
1209
|
+
];
|
|
1210
|
+
}
|
|
1211
|
+
const bad = items.filter((i) => rankOf(i) !== null).sort((a, b) => SEVERITY[rankOf(a)] - SEVERITY[rankOf(b)]);
|
|
1212
|
+
if (bad.length === 0) {
|
|
1213
|
+
const calm = items.length > 0 ? [
|
|
1214
|
+
{ text: OK, color: colors.success },
|
|
1215
|
+
{ text: ` all ${items.length} fine`, bold: true }
|
|
1216
|
+
] : [dim("? no data")];
|
|
1217
|
+
return [
|
|
1218
|
+
{ spans: calm, age: true },
|
|
1219
|
+
{ spans: calm, age: false }
|
|
1220
|
+
];
|
|
1221
|
+
}
|
|
1222
|
+
const fails = bad.filter((i) => i.state === "fail").length;
|
|
1223
|
+
const levels = [
|
|
1224
|
+
[],
|
|
1225
|
+
["unknown"],
|
|
1226
|
+
["unknown", "build"],
|
|
1227
|
+
["unknown", "build", "warn"]
|
|
1228
|
+
];
|
|
1229
|
+
const everything = new Set(levels[levels.length - 1]);
|
|
1230
|
+
const candidates = levels.map((level) => ({
|
|
1231
|
+
spans: foldedIssues(bad, new Set(level), fails),
|
|
1232
|
+
age: true
|
|
1233
|
+
}));
|
|
1234
|
+
for (let named = fails; named >= 0; named--)
|
|
1235
|
+
candidates.push({ spans: foldedIssues(bad, everything, named), age: false });
|
|
1236
|
+
return candidates;
|
|
1237
|
+
};
|
|
1238
|
+
var successSpans = (strip, budget) => {
|
|
1239
|
+
const fine = strip.items.filter((i) => rankOf(i) === null);
|
|
1240
|
+
if (fine.length === 0)
|
|
1241
|
+
return [
|
|
1242
|
+
dim(
|
|
1243
|
+
strip.items.every((i) => i.state === "unknown") ? "nothing measured" : "none"
|
|
1244
|
+
)
|
|
1245
|
+
];
|
|
1246
|
+
const tick = { text: OK, color: colors.success };
|
|
1247
|
+
const full = [
|
|
1248
|
+
tick,
|
|
1249
|
+
{ text: " " },
|
|
1250
|
+
...joinWith(
|
|
1251
|
+
fine.map((i) => [{ text: i.key, bold: true }]),
|
|
1252
|
+
dim(" \xB7 ")
|
|
1253
|
+
)
|
|
1254
|
+
];
|
|
1255
|
+
if (widthOf(full) <= budget) return full;
|
|
1256
|
+
return [tick, { text: ` ${fine.length} ${strip.title}` }];
|
|
1257
|
+
};
|
|
1258
|
+
var stripRows = (strip, width, now = Date.now()) => {
|
|
1259
|
+
const titleWidth = Math.max(strip.title.length, SUCCESS_TITLE.length);
|
|
1260
|
+
const titles = [
|
|
1261
|
+
` ${strip.title.padEnd(titleWidth)} `,
|
|
1262
|
+
` ${SUCCESS_TITLE.padEnd(titleWidth)} `
|
|
1263
|
+
];
|
|
1264
|
+
const label = ageLabel(strip.at, now);
|
|
1265
|
+
const ageText = label ? `${label} ago` : "";
|
|
1266
|
+
const ageWidth = ageText ? 3 + ageText.length : 0;
|
|
1267
|
+
const budget = width - titles[0].length;
|
|
1268
|
+
const candidates = issueCandidates(strip);
|
|
1269
|
+
const chosen = candidates.find(
|
|
1270
|
+
(c) => widthOf(c.spans) + (c.age ? ageWidth : 0) <= budget
|
|
1271
|
+
) ?? candidates[candidates.length - 1];
|
|
1272
|
+
const success = successSpans(strip, budget);
|
|
1273
|
+
return {
|
|
1274
|
+
issues: textOf(chosen.spans),
|
|
1275
|
+
success: textOf(success),
|
|
1276
|
+
age: chosen.age ? ageText : "",
|
|
1277
|
+
spans: { issues: chosen.spans, success },
|
|
1278
|
+
titles
|
|
1279
|
+
};
|
|
1109
1280
|
};
|
|
1110
1281
|
var ageLabel = (at, now = Date.now()) => {
|
|
1111
1282
|
if (at === void 0) return "";
|
|
@@ -1115,38 +1286,7 @@ var ageLabel = (at, now = Date.now()) => {
|
|
|
1115
1286
|
if (m < 60) return `${m}m`;
|
|
1116
1287
|
return `${Math.round(m / 60)}h`;
|
|
1117
1288
|
};
|
|
1118
|
-
var
|
|
1119
|
-
var itemWidth = (item) => item.state === "fail" ? pillWidth(itemText(item)) : itemText(item).length;
|
|
1120
|
-
var stripLayout = (strip, width, now = Date.now()) => {
|
|
1121
|
-
const age = ageLabel(strip.at, now);
|
|
1122
|
-
const fixed = 2 + strip.title.length + 2 + (age ? age.length + 3 : 0);
|
|
1123
|
-
const full = strip.items.map(itemText).join(" ");
|
|
1124
|
-
const fullWidth = strip.items.reduce((n, item) => n + itemWidth(item), 0) + Math.max(0, strip.items.length - 1) * 2;
|
|
1125
|
-
if (fixed + fullWidth <= width)
|
|
1126
|
-
return { parts: strip.items, summary: full, age };
|
|
1127
|
-
const sorted = [...strip.items].sort(
|
|
1128
|
-
(a, b) => SEVERITY[a.state] - SEVERITY[b.state]
|
|
1129
|
-
);
|
|
1130
|
-
const bad = sorted.filter((i) => i.state !== "ok" || i.alarm);
|
|
1131
|
-
const ok = strip.items.length - bad.length;
|
|
1132
|
-
const tally = (n, s) => n > 0 ? `${n} ${stripGlyph(s)[0]}` : "";
|
|
1133
|
-
let named = [];
|
|
1134
|
-
let text = "";
|
|
1135
|
-
for (let n = bad.length; n >= 0; n--) {
|
|
1136
|
-
named = bad.slice(0, n);
|
|
1137
|
-
const rest = bad.slice(n);
|
|
1138
|
-
const counts2 = ["fail", "warn", "unknown"].map((s) => tally(rest.filter((i) => i.state === s).length, s)).filter(Boolean);
|
|
1139
|
-
text = [tally(ok, "ok"), ...counts2, ...named.map(itemText)].filter(Boolean).join(" \xB7 ");
|
|
1140
|
-
const textWidth = text.length + 2 * named.filter((i) => i.state === "fail").length;
|
|
1141
|
-
if (fixed + textWidth <= width) break;
|
|
1142
|
-
}
|
|
1143
|
-
return { parts: null, summary: text, age };
|
|
1144
|
-
};
|
|
1145
|
-
var nameStyle = (item) => {
|
|
1146
|
-
if (item.alarm) return { color: colors.error, bold: true };
|
|
1147
|
-
if (item.state === "unknown") return { dimColor: true };
|
|
1148
|
-
return {};
|
|
1149
|
-
};
|
|
1289
|
+
var Spans = ({ spans }) => /* @__PURE__ */ jsx(Fragment, { children: spans.map((span, i) => /* @__PURE__ */ jsx(Text, { color: span.color, bold: span.bold, dimColor: span.dim, children: span.text }, i)) });
|
|
1150
1290
|
var StatusStripLine = ({
|
|
1151
1291
|
strip,
|
|
1152
1292
|
label = "status",
|
|
@@ -1154,45 +1294,30 @@ var StatusStripLine = ({
|
|
|
1154
1294
|
now
|
|
1155
1295
|
}) => {
|
|
1156
1296
|
if (strip === void 0)
|
|
1157
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
1158
|
-
/* @__PURE__ */
|
|
1159
|
-
|
|
1297
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1298
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1299
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
1300
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
1301
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${label} loading\u2026` })
|
|
1302
|
+
] }),
|
|
1303
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${SUCCESS_TITLE}` })
|
|
1160
1304
|
] });
|
|
1161
1305
|
if (strip === null)
|
|
1162
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
1163
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${stripGlyph("unknown")[0]} ` }),
|
|
1164
|
-
/* @__PURE__ */ jsx(Text, {
|
|
1306
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1307
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${stripGlyph("unknown")[0]} ${label} no signal / not configured` }),
|
|
1308
|
+
/* @__PURE__ */ jsx(Text, { children: " " })
|
|
1165
1309
|
] });
|
|
1166
|
-
const {
|
|
1167
|
-
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
1168
|
-
/* @__PURE__ */
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
return /* @__PURE__ */ jsxs(React5.Fragment, { children: [
|
|
1178
|
-
i > 0 ? /* @__PURE__ */ jsx(Text, { children: " " }) : null,
|
|
1179
|
-
/* @__PURE__ */ jsx(
|
|
1180
|
-
Text,
|
|
1181
|
-
{
|
|
1182
|
-
color: item.state === "unknown" ? void 0 : color,
|
|
1183
|
-
dimColor: item.state === "unknown",
|
|
1184
|
-
bold: asking,
|
|
1185
|
-
children: glyph
|
|
1186
|
-
}
|
|
1187
|
-
),
|
|
1188
|
-
item.alarm ? /* @__PURE__ */ jsx(Text, { color: colors.error, bold: true, children: ` ${ALARM}` }) : null,
|
|
1189
|
-
/* @__PURE__ */ jsxs(Text, { ...nameStyle(item), children: [
|
|
1190
|
-
" ",
|
|
1191
|
-
item.key
|
|
1192
|
-
] })
|
|
1193
|
-
] }, item.key);
|
|
1194
|
-
}) : /* @__PURE__ */ jsx(Text, { children: summary }),
|
|
1195
|
-
age ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${age}` }) : null
|
|
1310
|
+
const { spans, age, titles } = stripRows(strip, width, now);
|
|
1311
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
1312
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1313
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: titles[0] }),
|
|
1314
|
+
/* @__PURE__ */ jsx(Spans, { spans: spans.issues }),
|
|
1315
|
+
age ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${age}` }) : null
|
|
1316
|
+
] }),
|
|
1317
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1318
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: titles[1] }),
|
|
1319
|
+
/* @__PURE__ */ jsx(Spans, { spans: spans.success })
|
|
1320
|
+
] })
|
|
1196
1321
|
] });
|
|
1197
1322
|
};
|
|
1198
1323
|
var SEARCH_GLYPH_NERD = "\uF422";
|
|
@@ -2885,7 +3010,12 @@ var InboxHeader = ({
|
|
|
2885
3010
|
/* @__PURE__ */ jsx(Text3, { color: colors.info, dimColor: true, children: "\u254C".repeat(fill) })
|
|
2886
3011
|
] });
|
|
2887
3012
|
};
|
|
2888
|
-
var stripSignature = (strip) => strip === null ? "\u2205" :
|
|
3013
|
+
var stripSignature = (strip) => strip === null ? "\u2205" : [
|
|
3014
|
+
strip.note ?? "",
|
|
3015
|
+
...strip.items.map(
|
|
3016
|
+
(i) => `${i.key}=${i.state}:${i.cause ?? ""}${i.alarm ? ":build" : ""}`
|
|
3017
|
+
)
|
|
3018
|
+
].join(",");
|
|
2889
3019
|
var toCiStatusState = (status) => status ? { kind: "ready", status } : { kind: "error" };
|
|
2890
3020
|
var sameCiStatusState = (a, b) => {
|
|
2891
3021
|
if (a.kind !== b.kind) return false;
|
|
@@ -3123,10 +3253,7 @@ var ItemRow = ({
|
|
|
3123
3253
|
icon,
|
|
3124
3254
|
motion,
|
|
3125
3255
|
announcements,
|
|
3126
|
-
focusCell: focusCellFor(
|
|
3127
|
-
focus,
|
|
3128
|
-
"url" in item ? item.url : void 0
|
|
3129
|
-
)
|
|
3256
|
+
focusCell: focusCellFor(focus, "url" in item ? item.url : void 0)
|
|
3130
3257
|
}
|
|
3131
3258
|
);
|
|
3132
3259
|
};
|
|
@@ -3287,11 +3414,11 @@ var TURN_LEGEND = [
|
|
|
3287
3414
|
var CELL_GUTTER = 2;
|
|
3288
3415
|
var COL_GAP = 3;
|
|
3289
3416
|
var cellWidthOf = (col) => Math.max(0, ...col.rows.map((r) => r.cell.length)) + CELL_GUTTER;
|
|
3290
|
-
var
|
|
3417
|
+
var widthOf2 = (col) => Math.max(
|
|
3291
3418
|
col.title.length,
|
|
3292
3419
|
...col.rows.map((r) => cellWidthOf(col) + r.label.length)
|
|
3293
3420
|
);
|
|
3294
|
-
var groupWidth = (group) => Math.max(0, ...group.map(
|
|
3421
|
+
var groupWidth = (group) => Math.max(0, ...group.map(widthOf2));
|
|
3295
3422
|
var layoutWidth = (groups) => groups.reduce((total, g) => total + groupWidth(g), 0) + COL_GAP * Math.max(0, groups.length - 1);
|
|
3296
3423
|
var legendLayout = (cols, available) => {
|
|
3297
3424
|
const across = cols.map((c) => [c]);
|
|
@@ -3362,9 +3489,7 @@ var HelpModal = ({
|
|
|
3362
3489
|
["p", "open repo in new pane"],
|
|
3363
3490
|
["u", "unsubscribe from this item"],
|
|
3364
3491
|
["x", "remove yourself as reviewer"],
|
|
3365
|
-
...hasJira ? [
|
|
3366
|
-
["t", "Jira: move / open ticket"]
|
|
3367
|
-
] : [],
|
|
3492
|
+
...hasJira ? [["t", "Jira: move / open ticket"]] : [],
|
|
3368
3493
|
["\u2303K", "launch \xB7 open a ticket or PR by name"],
|
|
3369
3494
|
["/", "search"],
|
|
3370
3495
|
["f", "filter by repo"],
|
|
@@ -3630,7 +3755,7 @@ var BrowseScreen = ({
|
|
|
3630
3755
|
5,
|
|
3631
3756
|
// -10 rather than -8: the extra 2 are the frame's top and bottom border
|
|
3632
3757
|
// rows, so the tree never grows taller than the terminal inside the frame.
|
|
3633
|
-
rows - 10 - (filterActive ? 2 : 0) - (reserveCiRow ? 2 : 0) - (reserveStripRow ?
|
|
3758
|
+
rows - 10 - (filterActive ? 2 : 0) - (reserveCiRow ? 2 : 0) - (reserveStripRow ? 3 : 0) - (reserveFocusRow ? 2 : 0)
|
|
3634
3759
|
);
|
|
3635
3760
|
useEffect(() => {
|
|
3636
3761
|
setLocalSections(applyWork(sections));
|
|
@@ -4330,7 +4455,13 @@ var BrowseScreen = ({
|
|
|
4330
4455
|
headerHint,
|
|
4331
4456
|
["?", "help"],
|
|
4332
4457
|
["q", "quit"]
|
|
4333
|
-
] : activeItem?.kind === "subgroup-header" ? [
|
|
4458
|
+
] : activeItem?.kind === "subgroup-header" ? [
|
|
4459
|
+
["\u2191\u2193", "nav"],
|
|
4460
|
+
["\u2190\u2192", "tab"],
|
|
4461
|
+
headerHint,
|
|
4462
|
+
["?", "help"],
|
|
4463
|
+
["q", "quit"]
|
|
4464
|
+
] : [
|
|
4334
4465
|
["\u2191\u2193", "nav"],
|
|
4335
4466
|
["\u2190\u2192", "tab"],
|
|
4336
4467
|
["\u21B5/d", "open"],
|
|
@@ -4923,10 +5054,13 @@ var App = ({
|
|
|
4923
5054
|
const running = [...heldSince].filter(([tab]) => held.has(tab));
|
|
4924
5055
|
if (running.length === 0) return;
|
|
4925
5056
|
const remaining = (at) => at + TRANSIT_HOLD_MS - Date.now();
|
|
4926
|
-
const timer = setTimeout(
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
5057
|
+
const timer = setTimeout(
|
|
5058
|
+
() => {
|
|
5059
|
+
for (const [tab, at] of running)
|
|
5060
|
+
if (remaining(at) <= 0) settleTab(tab, transients);
|
|
5061
|
+
},
|
|
5062
|
+
Math.max(0, Math.min(...running.map(([, at]) => remaining(at))))
|
|
5063
|
+
);
|
|
4930
5064
|
return () => clearTimeout(timer);
|
|
4931
5065
|
}, [transients, heldSince, state.phase]);
|
|
4932
5066
|
const pendingSummary = useMemo(
|
|
@@ -5254,7 +5388,7 @@ var App = ({
|
|
|
5254
5388
|
flexDirection: "column",
|
|
5255
5389
|
minHeight: Math.max(
|
|
5256
5390
|
5,
|
|
5257
|
-
rows - 5 - (hasCiStatus ? 2 : 0) - (hasStrip ?
|
|
5391
|
+
rows - 5 - (hasCiStatus ? 2 : 0) - (hasStrip ? 3 : 0) - (hasFocusSlot ? 2 : 0)
|
|
5258
5392
|
),
|
|
5259
5393
|
children: state.phase === "loading" ? /* @__PURE__ */ jsx(LoadingScreen, { label: `Fetching ${title}\u2026` }) : /* @__PURE__ */ jsx(
|
|
5260
5394
|
NoRowsScreen,
|
|
@@ -5414,4 +5548,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
5414
5548
|
};
|
|
5415
5549
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
5416
5550
|
|
|
5417
|
-
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, DEFAULT_FOCUS_EMPTY, DEFAULT_FOCUS_LABEL, FOCUS_JUMP_KEY, FOCUS_MARK, FocusSlotLine, HealthPanel, LookupMiss, MIN_SIDEBAR_COLS, PrRow, SIDEBAR_COLS, SidePanel, StatusStripLine, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, focusCellFor, focusRefColor, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, isMenuGap, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, menuRowsOf, menuTone, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph,
|
|
5551
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, DEFAULT_FOCUS_EMPTY, DEFAULT_FOCUS_LABEL, FOCUS_JUMP_KEY, FOCUS_MARK, FocusSlotLine, HealthPanel, LookupMiss, MIN_SIDEBAR_COLS, PrRow, SIDEBAR_COLS, SidePanel, StatusStripLine, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, focusCellFor, focusRefColor, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, isMenuGap, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, menuRowsOf, menuTone, moveCursor, numberColumnsOf, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripRows, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, useChrome, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.77.0",
|
|
4
4
|
"description": "Ink components for rendering GitHub PR review comments and health — controlled, presentation-only, built on @kud/ink-ui and fed by @kud/gh.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=22"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsup",
|
|
@@ -49,19 +49,19 @@
|
|
|
49
49
|
"react": ">=19"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@kud/gh": "0.21.
|
|
53
|
-
"@kud/gh-workflow": "0.17.
|
|
52
|
+
"@kud/gh": "0.21.1",
|
|
53
|
+
"@kud/gh-workflow": "0.17.1",
|
|
54
54
|
"zx": "8.8.5"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@kud/ink-ui": "0.41.0",
|
|
58
|
-
"@types/node": "
|
|
59
|
-
"@types/react": "19.
|
|
60
|
-
"ink": "
|
|
58
|
+
"@types/node": "26.6.5",
|
|
59
|
+
"@types/react": "19.3.0",
|
|
60
|
+
"ink": "8.0.0",
|
|
61
61
|
"ink-testing-library": "4.0.0",
|
|
62
|
-
"react": "19.
|
|
63
|
-
"tsup": "
|
|
62
|
+
"react": "19.3.1",
|
|
63
|
+
"tsup": "9.0.0",
|
|
64
64
|
"typescript": "5.9.3",
|
|
65
|
-
"vitest": "
|
|
65
|
+
"vitest": "5.0.5"
|
|
66
66
|
}
|
|
67
67
|
}
|