@kud/gh-ink 0.62.1 → 0.63.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 CHANGED
@@ -486,6 +486,12 @@ type DetailContext = {
486
486
  */
487
487
  onTyping?: (typing: boolean) => void;
488
488
  };
489
+ type SettledResult = {
490
+ ok: true;
491
+ } | {
492
+ ok: false;
493
+ reason: string;
494
+ };
489
495
  type Action = {
490
496
  label: string;
491
497
  hint: string;
@@ -624,6 +630,19 @@ declare const buildActions: (item: AnyItem, login: string, showFlash: (msg: stri
624
630
  * the drop stops the pre-action cache outliving a quit inside that window.
625
631
  */
626
632
  onActed?: () => void;
633
+ /**
634
+ * A mutation settled (succeeded or failed). Called exactly once per
635
+ * invocation of the three optimistic actions: "Remove me as reviewer",
636
+ * "Close issue" → "Close #N", and "Close PR" → "Close #N".
637
+ *
638
+ * On success: `{ ok: true }`. On failure: `{ ok: false; reason }` where
639
+ * `reason` is the first line of the error message/stderr, trimmed and
640
+ * non-empty.
641
+ *
642
+ * When absent, behaviour is identical — the host simply does not receive
643
+ * the settlement signal.
644
+ */
645
+ onSettled?: (item: GHItem, result: SettledResult) => void;
627
646
  }) => Action[];
628
647
  type CiStatusState = {
629
648
  kind: "loading";
@@ -665,7 +684,7 @@ declare const ActionMenu: ({ item, actions, cursor, }: {
665
684
  cursor: number;
666
685
  }) => React.JSX.Element;
667
686
  declare const signatureOf: (sections: Section[]) => string;
668
- declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraSetupHint, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, stripFetcher, stripLabel, stripPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, liveLabel, }: {
687
+ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraSetupHint, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, stripFetcher, stripLabel, stripPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, liveLabel, onSettled, }: {
669
688
  fetcher: () => Promise<{
670
689
  sections: Section[];
671
690
  login: string;
@@ -739,6 +758,19 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jir
739
758
  */
740
759
  watchJitterMs?: number;
741
760
  extensions?: InboxExtension[];
761
+ /**
762
+ * A mutation settled (succeeded or failed). Called exactly once per
763
+ * invocation of the three optimistic actions: "Remove me as reviewer",
764
+ * "Close issue" → "Close #N", and "Close PR" → "Close #N".
765
+ *
766
+ * On success: `{ ok: true }`. On failure: `{ ok: false; reason }` where
767
+ * `reason` is the first line of the error message/stderr, trimmed and
768
+ * non-empty.
769
+ *
770
+ * When absent, behaviour is identical — the host simply does not receive
771
+ * the settlement signal.
772
+ */
773
+ onSettled?: (item: GHItem, result: SettledResult) => void;
742
774
  }) => React.JSX.Element;
743
775
 
744
776
  type RepoFilter = {
@@ -760,4 +792,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
760
792
  */
761
793
  declare const parsePatterns: (value: string) => string[];
762
794
 
763
- export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, type Command, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type LiveLabel, MIN_SIDEBAR_COLS, PrRow, type PrRowProps, type Rails, type RepoFilter, type RowAnnouncement, type RowMotion, SIDEBAR_COLS, SidePanel, type Sidebar, type SidebarRow, type StatusStrip, StatusStripLine, type StripItem, type StripState, type StripStatusState, type TrailingColumns, type TrailingShed, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
795
+ export { type Action, ActionMenu, App, COLS, CiStatusLine, type CiStatusState, type Command, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxExtension, type JiraTransition, type LiveLabel, MIN_SIDEBAR_COLS, PrRow, type PrRowProps, type Rails, type RepoFilter, type RowAnnouncement, type RowMotion, SIDEBAR_COLS, type SettledResult, SidePanel, type Sidebar, type SidebarRow, type StatusStrip, StatusStripLine, type StripItem, type StripState, type StripStatusState, type TrailingColumns, type TrailingShed, ageLabel, budgetNotice, buildActions, buildCheckoutCmd, clipboard, counts, drillCmd, explainGhAction, explainItem, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, itermRun, jumpToRepo, jumpToRepoPane, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, railCapacity, railHeights, railRowsOf, railWidth, railsOf, readCache, renderMarkdown, reposInSections, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, sectionShedOf, signatureOf, stripGlyph, stripLayout, stripSignature, toCiStatusState, trailingColumnsOf, truncate, useActionMenu, windowCount, withHeaders, withoutItem, workCount, writeCache };
package/dist/index.js CHANGED
@@ -1875,6 +1875,12 @@ var explainGhAction = (e) => {
1875
1875
  const human = GH_ACTION_FAILURES.find(([re]) => re.test(raw))?.[1];
1876
1876
  return human ? `${human} (${raw})` : raw;
1877
1877
  };
1878
+ var firstLine = (e) => {
1879
+ const err = e;
1880
+ const raw = (err.stderr ?? err.message ?? err.stdout ?? String(e)).trim();
1881
+ const line = raw.split("\n").find((l) => l.trim().length > 0);
1882
+ return (line?.trim() ?? "failed").slice(0, 200);
1883
+ };
1878
1884
  var unsubscribeFrom = async (item) => {
1879
1885
  const route = item.kind === "pr" ? "pulls" : "issues";
1880
1886
  const found = await quietly`gh api repos/${item.repo}/${route}/${item.number} --jq .node_id`;
@@ -1979,13 +1985,18 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
1979
1985
  run: () => {
1980
1986
  onRemove?.(item);
1981
1987
  showFlash(`\u22EF Removing you from #${item.number}\u2026`);
1982
- void quietly`gh pr edit ${item.number} --repo ${item.repo} --remove-reviewer ${login}`.then(() => {
1983
- showFlash(`\u2713 Removed you as reviewer on #${item.number}`);
1984
- ext?.onActed?.();
1985
- }).catch((e) => {
1986
- showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`);
1987
- onRefresh?.();
1988
- });
1988
+ void quietly`gh pr edit ${item.number} --repo ${item.repo} --remove-reviewer ${login}`.then(
1989
+ () => {
1990
+ showFlash(`\u2713 Removed you as reviewer on #${item.number}`);
1991
+ ext?.onActed?.();
1992
+ ext?.onSettled?.(item, { ok: true });
1993
+ },
1994
+ (e) => {
1995
+ showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`);
1996
+ onRefresh?.();
1997
+ ext?.onSettled?.(item, { ok: false, reason: firstLine(e) });
1998
+ }
1999
+ );
1989
2000
  }
1990
2001
  });
1991
2002
  }
@@ -2058,10 +2069,14 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
2058
2069
  run: () => {
2059
2070
  onRemove?.(item);
2060
2071
  showFlash(`\u2713 Closed #${item.number}`);
2061
- void quietly`gh issue close ${item.number} --repo ${item.repo}`.catch(
2072
+ void quietly`gh issue close ${item.number} --repo ${item.repo}`.then(
2062
2073
  () => {
2074
+ ext?.onSettled?.(item, { ok: true });
2075
+ },
2076
+ (e) => {
2063
2077
  showFlash(`\u2717 Close failed \u2014 restoring #${item.number}`);
2064
2078
  onRefresh?.();
2079
+ ext?.onSettled?.(item, { ok: false, reason: firstLine(e) });
2065
2080
  }
2066
2081
  );
2067
2082
  }
@@ -2110,10 +2125,14 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
2110
2125
  run: () => {
2111
2126
  onRemove?.(item);
2112
2127
  showFlash(`\u2713 Closed #${item.number}`);
2113
- void quietly`gh pr close ${item.number} --repo ${item.repo}`.catch(
2128
+ void quietly`gh pr close ${item.number} --repo ${item.repo}`.then(
2114
2129
  () => {
2130
+ ext?.onSettled?.(item, { ok: true });
2131
+ },
2132
+ (e) => {
2115
2133
  showFlash(`\u2717 Close failed \u2014 restoring #${item.number}`);
2116
2134
  onRefresh?.();
2135
+ ext?.onSettled?.(item, { ok: false, reason: firstLine(e) });
2117
2136
  }
2118
2137
  );
2119
2138
  }
@@ -2751,6 +2770,7 @@ var BrowseScreen = ({
2751
2770
  jiraTransitions,
2752
2771
  onRefresh,
2753
2772
  onActed,
2773
+ onSettled,
2754
2774
  refreshing,
2755
2775
  hasPending,
2756
2776
  pendingSummary,
@@ -3029,7 +3049,7 @@ var BrowseScreen = ({
3029
3049
  onRefresh,
3030
3050
  dismissItem,
3031
3051
  openDrillView,
3032
- { extensions, onOpenExt, onActed }
3052
+ { extensions, onOpenExt, onActed, onSettled }
3033
3053
  );
3034
3054
  menu.open(actions);
3035
3055
  };
@@ -3310,13 +3330,18 @@ var BrowseScreen = ({
3310
3330
  if (input === "x" && activeItem.kind === "pr" && activeItem.standing === "queued" && login) {
3311
3331
  dismissItem(activeItem);
3312
3332
  showFlash(`\u22EF Removing you from #${activeItem.number}\u2026`);
3313
- void quietly`gh pr edit ${activeItem.number} --repo ${activeItem.repo} --remove-reviewer ${login}`.then(() => {
3314
- showFlash(`\u2713 Removed you as reviewer on #${activeItem.number}`);
3315
- onActed?.();
3316
- }).catch((e) => {
3317
- showFlash(`\u2717 #${activeItem.number}: ${explainGhAction(e)}`);
3318
- onRefresh?.();
3319
- });
3333
+ void quietly`gh pr edit ${activeItem.number} --repo ${activeItem.repo} --remove-reviewer ${login}`.then(
3334
+ () => {
3335
+ showFlash(`\u2713 Removed you as reviewer on #${activeItem.number}`);
3336
+ onActed?.();
3337
+ onSettled?.(activeItem, { ok: true });
3338
+ },
3339
+ (e) => {
3340
+ showFlash(`\u2717 #${activeItem.number}: ${explainGhAction(e)}`);
3341
+ onRefresh?.();
3342
+ onSettled?.(activeItem, { ok: false, reason: firstLine(e) });
3343
+ }
3344
+ );
3320
3345
  return;
3321
3346
  }
3322
3347
  if (input === "b" && activeItem.kind === "pr" && activeItem.branch) {
@@ -3744,7 +3769,8 @@ var App = ({
3744
3769
  extensions,
3745
3770
  tabHelp,
3746
3771
  emptyHint,
3747
- liveLabel
3772
+ liveLabel,
3773
+ onSettled
3748
3774
  }) => {
3749
3775
  const { rows } = useWindowSize();
3750
3776
  const [state, setState] = useState({ phase: "loading" });
@@ -4155,6 +4181,7 @@ var App = ({
4155
4181
  jiraTransitions,
4156
4182
  onRefresh: applyOrRefresh,
4157
4183
  onActed,
4184
+ onSettled,
4158
4185
  refreshing,
4159
4186
  hasPending: pending !== null,
4160
4187
  pendingSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.62.1",
3
+ "version": "0.63.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",