docs: 0.1.3 workspace design - one-page shell, tabs with terminal and file panes (machines and SSH hosts), drag and drop, machine dialogs

This commit is contained in:
redtea committed 2026-10-15 19:51:02 +03:00
1 parent 0fa3de757c
commit 698bc68f99
1 file changed
+159
@@ -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, «Машины · Терминал · Настройки» — «Настройки» только администраторам, статус активной панели, аккаунт) и три области `<section data-view="machines|terminal|settings">`.
Старые адреса `/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`): шаблон отдаёт только содержимое, без `<html>`, шапки и подключений. Общая разметка `head` и `topbar`, сейчас скопированная в каждый шаблон, выносится в `{{define}}`-блоки.
**Формы во фрагментах** (`app.js`, перехват `submit` внутри областей и окон):
1. `onsubmit`-обработчики (`confirm(...)`) срабатывают как обычно; отказ — ничего не происходит;
2. форма отправляется через `fetch` (`redirect: 'follow'`, тело — `FormData`);
3. итоговый адрес ответа определяет, что перерисовать: тот же фрагмент по этому адресу (сохраняются `?err=...`, `?updated=1`, `#users`);
4. формы на `/logout` и ссылки наружу работают обычной загрузкой страницы.
**Скрипты фрагментов.** Встроенные `<script>` во фрагментах не исполняются. Поведение страниц оформляется модулями с `mount(root)` / `unmount()`, которые вызывает роутер: копирование команд и коды установки (настройки), `users.js`, список машин с опросом присутствия.
`pin.js`, `scroll.js`, `account.js` подключаются один раз в оболочке.
**Предупреждение при уходе.** `beforeunload` спрашивает «Покинуть сайт?», только если в рабочем пространстве есть живая сессия терминала или SSH-терминала.
## 2. Рабочее пространство и типы панелей
**Модель.** Вкладка содержит 1–4 панели в существующих раскладках (`1`, `h`, `v`, сетка), с изменением размеров и перетаскиванием панелей и вкладок, как сейчас. Тип панели:
| тип | что это |
|---|---|
| `term` | терминал на машине (как сейчас) |
| `ssh` | SSH-терминал через машину (как сейчас) |
| `files` | файловый менеджер на машине |
| `sshfiles` | файловый менеджер на SSH-хосте через машину |
**Разделение кода** (сейчас всё в `terminal.js` и `files.html`):
- `workspace.js` — вкладки, раскладки, разделение, перетаскивание панелей и вкладок, меню «+». С панелями работает через общий интерфейс: `el`, `kind`, `agent`, `title`, `state` и `text` (точка и строка статуса), `fit()`, `focus()`, `close()`, `acceptsDrop(item)`, `drop(item)`;
- `termpane.js` — нынешний класс `Session` (терминал и SSH-терминал), вынесенный почти без изменений;
- `filepane.js` — файловая панель (часть 3);
- `fileviews.js` — общие окна файлов (просмотр и правка, hex, свойства, перемещение, загрузка, редактор SQLite).
**Меню «+»** (на компьютере и телефоне):
- «Терминал ›» — «здесь (машина)», другие машины в сети, «SSH-хост…»;
- «Файловый менеджер ›» — «здесь (машина)», другие машины в сети, «SSH-хост…».
«SSH-хост…» открывает существующее окно SSH-подключения; тип панели зависит от пункта меню. Последний SSH-хост (без секретов) запоминается, как сейчас.
**Из списка машин:** кнопка терминала → новая вкладка-терминал этой машины и переход в «Терминал»; кнопка файлов → новая вкладка «Файлы». Если пространства ещё нет, оно создаётся.
**Заголовки.** Вкладка файлов — иконка папки и `машина · папка`, путь обрезается слева, обновляется при смене папки. Переименование двойным кликом — как у терминалов. Точка статуса файловой панели — машина в сети / офлайн / ошибка.
**Телефон.** Вкладки и меню те же; разделения и перетаскивания, как и сейчас, только на компьютере.
## 3. Файловая панель
Содержимое нынешней страницы файлов как компонент `FilePane(source)`: строка пути, «Вверх», «Обновить», «Папка / Файл / Загрузить», таблица с отметками, «В zip», «Удалить выбранное», загрузка перетаскиванием из ОС.
Состояние (текущая папка, список, отметки) — у экземпляра; элементы ищутся внутри корня панели, без глобальных `id`. Две файловые панели в одной вкладке не мешают друг другу.
**Общие окна** (`fileviews.js`) — одно на страницу. При открытии окну передаётся панель-источник: машина или SSH, путь, что обновить после действия.
**Источник данных** — один интерфейс (`list`, `read`, `write`, `mkdir`, `delete`, `move`, `copy`, `stat`, `chmod`, `download`, `upload`) с двумя реализациями:
- машина — существующий `/api/panel/agents/{id}/fs/...`;
- SSH-хост — новые задачи агента `sshfs.*` (ниже).
**Узкие панели** (меньше ~520 px, в разделённой вкладке): прячется колонка «Изменён», кнопки строки сворачиваются в меню «⋯» — как сейчас на телефоне.
### Файлы по SSH (`sshfiles`)
Агент держит одно SSH-подключение на панель: `sshConnect` и проверка ключа хоста из SSH-терминала. Подключение открывается при создании панели (окно «SSH-хост…»), получает id, закрывается при закрытии панели или через 10 минут простоя. Секреты передаются агенту один раз, панель их не хранит.
Операции — команды на хосте, пути в одинарных кавычках (`shq`):
| операция | команда |
|---|---|
| список | `stat` по содержимому папки; формат выбирается один раз при подключении: GNU/busybox `stat -c`, BSD/macOS `stat -f`; без `stat` — разбор `ls -la` без точного времени |
| чтение, скачивание | `cat -- 'файл'`, поток через stdout |
| запись, загрузка | `cat > 'файл.tmp' && mv 'файл.tmp' 'файл'`, поток через stdin |
| папка, удаление, переименование | `mkdir -p`, `rm -rf`, `mv` |
| права, владелец | `chmod`, `chown` |
| копирование, перенос внутри хоста | `cp -a`, `mv` |
| перенос между хостом и машинами | существующая передача tar по SSH |
Новые задачи агента: `sshfs.open` (подключиться, вернуть id и домашнюю папку), `sshfs.close`, `sshfs.list`, `sshfs.read`, `sshfs.write`, `sshfs.mkdir`, `sshfs.delete`, `sshfs.move`, `sshfs.copy`, `sshfs.stat`, `sshfs.chmod`, `sshfs.send` (скачивание через существующие куски `xfer`), `sshfs.put` (загрузка через куски `xfer`).
Серверу — те же правила доступа, что у файловых операций машины; изменения пишутся в аудит.
Не входит: zip и распаковка на SSH-хосте, редактор SQLite по SSH — эти кнопки в `sshfiles` скрыты. Новых зависимостей нет.
## 4. Перетаскивание
**Что тащится:** строка файла или папки; если строка отмечена — все отмеченные строки панели. У курсора бейдж (имя или «N элементов»).
Механизм — на `pointer`-событиях внутри страницы, тот же, что уже двигает вкладки и панели; подсказка над целью говорит, что произойдёт.
| цель | результат |
|---|---|
| другая файловая панель (любая машина или SSH-хост) | небольшое окно у места броска: «Переместить / Копировать / Отмена», куда (папка цели), «если уже есть: сообщить ▾». Внутри машины — `fs.copy`; внутри SSH-хоста — `cp`/`mv`; между разными — существующая передача tar с прогрессом в этом же окне |
| папка в списке панели | то же, цель — эта папка |
| терминал той же машины / того же SSH-хоста | пути в одинарных кавычках через пробел вставляются в терминал, без Enter |
| терминал другой машины | цель подсвечивается красным, подсказка «Нельзя: файл на X, терминал на Y» |
| заголовок вкладки | через 0,5 с наведения вкладка активируется, бросить можно в её панель |
| пустое место / Esc | отмена |
Правила конфликтов имён — те же, что в окне «Переместить или копировать». Загрузка из ОС перетаскиванием — в любую файловую панель. На телефоне перетаскивания нет; остаётся окно перемещения с выбором машины или SSH.
## 5. Окна машины
«Порты», «Статус», «Изменить» — нынешние страницы как фрагменты (`?frag=1`) в общем окне `machinedlg` (заголовок «машина · раздел», закрытие; на телефоне — на весь экран).
Открываются из кнопок строки в списке машин и из меню заголовка вкладки или панели (её машина). Формы — перехватом из части 1; результат (например, обновления клиента) показывается в окне.
Живые части (графики и опрос статуса, тест скорости, присутствие) — модули `mount` / `unmount`; при закрытии окна останавливаются.
## Ошибки и крайние случаи
- Машина офлайн: терминал — как сейчас; файловая панель — «машина офлайн» и «повторить», при возвращении машины обновляется сама.
- Нет доступа (роль «пользователь»): чужих машин нет в меню и списке; если доступ отобран при открытой вкладке — панель показывает «нет доступа».
- Сессия истекла (401 или редирект на `/login`): сообщение и переход на вход.
- Агент без `sshfs.*`: пункт «Файлы по SSH» сообщает «обновите агента».
- Фрагмент не загрузился: ошибка и «повторить» в его области, остальное приложение работает.
- SSH-подключение файловой панели оборвалось: следующая операция переподключается один раз; не вышло — панель сообщает и предлагает переподключиться.
## Проверка
На стенде (сервер, агент, тестовый sshd), Chrome на компьютере и в эмуляции телефона:
1. Переходы Машины ↔ Терминал ↔ Настройки при идущем выводе в терминале — вывод не прерывается; «Назад / Вперёд»; перезагрузка — сброс и «Покинуть сайт?».
2. Вкладки с терминалом, файлами, SSH-файлами; разделение, изменение размеров, перетаскивание панелей и вкладок.
3. Файловая панель машины и SSH-хоста: список, просмотр, правка, загрузка, скачивание, создание, удаление, переименование, права.
4. Перетаскивание: в соседнюю панель (своя машина, SSH), в папку, в терминал той же машины (вставка путей), в терминал чужой (запрет).
5. Окна машины: формы, обновление клиента, остановка графиков при закрытии.
6. Роли: обычный пользователь видит в меню и списке только свои машины.
7. Регрессия: PIN, ключи доступа, установщик, редактор SQLite, перенос на другие машины, прямой адрес.
## Версия и документация
Версия 0.1.3. README — разделы о рабочем пространстве, файлах по SSH и перетаскивании.