@kud/gh-ink 0.9.1 → 0.11.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 +9 -1
- package/dist/index.js +57 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -20,8 +20,9 @@ type HealthPanelProps = {
|
|
|
20
20
|
error: string | null;
|
|
21
21
|
reload: () => void;
|
|
22
22
|
onOpenCheck: (check: PrCheck) => void;
|
|
23
|
+
onMerged?: () => void;
|
|
23
24
|
};
|
|
24
|
-
declare const HealthPanel: ({ repo, number, data, error, reload, onOpenCheck, }: HealthPanelProps) => React.JSX.Element;
|
|
25
|
+
declare const HealthPanel: ({ repo, number, data, error, reload, onOpenCheck, onMerged, }: HealthPanelProps) => React.JSX.Element;
|
|
25
26
|
|
|
26
27
|
declare const renderMarkdown: (raw: string, width: number, fileLink?: (path: string, line?: number) => string) => StyledLine[];
|
|
27
28
|
|
|
@@ -67,6 +68,7 @@ type GHItem = {
|
|
|
67
68
|
health: Health;
|
|
68
69
|
author?: string;
|
|
69
70
|
age: string;
|
|
71
|
+
activityAge?: string;
|
|
70
72
|
ts: number;
|
|
71
73
|
unresolved: number;
|
|
72
74
|
conversation: number;
|
|
@@ -127,6 +129,12 @@ type DetailContext = {
|
|
|
127
129
|
onBack: () => void;
|
|
128
130
|
onRefresh: () => void;
|
|
129
131
|
onRemove: (item: GHItem) => void;
|
|
132
|
+
/**
|
|
133
|
+
* The PR just merged from here. Returns to the list, marks the row merged for
|
|
134
|
+
* a few seconds, then drops it — distinct from onRemove, which drops it at
|
|
135
|
+
* once. Optional so a host that cannot merge need not implement it.
|
|
136
|
+
*/
|
|
137
|
+
onMerged?: (item: GHItem) => void;
|
|
130
138
|
};
|
|
131
139
|
type Action = {
|
|
132
140
|
label: string;
|
package/dist/index.js
CHANGED
|
@@ -452,7 +452,8 @@ var HealthPanel = ({
|
|
|
452
452
|
data,
|
|
453
453
|
error,
|
|
454
454
|
reload,
|
|
455
|
-
onOpenCheck
|
|
455
|
+
onOpenCheck,
|
|
456
|
+
onMerged
|
|
456
457
|
}) => {
|
|
457
458
|
const [cursor, setCursor] = useState(0);
|
|
458
459
|
const [note, setNote] = useState(null);
|
|
@@ -475,13 +476,14 @@ var HealthPanel = ({
|
|
|
475
476
|
setNote(msg);
|
|
476
477
|
setTimeout(() => setNote(null), 2500);
|
|
477
478
|
};
|
|
478
|
-
const act = async (msg, run) => {
|
|
479
|
+
const act = async (msg, run, onSuccess) => {
|
|
479
480
|
setBusy(true);
|
|
480
481
|
setNote(`\u22EF ${msg}\u2026`);
|
|
481
482
|
try {
|
|
482
483
|
await run();
|
|
483
484
|
flash(`\u2713 ${msg}`);
|
|
484
|
-
|
|
485
|
+
if (onSuccess) onSuccess();
|
|
486
|
+
else reload();
|
|
485
487
|
} catch {
|
|
486
488
|
flash(`\u2717 ${msg} failed`);
|
|
487
489
|
} finally {
|
|
@@ -498,7 +500,7 @@ var HealthPanel = ({
|
|
|
498
500
|
if (busy) return;
|
|
499
501
|
if (confirm === "merge") {
|
|
500
502
|
if (input === "y" || input === "Y")
|
|
501
|
-
void act("merge", () => mergePr(repo, number));
|
|
503
|
+
void act("merge", () => mergePr(repo, number), onMerged);
|
|
502
504
|
setConfirm(null);
|
|
503
505
|
return;
|
|
504
506
|
}
|
|
@@ -733,7 +735,9 @@ var repoPriority = (repo) => {
|
|
|
733
735
|
};
|
|
734
736
|
var sortItems = (items) => [...items].sort((a, b) => {
|
|
735
737
|
const pd = repoPriority(a.repo) - repoPriority(b.repo);
|
|
736
|
-
|
|
738
|
+
if (pd !== 0) return pd;
|
|
739
|
+
const rd = a.repo.localeCompare(b.repo);
|
|
740
|
+
return rd !== 0 ? rd : b.ts - a.ts;
|
|
737
741
|
});
|
|
738
742
|
var sortByRecency = (items) => [...items].sort((a, b) => b.ts - a.ts);
|
|
739
743
|
var insertRepoHeaders = (items) => {
|
|
@@ -1444,11 +1448,17 @@ var RepoHeaderRow = ({ repo, gap }) => {
|
|
|
1444
1448
|
const fill = Math.max(4, 46 - label.length);
|
|
1445
1449
|
return /* @__PURE__ */ jsx(Box, { marginTop: gap ? 1 : 0, children: /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + label + "\u2500".repeat(fill) }) });
|
|
1446
1450
|
};
|
|
1451
|
+
var MERGED_HOLD_MS = 3e3;
|
|
1452
|
+
var MERGED_FRAME_MS = 150;
|
|
1453
|
+
var MERGED_FRAMES = ["\u2726", "\u2727", "\u2736", "\u2727"];
|
|
1454
|
+
var MERGED_COLOUR = "#A371F7";
|
|
1447
1455
|
var ItemRow = ({
|
|
1448
1456
|
item,
|
|
1449
1457
|
active,
|
|
1450
1458
|
gap,
|
|
1451
|
-
login
|
|
1459
|
+
login,
|
|
1460
|
+
merged,
|
|
1461
|
+
sparkFrame = 0
|
|
1452
1462
|
}) => {
|
|
1453
1463
|
if (item.kind === "repo-header")
|
|
1454
1464
|
return /* @__PURE__ */ jsx(RepoHeaderRow, { repo: item.repo, gap: gap ?? false });
|
|
@@ -1479,15 +1489,20 @@ var ItemRow = ({
|
|
|
1479
1489
|
/* @__PURE__ */ jsx(Text2, { bold: active, children: truncate(item.summary, titleMax2) })
|
|
1480
1490
|
] });
|
|
1481
1491
|
}
|
|
1482
|
-
const { glyph:
|
|
1492
|
+
const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
|
|
1493
|
+
const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : healthIcon;
|
|
1494
|
+
const color = merged ? MERGED_COLOUR : healthColor2;
|
|
1483
1495
|
const [turnIcon, turnColor] = !login || !item.lastActor ? [" ", "white"] : item.lastActor === login ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
|
|
1484
1496
|
const numStr = `#${item.number}`.padEnd(7);
|
|
1485
1497
|
const showAuthor = !!item.author && item.author !== login;
|
|
1486
1498
|
const unresolvedLabel = item.unresolved > 0 ? `\uF086 ${item.unresolved}` : "";
|
|
1499
|
+
const ageLabel = item.activityAge && item.activityAge !== item.age ? `${item.activityAge} \xB7 ${item.age}` : item.age;
|
|
1500
|
+
const mergedLabel = merged ? "MERGED" : "";
|
|
1487
1501
|
const suffix = [
|
|
1488
|
-
|
|
1502
|
+
ageLabel || "",
|
|
1489
1503
|
unresolvedLabel,
|
|
1490
|
-
showAuthor ? `by ${item.author}` : ""
|
|
1504
|
+
showAuthor ? `by ${item.author}` : "",
|
|
1505
|
+
mergedLabel
|
|
1491
1506
|
].filter(Boolean).join(" ");
|
|
1492
1507
|
const repoLabel = item.indent ? item.repo : "";
|
|
1493
1508
|
const fixedWidth = 2 + (item.indent ? 3 : 0) + 2 + 2 + 7 + repoLabel.length + suffix.length + 6;
|
|
@@ -1502,7 +1517,8 @@ var ItemRow = ({
|
|
|
1502
1517
|
repoLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: repoLabel }) : null,
|
|
1503
1518
|
unresolvedLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: "#FF8700", children: " " + unresolvedLabel }) : null,
|
|
1504
1519
|
showAuthor ? /* @__PURE__ */ jsx(Text2, { dimColor: true, italic: true, children: " by " + item.author }) : null,
|
|
1505
|
-
|
|
1520
|
+
ageLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + ageLabel }) : null,
|
|
1521
|
+
mergedLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: MERGED_COLOUR, children: " " + mergedLabel }) : null
|
|
1506
1522
|
] });
|
|
1507
1523
|
};
|
|
1508
1524
|
var useActionMenu = () => {
|
|
@@ -1791,7 +1807,8 @@ var BrowseScreen = ({
|
|
|
1791
1807
|
tabHelp,
|
|
1792
1808
|
isWorkRepo,
|
|
1793
1809
|
initialIncludeWork,
|
|
1794
|
-
brand
|
|
1810
|
+
brand,
|
|
1811
|
+
mergedUrls
|
|
1795
1812
|
}) => {
|
|
1796
1813
|
const { rows } = useWindowSize();
|
|
1797
1814
|
const [includeWork, setIncludeWork] = useState(
|
|
@@ -1881,6 +1898,13 @@ var BrowseScreen = ({
|
|
|
1881
1898
|
setFlash(msg);
|
|
1882
1899
|
setTimeout(() => setFlash(null), 2e3);
|
|
1883
1900
|
};
|
|
1901
|
+
const [sparkFrame, setSparkFrame] = useState(0);
|
|
1902
|
+
const sparkling = (mergedUrls?.length ?? 0) > 0;
|
|
1903
|
+
useEffect(() => {
|
|
1904
|
+
if (!sparkling) return;
|
|
1905
|
+
const id = setInterval(() => setSparkFrame((f) => f + 1), MERGED_FRAME_MS);
|
|
1906
|
+
return () => clearInterval(id);
|
|
1907
|
+
}, [sparkling]);
|
|
1884
1908
|
useEffect(() => {
|
|
1885
1909
|
if (refreshError) showFlash(`\u2717 ${refreshError.message}`);
|
|
1886
1910
|
}, [refreshError]);
|
|
@@ -2246,6 +2270,8 @@ var BrowseScreen = ({
|
|
|
2246
2270
|
item,
|
|
2247
2271
|
active: viewStart + i === cursor,
|
|
2248
2272
|
login,
|
|
2273
|
+
merged: (item.kind === "pr" || item.kind === "issue") && !!mergedUrls?.includes(item.url),
|
|
2274
|
+
sparkFrame,
|
|
2249
2275
|
gap: viewStart + i > 0 && (item.kind === "repo-header" || item.kind === "subgroup-header" || // A header is always followed by a blank line before its
|
|
2250
2276
|
// first child — in Other PRs that's "free" because the
|
|
2251
2277
|
// child is itself a repo-header (gap above). A jira row
|
|
@@ -2412,6 +2438,9 @@ var App = ({
|
|
|
2412
2438
|
clearInterval(id);
|
|
2413
2439
|
};
|
|
2414
2440
|
}, [hasCiStatus, ciFetcher, ciPollMs]);
|
|
2441
|
+
const [mergedUrls, setMergedUrls] = useState([]);
|
|
2442
|
+
const mergeTimers = useRef([]);
|
|
2443
|
+
useEffect(() => () => mergeTimers.current.forEach(clearTimeout), []);
|
|
2415
2444
|
if (state.phase === "loading" || state.phase === "empty" || state.phase === "failed")
|
|
2416
2445
|
return /* @__PURE__ */ jsxs(
|
|
2417
2446
|
Box,
|
|
@@ -2457,6 +2486,20 @@ var App = ({
|
|
|
2457
2486
|
sections: withoutItem(state.sections, target),
|
|
2458
2487
|
login: state.login
|
|
2459
2488
|
});
|
|
2489
|
+
const markMerged = (target) => {
|
|
2490
|
+
toBrowse();
|
|
2491
|
+
setMergedUrls(
|
|
2492
|
+
(prev) => prev.includes(target.url) ? prev : [...prev, target.url]
|
|
2493
|
+
);
|
|
2494
|
+
mergeTimers.current.push(
|
|
2495
|
+
setTimeout(() => {
|
|
2496
|
+
setMergedUrls((prev) => prev.filter((u) => u !== target.url));
|
|
2497
|
+
setState(
|
|
2498
|
+
(s) => s.phase === "browse" ? { ...s, sections: withoutItem(s.sections, target) } : s
|
|
2499
|
+
);
|
|
2500
|
+
}, MERGED_HOLD_MS)
|
|
2501
|
+
);
|
|
2502
|
+
};
|
|
2460
2503
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2461
2504
|
/* @__PURE__ */ jsx(
|
|
2462
2505
|
BrowseScreen,
|
|
@@ -2477,6 +2520,7 @@ var App = ({
|
|
|
2477
2520
|
isWorkRepo,
|
|
2478
2521
|
initialIncludeWork,
|
|
2479
2522
|
hidden: overlay !== null,
|
|
2523
|
+
mergedUrls,
|
|
2480
2524
|
ciStatusState: hasCiStatus ? ciStatusState : void 0,
|
|
2481
2525
|
ciJob,
|
|
2482
2526
|
tabHelp,
|
|
@@ -2508,7 +2552,8 @@ var App = ({
|
|
|
2508
2552
|
login: state.login,
|
|
2509
2553
|
onBack: toBrowse,
|
|
2510
2554
|
onRefresh: applyOrRefresh,
|
|
2511
|
-
onRemove: removeAndReturn
|
|
2555
|
+
onRemove: removeAndReturn,
|
|
2556
|
+
onMerged: markMerged
|
|
2512
2557
|
}),
|
|
2513
2558
|
overlay?.kind === "ext" && extensions?.find((e) => e.id === overlay.extId)?.body(toBrowse, overlay.target)
|
|
2514
2559
|
] });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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",
|