diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..716badf --- /dev/null +++ b/CLAUDE.md @@ -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`. diff --git a/README.md b/README.md index 7a8f97b..5a7b09b 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,12 @@ Code ждёт меня прямо сейчас?** достаточно открытых сессий сдвинули бы часы с центра. Чипы идут по срочности, а внутри одного состояния первой стоит **самая -давняя**: забывается та, что ждёт дольше всех, а не последняя. Время в -состоянии показывается только у первого чипа: пять счётчиков рядом — это -ряд чисел, а не ответ на вопрос. +давняя**: забывается та, что ждёт дольше всех, а не последняя. Время +показывается у **работающих** чипов — у всех сразу: именно у них счётчик о +чём-то говорит (пять минут — это сборка, сорок — сессия во что-то упёрлась). +У ждущих его нет: их возраст говорит лишь о том, когда вы перестали смотреть, +а это и так видно по самому чипу. Счётчик отключается в настройках +(«Show working time»); в меню возраст показывается у всех строк в любом случае. ## Место в панели diff --git a/lib/indicator.js b/lib/indicator.js index d4a42c8..38a91fe 100644 --- a/lib/indicator.js +++ b/lib/indicator.js @@ -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); - if (current) - this._ageLabel.age.text = formatAge(this._ageOf(current)); + // 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) + label.text = formatAge(this._ageOf(current)); + } } } diff --git a/prefs.js b/prefs.js index 9d934c6..b7d7a46 100644 --- a/prefs.js +++ b/prefs.js @@ -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'), diff --git a/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml b/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml index 0afaf58..95ceade 100644 --- a/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml +++ b/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml @@ -42,8 +42,8 @@ true - Show how long the session has been in this state - How long a session has been working, or waiting, is what decides whether to switch to it now or leave it running. + Show how long each working session has been at it + 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. true