@kud/gh-ink 0.35.0 → 0.37.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 +15 -1
- package/dist/index.js +87 -30
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import { PrComments, PrHealthData, PrCheck, Health } from '@kud/gh';
|
|
3
|
-
import { StyledLine } from '@kud/ink-ui';
|
|
3
|
+
import { StyledLine, PillVariant } from '@kud/ink-ui';
|
|
4
4
|
|
|
5
5
|
type CommentsPanelProps = {
|
|
6
6
|
repo: string;
|
|
@@ -204,6 +204,20 @@ type TaskRow = {
|
|
|
204
204
|
* separately instead of being smuggled past the truncation maths.
|
|
205
205
|
*/
|
|
206
206
|
note?: string;
|
|
207
|
+
/**
|
|
208
|
+
* A category this row BELONGS to — `epic`, `blocked`, `spike` — drawn as a
|
|
209
|
+
* filled pill after the summary.
|
|
210
|
+
*
|
|
211
|
+
* Deliberately not a second spelling of `note`, which the epic marker used to
|
|
212
|
+
* borrow. The two want opposite weights: a pill says the word itself is the
|
|
213
|
+
* information, while `note` is for a reference the reader follows — a parent
|
|
214
|
+
* ticket key, a source. Filling a breadcrumb gives it a weight it has not
|
|
215
|
+
* earned, and a row can legitimately carry both (a story under someone else's
|
|
216
|
+
* epic shows its parent's key AND, one day, a status of its own).
|
|
217
|
+
*/
|
|
218
|
+
pill?: string;
|
|
219
|
+
/** Which fill the pill takes. See `@kud/ink-ui`'s `PillVariant`. */
|
|
220
|
+
pillVariant?: PillVariant;
|
|
207
221
|
/**
|
|
208
222
|
* The Jira issue key behind this row, when one exists. Its PRESENCE is what
|
|
209
223
|
* turns on the ticket affordances — ↵ opens a menu led by `jira issue view`,
|
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 } from 'ink';
|
|
3
|
-
import { colors, ScrollView, TextInput, LoadingScreen, StatusMessage, FooterHints, useListCursor, Tabs } from '@kud/ink-ui';
|
|
3
|
+
import { colors, ScrollView, TextInput, LoadingScreen, 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';
|
|
@@ -676,6 +676,9 @@ var invalidateCache = (key) => {
|
|
|
676
676
|
};
|
|
677
677
|
|
|
678
678
|
// src/inbox/diff.ts
|
|
679
|
+
var MARK_SEP = "\0";
|
|
680
|
+
var markKey = (sectionId, rowKey) => `${sectionId}${MARK_SEP}${rowKey}`;
|
|
681
|
+
var rowKeyOfMark = (mark) => mark.slice(mark.indexOf(MARK_SEP) + 1);
|
|
679
682
|
var keyOf = (item) => {
|
|
680
683
|
if (item.kind === "pr" || item.kind === "issue") return item.url;
|
|
681
684
|
if (item.kind === "task") {
|
|
@@ -707,11 +710,27 @@ var indexRows = (sections) => {
|
|
|
707
710
|
for (const section of sections) {
|
|
708
711
|
section.items.forEach((item, index) => {
|
|
709
712
|
const key = keyOf(item);
|
|
710
|
-
if (key)
|
|
713
|
+
if (key)
|
|
714
|
+
out.set(markKey(section.id, key), {
|
|
715
|
+
item,
|
|
716
|
+
sectionId: section.id,
|
|
717
|
+
index
|
|
718
|
+
});
|
|
711
719
|
});
|
|
712
720
|
}
|
|
713
721
|
return out;
|
|
714
722
|
};
|
|
723
|
+
var rowsOf = (index) => {
|
|
724
|
+
const out = /* @__PURE__ */ new Map();
|
|
725
|
+
for (const [mark, placed] of index) {
|
|
726
|
+
const key = rowKeyOfMark(mark);
|
|
727
|
+
const seen = out.get(key);
|
|
728
|
+
if (seen) seen.add(placed.sectionId);
|
|
729
|
+
else out.set(key, /* @__PURE__ */ new Set([placed.sectionId]));
|
|
730
|
+
}
|
|
731
|
+
return out;
|
|
732
|
+
};
|
|
733
|
+
var sameSections = (a, b) => a.size === b.size && [...a].every((id) => b.has(id));
|
|
715
734
|
var spliceBack = (next, leaving) => {
|
|
716
735
|
if (leaving.length === 0) return next;
|
|
717
736
|
const bySection = /* @__PURE__ */ new Map();
|
|
@@ -732,30 +751,43 @@ var spliceBack = (next, leaving) => {
|
|
|
732
751
|
var diffSections = (prev, next) => {
|
|
733
752
|
const before = indexRows(prev);
|
|
734
753
|
const after = indexRows(next);
|
|
754
|
+
const boardBefore = rowsOf(before);
|
|
755
|
+
const boardAfter = rowsOf(after);
|
|
735
756
|
const transients = /* @__PURE__ */ new Map();
|
|
736
757
|
const leaving = [];
|
|
737
|
-
for (const [
|
|
738
|
-
|
|
739
|
-
|
|
758
|
+
for (const [mark] of after)
|
|
759
|
+
if (!before.has(mark))
|
|
760
|
+
transients.set(
|
|
761
|
+
mark,
|
|
762
|
+
boardBefore.has(rowKeyOfMark(mark)) ? "moved-in" : "in"
|
|
763
|
+
);
|
|
764
|
+
for (const [mark, was] of before) {
|
|
765
|
+
const now = after.get(mark);
|
|
740
766
|
if (!now) {
|
|
741
|
-
transients.set(
|
|
767
|
+
transients.set(
|
|
768
|
+
mark,
|
|
769
|
+
boardAfter.has(rowKeyOfMark(mark)) ? "moved-out" : "out"
|
|
770
|
+
);
|
|
742
771
|
leaving.push(was);
|
|
743
772
|
} else if (renderedState(was.item) !== renderedState(now.item)) {
|
|
744
|
-
transients.set(
|
|
773
|
+
transients.set(mark, "changed");
|
|
745
774
|
}
|
|
746
775
|
}
|
|
747
776
|
let added = 0;
|
|
748
777
|
let removed = 0;
|
|
749
|
-
|
|
750
|
-
for (const [, kind] of transients)
|
|
751
|
-
if (kind === "
|
|
752
|
-
|
|
753
|
-
|
|
778
|
+
const changedRows = /* @__PURE__ */ new Set();
|
|
779
|
+
for (const [mark, kind] of transients)
|
|
780
|
+
if (kind === "changed") changedRows.add(rowKeyOfMark(mark));
|
|
781
|
+
for (const [key, sections] of boardAfter) {
|
|
782
|
+
const had = boardBefore.get(key);
|
|
783
|
+
if (!had) added += 1;
|
|
784
|
+
else if (!sameSections(had, sections)) changedRows.add(key);
|
|
754
785
|
}
|
|
786
|
+
for (const key of boardBefore.keys()) if (!boardAfter.has(key)) removed += 1;
|
|
755
787
|
return {
|
|
756
788
|
transients,
|
|
757
789
|
union: spliceBack(next, leaving),
|
|
758
|
-
counts: { added, removed, changed }
|
|
790
|
+
counts: { added, removed, changed: changedRows.size }
|
|
759
791
|
};
|
|
760
792
|
};
|
|
761
793
|
var summariseDiff = (counts) => {
|
|
@@ -765,17 +797,19 @@ var summariseDiff = (counts) => {
|
|
|
765
797
|
if (counts.changed) parts.push(`${counts.changed} moved`);
|
|
766
798
|
return parts.join(" \xB7 ");
|
|
767
799
|
};
|
|
768
|
-
var transientOf = (transients, item) => {
|
|
800
|
+
var transientOf = (transients, item, sectionId) => {
|
|
769
801
|
if (!transients?.size) return void 0;
|
|
770
802
|
const key = keyOf(item);
|
|
771
|
-
return key ? transients.get(key) : void 0;
|
|
803
|
+
return key ? transients.get(markKey(sectionId, key)) : void 0;
|
|
772
804
|
};
|
|
773
805
|
var tabsOfMarks = (sections, marks) => {
|
|
774
806
|
const out = /* @__PURE__ */ new Map();
|
|
775
807
|
for (const section of sections)
|
|
776
808
|
for (const item of section.items) {
|
|
777
809
|
const key = keyOf(item);
|
|
778
|
-
if (key
|
|
810
|
+
if (!key) continue;
|
|
811
|
+
const mark = markKey(section.id, key);
|
|
812
|
+
if (marks.has(mark)) out.set(mark, section.id);
|
|
779
813
|
}
|
|
780
814
|
return out;
|
|
781
815
|
};
|
|
@@ -1835,13 +1869,26 @@ var TRANSIT_IN_FRAMES = ["\xB7", "\u25CB", "\u25CE", "\u25C9"];
|
|
|
1835
1869
|
var TRANSIT_COLOUR = {
|
|
1836
1870
|
in: "#3FB950",
|
|
1837
1871
|
out: "#8B949E",
|
|
1838
|
-
changed: "#FF8700"
|
|
1872
|
+
changed: "#FF8700",
|
|
1873
|
+
// A move is neither an arrival nor a departure, so it takes the colour of the
|
|
1874
|
+
// third thing that can happen to a row you already had: it changed. Both ends
|
|
1875
|
+
// of one move wear the same colour and the same word, which is what lets you
|
|
1876
|
+
// recognise the row you just watched leave when you land on the tab it went to.
|
|
1877
|
+
"moved-in": "#FF8700",
|
|
1878
|
+
"moved-out": "#FF8700"
|
|
1839
1879
|
};
|
|
1840
1880
|
var TRANSIT_LABEL = {
|
|
1841
1881
|
in: "NEW",
|
|
1842
1882
|
out: "GONE",
|
|
1843
|
-
changed: "UPDATED"
|
|
1844
|
-
|
|
1883
|
+
changed: "UPDATED",
|
|
1884
|
+
// Never GONE. The row is still on the board, one tab over, and telling you it
|
|
1885
|
+
// has gone is the marker lying about work you still own — which was the whole
|
|
1886
|
+
// failure: an epic moving tab said nothing at all and simply vanished.
|
|
1887
|
+
"moved-in": "MOVED",
|
|
1888
|
+
"moved-out": "MOVED"
|
|
1889
|
+
};
|
|
1890
|
+
var isDeparture = (t) => t === "out" || t === "moved-out";
|
|
1891
|
+
var isArrival = (t) => t === "in" || t === "moved-in";
|
|
1845
1892
|
var LEAVING_HOLD_MS = 2500;
|
|
1846
1893
|
var TAB_MARK = "\u25CF";
|
|
1847
1894
|
var tabLabel = (label, marked, id) => marked.size === 0 ? label : `${marked.has(id) ? TAB_MARK : " "} ${label}`;
|
|
@@ -1881,11 +1928,11 @@ var ItemRow = ({
|
|
|
1881
1928
|
] });
|
|
1882
1929
|
if (item.kind === "task") {
|
|
1883
1930
|
const note = item.note ?? "";
|
|
1884
|
-
const transitIcon = !transient ? " " : transient
|
|
1931
|
+
const transitIcon = !transient ? " " : isDeparture(transient) ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : isArrival(transient) ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : "\u25C9";
|
|
1885
1932
|
const transitLabel2 = transient ? TRANSIT_LABEL[transient] : "";
|
|
1886
1933
|
const titleMax2 = Math.max(
|
|
1887
1934
|
20,
|
|
1888
|
-
COLS - item.key.length - note.length - transitLabel2.length - prefix.length - 12
|
|
1935
|
+
COLS - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - transitLabel2.length - prefix.length - 12
|
|
1889
1936
|
);
|
|
1890
1937
|
return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
|
|
1891
1938
|
/* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
|
|
@@ -1896,18 +1943,22 @@ var ItemRow = ({
|
|
|
1896
1943
|
/* @__PURE__ */ jsx(
|
|
1897
1944
|
Text2,
|
|
1898
1945
|
{
|
|
1899
|
-
bold: active || transient
|
|
1900
|
-
dimColor: transient
|
|
1946
|
+
bold: active || !!transient && isArrival(transient),
|
|
1947
|
+
dimColor: !!transient && isDeparture(transient),
|
|
1901
1948
|
strikethrough: transient === "out",
|
|
1902
1949
|
children: truncate(item.summary, titleMax2)
|
|
1903
1950
|
}
|
|
1904
1951
|
),
|
|
1952
|
+
item.pill ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1953
|
+
/* @__PURE__ */ jsx(Text2, { children: " " }),
|
|
1954
|
+
/* @__PURE__ */ jsx(Pill, { variant: item.pillVariant ?? "muted", children: item.pill })
|
|
1955
|
+
] }) : null,
|
|
1905
1956
|
note ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${note}` }) : null,
|
|
1906
1957
|
transitLabel2 ? /* @__PURE__ */ jsx(Text2, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel2 }) : null
|
|
1907
1958
|
] });
|
|
1908
1959
|
}
|
|
1909
1960
|
const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
|
|
1910
|
-
const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient
|
|
1961
|
+
const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient && isDeparture(transient) ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : transient && isArrival(transient) ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : healthIcon;
|
|
1911
1962
|
const color = merged ? MERGED_COLOUR : transient ? TRANSIT_COLOUR[transient] : healthColor2;
|
|
1912
1963
|
const spokeLast = !!login && !!item.lastActor && item.lastActor === login;
|
|
1913
1964
|
const [turnIcon, turnColor] = item.pinned ? [PIN_MARK, "#FF8700"] : !login || !item.lastActor ? [" ", "white"] : spokeLast ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
|
|
@@ -1936,8 +1987,8 @@ var ItemRow = ({
|
|
|
1936
1987
|
/* @__PURE__ */ jsx(
|
|
1937
1988
|
Text2,
|
|
1938
1989
|
{
|
|
1939
|
-
bold: active || transient
|
|
1940
|
-
dimColor: transient
|
|
1990
|
+
bold: active || !!transient && isArrival(transient),
|
|
1991
|
+
dimColor: !!transient && isDeparture(transient),
|
|
1941
1992
|
strikethrough: transient === "out",
|
|
1942
1993
|
children: truncate(item.title, titleMax) + " "
|
|
1943
1994
|
}
|
|
@@ -2379,11 +2430,12 @@ var BrowseScreen = ({
|
|
|
2379
2430
|
const out = /* @__PURE__ */ new Set();
|
|
2380
2431
|
if (!transients?.size) return out;
|
|
2381
2432
|
for (const s of localSections)
|
|
2382
|
-
if (s.items.some((item) => transientOf(transients, item
|
|
2433
|
+
if (s.items.some((item) => transientOf(transients, item, s.id)))
|
|
2434
|
+
out.add(s.id);
|
|
2383
2435
|
return out;
|
|
2384
2436
|
}, [localSections, transients]);
|
|
2385
2437
|
const [sparkFrame, setSparkFrame] = useState(0);
|
|
2386
|
-
const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item));
|
|
2438
|
+
const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
|
|
2387
2439
|
useEffect(() => {
|
|
2388
2440
|
if (!sparkling) return;
|
|
2389
2441
|
const id = setInterval(() => setSparkFrame((f) => f + 1), MERGED_FRAME_MS);
|
|
@@ -2853,7 +2905,7 @@ var BrowseScreen = ({
|
|
|
2853
2905
|
login,
|
|
2854
2906
|
merged: (item.kind === "pr" || item.kind === "issue") && !!mergedUrls?.includes(item.url),
|
|
2855
2907
|
leaving: (item.kind === "pr" || item.kind === "issue") && !!leavingUrls?.includes(item.url),
|
|
2856
|
-
transient: transientOf(transients, item),
|
|
2908
|
+
transient: transientOf(transients, item, section.id),
|
|
2857
2909
|
prefix: treePrefix(section.items, viewStart + i),
|
|
2858
2910
|
parent: item.kind === "task" && depthOf(section.items[viewStart + i + 1]) > depthOf(item),
|
|
2859
2911
|
sparkFrame,
|
|
@@ -2986,7 +3038,12 @@ var App = ({
|
|
|
2986
3038
|
const settleTab = (tabId, marks) => {
|
|
2987
3039
|
const keys = [...transitTabs.current].filter(([, tab]) => tab === tabId).map(([key]) => key);
|
|
2988
3040
|
for (const key of keys) transitTabs.current.delete(key);
|
|
2989
|
-
const gone = new Set(
|
|
3041
|
+
const gone = new Set(
|
|
3042
|
+
keys.filter((key) => {
|
|
3043
|
+
const mark = marks.get(key);
|
|
3044
|
+
return !!mark && isDeparture(mark);
|
|
3045
|
+
}).map(rowKeyOfMark)
|
|
3046
|
+
);
|
|
2990
3047
|
setTransients((prev) => {
|
|
2991
3048
|
const next = new Map(prev);
|
|
2992
3049
|
for (const key of keys) next.delete(key);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.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.9.0",
|
|
52
|
-
"@kud/ink-ui": "0.
|
|
52
|
+
"@kud/ink-ui": "0.17.0",
|
|
53
53
|
"zx": "8.8.5"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|