@kud/gh-ink 0.21.0 → 0.22.1

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 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, initialIncludeWork, jiraBase, jiraKeyRe, jiraTransitions, workToggle, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, extensions, tabHelp, emptyHint, }: {
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
- initialIncludeWork?: boolean;
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>;
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, Switch, StatusMessage, FooterHints, useListCursor, Tabs } from '@kud/ink-ui';
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';
@@ -915,7 +915,7 @@ var STANDING = {
915
915
  reviewed: "spoken"
916
916
  };
917
917
  var YOURS = {
918
- authored: ["ci-fail", "conflict", "changes-req", "threads", "approved"],
918
+ authored: ["ci-fail", "conflict", "changes-req", "threads", "approved", "draft"],
919
919
  queued: ["waiting", "pending", "threads", "approved"],
920
920
  spoken: ["threads"]
921
921
  };
@@ -1285,6 +1285,20 @@ var drillLabel = (item) => {
1285
1285
  if (item.kind === "pr") return "Open PR";
1286
1286
  return "Drill in";
1287
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
+ };
1288
1302
  var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions, onRefresh, onRemove, onOpenView, ext) => {
1289
1303
  if (item.kind === "repo-header" || item.kind === "subgroup-header" || item.kind === "show-more" || item.kind === "show-less")
1290
1304
  return [];
@@ -1370,6 +1384,14 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
1370
1384
  showFlash(`\u2713 Copied ${item.repo}`);
1371
1385
  }
1372
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
+ });
1373
1395
  if (item.kind === "pr" && item.branch) {
1374
1396
  actions.push({
1375
1397
  label: "Copy branch name",
@@ -1557,7 +1579,7 @@ var InboxHeader = ({
1557
1579
  const total = sections.reduce((n, s) => n + topLevelCount(s), 0);
1558
1580
  const countSeg = loading ? " loading\u2026 " : quiet ? " " : ` ${String(total).padStart(3)} item${total !== 1 ? "s" : ""} \xB7 `;
1559
1581
  const userSeg = loading || quiet ? "" : `@${login} `;
1560
- const workLabel = work === void 0 ? "" : " w work \u25CF\u2500\u25CB home ";
1582
+ const workLabel = work === void 0 ? "" : ` ${work ? "work" : "home"} `;
1561
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];
1562
1584
  const statusSeg = statusText ? statusText + " " : "";
1563
1585
  const fill = Math.max(
@@ -1568,11 +1590,7 @@ var InboxHeader = ({
1568
1590
  /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: true, children: brand }),
1569
1591
  /* @__PURE__ */ jsx(Text2, { dimColor: true, children: countSeg }),
1570
1592
  userSeg ? /* @__PURE__ */ jsx(Text2, { children: userSeg }) : null,
1571
- work !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1572
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " w " }),
1573
- /* @__PURE__ */ jsx(Switch, { left: "work", right: "home", value: work ? "left" : "right" }),
1574
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " })
1575
- ] }) : null,
1593
+ work !== void 0 ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: workLabel }) : null,
1576
1594
  statusText ? /* @__PURE__ */ jsx(
1577
1595
  Text2,
1578
1596
  {
@@ -1858,7 +1876,6 @@ var LegendGroup = ({
1858
1876
  ] });
1859
1877
  var LEGEND_FOOTNOTE = "Each item lands in the FIRST tab that claims it, so counts are residuals rather than totals.";
1860
1878
  var HelpModal = ({
1861
- workToggle,
1862
1879
  extensions,
1863
1880
  hasJira,
1864
1881
  tabHelp,
@@ -1883,11 +1900,11 @@ var HelpModal = ({
1883
1900
  ["s", "switch to branch here"],
1884
1901
  ["j", "open repo in new tab"],
1885
1902
  ["p", "open repo in new pane"],
1903
+ ["u", "unsubscribe from this item"],
1886
1904
  ...hasJira ? [["t", "Jira: move / open ticket"]] : [],
1887
1905
  ["/", "search"],
1888
1906
  ["f", "filter by repo"],
1889
1907
  ["r", "refresh"],
1890
- ...workToggle ? [["w", "toggle work / home"]] : [],
1891
1908
  ...extensionLegend(extensions),
1892
1909
  ["?", "this help"],
1893
1910
  ["q", "quit"]
@@ -2064,7 +2081,6 @@ var BrowseScreen = ({
2064
2081
  pendingSummary,
2065
2082
  fetchedAt,
2066
2083
  refreshError,
2067
- workToggle,
2068
2084
  hidden,
2069
2085
  onOpenPr,
2070
2086
  onOpenIssue,
@@ -2074,18 +2090,15 @@ var BrowseScreen = ({
2074
2090
  ciJob,
2075
2091
  tabHelp,
2076
2092
  isWorkRepo,
2077
- initialIncludeWork,
2093
+ includeWork,
2078
2094
  brand,
2079
2095
  mergedUrls,
2080
2096
  transients,
2081
2097
  onTabChange
2082
2098
  }) => {
2083
2099
  const { rows } = useWindowSize();
2084
- const [includeWork, setIncludeWork] = useState(
2085
- () => workToggle ? initialIncludeWork ?? true : true
2086
- );
2087
- const applyWork = (secs, include) => !workToggle || !isWorkRepo ? secs : filterByOrigin(secs, include ? "work" : "home", isWorkRepo);
2088
- 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);
2089
2102
  const [localSections, setLocalSections] = useState(initialSections);
2090
2103
  const [tabIdx, setTabIdx] = useState(0);
2091
2104
  const [cursors, setCursors] = useState(
@@ -2121,7 +2134,7 @@ var BrowseScreen = ({
2121
2134
  setViewStarts((p) => ({ ...p, [activeId]: 0 }));
2122
2135
  }, [search]);
2123
2136
  useEffect(() => {
2124
- setLocalSections(applyWork(sections, includeWork));
2137
+ setLocalSections(applyWork(sections));
2125
2138
  }, [sections]);
2126
2139
  useEffect(() => {
2127
2140
  setTabIdx((prev) => Math.min(prev, Math.max(0, localSections.length - 1)));
@@ -2315,15 +2328,6 @@ var BrowseScreen = ({
2315
2328
  onRefresh?.();
2316
2329
  return;
2317
2330
  }
2318
- if (workToggle && input === "w") {
2319
- const next = !includeWork;
2320
- const nextSections = applyWork(sections, next);
2321
- const keptIdx = nextSections.findIndex((s) => s.id === activeId);
2322
- setIncludeWork(next);
2323
- setLocalSections(nextSections);
2324
- if (keptIdx >= 0) setTabIdx(keptIdx);
2325
- return;
2326
- }
2327
2331
  const ext = extensionFor(input, extensions);
2328
2332
  if (ext) {
2329
2333
  onOpenExt?.(ext.id, {
@@ -2414,6 +2418,11 @@ var BrowseScreen = ({
2414
2418
  }
2415
2419
  return;
2416
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
+ }
2417
2426
  if (input === "b" && activeItem.kind === "pr" && activeItem.branch) {
2418
2427
  clipboard(activeItem.branch);
2419
2428
  showFlash(`\u2713 Copied ${activeItem.branch}`);
@@ -2495,7 +2504,6 @@ var BrowseScreen = ({
2495
2504
  const overlay = help ? /* @__PURE__ */ jsx(
2496
2505
  HelpModal,
2497
2506
  {
2498
- workToggle,
2499
2507
  extensions,
2500
2508
  hasJira: !!jiraBase,
2501
2509
  tabHelp,
@@ -2527,7 +2535,7 @@ var BrowseScreen = ({
2527
2535
  brand,
2528
2536
  sections: localSections,
2529
2537
  login,
2530
- work: workToggle ? includeWork : void 0,
2538
+ work: includeWork,
2531
2539
  refreshing,
2532
2540
  hasPending,
2533
2541
  pendingSummary,
@@ -2639,11 +2647,10 @@ var App = ({
2639
2647
  title = "inbox",
2640
2648
  detailFor,
2641
2649
  isWorkRepo,
2642
- initialIncludeWork,
2650
+ includeWork,
2643
2651
  jiraBase,
2644
2652
  jiraKeyRe,
2645
2653
  jiraTransitions,
2646
- workToggle,
2647
2654
  hasCiStatus,
2648
2655
  ciJob,
2649
2656
  ciFetcher,
@@ -2852,7 +2859,7 @@ var App = ({
2852
2859
  brand: brandOf(title),
2853
2860
  sections: [],
2854
2861
  login: "",
2855
- work: workToggle && state.phase === "loading" ? initialIncludeWork ?? true : void 0,
2862
+ work: state.phase === "loading" ? includeWork : void 0,
2856
2863
  loading: state.phase === "loading",
2857
2864
  quiet: state.phase !== "loading"
2858
2865
  }
@@ -2911,9 +2918,8 @@ var App = ({
2911
2918
  pendingSummary,
2912
2919
  fetchedAt,
2913
2920
  refreshError: refreshError ?? void 0,
2914
- workToggle,
2915
2921
  isWorkRepo,
2916
- initialIncludeWork,
2922
+ includeWork,
2917
2923
  hidden: overlay !== null,
2918
2924
  mergedUrls,
2919
2925
  transients,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
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",