diff --git a/docs/superpowers/specs/2026-10-15-workspace-design.md b/docs/superpowers/specs/2026-10-15-workspace-design.md new file mode 100644 index 0000000..de3ec1f --- /dev/null +++ b/docs/superpowers/specs/2026-10-15-workspace-design.md @@ -0,0 +1,159 @@ +# LPRA 0.1.3 — рабочее пространство (подпроект 1) + +Дата: 2026-10-15. Статус: согласовано в обсуждении, ждёт проверки спецификации. +Подпроект 2 (встроенный ИИ-помощник) — отдельная спецификация, делается после этого. + +## Цель + +Работа с несколькими машинами в одном месте, без потери открытых терминалов при переходах по панели. +Терминалы, файловые менеджеры (своих машин и по SSH) живут во вкладках одного рабочего пространства; +файлы перетаскиваются между панелями и в терминал. + +Критерии готовности: +- переход Машины ↔ Терминал ↔ Настройки не обрывает сессии терминалов и не сбрасывает вкладки; +- перезагрузка страницы сбрасывает пространство (с предупреждением браузера, если есть живые сессии); +- файловый менеджер — тип панели во вкладке, в том числе по SSH; +- перетаскивание файлов: между файловыми панелями (окно «переместить / копировать»), в терминал той же машины (вставка путей), в терминал другой машины — запрет с сообщением. + +## Решения, принятые в обсуждении + +| вопрос | решение | +|---|---| +| Страницы «Порты», «Статус», «Изменить» | окна поверх текущей страницы | +| Файлы рядом с терминалами в одной вкладке | да: любая из до 4 панелей — терминал, SSH-терминал, файлы, SSH-файлы | +| Перезагрузка при живых сессиях | вопрос браузера «Покинуть сайт?» | +| Архитектура | одностраничная оболочка, остальное — HTML-фрагменты с сервера (вариант A) | +| Файлы по SSH | без SFTP-библиотеки: команды на хосте через SSH, как у существующей передачи tar | +| Состояние | одно на вкладку браузера, в памяти страницы; не сохраняется при перезагрузке | + +## 1. Каркас и маршруты + +**Оболочка.** Адреса `/`, `/terminal`, `/settings` отдают один шаблон `app.html`: шапка (LPRA, «Машины · Терминал · Настройки» — «Настройки» только администраторам, статус активной панели, аккаунт) и три области `
`. +Старые адреса `/agents/{id}/terminal` и `/agents/{id}/files` отдают ту же оболочку и при загрузке создают вкладку нужного типа для этой машины (закладки не ломаются). + +**Роутер** (`static/app.js`, без библиотек): +- перехватывает клики по внутренним ссылкам и `popstate`; меняет видимую область через `history.pushState`, подсвечивает пункт шапки; +- область «Терминал» создаётся один раз и потом только скрывается (`hidden`) — сессии, WebSocket и xterm живут, пока жива вкладка браузера; +- «Машины» и «Настройки» при каждом показе подгружаются заново как фрагмент: `GET /?frag=1`, `GET /settings?frag=1`. + +**Фрагменты на сервере.** `render()` получает режим фрагмента (параметр `frag=1`): шаблон отдаёт только содержимое, без ``, шапки и подключений. Общая разметка `head` и `topbar`, сейчас скопированная в каждый шаблон, выносится в `{{define}}`-блоки. + +**Формы во фрагментах** (`app.js`, перехват `submit` внутри областей и окон): +1. `onsubmit`-обработчики (`confirm(...)`) срабатывают как обычно; отказ — ничего не происходит; +2. форма отправляется через `fetch` (`redirect: 'follow'`, тело — `FormData`); +3. итоговый адрес ответа определяет, что перерисовать: тот же фрагмент по этому адресу (сохраняются `?err=...`, `?updated=1`, `#users`); +4. формы на `/logout` и ссылки наружу работают обычной загрузкой страницы. + +**Скрипты фрагментов.** Встроенные `