Put the clock on the working chips, and on all of them
The counter rode on the first chip, whichever state it happened to be in. It now shows on every chip that is working and on no other: how long a session has been at it is the thing that decides whether to wait for it, where the age of an idle one only says how long ago you stopped looking. The setting keeps its key and changes its name to "Show working time". CLAUDE.md is new, and its first rule is the one broken twice: writing to dconf from a shell attached to the live session bus wipes settings that cannot be recovered.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Работа над этим расширением
|
||||
|
||||
## Никогда не писать в dconf живой сессии
|
||||
|
||||
**Запрещено** выполнять `gsettings set`, `gsettings reset`, `dconf write` и `dconf reset`
|
||||
из bash этой сессии — ни голыми, ни с префиксом `XDG_CONFIG_HOME=...`.
|
||||
|
||||
Почему префикс не помогает: базу выбирает не клиент, а `dconf-service`, поднятый шиной
|
||||
живой сессии, и берёт он её из **своего** окружения. Любая запись уходит в
|
||||
`~/.config/dconf/user` Антона.
|
||||
|
||||
Чем это кончалось (дважды): затёрт `org.gnome.shell enabled-extensions` — во второй раз,
|
||||
9 и 10 августа 2026, на работающем shell, отчего у него мгновенно отвалились dock, ding,
|
||||
appindicators и tiling-assistant. Прежний список dconf не хранит, восстановить его точно
|
||||
нельзя, только реконструировать.
|
||||
|
||||
Что делать вместо:
|
||||
|
||||
- нужно поменять настройку в **живой** сессии — отдать Антону команду, он выполнит её сам
|
||||
через `! <команда>`;
|
||||
- нужна настройка для **вложенного** shell — экспортировать `XDG_CONFIG_HOME` до форка
|
||||
шины и звать `gsettings` уже внутри этой шины (см. рецепт ниже);
|
||||
- читать (`dconf read`, `gsettings get`) можно свободно.
|
||||
|
||||
## Проверка изменений — только во вложенном shell
|
||||
|
||||
Живую сессию не трогаем вообще: расширение включает и перезагружает shell Антон сам.
|
||||
У него несколько сессий Claude Code в zellij внутри gnome-terminal, и падение
|
||||
gnome-shell унесёт весь его рабочий набор.
|
||||
|
||||
Рецепт для проверок только на чтение (GNOME 46, проверен 10.08.2026):
|
||||
|
||||
```sh
|
||||
XDG_STATE_HOME=$TMP/state timeout 50 dbus-run-session -- \
|
||||
gnome-shell --nested --wayland > $TMP/nested.log 2>&1
|
||||
```
|
||||
|
||||
Подставные файлы сессий обязаны нести `event_ts` (плюс `since`, `started`): без него
|
||||
`SessionStore` считает их древними и **удаляет** до первой отрисовки.
|
||||
|
||||
Скриншот из вложенного shell снять нельзя. Чтобы увидеть, что он нарисовал, добавляем
|
||||
временный `log()` в код, читаем его из `nested.log` — и убираем инструментацию перед
|
||||
коммитом.
|
||||
|
||||
## Коммиты
|
||||
|
||||
Без трейлера `Co-Authored-By`.
|
||||
@@ -47,9 +47,12 @@ Code ждёт меня прямо сейчас?**
|
||||
достаточно открытых сессий сдвинули бы часы с центра.
|
||||
|
||||
Чипы идут по срочности, а внутри одного состояния первой стоит **самая
|
||||
давняя**: забывается та, что ждёт дольше всех, а не последняя. Время в
|
||||
состоянии показывается только у первого чипа: пять счётчиков рядом — это
|
||||
ряд чисел, а не ответ на вопрос.
|
||||
давняя**: забывается та, что ждёт дольше всех, а не последняя. Время
|
||||
показывается у **работающих** чипов — у всех сразу: именно у них счётчик о
|
||||
чём-то говорит (пять минут — это сборка, сорок — сессия во что-то упёрлась).
|
||||
У ждущих его нет: их возраст говорит лишь о том, когда вы перестали смотреть,
|
||||
а это и так видно по самому чипу. Счётчик отключается в настройках
|
||||
(«Show working time»); в меню возраст показывается у всех строк в любом случае.
|
||||
|
||||
## Место в панели
|
||||
|
||||
|
||||
+22
-15
@@ -61,6 +61,7 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
this._store = new SessionStore();
|
||||
this._zellij = new ZellijTabs();
|
||||
this._chipLabels = new Map();
|
||||
this._ageLabels = new Map();
|
||||
this._rows = [];
|
||||
|
||||
this._buildPanel();
|
||||
@@ -222,21 +223,24 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
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 && shown.length > 0;
|
||||
// Age rides on the working chips, and on all of them. A session that
|
||||
// is working is the one you might be waiting on, and how long it has
|
||||
// been at it is the whole question -- five minutes is a build, forty
|
||||
// is a session stuck on something. The states where nothing is
|
||||
// happening have no such clock: their age says how long ago you
|
||||
// stopped looking, which the row's own presence already says.
|
||||
const ageOn = session => showAge && session.state === 'busy';
|
||||
const signature = shown
|
||||
.map(s => `${s.sessionId}:${s.state}:${labelFor(s)}`)
|
||||
.join('|') + `|${ageOnFirst}|${hidden}`;
|
||||
.join('|') + `|${showAge}|${hidden}`;
|
||||
if (signature !== this._chipSignature) {
|
||||
this._chipBox.destroy_all_children();
|
||||
this._ageLabel = null;
|
||||
shown.forEach((session, i) => {
|
||||
this._ageLabels = new Map();
|
||||
shown.forEach(session => {
|
||||
const { chip, age } = this._buildChip(
|
||||
session, labelFor(session), ageOnFirst && i === 0);
|
||||
session, labelFor(session), ageOn(session));
|
||||
if (age)
|
||||
this._ageLabel = { age, sessionId: session.sessionId };
|
||||
this._ageLabels.set(session.sessionId, age);
|
||||
this._chipBox.add_child(chip);
|
||||
});
|
||||
if (hidden > 0) {
|
||||
@@ -249,13 +253,16 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
|
||||
this._chipSignature = signature;
|
||||
}
|
||||
|
||||
if (this._ageLabel) {
|
||||
// Looked up again rather than captured: a session that returns to
|
||||
// the same state within one refresh keeps the signature unchanged,
|
||||
// and a captured object would then show an age that stopped moving.
|
||||
const current = sessions.find(s => s.sessionId === this._ageLabel.sessionId);
|
||||
// Looked up again rather than captured: a session that returns to the
|
||||
// same state within one refresh keeps the signature unchanged, and a
|
||||
// captured object would then show an age that stopped moving.
|
||||
if (this._ageLabels.size) {
|
||||
const byId = new Map(sessions.map(s => [s.sessionId, s]));
|
||||
for (const [sessionId, label] of this._ageLabels) {
|
||||
const current = byId.get(sessionId);
|
||||
if (current)
|
||||
this._ageLabel.age.text = formatAge(this._ageOf(current));
|
||||
label.text = formatAge(this._ageOf(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ export default class ClaudeCodeStatusPreferences extends ExtensionPreferences {
|
||||
_('The most urgent sessions get a chip; the rest are counted as “+N”.'),
|
||||
1, 12));
|
||||
dispGroup.add(this._switchRow(settings, 'show-age',
|
||||
_('Show time in state'),
|
||||
_('Shown on the most urgent session only.')));
|
||||
_('Show working time'),
|
||||
_('A counter on every session that is working, so you can see how long it has been at it. The menu lists ages either way.')));
|
||||
|
||||
const zellijGroup = new Adw.PreferencesGroup({
|
||||
title: _('zellij'),
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
</key>
|
||||
<key name="show-age" type="b">
|
||||
<default>true</default>
|
||||
<summary>Show how long the session has been in this state</summary>
|
||||
<description>How long a session has been working, or waiting, is what decides whether to switch to it now or leave it running.</description>
|
||||
<summary>Show how long each working session has been at it</summary>
|
||||
<description>A counter on every chip that is working, which is what says whether to wait for it or go and look: five minutes is a build, forty is a session stuck on something. The idle states get no counter — their age only says how long ago you stopped looking.</description>
|
||||
</key>
|
||||
<key name="zellij-integration" type="b">
|
||||
<default>true</default>
|
||||
|
||||
Reference in New Issue
Block a user