@kud/gh-ink 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +45 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import React2, { useState, useRef, useEffect } from 'react';
2
2
  import { useWindowSize, useInput, Text, Box } from 'ink';
3
- import { colors, ScrollView, TextInput, LoadingScreen, Switch, Tabs, FooterHints } from '@kud/ink-ui';
3
+ import { colors, ScrollView, TextInput, LoadingScreen, Switch, useListCursor, Tabs, FooterHints } 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';
@@ -847,6 +847,12 @@ var maxViewStart = (items, budget) => {
847
847
  start++;
848
848
  return start;
849
849
  };
850
+ var firstSelectable = (section) => Math.max(
851
+ 0,
852
+ section.items.findIndex(
853
+ (i) => i.kind !== "repo-header" && i.kind !== "subgroup-header"
854
+ )
855
+ );
850
856
  var withHeaders = (items, idx) => {
851
857
  let start = idx;
852
858
  while (start > 0 && (items[start - 1].kind === "repo-header" || items[start - 1].kind === "subgroup-header"))
@@ -1679,25 +1685,17 @@ var BrowseScreen = ({
1679
1685
  const [localSections, setLocalSections] = useState(initialSections);
1680
1686
  const [tabIdx, setTabIdx] = useState(0);
1681
1687
  const [cursors, setCursors] = useState(
1682
- initialSections.map(
1683
- (s) => Math.max(
1684
- 0,
1685
- s.items.findIndex(
1686
- (i) => i.kind !== "repo-header" && i.kind !== "subgroup-header"
1687
- )
1688
- )
1689
- )
1690
- );
1691
- const [viewStarts, setViewStarts] = useState(
1692
- initialSections.map(() => 0)
1688
+ () => Object.fromEntries(initialSections.map((s) => [s.id, firstSelectable(s)]))
1693
1689
  );
1690
+ const [viewStarts, setViewStarts] = useState({});
1691
+ const safeTabIdx = Math.min(tabIdx, Math.max(0, localSections.length - 1));
1692
+ const activeId = localSections[safeTabIdx]?.id ?? "";
1694
1693
  const [flash, setFlash] = useState(null);
1695
1694
  const menu = useActionMenu();
1696
1695
  const [search, setSearch] = useState(null);
1697
1696
  const [searchInput, setSearchInput] = useState(false);
1698
1697
  const [repoFilter, setRepoFilter] = useState(/* @__PURE__ */ new Set());
1699
1698
  const [repoPicker, setRepoPicker] = useState(false);
1700
- const [repoCursor, setRepoCursor] = useState(0);
1701
1699
  const [help, setHelp] = useState(false);
1702
1700
  const [explain, setExplain] = useState(false);
1703
1701
  const filterActive = search != null || repoFilter.size > 0;
@@ -1710,8 +1708,8 @@ var BrowseScreen = ({
1710
1708
  rows - 10 - (filterActive ? 2 : 0) - (reserveCiRow ? 2 : 0)
1711
1709
  );
1712
1710
  useEffect(() => {
1713
- setCursors((p) => p.map((c, i) => i === tabIdx ? 0 : c));
1714
- setViewStarts((p) => p.map((v, i) => i === tabIdx ? 0 : v));
1711
+ setCursors((p) => ({ ...p, [activeId]: 0 }));
1712
+ setViewStarts((p) => ({ ...p, [activeId]: 0 }));
1715
1713
  }, [search]);
1716
1714
  useEffect(() => {
1717
1715
  setLocalSections(applyWork(sections, includeWork));
@@ -1719,20 +1717,30 @@ var BrowseScreen = ({
1719
1717
  useEffect(() => {
1720
1718
  setTabIdx((prev) => Math.min(prev, Math.max(0, localSections.length - 1)));
1721
1719
  setCursors(
1722
- (prev) => localSections.map((s, i) => {
1723
- const c = Math.min(prev[i] ?? 0, s.items.length - 1);
1724
- if (c < 0) return 0;
1725
- return s.items[c]?.kind === "repo-header" || s.items[c]?.kind === "subgroup-header" ? moveCursor(s.items, c, 1) : c;
1726
- })
1720
+ (prev) => Object.fromEntries(
1721
+ localSections.map((s) => {
1722
+ const c = Math.min(
1723
+ prev[s.id] ?? firstSelectable(s),
1724
+ s.items.length - 1
1725
+ );
1726
+ if (c < 0) return [s.id, 0];
1727
+ return [
1728
+ s.id,
1729
+ s.items[c]?.kind === "repo-header" || s.items[c]?.kind === "subgroup-header" ? moveCursor(s.items, c, 1) : c
1730
+ ];
1731
+ })
1732
+ )
1727
1733
  );
1728
1734
  setViewStarts(
1729
- (prev) => localSections.map(
1730
- (s, i) => Math.min(prev[i] ?? 0, maxViewStart(s.items, listHeight))
1735
+ (prev) => Object.fromEntries(
1736
+ localSections.map((s) => [
1737
+ s.id,
1738
+ Math.min(prev[s.id] ?? 0, maxViewStart(s.items, listHeight))
1739
+ ])
1731
1740
  )
1732
1741
  );
1733
1742
  }, [localSections, listHeight]);
1734
1743
  const removeItemFromSections = (target) => setLocalSections((prev) => withoutItem(prev, target));
1735
- const safeTabIdx = Math.min(tabIdx, Math.max(0, localSections.length - 1));
1736
1744
  const rawSection = localSections[safeTabIdx] ?? {
1737
1745
  id: "empty",
1738
1746
  label: "",
@@ -1742,8 +1750,12 @@ var BrowseScreen = ({
1742
1750
  const filtered = repoFilter.size > 0 ? filterByRepos(searched, repoFilter) : searched;
1743
1751
  const section = filterActive ? { ...rawSection, items: filtered[0]?.items ?? [] } : rawSection;
1744
1752
  const allRepos = reposInSections(localSections);
1745
- const cursor = cursors[safeTabIdx] ?? 0;
1746
- const viewStart = viewStarts[safeTabIdx] ?? 0;
1753
+ const { cursor: repoCursor, setCursor: setRepoCursor } = useListCursor(
1754
+ allRepos.length,
1755
+ { vimKeys: false, isActive: repoPicker }
1756
+ );
1757
+ const cursor = cursors[activeId] ?? 0;
1758
+ const viewStart = viewStarts[activeId] ?? 0;
1747
1759
  const visibleCount = windowCount(section.items, viewStart, listHeight);
1748
1760
  const visibleItems = section.items.slice(viewStart, viewStart + visibleCount);
1749
1761
  const hasMore = viewStart + visibleCount < section.items.length;
@@ -1798,9 +1810,6 @@ var BrowseScreen = ({
1798
1810
  }
1799
1811
  if (repoPicker) {
1800
1812
  if (key.escape || key.return) return setRepoPicker(false);
1801
- if (key.upArrow) return setRepoCursor((c) => Math.max(0, c - 1));
1802
- if (key.downArrow)
1803
- return setRepoCursor((c) => Math.min(allRepos.length - 1, c + 1));
1804
1813
  if (input === "a") return setRepoFilter(/* @__PURE__ */ new Set());
1805
1814
  if (input === " ") {
1806
1815
  const repo = allRepos[repoCursor];
@@ -1848,16 +1857,16 @@ var BrowseScreen = ({
1848
1857
  if (key.upArrow) {
1849
1858
  const next = moveCursor(section.items, cursor, -1);
1850
1859
  const newVs = Math.min(viewStart, withHeaders(section.items, next));
1851
- setCursors((p) => p.map((c, i) => i === tabIdx ? next : c));
1852
- setViewStarts((p) => p.map((v, i) => i === tabIdx ? newVs : v));
1860
+ setCursors((p) => ({ ...p, [activeId]: next }));
1861
+ setViewStarts((p) => ({ ...p, [activeId]: newVs }));
1853
1862
  }
1854
1863
  if (key.downArrow) {
1855
1864
  const next = moveCursor(section.items, cursor, 1);
1856
1865
  let newVs = viewStart;
1857
1866
  while (next >= newVs + windowCount(section.items, newVs, listHeight))
1858
1867
  newVs++;
1859
- setCursors((p) => p.map((c, i) => i === tabIdx ? next : c));
1860
- setViewStarts((p) => p.map((v, i) => i === tabIdx ? newVs : v));
1868
+ setCursors((p) => ({ ...p, [activeId]: next }));
1869
+ setViewStarts((p) => ({ ...p, [activeId]: newVs }));
1861
1870
  }
1862
1871
  if (key.leftArrow) setTabIdx((i) => Math.max(0, i - 1));
1863
1872
  if (key.rightArrow)
@@ -1873,8 +1882,11 @@ var BrowseScreen = ({
1873
1882
  }
1874
1883
  if (workToggle && input === "w") {
1875
1884
  const next = !includeWork;
1885
+ const nextSections = applyWork(sections, next);
1886
+ const keptIdx = nextSections.findIndex((s) => s.id === activeId);
1876
1887
  setIncludeWork(next);
1877
- setLocalSections(applyWork(sections, next));
1888
+ setLocalSections(nextSections);
1889
+ if (keptIdx >= 0) setTabIdx(keptIdx);
1878
1890
  return;
1879
1891
  }
1880
1892
  if (input === "J" && ciStatus) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.3.0",
3
+ "version": "0.3.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",