@kud/gh-ink 0.16.1 → 0.16.3

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.
Files changed (2) hide show
  1. package/dist/index.js +81 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1644,7 +1644,8 @@ var ItemRow = ({
1644
1644
  const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
1645
1645
  const icon = merged ? MERGED_FRAMES[sparkFrame % MERGED_FRAMES.length] : transient === "out" ? TRANSIT_OUT_FRAMES[sparkFrame % TRANSIT_OUT_FRAMES.length] : transient === "in" ? TRANSIT_IN_FRAMES[sparkFrame % TRANSIT_IN_FRAMES.length] : healthIcon;
1646
1646
  const color = merged ? MERGED_COLOUR : transient ? TRANSIT_COLOUR[transient] : healthColor2;
1647
- const [turnIcon, turnColor] = !login || !item.lastActor ? [" ", "white"] : item.lastActor === login ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
1647
+ const spokeLast = !!login && !!item.lastActor && item.lastActor === login;
1648
+ const [turnIcon, turnColor] = !login || !item.lastActor ? [" ", "white"] : spokeLast ? ["\u2192", "#888888"] : ["\u2190", "#FF8700"];
1648
1649
  const numStr = `#${item.number}`.padEnd(7);
1649
1650
  const showAuthor = !!item.author && item.author !== login;
1650
1651
  const unresolvedLabel = item.unresolved > 0 ? `\uF086 ${item.unresolved}` : "";
@@ -1677,7 +1678,7 @@ var ItemRow = ({
1677
1678
  }
1678
1679
  ),
1679
1680
  repoLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: repoLabel }) : null,
1680
- unresolvedLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: "#FF8700", children: " " + unresolvedLabel }) : null,
1681
+ unresolvedLabel ? /* @__PURE__ */ jsx(Text2, { bold: !spokeLast, color: spokeLast ? "#888888" : "#FF8700", children: " " + unresolvedLabel }) : null,
1681
1682
  showAuthor ? /* @__PURE__ */ jsx(Text2, { dimColor: true, italic: true, children: " by " + item.author }) : null,
1682
1683
  ageLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + ageLabel }) : null,
1683
1684
  mergedLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: MERGED_COLOUR, children: " " + mergedLabel }) : null,
@@ -1760,35 +1761,48 @@ var legendLayout = (cols, available) => {
1760
1761
  if (layoutWidth(stacked) <= available) return stacked;
1761
1762
  return [cols];
1762
1763
  };
1764
+ var groupLines = (group) => group.flatMap((col, index) => {
1765
+ const cellWidth = cellWidthOf(col);
1766
+ const head = index === 0 ? [] : [{ kind: "gap" }];
1767
+ return [
1768
+ ...head,
1769
+ { kind: "title", text: col.title },
1770
+ ...col.rows.map((row) => ({ kind: "row", row, cellWidth }))
1771
+ ];
1772
+ });
1773
+ var scrolledPastTitle = (lines, offset) => {
1774
+ let seen = null;
1775
+ for (let i = 0; i <= offset && i < lines.length; i++) {
1776
+ const line = lines[i];
1777
+ if (line.kind === "title") seen = i === offset ? null : line.text;
1778
+ }
1779
+ return seen;
1780
+ };
1763
1781
  var LegendGroup = ({
1764
- group,
1782
+ lines,
1783
+ pinned,
1765
1784
  width,
1766
1785
  marginRight
1767
- }) => /* @__PURE__ */ jsx(Box, { flexDirection: "column", width, marginRight, children: group.map((col, index) => {
1768
- const cellWidth = cellWidthOf(col);
1769
- return /* @__PURE__ */ jsxs(
1770
- Box,
1771
- {
1772
- flexDirection: "column",
1773
- marginTop: index === 0 ? 0 : 1,
1774
- children: [
1775
- /* @__PURE__ */ jsx(Text2, { bold: true, dimColor: true, children: col.title }),
1776
- col.rows.map((row) => /* @__PURE__ */ jsxs(Box, { children: [
1777
- /* @__PURE__ */ jsx(Text2, { color: row.color, bold: row.bold, children: row.cell.padEnd(cellWidth) }),
1778
- /* @__PURE__ */ jsx(Text2, { wrap: "truncate-end", children: row.label })
1779
- ] }, col.title + row.cell + row.label))
1780
- ]
1781
- },
1782
- col.title
1783
- );
1784
- }) });
1786
+ }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, marginRight, children: [
1787
+ pinned !== void 0 ? /* @__PURE__ */ jsx(Text2, { bold: true, dimColor: true, children: pinned ?? " " }) : null,
1788
+ lines.map(
1789
+ (line, index) => line.kind === "gap" ? /* @__PURE__ */ jsx(Text2, { children: " " }, index) : line.kind === "title" ? /* @__PURE__ */ jsx(Text2, { bold: true, dimColor: true, children: line.text }, index) : /* @__PURE__ */ jsxs(Box, { children: [
1790
+ /* @__PURE__ */ jsx(Text2, { color: line.row.color, bold: line.row.bold, children: line.row.cell.padEnd(line.cellWidth) }),
1791
+ /* @__PURE__ */ jsx(Text2, { wrap: "truncate-end", children: line.row.label })
1792
+ ] }, index)
1793
+ )
1794
+ ] });
1795
+ var LEGEND_FOOTNOTE = "Each item lands in the FIRST tab that claims it, so counts are residuals rather than totals.";
1785
1796
  var HelpModal = ({
1786
1797
  workToggle,
1787
1798
  extensions,
1788
1799
  hasJira,
1789
- tabHelp
1800
+ tabHelp,
1801
+ maxRows,
1802
+ scroll = 0,
1803
+ onScrollRange
1790
1804
  }) => {
1791
- const { columns } = useWindowSize();
1805
+ const { columns, rows } = useWindowSize();
1792
1806
  const available = Math.max(
1793
1807
  20,
1794
1808
  columns - FRAME_CHROME_COLS - MODAL_CHROME_COLS
@@ -1857,6 +1871,28 @@ var HelpModal = ({
1857
1871
  const groups = legendLayout(cols, available);
1858
1872
  const widths = groups.map((g) => Math.min(groupWidth(g), available));
1859
1873
  const contentWidth = Math.min(available, layoutWidth(groups));
1874
+ const lines = groups.map(groupLines);
1875
+ const totalRows = Math.max(0, ...lines.map((l) => l.length));
1876
+ const footnoteRows = tabHelp ? Math.ceil(LEGEND_FOOTNOTE.length / Math.max(1, contentWidth)) : 0;
1877
+ const chromeRows = 2 + 1 + 1 + 1;
1878
+ const fitWithin = (extra) => {
1879
+ const body = Math.max(3, (maxRows ?? rows) - chromeRows - extra);
1880
+ return { body, max: Math.max(0, totalRows - body) };
1881
+ };
1882
+ const loose = fitWithin(footnoteRows);
1883
+ const { body: bodyRows, max: maxScroll } = loose.max > 0 ? fitWithin(1) : loose;
1884
+ const offset = Math.min(scroll, maxScroll);
1885
+ const scrollable = maxScroll > 0;
1886
+ const headAligned = scrollable && lines.every((l) => l[offset]?.kind === "title");
1887
+ const pinnedOf = (l) => {
1888
+ const at = l[offset];
1889
+ return headAligned && at?.kind === "title" ? at.text : scrolledPastTitle(l, offset);
1890
+ };
1891
+ const bodyStart = offset + (headAligned ? 1 : 0);
1892
+ const shown = Math.min(totalRows - offset, bodyRows + (headAligned ? 1 : 0));
1893
+ useEffect(() => {
1894
+ onScrollRange?.(maxScroll, bodyRows);
1895
+ }, [maxScroll, bodyRows]);
1860
1896
  return /* @__PURE__ */ jsxs(
1861
1897
  Box,
1862
1898
  {
@@ -1867,18 +1903,22 @@ var HelpModal = ({
1867
1903
  paddingX: 1,
1868
1904
  width: contentWidth + MODAL_CHROME_COLS,
1869
1905
  children: [
1870
- /* @__PURE__ */ jsx(Text2, { color: "cyan", bold: true, children: "Legend" }),
1906
+ /* @__PURE__ */ jsxs(Box, { children: [
1907
+ /* @__PURE__ */ jsx(Text2, { color: "cyan", bold: true, children: "Legend" }),
1908
+ scrollable ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${offset + 1}\u2013${offset + shown} of ${totalRows}` }) : null
1909
+ ] }),
1871
1910
  /* @__PURE__ */ jsx(Box, { marginTop: 1, width: contentWidth, children: groups.map((group, index) => /* @__PURE__ */ jsx(
1872
1911
  LegendGroup,
1873
1912
  {
1874
- group,
1913
+ lines: lines[index].slice(bodyStart, bodyStart + bodyRows),
1914
+ pinned: scrollable ? pinnedOf(lines[index]) : void 0,
1875
1915
  width: widths[index],
1876
1916
  marginRight: index === groups.length - 1 ? 0 : COL_GAP
1877
1917
  },
1878
1918
  group[0]?.title ?? index
1879
1919
  )) }),
1880
- tabHelp ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "Each item lands in the FIRST tab that claims it, so counts are residuals rather than totals." }) : null,
1881
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "esc \xB7 ? close" })
1920
+ tabHelp && !scrollable ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: LEGEND_FOOTNOTE }) : null,
1921
+ /* @__PURE__ */ jsx(Text2, { dimColor: true, children: scrollable ? "\u2191\u2193 scroll \xB7 esc \xB7 ? close" : "esc \xB7 ? close" })
1882
1922
  ]
1883
1923
  }
1884
1924
  );
@@ -2000,6 +2040,8 @@ var BrowseScreen = ({
2000
2040
  const [repoFilter, setRepoFilter] = useState(/* @__PURE__ */ new Set());
2001
2041
  const [repoPicker, setRepoPicker] = useState(false);
2002
2042
  const [help, setHelp] = useState(false);
2043
+ const [helpScroll, setHelpScroll] = useState(0);
2044
+ const [helpRange, setHelpRange] = useState({ max: 0, page: 1 });
2003
2045
  const [explain, setExplain] = useState(false);
2004
2046
  const filterActive = search != null || repoFilter.size > 0;
2005
2047
  const reserveCiRow = ciStatusState != null;
@@ -2118,10 +2160,18 @@ var BrowseScreen = ({
2118
2160
  if (hidden) return;
2119
2161
  if (key.ctrl || key.meta) return;
2120
2162
  if (help) {
2163
+ if (helpRange.max > 0) {
2164
+ const step = (n) => setHelpScroll((s) => Math.min(helpRange.max, Math.max(0, s + n)));
2165
+ if (key.upArrow) return step(-1);
2166
+ if (key.downArrow) return step(1);
2167
+ if (key.pageUp) return step(-helpRange.page);
2168
+ if (key.pageDown) return step(helpRange.page);
2169
+ }
2121
2170
  setHelp(false);
2122
2171
  return;
2123
2172
  }
2124
2173
  if (input === "?") {
2174
+ setHelpScroll(0);
2125
2175
  setHelp(true);
2126
2176
  return;
2127
2177
  }
@@ -2384,7 +2434,10 @@ var BrowseScreen = ({
2384
2434
  workToggle,
2385
2435
  extensions,
2386
2436
  hasJira: !!jiraBase,
2387
- tabHelp
2437
+ tabHelp,
2438
+ maxRows: listHeight,
2439
+ scroll: helpScroll,
2440
+ onScrollRange: (max, page) => setHelpRange({ max, page })
2388
2441
  }
2389
2442
  ) : explain && activeItem && (activeItem.kind === "pr" || activeItem.kind === "issue") ? /* @__PURE__ */ jsx(ExplainModal, { item: activeItem, login }) : repoPicker ? /* @__PURE__ */ jsx(
2390
2443
  RepoPicker,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
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",