@kud/gh-ink 0.52.1 → 0.54.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 +59 -16
- package/dist/index.js +111 -62
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -66,11 +66,26 @@ type SidebarRow = {
|
|
|
66
66
|
*/
|
|
67
67
|
url?: string;
|
|
68
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Plain data, deliberately: a host caches its last fetch to disk and paints it
|
|
71
|
+
* on the next launch, and this rides along in that file. A function on it would
|
|
72
|
+
* not survive the round trip, so the host's words for `live` travel as a prop
|
|
73
|
+
* on `SidePanel` instead — vocabulary is configuration, not data that goes
|
|
74
|
+
* stale.
|
|
75
|
+
*/
|
|
69
76
|
type Sidebar = {
|
|
70
77
|
/** Heading for the rail. The host's vocabulary, never ours. */
|
|
71
78
|
title: string;
|
|
72
79
|
rows: SidebarRow[];
|
|
73
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* What to call `live` on screen, in the host's words — `4 on board` and `off
|
|
83
|
+
* board` on a ticket cockpit, say. Takes the number rather than a word pair
|
|
84
|
+
* because the two states need not share a sentence shape: "off board" has no
|
|
85
|
+
* N in it. `live` itself stays a number, so the absent-draws-nothing contract
|
|
86
|
+
* and the host's sort are untouched.
|
|
87
|
+
*/
|
|
88
|
+
type LiveLabel = (live: number) => string;
|
|
74
89
|
/**
|
|
75
90
|
* The rail's width, INCLUDING its rule and padding. Exported because the list
|
|
76
91
|
* beside it has to shrink by exactly this much: every row in that list truncates
|
|
@@ -83,18 +98,26 @@ type Sidebar = {
|
|
|
83
98
|
* word and left the rail listing keys with an ellipsis after them. The key line
|
|
84
99
|
* was never the constraint — the label is what makes a key readable, and a rail
|
|
85
100
|
* you cannot read is forty columns spent on nothing.
|
|
101
|
+
*
|
|
102
|
+
* Widened again once the label became the row's first line: at 45 usable
|
|
103
|
+
* columns a real epic title still lost its last two words. But a 200-column
|
|
104
|
+
* terminal has room to give where a 120-column one does not — at 120 a rail
|
|
105
|
+
* this wide leaves the list 52 columns, which elides a PR title past its own
|
|
106
|
+
* key. So this is the CEILING, and `railWidth` sizes the rail to the frame:
|
|
107
|
+
* a third of it, never below the 52 the grid was laid out for, never above.
|
|
86
108
|
*/
|
|
87
|
-
declare const SIDEBAR_COLS =
|
|
109
|
+
declare const SIDEBAR_COLS = 64;
|
|
110
|
+
declare const MIN_SIDEBAR_COLS = 52;
|
|
111
|
+
/** How wide the rail is in a frame of `cols` — pass the same number to `width`. */
|
|
112
|
+
declare const railWidth: (cols: number) => number;
|
|
88
113
|
/**
|
|
89
|
-
* The
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* number — and a zero is printed wherever it was counted, because "nothing is
|
|
95
|
-
* moving" is exactly the thing a roadmap is read to notice.
|
|
114
|
+
* The plain-text facts of a row, for a host or a test that wants them as one
|
|
115
|
+
* string: how far through, then how much is moving. Either half may be missing
|
|
116
|
+
* and the other still worth saying, and a zero is printed wherever it was
|
|
117
|
+
* counted, because "nothing is moving" is exactly the thing a roadmap is read
|
|
118
|
+
* to notice.
|
|
96
119
|
*/
|
|
97
|
-
declare const counts: (row: SidebarRow) => string;
|
|
120
|
+
declare const counts: (row: SidebarRow, liveLabel?: LiveLabel) => string;
|
|
98
121
|
/**
|
|
99
122
|
* How many rows fit in `height` — one fewer than would physically fit whenever
|
|
100
123
|
* that means anything is left over, to buy the line that says so.
|
|
@@ -108,18 +131,23 @@ declare const railCapacity: (height: number, rows: number) => number;
|
|
|
108
131
|
/**
|
|
109
132
|
* A right-hand rail of initiatives, standing beside the list.
|
|
110
133
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
134
|
+
* Label first, facts beneath, because a roadmap is read by name: nobody thinks
|
|
135
|
+
* "ACC-11312", they think "the OpenSearch migration". So the label is the
|
|
136
|
+
* bright line and the key drops to the facts line as what it is — a reference
|
|
137
|
+
* you follow, in the secondary tier. Two lines rather than one because both are
|
|
138
|
+
* load-bearing and neither survives the other being cut: words with no key
|
|
139
|
+
* cannot be opened, and a key with no words cannot be read at a glance.
|
|
115
140
|
*
|
|
116
141
|
* Presentational, like every other row renderer here: it DRAWS a cursor but does
|
|
117
142
|
* not own one, and it never calls `useInput`. Where the cursor is, and whether
|
|
118
143
|
* the arrows are pointed at this rail at all, are the host's state — which is
|
|
119
144
|
* what lets a screen with two focus regions have exactly one of them lit.
|
|
120
145
|
*/
|
|
121
|
-
declare const SidePanel: ({ sidebar, height, focused, cursor, }: {
|
|
146
|
+
declare const SidePanel: ({ sidebar, liveLabel, width, height, focused, cursor, }: {
|
|
122
147
|
sidebar: Sidebar;
|
|
148
|
+
liveLabel?: LiveLabel;
|
|
149
|
+
/** From `railWidth`, so the list beside it can subtract the same number. */
|
|
150
|
+
width?: number;
|
|
123
151
|
height?: number;
|
|
124
152
|
/** The arrows are pointed here, so this rail draws the cursor. */
|
|
125
153
|
focused?: boolean;
|
|
@@ -184,12 +212,20 @@ type CachedCockpit = {
|
|
|
184
212
|
login: string;
|
|
185
213
|
at: number;
|
|
186
214
|
budget?: InboxBudget;
|
|
215
|
+
/**
|
|
216
|
+
* The rail, when the host supplied one. Cached beside the rows because it is
|
|
217
|
+
* painted beside them: a launch that trusts the cache never refetches, so a
|
|
218
|
+
* rail left out of the file is a rail that does not exist until the next
|
|
219
|
+
* refresh — `i` did nothing and the footer did not even offer it.
|
|
220
|
+
*/
|
|
221
|
+
sidebar?: Sidebar;
|
|
187
222
|
};
|
|
188
223
|
declare const readCache: (key: string) => CachedCockpit | null;
|
|
189
224
|
declare const writeCache: (key: string, data: {
|
|
190
225
|
sections: Section[];
|
|
191
226
|
login: string;
|
|
192
227
|
budget?: InboxBudget;
|
|
228
|
+
sidebar?: Sidebar;
|
|
193
229
|
}) => void;
|
|
194
230
|
|
|
195
231
|
type DetailContext = {
|
|
@@ -378,7 +414,7 @@ declare const ActionMenu: ({ item, actions, cursor, }: {
|
|
|
378
414
|
cursor: number;
|
|
379
415
|
}) => React.JSX.Element;
|
|
380
416
|
declare const signatureOf: (sections: Section[]) => string;
|
|
381
|
-
declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, }: {
|
|
417
|
+
declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, liveLabel, }: {
|
|
382
418
|
fetcher: () => Promise<{
|
|
383
419
|
sections: Section[];
|
|
384
420
|
login: string;
|
|
@@ -401,6 +437,13 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jir
|
|
|
401
437
|
sidebar?: Sidebar;
|
|
402
438
|
}>;
|
|
403
439
|
cacheKey?: string;
|
|
440
|
+
/**
|
|
441
|
+
* What the rail calls a row's `live` count, in this host's words — `4 on
|
|
442
|
+
* board` / `off board` on a ticket cockpit. A prop rather than part of the
|
|
443
|
+
* fetch result because it is vocabulary, not data: it never goes stale, and
|
|
444
|
+
* a function would not survive the cache the sidebar is painted from.
|
|
445
|
+
*/
|
|
446
|
+
liveLabel?: LiveLabel;
|
|
404
447
|
title?: string;
|
|
405
448
|
detailFor?: (ctx: DetailContext) => React.ReactNode;
|
|
406
449
|
/** A two-way repo split, and what to call the side on screen. See OriginSplit. */
|
|
@@ -453,4 +496,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
453
496
|
*/
|
|
454
497
|
declare const parsePatterns: (value: string) => string[];
|
|
455
498
|
|
|
456
|
-
export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type RepoFilter, SIDEBAR_COLS, SidePanel, type Sidebar, type SidebarRow, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
|
499
|
+
export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type LiveLabel, MIN_SIDEBAR_COLS, type RepoFilter, SIDEBAR_COLS, SidePanel, type Sidebar, type SidebarRow, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, railWidth, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React2, { createContext, useState, useRef, useEffect, useMemo, useContext } from 'react';
|
|
2
2
|
import { useWindowSize, useInput, Text, Box, useApp } from 'ink';
|
|
3
|
-
import { colors, ScrollView, TextInput, LoadingScreen, StatusMessage, FooterHints, useListCursor, Tabs, pillWidth, Pill } from '@kud/ink-ui';
|
|
3
|
+
import { colors, ScrollView, TextInput, LoadingScreen, ProgressBar, StatusMessage, FooterHints, useListCursor, Tabs, pillWidth, Pill } from '@kud/ink-ui';
|
|
4
4
|
import { isPassCheck, isFailCheck, isInconclusiveCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer } from '@kud/gh';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { $ } from 'zx';
|
|
@@ -585,73 +585,115 @@ var HealthPanel = ({
|
|
|
585
585
|
] }) }) : note ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: colors.success, children: " " + note }) }) : null
|
|
586
586
|
] });
|
|
587
587
|
};
|
|
588
|
-
var
|
|
589
|
-
var
|
|
588
|
+
var SIDEBAR_COLS = 64;
|
|
589
|
+
var MIN_SIDEBAR_COLS = 52;
|
|
590
|
+
var railWidth = (cols) => Math.max(MIN_SIDEBAR_COLS, Math.min(SIDEBAR_COLS, Math.round(cols / 3)));
|
|
590
591
|
var RULE = 1;
|
|
591
592
|
var PAD = 2;
|
|
592
|
-
var
|
|
593
|
-
var
|
|
594
|
-
var
|
|
595
|
-
var
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
var
|
|
593
|
+
var contentOf = (width) => width - RULE - PAD;
|
|
594
|
+
var MARKS = 4;
|
|
595
|
+
var FACTS_INDENT = 6;
|
|
596
|
+
var KEY_COLS = 10;
|
|
597
|
+
var BAR_COLS = 10;
|
|
598
|
+
var FRACTION_COLS = 5;
|
|
599
|
+
var GUTTER = " ";
|
|
600
|
+
var FACTS_FIXED = FACTS_INDENT + KEY_COLS + BAR_COLS + FRACTION_COLS + GUTTER.length * 3;
|
|
601
|
+
var defaultLiveLabel = (live) => live === 0 ? "nothing live" : `${live} live`;
|
|
602
|
+
var fractionOf = (row) => row.done !== void 0 && row.total !== void 0 ? `${row.done}/${row.total}` : "";
|
|
603
|
+
var counts = (row, liveLabel = defaultLiveLabel) => [fractionOf(row), row.live !== void 0 ? liveLabel(row.live) : ""].filter(Boolean).join(" \xB7 ");
|
|
604
|
+
var DANGLING = /[\s:;,.\-—–(\[{]+$/u;
|
|
605
|
+
var truncateWords = (text, max) => {
|
|
606
|
+
const chars = [...text];
|
|
607
|
+
if (chars.length <= max) return text;
|
|
608
|
+
const room = Math.max(0, max - 1);
|
|
609
|
+
const hard = chars.slice(0, room).join("");
|
|
610
|
+
const boundary = chars[room] === " " ? room : hard.lastIndexOf(" ");
|
|
611
|
+
const soft = boundary >= Math.floor(room * 0.6) ? hard.slice(0, boundary) : hard;
|
|
612
|
+
return soft.replace(DANGLING, "") + "\u2026";
|
|
613
|
+
};
|
|
600
614
|
var ROW_LINES = 3;
|
|
601
615
|
var HEADING_LINES = 2;
|
|
602
616
|
var railCapacity = (height, rows) => {
|
|
603
617
|
const fits = Math.max(0, Math.floor((height - HEADING_LINES) / ROW_LINES));
|
|
604
618
|
return fits >= rows ? rows : Math.max(0, fits - 1);
|
|
605
619
|
};
|
|
620
|
+
var percentOf = (done, total) => total === 0 ? 0 : Math.round(done / total * 100);
|
|
621
|
+
var Progress = ({ row }) => {
|
|
622
|
+
const fraction = fractionOf(row);
|
|
623
|
+
const bar = row.done !== void 0 && row.total !== void 0 && row.total > 0 ? /* @__PURE__ */ jsx(
|
|
624
|
+
ProgressBar,
|
|
625
|
+
{
|
|
626
|
+
value: percentOf(row.done, row.total),
|
|
627
|
+
width: BAR_COLS,
|
|
628
|
+
color: colors.secondary
|
|
629
|
+
}
|
|
630
|
+
) : /* @__PURE__ */ jsx(Text, { children: " ".repeat(BAR_COLS) });
|
|
631
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
632
|
+
bar,
|
|
633
|
+
/* @__PURE__ */ jsx(Text, { color: colors.secondary, children: GUTTER + fraction.padStart(FRACTION_COLS) })
|
|
634
|
+
] });
|
|
635
|
+
};
|
|
606
636
|
var SidePanel = ({
|
|
607
637
|
sidebar,
|
|
638
|
+
liveLabel = defaultLiveLabel,
|
|
639
|
+
width = SIDEBAR_COLS,
|
|
608
640
|
height,
|
|
609
641
|
focused = false,
|
|
610
642
|
cursor = 0
|
|
611
643
|
}) => {
|
|
644
|
+
const content = contentOf(width);
|
|
645
|
+
const labelCols = content - MARKS;
|
|
646
|
+
const liveCols = content - FACTS_FIXED;
|
|
612
647
|
const capacity = height === void 0 ? sidebar.rows.length : railCapacity(height, sidebar.rows.length);
|
|
613
648
|
const start = focused && cursor >= capacity ? Math.min(cursor - capacity + 1, sidebar.rows.length - capacity) : 0;
|
|
614
649
|
const shown = sidebar.rows.slice(start, start + capacity);
|
|
615
650
|
const hidden = sidebar.rows.length - shown.length;
|
|
651
|
+
const focus = focused ? " \u25CF focus" : "";
|
|
652
|
+
const rule = "\u254C".repeat(
|
|
653
|
+
Math.max(0, content - [...sidebar.title].length - focus.length - 1)
|
|
654
|
+
);
|
|
616
655
|
return (
|
|
617
656
|
// A rule down the left rather than a full box: the rail's other three edges
|
|
618
657
|
// already have the frame's border a column or two away, and a second
|
|
619
658
|
// rectangle inside the first reads as a nested panel — something you could
|
|
620
659
|
// focus and act on, which this cannot be. One line is the whole claim: what
|
|
621
|
-
// is left of it is the list, what is right of it is not.
|
|
660
|
+
// is left of it is the list, what is right of it is not. In the frame's own
|
|
661
|
+
// colour and not dimmed, so the two rules read as one hierarchy.
|
|
622
662
|
/* @__PURE__ */ jsxs(
|
|
623
663
|
Box,
|
|
624
664
|
{
|
|
625
665
|
flexDirection: "column",
|
|
626
|
-
width
|
|
666
|
+
width,
|
|
627
667
|
flexShrink: 0,
|
|
628
668
|
height,
|
|
629
669
|
borderStyle: "single",
|
|
630
|
-
borderColor:
|
|
631
|
-
borderDimColor: true,
|
|
670
|
+
borderColor: colors.muted,
|
|
632
671
|
borderTop: false,
|
|
633
672
|
borderRight: false,
|
|
634
673
|
borderBottom: false,
|
|
635
674
|
paddingLeft: PAD,
|
|
636
675
|
children: [
|
|
637
676
|
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
638
|
-
/* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: "\xBB " }),
|
|
639
677
|
/* @__PURE__ */ jsx(Text, { bold: true, children: sidebar.title }),
|
|
640
|
-
focused ? /* @__PURE__ */ jsx(Text, { color:
|
|
678
|
+
focused ? /* @__PURE__ */ jsx(Text, { color: colors.accent, children: focus }) : null,
|
|
679
|
+
/* @__PURE__ */ jsx(Text, { color: colors.info, dimColor: true, children: ` ${rule}` })
|
|
641
680
|
] }),
|
|
642
|
-
sidebar.rows.length === 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft:
|
|
681
|
+
sidebar.rows.length === 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: MARKS, children: /* @__PURE__ */ jsx(Text, { color: colors.muted, children: "nothing open" }) }) : shown.map((row, i) => {
|
|
643
682
|
const active = focused && start + i === cursor;
|
|
644
683
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
645
684
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
646
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
647
|
-
/* @__PURE__ */ jsx(Text, { color:
|
|
648
|
-
/* @__PURE__ */ jsx(Text, {
|
|
649
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${counts(row)}` })
|
|
685
|
+
/* @__PURE__ */ jsx(Text, { color: colors.info, children: active ? "\u276F " : " " }),
|
|
686
|
+
/* @__PURE__ */ jsx(Text, { color: colors.accent, bold: true, children: row.wantsYou ? "\u2190 " : " " }),
|
|
687
|
+
/* @__PURE__ */ jsx(Text, { bold: active, children: truncateWords(row.label, labelCols) })
|
|
650
688
|
] }),
|
|
651
|
-
/* @__PURE__ */
|
|
689
|
+
/* @__PURE__ */ jsxs(Box, { paddingLeft: FACTS_INDENT, children: [
|
|
690
|
+
/* @__PURE__ */ jsx(Text, { color: colors.secondary, children: row.key.padEnd(KEY_COLS) + GUTTER }),
|
|
691
|
+
/* @__PURE__ */ jsx(Progress, { row }),
|
|
692
|
+
/* @__PURE__ */ jsx(Text, { children: GUTTER + (row.live !== void 0 ? liveLabel(row.live).slice(0, liveCols) : "") })
|
|
693
|
+
] })
|
|
652
694
|
] }, row.key);
|
|
653
695
|
}),
|
|
654
|
-
hidden > 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft:
|
|
696
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: MARKS, children: /* @__PURE__ */ jsx(Text, { color: colors.muted, children: `+${hidden} more` }) }) : null
|
|
655
697
|
]
|
|
656
698
|
}
|
|
657
699
|
)
|
|
@@ -720,7 +762,8 @@ var readCache = (key) => {
|
|
|
720
762
|
sections: raw.sections,
|
|
721
763
|
login: raw.login ?? "",
|
|
722
764
|
at: raw.at ?? 0,
|
|
723
|
-
budget: raw.budget
|
|
765
|
+
budget: raw.budget,
|
|
766
|
+
...Array.isArray(raw.sidebar?.rows) ? { sidebar: raw.sidebar } : {}
|
|
724
767
|
};
|
|
725
768
|
} catch {
|
|
726
769
|
return null;
|
|
@@ -1135,7 +1178,7 @@ var withHeaders = (items, idx) => {
|
|
|
1135
1178
|
start--;
|
|
1136
1179
|
return start;
|
|
1137
1180
|
};
|
|
1138
|
-
var
|
|
1181
|
+
var truncate = (str, max) => {
|
|
1139
1182
|
if (str.length <= max) return str;
|
|
1140
1183
|
const half = Math.floor((max - 1) / 2);
|
|
1141
1184
|
return `${str.slice(0, half)}\u2026${str.slice(-half)}`;
|
|
@@ -1723,18 +1766,18 @@ var InboxHeader = ({
|
|
|
1723
1766
|
const userSeg = loading || quiet ? "" : `@${login} `;
|
|
1724
1767
|
const workLabel = scopeLabel ? ` ${scopeLabel} ` : "";
|
|
1725
1768
|
const budgetSeg = budgetLabel ? budgetLabel + " " : "";
|
|
1726
|
-
const [statusText, statusColor] = hasPending ? [`\u25CF ${pendingSummary || "new"} \xB7 r apply`,
|
|
1769
|
+
const [statusText, statusColor] = hasPending ? [`\u25CF ${pendingSummary || "new"} \xB7 r apply`, colors.accent] : refreshing ? ["\u21BB refreshing\u2026", "cyan"] : appliedSummary ? [`\u25C9 ${appliedSummary}`, colors.accent] : fetchedAt ? [`updated ${agoText(fetchedAt)}`, void 0] : ["", void 0];
|
|
1727
1770
|
const statusSeg = statusText ? statusText + " " : "";
|
|
1728
1771
|
const fill = Math.max(
|
|
1729
1772
|
4,
|
|
1730
1773
|
COLS - brand.length - countSeg.length - userSeg.length - workLabel.length - budgetSeg.length - statusSeg.length
|
|
1731
1774
|
);
|
|
1732
1775
|
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
1733
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
1776
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: true, children: brand }),
|
|
1734
1777
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: countSeg }),
|
|
1735
1778
|
userSeg ? /* @__PURE__ */ jsx(Text3, { children: userSeg }) : null,
|
|
1736
1779
|
scopeLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: workLabel }) : null,
|
|
1737
|
-
budgetLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: budgetCritical ? "#FF5F5F" :
|
|
1780
|
+
budgetLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: budgetCritical ? "#FF5F5F" : colors.accent, children: budgetSeg }) : null,
|
|
1738
1781
|
statusText ? /* @__PURE__ */ jsx(
|
|
1739
1782
|
Text3,
|
|
1740
1783
|
{
|
|
@@ -1783,7 +1826,7 @@ var CiStatusLine = ({
|
|
|
1783
1826
|
/* @__PURE__ */ jsx(Text3, { color, bold: true, children: "\u25CF " }),
|
|
1784
1827
|
/* @__PURE__ */ jsx(Text3, { bold: true, children: status.job }),
|
|
1785
1828
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + label }),
|
|
1786
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
1829
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, children: " #" + status.buildNumber }),
|
|
1787
1830
|
status.age ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + status.age }) : null
|
|
1788
1831
|
] });
|
|
1789
1832
|
};
|
|
@@ -1825,13 +1868,13 @@ var TRANSIT_IN_FRAMES = ["\xB7", "\u25CB", "\u25CE", "\u25C9"];
|
|
|
1825
1868
|
var TRANSIT_COLOUR = {
|
|
1826
1869
|
in: "#3FB950",
|
|
1827
1870
|
out: "#8B949E",
|
|
1828
|
-
changed:
|
|
1871
|
+
changed: colors.accent,
|
|
1829
1872
|
// A move is neither an arrival nor a departure, so it takes the colour of the
|
|
1830
1873
|
// third thing that can happen to a row you already had: it changed. Both ends
|
|
1831
1874
|
// of one move wear the same colour and the same word, which is what lets you
|
|
1832
1875
|
// recognise the row you just watched leave when you land on the tab it went to.
|
|
1833
|
-
"moved-in":
|
|
1834
|
-
"moved-out":
|
|
1876
|
+
"moved-in": colors.accent,
|
|
1877
|
+
"moved-out": colors.accent
|
|
1835
1878
|
};
|
|
1836
1879
|
var TRANSIT_LABEL = {
|
|
1837
1880
|
out: "gone"};
|
|
@@ -1863,7 +1906,7 @@ var ItemRow = ({
|
|
|
1863
1906
|
return /* @__PURE__ */ jsx(RepoHeaderRow, { repo: item.repo, gap: gap ?? false, active });
|
|
1864
1907
|
if (item.kind === "subgroup-header")
|
|
1865
1908
|
return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
|
|
1866
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
1909
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: true, children: " \xBB " }),
|
|
1867
1910
|
/* @__PURE__ */ jsx(Text3, { bold: true, children: item.label })
|
|
1868
1911
|
] });
|
|
1869
1912
|
if (item.kind === "show-more")
|
|
@@ -1892,14 +1935,14 @@ var ItemRow = ({
|
|
|
1892
1935
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
|
|
1893
1936
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: parent ? "\u252C " : " " }),
|
|
1894
1937
|
/* @__PURE__ */ jsx(Text3, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
|
|
1895
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
1938
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: active, children: item.key + " " }),
|
|
1896
1939
|
/* @__PURE__ */ jsx(
|
|
1897
1940
|
Text3,
|
|
1898
1941
|
{
|
|
1899
1942
|
bold: active || !!transient && isArrival(transient),
|
|
1900
1943
|
dimColor: !!transient && isDeparture(transient),
|
|
1901
1944
|
strikethrough: transient === "out",
|
|
1902
|
-
children:
|
|
1945
|
+
children: truncate(item.summary, titleMax2)
|
|
1903
1946
|
}
|
|
1904
1947
|
),
|
|
1905
1948
|
item.pill && !backdropped ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1925,7 +1968,7 @@ var ItemRow = ({
|
|
|
1925
1968
|
const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient && isDeparture(transient) ? TRANSIT_OUT_FRAMES[rampAt(TRANSIT_OUT_FRAMES.length)] : transient && isArrival(transient) ? TRANSIT_IN_FRAMES[rampAt(TRANSIT_IN_FRAMES.length)] : healthIcon;
|
|
1926
1969
|
const color = merged ? MERGED_COLOUR : transient ? TRANSIT_COLOUR[transient] : healthColor2;
|
|
1927
1970
|
const spokeLast = !!login && !!item.lastActor && item.lastActor === login;
|
|
1928
|
-
const [turnIcon, turnColor] = item.pinned ? [PIN_MARK,
|
|
1971
|
+
const [turnIcon, turnColor] = item.pinned ? [PIN_MARK, colors.accent] : !login || !item.lastActor ? [" ", "white"] : spokeLast ? ["\u2192", colors.secondary] : ["\u2190", colors.accent];
|
|
1929
1972
|
const numStr = `#${item.number}`.padEnd(7);
|
|
1930
1973
|
const showAuthor = !!item.author && item.author !== login;
|
|
1931
1974
|
const sizeLabel = sizeOf(item);
|
|
@@ -1958,7 +2001,7 @@ var ItemRow = ({
|
|
|
1958
2001
|
if (next.length <= LABEL_CELL_MAX) fitted.push(name);
|
|
1959
2002
|
}
|
|
1960
2003
|
if (fitted.length === 0) {
|
|
1961
|
-
return `\uF02B ${
|
|
2004
|
+
return `\uF02B ${truncate(shown[0], LABEL_CELL_MAX)}`;
|
|
1962
2005
|
}
|
|
1963
2006
|
return `\uF02B ${fitted.join(", ")}`;
|
|
1964
2007
|
};
|
|
@@ -1994,14 +2037,14 @@ var ItemRow = ({
|
|
|
1994
2037
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
|
|
1995
2038
|
/* @__PURE__ */ jsx(Text3, { color, bold: true, children: icon + " " }),
|
|
1996
2039
|
/* @__PURE__ */ jsx(Text3, { color: turnColor, bold: turnIcon === "\u2190", children: turnIcon + " " }),
|
|
1997
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
2040
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, children: numStr }),
|
|
1998
2041
|
/* @__PURE__ */ jsx(
|
|
1999
2042
|
Text3,
|
|
2000
2043
|
{
|
|
2001
2044
|
bold: active || !!transient && isArrival(transient),
|
|
2002
2045
|
dimColor: !!transient && isDeparture(transient),
|
|
2003
2046
|
strikethrough: transient === "out",
|
|
2004
|
-
children:
|
|
2047
|
+
children: truncate(item.title, titleMax) + " "
|
|
2005
2048
|
}
|
|
2006
2049
|
),
|
|
2007
2050
|
labelLabel ? /* @__PURE__ */ jsx(Text3, { color: colors.secondary, children: labelLabel + " " }) : null,
|
|
@@ -2012,7 +2055,7 @@ var ItemRow = ({
|
|
|
2012
2055
|
" ",
|
|
2013
2056
|
/* @__PURE__ */ jsx(Text3, { color: colors.error, children: sizeParts.removed })
|
|
2014
2057
|
] }) : null,
|
|
2015
|
-
unresolvedLabel && !givingUp.threads ? /* @__PURE__ */ jsx(Text3, { bold: !spokeLast, color: spokeLast ? colors.secondary :
|
|
2058
|
+
unresolvedLabel && !givingUp.threads ? /* @__PURE__ */ jsx(Text3, { bold: !spokeLast, color: spokeLast ? colors.secondary : colors.accent, children: " " + unresolvedLabel }) : null,
|
|
2016
2059
|
showAuthor && !givingUp.author ? /* @__PURE__ */ jsx(Text3, { dimColor: true, italic: true, children: " by " + item.author }) : null,
|
|
2017
2060
|
ageLabel && !givingUp.age ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + ageLabel }) : null,
|
|
2018
2061
|
mergedLabel && !backdropped ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -2082,7 +2125,7 @@ var ActionMenu = ({
|
|
|
2082
2125
|
);
|
|
2083
2126
|
};
|
|
2084
2127
|
var TURN_LEGEND = [
|
|
2085
|
-
["\u2190",
|
|
2128
|
+
["\u2190", colors.accent, "They spoke last \xB7 your turn"],
|
|
2086
2129
|
["\u2192", colors.secondary, "You spoke last \xB7 waiting on them"]
|
|
2087
2130
|
];
|
|
2088
2131
|
var CELL_GUTTER = 2;
|
|
@@ -2187,7 +2230,7 @@ var HelpModal = ({
|
|
|
2187
2230
|
})),
|
|
2188
2231
|
{
|
|
2189
2232
|
cell: "\uF086",
|
|
2190
|
-
color:
|
|
2233
|
+
color: colors.accent,
|
|
2191
2234
|
bold: true,
|
|
2192
2235
|
label: "Open-thread count"
|
|
2193
2236
|
}
|
|
@@ -2200,7 +2243,7 @@ var HelpModal = ({
|
|
|
2200
2243
|
title: "Tabs",
|
|
2201
2244
|
rows: tabHelp.map(([tab, meaning]) => ({
|
|
2202
2245
|
cell: tab,
|
|
2203
|
-
color:
|
|
2246
|
+
color: colors.accent,
|
|
2204
2247
|
label: meaning
|
|
2205
2248
|
}))
|
|
2206
2249
|
}
|
|
@@ -2275,7 +2318,7 @@ var ExplainModal = ({ item, login }) => /* @__PURE__ */ jsxs(
|
|
|
2275
2318
|
paddingX: 1,
|
|
2276
2319
|
width: Math.min(COLS, 78),
|
|
2277
2320
|
children: [
|
|
2278
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
2321
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: true, children: `#${item.number} \xB7 ${item.repo}` }),
|
|
2279
2322
|
/* @__PURE__ */ jsx(Text3, { children: item.title }),
|
|
2280
2323
|
explainItem(item, login).map((section) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
2281
2324
|
/* @__PURE__ */ jsx(Text3, { bold: true, dimColor: true, children: section.heading }),
|
|
@@ -2364,7 +2407,8 @@ var BrowseScreen = ({
|
|
|
2364
2407
|
onLeave,
|
|
2365
2408
|
transients,
|
|
2366
2409
|
onTabChange,
|
|
2367
|
-
sidebar
|
|
2410
|
+
sidebar,
|
|
2411
|
+
liveLabel
|
|
2368
2412
|
}) => {
|
|
2369
2413
|
const { rows } = useWindowSize();
|
|
2370
2414
|
const { exit } = useApp();
|
|
@@ -2488,7 +2532,8 @@ var BrowseScreen = ({
|
|
|
2488
2532
|
const railRows = sidebar?.rows ?? [];
|
|
2489
2533
|
const railActive = showRail && railFocus && railRows.length > 0;
|
|
2490
2534
|
const railAt = Math.min(railCursor, Math.max(0, railRows.length - 1));
|
|
2491
|
-
const
|
|
2535
|
+
const railCols = railWidth(COLS);
|
|
2536
|
+
const listCols = showRail ? COLS - railCols : COLS;
|
|
2492
2537
|
const [sparkFrame, setSparkFrame] = useState(0);
|
|
2493
2538
|
const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || // Any MARKED TAB, not just rows on screen. The scope used to be this tab's
|
|
2494
2539
|
// own items, which was right while the animation lived on the rows and is
|
|
@@ -2577,11 +2622,11 @@ var BrowseScreen = ({
|
|
|
2577
2622
|
if (key.ctrl || key.meta) return;
|
|
2578
2623
|
if (help) {
|
|
2579
2624
|
if (helpRange.max > 0) {
|
|
2580
|
-
const
|
|
2581
|
-
if (key.upArrow) return
|
|
2582
|
-
if (key.downArrow) return
|
|
2583
|
-
if (key.pageUp) return
|
|
2584
|
-
if (key.pageDown) return
|
|
2625
|
+
const step2 = (n) => setHelpScroll((s) => Math.min(helpRange.max, Math.max(0, s + n)));
|
|
2626
|
+
if (key.upArrow) return step2(-1);
|
|
2627
|
+
if (key.downArrow) return step2(1);
|
|
2628
|
+
if (key.pageUp) return step2(-helpRange.page);
|
|
2629
|
+
if (key.pageDown) return step2(helpRange.page);
|
|
2585
2630
|
}
|
|
2586
2631
|
setHelp(false);
|
|
2587
2632
|
return;
|
|
@@ -2691,12 +2736,10 @@ var BrowseScreen = ({
|
|
|
2691
2736
|
setCursors((p) => ({ ...p, [activeId]: next }));
|
|
2692
2737
|
setViewStarts((p) => ({ ...p, [activeId]: newVs }));
|
|
2693
2738
|
}
|
|
2694
|
-
|
|
2695
|
-
if (
|
|
2696
|
-
setTabIdx((i) => Math.min(localSections.length - 1, i + 1));
|
|
2697
|
-
if (key.tab)
|
|
2739
|
+
const step = key.leftArrow || key.tab && key.shift ? -1 : key.rightArrow || key.tab ? 1 : 0;
|
|
2740
|
+
if (step !== 0)
|
|
2698
2741
|
setTabIdx(
|
|
2699
|
-
(i) => (i +
|
|
2742
|
+
(i) => (i + step + localSections.length) % localSections.length
|
|
2700
2743
|
);
|
|
2701
2744
|
if (input === "q") exit();
|
|
2702
2745
|
if (input === "r") {
|
|
@@ -3011,7 +3054,7 @@ var BrowseScreen = ({
|
|
|
3011
3054
|
),
|
|
3012
3055
|
ciStatusState ? /* @__PURE__ */ jsx(CiStatusLine, { state: ciStatusState, job: ciJob }) : null,
|
|
3013
3056
|
skippedForBudget ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3014
|
-
/* @__PURE__ */ jsx(Text3, { bold: true, color:
|
|
3057
|
+
/* @__PURE__ */ jsx(Text3, { bold: true, color: colors.accent, children: " \u26A1 auto-refresh paused to save API budget" }),
|
|
3015
3058
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " r refreshes anyway" })
|
|
3016
3059
|
] }) : null,
|
|
3017
3060
|
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
@@ -3043,7 +3086,7 @@ var BrowseScreen = ({
|
|
|
3043
3086
|
s.id,
|
|
3044
3087
|
pulseSettled ? PULSE_SETTLED_FRAME : sparkFrame
|
|
3045
3088
|
),
|
|
3046
|
-
markerColor:
|
|
3089
|
+
markerColor: colors.accent
|
|
3047
3090
|
}))
|
|
3048
3091
|
}
|
|
3049
3092
|
) }),
|
|
@@ -3053,7 +3096,7 @@ var BrowseScreen = ({
|
|
|
3053
3096
|
searchInput ? /* @__PURE__ */ jsx(Text3, { color: "cyan", children: "\u258F" }) : null,
|
|
3054
3097
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${matchCount} match${matchCount !== 1 ? "es" : ""}${searchInput ? " \u21B5 accept \xB7 esc clear" : " esc clear"}` })
|
|
3055
3098
|
] }) : repoFilter.size > 0 ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
3056
|
-
/* @__PURE__ */ jsx(Text3, { color:
|
|
3099
|
+
/* @__PURE__ */ jsx(Text3, { color: colors.accent, children: " \u25C9 " }),
|
|
3057
3100
|
/* @__PURE__ */ jsx(Text3, { children: `${repoFilter.size} repo${repoFilter.size !== 1 ? "s" : ""}` }),
|
|
3058
3101
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: " f edit \xB7 esc clear" })
|
|
3059
3102
|
] }) : null,
|
|
@@ -3117,6 +3160,8 @@ var BrowseScreen = ({
|
|
|
3117
3160
|
SidePanel,
|
|
3118
3161
|
{
|
|
3119
3162
|
sidebar,
|
|
3163
|
+
liveLabel,
|
|
3164
|
+
width: railCols,
|
|
3120
3165
|
height: listHeight,
|
|
3121
3166
|
focused: railActive,
|
|
3122
3167
|
cursor: railAt
|
|
@@ -3179,7 +3224,8 @@ var App = ({
|
|
|
3179
3224
|
watchJitterMs = 0,
|
|
3180
3225
|
extensions,
|
|
3181
3226
|
tabHelp,
|
|
3182
|
-
emptyHint
|
|
3227
|
+
emptyHint,
|
|
3228
|
+
liveLabel
|
|
3183
3229
|
}) => {
|
|
3184
3230
|
const { rows } = useWindowSize();
|
|
3185
3231
|
const [state, setState] = useState({ phase: "loading" });
|
|
@@ -3356,6 +3402,7 @@ var App = ({
|
|
|
3356
3402
|
displayedKey.current = signatureOf(cached.sections);
|
|
3357
3403
|
displayedSections.current = cached.sections;
|
|
3358
3404
|
setFetchedAt(cached.at);
|
|
3405
|
+
if (cached.sidebar) setSidebar(cached.sidebar);
|
|
3359
3406
|
setState({
|
|
3360
3407
|
phase: "browse",
|
|
3361
3408
|
sections: cached.sections,
|
|
@@ -3400,6 +3447,7 @@ var App = ({
|
|
|
3400
3447
|
const shared = cacheKey ? readCache(cacheKey) : null;
|
|
3401
3448
|
if (shared && shared.at > firedAt) {
|
|
3402
3449
|
receive({ sections: shared.sections, login: shared.login });
|
|
3450
|
+
if (shared.sidebar) setSidebar(shared.sidebar);
|
|
3403
3451
|
setFetchedAt(shared.at);
|
|
3404
3452
|
return;
|
|
3405
3453
|
}
|
|
@@ -3544,6 +3592,7 @@ var App = ({
|
|
|
3544
3592
|
transients,
|
|
3545
3593
|
onTabChange: setVisibleTab,
|
|
3546
3594
|
sidebar,
|
|
3595
|
+
liveLabel,
|
|
3547
3596
|
ciStatusState: hasCiStatus ? ciStatusState : void 0,
|
|
3548
3597
|
ciJob,
|
|
3549
3598
|
tabHelp,
|
|
@@ -3596,4 +3645,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
3596
3645
|
};
|
|
3597
3646
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3598
3647
|
|
|
3599
|
-
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState,
|
|
3648
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, MIN_SIDEBAR_COLS, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, railWidth, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, toCiStatusState, truncate, useActionMenu, 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.54.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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@kud/gh": "0.16.0",
|
|
52
52
|
"@kud/gh-workflow": "0.11.0",
|
|
53
|
-
"@kud/ink-ui": "0.
|
|
53
|
+
"@kud/ink-ui": "0.29.0",
|
|
54
54
|
"zx": "8.8.5"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|