gh-glance 0.5.0 → 0.5.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 (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/index.mjs +40 -6
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.1] - 2026-08-04
9
+
10
+ ### Changed
11
+
12
+ - **The blank-icons hint waits for a start that is actually stuck.** It shipped
13
+ in 0.5.0 on the loading line unconditionally, so every start put a line naming
14
+ an environment variable in front of everyone for the second or two the first
15
+ fetch takes -- a dim `loading actions…` reads as "working", the same line with
16
+ a remedy attached reads as a warning about a problem you do not have. It now
17
+ appears only once a first fetch has been running 3s, which is past the slow end
18
+ of what `gh run list` costs on the tab you land on (measured: 1.4-3.0s after
19
+ the first frame, against 0.6-1.1s for the other three tabs). An ordinary start
20
+ never shows it; a pane of blank boxes that is genuinely waiting still explains
21
+ itself.
22
+
8
23
  ## [0.5.0] - 2026-08-04
9
24
 
10
25
  Findings from a cross-functional audit, filtered hard: everything that would
@@ -516,6 +531,7 @@ engineering, security, QA and UX. What follows is what changed as a result.
516
531
  - The `main` field from `package.json`. It advertised the file as importable,
517
532
  but importing it took over the terminal or exited the host process.
518
533
 
534
+ [0.5.1]: https://github.com/juan294/gh-glance/releases/tag/v0.5.1
519
535
  [0.5.0]: https://github.com/juan294/gh-glance/releases/tag/v0.5.0
520
536
  [0.4.1]: https://github.com/juan294/gh-glance/releases/tag/v0.4.1
521
537
  [0.4.0]: https://github.com/juan294/gh-glance/releases/tag/v0.4.0
package/index.mjs CHANGED
@@ -53,6 +53,23 @@ const REFRESH_MS = 5000;
53
53
  // last movement, not from tab switches or Enter) quietly drops it.
54
54
  const SELECTION_IDLE_MS = 60_000;
55
55
 
56
+ // How long a first fetch has to still be unresolved before the loading line
57
+ // offers the Nerd Font escape hatch. The hint used to be unconditional, which
58
+ // put a line naming an environment variable in front of every user on every
59
+ // start to catch the minority whose terminal cannot draw the glyphs -- a dim
60
+ // "loading actions…" reads as "working", the same line with a remedy attached
61
+ // reads as a warning, and it was on screen for the whole first fetch every time.
62
+ //
63
+ // The threshold is set from the fetch that actually gates it, not from a round
64
+ // number: the hint hangs off the *visible* tab's loading line, and the tab you
65
+ // land on is Actions, whose `gh run list` is by far the slowest call here
66
+ // (measured against this repo: issues/PRs/alerts resolve 0.6-1.1s after the
67
+ // first frame, runs 1.4-3.0s). 1.5s would have fired on most ordinary starts --
68
+ // i.e. changed nothing. 3s sits past the slow end of that range, so a start that
69
+ // reaches it is genuinely stuck rather than merely fetching, which is exactly
70
+ // the moment the remedy is worth a line.
71
+ const ICON_HINT_AFTER_MS = 3000;
72
+
56
73
  // `gh run list` costs roughly linearly in --limit (measured: ~1.2s at 20 runs,
57
74
  // ~3.0s at 100, ~4.9s at 150), so asking for a fixed 150 to render ~35 visible
58
75
  // rows was paying several seconds per refresh for rows nobody sees. Actions is
@@ -2207,6 +2224,10 @@ function App() {
2207
2224
  // The `?` overlay. Renders only on a keypress, so consecutive idle frames are
2208
2225
  // still byte-identical and the redraw suppression is untouched.
2209
2226
  const [showHelp, setShowHelp] = useState(false);
2227
+ // Set once, ICON_HINT_AFTER_MS into a first load that is still running. Never
2228
+ // reset: it only gates a line that a resolved tab stops rendering anyway, and
2229
+ // clearing it would cost a second state write for no visible difference.
2230
+ const [iconHintDue, setIconHintDue] = useState(false);
2210
2231
  const [errors, setErrors] = useState({ actions: null, issues: null, prs: null, security: null });
2211
2232
  const [loading, setLoading] = useState({ actions: true, issues: true, prs: true, security: true });
2212
2233
  const [now, setNow] = useState(new Date());
@@ -2613,6 +2634,18 @@ function App() {
2613
2634
  return () => clearInterval(id);
2614
2635
  }, [showSpinner]);
2615
2636
 
2637
+ // Armed on the same condition the loading line renders on, so a start that
2638
+ // resolves inside ICON_HINT_AFTER_MS clears the timer on the way past and
2639
+ // never writes state at all -- the fast path stays exactly as many renders as
2640
+ // it was. `anyFirstLoad` excludes tabs that failed (see firstLoad above), so a
2641
+ // wedged tab cannot hold this armed: an error line already says what happened,
2642
+ // and a font hint underneath it would be answering a question nobody asked.
2643
+ useEffect(() => {
2644
+ if (!anyFirstLoad || iconHintDue) return;
2645
+ const timer = setTimeout(() => setIconHintDue(true), ICON_HINT_AFTER_MS);
2646
+ return () => clearTimeout(timer);
2647
+ }, [anyFirstLoad, iconHintDue]);
2648
+
2616
2649
  const items = data[tab.key];
2617
2650
  const error = errors[tab.key];
2618
2651
  const spin = SPINNER[frame % SPINNER.length];
@@ -2834,12 +2867,13 @@ function App() {
2834
2867
  { dimColor: true },
2835
2868
  firstLoad[tab.key]
2836
2869
  ? `${showSpinner ? spin : SPINNER[0]} loading ${tab.label.toLowerCase()}…` +
2837
- // Shown only while the first fetch is in flight, so it costs a
2838
- // second of one line at startup and nothing after -- and it lands
2839
- // exactly when someone is staring at a fresh pane deciding whether
2840
- // the thing works. Suppressed once icons are already ASCII, since
2841
- // then there is nothing to fix.
2842
- (USING_NERD_ICONS ? " (icons blank? GH_GLANCE_ICONS=unicode)" : "")
2870
+ // Appended only once a first fetch has been running for
2871
+ // ICON_HINT_AFTER_MS, so an ordinary start never shows it: the
2872
+ // remedy is for someone looking at a pane of blank boxes, and by
2873
+ // then they have been looking long enough to want an explanation.
2874
+ // Suppressed once icons are already ASCII, since then there is
2875
+ // nothing to fix.
2876
+ (USING_NERD_ICONS && iconHintDue ? " (icons blank? GH_GLANCE_ICONS=unicode)" : "")
2843
2877
  : tab.key === "security" && securityNotes.length === ALERT_SOURCES.length
2844
2878
  ? "no alert sources available"
2845
2879
  : `no ${tab.countLabel}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-glance",
3
- "version": "0.5.0",
3
+ "version": "0.5.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": {