@kud/gh-ink 0.64.0 → 0.65.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
@@ -543,18 +543,20 @@ declare const reposInSections: (sections: Section[]) => string[];
543
543
  /**
544
544
  * The next selectable row in `dir`, or `current` when there is none.
545
545
  *
546
- * A repo-header is a STOP, and a subgroup-header is not. The two look alike and
547
- * are not: the fence names a thing you can act on — open the checkout, copy every
548
- * URL under it — where a band label ("Your move") names an arrangement of rows
549
- * and has nothing behind it to open. Landing on the second would be a keystroke
550
- * spent on a row whose every key is inert.
546
+ * Both header kinds are STOPS. A repo-header names a thing you can act on —
547
+ * open the checkout, copy every URL under it — and a band header
548
+ * ("» Your move") does now too: `c` copies the whole band. Landing on one
549
+ * used to be a keystroke spent on a row whose every key was inert, which is
550
+ * why `moveCursor` stepped over subgroup-headers; now that the key does
551
+ * something there, skipping it would hide the row that says how many links
552
+ * the key will take.
551
553
  *
552
- * Unconditional in both directions, deliberately. Skipping the fence going down
553
- * and landing on it going up would make ↓ then ↑ end somewhere other than where
554
- * it started, and an arrow pair that is not its own inverse reads as the list
555
- * drifting rather than as a shortcut. The cost is one press per repo group; the
556
- * fence is also the "you have crossed into another repo" beat the eye takes
557
- * anyway, so the press buys orientation as well as position.
554
+ * Unconditional in both directions, deliberately. Skipping a header going
555
+ * down and landing on it going up would make ↓ then ↑ end somewhere other
556
+ * than where it started, and an arrow pair that is not its own inverse reads
557
+ * as the list drifting rather than as a shortcut. The cost is one press per
558
+ * header; each is also the "you have crossed into another group" beat the eye
559
+ * takes anyway, so the press buys orientation as well as position.
558
560
  */
559
561
  declare const moveCursor: (items: AnyItem[], current: number, dir: 1 | -1) => number;
560
562
  /**
package/dist/index.js CHANGED
@@ -1524,9 +1524,7 @@ var reposInSections = (sections) => [
1524
1524
  )
1525
1525
  ].sort();
1526
1526
  var moveCursor = (items, current, dir) => {
1527
- let next = current + dir;
1528
- while (next >= 0 && next < items.length && items[next].kind === "subgroup-header")
1529
- next += dir;
1527
+ const next = current + dir;
1530
1528
  if (next < 0 || next >= items.length) return current;
1531
1529
  return next;
1532
1530
  };
@@ -1602,6 +1600,28 @@ var treeUrls = (items, i) => {
1602
1600
  }
1603
1601
  return urls;
1604
1602
  };
1603
+ var bandUrls = (items, i) => {
1604
+ const urls = [];
1605
+ for (let j = i + 1; j < items.length; j += 1) {
1606
+ const item = items[j];
1607
+ if (item.kind === "subgroup-header") break;
1608
+ if (item.kind === "show-more") {
1609
+ for (const child of item.hidden) if (child.url) urls.push(child.url);
1610
+ continue;
1611
+ }
1612
+ const url = item.url;
1613
+ if (url) urls.push(url);
1614
+ }
1615
+ return urls;
1616
+ };
1617
+ var copyUrls = (items, i) => {
1618
+ const item = items[i];
1619
+ if (!item) return [];
1620
+ if (item.kind === "repo-header") return groupUrls(items, i);
1621
+ if (item.kind === "subgroup-header") return bandUrls(items, i);
1622
+ const url = item.url;
1623
+ return url ? [url] : [];
1624
+ };
1605
1625
  var gapsAbove = (items, i) => {
1606
1626
  const item = items[i];
1607
1627
  if (!item || i === 0) return false;
@@ -2381,7 +2401,8 @@ var ItemRow = ({
2381
2401
  return /* @__PURE__ */ jsx(RepoHeaderRow, { repo: item.repo, gap: gap ?? false, active });
2382
2402
  if (item.kind === "subgroup-header")
2383
2403
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
2384
- /* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: true, children: " \xBB " }),
2404
+ /* @__PURE__ */ jsx(Text3, { color: colors.info, children: active ? "\u276F " : " " }),
2405
+ /* @__PURE__ */ jsx(Text3, { color: colors.accent, bold: true, children: "\xBB " }),
2385
2406
  /* @__PURE__ */ jsx(Text3, { bold: true, children: item.label })
2386
2407
  ] });
2387
2408
  if (item.kind === "show-more")
@@ -2879,17 +2900,7 @@ var BrowseScreen = ({
2879
2900
  s.items.length - 1
2880
2901
  );
2881
2902
  if (c < 0) return [s.id, 0];
2882
- return [
2883
- s.id,
2884
- // Only `subgroup-header`, which is the one kind the arrows refuse to
2885
- // stand on — `moveCursor` skips it and nothing else. A REPO header is
2886
- // selectable on purpose: it opens the repo and `C` copies the group,
2887
- // and the footer advertises both. Stepping off it here meant that
2888
- // standing on a repo and pressing `r` moved you onto an issue, every
2889
- // single time, and the rule predated repo headers being selectable
2890
- // at all.
2891
- s.items[c]?.kind === "subgroup-header" ? moveCursor(s.items, c, 1) : c
2892
- ];
2903
+ return [s.id, c];
2893
2904
  })
2894
2905
  )
2895
2906
  );
@@ -3226,8 +3237,10 @@ var BrowseScreen = ({
3226
3237
  return;
3227
3238
  }
3228
3239
  if (input === "c") {
3229
- clipboard(repoUrl);
3230
- showFlash(`\u2713 Copied URL for ${repo}`);
3240
+ const urls = copyUrls(section.items, cursor);
3241
+ if (urls.length === 0) return;
3242
+ clipboard(urls.join("\n"));
3243
+ showFlash(`\u2713 Copied ${urls.length} link${urls.length === 1 ? "" : "s"}`);
3231
3244
  return;
3232
3245
  }
3233
3246
  if (input === "C" || input === "O") {
@@ -3246,7 +3259,16 @@ var BrowseScreen = ({
3246
3259
  }
3247
3260
  return;
3248
3261
  }
3249
- if (!activeItem || activeItem.kind === "subgroup-header") return;
3262
+ if (activeItem?.kind === "subgroup-header") {
3263
+ if (input === "c") {
3264
+ const urls = copyUrls(section.items, cursor);
3265
+ if (urls.length === 0) return;
3266
+ clipboard(urls.join("\n"));
3267
+ showFlash(`\u2713 Copied ${urls.length} link${urls.length === 1 ? "" : "s"}`);
3268
+ }
3269
+ return;
3270
+ }
3271
+ if (!activeItem) return;
3250
3272
  if (key.return && activeItem.kind === "show-more") {
3251
3273
  setLocalSections(
3252
3274
  (prev) => prev.map((s) => ({
@@ -3438,6 +3460,11 @@ var BrowseScreen = ({
3438
3460
  menu.open(actions);
3439
3461
  }
3440
3462
  });
3463
+ const headerLinks = activeItem?.kind === "repo-header" || activeItem?.kind === "subgroup-header" ? copyUrls(section.items, cursor).length : 0;
3464
+ const headerHint = [
3465
+ "c",
3466
+ `copy ${headerLinks} link${headerLinks === 1 ? "" : "s"}`
3467
+ ];
3441
3468
  const hints = railActive ? (
3442
3469
  // The rail's own keymap while it holds focus, not the list's with a line
3443
3470
  // bolted on. A footer advertising `m actions` beside a cursor that cannot
@@ -3457,7 +3484,13 @@ var BrowseScreen = ({
3457
3484
  ["\u2190\u2192", "tab"],
3458
3485
  ["\u21B5", "open repo"],
3459
3486
  ["o", "browser"],
3460
- ["C", "copy group"],
3487
+ headerHint,
3488
+ ["?", "help"],
3489
+ ["q", "quit"]
3490
+ ] : activeItem?.kind === "subgroup-header" ? [
3491
+ ["\u2191\u2193", "nav"],
3492
+ ["\u2190\u2192", "tab"],
3493
+ headerHint,
3461
3494
  ["?", "help"],
3462
3495
  ["q", "quit"]
3463
3496
  ] : [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.64.0",
3
+ "version": "0.65.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",