@kud/gh-ink 0.33.0 → 0.35.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 +82 -6
- package/dist/index.js +60 -20
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -140,7 +140,48 @@ type GHItem = {
|
|
|
140
140
|
* handing back a `lastActor` that no longer claims a turn.
|
|
141
141
|
*/
|
|
142
142
|
pinned?: boolean;
|
|
143
|
-
|
|
143
|
+
/**
|
|
144
|
+
* What this row STANDS FOR, when that differs from what it is. Absent means
|
|
145
|
+
* the row is a unit of work and is counted as one.
|
|
146
|
+
*
|
|
147
|
+
* `"container"` marks a row that exists to carry context rather than to be
|
|
148
|
+
* done: the initiative a story hangs under, drawn as a real row because it is
|
|
149
|
+
* still selectable, openable, and worth seeing until it closes — but not
|
|
150
|
+
* itself a thing on anyone's plate. Four stories under one container is four
|
|
151
|
+
* items, not five.
|
|
152
|
+
*
|
|
153
|
+
* Deliberately not `uncounted`. A host knows what a row IS; it should not have
|
|
154
|
+
* to know what the tab badge does with that, and a field named after one
|
|
155
|
+
* consumer starts lying the moment a second one reads it.
|
|
156
|
+
*
|
|
157
|
+
* Orthogonal to `depth`, which says where the row hangs rather than what it
|
|
158
|
+
* stands for. A container is a genuine depth-0 row with genuine depth-1
|
|
159
|
+
* children; spelling it as a depth would mean lying about the tree to fix a
|
|
160
|
+
* number, and every site that draws indentation reads that lie as truth.
|
|
161
|
+
*
|
|
162
|
+
* A union with one member rather than a boolean. `indent` was a boolean that
|
|
163
|
+
* turned out to need a scalar, and widening it cost a deprecation that is
|
|
164
|
+
* still in this file. Adding a second role here is additive; turning a boolean
|
|
165
|
+
* into a union is not.
|
|
166
|
+
*/
|
|
167
|
+
role?: "container";
|
|
168
|
+
/**
|
|
169
|
+
* How deep this row hangs in the tree: 0 top level, 1 a child, 2 a
|
|
170
|
+
* grandchild. Absent means 0.
|
|
171
|
+
*
|
|
172
|
+
* Always read it through `depthOf`, never off the row — `indent` is still a
|
|
173
|
+
* legal way to spell "depth 1", and that helper is the only place that knows
|
|
174
|
+
* both spellings. A site that reads `item.indent` directly prices a `depth: 2`
|
|
175
|
+
* row as top level, which is the bug this field exists to end rather than a
|
|
176
|
+
* new one to introduce.
|
|
177
|
+
*/
|
|
178
|
+
depth?: number;
|
|
179
|
+
/**
|
|
180
|
+
* @deprecated Legacy spelling of `depth: 1`. Write `depth`; this is kept so
|
|
181
|
+
* every existing producer still typechecks, and it is read in exactly one
|
|
182
|
+
* place (`depthOf`). Due to be removed at the next major.
|
|
183
|
+
*/
|
|
184
|
+
indent?: boolean;
|
|
144
185
|
};
|
|
145
186
|
type TaskRow = {
|
|
146
187
|
kind: "task";
|
|
@@ -149,7 +190,12 @@ type TaskRow = {
|
|
|
149
190
|
url: string;
|
|
150
191
|
status: string;
|
|
151
192
|
age: string;
|
|
152
|
-
|
|
193
|
+
/** What this row stands for. See `GHItem.role`. */
|
|
194
|
+
role?: "container";
|
|
195
|
+
/** How deep this row hangs. See `GHItem.depth`; read it through `depthOf`. */
|
|
196
|
+
depth?: number;
|
|
197
|
+
/** @deprecated Legacy spelling of `depth: 1`. See `GHItem.indent`. */
|
|
198
|
+
indent?: boolean;
|
|
153
199
|
instanceKey?: string;
|
|
154
200
|
/**
|
|
155
201
|
* Trailing annotation, rendered dim after the summary — a recurrence marker,
|
|
@@ -176,23 +222,35 @@ type RepoHeader = {
|
|
|
176
222
|
kind: "repo-header";
|
|
177
223
|
repo: string;
|
|
178
224
|
age: string;
|
|
179
|
-
|
|
225
|
+
/** How deep this row hangs. See `GHItem.depth`; read it through `depthOf`. */
|
|
226
|
+
depth?: number;
|
|
227
|
+
/** @deprecated Legacy spelling of `depth: 1`. See `GHItem.indent`. */
|
|
228
|
+
indent?: boolean;
|
|
180
229
|
};
|
|
181
230
|
type ShowMore = {
|
|
182
231
|
kind: "show-more";
|
|
183
232
|
hidden: GHItem[];
|
|
184
|
-
|
|
233
|
+
/** How deep this row hangs. See `GHItem.depth`; read it through `depthOf`. */
|
|
234
|
+
depth?: number;
|
|
235
|
+
/** @deprecated Legacy spelling of `depth: 1`. See `GHItem.indent`. */
|
|
236
|
+
indent?: boolean;
|
|
185
237
|
};
|
|
186
238
|
type ShowLess = {
|
|
187
239
|
kind: "show-less";
|
|
188
240
|
toHide: GHItem[];
|
|
189
|
-
|
|
241
|
+
/** How deep this row hangs. See `GHItem.depth`; read it through `depthOf`. */
|
|
242
|
+
depth?: number;
|
|
243
|
+
/** @deprecated Legacy spelling of `depth: 1`. See `GHItem.indent`. */
|
|
244
|
+
indent?: boolean;
|
|
190
245
|
};
|
|
191
246
|
type SubgroupHeader = {
|
|
192
247
|
kind: "subgroup-header";
|
|
193
248
|
label: string;
|
|
194
249
|
age: string;
|
|
195
|
-
|
|
250
|
+
/** How deep this row hangs. See `GHItem.depth`; read it through `depthOf`. */
|
|
251
|
+
depth?: number;
|
|
252
|
+
/** @deprecated Legacy spelling of `depth: 1`. See `GHItem.indent`. */
|
|
253
|
+
indent?: boolean;
|
|
196
254
|
};
|
|
197
255
|
type AnyItem = GHItem | TaskRow | RepoHeader | SubgroupHeader | ShowMore | ShowLess;
|
|
198
256
|
type CiStatus = {
|
|
@@ -344,6 +402,24 @@ declare const openInTab: (cmd: string) => Promise<void>;
|
|
|
344
402
|
declare const runInPaneHorizontal: (cmd: string) => Promise<void>;
|
|
345
403
|
declare const runHere: (cmd: string) => void;
|
|
346
404
|
declare const COLS: number;
|
|
405
|
+
/**
|
|
406
|
+
* What the tab badge and the header total both count: rows that are somebody's
|
|
407
|
+
* work, at the top level.
|
|
408
|
+
*
|
|
409
|
+
* The exclusions are two different kinds of thing. Headers and the
|
|
410
|
+
* show-more/show-less affordances are furniture — not entities at all, which is
|
|
411
|
+
* why the search and repo filters drop them too. This function used to exclude
|
|
412
|
+
* only the two header kinds while its three siblings excluded all four, so a
|
|
413
|
+
* collapsed tail sitting at the top level counted as an item.
|
|
414
|
+
* `role: "container"` rows ARE entities, selectable and openable; they simply
|
|
415
|
+
* are not work. An epic is the case that forced the distinction: real, owned,
|
|
416
|
+
* and on screen until it closes, but the work is the stories under it, and
|
|
417
|
+
* counting it as well reported five things to do where there were four.
|
|
418
|
+
*
|
|
419
|
+
* One function, both consumers, on purpose. The badge and the whole-board total
|
|
420
|
+
* are the same claim at two scales; computed apart they drift, and a header that
|
|
421
|
+
* disagrees with the sum of its own tabs is worse than either number alone.
|
|
422
|
+
*/
|
|
347
423
|
declare const topLevelCount: (s: Section) => number;
|
|
348
424
|
declare const drillCmd: (item: AnyItem) => string | null;
|
|
349
425
|
declare const explainGhAction: (e: unknown) => string;
|
package/dist/index.js
CHANGED
|
@@ -780,6 +780,11 @@ var tabsOfMarks = (sections, marks) => {
|
|
|
780
780
|
return out;
|
|
781
781
|
};
|
|
782
782
|
var quietly = $({ quiet: true });
|
|
783
|
+
var depthOf = (item) => {
|
|
784
|
+
if (!item) return 0;
|
|
785
|
+
if (typeof item.depth === "number") return Math.max(0, Math.floor(item.depth));
|
|
786
|
+
return item.indent === true ? 1 : 0;
|
|
787
|
+
};
|
|
783
788
|
var relativeTime = (iso) => {
|
|
784
789
|
const diff = (Date.now() - new Date(iso).getTime()) / 1e3;
|
|
785
790
|
if (diff < 3600) return `${Math.floor(diff / 60)}m`;
|
|
@@ -902,7 +907,7 @@ var insertRepoHeaders = (items) => {
|
|
|
902
907
|
const result = [];
|
|
903
908
|
let lastRepo = "";
|
|
904
909
|
for (const item of items) {
|
|
905
|
-
if (
|
|
910
|
+
if (depthOf(item) === 0 && item.repo !== lastRepo) {
|
|
906
911
|
lastRepo = item.repo;
|
|
907
912
|
result.push({
|
|
908
913
|
kind: "repo-header",
|
|
@@ -1077,7 +1082,30 @@ var budgetNotice = (budget, now = Date.now()) => {
|
|
|
1077
1082
|
critical: false
|
|
1078
1083
|
};
|
|
1079
1084
|
};
|
|
1080
|
-
var
|
|
1085
|
+
var isLastAtDepth = (items, i) => {
|
|
1086
|
+
const depth = depthOf(items[i]);
|
|
1087
|
+
for (let j = i + 1; j < items.length; j += 1) {
|
|
1088
|
+
const d = depthOf(items[j]);
|
|
1089
|
+
if (d > depth) continue;
|
|
1090
|
+
return d < depth;
|
|
1091
|
+
}
|
|
1092
|
+
return true;
|
|
1093
|
+
};
|
|
1094
|
+
var treePrefix = (items, i) => {
|
|
1095
|
+
const depth = depthOf(items[i]);
|
|
1096
|
+
if (depth === 0) return "";
|
|
1097
|
+
const ancestorLast = [];
|
|
1098
|
+
let level = depth - 1;
|
|
1099
|
+
for (let j = i - 1; j >= 0 && level >= 1; j -= 1) {
|
|
1100
|
+
if (depthOf(items[j]) === level) {
|
|
1101
|
+
ancestorLast[level] = isLastAtDepth(items, j);
|
|
1102
|
+
level -= 1;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
let prefix = "";
|
|
1106
|
+
for (let k = 1; k < depth; k += 1) prefix += ancestorLast[k] ? " " : "\u2502 ";
|
|
1107
|
+
return prefix + (isLastAtDepth(items, i) ? "\u2514\u2500 " : "\u251C\u2500 ");
|
|
1108
|
+
};
|
|
1081
1109
|
var groupUrls = (items, i) => {
|
|
1082
1110
|
const urls = [];
|
|
1083
1111
|
for (let j = i + 1; j < items.length; j += 1) {
|
|
@@ -1095,11 +1123,13 @@ var groupUrls = (items, i) => {
|
|
|
1095
1123
|
var treeUrls = (items, i) => {
|
|
1096
1124
|
if (items[i]?.kind === "repo-header") return groupUrls(items, i);
|
|
1097
1125
|
let root = i;
|
|
1098
|
-
while (root > 0 &&
|
|
1126
|
+
while (root > 0 && depthOf(items[root]) > 0 && items[root]?.kind !== "task")
|
|
1127
|
+
root -= 1;
|
|
1128
|
+
const rootDepth = depthOf(items[root]);
|
|
1099
1129
|
const urls = [];
|
|
1100
1130
|
for (let j = root; j < items.length; j += 1) {
|
|
1101
1131
|
const item = items[j];
|
|
1102
|
-
if (j > root &&
|
|
1132
|
+
if (j > root && depthOf(item) <= rootDepth) break;
|
|
1103
1133
|
if (item.kind === "show-more") {
|
|
1104
1134
|
for (const child of item.hidden) if (child.url) urls.push(child.url);
|
|
1105
1135
|
continue;
|
|
@@ -1115,7 +1145,8 @@ var gapsAbove = (items, i) => {
|
|
|
1115
1145
|
if (item.kind === "repo-header" || item.kind === "subgroup-header")
|
|
1116
1146
|
return true;
|
|
1117
1147
|
if (item.kind !== "task") return false;
|
|
1118
|
-
|
|
1148
|
+
if (depthOf(item) > 0) return false;
|
|
1149
|
+
return depthOf(items[i + 1]) > 0 || depthOf(items[i - 1]) > 0;
|
|
1119
1150
|
};
|
|
1120
1151
|
var fitCount = (items, start, budget) => {
|
|
1121
1152
|
let lines = 0;
|
|
@@ -1360,7 +1391,7 @@ var Backdrop = ({
|
|
|
1360
1391
|
}
|
|
1361
1392
|
) : children });
|
|
1362
1393
|
var topLevelCount = (s) => s.items.filter(
|
|
1363
|
-
(i) => i.kind !== "repo-header" && i.kind !== "subgroup-header" && !i.
|
|
1394
|
+
(i) => i.kind !== "repo-header" && i.kind !== "subgroup-header" && i.kind !== "show-more" && i.kind !== "show-less" && !("role" in i && i.role === "container") && depthOf(i) === 0
|
|
1364
1395
|
).length;
|
|
1365
1396
|
var drillCmd = (item) => {
|
|
1366
1397
|
if (item.kind === "task")
|
|
@@ -1608,7 +1639,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1608
1639
|
}
|
|
1609
1640
|
});
|
|
1610
1641
|
if (jiraBase && jiraKeyRe) {
|
|
1611
|
-
const jiraKey =
|
|
1642
|
+
const jiraKey = depthOf(item) === 0 ? item.title.match(jiraKeyRe)?.[0] : null;
|
|
1612
1643
|
if (jiraKey) {
|
|
1613
1644
|
actions.push({
|
|
1614
1645
|
label: `Open ${jiraKey} in Jira`,
|
|
@@ -1776,13 +1807,14 @@ var CiStatusLine = ({
|
|
|
1776
1807
|
status.age ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + status.age }) : null
|
|
1777
1808
|
] });
|
|
1778
1809
|
};
|
|
1810
|
+
var FENCE_COLS = 46;
|
|
1779
1811
|
var RepoHeaderRow = ({
|
|
1780
1812
|
repo,
|
|
1781
1813
|
gap,
|
|
1782
1814
|
active
|
|
1783
1815
|
}) => {
|
|
1784
1816
|
const label = `\u2500\u2500 ${repo} `;
|
|
1785
|
-
const fill = Math.max(4,
|
|
1817
|
+
const fill = Math.max(4, FENCE_COLS - label.length);
|
|
1786
1818
|
const rule = "\u2500".repeat(fill);
|
|
1787
1819
|
return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
|
|
1788
1820
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
@@ -1821,7 +1853,7 @@ var ItemRow = ({
|
|
|
1821
1853
|
merged,
|
|
1822
1854
|
transient: refreshMark,
|
|
1823
1855
|
leaving,
|
|
1824
|
-
|
|
1856
|
+
prefix = "",
|
|
1825
1857
|
parent,
|
|
1826
1858
|
sparkFrame = 0
|
|
1827
1859
|
}) => {
|
|
@@ -1836,14 +1868,14 @@ var ItemRow = ({
|
|
|
1836
1868
|
if (item.kind === "show-more")
|
|
1837
1869
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1838
1870
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
1839
|
-
/* @__PURE__ */ jsx(Text2, { dimColor: true, children:
|
|
1871
|
+
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
|
|
1840
1872
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: active ? "\u21B5 " : " " }),
|
|
1841
1873
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: `+${item.hidden.length} more` })
|
|
1842
1874
|
] });
|
|
1843
1875
|
if (item.kind === "show-less")
|
|
1844
1876
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1845
1877
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
1846
|
-
/* @__PURE__ */ jsx(Text2, { dimColor: true, children:
|
|
1878
|
+
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
|
|
1847
1879
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: active ? "\u21B5 " : " " }),
|
|
1848
1880
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: "show less" })
|
|
1849
1881
|
] });
|
|
@@ -1853,10 +1885,11 @@ var ItemRow = ({
|
|
|
1853
1885
|
const transitLabel2 = transient ? TRANSIT_LABEL[transient] : "";
|
|
1854
1886
|
const titleMax2 = Math.max(
|
|
1855
1887
|
20,
|
|
1856
|
-
COLS - item.key.length - note.length - transitLabel2.length - 12
|
|
1888
|
+
COLS - item.key.length - note.length - transitLabel2.length - prefix.length - 12
|
|
1857
1889
|
);
|
|
1858
1890
|
return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
|
|
1859
1891
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
1892
|
+
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
|
|
1860
1893
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: parent ? "\u252C " : " " }),
|
|
1861
1894
|
/* @__PURE__ */ jsx(Text2, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
|
|
1862
1895
|
/* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: active, children: item.key + " " }),
|
|
@@ -1891,12 +1924,12 @@ var ItemRow = ({
|
|
|
1891
1924
|
mergedLabel,
|
|
1892
1925
|
transitLabel
|
|
1893
1926
|
].filter(Boolean).join(" ");
|
|
1894
|
-
const repoLabel = item
|
|
1895
|
-
const fixedWidth = 2 +
|
|
1927
|
+
const repoLabel = depthOf(item) > 0 ? item.repo : "";
|
|
1928
|
+
const fixedWidth = 2 + prefix.length + 2 + 2 + 7 + repoLabel.length + suffix.length + 6;
|
|
1896
1929
|
const titleMax = Math.max(20, COLS - fixedWidth);
|
|
1897
1930
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
1898
1931
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
1899
|
-
|
|
1932
|
+
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
|
|
1900
1933
|
/* @__PURE__ */ jsx(Text2, { color, bold: true, children: icon + " " }),
|
|
1901
1934
|
/* @__PURE__ */ jsx(Text2, { color: turnColor, bold: turnIcon === "\u2190", children: turnIcon + " " }),
|
|
1902
1935
|
/* @__PURE__ */ jsx(Text2, { color: "#FF8700", children: numStr }),
|
|
@@ -2545,7 +2578,12 @@ var BrowseScreen = ({
|
|
|
2545
2578
|
{
|
|
2546
2579
|
kind: "show-less",
|
|
2547
2580
|
toHide: activeItem.hidden,
|
|
2548
|
-
|
|
2581
|
+
// The depth of the row it replaces, never a constant. A
|
|
2582
|
+
// collapsed row sitting under a story is at depth 2, and
|
|
2583
|
+
// pinning it to 1 makes the walk in `treeUrls` stop one
|
|
2584
|
+
// level early and drop every row it holds — silently, since
|
|
2585
|
+
// nothing on screen reports what the clipboard missed.
|
|
2586
|
+
depth: depthOf(activeItem)
|
|
2549
2587
|
}
|
|
2550
2588
|
] : [i]
|
|
2551
2589
|
)
|
|
@@ -2562,7 +2600,9 @@ var BrowseScreen = ({
|
|
|
2562
2600
|
{
|
|
2563
2601
|
kind: "show-more",
|
|
2564
2602
|
hidden: activeItem.toHide,
|
|
2565
|
-
|
|
2603
|
+
// Same reasoning as the show-less above: carry the depth
|
|
2604
|
+
// of the row being replaced, not a constant.
|
|
2605
|
+
depth: depthOf(activeItem)
|
|
2566
2606
|
}
|
|
2567
2607
|
] : [i]
|
|
2568
2608
|
)
|
|
@@ -2814,8 +2854,8 @@ var BrowseScreen = ({
|
|
|
2814
2854
|
merged: (item.kind === "pr" || item.kind === "issue") && !!mergedUrls?.includes(item.url),
|
|
2815
2855
|
leaving: (item.kind === "pr" || item.kind === "issue") && !!leavingUrls?.includes(item.url),
|
|
2816
2856
|
transient: transientOf(transients, item),
|
|
2817
|
-
|
|
2818
|
-
parent: item.kind === "task" &&
|
|
2857
|
+
prefix: treePrefix(section.items, viewStart + i),
|
|
2858
|
+
parent: item.kind === "task" && depthOf(section.items[viewStart + i + 1]) > depthOf(item),
|
|
2819
2859
|
sparkFrame,
|
|
2820
2860
|
gap: i > 0 && gapsAbove(section.items, viewStart + i)
|
|
2821
2861
|
},
|
|
@@ -3023,7 +3063,7 @@ var App = ({
|
|
|
3023
3063
|
receive(fresh);
|
|
3024
3064
|
}).catch((err) => {
|
|
3025
3065
|
setRefreshing(false);
|
|
3026
|
-
const message = err
|
|
3066
|
+
const message = explainGhAction(err);
|
|
3027
3067
|
if (!displayedKey.current) {
|
|
3028
3068
|
setState({ phase: "failed", message });
|
|
3029
3069
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.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",
|