gh-glance 0.19.0 → 0.19.2

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,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.19.2] - 2026-11-06
11
+
12
+ ### Changed
13
+
14
+ - The fifth section is now named `Events` (`5:Events`) at every width that shows
15
+ a name, in the tab bar, its view title and the help text. Narrow panes no longer switch it
16
+ to a different word (`Status`, earlier `Notices`). Full tab names now fit
17
+ in panes 7 columns narrower.
18
+ - A wait that ends in under two minutes no longer counts toward the `Events`
19
+ unread badge. The view still lists it. Each wait is measured on its own, so
20
+ repeated short waits do not add up. Problems, causes that need action and
21
+ longer waits count as before.
22
+
23
+ ## [0.19.1] - 2026-11-04
24
+
25
+ ### Changed
26
+
27
+ - The tab bar shows full section names whenever they fit the pane, instead of
28
+ switching to short names below a fixed 98 columns. With few counts, full
29
+ names now appear from about 85 columns.
30
+ - The short name of the Notifications tab is now `Status` (was `Notices`).
31
+
10
32
  ## [0.19.0] - 2026-11-02
11
33
 
12
34
  ### Added
@@ -1421,7 +1443,9 @@ engineering, security, QA and UX. What follows is what changed as a result.
1421
1443
  - The `main` field from `package.json`. It advertised the file as importable,
1422
1444
  but importing it took over the terminal or exited the host process.
1423
1445
 
1424
- [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.19.0...HEAD
1446
+ [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.19.2...HEAD
1447
+ [0.19.2]: https://github.com/juan294/gh-glance/compare/v0.19.1...v0.19.2
1448
+ [0.19.1]: https://github.com/juan294/gh-glance/compare/v0.19.0...v0.19.1
1425
1449
  [0.19.0]: https://github.com/juan294/gh-glance/compare/v0.18.1...v0.19.0
1426
1450
  [0.18.1]: https://github.com/juan294/gh-glance/compare/v0.18.0...v0.18.1
1427
1451
  [0.18.0]: https://github.com/juan294/gh-glance/compare/v0.17.1...v0.18.0
package/README.md CHANGED
@@ -20,15 +20,15 @@ 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:Pull requests 4:Security 5:Events
24
24
  ───────────────────────────────────────────────────────────────────────────
25
25
  ╭─ Actions · owner/repo ──────────────────────────────────────────────────╮
26
26
  │ TITLE │WORKFLOW │BRANCH │TIME │UPDATED │
27
27
  │ ─────────────────────────────────────────────────────────────────────── │
28
- │ >+ ci: pin actions to com… #443 CI develop 1m20s 93d ago │
29
- │ x fix: restore the prima… #442 Code… develop 1m28s 93d ago │
30
- │ + chore: bump dependenci… #441 CI dependa…int-10 30s 93d ago │
31
- │ - docs: update the readme #440 CI develop 15s 94d ago │
28
+ │ >+ ci: pin actions to com… #443 CI develop 1m20s 96d ago │
29
+ │ x fix: restore the prima… #442 Code… develop 1m28s 96d ago │
30
+ │ + chore: bump dependenci… #441 CI dependa…int-10 30s 96d ago │
31
+ │ - docs: update the readme #440 CI develop 15s 97d ago │
32
32
  │ │
33
33
  │ │
34
34
  ╰──────────────────────────────────────────────────────────────── 4 of 4 ─╯
@@ -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:Events` gains an
51
+ > unread count such as `(3)` when waits or problems arrive while it is closed;
52
+ > see [Events](#events).
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. -->
@@ -88,7 +89,7 @@ current.
88
89
  Security -- see [Limitations](#limitations).
89
90
  - Tab bar with live counts, pinned to the top of the pane above a divider,
90
91
  switchable via `1`-`5`, arrow keys, or `Tab`/`Shift+Tab`
91
- - **[Notifications](#notifications)** -- `5` opens the pane's status history:
92
+ - **[Events](#events)** -- `5` opens the pane's status history:
92
93
  automatic waits, failed refreshes, Security source notes, cleared causes and
93
94
  GitHub's own platform status. Only causes that need you stay above a table
94
95
  - Adjustable full-table columns, with visible header grips, a keyboard width
@@ -306,8 +307,8 @@ stdout isn't a terminal rather than streaming redraw frames into a pipe.
306
307
  | Key | Action |
307
308
  |---|---|
308
309
  | `1` `2` `3` `4` | Jump to Actions / Issues / Pull Requests / Security |
309
- | `5` | Open Notifications, the pane's status history. `↑` / `↓` and `PgUp` / `PgDn` scroll it, `r` checks GitHub's status page, and `1`-`4` or `Esc` return to a section. |
310
- | `←` / `→` | Previous / next tab (Notifications is the fifth position) |
310
+ | `5` | Open Events, the pane's status history. `↑` / `↓` and `PgUp` / `PgDn` scroll it, `r` checks GitHub's status page, and `1`-`4` or `Esc` return to a section. |
311
+ | `←` / `→` | Previous / next tab (Events is the fifth position) |
311
312
  | `Tab` / `Shift+Tab` | Next / previous tab |
312
313
  | `↑` / `↓` or `j` / `k` | Move the cursor between rows |
313
314
  | `PgUp` / `PgDn` | Move a page at a time |
@@ -327,12 +328,12 @@ stdout isn't a terminal rather than streaming redraw frames into a pipe.
327
328
  | `?` | Open or close contextual help; `n` / `p` moves through its pages |
328
329
  | `q` / `Esc` / `Ctrl+C` | Quit from ordinary navigation; `Esc` leaves feature views, editors and width mode. While editing a query or range, `q` is text and `Ctrl+C` quits. |
329
330
 
330
- ### Notifications
331
+ ### Events
331
332
 
332
333
  Lines above a table appear only when you must act: a local coordination store
333
334
  that needs `gh-glance --doctor`, a lost GitHub login, an inaccessible
334
335
  repository, or a repeated request limit that waits for `r`. Everything else
335
- goes to the fifth section, `5:Notifications`: automatic waits (budget checks,
336
+ goes to the fifth section, `5:Events`: automatic waits (budget checks,
336
337
  quota pacing, other panes), failed refreshes with their raw text, Security
337
338
  sources that are not enabled or not available, refreshes, and the moment each
338
339
  cause cleared. The table no longer moves when a wait starts or ends.
@@ -363,9 +364,11 @@ only a proxy reaches the internet the row reads "GitHub status unavailable",
363
364
  and `GH_GLANCE_GITHUB_STATUS=off` turns it off.
364
365
 
365
366
  The tab label shows how many waits and problems arrived since you last opened
366
- the view, for example `5:Notifications (3)`: red when any is a problem or needs
367
- action, yellow when all are waits. Routine refreshes never change the badge, so
368
- an idle pane still redraws nothing. The status bar keeps its one-row state
367
+ the view, for example `5:Events (3)`: red when any is a problem or needs
368
+ action, yellow when all are waits. A wait that ends in under two minutes stops
369
+ counting at that point, although the view still lists it. Each wait is measured
370
+ on its own, so repeated short waits do not add up. Routine
371
+ refreshes never change the badge, so an idle pane still redraws nothing. The status bar keeps its one-row state
369
372
  (`Paused`, `Stale`, ...) as before.
370
373
 
371
374
  ### Adjusting column widths
@@ -677,7 +680,7 @@ Environment variables work too, and the flags take precedence:
677
680
  | `GH_TOKEN`, `GITHUB_TOKEN`, `GH_ENTERPRISE_TOKEN`, `GITHUB_ENTERPRISE_TOKEN` | Used by the default `gh` provider with `gh`'s host-specific precedence. gh-glance never logs or stores the raw values. One-way credential digests keep private rows access-partitioned; credentials GitHub verifies as the same principal share its quota ledger. App-provider children remove these variables before setting their memory-only installation token. |
678
681
  | `GH_CONFIG_DIR` | Selects `gh`'s account configuration and contributes its normalized identity to the cache and governor namespaces. Separate values can isolate simultaneous panes on different accounts; see [No account pinning](#limitations). |
679
682
  | `GH_GLANCE_REFRESH=<seconds>` | Minimum active-tab poll interval, 2-3600. Sets the floor for every pane in a shell; `--refresh` takes precedence. See [Rate limit](#rate-limit). |
680
- | `GH_GLANCE_GITHUB_STATUS=off` | Turn off the GitHub platform status check in Notifications. An `http(s)` URL instead points it at another Statuspage `summary.json` endpoint. Any other value also turns it off and says so in the view. |
683
+ | `GH_GLANCE_GITHUB_STATUS=off` | Turn off the GitHub platform status check in Events. An `http(s)` URL instead points it at another Statuspage `summary.json` endpoint. Any other value also turns it off and says so in the view. |
681
684
  | `GH_GLANCE_ICONS=unicode` | Unicode status glyphs and single-cell text substitutes for Nerd Font row icons |
682
685
  | `GH_GLANCE_ICONS=ascii` | ASCII-only status and row icons |
683
686
  | `GH_GLANCE_NO_ANIMATION=1` | Stop status and run-state motion. Semantic words such as Watching, Checking, and Paused still report what is happening. |
@@ -989,7 +992,7 @@ loads the saved rows for that same target immediately. Their age remains
989
992
  explicit through the `stale` label, and the footer still reports the current
990
993
  hold; cached data never turns a failure into a false success. A rate limit
991
994
  retries automatically, so it does not take rows from the table: a sustained
992
- hold is listed in [Notifications](#notifications) with its cause (Core,
995
+ hold is listed in [Events](#events) with its cause (Core,
993
996
  GraphQL, or the tab that hit it) and next automatic retry, and the badge turns
994
997
  yellow. Only a hold that needs you, such as a repeated limit that waits for
995
998
  `r`, stays above the table with its action and the age of the last complete
@@ -1106,7 +1109,7 @@ monitor makes no GitHub request or coordination write.
1106
1109
  Advanced Security](https://docs.github.com/en/code-security/getting-started/github-security-features).
1107
1110
  On repos/plans without it, `gh-glance` doesn't crash or spam errors -- it
1108
1111
  lists each disabled source, such as `Code scanning: not enabled (needs GitHub
1109
- Advanced Security)`, in [Notifications](#notifications) instead of above the
1112
+ Advanced Security)`, in [Events](#events) instead of above the
1110
1113
  table (when no alerts are shown, the Security panel collapses them into one
1111
1114
  line such as `Code scanning, Secret scanning: not enabled here`), and still
1112
1115
  displays whatever it *can* get (Dependabot alerts work independently of
@@ -1195,11 +1198,11 @@ GH_GLANCE_ICONS=ascii gh-glance
1195
1198
  | `GraphQL: Could not resolve to a Repository...` in older gh-glance versions | The response has the same ambiguity: a missing or renamed target, or a private repository not visible to the active account. Run `gh-glance --doctor --probe`. |
1196
1199
  | Actions says `not available for this repository` while Repository access is `ok` | The repository resolved, but that endpoint is unavailable; inspect the corresponding `--doctor --probe` block. |
1197
1200
  | Row icons are blank boxes | Your terminal font is not a Nerd Font. Use `GH_GLANCE_ICONS=unicode`. |
1198
- | Notifications lists a Security "not enabled" note | Code scanning and secret scanning need GitHub Advanced Security. Dependabot alerts work independently. The note appears only when the feature genuinely is unavailable: auth, SSO and network failures show the real error instead. |
1201
+ | Events lists a Security "not enabled" note | Code scanning and secret scanning need GitHub Advanced Security. Dependabot alerts work independently. The note appears only when the feature genuinely is unavailable: auth, SSO and network failures show the real error instead. |
1199
1202
  | `none of the git remotes ... point to a known GitHub host` | `gh` is not authenticated for that host. Run `gh auth login --hostname <host>`. |
1200
1203
  | Security tab empty on an enterprise host in an older gh-glance version | Older versions could route alert endpoints separately. Current gh-glance routes every API call and the governor to one effective host; use `--repo host/owner/name`, `GH_HOST`, or a qualified `GH_REPO`, then confirm the resolved host with `--doctor`. |
1201
1204
  | Tabs start failing after working for a while | The enterprise SAML session lapsed. Re-authorize in the browser; the dashboard recovers within about 30 seconds. Run `gh-glance --doctor --probe` to confirm. |
1202
- | A tab's count is red | That tab's last fetch failed. The error is listed in Notifications (`5`); when it needs you (login, repository or endpoint access), it also stays above that tab's table, translated into what to do about it where `gh-glance` recognises the failure. |
1205
+ | A tab's count is red | That tab's last fetch failed. The error is listed in Events (`5`); when it needs you (login, repository or endpoint access), it also stays above that tab's table, translated into what to do about it where `gh-glance` recognises the failure. |
1203
1206
  | Security tab shows `?` instead of a number | The alert endpoints could not be read at all -- an expired SAML session, a token without `security_events`, or an org OAuth restriction. `?` means "unknown", not "zero"; run `gh-glance --doctor --probe` to see which probe failed and how it was classified. |
1204
1207
  | `Watching` with `next 2m` | The active tab has a shared budget probe or safe grant scheduled. The interval names this grant only; it is not a recurring polling interval. Pressing `r` raises safe priority but cannot bypass the lane or reserve. |
1205
1208
  | `Shared` | This pane is using a generation acquired for matching local subscribers. This is coalescing, not quota scarcity. |
@@ -1207,7 +1210,7 @@ GH_GLANCE_ICONS=ascii gh-glance
1207
1210
  | `Paused` without a reset time | Budget or coordinator evidence is unknown, corrupt, locked, or unwritable. No data call is started. Run `gh-glance --doctor`; also check the config directory permissions and whether another live process owns its private lock. |
1208
1211
  | `--doctor` reports an acquisition lock as `orphaned` | A dead owner or an aged incomplete lock record was found. A running pane retries recovery through the private lock protocol; plain doctor only inspects it. If the state does not change, inspect the reported lock age and the config directory permissions. |
1209
1212
  | `--doctor` reports an acquisition lock as `busy` | A live or unconfirmed owner, or a recent incomplete lock record, is present. Keep the other pane running and check again after the reported age advances; plain doctor does not steal a lock. |
1210
- | Rows stay old while the footer shows a coordination reason | The rows remain last-known-good data. The reason persists until a valid shared snapshot or completed cleanup clears that acquisition failure; a healthy rate-budget probe alone does not prove that acquisition succeeded. The footer shows the source age and Notifications (`5`) gives the longer reason. |
1213
+ | Rows stay old while the footer shows a coordination reason | The rows remain last-known-good data. The reason persists until a valid shared snapshot or completed cleanup clears that acquisition failure; a healthy rate-budget probe alone does not prove that acquisition succeeded. The footer shows the source age and Events (`5`) gives the longer reason. |
1211
1214
  | A failing tab seems to have stopped retrying | Recognized endpoint failures back off rather than re-spawning `gh` at the floor. Press `r` to request a higher-priority retry, or `R` to also clear the endpoint backoff; either way the retry waits for a safe grant, so the tab remains Watching or Paused. |
1212
1215
  | `unknown argument: -v` | `-v` used to mean `--version` and no longer does, because this CLI also has `--verbose`. Use `--version` or `--verbose` explicitly. |
1213
1216
  | Cached rows plus `Paused`, `Stale`, or `Disconnected` | The rows are last-known-good data while the live source is blocked, overdue, or unavailable. A cache read never advances source success. The footer never calls stale or disconnected rows `Watching`. |
package/index.mjs CHANGED
@@ -12362,7 +12362,7 @@ const { version } = JSON.parse(readFileSync(new URL("./package.json", import.met
12362
12362
  // this file already applies to argv vectors and to classify().
12363
12363
  const KEY_TABLE = [
12364
12364
  ["1 2 3 4", "Actions / Issues / Pull requests / Security"],
12365
- ["5", "Notifications: status history; Up / Down scroll, r checks GitHub status, Esc returns"],
12365
+ ["5", "Events: status history; Up / Down scroll, r checks GitHub status, Esc returns"],
12366
12366
  ["Left / Right", "Previous / next tab"],
12367
12367
  ["Tab / Shift+Tab", "Next / previous tab"],
12368
12368
  ["Up / Down, j / k", "Move the cursor between rows"],
@@ -12530,7 +12530,7 @@ Environment:
12530
12530
  (--refresh takes precedence)
12531
12531
  GH_GLANCE_GITHUB_STATUS=off
12532
12532
  Turn off the githubstatus.com check in
12533
- Notifications (an http(s) URL replaces the endpoint)
12533
+ Events (an http(s) URL replaces the endpoint)
12534
12534
  GH_GLANCE_ICONS=unicode Unicode status glyphs and text row substitutes
12535
12535
  GH_GLANCE_ICONS=ascii ASCII-only status and row icons
12536
12536
  GH_GLANCE_NO_ANIMATION=1 Stop motion; semantic status words remain
@@ -16828,7 +16828,7 @@ function contextHelpPages({ view, target, tab, remoteSetup = false }, keys = key
16828
16828
  issues: "Issues shows open issues in the selected repository.",
16829
16829
  prs: "Pull requests shows open requests to merge changes into the selected repository.",
16830
16830
  security: "Security shows alerts returned by the checked sources; unavailable sources can hide alerts.",
16831
- notifications: "Notifications lists status events for this pane: waits, refreshes, failures and cleared causes.",
16831
+ notifications: "Events lists what happened in this pane: waits, refreshes, failures and cleared causes.",
16832
16832
  };
16833
16833
  const title = remoteSetup ? "No GitHub remote found" : view.title;
16834
16834
  const action = remoteSetup ? "Close help with ?, then Enter to start gh repo create; or restart with gh-glance --repo owner/name."
@@ -17026,6 +17026,9 @@ const VERDICT_NOTICE_LEVELS = Object.freeze({
17026
17026
  "auth-problem": "action", unavailable: "action", "no-remote": "action", "rate-limited": "wait",
17027
17027
  });
17028
17028
  const NOTIFICATION_LOG_MAX = 500;
17029
+ // A wait that ends within this long left nothing to look at: it stays in the
17030
+ // history but does not count as unread. Each episode is measured on its own.
17031
+ const SHORT_WAIT_MS = 120_000;
17029
17032
  // Tabs whose requests draw on each rate resource; a cause on that resource
17030
17033
  // holds them up. The cost table is fixed, so this is computed once.
17031
17034
  const NOTICE_RESOURCE_TABS = Object.freeze(Object.fromEntries(RATE_RESOURCES.map((resource) =>
@@ -17058,7 +17061,8 @@ function fetchErrorLevel(error) {
17058
17061
  // one affecting the same tab, came after it); an ended entry never reopens, so a recurring condition reads
17059
17062
  // as a new episode. An entry the bound already dropped cannot be ended, so its
17060
17063
  // "Cleared" line is silently skipped. Earlier-session history is context, not
17061
- // news, so it never raises the badge.
17064
+ // news, so it never raises the badge, and neither does a short wait that has
17065
+ // already cleared.
17062
17066
  function createNotificationLog({ max = NOTIFICATION_LOG_MAX, now = Date.now } = {}) {
17063
17067
  const entries = []; // oldest first
17064
17068
  let nextId = 1;
@@ -17126,6 +17130,7 @@ function createNotificationLog({ max = NOTIFICATION_LOG_MAX, now = Date.now } =
17126
17130
  let yellow = false;
17127
17131
  for (const entry of entries) {
17128
17132
  if (entry.level === "info" || entry.source === EARLIER_SESSION_SOURCE || entry.id <= seenId) continue;
17133
+ if (entry.level === "wait" && entry.endedAt !== null && entry.endedAt - entry.firstAt < SHORT_WAIT_MS) continue;
17129
17134
  unread += 1;
17130
17135
  if (entry.level === "wait") yellow = true;
17131
17136
  else red = true;
@@ -17979,14 +17984,17 @@ const MIN_TABLE_WIDTH = Math.max(...TABS.map((t) => minimumWidthFor(t.header)));
17979
17984
  // status line survive at any width a terminal is realistically set to.
17980
17985
  const MIN_COMPACT_WIDTH = Math.max(...TABS.map((t) => minimumWidthFor(t.compactHeader)));
17981
17986
 
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;
17987
+ // The tab bar is laid out independently of the table. Full labels are used
17988
+ // whenever they fit the measured bar (~90 columns with counts on every tab, less
17989
+ // with fewer); short labels need ~76. Once the bar has dropped to short labels,
17990
+ // full ones return only with this many spare columns, so a pane parked on the
17991
+ // boundary, or a count gaining a digit, does not flip labels back and forth and
17992
+ // defeat the unchanged-frame optimization. Below the short width the bar drops
17993
+ // names and keeps digits and counts (see tabBarEntries).
17988
17994
  const TAB_LABEL_HYSTERESIS = 4;
17989
- const NOTIFICATIONS_LABEL = Object.freeze({ full: "Notifications", short: "Notices" });
17995
+ // One name at every tier: a label that changes with the pane width reads as a
17996
+ // different feature.
17997
+ const NOTIFICATIONS_LABEL = Object.freeze({ full: "Events", short: "Events" });
17990
17998
 
17991
17999
  function tabFailureSuffix({ count, failed, brokenCI }) {
17992
18000
  if (count == null) return failed ? " x" : "";
@@ -18001,7 +18009,7 @@ function tabBarWidth(entries) {
18001
18009
  // `cols` wins: full names, short names, digits with counts, the same without
18002
18010
  // gaps, then bare digits that keep only the Notifications badge -- so every
18003
18011
  // 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,
18012
+ function tabBarEntries({ cols, counts, brokenCI, firstLoad, failed, spin, preferShort = false, activeIndex,
18005
18013
  notifications = { unread: 0, tone: "none" } }) {
18006
18014
  const sections = TABS.map((tab) => {
18007
18015
  const count = counts[tab.key];
@@ -18032,9 +18040,11 @@ function tabBarEntries({ cols, counts, brokenCI, firstLoad, failed, spin, useSho
18032
18040
  // identical, so the bar does not shift between the two modes.
18033
18041
  return { key: section.key, active, label: active ? `[${body}]` : ` ${body} `,
18034
18042
  margin: index === sections.length - 1 || tier === "tight" || tier === "digits" ? 0 : tier === "full" ? 2 : 1,
18035
- color: section.color, dim: !active && section.quiet };
18043
+ color: section.color, dim: !active && section.quiet, tier };
18036
18044
  });
18037
- for (const tier of [...useShort ? [] : ["full"], "short", "compact", "tight"]) {
18045
+ const full = build("full");
18046
+ if (tabBarWidth(full) + (preferShort ? TAB_LABEL_HYSTERESIS : 0) <= cols) return full;
18047
+ for (const tier of ["short", "compact", "tight"]) {
18038
18048
  const entries = build(tier);
18039
18049
  if (tabBarWidth(entries) <= cols) return entries;
18040
18050
  }
@@ -18543,7 +18553,7 @@ function StatusBar({
18543
18553
 
18544
18554
  // ---------- Layout ----------
18545
18555
 
18546
- // Terminal size, and the label breakpoint that depends on it.
18556
+ // Terminal size.
18547
18557
  //
18548
18558
  // The usableSize guard is applied on every read path, not just the first: pty
18549
18559
  // wrappers and a terminal mid-resize report 0 or undefined, and taking either
@@ -18553,7 +18563,6 @@ function useTerminalSize(stdout) {
18553
18563
  const [size, setSize] = useState(() => ({
18554
18564
  rows: usableSize(stdout?.rows, DEFAULT_ROWS),
18555
18565
  cols: usableSize(stdout?.columns, DEFAULT_COLS),
18556
- useShortLabels: usableSize(stdout?.columns, DEFAULT_COLS) < TAB_LABEL_FULL_WIDTH,
18557
18566
  }));
18558
18567
 
18559
18568
  useEffect(() => {
@@ -18561,21 +18570,9 @@ function useTerminalSize(stdout) {
18561
18570
  function onResize() {
18562
18571
  const cols = usableSize(stdout.columns, DEFAULT_COLS);
18563
18572
  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
- });
18573
+ // Same object when nothing moved, so a resize event that changes
18574
+ // nothing cannot cost a redraw.
18575
+ setSize((previous) => previous.rows === rows && previous.cols === cols ? previous : { rows, cols });
18579
18576
  }
18580
18577
  stdout.on("resize", onResize);
18581
18578
  return () => stdout.off("resize", onResize);
@@ -19636,7 +19633,11 @@ function App({ onCreateRemote = () => {} } = {}) {
19636
19633
  jevOperationRef.current=null;runtimeJevSession?.clear();setFeature(current=>current?{...current,semantic:{message:'Jev results expired; J Run again.'}}:null);
19637
19634
  }
19638
19635
  },[feature,now]);
19639
- const { rows, cols, useShortLabels } = useTerminalSize(stdout);
19636
+ const { rows, cols } = useTerminalSize(stdout);
19637
+ // Whether the last frame's tab bar used short labels: tabBarEntries needs it
19638
+ // for hysteresis, and it depends on counts as well as width, so it is
19639
+ // tracked per frame rather than per resize.
19640
+ const shortTabLabelsRef = useRef(false);
19640
19641
  const [frame, setFrame] = useState(0);
19641
19642
  // Per tab, so switching away and back keeps your place. Keyed by item, and
19642
19643
  // both are plain state: they change only on a keypress, so an idle repo still
@@ -23121,23 +23122,24 @@ function App({ onCreateRemote = () => {} } = {}) {
23121
23122
  const liveRows = Math.min(rows, usableSize(stdout?.rows, rows));
23122
23123
  const frameRows = Math.max(1, liveRows - 1);
23123
23124
 
23125
+ const tabEntries = tabBarEntries({
23126
+ cols: frameCols,
23127
+ counts,
23128
+ brokenCI,
23129
+ firstLoad,
23130
+ failed,
23131
+ spin,
23132
+ preferShort: shortTabLabelsRef.current,
23133
+ activeIndex: notificationsOpen ? TABS.length : activeIndex,
23134
+ // An active GitHub outage keeps the badge red even after it was read.
23135
+ notifications: githubStatus.outage ? { ...notificationBadge, tone: "red" } : notificationBadge,
23136
+ });
23137
+ shortTabLabelsRef.current = tabEntries[0].tier !== "full";
23138
+
23124
23139
  return e(
23125
23140
  Box,
23126
23141
  { 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
- }),
23142
+ e(TabBar, { entries: tabEntries }),
23141
23143
  e(Divider, { width: frameCols }),
23142
23144
  e(PanelEdge, { width: frameCols, top: true, label: topLabel, labelColor: TITLE_COLOR }),
23143
23145
  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.2",
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": {