@kud/gh-ink 0.53.0 → 0.55.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 +49 -14
- package/dist/index.js +107 -18
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import { PrComments, PrHealthData, PrCheck, Health } from '@kud/gh';
|
|
3
3
|
import { StyledLine } from '@kud/ink-ui';
|
|
4
4
|
import { AnyItem, Section, CiStatus, GHItem, OriginSplit } from '@kud/gh-workflow';
|
|
5
|
-
export { AnyItem, CiStatus, GHDetail, GHItem, InboxConfig, Move, OriginSplit, PIN_MARK, RepoHeader, RepoProfile, Section, ShowLess, ShowMore, Standing, SubgroupHeader, TaskRow, checkoutDirs, configureInbox, filterByOrigin, filterByRepos, filterBySearch, impliedLabels, inboxConfig, insertRepoHeaders, labelPriority, layoutGHItems, profileOf, relativeTime, repoPriority, resetInboxConfig, sortByRecency, sortItems, whoseMove } from '@kud/gh-workflow';
|
|
5
|
+
export { AnyItem, Band, CiStatus, GHDetail, GHItem, InboxConfig, Move, OriginSplit, PIN_MARK, RepoHeader, RepoProfile, Section, ShowLess, ShowMore, Standing, SubgroupHeader, TaskRow, bandOf, checkoutDirs, configureInbox, filterByOrigin, filterByRepos, filterBySearch, impliedLabels, inboxConfig, insertRepoHeaders, labelPriority, layoutGHItems, profileOf, relativeTime, repoPriority, resetInboxConfig, sortByRecency, sortItems, whoseMove } from '@kud/gh-workflow';
|
|
6
6
|
|
|
7
7
|
type CommentsPanelProps = {
|
|
8
8
|
repo: string;
|
|
@@ -66,19 +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
|
-
/**
|
|
74
|
-
* What to call `live` on screen, in the host's words — `4 on board` and `off
|
|
75
|
-
* board` on a ticket cockpit, say. Takes the number rather than a word pair
|
|
76
|
-
* because the two states need not share a sentence shape: "off board" has no
|
|
77
|
-
* N in it. `live` itself stays a number, so the absent-draws-nothing contract
|
|
78
|
-
* and the host's sort are untouched.
|
|
79
|
-
*/
|
|
80
|
-
liveLabel?: (live: number) => string;
|
|
81
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;
|
|
82
89
|
/**
|
|
83
90
|
* The rail's width, INCLUDING its rule and padding. Exported because the list
|
|
84
91
|
* beside it has to shrink by exactly this much: every row in that list truncates
|
|
@@ -91,8 +98,18 @@ type Sidebar = {
|
|
|
91
98
|
* word and left the rail listing keys with an ellipsis after them. The key line
|
|
92
99
|
* was never the constraint — the label is what makes a key readable, and a rail
|
|
93
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.
|
|
94
108
|
*/
|
|
95
|
-
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;
|
|
96
113
|
/**
|
|
97
114
|
* The plain-text facts of a row, for a host or a test that wants them as one
|
|
98
115
|
* string: how far through, then how much is moving. Either half may be missing
|
|
@@ -100,7 +117,7 @@ declare const SIDEBAR_COLS = 52;
|
|
|
100
117
|
* counted, because "nothing is moving" is exactly the thing a roadmap is read
|
|
101
118
|
* to notice.
|
|
102
119
|
*/
|
|
103
|
-
declare const counts: (row: SidebarRow, liveLabel?:
|
|
120
|
+
declare const counts: (row: SidebarRow, liveLabel?: LiveLabel) => string;
|
|
104
121
|
/**
|
|
105
122
|
* How many rows fit in `height` — one fewer than would physically fit whenever
|
|
106
123
|
* that means anything is left over, to buy the line that says so.
|
|
@@ -126,8 +143,11 @@ declare const railCapacity: (height: number, rows: number) => number;
|
|
|
126
143
|
* the arrows are pointed at this rail at all, are the host's state — which is
|
|
127
144
|
* what lets a screen with two focus regions have exactly one of them lit.
|
|
128
145
|
*/
|
|
129
|
-
declare const SidePanel: ({ sidebar, height, focused, cursor, }: {
|
|
146
|
+
declare const SidePanel: ({ sidebar, liveLabel, width, height, focused, cursor, }: {
|
|
130
147
|
sidebar: Sidebar;
|
|
148
|
+
liveLabel?: LiveLabel;
|
|
149
|
+
/** From `railWidth`, so the list beside it can subtract the same number. */
|
|
150
|
+
width?: number;
|
|
131
151
|
height?: number;
|
|
132
152
|
/** The arrows are pointed here, so this rail draws the cursor. */
|
|
133
153
|
focused?: boolean;
|
|
@@ -192,12 +212,20 @@ type CachedCockpit = {
|
|
|
192
212
|
login: string;
|
|
193
213
|
at: number;
|
|
194
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;
|
|
195
222
|
};
|
|
196
223
|
declare const readCache: (key: string) => CachedCockpit | null;
|
|
197
224
|
declare const writeCache: (key: string, data: {
|
|
198
225
|
sections: Section[];
|
|
199
226
|
login: string;
|
|
200
227
|
budget?: InboxBudget;
|
|
228
|
+
sidebar?: Sidebar;
|
|
201
229
|
}) => void;
|
|
202
230
|
|
|
203
231
|
type DetailContext = {
|
|
@@ -386,7 +414,7 @@ declare const ActionMenu: ({ item, actions, cursor, }: {
|
|
|
386
414
|
cursor: number;
|
|
387
415
|
}) => React.JSX.Element;
|
|
388
416
|
declare const signatureOf: (sections: Section[]) => string;
|
|
389
|
-
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, }: {
|
|
390
418
|
fetcher: () => Promise<{
|
|
391
419
|
sections: Section[];
|
|
392
420
|
login: string;
|
|
@@ -409,6 +437,13 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jir
|
|
|
409
437
|
sidebar?: Sidebar;
|
|
410
438
|
}>;
|
|
411
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;
|
|
412
447
|
title?: string;
|
|
413
448
|
detailFor?: (ctx: DetailContext) => React.ReactNode;
|
|
414
449
|
/** A two-way repo split, and what to call the side on screen. See OriginSplit. */
|
|
@@ -461,4 +496,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
461
496
|
*/
|
|
462
497
|
declare const parsePatterns: (value: string) => string[];
|
|
463
498
|
|
|
464
|
-
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
|
@@ -9,7 +9,7 @@ import { readFileSync, mkdirSync, writeFileSync, existsSync, readdirSync, statSy
|
|
|
9
9
|
import { join, dirname, basename } from 'path';
|
|
10
10
|
import { homedir } from 'os';
|
|
11
11
|
import { depthOf, checkoutDirs, profileOf, inboxConfig, relativeTime, filterBySearch, filterByRepos, filterByOrigin, PIN_MARK, sizeOf, sizePartsOf, impliedLabels, labelPriority } from '@kud/gh-workflow';
|
|
12
|
-
export { PIN_MARK, checkoutDirs, configureInbox, filterByOrigin, filterByRepos, filterBySearch, impliedLabels, inboxConfig, insertRepoHeaders, labelPriority, layoutGHItems, profileOf, relativeTime, repoPriority, resetInboxConfig, sortByRecency, sortItems, whoseMove } from '@kud/gh-workflow';
|
|
12
|
+
export { PIN_MARK, bandOf, checkoutDirs, configureInbox, filterByOrigin, filterByRepos, filterBySearch, impliedLabels, inboxConfig, insertRepoHeaders, labelPriority, layoutGHItems, profileOf, relativeTime, repoPriority, resetInboxConfig, sortByRecency, sortItems, whoseMove } from '@kud/gh-workflow';
|
|
13
13
|
|
|
14
14
|
// src/components/comments-panel.tsx
|
|
15
15
|
var ENTITIES = {
|
|
@@ -585,18 +585,19 @@ 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 SIDEBAR_COLS =
|
|
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)));
|
|
589
591
|
var RULE = 1;
|
|
590
592
|
var PAD = 2;
|
|
591
|
-
var
|
|
593
|
+
var contentOf = (width) => width - RULE - PAD;
|
|
592
594
|
var MARKS = 4;
|
|
593
|
-
var LABEL_COLS = CONTENT - MARKS;
|
|
594
595
|
var FACTS_INDENT = 6;
|
|
595
596
|
var KEY_COLS = 10;
|
|
596
597
|
var BAR_COLS = 10;
|
|
597
598
|
var FRACTION_COLS = 5;
|
|
598
599
|
var GUTTER = " ";
|
|
599
|
-
var
|
|
600
|
+
var FACTS_FIXED = FACTS_INDENT + KEY_COLS + BAR_COLS + FRACTION_COLS + GUTTER.length * 3;
|
|
600
601
|
var defaultLiveLabel = (live) => live === 0 ? "nothing live" : `${live} live`;
|
|
601
602
|
var fractionOf = (row) => row.done !== void 0 && row.total !== void 0 ? `${row.done}/${row.total}` : "";
|
|
602
603
|
var counts = (row, liveLabel = defaultLiveLabel) => [fractionOf(row), row.live !== void 0 ? liveLabel(row.live) : ""].filter(Boolean).join(" \xB7 ");
|
|
@@ -634,18 +635,22 @@ var Progress = ({ row }) => {
|
|
|
634
635
|
};
|
|
635
636
|
var SidePanel = ({
|
|
636
637
|
sidebar,
|
|
638
|
+
liveLabel = defaultLiveLabel,
|
|
639
|
+
width = SIDEBAR_COLS,
|
|
637
640
|
height,
|
|
638
641
|
focused = false,
|
|
639
642
|
cursor = 0
|
|
640
643
|
}) => {
|
|
641
|
-
const
|
|
644
|
+
const content = contentOf(width);
|
|
645
|
+
const labelCols = content - MARKS;
|
|
646
|
+
const liveCols = content - FACTS_FIXED;
|
|
642
647
|
const capacity = height === void 0 ? sidebar.rows.length : railCapacity(height, sidebar.rows.length);
|
|
643
648
|
const start = focused && cursor >= capacity ? Math.min(cursor - capacity + 1, sidebar.rows.length - capacity) : 0;
|
|
644
649
|
const shown = sidebar.rows.slice(start, start + capacity);
|
|
645
650
|
const hidden = sidebar.rows.length - shown.length;
|
|
646
651
|
const focus = focused ? " \u25CF focus" : "";
|
|
647
652
|
const rule = "\u254C".repeat(
|
|
648
|
-
Math.max(0,
|
|
653
|
+
Math.max(0, content - [...sidebar.title].length - focus.length - 1)
|
|
649
654
|
);
|
|
650
655
|
return (
|
|
651
656
|
// A rule down the left rather than a full box: the rail's other three edges
|
|
@@ -658,7 +663,7 @@ var SidePanel = ({
|
|
|
658
663
|
Box,
|
|
659
664
|
{
|
|
660
665
|
flexDirection: "column",
|
|
661
|
-
width
|
|
666
|
+
width,
|
|
662
667
|
flexShrink: 0,
|
|
663
668
|
height,
|
|
664
669
|
borderStyle: "single",
|
|
@@ -679,12 +684,12 @@ var SidePanel = ({
|
|
|
679
684
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
680
685
|
/* @__PURE__ */ jsx(Text, { color: colors.info, children: active ? "\u276F " : " " }),
|
|
681
686
|
/* @__PURE__ */ jsx(Text, { color: colors.accent, bold: true, children: row.wantsYou ? "\u2190 " : " " }),
|
|
682
|
-
/* @__PURE__ */ jsx(Text, { bold: active, children: truncateWords(row.label,
|
|
687
|
+
/* @__PURE__ */ jsx(Text, { bold: active, children: truncateWords(row.label, labelCols) })
|
|
683
688
|
] }),
|
|
684
689
|
/* @__PURE__ */ jsxs(Box, { paddingLeft: FACTS_INDENT, children: [
|
|
685
690
|
/* @__PURE__ */ jsx(Text, { color: colors.secondary, children: row.key.padEnd(KEY_COLS) + GUTTER }),
|
|
686
691
|
/* @__PURE__ */ jsx(Progress, { row }),
|
|
687
|
-
/* @__PURE__ */ jsx(Text, { children: GUTTER + (row.live !== void 0 ? liveLabel(row.live).slice(0,
|
|
692
|
+
/* @__PURE__ */ jsx(Text, { children: GUTTER + (row.live !== void 0 ? liveLabel(row.live).slice(0, liveCols) : "") })
|
|
688
693
|
] })
|
|
689
694
|
] }, row.key);
|
|
690
695
|
}),
|
|
@@ -757,7 +762,8 @@ var readCache = (key) => {
|
|
|
757
762
|
sections: raw.sections,
|
|
758
763
|
login: raw.login ?? "",
|
|
759
764
|
at: raw.at ?? 0,
|
|
760
|
-
budget: raw.budget
|
|
765
|
+
budget: raw.budget,
|
|
766
|
+
...Array.isArray(raw.sidebar?.rows) ? { sidebar: raw.sidebar } : {}
|
|
761
767
|
};
|
|
762
768
|
} catch {
|
|
763
769
|
return null;
|
|
@@ -928,6 +934,66 @@ var tabsOfMarks = (sections, marks) => {
|
|
|
928
934
|
}
|
|
929
935
|
return out;
|
|
930
936
|
};
|
|
937
|
+
var HOLD_MS = 5 * 60 * 1e3;
|
|
938
|
+
var anchorIndex = (prevItems, prevIndex, freshItems) => {
|
|
939
|
+
const freshAt = /* @__PURE__ */ new Map();
|
|
940
|
+
freshItems.forEach((item, i) => {
|
|
941
|
+
const key = keyOf(item);
|
|
942
|
+
if (key) freshAt.set(key, i);
|
|
943
|
+
});
|
|
944
|
+
for (let i = prevIndex - 1; i >= 0; i--) {
|
|
945
|
+
const key = keyOf(prevItems[i]);
|
|
946
|
+
const at = key === null ? void 0 : freshAt.get(key);
|
|
947
|
+
if (at !== void 0) return at + 1;
|
|
948
|
+
}
|
|
949
|
+
return Math.min(prevIndex, freshItems.length);
|
|
950
|
+
};
|
|
951
|
+
var reconcile = (prev, fresh, now, holdMs = HOLD_MS) => {
|
|
952
|
+
if (prev.length === 0) return fresh;
|
|
953
|
+
const present = /* @__PURE__ */ new Set();
|
|
954
|
+
for (const section of fresh)
|
|
955
|
+
for (const item of section.items) {
|
|
956
|
+
const key = keyOf(item);
|
|
957
|
+
if (key) present.add(key);
|
|
958
|
+
}
|
|
959
|
+
const bySection = /* @__PURE__ */ new Map();
|
|
960
|
+
const labels = /* @__PURE__ */ new Map();
|
|
961
|
+
prev.forEach((section, at) => {
|
|
962
|
+
if (section.sampled) return;
|
|
963
|
+
section.items.forEach((item, index) => {
|
|
964
|
+
if (item.kind !== "pr" && item.kind !== "issue") return;
|
|
965
|
+
const row = item;
|
|
966
|
+
if (present.has(row.url)) return;
|
|
967
|
+
const heldSince = row.heldSince ?? now;
|
|
968
|
+
if (now - heldSince > holdMs) return;
|
|
969
|
+
const held = { ...row, heldSince };
|
|
970
|
+
const list = bySection.get(section.id);
|
|
971
|
+
if (list) list.push({ item: held, index });
|
|
972
|
+
else bySection.set(section.id, [{ item: held, index }]);
|
|
973
|
+
labels.set(section.id, { label: section.label, at });
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
if (bySection.size === 0) return fresh;
|
|
977
|
+
const out = fresh.map((section) => {
|
|
978
|
+
const rows = bySection.get(section.id);
|
|
979
|
+
if (!rows) return section;
|
|
980
|
+
bySection.delete(section.id);
|
|
981
|
+
const prevItems = prev.find((s) => s.id === section.id)?.items ?? [];
|
|
982
|
+
const items = [...section.items];
|
|
983
|
+
for (const row of [...rows].sort((a, b) => a.index - b.index))
|
|
984
|
+
items.splice(anchorIndex(prevItems, row.index, items), 0, row.item);
|
|
985
|
+
return { ...section, items };
|
|
986
|
+
});
|
|
987
|
+
for (const [id, rows] of bySection) {
|
|
988
|
+
const { label, at } = labels.get(id);
|
|
989
|
+
out.splice(Math.min(at, out.length), 0, {
|
|
990
|
+
id,
|
|
991
|
+
label,
|
|
992
|
+
items: rows.sort((a, b) => a.index - b.index).map((r) => r.item)
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
return out;
|
|
996
|
+
};
|
|
931
997
|
var quietly = $({ quiet: true });
|
|
932
998
|
var healthSentence = (item) => {
|
|
933
999
|
const glyph = displayFor(item.health).glyph.trim();
|
|
@@ -1889,7 +1955,8 @@ var ItemRow = ({
|
|
|
1889
1955
|
prefix = "",
|
|
1890
1956
|
parent,
|
|
1891
1957
|
sparkFrame = 0,
|
|
1892
|
-
cols = COLS
|
|
1958
|
+
cols = COLS,
|
|
1959
|
+
markerCols = 0
|
|
1893
1960
|
}) => {
|
|
1894
1961
|
const backdropped = useBackdropped();
|
|
1895
1962
|
const transient = leaving ? "out" : refreshMark;
|
|
@@ -1922,13 +1989,14 @@ var ItemRow = ({
|
|
|
1922
1989
|
const transitIcon = !transient ? " " : isDeparture(transient) ? TRANSIT_OUT_FRAMES[rampAt(TRANSIT_OUT_FRAMES.length)] : isArrival(transient) ? TRANSIT_IN_FRAMES[rampAt(TRANSIT_IN_FRAMES.length)] : "\u25C9";
|
|
1923
1990
|
const titleMax2 = Math.max(
|
|
1924
1991
|
1,
|
|
1925
|
-
cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - (farewellLabel ? pillWidth(farewellLabel) + 2 : 0) - prefix.length - 12
|
|
1992
|
+
cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - (farewellLabel ? pillWidth(farewellLabel) + 2 : 0) - prefix.length - (markerCols > 0 ? markerCols + 1 : 0) - 12
|
|
1926
1993
|
);
|
|
1927
1994
|
return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
|
|
1928
1995
|
/* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
1929
1996
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
|
|
1930
1997
|
/* @__PURE__ */ jsx(Text3, { dimColor: true, children: parent ? "\u252C " : " " }),
|
|
1931
1998
|
/* @__PURE__ */ jsx(Text3, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
|
|
1999
|
+
markerCols > 0 ? /* @__PURE__ */ jsx(Text3, { color: item.markerColor, children: (item.marker ?? "").padEnd(markerCols) + " " }) : null,
|
|
1932
2000
|
/* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: active, children: item.key + " " }),
|
|
1933
2001
|
/* @__PURE__ */ jsx(
|
|
1934
2002
|
Text3,
|
|
@@ -2401,7 +2469,8 @@ var BrowseScreen = ({
|
|
|
2401
2469
|
onLeave,
|
|
2402
2470
|
transients,
|
|
2403
2471
|
onTabChange,
|
|
2404
|
-
sidebar
|
|
2472
|
+
sidebar,
|
|
2473
|
+
liveLabel
|
|
2405
2474
|
}) => {
|
|
2406
2475
|
const { rows } = useWindowSize();
|
|
2407
2476
|
const { exit } = useApp();
|
|
@@ -2504,6 +2573,10 @@ var BrowseScreen = ({
|
|
|
2504
2573
|
const viewStart = viewStarts[activeId] ?? 0;
|
|
2505
2574
|
const visibleCount = windowCount(section.items, viewStart, listHeight);
|
|
2506
2575
|
const visibleItems = section.items.slice(viewStart, viewStart + visibleCount);
|
|
2576
|
+
const markerCols = section.items.reduce(
|
|
2577
|
+
(w, i) => i.kind === "task" ? Math.max(w, [...i.marker ?? ""].length) : w,
|
|
2578
|
+
0
|
|
2579
|
+
);
|
|
2507
2580
|
const hasMore = viewStart + visibleCount < section.items.length;
|
|
2508
2581
|
const activeItem = section.items[cursor];
|
|
2509
2582
|
const showFlash = (msg) => {
|
|
@@ -2525,7 +2598,8 @@ var BrowseScreen = ({
|
|
|
2525
2598
|
const railRows = sidebar?.rows ?? [];
|
|
2526
2599
|
const railActive = showRail && railFocus && railRows.length > 0;
|
|
2527
2600
|
const railAt = Math.min(railCursor, Math.max(0, railRows.length - 1));
|
|
2528
|
-
const
|
|
2601
|
+
const railCols = railWidth(COLS);
|
|
2602
|
+
const listCols = showRail ? COLS - railCols : COLS;
|
|
2529
2603
|
const [sparkFrame, setSparkFrame] = useState(0);
|
|
2530
2604
|
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
|
|
2531
2605
|
// own items, which was right while the animation lived on the rows and is
|
|
@@ -3122,6 +3196,7 @@ var BrowseScreen = ({
|
|
|
3122
3196
|
parent: item.kind === "task" && depthOf(section.items[viewStart + i + 1]) > depthOf(item),
|
|
3123
3197
|
sparkFrame,
|
|
3124
3198
|
cols: listCols,
|
|
3199
|
+
markerCols,
|
|
3125
3200
|
gap: i > 0 && gapsAbove(section.items, viewStart + i)
|
|
3126
3201
|
},
|
|
3127
3202
|
`${viewStart + i}:${rowKey(item)}`
|
|
@@ -3152,6 +3227,8 @@ var BrowseScreen = ({
|
|
|
3152
3227
|
SidePanel,
|
|
3153
3228
|
{
|
|
3154
3229
|
sidebar,
|
|
3230
|
+
liveLabel,
|
|
3231
|
+
width: railCols,
|
|
3155
3232
|
height: listHeight,
|
|
3156
3233
|
focused: railActive,
|
|
3157
3234
|
cursor: railAt
|
|
@@ -3214,7 +3291,8 @@ var App = ({
|
|
|
3214
3291
|
watchJitterMs = 0,
|
|
3215
3292
|
extensions,
|
|
3216
3293
|
tabHelp,
|
|
3217
|
-
emptyHint
|
|
3294
|
+
emptyHint,
|
|
3295
|
+
liveLabel
|
|
3218
3296
|
}) => {
|
|
3219
3297
|
const { rows } = useWindowSize();
|
|
3220
3298
|
const [state, setState] = useState({ phase: "loading" });
|
|
@@ -3340,7 +3418,15 @@ var App = ({
|
|
|
3340
3418
|
}
|
|
3341
3419
|
setSkippedForBudget(false);
|
|
3342
3420
|
if (manual) setRefreshing(true);
|
|
3343
|
-
fetcher().then((
|
|
3421
|
+
fetcher().then((fetched) => {
|
|
3422
|
+
const fresh = {
|
|
3423
|
+
...fetched,
|
|
3424
|
+
sections: reconcile(
|
|
3425
|
+
displayedSections.current,
|
|
3426
|
+
fetched.sections,
|
|
3427
|
+
Date.now()
|
|
3428
|
+
)
|
|
3429
|
+
};
|
|
3344
3430
|
if (fresh.budget) setBudget(fresh.budget);
|
|
3345
3431
|
if (fresh.sidebar !== void 0) setSidebar(fresh.sidebar);
|
|
3346
3432
|
if (cacheKey) writeCache(cacheKey, fresh);
|
|
@@ -3391,6 +3477,7 @@ var App = ({
|
|
|
3391
3477
|
displayedKey.current = signatureOf(cached.sections);
|
|
3392
3478
|
displayedSections.current = cached.sections;
|
|
3393
3479
|
setFetchedAt(cached.at);
|
|
3480
|
+
if (cached.sidebar) setSidebar(cached.sidebar);
|
|
3394
3481
|
setState({
|
|
3395
3482
|
phase: "browse",
|
|
3396
3483
|
sections: cached.sections,
|
|
@@ -3435,6 +3522,7 @@ var App = ({
|
|
|
3435
3522
|
const shared = cacheKey ? readCache(cacheKey) : null;
|
|
3436
3523
|
if (shared && shared.at > firedAt) {
|
|
3437
3524
|
receive({ sections: shared.sections, login: shared.login });
|
|
3525
|
+
if (shared.sidebar) setSidebar(shared.sidebar);
|
|
3438
3526
|
setFetchedAt(shared.at);
|
|
3439
3527
|
return;
|
|
3440
3528
|
}
|
|
@@ -3579,6 +3667,7 @@ var App = ({
|
|
|
3579
3667
|
transients,
|
|
3580
3668
|
onTabChange: setVisibleTab,
|
|
3581
3669
|
sidebar,
|
|
3670
|
+
liveLabel,
|
|
3582
3671
|
ciStatusState: hasCiStatus ? ciStatusState : void 0,
|
|
3583
3672
|
ciJob,
|
|
3584
3673
|
tabHelp,
|
|
@@ -3631,4 +3720,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
3631
3720
|
};
|
|
3632
3721
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3633
3722
|
|
|
3634
|
-
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, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
|
|
3723
|
+
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.55.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",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@kud/gh": "0.16.0",
|
|
52
|
-
"@kud/gh-workflow": "0.
|
|
52
|
+
"@kud/gh-workflow": "0.12.0",
|
|
53
53
|
"@kud/ink-ui": "0.29.0",
|
|
54
54
|
"zx": "8.8.5"
|
|
55
55
|
},
|