@kud/gh-ink 0.20.0 → 0.22.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 +44 -4
- package/dist/index.js +58 -37
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -268,7 +268,7 @@ declare const ActionMenu: ({ item, actions, cursor, }: {
|
|
|
268
268
|
actions: Action[];
|
|
269
269
|
cursor: number;
|
|
270
270
|
}) => React.JSX.Element;
|
|
271
|
-
declare const App: ({ fetcher, cacheKey, title, detailFor, isWorkRepo,
|
|
271
|
+
declare const App: ({ fetcher, cacheKey, title, detailFor, isWorkRepo, includeWork, jiraBase, jiraKeyRe, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, extensions, tabHelp, emptyHint, }: {
|
|
272
272
|
fetcher: () => Promise<{
|
|
273
273
|
sections: Section[];
|
|
274
274
|
login: string;
|
|
@@ -278,13 +278,21 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, isWorkRepo, initialIn
|
|
|
278
278
|
title?: string;
|
|
279
279
|
detailFor?: (ctx: DetailContext) => React.ReactNode;
|
|
280
280
|
isWorkRepo?: (repo: string) => boolean;
|
|
281
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Which side of the split to show, chosen ONCE by the host at launch — there
|
|
283
|
+
* is no in-app toggle. `undefined` means no split: every row stands.
|
|
284
|
+
*
|
|
285
|
+
* The host decides from where the command was run, so the answer is settled
|
|
286
|
+
* before the first paint and cannot change under the reader. A `w` key used to
|
|
287
|
+
* flip it mid-session, which meant the inbox could disagree with the scope the
|
|
288
|
+
* command had been started in, with nothing on screen explaining why.
|
|
289
|
+
*/
|
|
290
|
+
includeWork?: boolean;
|
|
282
291
|
tabHelp?: [string, string][];
|
|
283
292
|
emptyHint?: string;
|
|
284
293
|
jiraBase?: string;
|
|
285
294
|
jiraKeyRe?: RegExp;
|
|
286
295
|
jiraTransitions?: JiraTransition[];
|
|
287
|
-
workToggle?: boolean;
|
|
288
296
|
hasCiStatus?: boolean;
|
|
289
297
|
ciJob?: string;
|
|
290
298
|
ciFetcher?: () => Promise<CiStatus | null>;
|
|
@@ -350,6 +358,19 @@ type InboxConfig = {
|
|
|
350
358
|
* one machine would otherwise share a cache keyed only by section name.
|
|
351
359
|
*/
|
|
352
360
|
cacheNamespace: string;
|
|
361
|
+
/**
|
|
362
|
+
* How long a cached inbox is trusted before a launch refetches. Every launch
|
|
363
|
+
* past it pays the full query — eight searches with check rollups — so this is
|
|
364
|
+
* the single knob between "always current" and "always slow, and drawing 502s
|
|
365
|
+
* from the API".
|
|
366
|
+
*
|
|
367
|
+
* It can be generous because staleness is bounded from the other end: acting
|
|
368
|
+
* on a row drops the entry outright, and `r` refetches on demand. What the TTL
|
|
369
|
+
* actually governs is how long a change made ELSEWHERE — someone approving
|
|
370
|
+
* your PR while you were not looking — can go unseen, which is a glance
|
|
371
|
+
* arriving late, not a wrong action taken.
|
|
372
|
+
*/
|
|
373
|
+
cacheTtlMs: number;
|
|
353
374
|
};
|
|
354
375
|
/** Supply the host's opinions. Call once, before rendering. */
|
|
355
376
|
declare const configureInbox: (config: Partial<InboxConfig>) => void;
|
|
@@ -366,4 +387,23 @@ declare const profileOf: (repo: string) => RepoProfile | null;
|
|
|
366
387
|
*/
|
|
367
388
|
declare const checkoutDirs: () => string[];
|
|
368
389
|
|
|
369
|
-
|
|
390
|
+
type RepoFilter = {
|
|
391
|
+
/** Show only repos matching one of these. Empty or absent means show all. */
|
|
392
|
+
include?: readonly string[];
|
|
393
|
+
/** Hide repos matching one of these. Applied after include, and wins. */
|
|
394
|
+
exclude?: readonly string[];
|
|
395
|
+
};
|
|
396
|
+
/**
|
|
397
|
+
* Whether a repo survives the filter. No include list means everything is in;
|
|
398
|
+
* exclude is applied afterwards and always wins, so a reader can take a whole
|
|
399
|
+
* org and drop one repo from it without listing the rest.
|
|
400
|
+
*/
|
|
401
|
+
declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
402
|
+
/**
|
|
403
|
+
* Parse a comma-separated pattern list, as a flag supplies it. Blank entries and
|
|
404
|
+
* surrounding whitespace are dropped, so a trailing comma or a quoted list with
|
|
405
|
+
* spaces after the commas both behave.
|
|
406
|
+
*/
|
|
407
|
+
declare const parsePatterns: (value: string) => string[];
|
|
408
|
+
|
|
409
|
+
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 InboxConfig, type InboxExtension, type JiraTransition, type RepoFilter, type RepoHeader, type RepoProfile, type Section, type ShowLess, type ShowMore, type Standing, type SubgroupHeader, type TaskRow, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
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,
|
|
3
|
+
import { colors, ScrollView, TextInput, LoadingScreen, StatusMessage, FooterHints, useListCursor, Tabs } 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';
|
|
@@ -611,7 +611,8 @@ var healthLegend = [
|
|
|
611
611
|
var EMPTY = {
|
|
612
612
|
repoPriority: [],
|
|
613
613
|
profiles: [],
|
|
614
|
-
cacheNamespace: "gh-ink"
|
|
614
|
+
cacheNamespace: "gh-ink",
|
|
615
|
+
cacheTtlMs: 6e5
|
|
615
616
|
};
|
|
616
617
|
var current = EMPTY;
|
|
617
618
|
var configureInbox = (config) => {
|
|
@@ -630,7 +631,7 @@ var checkoutDirs = () => [
|
|
|
630
631
|
];
|
|
631
632
|
|
|
632
633
|
// src/inbox/cache.ts
|
|
633
|
-
var
|
|
634
|
+
var cacheTtlMs = () => inboxConfig().cacheTtlMs;
|
|
634
635
|
var CACHE_VERSION = 2;
|
|
635
636
|
var cacheDir = () => join(
|
|
636
637
|
process.env.XDG_CACHE_HOME || join(homedir(), ".cache"),
|
|
@@ -647,7 +648,7 @@ var readCache = (key) => {
|
|
|
647
648
|
return null;
|
|
648
649
|
}
|
|
649
650
|
};
|
|
650
|
-
var isFresh = (cached, now = Date.now()) => !!cached && now - cached.at <
|
|
651
|
+
var isFresh = (cached, now = Date.now()) => !!cached && now - cached.at < cacheTtlMs();
|
|
651
652
|
var writeCache = (key, data) => {
|
|
652
653
|
try {
|
|
653
654
|
mkdirSync(cacheDir(), { recursive: true });
|
|
@@ -1284,6 +1285,20 @@ var drillLabel = (item) => {
|
|
|
1284
1285
|
if (item.kind === "pr") return "Open PR";
|
|
1285
1286
|
return "Drill in";
|
|
1286
1287
|
};
|
|
1288
|
+
var UNSUBSCRIBE_MUTATION = `
|
|
1289
|
+
mutation($id: ID!) {
|
|
1290
|
+
updateSubscription(input: { subscribableId: $id, state: UNSUBSCRIBED }) {
|
|
1291
|
+
subscribable { viewerSubscription }
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
`;
|
|
1295
|
+
var unsubscribeFrom = async (item) => {
|
|
1296
|
+
const view = item.kind === "pr" ? "pr" : "issue";
|
|
1297
|
+
const found = await quietly`gh ${view} view ${item.number} --repo ${item.repo} --json id --jq .id`;
|
|
1298
|
+
const id = found.stdout.trim();
|
|
1299
|
+
if (!id) throw new Error(`no node id for ${item.repo}#${item.number}`);
|
|
1300
|
+
await quietly`gh api graphql -f query=${UNSUBSCRIBE_MUTATION} -f id=${id}`;
|
|
1301
|
+
};
|
|
1287
1302
|
var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions, onRefresh, onRemove, onOpenView, ext) => {
|
|
1288
1303
|
if (item.kind === "repo-header" || item.kind === "subgroup-header" || item.kind === "show-more" || item.kind === "show-less")
|
|
1289
1304
|
return [];
|
|
@@ -1369,6 +1384,14 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1369
1384
|
showFlash(`\u2713 Copied ${item.repo}`);
|
|
1370
1385
|
}
|
|
1371
1386
|
});
|
|
1387
|
+
actions.push({
|
|
1388
|
+
label: "Unsubscribe",
|
|
1389
|
+
hint: "u",
|
|
1390
|
+
run: () => {
|
|
1391
|
+
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
1392
|
+
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch(() => showFlash(`\u2717 Unsubscribe failed for #${item.number}`));
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1372
1395
|
if (item.kind === "pr" && item.branch) {
|
|
1373
1396
|
actions.push({
|
|
1374
1397
|
label: "Copy branch name",
|
|
@@ -1556,7 +1579,7 @@ var InboxHeader = ({
|
|
|
1556
1579
|
const total = sections.reduce((n, s) => n + topLevelCount(s), 0);
|
|
1557
1580
|
const countSeg = loading ? " loading\u2026 " : quiet ? " " : ` ${String(total).padStart(3)} item${total !== 1 ? "s" : ""} \xB7 `;
|
|
1558
1581
|
const userSeg = loading || quiet ? "" : `@${login} `;
|
|
1559
|
-
const workLabel = work === void 0 ? "" :
|
|
1582
|
+
const workLabel = work === void 0 ? "" : ` ${work ? "work" : "home"} `;
|
|
1560
1583
|
const [statusText, statusColor] = hasPending ? [`\u25CF ${pendingSummary || "new"} \xB7 r apply`, "#FF8700"] : refreshing ? ["\u21BB refreshing\u2026", "cyan"] : fetchedAt ? [`updated ${agoText(fetchedAt)}`, void 0] : ["", void 0];
|
|
1561
1584
|
const statusSeg = statusText ? statusText + " " : "";
|
|
1562
1585
|
const fill = Math.max(
|
|
@@ -1567,11 +1590,7 @@ var InboxHeader = ({
|
|
|
1567
1590
|
/* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: true, children: brand }),
|
|
1568
1591
|
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: countSeg }),
|
|
1569
1592
|
userSeg ? /* @__PURE__ */ jsx(Text2, { children: userSeg }) : null,
|
|
1570
|
-
work !== void 0 ? /* @__PURE__ */
|
|
1571
|
-
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: " w " }),
|
|
1572
|
-
/* @__PURE__ */ jsx(Switch, { left: "work", right: "home", value: work ? "left" : "right" }),
|
|
1573
|
-
/* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " })
|
|
1574
|
-
] }) : null,
|
|
1593
|
+
work !== void 0 ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: workLabel }) : null,
|
|
1575
1594
|
statusText ? /* @__PURE__ */ jsx(
|
|
1576
1595
|
Text2,
|
|
1577
1596
|
{
|
|
@@ -1857,7 +1876,6 @@ var LegendGroup = ({
|
|
|
1857
1876
|
] });
|
|
1858
1877
|
var LEGEND_FOOTNOTE = "Each item lands in the FIRST tab that claims it, so counts are residuals rather than totals.";
|
|
1859
1878
|
var HelpModal = ({
|
|
1860
|
-
workToggle,
|
|
1861
1879
|
extensions,
|
|
1862
1880
|
hasJira,
|
|
1863
1881
|
tabHelp,
|
|
@@ -1882,11 +1900,11 @@ var HelpModal = ({
|
|
|
1882
1900
|
["s", "switch to branch here"],
|
|
1883
1901
|
["j", "open repo in new tab"],
|
|
1884
1902
|
["p", "open repo in new pane"],
|
|
1903
|
+
["u", "unsubscribe from this item"],
|
|
1885
1904
|
...hasJira ? [["t", "Jira: move / open ticket"]] : [],
|
|
1886
1905
|
["/", "search"],
|
|
1887
1906
|
["f", "filter by repo"],
|
|
1888
1907
|
["r", "refresh"],
|
|
1889
|
-
...workToggle ? [["w", "toggle work / home"]] : [],
|
|
1890
1908
|
...extensionLegend(extensions),
|
|
1891
1909
|
["?", "this help"],
|
|
1892
1910
|
["q", "quit"]
|
|
@@ -2063,7 +2081,6 @@ var BrowseScreen = ({
|
|
|
2063
2081
|
pendingSummary,
|
|
2064
2082
|
fetchedAt,
|
|
2065
2083
|
refreshError,
|
|
2066
|
-
workToggle,
|
|
2067
2084
|
hidden,
|
|
2068
2085
|
onOpenPr,
|
|
2069
2086
|
onOpenIssue,
|
|
@@ -2073,18 +2090,15 @@ var BrowseScreen = ({
|
|
|
2073
2090
|
ciJob,
|
|
2074
2091
|
tabHelp,
|
|
2075
2092
|
isWorkRepo,
|
|
2076
|
-
|
|
2093
|
+
includeWork,
|
|
2077
2094
|
brand,
|
|
2078
2095
|
mergedUrls,
|
|
2079
2096
|
transients,
|
|
2080
2097
|
onTabChange
|
|
2081
2098
|
}) => {
|
|
2082
2099
|
const { rows } = useWindowSize();
|
|
2083
|
-
const
|
|
2084
|
-
|
|
2085
|
-
);
|
|
2086
|
-
const applyWork = (secs, include) => !workToggle || !isWorkRepo ? secs : filterByOrigin(secs, include ? "work" : "home", isWorkRepo);
|
|
2087
|
-
const initialSections = applyWork(sections, includeWork);
|
|
2100
|
+
const applyWork = (secs) => includeWork === void 0 || !isWorkRepo ? secs : filterByOrigin(secs, includeWork ? "work" : "home", isWorkRepo);
|
|
2101
|
+
const initialSections = applyWork(sections);
|
|
2088
2102
|
const [localSections, setLocalSections] = useState(initialSections);
|
|
2089
2103
|
const [tabIdx, setTabIdx] = useState(0);
|
|
2090
2104
|
const [cursors, setCursors] = useState(
|
|
@@ -2120,7 +2134,7 @@ var BrowseScreen = ({
|
|
|
2120
2134
|
setViewStarts((p) => ({ ...p, [activeId]: 0 }));
|
|
2121
2135
|
}, [search]);
|
|
2122
2136
|
useEffect(() => {
|
|
2123
|
-
setLocalSections(applyWork(sections
|
|
2137
|
+
setLocalSections(applyWork(sections));
|
|
2124
2138
|
}, [sections]);
|
|
2125
2139
|
useEffect(() => {
|
|
2126
2140
|
setTabIdx((prev) => Math.min(prev, Math.max(0, localSections.length - 1)));
|
|
@@ -2314,15 +2328,6 @@ var BrowseScreen = ({
|
|
|
2314
2328
|
onRefresh?.();
|
|
2315
2329
|
return;
|
|
2316
2330
|
}
|
|
2317
|
-
if (workToggle && input === "w") {
|
|
2318
|
-
const next = !includeWork;
|
|
2319
|
-
const nextSections = applyWork(sections, next);
|
|
2320
|
-
const keptIdx = nextSections.findIndex((s) => s.id === activeId);
|
|
2321
|
-
setIncludeWork(next);
|
|
2322
|
-
setLocalSections(nextSections);
|
|
2323
|
-
if (keptIdx >= 0) setTabIdx(keptIdx);
|
|
2324
|
-
return;
|
|
2325
|
-
}
|
|
2326
2331
|
const ext = extensionFor(input, extensions);
|
|
2327
2332
|
if (ext) {
|
|
2328
2333
|
onOpenExt?.(ext.id, {
|
|
@@ -2413,6 +2418,11 @@ var BrowseScreen = ({
|
|
|
2413
2418
|
}
|
|
2414
2419
|
return;
|
|
2415
2420
|
}
|
|
2421
|
+
if (input === "u" && (activeItem.kind === "pr" || activeItem.kind === "issue")) {
|
|
2422
|
+
showFlash(`\u22EF Unsubscribing from #${activeItem.number}\u2026`);
|
|
2423
|
+
void unsubscribeFrom(activeItem).then(() => showFlash(`\u2713 Unsubscribed from #${activeItem.number}`)).catch(() => showFlash(`\u2717 Unsubscribe failed for #${activeItem.number}`));
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2416
2426
|
if (input === "b" && activeItem.kind === "pr" && activeItem.branch) {
|
|
2417
2427
|
clipboard(activeItem.branch);
|
|
2418
2428
|
showFlash(`\u2713 Copied ${activeItem.branch}`);
|
|
@@ -2494,7 +2504,6 @@ var BrowseScreen = ({
|
|
|
2494
2504
|
const overlay = help ? /* @__PURE__ */ jsx(
|
|
2495
2505
|
HelpModal,
|
|
2496
2506
|
{
|
|
2497
|
-
workToggle,
|
|
2498
2507
|
extensions,
|
|
2499
2508
|
hasJira: !!jiraBase,
|
|
2500
2509
|
tabHelp,
|
|
@@ -2526,7 +2535,7 @@ var BrowseScreen = ({
|
|
|
2526
2535
|
brand,
|
|
2527
2536
|
sections: localSections,
|
|
2528
2537
|
login,
|
|
2529
|
-
work:
|
|
2538
|
+
work: includeWork,
|
|
2530
2539
|
refreshing,
|
|
2531
2540
|
hasPending,
|
|
2532
2541
|
pendingSummary,
|
|
@@ -2638,11 +2647,10 @@ var App = ({
|
|
|
2638
2647
|
title = "inbox",
|
|
2639
2648
|
detailFor,
|
|
2640
2649
|
isWorkRepo,
|
|
2641
|
-
|
|
2650
|
+
includeWork,
|
|
2642
2651
|
jiraBase,
|
|
2643
2652
|
jiraKeyRe,
|
|
2644
2653
|
jiraTransitions,
|
|
2645
|
-
workToggle,
|
|
2646
2654
|
hasCiStatus,
|
|
2647
2655
|
ciJob,
|
|
2648
2656
|
ciFetcher,
|
|
@@ -2851,7 +2859,7 @@ var App = ({
|
|
|
2851
2859
|
brand: brandOf(title),
|
|
2852
2860
|
sections: [],
|
|
2853
2861
|
login: "",
|
|
2854
|
-
work:
|
|
2862
|
+
work: state.phase === "loading" ? includeWork : void 0,
|
|
2855
2863
|
loading: state.phase === "loading",
|
|
2856
2864
|
quiet: state.phase !== "loading"
|
|
2857
2865
|
}
|
|
@@ -2910,9 +2918,8 @@ var App = ({
|
|
|
2910
2918
|
pendingSummary,
|
|
2911
2919
|
fetchedAt,
|
|
2912
2920
|
refreshError: refreshError ?? void 0,
|
|
2913
|
-
workToggle,
|
|
2914
2921
|
isWorkRepo,
|
|
2915
|
-
|
|
2922
|
+
includeWork,
|
|
2916
2923
|
hidden: overlay !== null,
|
|
2917
2924
|
mergedUrls,
|
|
2918
2925
|
transients,
|
|
@@ -2955,4 +2962,18 @@ var App = ({
|
|
|
2955
2962
|
] });
|
|
2956
2963
|
};
|
|
2957
2964
|
|
|
2958
|
-
|
|
2965
|
+
// src/inbox/filter.ts
|
|
2966
|
+
var toRegExp = (pattern) => {
|
|
2967
|
+
const full = pattern.includes("/") ? pattern : `${pattern}/*`;
|
|
2968
|
+
const escaped = full.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
2969
|
+
return new RegExp(`^${escaped.replace(/\*/g, "[^/]*")}$`, "i");
|
|
2970
|
+
};
|
|
2971
|
+
var matchesAny = (repo, patterns) => patterns.some((p) => toRegExp(p).test(repo));
|
|
2972
|
+
var matchesFilter = (repo, filter) => {
|
|
2973
|
+
const { include = [], exclude = [] } = filter;
|
|
2974
|
+
if (include.length > 0 && !matchesAny(repo, include)) return false;
|
|
2975
|
+
return !matchesAny(repo, exclude);
|
|
2976
|
+
};
|
|
2977
|
+
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
2978
|
+
|
|
2979
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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",
|