# dsh-mobile-upgrade

[English](../../README.md) | [简体中文](../zh-Hans/README.md) | [繁體中文](../zh-Hant/README.md) | [日本語](../ja/README.md) | [한국어](../ko/README.md) | [Español](../es/README.md) | [Français](../fr/README.md) | [Deutsch](../de/README.md) | Русский

Удобства для мобильных устройств в веб-профиле [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`), оформленные как один плагин. Почти всё отрисовывается через собственные слоты и сигналы состояния хоста — без захвата разметки, без опроса DOM чата. Некоторые накладки намеренно сделаны чистым DOM с фиксированным позиционированием — чип/карточка соединения внизу слева, баннер самообновления, общий тост: они должны оставаться живыми именно тогда, когда React-дерево хоста заморожено.

## Что вы получаете

- **Строка перезапуска** — пункт «Перезапустить службу» в Настройки → Общие. Сначала запрашивает подтверждение, перезапускает процесс `dsh` и автоматически перезагружает страницу, когда служба снова отвечает.
- **Боковая панель-шторка на узких экранах** — ниже 1024px свёрнутая боковая панель целиком убирается за экран, и вид свёрнутого состояния создаёт собственная плавающая кнопка плагина: чистый DOM-элемент, круглая и полупрозрачная, 56px, — это не боковая панель (старый чип был самой панелью, уменьшенной — он ломался при пересборках хоста и смене стиля скином). Кнопка несёт собственную иконку — кита хоста, клонированного и очищенного от стилевых хуков, — в зафиксированном размере, который не тронет ни один скин. Нажатие заставляет плагин догонять рейл хоста и разворачивать его, и как только развёрнутое содержимое смонтировано, панель полной высоты въезжает со стороны кнопки; пока шторка открыта, кнопка растворяется, а закрывающее нажатие (скрим, строка сессии или переключатель в шапке шторки) выкатывает панель обратно в том же кадре. Ширина панели больше не меняется, въезд — только transform и не может застыть на полпути; развёрнутая шторка по-прежнему получает непрозрачную поверхность из собственных токенов боковой панели темы, которую стеклянный скин не сделает прозрачной. Кнопку можно перетащить к любому краю; шторка открывается с той стороны, где она припаркована.
- **Прокручиваемая карточка вопроса** — ниже 1024px открытый вопрос больше не может вытеснить собственные варианты за пределы экрана: вопрос получает собственную область прокрутки с ограничением высоты, поэтому длинный вопрос прокручивается на месте, список вариантов под ним занимает всё оставшееся место, а варианты, строка отправки и кнопки сворачивания и закрытия остаются на экране и доступны. Короткий вопрос не затрагивается, а карточка по-прежнему подстраивается под своё содержимое.
- **Реактивность, устойчивая к шторму** — наблюдатели шторки/меню и опросчик настроек реагируют на шквал стриминговых сообщений с ограниченной частотой и отступают, пока хост недоступен; при перемонтировании рейла во время переподключения состояние шторки сохраняется, а не мерцает, а нажатия, закрывающие шторку, повторяются через перемонтирование, вместо того чтобы попадать по устаревшему переключателю: множество одновременно стримящихся сессий больше не замораживает ввод и не дёргает боковую панель до некликабельности — открыта она или закрыта.
- **Защита каталога сабагентов** — хост отдаёт каталог сабагентов, перечисляя весь корпус сессий на каждый запрос (~1s на длинной истории), а его клиент спрашивает при каждом выборе, наведении на чип и событии состава. Плагин оборачивает это обновление: родитель без детей в живом списке сессий не опрашивается вовсе, к одному родителю обращаются не чаще одного раза за 2,5с, а запуски обновлений разнесены — веерный запуск сабагентов больше не морит хост голодом полными проходами по корпусу.
- **Чипы родословной, открывающиеся кликом** — на десктопе чипы сабагентов («N subagents» и переключатель заголовка сабагента) в текущем хосте открываются только при наведении; клик ничего не делает. Плагин переводит клик по чипу в пару событий наведения, которую хост уже понимает, поэтому клик открывает и закрывает дерево (крошки предков сохраняют переход по клику).
- **Сборщик заголовка на узких экранах** — ниже 1024px чипы заголовка сессии (родословная, задачи, пресет) шире телефона и работают только по наведению. Они сворачиваются в одну таблетку (`≡ заголовок · N agents · M jobs`); нажатие открывает лист во всю ширину с родословной сессии (каждый предок нажимаем, у каждого — число потомков), деревом сабагентов с корнем в корне семейства — строки отрисовываются из живого списка сессий, ветви получают свой каталог по требованию через защищённое обновление — и фоновыми задачами этой сессии. На десктопе ничего не меняется.
- **Самообновление** — клиентский бандл раздаётся неизменным по ревизиям, поэтому вкладка на телефоне может днями работать на старой сборке. Страница сравнивает ревизию, с которой загрузилась, с той, что сервер публикует сейчас, и перезагружает себя, когда страница простаивает (никогда во время набора черновика), или предлагает нажимаемый баннер.
- **Регулирование рендера списка сессий** — хост отправляет кадр с полным значением на каждое изменение проекции каждой подключённой сессии, а представление тайминга работающего субагента меняется на каждом зафиксированном событии — занятый фоновый флот вливает в страницу 50-150 кадров/с. Каждый кадр раньше перерисовывал весь список сессий (одна пересборка проходит по всем сводкам плюс O(n²)-чистку кеша — по замерам это большая часть времени главного потока; страница замерзала до перезагрузки). Плагин сворачивает эти перерисовки в адаптивный завершающий интервал, а значения по-прежнему без изменений поступают в хранилища каждой сессии — фоновый стриминг больше не замораживает страницу, в любой сессии, новой или старой.
- **Карточка соединения** — элемент управления соединением в левом нижнем углу хоста никак не реагирует на щелчок: запрошенное переподключение ныряет обратно в тот же зависший транспорт, уведомление о восстановлении — некликабельный div, исчезающий по таймеру, а узкая боковая панель (рейл/шторка) его вообще не отрисовывает. Плагин закрепляет статус чистым DOM-чипом в этом углу (жив даже при замороженном React-дереве хоста) и показывает над ним полупостоянную карточку: она держится, пока указатель внутри, иначе исчезает сама, и несёт два действия, которые действительно работают — немедленное переподключение через сервис соединения и полную ручную перезагрузку страницы. Зависшие или неудачные запросы `/api/` появляются там же: бейдж со счётчиком на чипе, список запросов и проба живости внутри карточки — сколько бы запросов ни висело и ни падало, предупреждений рядом с элементами композера больше не будет.
- **Переключатели по функциям** — плагин устанавливает раздел `dsh-mobile-upgrade` в Настройки → Плагины с переключателем для каждой функции.

## Скриншоты

Плавающая кнопка, шторка сессий, вкладки настроек на узких экранах и сборщик заголовка (с пояснениями):

| | |
|---|---|
| ![Плавающая кнопка](../../res/shot-floating-chip.png) | ![Шторка сессий](../../res/shot-drawer.png) |
| ![Вкладки настроек](../../res/shot-settings-tabs.png) | ![Сборщик заголовка](../../res/shot-header-collector.png) |

## Установка

```sh
dsh plugin --profile web add dsh-mobile-upgrade
```

Перезапустите `dsh web` и откройте веб-профиль на телефоне: строка перезапуска — в Настройки → Общие, а переключатели — в Настройки → Плагины → dsh-mobile-upgrade. Вложения загружаются через собственную систему вложений композера хоста.

Требуется `dsh` 0.2.0-rc.2 или новее (встроенная система вложений хоста заменила функцию загрузки этого плагина).

## Конфигурация

Карточка настроек плагина показывает по одному переключателю на функцию (см. «Переключатели функций»); `restartEnabled` также управляет маршрутом перезапуска:

| Ключ | По умолчанию | Значение |
|---|---|---|
| `restartEnabled` | `true` | Предоставлять строку перезапуска и её маршрут |

## Переключатели функций

Каждую функцию можно включать и выключать в Настройки → Плагины → dsh-mobile-upgrade (вступает в силу при следующей загрузке страницы) либо переопределить на устройстве ключом `localStorage` — `mfx-restart`, `mfx-drawer`, `mfx-net`, `mfx-questions`, `mfx-selfupdate`, `mfx-agents`, `mfx-header`, `mfx-listthrottle`, `mfx-conn` — значение `"0"` отключает функцию.

## Известные ограничения

- Клиент цепляется за конкретные элементы интерфейса хоста по их хешированным CSS-классам (меню, переключатель шторки, редактор провайдеров, чипы заголовка сессии). Если сборка хоста переименует их, затронутые функции деградируют, пока этот плагин не догонит; остальное продолжает работать. Область вопроса в карточке определяется вместо этого по её собственному хуку `data-question-key`, поэтому продолжает работать и после пересборки хоста, которая лишь заново хеширует свои CSS-модули.
- Сборщик заголовка и клик-шим родословной едут на собственных данных хоста (живой список сессий и его каталоги сабагентов); они не добавляют запросов сверх защищённых выборок ветвей, а нажатие на строку, чей режим ещё загружается, завершится само, как только каталог придёт.
- Переключатели модальностей ввода могут изменять только маршруты провайдеров, настроенные вами самостоятельно; маршруты из одного каталога только для чтения по замыслу — выдуманный раздел каталога обрушивает весь каталог моделей.

## Замечания по безопасности

HTTP-маршруты плагина (перезапуск, правка настроек) сами по себе не выполняют аутентификацию — они доверяют веб-поверхности `dsh`, в которую загружены. Прежде чем открывать развёртывание за пределы localhost, поместите его за ту же преграду, что и остальной интерфейс (аутентификация обратного прокси, привязка к loopback).

## Ссылки сообщества

- [Linux.Do](https://linux.do) — Сообщество для обмена и обсуждения технологий.

## Лицензия

Распространяется под [Synthetic Source License (SySL), версия 1.0](LICENSE).

> УВЕДОМЛЕНИЕ: Это программное обеспечение включает код, сгенерированный искусственным интеллектом. Условия Synthetic Source License, включая требования о раскрытии моделей, см. в файле LICENSE.
