dsh-sidebar-pins
v0.1.0
Published
Pinned-sessions block for the DeepSeek Harness sidebar: a collapsible pinned panel above the Settings row, hover pin buttons on session rows and a session-header toggle. Pins live in the plugin's own settings namespace and are migrated once from dsh-sessi
Maintainers
Readme
English | Русский
dsh-sidebar-pins
Самодостаточные закреплённые сессии для левой панели DeepSeek Harness: свой набор пинов, свои кнопки закрепления и блок «📌 ЗАКРЕПЛЁННЫЕ» сверху сайдбара — вместо всплывающего окна, которое уезжает в правый верхний угол и закрывается от клика мимо.
Плагин написан под DSH 0.1.5-rc.1 и заменяет dsh-session-pin целиком:
после его установки старый плагин можно удалить (см. «Удаление старого плагина»).
Зачем
dsh-session-pin 0.7.11 на этой версии ядра не может выдать то, что обещает:
- он переставляет хостовый список (
workspaces.insertSessionBefore) — и тот действительно переставлен, это видно в~/.dsh/storages/workspace.json; - а сайдбар рисует браузерный порядок (
dsh.workspace.view.v5), поверх которого хостовый список уже не влияет, — публичного API к этому хранилищу у плагинов нет; - слот
sidebar.workspacesобъявлен ядром какkind: "single", поэтому встроить в него второй блок невозможно; - слота
sessions.row.actionна 0.1.5-rc.1 нет, поэтому кнопки в строках — DOM-оверлей.
Что делает
| Поверхность | Поведение |
|---|---|
| Блок «Закреплённые» | Внутри подвала сайдбара, над строкой «Настройки». Место выводится только из собственной метки-якоря: собирается вся цепочка контейнеров от неё вверх, и панель садится перед той веткой, что ведёт к якорю. Контейнеры, которые раскладывают детей в строку (штатный footerActions — именно такой), пропускаются по измерению, поэтому панель не встаёт сбоку от «Настройки»; уровни выше корня сайдбара не рассматриваются вообще — иначе панель оказалась бы над всем сайдбаром. Высота — до 30vh, список пинов прокручивается внутри. Сворачивание вручную помнится per-browser; в режиме рейки панель показывает только иконку и количество |
| Строка пина | Клик — открыть сессию; × справа — открепить (появляется при наведении) |
| Кнопка в строке сессии | 📌 при наведении на строку — закрепить/открепить. Сессия определяется по React-fiber, с откатом на вложенный span с уникальным заголовком |
| Кнопка в шапке сессии | Тумблер закрепа открытой сессии (слот conversation.session.header.actions) |
| Пропавшие сессии | Остаются в списке приглушёнными (сырой id), чтобы их можно было открепить |
| Легаси-панель | Плавающая панель, подвал «Pinned sessions», полоса бордов, старая кнопка в шапке и старые кнопки в строках скрываются — вся закреп-UI теперь здесь |
Плагин ничего не пишет в лог сессии, не обращается к сети и не влияет на запросы к модели.
Хранилище
- Основное место — свой namespace настроек
sidebar-pins(регистрирует хостовая половина), то есть пины durable и видны в~/.dsh/settings.yaml. - Если клиент не видит namespace (хост-половина не смонтировалась, remote-браузер) —
откат на
localStorage["dsh.sidebar-pins.pinned"]. - При первом запуске набор разово переносится из
session-pin(хостовой половиной, до открытия страницы), поэтому пины переживают удаление старого плагина. Перенос идёт только пока полеpinnedне появилось в пользовательском слое: осознанно опустошённый набор больше не заполняется.
Установка
Из GitHub:
dsh plugin --profile web add github:AndreyTepaykin/dsh-sidebar-pinsИз npm (когда пакет опубликован):
dsh plugin --profile web add dsh-sidebar-pinsДля локальной разработки — из каталога с рабочей копией плагина:
dsh plugin --profile web add .Затем перезапустить DSH (клиентский бандл попадает на страницу при пересборке boot-графа) и обновить страницу GUI.
Проверка
node --test "test/*.test.mjs" # 43 теста
dsh --profile web --dump-config | Select-String "sidebar-pins" # строка загрузчикаПрямо над кнопкой «Настройки» появится «📌 ЗАКРЕПЛЁННЫЕ (3)» — панель высотой 30vh с прокруткой; список сессий остаётся выше.
Если панели нет, плагин сам скажет почему — в консоли браузера:
__dshSidebarPinsОбъект диагностики (window.__dshSidebarPins) всегда заполнен: paneConnected,
paneParent, anchors (якорь и цепочка контейнеров до него), seat/seatLabel
(какое место выбрано), skipped (места, отброшенные как row-контейнеры), rail,
rows и rowButtons (сколько строк сайдбара видно и на скольких появилась кнопка),
styleNode, pinned, ignoredPosition и notes со всеми предупреждениями. Подробный лог включается отдельно:
localStorage.setItem("dsh.sidebar-pins.debug", "1");
location.reload();Панель к тому же само-восстанавливается без опроса таймером: всё, что может её
снести, — это изменение DOM, поэтому наблюдается ровно три цели: тело документа
(снятие/перерисовка панели), head (стилевой узел) и класс корня сайдбара (переход
список ↔ рейка). Любое из этих изменений возвращает панель и стиль на место.
Настройки (браузер, localStorage)
| Ключ | Значение |
|---|---|
| dsh.sidebar-pins.collapsed | 1 — панель свёрнута до строки заголовка |
| dsh.sidebar-pins.show-legacy | 1 — не скрывать легаси-поверхности dsh-session-pin |
| dsh.sidebar-pins.debug | 1 — писать подробную диагностику в консоль |
dsh.sidebar-pins.position больше не читается: место панели фиксировано. Значение,
оставшееся в localStorage от прежней версии, показывается в диагностике как
ignoredPosition и ни на что не влияет.
Удаление старого плагина
dsh plugin --profile web remove dsh-session-pinПорядок важен: сначала убедитесь, что блок и кнопки работают (пины перенесены),
потом удаляйте. После удаления в ~/.dsh/settings.yaml останется неиспользуемая
секция session-pin — её можно убрать вручную, плагин её не читает.
Ограничения
- Основной список не переупорядочивается. Закреплённые живут в панели снизу и остаются в обычном списке: браузерный порядок живёт в props-store ядра, недоступном плагинам.
- Высота панели — до
30vh(сайдбар занимает высоту окна), а не фиксированное число пикселей; если сайдбар короткий, панель отдаёт место, а не выдавливает «Настройки» за край. Свёрнутая (и любая в режиме рейки) панель занимает только строку заголовка. - Идентификация строки сессии опирается на React-fiber (
__reactFiber$…, React 18) с откатом на любой вложенныйspanс уникальным заголовком: при дублирующихся заголовках кнопка не появится (вместо угадывания). Кнопка в шапке сессии от этой эвристики не зависит. - В режиме рейки строк сессий в DOM нет вообще — там нечего помечать кнопкой,
поэтому 📌 в строках появляется при развёрнутом сайдбаре (
rows: 0в диагностике в рейке — это норма, а не сбой). - Место панели выводится только из своей метки-якоря и не зависит ни от строк
сессий, ни от обёрток. Живой хост добавляет лишний безымянный
divвокруг корня сайдбара и по одномуdivвокруг каждой записи слота подвала, а самfooterActionsраскладывает детей в строку. Поэтому вместо фиксированной цепочки собирается весь путь вверх от якоря, и место выбирается по измерению: контейнер должен раскладывать детей столбцом (иначе панель легла бы сбоку от «Настройки»), а панель — оказаться выше строки «Настройки». Уровни выше корня сайдбара не рассматриваются: оттуда панель попала бы над всем сайдбаром. Выбранное место и отброшенные видны в диагностике (seat,seatLabel,skipped), а последнее убежище — конец ближайшего «столбцового» контейнера, то есть панель остаётся внутри сайдбара. Стилевой узел переустанавливается на каждом проходе: панель без стилей разрослась бы по содержимому и выдавила «Настройки» за край. - Плагин сознательно не импортирует ни одного внешнего пакета. При установке
ссылкой (
link:) в профиль это ещё и вынужденно: Node разрешает bare-импорты от реального пути пакета (рабочей копии), где нетnode_modules, и вход не загрузился бы сERR_MODULE_NOT_FOUND. Поэтому схема настроек описана руками (см. ниже) — при установке из npm/git ограничение снято, но ручная схема остаётся, чтобы поведение не зависело от способа установки.
Сериализованная схема
Хостовая половина отдаёт конверт схемы, записанный вручную, — он должен совпадать с тем, что ре-гидратирует встроенный в оболочку schemastery. Проверка:
node scripts/check-schema-envelope.mjsСкрипт поднимает тот же вендорный код из dsh-client-ui-settings/lib/client.js,
ре-гидратирует конверт и проверяет {pinned:["a","b"]}, {}, {pinned:[1]}.
Этот же тест есть в наборе (test/host.test.mjs) и пропускается, если оболочки
на машине нет. Запускайте после обновления DSH.
Разработка
node --test "test/*.test.mjs"43 теста: чистые хелперы (нормализация id, разбор легаси-документа, модели строк, словарь, toggle), хост-половина (регистрация, схема, миграция, конверт), цепочка мест от своего якоря (включая лишние обёртки живого хоста и отсутствие строк сессий), отказ от row-контейнеров, посадка над строкой «Настройки», геометрическая проверка и переход к следующему месту, последнее убежище внутри сайдбара, поведение в рейке (только иконка и число), отсутствие перерисовки без изменений, наблюдаемые цели, восстановление снесённых панели и стилевого узла, кнопки в строках (fiber / вложенный заголовок / неоднозначность), делегированный клик, кнопка в шапке, откат на localStorage и размонтирование.
