spec: archive headless-panel change (#4)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-18 22:59:24 +04:00
parent 5320bc2c46
commit 521182d4a4
7 changed files with 160 additions and 0 deletions

View File

@ -0,0 +1,64 @@
# panel-appearance Specification
## Purpose
Позволяет пользователю выбрать фон панели прямо на странице, отдельно на каждом устройстве
(Safari на iPad, Toolbelt на Mac), чтобы панель не резала глаза рядом с его терминалом.
## Requirements
### Requirement: Шестерёнка настройки фона
Нижняя строка панели (рядом с «обновлено ЧЧ:ММ:СС») SHALL содержать значок ⚙. Тап по нему SHALL
раскрывать строку с образцами фона, кнопкой «свой цвет» и кнопкой «сброс»; повторный тап SHALL её
сворачивать. В свёрнутом виде, кроме значка, ничего нового на странице не видно.
#### Scenario: Раскрыть и свернуть
- **WHEN** пользователь нажимает ⚙
- **THEN** появляется строка образцов; при повторном нажатии она скрывается
#### Scenario: Попадание пальцем
- **WHEN** страница открыта в Safari на iPad
- **THEN** ⚙, образцы и кнопки нажимаются пальцем без промахов (не мельче привычных элементов
управления iPadOS)
### Requirement: Выбор фона
Выбор образца или своего цвета SHALL сразу менять фон всей панели, включая окно сообщений. Цвет
основного текста, приглушённого текста, линий дерева и разделителей SHALL подстраиваться под
яркость фона, чтобы оставаться читаемым. Цвета статусов и лимитов (зелёный, жёлтый, красный)
SHALL оставаться прежними.
#### Scenario: Светлый фон
- **WHEN** пользователь выбирает светлый образец
- **THEN** фон становится светлым, а текст — тёмным и читаемым
#### Scenario: Свой цвет
- **WHEN** пользователь нажимает «свой цвет» и выбирает цвет в системной палитре
- **THEN** фон панели становится этим цветом, текст подстраивается
### Requirement: Выбор хранится на устройстве
Выбранный фон SHALL сохраняться средствами того браузера или web view, где он выбран, и SHALL
применяться при каждом открытии и автоматической перезагрузке страницы без мелькания темы по
умолчанию. Выбор на одном устройстве SHALL NOT влиять на другие. Если клиент не сохраняет данные
страницы между своими перезапусками (возможно, web view Toolbelt), после перезапуска клиента
показывается тема по умолчанию; на сервере выбор не хранится.
#### Scenario: Клиент без постоянного хранилища
- **WHEN** выбор фона сделан в Toolbelt, а web view Toolbelt не сохраняет данные между
перезапусками iTerm2, и iTerm2 перезапущен
- **THEN** панель в Toolbelt показывает тему по умолчанию
#### Scenario: Перезагрузка страницы
- **WHEN** пользователь выбрал фон в Safari на iPad и страница перезагрузилась (сама раз в минуту
или вручную)
- **THEN** сразу отображается выбранный фон
#### Scenario: Разные устройства
- **WHEN** в Safari на iPad выбран светлый фон
- **THEN** панель в Toolbelt на Mac сохраняет свой фон
### Requirement: Сброс к теме по умолчанию
«Сброс» SHALL удалять сохранённый выбор и возвращать тему, которую отдаёт сервер. Тема по умолчанию
SHALL выглядеть как нынешний профиль iTerm2 пользователя (`Chat`: фон `#002b36`, текст `#aacace`).
#### Scenario: Сброс
- **WHEN** пользователь нажимает «сброс»
- **THEN** панель показывает тему по умолчанию, и после перезагрузки она остаётся по умолчанию

View File

@ -0,0 +1,96 @@
# panel-service Specification
## Purpose
Определяет, где и как работает сервер панели «Claude»: фоновая служба пользователя, не зависящая
от iTerm2, единственный владелец порта панели, тонкая регистрация в Toolbelt и установка/удаление.
## Requirements
### Requirement: Сервер панели работает без iTerm2
Сервер панели SHALL отдавать страницу, дерево сессий, лимиты и баланс на `127.0.0.1:<port>`
(по умолчанию 8766), пока пользователь вошёл в систему, независимо от того, запущен ли iTerm2.
#### Scenario: iTerm2 не запущен
- **WHEN** пользователь вошёл в систему, iTerm2 не запущен, а сессия Claude Code работает в tmux
- **THEN** `http://127.0.0.1:8766/` отвечает 200 и показывает эту сессию с её субагентами, моделью,
контекстом и worktree, а нижний блок показывает лимиты и баланс
#### Scenario: Имена сессий в tmux
- **WHEN** сессия Claude Code работает в панели tmux
- **THEN** её имя берётся из заголовка панели tmux или из `сессия:окно имя_окна`, как и раньше
### Requirement: Служба стартует при входе и перезапускается при падении
Сервер панели SHALL запускаться автоматически при входе пользователя в систему и SHALL
перезапускаться, если его процесс завершился.
#### Scenario: Вход в систему
- **WHEN** пользователь входит в систему на Mac после перезагрузки
- **THEN** сервер панели запускается без открытия iTerm2 и каких-либо действий пользователя
#### Scenario: Падение процесса
- **WHEN** процесс сервера панели завершился (ошибка, `kill`)
- **THEN** он снова запущен и отвечает на порту в пределах нескольких десятков секунд
### Requirement: Порт панели занимает один процесс
На порту панели SHALL работать ровно один сервер; лимиты и баланс SHALL опрашиваться одним
процессом, чтобы не удваивать запросы к API.
#### Scenario: iTerm2 запускается при работающей службе
- **WHEN** служба уже отвечает на порту и пользователь запускает iTerm2
- **THEN** второй сервер не запускается, в API лимитов идёт один поток запросов, а в Toolbelt
появляется панель «Claude», показывающая ту же страницу
### Requirement: Toolbelt показывает панель фоновой службы
Скрипт панели в iTerm2 SHALL регистрировать web view «Claude» в Toolbelt, указывающий на адрес
фоновой службы, и SHALL NOT поднимать собственный сервер.
#### Scenario: Toolbelt при запущенной службе
- **WHEN** iTerm2 запущен и пользователь выбирает Toolbelt → Claude
- **THEN** панель показывает ту же страницу, что и `http://127.0.0.1:8766/` в браузере
#### Scenario: Служба не отвечает при старте iTerm2
- **WHEN** iTerm2 стартует, а служба на порту не отвечает
- **THEN** скрипт просит launchd запустить службу и всё равно регистрирует web view
### Requirement: Сервер доступен только локально
Сервер панели SHALL слушать только `127.0.0.1`; доступ с других устройств возможен только через
туннель (например, SSH local forward).
#### Scenario: Запрос из локальной сети
- **WHEN** устройство в той же сети обращается к Mac по его LAN-адресу на порт панели
- **THEN** соединение не устанавливается
### Requirement: Действия по клику работают через tmux
Переход к сессии и открытие worktree по клику SHALL выполняться средствами tmux для сессий в tmux,
независимо от iTerm2 и от того, на каком устройстве открыта страница.
#### Scenario: Переход к сессии с iPad
- **WHEN** пользователь с iPad (страница через туннель) нажимает «↗ вкладка» у сессии в tmux
- **THEN** клиент tmux переключается на окно и панель этой сессии
#### Scenario: Открытие worktree с iPad
- **WHEN** пользователь нажимает `⎇ папка` у сессии в tmux
- **THEN** в том же окне tmux открывается новая панель с файловым менеджером в этой папке
### Requirement: Установка и удаление службы
`install.sh` SHALL устанавливать, (пере)запускать службу и останавливать прежний сервер, державший
порт; повторный запуск SHALL быть безопасным. `uninstall.sh` SHALL останавливать и удалять службу.
#### Scenario: Обновление старой установки
- **WHEN** у пользователя стоит прежняя версия (сервер внутри iTerm2) и он запускает `install.sh`
- **THEN** прежний сервер остановлен, служба установлена и отвечает на порту, а iTerm2-скрипт
обновлён до регистратора
#### Scenario: Повторная установка
- **WHEN** `install.sh` запускается второй раз подряд
- **THEN** служба одна, отвечает на порту, записи хуков в `settings.json` не задублированы
#### Scenario: Запуск по SSH
- **WHEN** `install.sh` или `uninstall.sh` запущен из SSH-сессии
- **THEN** скрипт сообщает, что его нужно запускать за самим Mac, и завершается с ненулевым кодом,
ничего не изменив: прежний сервер продолжает работать
#### Scenario: Удаление
- **WHEN** пользователь запускает `uninstall.sh`
- **THEN** служба остановлена, её описание удалено, порт свободен и после повторного входа в систему
служба не запускается