@kud/gh-ink 0.37.0 → 0.38.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
@@ -24,6 +24,57 @@ type HealthPanelProps = {
24
24
  };
25
25
  declare const HealthPanel: ({ repo, number, data, error, reload, onOpenCheck, onMerged, }: HealthPanelProps) => React.JSX.Element;
26
26
 
27
+ /**
28
+ * One initiative in the rail — a container of work rather than a unit of it.
29
+ *
30
+ * Deliberately not a `TaskRow`. The list answers "what is in front of me today"
31
+ * and prices every row by stage; this answers "what am I building, over months",
32
+ * where a stage means nothing because an initiative's stage is only ever an
33
+ * aggregate of its children's. Sharing a shape would have meant carrying a
34
+ * `status` neither side agrees on.
35
+ */
36
+ type SidebarRow = {
37
+ /** Stable identity, and the left column: a ticket key, an id, a slug. */
38
+ key: string;
39
+ /** What it is, in words. Never omitted — a key alone cannot be read. */
40
+ label: string;
41
+ /**
42
+ * How much is moving under it right now. Absent draws nothing rather than a
43
+ * zero: "no live work" and "we did not count" are different claims, and a
44
+ * hard 0 asserts the first from evidence for neither.
45
+ */
46
+ live?: number;
47
+ /** Something under it is waiting on you. */
48
+ wantsYou?: boolean;
49
+ };
50
+ type Sidebar = {
51
+ /** Heading for the rail. The host's vocabulary, never ours. */
52
+ title: string;
53
+ rows: SidebarRow[];
54
+ };
55
+ /**
56
+ * The rail's width, INCLUDING its one-column gutter. Exported because the list
57
+ * beside it has to shrink by exactly this much: every row in that list truncates
58
+ * against a budget, and a budget that does not know the rail is there overflows
59
+ * by the rail's whole width — which in a frame sized to fill the terminal
60
+ * scrolls the panel rather than clipping a row.
61
+ */
62
+ declare const SIDEBAR_COLS = 30;
63
+ /**
64
+ * How many rows fit in `height` — one fewer than would physically fit whenever
65
+ * that means anything is left over, to buy the line that says so.
66
+ *
67
+ * Computed rather than left to the layout, because the layout's answer is to CUT,
68
+ * silently. A roadmap quietly missing its last three initiatives looks exactly
69
+ * like a roadmap that has none, and the whole reason the rail exists is that an
70
+ * initiative with nothing moving on it was invisible.
71
+ */
72
+ declare const railCapacity: (height: number, rows: number) => number;
73
+ declare const SidePanel: ({ sidebar, height, }: {
74
+ sidebar: Sidebar;
75
+ height?: number;
76
+ }) => React.JSX.Element;
77
+
27
78
  declare const renderMarkdown: (raw: string, width: number, fileLink?: (path: string, line?: number) => string) => StyledLine[];
28
79
 
29
80
  declare const healthDisplay: Record<Health, {
@@ -493,6 +544,15 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jir
493
544
  * can decline to spend the last of it on a refresh nobody requested.
494
545
  */
495
546
  budget?: InboxBudget;
547
+ /**
548
+ * A rail of containers to stand beside the list — initiatives, epics,
549
+ * whatever this host groups its work under. Omit it and no rail exists: no
550
+ * key, no hint, no columns spent.
551
+ *
552
+ * Part of the fetch result rather than a static prop because it is data, and
553
+ * data that goes stale the same way the rows do.
554
+ */
555
+ sidebar?: Sidebar;
496
556
  }>;
497
557
  cacheKey?: string;
498
558
  title?: string;
@@ -615,4 +675,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
615
675
  */
616
676
  declare const parsePatterns: (value: string) => string[];
617
677
 
618
- export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, PIN_MARK, type RepoFilter, type RepoHeader, type RepoProfile, type Section, type ShowLess, type ShowMore, type Standing, type SubgroupHeader, type TaskRow, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
678
+ export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxBudget, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, PIN_MARK, type RepoFilter, type RepoHeader, type RepoProfile, SIDEBAR_COLS, type Section, type ShowLess, type ShowMore, SidePanel, type Sidebar, type SidebarRow, type Standing, type SubgroupHeader, type TaskRow, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, railCapacity, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
package/dist/index.js CHANGED
@@ -583,6 +583,48 @@ var HealthPanel = ({
583
583
  ] }) }) : note ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: colors.success, children: " " + note }) }) : null
584
584
  ] });
585
585
  };
586
+ var ACCENT = "#FF8700";
587
+ var SIDEBAR_COLS = 30;
588
+ var GUTTER = 1;
589
+ var CONTENT = SIDEBAR_COLS - GUTTER;
590
+ var truncate = (text, max) => [...text].length <= max ? text : [...text].slice(0, Math.max(0, max - 1)).join("") + "\u2026";
591
+ var ROW_LINES = 3;
592
+ var HEADING_LINES = 2;
593
+ var railCapacity = (height, rows) => {
594
+ const fits = Math.max(0, Math.floor((height - HEADING_LINES) / ROW_LINES));
595
+ return fits >= rows ? rows : Math.max(0, fits - 1);
596
+ };
597
+ var SidePanel = ({
598
+ sidebar,
599
+ height
600
+ }) => {
601
+ const shown = height === void 0 ? sidebar.rows : sidebar.rows.slice(0, railCapacity(height, sidebar.rows.length));
602
+ const hidden = sidebar.rows.length - shown.length;
603
+ return /* @__PURE__ */ jsxs(
604
+ Box,
605
+ {
606
+ flexDirection: "column",
607
+ width: SIDEBAR_COLS,
608
+ flexShrink: 0,
609
+ height,
610
+ children: [
611
+ /* @__PURE__ */ jsxs(Box, { marginBottom: 1, paddingLeft: GUTTER, children: [
612
+ /* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: "\xBB " }),
613
+ /* @__PURE__ */ jsx(Text, { bold: true, children: sidebar.title })
614
+ ] }),
615
+ sidebar.rows.length === 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: GUTTER + 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "nothing open" }) }) : shown.map((row) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
616
+ /* @__PURE__ */ jsxs(Box, { paddingLeft: GUTTER, children: [
617
+ /* @__PURE__ */ jsx(Text, { color: ACCENT, bold: true, children: row.wantsYou ? "\u2190 " : " " }),
618
+ /* @__PURE__ */ jsx(Text, { color: ACCENT, children: row.key }),
619
+ row.live !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${row.live} live` }) : null
620
+ ] }),
621
+ /* @__PURE__ */ jsx(Box, { paddingLeft: GUTTER + 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncate(row.label, CONTENT - 2) }) })
622
+ ] }, row.key)),
623
+ hidden > 0 ? /* @__PURE__ */ jsx(Box, { paddingLeft: GUTTER + 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: `+${hidden} more` }) }) : null
624
+ ]
625
+ }
626
+ );
627
+ };
586
628
  var healthDisplay = {
587
629
  none: { glyph: " ", color: colors.muted },
588
630
  draft: { glyph: "~", color: colors.muted },
@@ -1215,7 +1257,7 @@ var withHeaders = (items, idx) => {
1215
1257
  start--;
1216
1258
  return start;
1217
1259
  };
1218
- var truncate = (str, max) => {
1260
+ var truncate2 = (str, max) => {
1219
1261
  if (str.length <= max) return str;
1220
1262
  const half = Math.floor((max - 1) / 2);
1221
1263
  return `${str.slice(0, half)}\u2026${str.slice(-half)}`;
@@ -1405,7 +1447,7 @@ var COLS = (process.stdout.columns ?? 120) - FRAME_CHROME_COLS;
1405
1447
  var MODAL_CHROME_COLS = 2 + 1 * 2;
1406
1448
  var OVERLAY_BG = "#22222e";
1407
1449
  var DimContext = createContext(false);
1408
- var Text2 = ({ children, ...props }) => {
1450
+ var Text3 = ({ children, ...props }) => {
1409
1451
  const dimmed = useContext(DimContext);
1410
1452
  return dimmed ? /* @__PURE__ */ jsx(Text, { ...props, bold: false, dimColor: true, children }) : /* @__PURE__ */ jsx(Text, { ...props, children });
1411
1453
  };
@@ -1784,13 +1826,13 @@ var InboxHeader = ({
1784
1826
  COLS - brand.length - countSeg.length - userSeg.length - workLabel.length - budgetSeg.length - statusSeg.length
1785
1827
  );
1786
1828
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1787
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: true, children: brand }),
1788
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: countSeg }),
1789
- userSeg ? /* @__PURE__ */ jsx(Text2, { children: userSeg }) : null,
1790
- scopeLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: workLabel }) : null,
1791
- budgetLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: budgetCritical ? "#FF5F5F" : "#FF8700", children: budgetSeg }) : null,
1829
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", bold: true, children: brand }),
1830
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: countSeg }),
1831
+ userSeg ? /* @__PURE__ */ jsx(Text3, { children: userSeg }) : null,
1832
+ scopeLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: workLabel }) : null,
1833
+ budgetLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: budgetCritical ? "#FF5F5F" : "#FF8700", children: budgetSeg }) : null,
1792
1834
  statusText ? /* @__PURE__ */ jsx(
1793
- Text2,
1835
+ Text3,
1794
1836
  {
1795
1837
  color: statusColor,
1796
1838
  dimColor: !statusColor,
@@ -1798,7 +1840,7 @@ var InboxHeader = ({
1798
1840
  children: statusSeg
1799
1841
  }
1800
1842
  ) : null,
1801
- /* @__PURE__ */ jsx(Text2, { color: "cyan", dimColor: true, children: "\u254C".repeat(fill) })
1843
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", dimColor: true, children: "\u254C".repeat(fill) })
1802
1844
  ] });
1803
1845
  };
1804
1846
  var toCiStatusState = (status) => status ? { kind: "ready", status } : { kind: "error" };
@@ -1821,24 +1863,24 @@ var CiStatusLine = ({
1821
1863
  const name = job ?? "ci";
1822
1864
  if (state.kind === "loading")
1823
1865
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1824
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " \xB7 " }),
1825
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: `${name} loading\u2026` })
1866
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " \xB7 " }),
1867
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: `${name} loading\u2026` })
1826
1868
  ] });
1827
1869
  if (state.kind === "error")
1828
1870
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1829
- /* @__PURE__ */ jsx(Text2, { color: "red", bold: true, children: " \u2717 " }),
1830
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: `${name} no build / not configured` })
1871
+ /* @__PURE__ */ jsx(Text3, { color: "red", bold: true, children: " \u2717 " }),
1872
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: `${name} no build / not configured` })
1831
1873
  ] });
1832
1874
  const { status } = state;
1833
1875
  const [, color] = jenkinsResultDisplay(status.result, status.building);
1834
1876
  const label = status.building ? "BUILDING" : status.result;
1835
1877
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
1836
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " }),
1837
- /* @__PURE__ */ jsx(Text2, { color, bold: true, children: "\u25CF " }),
1838
- /* @__PURE__ */ jsx(Text2, { bold: true, children: status.job }),
1839
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + label }),
1840
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", children: " #" + status.buildNumber }),
1841
- status.age ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + status.age }) : null
1878
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " }),
1879
+ /* @__PURE__ */ jsx(Text3, { color, bold: true, children: "\u25CF " }),
1880
+ /* @__PURE__ */ jsx(Text3, { bold: true, children: status.job }),
1881
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + label }),
1882
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", children: " #" + status.buildNumber }),
1883
+ status.age ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + status.age }) : null
1842
1884
  ] });
1843
1885
  };
1844
1886
  var FENCE_COLS = 46;
@@ -1851,10 +1893,10 @@ var RepoHeaderRow = ({
1851
1893
  const fill = Math.max(4, FENCE_COLS - label.length);
1852
1894
  const rule = "\u2500".repeat(fill);
1853
1895
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
1854
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1855
- /* @__PURE__ */ jsx(Text2, { dimColor: !active, children: "\u2500\u2500 " }),
1856
- /* @__PURE__ */ jsx(Text2, { dimColor: !active, bold: active, children: repo }),
1857
- /* @__PURE__ */ jsx(Text2, { dimColor: !active, children: " " + rule })
1896
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
1897
+ /* @__PURE__ */ jsx(Text3, { dimColor: !active, children: "\u2500\u2500 " }),
1898
+ /* @__PURE__ */ jsx(Text3, { dimColor: !active, bold: active, children: repo }),
1899
+ /* @__PURE__ */ jsx(Text3, { dimColor: !active, children: " " + rule })
1858
1900
  ] });
1859
1901
  };
1860
1902
  var MERGED_HOLD_MS = 5e3;
@@ -1902,29 +1944,30 @@ var ItemRow = ({
1902
1944
  leaving,
1903
1945
  prefix = "",
1904
1946
  parent,
1905
- sparkFrame = 0
1947
+ sparkFrame = 0,
1948
+ cols = COLS
1906
1949
  }) => {
1907
1950
  const transient = leaving ? "out" : refreshMark;
1908
1951
  if (item.kind === "repo-header")
1909
1952
  return /* @__PURE__ */ jsx(RepoHeaderRow, { repo: item.repo, gap: gap ?? false, active });
1910
1953
  if (item.kind === "subgroup-header")
1911
1954
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
1912
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: true, children: " \xBB " }),
1913
- /* @__PURE__ */ jsx(Text2, { bold: true, children: item.label })
1955
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", bold: true, children: " \xBB " }),
1956
+ /* @__PURE__ */ jsx(Text3, { bold: true, children: item.label })
1914
1957
  ] });
1915
1958
  if (item.kind === "show-more")
1916
1959
  return /* @__PURE__ */ jsxs(Box, { children: [
1917
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1918
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
1919
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: active ? "\u21B5 " : " " }),
1920
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: `+${item.hidden.length} more` })
1960
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
1961
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
1962
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: active ? "\u21B5 " : " " }),
1963
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: `+${item.hidden.length} more` })
1921
1964
  ] });
1922
1965
  if (item.kind === "show-less")
1923
1966
  return /* @__PURE__ */ jsxs(Box, { children: [
1924
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1925
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
1926
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: active ? "\u21B5 " : " " }),
1927
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "show less" })
1967
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
1968
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
1969
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: active ? "\u21B5 " : " " }),
1970
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "show less" })
1928
1971
  ] });
1929
1972
  if (item.kind === "task") {
1930
1973
  const note = item.note ?? "";
@@ -1932,29 +1975,29 @@ var ItemRow = ({
1932
1975
  const transitLabel2 = transient ? TRANSIT_LABEL[transient] : "";
1933
1976
  const titleMax2 = Math.max(
1934
1977
  20,
1935
- COLS - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - transitLabel2.length - prefix.length - 12
1978
+ cols - item.key.length - note.length - (item.pill ? pillWidth(item.pill) + 1 : 0) - transitLabel2.length - prefix.length - 12
1936
1979
  );
1937
1980
  return /* @__PURE__ */ jsxs(Box, { marginTop: gap ? 1 : 0, children: [
1938
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1939
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
1940
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: parent ? "\u252C " : " " }),
1941
- /* @__PURE__ */ jsx(Text2, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
1942
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: active, children: item.key + " " }),
1981
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
1982
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
1983
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: parent ? "\u252C " : " " }),
1984
+ /* @__PURE__ */ jsx(Text3, { bold: true, color: transient ? TRANSIT_COLOUR[transient] : void 0, children: transitIcon + " " }),
1985
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", bold: active, children: item.key + " " }),
1943
1986
  /* @__PURE__ */ jsx(
1944
- Text2,
1987
+ Text3,
1945
1988
  {
1946
1989
  bold: active || !!transient && isArrival(transient),
1947
1990
  dimColor: !!transient && isDeparture(transient),
1948
1991
  strikethrough: transient === "out",
1949
- children: truncate(item.summary, titleMax2)
1992
+ children: truncate2(item.summary, titleMax2)
1950
1993
  }
1951
1994
  ),
1952
1995
  item.pill ? /* @__PURE__ */ jsxs(Fragment, { children: [
1953
- /* @__PURE__ */ jsx(Text2, { children: " " }),
1996
+ /* @__PURE__ */ jsx(Text3, { children: " " }),
1954
1997
  /* @__PURE__ */ jsx(Pill, { variant: item.pillVariant ?? "muted", children: item.pill })
1955
1998
  ] }) : null,
1956
- note ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${note}` }) : null,
1957
- transitLabel2 ? /* @__PURE__ */ jsx(Text2, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel2 }) : null
1999
+ note ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${note}` }) : null,
2000
+ transitLabel2 ? /* @__PURE__ */ jsx(Text3, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel2 }) : null
1958
2001
  ] });
1959
2002
  }
1960
2003
  const { glyph: healthIcon, color: healthColor2 } = healthDisplay[item.health];
@@ -1977,28 +2020,28 @@ var ItemRow = ({
1977
2020
  ].filter(Boolean).join(" ");
1978
2021
  const repoLabel = depthOf(item) > 0 ? item.repo : "";
1979
2022
  const fixedWidth = 2 + prefix.length + 2 + 2 + 7 + repoLabel.length + suffix.length + 6;
1980
- const titleMax = Math.max(20, COLS - fixedWidth);
2023
+ const titleMax = Math.max(20, cols - fixedWidth);
1981
2024
  return /* @__PURE__ */ jsxs(Box, { children: [
1982
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
1983
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: prefix }),
1984
- /* @__PURE__ */ jsx(Text2, { color, bold: true, children: icon + " " }),
1985
- /* @__PURE__ */ jsx(Text2, { color: turnColor, bold: turnIcon === "\u2190", children: turnIcon + " " }),
1986
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", children: numStr }),
2025
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
2026
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: prefix }),
2027
+ /* @__PURE__ */ jsx(Text3, { color, bold: true, children: icon + " " }),
2028
+ /* @__PURE__ */ jsx(Text3, { color: turnColor, bold: turnIcon === "\u2190", children: turnIcon + " " }),
2029
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", children: numStr }),
1987
2030
  /* @__PURE__ */ jsx(
1988
- Text2,
2031
+ Text3,
1989
2032
  {
1990
2033
  bold: active || !!transient && isArrival(transient),
1991
2034
  dimColor: !!transient && isDeparture(transient),
1992
2035
  strikethrough: transient === "out",
1993
- children: truncate(item.title, titleMax) + " "
2036
+ children: truncate2(item.title, titleMax) + " "
1994
2037
  }
1995
2038
  ),
1996
- repoLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: repoLabel }) : null,
1997
- unresolvedLabel ? /* @__PURE__ */ jsx(Text2, { bold: !spokeLast, color: spokeLast ? "#888888" : "#FF8700", children: " " + unresolvedLabel }) : null,
1998
- showAuthor ? /* @__PURE__ */ jsx(Text2, { dimColor: true, italic: true, children: " by " + item.author }) : null,
1999
- ageLabel ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + ageLabel }) : null,
2000
- mergedLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: MERGED_COLOUR, children: " " + mergedLabel }) : null,
2001
- transitLabel ? /* @__PURE__ */ jsx(Text2, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel }) : null
2039
+ repoLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: repoLabel }) : null,
2040
+ unresolvedLabel ? /* @__PURE__ */ jsx(Text3, { bold: !spokeLast, color: spokeLast ? "#888888" : "#FF8700", children: " " + unresolvedLabel }) : null,
2041
+ showAuthor ? /* @__PURE__ */ jsx(Text3, { dimColor: true, italic: true, children: " by " + item.author }) : null,
2042
+ ageLabel ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + ageLabel }) : null,
2043
+ mergedLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: MERGED_COLOUR, children: " " + mergedLabel }) : null,
2044
+ transitLabel ? /* @__PURE__ */ jsx(Text3, { bold: true, color: TRANSIT_COLOUR[transient], children: " " + transitLabel }) : null
2002
2045
  ] });
2003
2046
  };
2004
2047
  var useActionMenu = () => {
@@ -2044,15 +2087,15 @@ var ActionMenu = ({
2044
2087
  paddingX: 1,
2045
2088
  marginTop: 1,
2046
2089
  children: [
2047
- /* @__PURE__ */ jsx(Text2, { color: "cyan", bold: true, children: title }),
2048
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "\u2500".repeat(32) }),
2090
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", bold: true, children: title }),
2091
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(32) }),
2049
2092
  actions.map((a, i) => /* @__PURE__ */ jsxs(Box, { children: [
2050
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: i === cursor ? "\u276F " : " " }),
2051
- /* @__PURE__ */ jsx(Text2, { bold: i === cursor, children: a.label }),
2052
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " " + a.hint })
2093
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: i === cursor ? "\u276F " : " " }),
2094
+ /* @__PURE__ */ jsx(Text3, { bold: i === cursor, children: a.label }),
2095
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " " + a.hint })
2053
2096
  ] }, a.label)),
2054
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "\u2500".repeat(32) }),
2055
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "\u2191\u2193 navigate \u21B5 confirm esc cancel" })
2097
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(32) }),
2098
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2191\u2193 navigate \u21B5 confirm esc cancel" })
2056
2099
  ]
2057
2100
  }
2058
2101
  );
@@ -2100,11 +2143,11 @@ var LegendGroup = ({
2100
2143
  width,
2101
2144
  marginRight
2102
2145
  }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, marginRight, children: [
2103
- pinned !== void 0 ? /* @__PURE__ */ jsx(Text2, { bold: true, dimColor: true, children: pinned ?? " " }) : null,
2146
+ pinned !== void 0 ? /* @__PURE__ */ jsx(Text3, { bold: true, dimColor: true, children: pinned ?? " " }) : null,
2104
2147
  lines.map(
2105
- (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: [
2106
- /* @__PURE__ */ jsx(Text2, { color: line.row.color, bold: line.row.bold, children: line.row.cell.padEnd(line.cellWidth) }),
2107
- /* @__PURE__ */ jsx(Text2, { wrap: "truncate-end", children: line.row.label })
2148
+ (line, index) => line.kind === "gap" ? /* @__PURE__ */ jsx(Text3, { children: " " }, index) : line.kind === "title" ? /* @__PURE__ */ jsx(Text3, { bold: true, dimColor: true, children: line.text }, index) : /* @__PURE__ */ jsxs(Box, { children: [
2149
+ /* @__PURE__ */ jsx(Text3, { color: line.row.color, bold: line.row.bold, children: line.row.cell.padEnd(line.cellWidth) }),
2150
+ /* @__PURE__ */ jsx(Text3, { wrap: "truncate-end", children: line.row.label })
2108
2151
  ] }, index)
2109
2152
  )
2110
2153
  ] });
@@ -2222,8 +2265,8 @@ var HelpModal = ({
2222
2265
  width: contentWidth + MODAL_CHROME_COLS,
2223
2266
  children: [
2224
2267
  /* @__PURE__ */ jsxs(Box, { children: [
2225
- /* @__PURE__ */ jsx(Text2, { color: "cyan", bold: true, children: "Legend" }),
2226
- scrollable ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${offset + 1}\u2013${offset + shown} of ${totalRows}` }) : null
2268
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", bold: true, children: "Legend" }),
2269
+ scrollable ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${offset + 1}\u2013${offset + shown} of ${totalRows}` }) : null
2227
2270
  ] }),
2228
2271
  /* @__PURE__ */ jsx(Box, { marginTop: 1, width: contentWidth, children: groups.map((group, index) => /* @__PURE__ */ jsx(
2229
2272
  LegendGroup,
@@ -2235,8 +2278,8 @@ var HelpModal = ({
2235
2278
  },
2236
2279
  group[0]?.title ?? index
2237
2280
  )) }),
2238
- tabHelp && !scrollable ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: LEGEND_FOOTNOTE }) : null,
2239
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: scrollable ? "\u2191\u2193 scroll \xB7 esc \xB7 ? close" : "esc \xB7 ? close" })
2281
+ tabHelp && !scrollable ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: LEGEND_FOOTNOTE }) : null,
2282
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: scrollable ? "\u2191\u2193 scroll \xB7 esc \xB7 ? close" : "esc \xB7 ? close" })
2240
2283
  ]
2241
2284
  }
2242
2285
  );
@@ -2251,13 +2294,13 @@ var ExplainModal = ({ item, login }) => /* @__PURE__ */ jsxs(
2251
2294
  paddingX: 1,
2252
2295
  width: Math.min(COLS, 78),
2253
2296
  children: [
2254
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", bold: true, children: `#${item.number} \xB7 ${item.repo}` }),
2255
- /* @__PURE__ */ jsx(Text2, { children: item.title }),
2297
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", bold: true, children: `#${item.number} \xB7 ${item.repo}` }),
2298
+ /* @__PURE__ */ jsx(Text3, { children: item.title }),
2256
2299
  explainItem(item, login).map((section) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
2257
- /* @__PURE__ */ jsx(Text2, { bold: true, dimColor: true, children: section.heading }),
2258
- section.lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { children: " " + line }, i))
2300
+ /* @__PURE__ */ jsx(Text3, { bold: true, dimColor: true, children: section.heading }),
2301
+ section.lines.map((line, i) => /* @__PURE__ */ jsx(Text3, { children: " " + line }, i))
2259
2302
  ] }, section.heading)),
2260
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "esc \xB7 e close" }) })
2303
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "esc \xB7 e close" }) })
2261
2304
  ]
2262
2305
  }
2263
2306
  );
@@ -2283,21 +2326,21 @@ var RepoPicker = ({
2283
2326
  paddingX: 1,
2284
2327
  minWidth: 42,
2285
2328
  children: [
2286
- /* @__PURE__ */ jsx(Text2, { color: "cyan", bold: true, children: `Filter by repo (${selected.size} on)` }),
2287
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "\u2500".repeat(36) }),
2329
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", bold: true, children: `Filter by repo (${selected.size} on)` }),
2330
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(36) }),
2288
2331
  visible.map((repo, i) => {
2289
2332
  const idx = start + i;
2290
2333
  const on = selected.has(repo);
2291
2334
  const active = idx === cursor;
2292
2335
  return /* @__PURE__ */ jsxs(Box, { children: [
2293
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: active ? "\u276F " : " " }),
2294
- /* @__PURE__ */ jsx(Text2, { color: on ? "green" : void 0, children: on ? "\u25C9 " : "\u25CB " }),
2295
- /* @__PURE__ */ jsx(Text2, { bold: active, children: repo })
2336
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: active ? "\u276F " : " " }),
2337
+ /* @__PURE__ */ jsx(Text3, { color: on ? "green" : void 0, children: on ? "\u25C9 " : "\u25CB " }),
2338
+ /* @__PURE__ */ jsx(Text3, { bold: active, children: repo })
2296
2339
  ] }, repo);
2297
2340
  }),
2298
- repos.length > budget ? /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` \u2026 ${repos.length} repos total` }) : null,
2299
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "\u2500".repeat(36) }),
2300
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: "space toggle \xB7 a clear all \xB7 \u21B5/esc done" })
2341
+ repos.length > budget ? /* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` \u2026 ${repos.length} repos total` }) : null,
2342
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "\u2500".repeat(36) }),
2343
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: "space toggle \xB7 a clear all \xB7 \u21B5/esc done" })
2301
2344
  ]
2302
2345
  }
2303
2346
  );
@@ -2334,7 +2377,8 @@ var BrowseScreen = ({
2334
2377
  leavingUrls,
2335
2378
  onLeave,
2336
2379
  transients,
2337
- onTabChange
2380
+ onTabChange,
2381
+ sidebar
2338
2382
  }) => {
2339
2383
  const { rows } = useWindowSize();
2340
2384
  const applyWork = (secs) => origin ? filterByOrigin(secs, origin.show, origin.match) : secs;
@@ -2434,6 +2478,9 @@ var BrowseScreen = ({
2434
2478
  out.add(s.id);
2435
2479
  return out;
2436
2480
  }, [localSections, transients]);
2481
+ const [railOpen, setRailOpen] = useState(false);
2482
+ const showRail = railOpen && !!sidebar;
2483
+ const listCols = showRail ? COLS - SIDEBAR_COLS : COLS;
2437
2484
  const [sparkFrame, setSparkFrame] = useState(0);
2438
2485
  const sparkling = (mergedUrls?.length ?? 0) > 0 || (leavingUrls?.length ?? 0) > 0 || !!transients?.size && section.items.some((item) => transientOf(transients, item, section.id));
2439
2486
  useEffect(() => {
@@ -2534,6 +2581,10 @@ var BrowseScreen = ({
2534
2581
  setRepoPicker(true);
2535
2582
  return;
2536
2583
  }
2584
+ if (input === "i" && sidebar) {
2585
+ setRailOpen((open) => !open);
2586
+ return;
2587
+ }
2537
2588
  if (key.escape && search != null) {
2538
2589
  setSearch(null);
2539
2590
  return;
@@ -2819,6 +2870,9 @@ var BrowseScreen = ({
2819
2870
  ["\u2190\u2192", "tab"],
2820
2871
  ["\u21B5/d", "open"],
2821
2872
  ["m", "actions"],
2873
+ // Advertised only where it does something, and named for what it
2874
+ // shows rather than for the furniture: nobody wants "a sidebar".
2875
+ ...sidebar ? [["i", railOpen ? "hide" : sidebar.title.toLowerCase()]] : [],
2822
2876
  ["?", "help"],
2823
2877
  ["q", "quit"]
2824
2878
  ];
@@ -2871,8 +2925,8 @@ var BrowseScreen = ({
2871
2925
  ),
2872
2926
  ciStatusState ? /* @__PURE__ */ jsx(CiStatusLine, { state: ciStatusState, job: ciJob }) : null,
2873
2927
  skippedForBudget ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
2874
- /* @__PURE__ */ jsx(Text2, { bold: true, color: "#FF8700", children: " \u26A1 auto-refresh paused to save API budget" }),
2875
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " r refreshes anyway" })
2928
+ /* @__PURE__ */ jsx(Text3, { bold: true, color: "#FF8700", children: " \u26A1 auto-refresh paused to save API budget" }),
2929
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " r refreshes anyway" })
2876
2930
  ] }) : null,
2877
2931
  /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(
2878
2932
  Tabs,
@@ -2886,52 +2940,65 @@ var BrowseScreen = ({
2886
2940
  }
2887
2941
  ) }),
2888
2942
  search != null ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
2889
- /* @__PURE__ */ jsx(Text2, { color: "cyan", children: " / " }),
2890
- /* @__PURE__ */ jsx(Text2, { children: search }),
2891
- searchInput ? /* @__PURE__ */ jsx(Text2, { color: "cyan", children: "\u258F" }) : null,
2892
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: ` ${matchCount} match${matchCount !== 1 ? "es" : ""}${searchInput ? " \u21B5 accept \xB7 esc clear" : " esc clear"}` })
2943
+ /* @__PURE__ */ jsx(Text3, { color: "cyan", children: " / " }),
2944
+ /* @__PURE__ */ jsx(Text3, { children: search }),
2945
+ searchInput ? /* @__PURE__ */ jsx(Text3, { color: "cyan", children: "\u258F" }) : null,
2946
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: ` ${matchCount} match${matchCount !== 1 ? "es" : ""}${searchInput ? " \u21B5 accept \xB7 esc clear" : " esc clear"}` })
2893
2947
  ] }) : repoFilter.size > 0 ? /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
2894
- /* @__PURE__ */ jsx(Text2, { color: "#FF8700", children: " \u25C9 " }),
2895
- /* @__PURE__ */ jsx(Text2, { children: `${repoFilter.size} repo${repoFilter.size !== 1 ? "s" : ""}` }),
2896
- /* @__PURE__ */ jsx(Text2, { dimColor: true, children: " f edit \xB7 esc clear" })
2948
+ /* @__PURE__ */ jsx(Text3, { color: "#FF8700", children: " \u25C9 " }),
2949
+ /* @__PURE__ */ jsx(Text3, { children: `${repoFilter.size} repo${repoFilter.size !== 1 ? "s" : ""}` }),
2950
+ /* @__PURE__ */ jsx(Text3, { dimColor: true, children: " f edit \xB7 esc clear" })
2897
2951
  ] }) : null,
2898
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", minHeight: listHeight, children: [
2899
- /* @__PURE__ */ jsxs(Backdrop, { dimmed: !!overlay, absolute: !!overlay, height: listHeight, children: [
2900
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, i) => /* @__PURE__ */ jsx(
2901
- ItemRow,
2902
- {
2903
- item,
2904
- active: viewStart + i === cursor,
2905
- login,
2906
- merged: (item.kind === "pr" || item.kind === "issue") && !!mergedUrls?.includes(item.url),
2907
- leaving: (item.kind === "pr" || item.kind === "issue") && !!leavingUrls?.includes(item.url),
2908
- transient: transientOf(transients, item, section.id),
2909
- prefix: treePrefix(section.items, viewStart + i),
2910
- parent: item.kind === "task" && depthOf(section.items[viewStart + i + 1]) > depthOf(item),
2911
- sparkFrame,
2912
- gap: i > 0 && gapsAbove(section.items, viewStart + i)
2913
- },
2914
- `${viewStart + i}:${item.kind === "task" ? item.instanceKey ?? item.key : item.kind === "repo-header" ? `header:${item.repo}` : item.kind === "subgroup-header" ? `subgroup:${item.label}` : item.kind === "show-more" ? `show-more:${item.hidden[0]?.repo ?? i}` : item.kind === "show-less" ? `show-less:${item.toHide[0]?.repo ?? i}` : `${item.repo}/${item.number}`}`
2915
- )) }),
2916
- hasMore && /* @__PURE__ */ jsxs(Text2, { dimColor: true, children: [
2917
- " ",
2918
- "\u2193 ",
2919
- section.items.length - viewStart - visibleCount,
2920
- " more"
2921
- ] })
2922
- ] }),
2923
- overlay ? /* @__PURE__ */ jsx(
2952
+ /* @__PURE__ */ jsxs(Box, { children: [
2953
+ /* @__PURE__ */ jsxs(
2924
2954
  Box,
2925
2955
  {
2926
- flexGrow: 1,
2927
2956
  flexDirection: "column",
2928
- justifyContent: "center",
2929
- alignItems: "center",
2930
- children: overlay
2957
+ minHeight: listHeight,
2958
+ width: showRail ? listCols : void 0,
2959
+ flexShrink: 0,
2960
+ children: [
2961
+ /* @__PURE__ */ jsxs(Backdrop, { dimmed: !!overlay, absolute: !!overlay, height: listHeight, children: [
2962
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, i) => /* @__PURE__ */ jsx(
2963
+ ItemRow,
2964
+ {
2965
+ item,
2966
+ active: viewStart + i === cursor,
2967
+ login,
2968
+ merged: (item.kind === "pr" || item.kind === "issue") && !!mergedUrls?.includes(item.url),
2969
+ leaving: (item.kind === "pr" || item.kind === "issue") && !!leavingUrls?.includes(item.url),
2970
+ transient: transientOf(transients, item, section.id),
2971
+ prefix: treePrefix(section.items, viewStart + i),
2972
+ parent: item.kind === "task" && depthOf(section.items[viewStart + i + 1]) > depthOf(item),
2973
+ sparkFrame,
2974
+ cols: listCols,
2975
+ gap: i > 0 && gapsAbove(section.items, viewStart + i)
2976
+ },
2977
+ `${viewStart + i}:${item.kind === "task" ? item.instanceKey ?? item.key : item.kind === "repo-header" ? `header:${item.repo}` : item.kind === "subgroup-header" ? `subgroup:${item.label}` : item.kind === "show-more" ? `show-more:${item.hidden[0]?.repo ?? i}` : item.kind === "show-less" ? `show-less:${item.toHide[0]?.repo ?? i}` : `${item.repo}/${item.number}`}`
2978
+ )) }),
2979
+ hasMore && /* @__PURE__ */ jsxs(Text3, { dimColor: true, children: [
2980
+ " ",
2981
+ "\u2193 ",
2982
+ section.items.length - viewStart - visibleCount,
2983
+ " more"
2984
+ ] })
2985
+ ] }),
2986
+ overlay ? /* @__PURE__ */ jsx(
2987
+ Box,
2988
+ {
2989
+ flexGrow: 1,
2990
+ flexDirection: "column",
2991
+ justifyContent: "center",
2992
+ alignItems: "center",
2993
+ children: overlay
2994
+ }
2995
+ ) : null
2996
+ ]
2931
2997
  }
2932
- ) : null
2998
+ ),
2999
+ showRail && sidebar ? /* @__PURE__ */ jsx(SidePanel, { sidebar, height: listHeight }) : null
2933
3000
  ] }),
2934
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text2, { color: "green", children: flash }) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
3001
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: flash ? /* @__PURE__ */ jsx(Text3, { color: "green", children: flash }) : /* @__PURE__ */ jsx(FooterHints, { hints }) })
2935
3002
  ]
2936
3003
  }
2937
3004
  );
@@ -2947,7 +3014,7 @@ var NoRowsScreen = ({
2947
3014
  });
2948
3015
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
2949
3016
  /* @__PURE__ */ jsx(StatusMessage, { variant: reason === "empty" ? "info" : "error", children: reason === "empty" ? "Nothing open." : `Fetch failed \u2014 ${detail ?? "no reason reported"}` }),
2950
- reason === "empty" && detail ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text2, { dimColor: true, children: detail }) }) : null,
3017
+ reason === "empty" && detail ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text3, { dimColor: true, children: detail }) }) : null,
2951
3018
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(
2952
3019
  FooterHints,
2953
3020
  {
@@ -3112,6 +3179,7 @@ var App = ({
3112
3179
  if (manual) setRefreshing(true);
3113
3180
  fetcher().then((fresh) => {
3114
3181
  if (fresh.budget) setBudget(fresh.budget);
3182
+ if (fresh.sidebar !== void 0) setSidebar(fresh.sidebar);
3115
3183
  if (cacheKey) writeCache(cacheKey, fresh);
3116
3184
  setRefreshing(false);
3117
3185
  setFetchedAt(Date.now());
@@ -3222,6 +3290,7 @@ var App = ({
3222
3290
  const [budget, setBudget] = useState(
3223
3291
  () => cacheKey ? readCache(cacheKey)?.budget ?? null : null
3224
3292
  );
3293
+ const [sidebar, setSidebar] = useState(void 0);
3225
3294
  const [skippedForBudget, setSkippedForBudget] = useState(false);
3226
3295
  const [mergedUrls, setMergedUrls] = useState([]);
3227
3296
  const [leavingUrls, setLeavingUrls] = useState([]);
@@ -3328,6 +3397,7 @@ var App = ({
3328
3397
  onLeave: markLeaving,
3329
3398
  transients,
3330
3399
  onTabChange: setVisibleTab,
3400
+ sidebar,
3331
3401
  ciStatusState: hasCiStatus ? ciStatusState : void 0,
3332
3402
  ciJob,
3333
3403
  tabHelp,
@@ -3380,4 +3450,4 @@ var matchesFilter = (repo, filter) => {
3380
3450
  };
3381
3451
  var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
3382
3452
 
3383
- export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, PIN_MARK, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
3453
+ export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, PIN_MARK, SIDEBAR_COLS, SidePanel, budgetNotice, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, railCapacity, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate2 as truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh-ink",
3
- "version": "0.37.0",
3
+ "version": "0.38.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",