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 +9 -0
- package/README.md +4 -3
- package/index.mjs +37 -42
- package/package.json +1 -1
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:
|
|
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:
|
|
51
|
-
> problems arrive while it is closed;
|
|
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
|
|
17983
|
-
//
|
|
17984
|
-
//
|
|
17985
|
-
//
|
|
17986
|
-
//
|
|
17987
|
-
|
|
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: "
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
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
|
|
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