gh-glance 0.19.0 → 0.19.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/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.19.1] - 2026-11-04
11
+
12
+ ### Changed
13
+
14
+ - The tab bar shows full section names whenever they fit the pane, instead of
15
+ switching to short names below a fixed 98 columns. With few counts, full
16
+ names now appear from about 85 columns.
17
+ - The short name of the Notifications tab is now `Status` (was `Notices`).
18
+
10
19
  ## [0.19.0] - 2026-11-02
11
20
 
12
21
  ### Added
package/README.md CHANGED
@@ -20,7 +20,7 @@ pane next to your editor) where you want an always-on glance at repo activity
20
20
  without switching to the browser.
21
21
 
22
22
  ```
23
- [1:Actions (4)] 2:Issues 3:PRs 4:Security 5:Notices
23
+ [1:Actions (4)] 2:Issues 3:PRs 4:Security 5:Status
24
24
  ───────────────────────────────────────────────────────────────────────────
25
25
  ╭─ Actions · owner/repo ──────────────────────────────────────────────────╮
26
26
  │ TITLE │WORKFLOW │BRANCH │TIME │UPDATED │
@@ -47,8 +47,9 @@ without switching to the browser.
47
47
  > red" is answerable without switching to that tab — an `x` means the tab's
48
48
  > latest fetch failed, and a `?` in place of the Security count means its
49
49
  > endpoints could not be read, which is not the same as there being nothing to
50
- > report. `5:Notices` gains an unread count such as `(3)` when waits or
51
- > problems arrive while it is closed; see [Notifications](#notifications).
50
+ > report. `5:Status` (`5:Notifications` when the full names fit) gains an
51
+ > unread count such as `(3)` when waits or problems arrive while it is closed;
52
+ > see [Notifications](#notifications).
52
53
 
53
54
  <!-- contract:allow-emoji -- the check/cross above stand in for Nerd Font
54
55
  Octicons the app actually draws; they are literal examples, not decoration. -->
package/index.mjs CHANGED
@@ -17979,14 +17979,15 @@ const MIN_TABLE_WIDTH = Math.max(...TABS.map((t) => minimumWidthFor(t.header)));
17979
17979
  // status line survive at any width a terminal is realistically set to.
17980
17980
  const MIN_COMPACT_WIDTH = Math.max(...TABS.map((t) => minimumWidthFor(t.compactHeader)));
17981
17981
 
17982
- // The tab bar is laid out independently of the table. Full labels for the five
17983
- // sections need ~97 columns; short labels ~77. Hysteresis on the full/short
17984
- // breakpoint so a pane parked exactly on it does not flip labels on every
17985
- // resize event and defeat the unchanged-frame optimization. Below the short
17986
- // width the bar drops names and keeps digits and counts (see tabBarEntries).
17987
- const TAB_LABEL_FULL_WIDTH = 98;
17982
+ // The tab bar is laid out independently of the table. Full labels are used
17983
+ // whenever they fit the measured bar (~97 columns with counts on every tab, less
17984
+ // with fewer); short labels need ~76. Once the bar has dropped to short labels,
17985
+ // full ones return only with this many spare columns, so a pane parked on the
17986
+ // boundary, or a count gaining a digit, does not flip labels back and forth and
17987
+ // defeat the unchanged-frame optimization. Below the short width the bar drops
17988
+ // names and keeps digits and counts (see tabBarEntries).
17988
17989
  const TAB_LABEL_HYSTERESIS = 4;
17989
- const NOTIFICATIONS_LABEL = Object.freeze({ full: "Notifications", short: "Notices" });
17990
+ const NOTIFICATIONS_LABEL = Object.freeze({ full: "Notifications", short: "Status" });
17990
17991
 
17991
17992
  function tabFailureSuffix({ count, failed, brokenCI }) {
17992
17993
  if (count == null) return failed ? " x" : "";
@@ -18001,7 +18002,7 @@ function tabBarWidth(entries) {
18001
18002
  // `cols` wins: full names, short names, digits with counts, the same without
18002
18003
  // gaps, then bare digits that keep only the Notifications badge -- so every
18003
18004
  // section key stays visible, and the row never wraps, down to a ~20-column pane.
18004
- function tabBarEntries({ cols, counts, brokenCI, firstLoad, failed, spin, useShort, activeIndex,
18005
+ function tabBarEntries({ cols, counts, brokenCI, firstLoad, failed, spin, preferShort = false, activeIndex,
18005
18006
  notifications = { unread: 0, tone: "none" } }) {
18006
18007
  const sections = TABS.map((tab) => {
18007
18008
  const count = counts[tab.key];
@@ -18032,9 +18033,11 @@ function tabBarEntries({ cols, counts, brokenCI, firstLoad, failed, spin, useSho
18032
18033
  // identical, so the bar does not shift between the two modes.
18033
18034
  return { key: section.key, active, label: active ? `[${body}]` : ` ${body} `,
18034
18035
  margin: index === sections.length - 1 || tier === "tight" || tier === "digits" ? 0 : tier === "full" ? 2 : 1,
18035
- color: section.color, dim: !active && section.quiet };
18036
+ color: section.color, dim: !active && section.quiet, tier };
18036
18037
  });
18037
- for (const tier of [...useShort ? [] : ["full"], "short", "compact", "tight"]) {
18038
+ const full = build("full");
18039
+ if (tabBarWidth(full) + (preferShort ? TAB_LABEL_HYSTERESIS : 0) <= cols) return full;
18040
+ for (const tier of ["short", "compact", "tight"]) {
18038
18041
  const entries = build(tier);
18039
18042
  if (tabBarWidth(entries) <= cols) return entries;
18040
18043
  }
@@ -18543,7 +18546,7 @@ function StatusBar({
18543
18546
 
18544
18547
  // ---------- Layout ----------
18545
18548
 
18546
- // Terminal size, and the label breakpoint that depends on it.
18549
+ // Terminal size.
18547
18550
  //
18548
18551
  // The usableSize guard is applied on every read path, not just the first: pty
18549
18552
  // wrappers and a terminal mid-resize report 0 or undefined, and taking either
@@ -18553,7 +18556,6 @@ function useTerminalSize(stdout) {
18553
18556
  const [size, setSize] = useState(() => ({
18554
18557
  rows: usableSize(stdout?.rows, DEFAULT_ROWS),
18555
18558
  cols: usableSize(stdout?.columns, DEFAULT_COLS),
18556
- useShortLabels: usableSize(stdout?.columns, DEFAULT_COLS) < TAB_LABEL_FULL_WIDTH,
18557
18559
  }));
18558
18560
 
18559
18561
  useEffect(() => {
@@ -18561,21 +18563,9 @@ function useTerminalSize(stdout) {
18561
18563
  function onResize() {
18562
18564
  const cols = usableSize(stdout.columns, DEFAULT_COLS);
18563
18565
  const rows = usableSize(stdout.rows, DEFAULT_ROWS);
18564
- setSize((previous) => {
18565
- // Hysteresis on the label breakpoint: switch to short labels below it,
18566
- // back to full only once comfortably above, so a pane dragged along the
18567
- // boundary does not emit a different frame on every resize event.
18568
- const useShortLabels = previous.useShortLabels
18569
- ? cols < TAB_LABEL_FULL_WIDTH + TAB_LABEL_HYSTERESIS
18570
- : cols < TAB_LABEL_FULL_WIDTH;
18571
- // Same object when nothing moved, so a resize event that changes
18572
- // nothing cannot cost a redraw.
18573
- return previous.rows === rows &&
18574
- previous.cols === cols &&
18575
- previous.useShortLabels === useShortLabels
18576
- ? previous
18577
- : { rows, cols, useShortLabels };
18578
- });
18566
+ // Same object when nothing moved, so a resize event that changes
18567
+ // nothing cannot cost a redraw.
18568
+ setSize((previous) => previous.rows === rows && previous.cols === cols ? previous : { rows, cols });
18579
18569
  }
18580
18570
  stdout.on("resize", onResize);
18581
18571
  return () => stdout.off("resize", onResize);
@@ -19636,7 +19626,11 @@ function App({ onCreateRemote = () => {} } = {}) {
19636
19626
  jevOperationRef.current=null;runtimeJevSession?.clear();setFeature(current=>current?{...current,semantic:{message:'Jev results expired; J Run again.'}}:null);
19637
19627
  }
19638
19628
  },[feature,now]);
19639
- const { rows, cols, useShortLabels } = useTerminalSize(stdout);
19629
+ const { rows, cols } = useTerminalSize(stdout);
19630
+ // Whether the last frame's tab bar used short labels: tabBarEntries needs it
19631
+ // for hysteresis, and it depends on counts as well as width, so it is
19632
+ // tracked per frame rather than per resize.
19633
+ const shortTabLabelsRef = useRef(false);
19640
19634
  const [frame, setFrame] = useState(0);
19641
19635
  // Per tab, so switching away and back keeps your place. Keyed by item, and
19642
19636
  // both are plain state: they change only on a keypress, so an idle repo still
@@ -23121,23 +23115,24 @@ function App({ onCreateRemote = () => {} } = {}) {
23121
23115
  const liveRows = Math.min(rows, usableSize(stdout?.rows, rows));
23122
23116
  const frameRows = Math.max(1, liveRows - 1);
23123
23117
 
23118
+ const tabEntries = tabBarEntries({
23119
+ cols: frameCols,
23120
+ counts,
23121
+ brokenCI,
23122
+ firstLoad,
23123
+ failed,
23124
+ spin,
23125
+ preferShort: shortTabLabelsRef.current,
23126
+ activeIndex: notificationsOpen ? TABS.length : activeIndex,
23127
+ // An active GitHub outage keeps the badge red even after it was read.
23128
+ notifications: githubStatus.outage ? { ...notificationBadge, tone: "red" } : notificationBadge,
23129
+ });
23130
+ shortTabLabelsRef.current = tabEntries[0].tier !== "full";
23131
+
23124
23132
  return e(
23125
23133
  Box,
23126
23134
  { flexDirection: "column", width: frameCols, height: frameRows },
23127
- e(TabBar, {
23128
- entries: tabBarEntries({
23129
- cols: frameCols,
23130
- counts,
23131
- brokenCI,
23132
- firstLoad,
23133
- failed,
23134
- spin,
23135
- useShort: useShortLabels,
23136
- activeIndex: notificationsOpen ? TABS.length : activeIndex,
23137
- // An active GitHub outage keeps the badge red even after it was read.
23138
- notifications: githubStatus.outage ? { ...notificationBadge, tone: "red" } : notificationBadge,
23139
- }),
23140
- }),
23135
+ e(TabBar, { entries: tabEntries }),
23141
23136
  e(Divider, { width: frameCols }),
23142
23137
  e(PanelEdge, { width: frameCols, top: true, label: topLabel, labelColor: TITLE_COLOR }),
23143
23138
  e(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-glance",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "A live-refreshing GitHub dashboard for your terminal: Actions, Issues, Pull Requests, and Security alerts in one narrow pane",
5
5
  "type": "module",
6
6
  "bin": {