Cap the chips at three, and make the menu report-only
Two changes that pull in the same direction: the panel says less, and the menu stops pretending to do anything. The chip row had no bound. It sits in the centre box next to the clock, so enough open sessions would have shoved the clock off centre. It now shows the first N, settable and three by default, and counts the rest as "+N". Chips are already ordered by urgency, so the ones that survive the cut are the ones that need you soonest. Labels are still assigned across every session, including hidden ones, so a chip does not change when the cap does or when a session ahead of it disappears. Clicking a menu row used to switch the zellij tab and raise a terminal. That is gone. It cost real machinery for what it saved -- gnome-terminal runs every window under one shared server process, so windows cannot be matched by pid and the code fell back to matching the zellij session name against window titles, with all the ways that misses. The menu reports status; alt-tab is not the bottleneck. Rows are built inert rather than demoted after the fact, because PopupBaseMenuItem latches _activatable in its constructor. zellij tab lookup stays: naming the tab is the better half of that feature and costs one process every couple of minutes. The preferences test now asserts a control per settings key rather than a switch per key, so the new spin row counts and a future non-boolean setting cannot slip in without one.
This commit is contained in:
+28
-50
@@ -6,7 +6,6 @@ import Clutter from 'gi://Clutter';
|
||||
import GLib from 'gi://GLib';
|
||||
import Pango from 'gi://Pango';
|
||||
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||||
import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||
@@ -27,11 +26,6 @@ function stateLabel(state) {
|
||||
}
|
||||
}
|
||||
|
||||
const TERMINAL_CLASSES = [
|
||||
'gnome-terminal', 'org.gnome.terminal', 'kitty', 'alacritty',
|
||||
'foot', 'wezterm', 'konsole', 'xterm', 'ghostty',
|
||||
];
|
||||
|
||||
/** Paint a state glyph in the panel's own text colour.
|
||||
*
|
||||
* Nothing here picks a colour: the foreground comes from the theme node, so
|
||||
@@ -164,6 +158,7 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
_updatePanel(sessions) {
|
||||
const showAge = this._settings.get_boolean('show-age');
|
||||
const abbreviate = this._settings.get_boolean('abbreviate-names');
|
||||
const maxChips = this._settings.get_int('max-chips');
|
||||
|
||||
this._chipLabels = assignChips(
|
||||
sessions.map(s => ({
|
||||
@@ -181,23 +176,38 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
// than sitting there empty.
|
||||
this.visible = sessions.length > 0;
|
||||
|
||||
// Chips are ordered by urgency, so cutting the tail keeps the ones that
|
||||
// need you soonest. Without a cap the row grows without bound, and it
|
||||
// sits in the centre box -- enough sessions would shove the clock off
|
||||
// centre. Labels are still assigned over every session, so the menu and
|
||||
// the panel agree and a chip does not change when the cap does.
|
||||
const shown = sessions.slice(0, maxChips);
|
||||
const hidden = sessions.length - shown.length;
|
||||
|
||||
// Age rides on the first chip only. Sessions are sorted by urgency, so
|
||||
// that is the one whose age decides anything; five ages side by side
|
||||
// would just be a wide row of numbers.
|
||||
const ageOnFirst = showAge && sessions.length > 0;
|
||||
const signature = sessions
|
||||
const ageOnFirst = showAge && shown.length > 0;
|
||||
const signature = shown
|
||||
.map(s => `${s.sessionId}:${s.state}:${labelFor(s)}`)
|
||||
.join('|') + `|${ageOnFirst}`;
|
||||
.join('|') + `|${ageOnFirst}|${hidden}`;
|
||||
if (signature !== this._chipSignature) {
|
||||
this._chipBox.destroy_all_children();
|
||||
this._ageLabel = null;
|
||||
sessions.forEach((session, i) => {
|
||||
shown.forEach((session, i) => {
|
||||
const { chip, age } = this._buildChip(
|
||||
session, labelFor(session), ageOnFirst && i === 0);
|
||||
if (age)
|
||||
this._ageLabel = { age, session };
|
||||
this._chipBox.add_child(chip);
|
||||
});
|
||||
if (hidden > 0) {
|
||||
this._chipBox.add_child(new St.Label({
|
||||
style_class: 'ccs-overflow',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
text: `+${hidden}`,
|
||||
}));
|
||||
}
|
||||
this._chipSignature = signature;
|
||||
}
|
||||
|
||||
@@ -210,8 +220,10 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
|
||||
// Rebuild only when the set of sessions or their states changed; ages
|
||||
// alone are refreshed in place so an open menu does not flicker.
|
||||
// The tab name is not in the signature: it only feeds the subtitle,
|
||||
// which is refreshed in place below.
|
||||
const signature = sessions
|
||||
.map(s => `${s.sessionId}:${s.state}:${this._tabFor(s) ?? ''}:${this._chipLabels?.get(s.sessionId) ?? ''}`)
|
||||
.map(s => `${s.sessionId}:${s.state}:${this._chipLabels?.get(s.sessionId) ?? ''}`)
|
||||
.join('|');
|
||||
if (signature !== this._rowSignature) {
|
||||
this._rebuildRows(sessions);
|
||||
@@ -259,13 +271,12 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
}
|
||||
|
||||
_buildRow(session) {
|
||||
const tab = this._tabFor(session);
|
||||
// Reactivity is decided at construction, not patched afterwards:
|
||||
// PopupBaseMenuItem latches _activatable in its constructor, so a row
|
||||
// switched to reactive=false later keeps the styling of a clickable one
|
||||
// and still looks like it does something.
|
||||
// The menu reports; it does not act. Rows are built inert rather than
|
||||
// switched off afterwards, because PopupBaseMenuItem latches
|
||||
// _activatable in its constructor and a row demoted later keeps the
|
||||
// styling of a clickable one.
|
||||
const item = new PopupMenu.PopupBaseMenuItem(
|
||||
tab ? {} : { reactive: false, can_focus: false });
|
||||
{ reactive: false, can_focus: false });
|
||||
item.add_style_class_name('ccs-row');
|
||||
|
||||
const column = new St.BoxLayout({ vertical: true, x_expand: true });
|
||||
@@ -296,9 +307,6 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
column.add_child(subtitle);
|
||||
item.add_child(column);
|
||||
|
||||
if (tab)
|
||||
item.connect('activate', () => this._switchTo(session, tab));
|
||||
|
||||
this._rows.push({ sessionId: session.sessionId, age, subtitle });
|
||||
return item;
|
||||
}
|
||||
@@ -360,36 +368,6 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
.catch(e => logError(e, 'claude-code-status: zellij refresh failed'));
|
||||
}
|
||||
|
||||
_switchTo(session, tab) {
|
||||
this._zellij.goToTab(session.zellijSession, tab);
|
||||
this._focusTerminal(session);
|
||||
}
|
||||
|
||||
/** Best effort: raise a terminal window showing this zellij session.
|
||||
*
|
||||
* Matching by pid does not work for gnome-terminal, where every window
|
||||
* belongs to one shared server process, so the window title is the only
|
||||
* handle available -- and zellij puts the session name there.
|
||||
*/
|
||||
_focusTerminal(session) {
|
||||
if (!session.zellijSession)
|
||||
return;
|
||||
// list_all_windows() rather than get_window_actors(): the latter is
|
||||
// deprecated from GNOME 46 on, and this has to work across 45-48.
|
||||
for (const win of global.display.list_all_windows()) {
|
||||
const wmClass = (win.get_wm_class() ?? '').toLowerCase();
|
||||
if (!TERMINAL_CLASSES.some(c => wmClass.includes(c)))
|
||||
continue;
|
||||
// Only a window that names this zellij session is raised. Falling
|
||||
// back to any terminal at all would raise an unrelated one, which
|
||||
// is worse than leaving focus where the user put it.
|
||||
if ((win.get_title() ?? '').includes(session.zellijSession)) {
|
||||
Main.activateWindow(win);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Visuals --------------------------------------------------------
|
||||
|
||||
// ---- Teardown -------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user