@kud/gh-ink 0.3.0 → 0.4.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 +7 -2
- package/dist/index.js +73 -50
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -33,11 +33,16 @@ declare const healthGlyph: (h: Health) => string;
|
|
|
33
33
|
declare const healthColor: (h: Health) => string;
|
|
34
34
|
declare const healthLegend: [Health, string][];
|
|
35
35
|
|
|
36
|
+
type ExtensionTarget = {
|
|
37
|
+
item?: AnyItem;
|
|
38
|
+
ciJob?: string;
|
|
39
|
+
login: string;
|
|
40
|
+
};
|
|
36
41
|
interface InboxExtension {
|
|
37
42
|
id: string;
|
|
38
43
|
title: string;
|
|
39
44
|
key: string;
|
|
40
|
-
body: (onExit: () => void, target?:
|
|
45
|
+
body: (onExit: () => void, target?: ExtensionTarget) => ReactNode;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
type GHDetail = {
|
|
@@ -233,4 +238,4 @@ declare const writeCache: (key: string, data: {
|
|
|
233
238
|
login: string;
|
|
234
239
|
}) => void;
|
|
235
240
|
|
|
236
|
-
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxExtension, type JiraRow, type JiraTransition, type RepoHeader, type Section, type ShowLess, type ShowMore, type SubgroupHeader, buildActions, buildCheckoutCmd, clipboard, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, healthColor, healthDisplay, healthGlyph, healthLegend, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, maxViewStart, moveCursor, openInTab, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, windowCount, withHeaders, withoutItem, writeCache };
|
|
241
|
+
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxExtension, type JiraRow, type JiraTransition, type RepoHeader, type Section, type ShowLess, type ShowMore, type SubgroupHeader, buildActions, buildCheckoutCmd, clipboard, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, healthColor, healthDisplay, healthGlyph, healthLegend, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, maxViewStart, moveCursor, openInTab, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, windowCount, withHeaders, withoutItem, writeCache };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React2, { useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { useWindowSize, useInput, Text, Box } from 'ink';
|
|
3
|
-
import { colors, ScrollView, TextInput, LoadingScreen, Switch, Tabs, FooterHints } from '@kud/ink-ui';
|
|
3
|
+
import { colors, ScrollView, TextInput, LoadingScreen, Switch, useListCursor, Tabs, FooterHints } from '@kud/ink-ui';
|
|
4
4
|
import { isPassCheck, isFailCheck, resolveThread, unresolveThread, replyToThread, rerunFailedRun, mergePr, reRequestReviewer } from '@kud/gh';
|
|
5
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
6
|
import { $ } from 'zx';
|
|
@@ -622,6 +622,7 @@ var writeCache = (key, data) => {
|
|
|
622
622
|
} catch {
|
|
623
623
|
}
|
|
624
624
|
};
|
|
625
|
+
var quietly = $({ quiet: true });
|
|
625
626
|
var relativeTime = (iso) => {
|
|
626
627
|
const diff = (Date.now() - new Date(iso).getTime()) / 1e3;
|
|
627
628
|
if (diff < 3600) return `${Math.floor(diff / 60)}m`;
|
|
@@ -847,6 +848,12 @@ var maxViewStart = (items, budget) => {
|
|
|
847
848
|
start++;
|
|
848
849
|
return start;
|
|
849
850
|
};
|
|
851
|
+
var firstSelectable = (section) => Math.max(
|
|
852
|
+
0,
|
|
853
|
+
section.items.findIndex(
|
|
854
|
+
(i) => i.kind !== "repo-header" && i.kind !== "subgroup-header"
|
|
855
|
+
)
|
|
856
|
+
);
|
|
850
857
|
var withHeaders = (items, idx) => {
|
|
851
858
|
let start = idx;
|
|
852
859
|
while (start > 0 && (items[start - 1].kind === "repo-header" || items[start - 1].kind === "subgroup-header"))
|
|
@@ -1065,7 +1072,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1065
1072
|
label: "Open in browser",
|
|
1066
1073
|
hint: "o",
|
|
1067
1074
|
run: () => {
|
|
1068
|
-
|
|
1075
|
+
quietly`open ${item.url}`.catch(() => {
|
|
1069
1076
|
});
|
|
1070
1077
|
showFlash("\u2197 Opened in browser");
|
|
1071
1078
|
}
|
|
@@ -1112,7 +1119,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1112
1119
|
hint: "",
|
|
1113
1120
|
run: () => {
|
|
1114
1121
|
showFlash(`\u22EF ${label} \xB7 ${resolution}\u2026`);
|
|
1115
|
-
void
|
|
1122
|
+
void quietly`jira issue move ${item.key} ${state} --resolution ${resolution}`.then(() => {
|
|
1116
1123
|
showFlash(`\u2713 ${label} \xB7 ${resolution}`);
|
|
1117
1124
|
setTimeout(() => onRefresh?.(), 1500);
|
|
1118
1125
|
}).catch(() => showFlash(`\u2717 Move to ${label} failed`));
|
|
@@ -1123,7 +1130,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1123
1130
|
hint: "",
|
|
1124
1131
|
run: () => {
|
|
1125
1132
|
showFlash(`\u22EF Moving to ${label}\u2026`);
|
|
1126
|
-
void
|
|
1133
|
+
void quietly`jira issue move ${item.key} ${state}`.then(() => {
|
|
1127
1134
|
showFlash(`\u2713 Moved to ${label}`);
|
|
1128
1135
|
setTimeout(() => onRefresh?.(), 1500);
|
|
1129
1136
|
}).catch(() => showFlash(`\u2717 Move to ${label} failed`));
|
|
@@ -1204,7 +1211,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1204
1211
|
run: () => {
|
|
1205
1212
|
onRemove?.(item);
|
|
1206
1213
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
1207
|
-
void
|
|
1214
|
+
void quietly`gh issue close ${item.number} --repo ${item.repo}`.catch(
|
|
1208
1215
|
() => {
|
|
1209
1216
|
showFlash(`\u2717 Close failed \u2014 restoring #${item.number}`);
|
|
1210
1217
|
onRefresh?.();
|
|
@@ -1241,7 +1248,7 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1241
1248
|
label: `Open ${jiraKey} in Jira`,
|
|
1242
1249
|
hint: "t",
|
|
1243
1250
|
run: () => {
|
|
1244
|
-
|
|
1251
|
+
quietly`open ${jiraBase}/${jiraKey}`.catch(() => {
|
|
1245
1252
|
});
|
|
1246
1253
|
showFlash(`\u2197 Opened ${jiraKey} in Jira`);
|
|
1247
1254
|
}
|
|
@@ -1260,10 +1267,12 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1260
1267
|
run: () => {
|
|
1261
1268
|
onRemove?.(item);
|
|
1262
1269
|
showFlash(`\u2713 Closed #${item.number}`);
|
|
1263
|
-
void
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1270
|
+
void quietly`gh pr close ${item.number} --repo ${item.repo}`.catch(
|
|
1271
|
+
() => {
|
|
1272
|
+
showFlash(`\u2717 Close failed \u2014 restoring #${item.number}`);
|
|
1273
|
+
onRefresh?.();
|
|
1274
|
+
}
|
|
1275
|
+
);
|
|
1267
1276
|
}
|
|
1268
1277
|
},
|
|
1269
1278
|
{ label: "Cancel", hint: "", run: () => {
|
|
@@ -1283,8 +1292,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1283
1292
|
run: () => {
|
|
1284
1293
|
onRemove?.(item);
|
|
1285
1294
|
showFlash(`\u2713 Closed #${item.number} and deleted ${item.branch}`);
|
|
1286
|
-
void
|
|
1287
|
-
() =>
|
|
1295
|
+
void quietly`gh pr close ${item.number} --repo ${item.repo}`.then(
|
|
1296
|
+
() => quietly`gh api -X DELETE ${`repos/${item.repo}/git/refs/heads/${item.branch}`}`
|
|
1288
1297
|
).catch(() => {
|
|
1289
1298
|
showFlash(`\u2717 Close + delete failed \u2014 restoring`);
|
|
1290
1299
|
onRefresh?.();
|
|
@@ -1648,6 +1657,7 @@ var RepoPicker = ({
|
|
|
1648
1657
|
}
|
|
1649
1658
|
);
|
|
1650
1659
|
};
|
|
1660
|
+
var extensionFor = (input, extensions) => input ? extensions?.find((e) => e.key === input) : void 0;
|
|
1651
1661
|
var BrowseScreen = ({
|
|
1652
1662
|
sections,
|
|
1653
1663
|
login,
|
|
@@ -1663,6 +1673,7 @@ var BrowseScreen = ({
|
|
|
1663
1673
|
onOpenPr,
|
|
1664
1674
|
onOpenIssue,
|
|
1665
1675
|
onOpenExt,
|
|
1676
|
+
extensions,
|
|
1666
1677
|
ciStatusState,
|
|
1667
1678
|
ciJob,
|
|
1668
1679
|
tabHelp,
|
|
@@ -1679,25 +1690,17 @@ var BrowseScreen = ({
|
|
|
1679
1690
|
const [localSections, setLocalSections] = useState(initialSections);
|
|
1680
1691
|
const [tabIdx, setTabIdx] = useState(0);
|
|
1681
1692
|
const [cursors, setCursors] = useState(
|
|
1682
|
-
initialSections.map(
|
|
1683
|
-
(s) => Math.max(
|
|
1684
|
-
0,
|
|
1685
|
-
s.items.findIndex(
|
|
1686
|
-
(i) => i.kind !== "repo-header" && i.kind !== "subgroup-header"
|
|
1687
|
-
)
|
|
1688
|
-
)
|
|
1689
|
-
)
|
|
1690
|
-
);
|
|
1691
|
-
const [viewStarts, setViewStarts] = useState(
|
|
1692
|
-
initialSections.map(() => 0)
|
|
1693
|
+
() => Object.fromEntries(initialSections.map((s) => [s.id, firstSelectable(s)]))
|
|
1693
1694
|
);
|
|
1695
|
+
const [viewStarts, setViewStarts] = useState({});
|
|
1696
|
+
const safeTabIdx = Math.min(tabIdx, Math.max(0, localSections.length - 1));
|
|
1697
|
+
const activeId = localSections[safeTabIdx]?.id ?? "";
|
|
1694
1698
|
const [flash, setFlash] = useState(null);
|
|
1695
1699
|
const menu = useActionMenu();
|
|
1696
1700
|
const [search, setSearch] = useState(null);
|
|
1697
1701
|
const [searchInput, setSearchInput] = useState(false);
|
|
1698
1702
|
const [repoFilter, setRepoFilter] = useState(/* @__PURE__ */ new Set());
|
|
1699
1703
|
const [repoPicker, setRepoPicker] = useState(false);
|
|
1700
|
-
const [repoCursor, setRepoCursor] = useState(0);
|
|
1701
1704
|
const [help, setHelp] = useState(false);
|
|
1702
1705
|
const [explain, setExplain] = useState(false);
|
|
1703
1706
|
const filterActive = search != null || repoFilter.size > 0;
|
|
@@ -1710,8 +1713,8 @@ var BrowseScreen = ({
|
|
|
1710
1713
|
rows - 10 - (filterActive ? 2 : 0) - (reserveCiRow ? 2 : 0)
|
|
1711
1714
|
);
|
|
1712
1715
|
useEffect(() => {
|
|
1713
|
-
setCursors((p) => p
|
|
1714
|
-
setViewStarts((p) => p
|
|
1716
|
+
setCursors((p) => ({ ...p, [activeId]: 0 }));
|
|
1717
|
+
setViewStarts((p) => ({ ...p, [activeId]: 0 }));
|
|
1715
1718
|
}, [search]);
|
|
1716
1719
|
useEffect(() => {
|
|
1717
1720
|
setLocalSections(applyWork(sections, includeWork));
|
|
@@ -1719,20 +1722,30 @@ var BrowseScreen = ({
|
|
|
1719
1722
|
useEffect(() => {
|
|
1720
1723
|
setTabIdx((prev) => Math.min(prev, Math.max(0, localSections.length - 1)));
|
|
1721
1724
|
setCursors(
|
|
1722
|
-
(prev) =>
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1725
|
+
(prev) => Object.fromEntries(
|
|
1726
|
+
localSections.map((s) => {
|
|
1727
|
+
const c = Math.min(
|
|
1728
|
+
prev[s.id] ?? firstSelectable(s),
|
|
1729
|
+
s.items.length - 1
|
|
1730
|
+
);
|
|
1731
|
+
if (c < 0) return [s.id, 0];
|
|
1732
|
+
return [
|
|
1733
|
+
s.id,
|
|
1734
|
+
s.items[c]?.kind === "repo-header" || s.items[c]?.kind === "subgroup-header" ? moveCursor(s.items, c, 1) : c
|
|
1735
|
+
];
|
|
1736
|
+
})
|
|
1737
|
+
)
|
|
1727
1738
|
);
|
|
1728
1739
|
setViewStarts(
|
|
1729
|
-
(prev) =>
|
|
1730
|
-
(s
|
|
1740
|
+
(prev) => Object.fromEntries(
|
|
1741
|
+
localSections.map((s) => [
|
|
1742
|
+
s.id,
|
|
1743
|
+
Math.min(prev[s.id] ?? 0, maxViewStart(s.items, listHeight))
|
|
1744
|
+
])
|
|
1731
1745
|
)
|
|
1732
1746
|
);
|
|
1733
1747
|
}, [localSections, listHeight]);
|
|
1734
1748
|
const removeItemFromSections = (target) => setLocalSections((prev) => withoutItem(prev, target));
|
|
1735
|
-
const safeTabIdx = Math.min(tabIdx, Math.max(0, localSections.length - 1));
|
|
1736
1749
|
const rawSection = localSections[safeTabIdx] ?? {
|
|
1737
1750
|
id: "empty",
|
|
1738
1751
|
label: "",
|
|
@@ -1742,8 +1755,12 @@ var BrowseScreen = ({
|
|
|
1742
1755
|
const filtered = repoFilter.size > 0 ? filterByRepos(searched, repoFilter) : searched;
|
|
1743
1756
|
const section = filterActive ? { ...rawSection, items: filtered[0]?.items ?? [] } : rawSection;
|
|
1744
1757
|
const allRepos = reposInSections(localSections);
|
|
1745
|
-
const cursor
|
|
1746
|
-
|
|
1758
|
+
const { cursor: repoCursor, setCursor: setRepoCursor } = useListCursor(
|
|
1759
|
+
allRepos.length,
|
|
1760
|
+
{ vimKeys: false, isActive: repoPicker }
|
|
1761
|
+
);
|
|
1762
|
+
const cursor = cursors[activeId] ?? 0;
|
|
1763
|
+
const viewStart = viewStarts[activeId] ?? 0;
|
|
1747
1764
|
const visibleCount = windowCount(section.items, viewStart, listHeight);
|
|
1748
1765
|
const visibleItems = section.items.slice(viewStart, viewStart + visibleCount);
|
|
1749
1766
|
const hasMore = viewStart + visibleCount < section.items.length;
|
|
@@ -1798,9 +1815,6 @@ var BrowseScreen = ({
|
|
|
1798
1815
|
}
|
|
1799
1816
|
if (repoPicker) {
|
|
1800
1817
|
if (key.escape || key.return) return setRepoPicker(false);
|
|
1801
|
-
if (key.upArrow) return setRepoCursor((c) => Math.max(0, c - 1));
|
|
1802
|
-
if (key.downArrow)
|
|
1803
|
-
return setRepoCursor((c) => Math.min(allRepos.length - 1, c + 1));
|
|
1804
1818
|
if (input === "a") return setRepoFilter(/* @__PURE__ */ new Set());
|
|
1805
1819
|
if (input === " ") {
|
|
1806
1820
|
const repo = allRepos[repoCursor];
|
|
@@ -1848,16 +1862,16 @@ var BrowseScreen = ({
|
|
|
1848
1862
|
if (key.upArrow) {
|
|
1849
1863
|
const next = moveCursor(section.items, cursor, -1);
|
|
1850
1864
|
const newVs = Math.min(viewStart, withHeaders(section.items, next));
|
|
1851
|
-
setCursors((p) => p
|
|
1852
|
-
setViewStarts((p) => p
|
|
1865
|
+
setCursors((p) => ({ ...p, [activeId]: next }));
|
|
1866
|
+
setViewStarts((p) => ({ ...p, [activeId]: newVs }));
|
|
1853
1867
|
}
|
|
1854
1868
|
if (key.downArrow) {
|
|
1855
1869
|
const next = moveCursor(section.items, cursor, 1);
|
|
1856
1870
|
let newVs = viewStart;
|
|
1857
1871
|
while (next >= newVs + windowCount(section.items, newVs, listHeight))
|
|
1858
1872
|
newVs++;
|
|
1859
|
-
setCursors((p) => p
|
|
1860
|
-
setViewStarts((p) => p
|
|
1873
|
+
setCursors((p) => ({ ...p, [activeId]: next }));
|
|
1874
|
+
setViewStarts((p) => ({ ...p, [activeId]: newVs }));
|
|
1861
1875
|
}
|
|
1862
1876
|
if (key.leftArrow) setTabIdx((i) => Math.max(0, i - 1));
|
|
1863
1877
|
if (key.rightArrow)
|
|
@@ -1873,12 +1887,20 @@ var BrowseScreen = ({
|
|
|
1873
1887
|
}
|
|
1874
1888
|
if (workToggle && input === "w") {
|
|
1875
1889
|
const next = !includeWork;
|
|
1890
|
+
const nextSections = applyWork(sections, next);
|
|
1891
|
+
const keptIdx = nextSections.findIndex((s) => s.id === activeId);
|
|
1876
1892
|
setIncludeWork(next);
|
|
1877
|
-
setLocalSections(
|
|
1893
|
+
setLocalSections(nextSections);
|
|
1894
|
+
if (keptIdx >= 0) setTabIdx(keptIdx);
|
|
1878
1895
|
return;
|
|
1879
1896
|
}
|
|
1880
|
-
|
|
1881
|
-
|
|
1897
|
+
const ext = extensionFor(input, extensions);
|
|
1898
|
+
if (ext) {
|
|
1899
|
+
onOpenExt?.(ext.id, {
|
|
1900
|
+
item: activeItem ?? void 0,
|
|
1901
|
+
ciJob: ciStatus?.job,
|
|
1902
|
+
login
|
|
1903
|
+
});
|
|
1882
1904
|
return;
|
|
1883
1905
|
}
|
|
1884
1906
|
if (!activeItem || activeItem.kind === "repo-header" || activeItem.kind === "subgroup-header")
|
|
@@ -1934,7 +1956,7 @@ var BrowseScreen = ({
|
|
|
1934
1956
|
return;
|
|
1935
1957
|
}
|
|
1936
1958
|
if (input === "o") {
|
|
1937
|
-
|
|
1959
|
+
quietly`open ${activeItem.url}`.catch(() => {
|
|
1938
1960
|
});
|
|
1939
1961
|
const label = activeItem.kind === "jira" ? activeItem.key : `#${activeItem.number}`;
|
|
1940
1962
|
showFlash(`\u2197 Opened ${label}`);
|
|
@@ -2000,7 +2022,7 @@ var BrowseScreen = ({
|
|
|
2000
2022
|
run: () => {
|
|
2001
2023
|
menu.close();
|
|
2002
2024
|
showFlash(`\u22EF ${label} \xB7 ${resolution}\u2026`);
|
|
2003
|
-
|
|
2025
|
+
quietly`jira issue move ${jiraKey} ${state} --resolution ${resolution}`.then(() => {
|
|
2004
2026
|
showFlash(`\u2713 ${label} \xB7 ${resolution}`);
|
|
2005
2027
|
setTimeout(() => onRefresh?.(), 1500);
|
|
2006
2028
|
}).catch(() => showFlash(`\u2717 Move to ${label} failed`));
|
|
@@ -2012,7 +2034,7 @@ var BrowseScreen = ({
|
|
|
2012
2034
|
run: () => {
|
|
2013
2035
|
menu.close();
|
|
2014
2036
|
showFlash(`\u22EF Moving to ${label}\u2026`);
|
|
2015
|
-
|
|
2037
|
+
quietly`jira issue move ${jiraKey} ${state}`.then(() => {
|
|
2016
2038
|
showFlash(`\u2713 Moved to ${label}`);
|
|
2017
2039
|
setTimeout(() => onRefresh?.(), 1500);
|
|
2018
2040
|
}).catch(() => showFlash(`\u2717 Move to ${label} failed`));
|
|
@@ -2352,7 +2374,8 @@ var App = ({
|
|
|
2352
2374
|
target,
|
|
2353
2375
|
sections: state.sections,
|
|
2354
2376
|
login: state.login
|
|
2355
|
-
})
|
|
2377
|
+
}),
|
|
2378
|
+
extensions
|
|
2356
2379
|
}
|
|
2357
2380
|
),
|
|
2358
2381
|
(overlay?.kind === "pr" || overlay?.kind === "issue") && detailFor?.({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|