@goodandready/dsh-session-control 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -96
- package/docs/README.ru.md +146 -0
- package/docs/README.zh.md +146 -0
- package/docs/design/DESIGN.md +231 -0
- package/docs/superpowers/specs/2026-09-05-dsh-session-control-design.md +250 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,147 +1,146 @@
|
|
|
1
1
|
# @goodandready/dsh-session-control
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
диалогов, поиск по содержимому, чтение архивных разговоров и уборка шума.
|
|
3
|
+
[English](README.md) | [Русский](docs/README.ru.md) | [中文](docs/README.zh.md)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
6
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://goodandready.app)
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
Остальная панель остаётся штатной: бренд, кнопка новой сессии, подвал и вход в
|
|
10
|
-
настройки не затрагиваются.
|
|
10
|
+
Advanced session management for the **DeepSeek Harness** sidebar: pin essential conversations, search conversation contents with match snippets, read archived transcripts in an isolated modal, hide noise, and manage multiple sessions with bulk actions.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
как `kind: "single"`, второго регистранта у него не бывает, а другого слота в
|
|
14
|
-
теле панели нет.
|
|
12
|
+
---
|
|
15
13
|
|
|
16
|
-
##
|
|
14
|
+
## What the Plugin Does to the Sidebar
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
The plugin **replaces the body of the sidebar** — the section containing the workspace and session lists. The rest of the sidebar remains completely stock: branding, the new session button, footer, and navigation to settings remain untouched.
|
|
17
|
+
|
|
18
|
+
Replacement is intentional and architecturally required: the `sidebar.workspaces` slot is declared by the harness core as `kind: "single"`, which does not allow secondary registrants, and no other extension slots exist in the sidebar body.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Comparison: Stock Sidebar vs. dsh-session-control
|
|
23
|
+
|
|
24
|
+
| Capability | Stock Sidebar | With `dsh-session-control` |
|
|
19
25
|
|---|---|---|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
26
|
+
| **Pin Conversations** | ❌ None | ✅ Global pinned section at the top of the sidebar |
|
|
27
|
+
| **Archive Section** | ❌ Completely hidden | ✅ Dedicated section grouped by time periods |
|
|
28
|
+
| **Read Archived Sessions** | ❌ Impossible | ✅ Read-only full transcript viewer modal |
|
|
29
|
+
| **Search Results** | ⚠️ Title only | ✅ Title + contextual match snippet preview |
|
|
30
|
+
| **Blank Sessions** | ⚠️ Mixed with active ones | ✅ Hidden automatically (except current active session) |
|
|
31
|
+
| **Bulk Actions** | ❌ None | ✅ Multi-selection with checkboxes and Shift+Click ranges |
|
|
32
|
+
| **Reversible Hiding** | ❌ Only irreversible archive | ✅ Reversible one-click hide / unhide |
|
|
33
|
+
| **Active Session Indicator** | ❌ Not indicated | ✅ Live running pulse dot in row |
|
|
34
|
+
| **In-Place Rename** | ⚠️ Modal prompt | ✅ Inline double-click or F2 edit |
|
|
35
|
+
|
|
36
|
+
> Standard harness features — workspaces, deep conversation search, and branching sessions — remain fully intact: the plugin reuses and enhances them rather than reinventing them.
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
сессии — остаются на месте: плагин их переиспользует, а не переписывает.
|
|
38
|
+
---
|
|
32
39
|
|
|
33
|
-
##
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install via the `dsh` CLI for your web profile:
|
|
34
43
|
|
|
35
44
|
```bash
|
|
36
45
|
dsh plugin --profile web add @goodandready/dsh-session-control
|
|
37
46
|
```
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
Restart the DeepSeek Harness web profile to activate the bundle patch.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## How to Restore the Stock Sidebar
|
|
53
|
+
|
|
54
|
+
To revert to the stock sidebar at any time:
|
|
40
55
|
|
|
41
56
|
```bash
|
|
42
57
|
dsh plugin --profile web remove @goodandready/dsh-session-control
|
|
43
58
|
```
|
|
44
59
|
|
|
45
|
-
|
|
46
|
-
виду. Закрепления и скрытия остаются в настройках и подхватятся, если плагин
|
|
47
|
-
поставить снова.
|
|
60
|
+
The stock `ui-workspace` row is instantly re-enabled, returning the sidebar to its default look. All pinned and hidden session preferences are preserved in host storage and will seamlessly reapply if the plugin is installed again.
|
|
48
61
|
|
|
49
|
-
|
|
62
|
+
---
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
Переживают перезагрузку, смену браузера и устройство: настройки хранятся на
|
|
53
|
-
хосте.
|
|
64
|
+
## Features
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
чем именно нашлась строка, без открытия сессии.
|
|
66
|
+
### 📌 Pinned Sessions
|
|
67
|
+
Pin important conversations to a dedicated, globally visible top group above workspaces. Pinned sessions survive page reloads, browser switches, and device migrations because configuration is persisted in host storage.
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
разметку разом. При активном поиске периоды раскрываются, чтобы совпадения не
|
|
62
|
-
прятались за свёрнутым заголовком.
|
|
69
|
+
### 🔍 Search with Contextual Snippets
|
|
70
|
+
The core searches conversation contents and returns context around the matching query — `dsh-session-control` displays this preview as a second line under the session title so you immediately see why a conversation matched without opening it.
|
|
63
71
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
отдельным окном, только на чтение.
|
|
72
|
+
### 🗄️ Period-Based Archive
|
|
73
|
+
Organized into intuitive collapsible periods: *Today*, *This Week*, *This Month*, and *Older*. Collapsed periods are unmounted from the DOM, preventing performance degradation even with thousands of historical sessions. Active search automatically expands relevant periods.
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
даже пустая. Переключатель в карточке настроек возвращает прежнее поведение.
|
|
75
|
+
### 📜 Read-Only Archived Transcript Viewer
|
|
76
|
+
Archived sessions cannot be opened for active chatting in the core. The plugin provides an isolated, read-only transcript viewer modal via `GET /dsh-session-control/transcript?session=<id>` to inspect past agent turns, tools called, and user prompts safely.
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
### 🧹 Automatic Noise Reduction (Hide Blank Sessions)
|
|
79
|
+
Automated schedulers, messenger gateways, and kanban boards continuously spawn sessions without messages. Blank sessions are hidden by default to keep the sidebar clean. The active session is never hidden even if empty. Toggleable via settings.
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
### 👁️ Reversible Session Hiding
|
|
82
|
+
Unlike core archiving which is one-way, `dsh-session-control` offers lightweight reversible hiding for active sessions you temporarily want out of view.
|
|
77
83
|
|
|
78
|
-
|
|
84
|
+
### ☑️ Multi-Select & Batch Operations
|
|
85
|
+
Hover checkboxes and Shift+Click range selection allow batch hiding, unhiding, and pinning of dozens of sessions simultaneously.
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
### ✏️ In-Place Inline Renaming
|
|
88
|
+
Rename conversations instantly with a double click on the title or by pressing `F2`.
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
`dsh-session-control`:
|
|
90
|
+
---
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|---|---|
|
|
87
|
-
| `pinned` | закреплённые сессии, порядок массива = порядок в панели |
|
|
88
|
-
| `hidden` | скрытые сессии |
|
|
89
|
-
| `hideBlank` | прятать сессии без сообщений (по умолчанию да) |
|
|
92
|
+
## Configuration
|
|
90
93
|
|
|
91
|
-
|
|
94
|
+
Navigate to **Settings → Plugins → Plugin Settings → Session Control**:
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
| Setting | Type | Default | Description |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `pinned` | `string[]` | `[]` | Array of pinned session IDs (array order determines display order) |
|
|
99
|
+
| `hidden` | `string[]` | `[]` | Array of reversibly hidden session IDs |
|
|
100
|
+
| `hideBlank` | `boolean` | `true` | Automatically hide sessions with zero messages |
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
членство сессии выводится из её рабочего каталога: реестр отвергает сессию, чей
|
|
97
|
-
`cwd` не совпадает с путём папки. Это не пробел в API, а устройство модели, и
|
|
98
|
-
поэтому такого пункта нет и в штатной панели.
|
|
102
|
+
All settings are stored on the host and synchronize across client instances.
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
API нет. Архивная сессия доступна на чтение, но вернуть её в папку нельзя.
|
|
104
|
+
---
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
формате, который ядро отказывается читать. Такие показываются с пояснением, а
|
|
105
|
-
не с ложным «нет сообщений».
|
|
106
|
+
## Intentional Constraints
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
* **No cross-workspace session moving:** Workspaces represent distinct disk directories. Session membership is derived directly from the session's working directory (`cwd`). The core registry strictly enforces this binding.
|
|
109
|
+
* **No unarchiving:** DeepSeek Harness core provides an archive operation but no unarchive API method. Archived sessions remain accessible via the read-only transcript viewer.
|
|
110
|
+
* **Legacy log format handling:** Older legacy session logs that cannot be deserialized by the core are gracefully flagged with descriptive notices rather than displaying a false "no messages" state.
|
|
111
|
+
* **No session hard deletion:** Session deletion is not exposed in public harness APIs, and the plugin adheres strictly to safe API contracts.
|
|
109
112
|
|
|
110
|
-
|
|
113
|
+
---
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
украшение архитектуры.
|
|
115
|
+
## Architecture & Reliability
|
|
114
116
|
|
|
115
|
-
|
|
116
|
-
который стоит в обязательных зависимостях у модулей боковой панели и интерфейса
|
|
117
|
-
беседы. Если его не отдать, приложение не поднимется вовсе.
|
|
117
|
+
The plugin utilizes a decoupled **two-half architecture** for maximum resilience:
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
1. **Service Half (`uiWorkspace` Provider):**
|
|
120
|
+
* The replaced core `ui-workspace` module exports a required service `uiWorkspace` that is an essential dependency for `dsh-client-ui-sidebar` and `dsh-client-ui-conversation`.
|
|
121
|
+
* The plugin's service half delivers this service and root hooks cleanly with zero React rendering and zero plugin logic overhead.
|
|
122
|
+
2. **Interface Half (UI & Views):**
|
|
123
|
+
* Encapsulates the custom workspace tree, search results, transcript modal, and settings card.
|
|
124
|
+
* Completely wrapped in defensive error boundaries. If a UI exception occurs, the main sidebar, conversation pane, and settings remain fully operational.
|
|
125
|
+
3. **Server Route:**
|
|
126
|
+
* Exposes `GET /dsh-session-control/transcript` to parse and stream archived JSONL session logs safely without loading heavy unneeded payloads.
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
продолжают работать.
|
|
128
|
+
---
|
|
126
129
|
|
|
127
|
-
|
|
128
|
-
остальные действия идут через штатные вызовы ядра.
|
|
130
|
+
## UI Localization
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
The plugin core ships with English strings by default. Multilingual localizations (including Russian and Chinese) are loaded through language packs (such as `@goodandready/dsh-russian-lang`). Dictionary registration is fail-safe and never degrades sidebar functionality.
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
плагином; регистрация словаря защищена и не может уронить панель, если
|
|
134
|
-
пространство уже занято.
|
|
134
|
+
---
|
|
135
135
|
|
|
136
|
-
##
|
|
136
|
+
## Compatibility
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
- Tested with DeepSeek Harness `0.1.2-rc.1` and `0.1.3-alpha.1`.
|
|
139
|
+
- Compatible with Node.js `^20.19.0` or `>=22.12.0`.
|
|
140
|
+
- Seamlessly integrates alongside `@goodandready/dsh-lanmode`, `@goodandready/dsh-kanban`, `@goodandready/dsh-cron`, and other DSH plugins.
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
спрашивает бэкенд, какие методы у него есть, а не полагается на номер версии.
|
|
142
|
+
---
|
|
144
143
|
|
|
145
|
-
##
|
|
144
|
+
## License
|
|
146
145
|
|
|
147
|
-
MIT
|
|
146
|
+
MIT © GoodAndReady
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @goodandready/dsh-session-control
|
|
2
|
+
|
|
3
|
+
[English](../README.md) | [Русский](README.ru.md) | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
6
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://goodandready.app)
|
|
9
|
+
|
|
10
|
+
Продвинутое управление сессиями в боковой панели **DeepSeek Harness**: закрепление нужных диалогов, поиск по содержимому с фрагментом совпадения, чтение архивных разговоров в модальном окне, уборка шума и групповые действия над сессиями.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Что плагин делает с панелью
|
|
15
|
+
|
|
16
|
+
Плагин **заменяет тело боковой панели** — блок со списком рабочих папок и сессий. Остальная панель остаётся штатной: бренд, кнопка создания новой сессии, подвал и вход в настройки не затрагиваются.
|
|
17
|
+
|
|
18
|
+
Замена необходима архитектурно: слот `sidebar.workspaces` объявлен ядром как `kind: "single"`, второго регистранта у него не бывает, а другого слота в теле панели нет.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Сравнение со штатной панелью
|
|
23
|
+
|
|
24
|
+
| Возможность | Штатная панель | С плагином `dsh-session-control` |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **Закрепление диалогов** | ❌ Нет | ✅ Общая группа закреплённых сессий вверху панели |
|
|
27
|
+
| **Раздел архива** | ❌ Не виден нигде | ✅ Отдельный раздел, разбитый по периодам |
|
|
28
|
+
| **Чтение архивной сессии** | ❌ Невозможно | ✅ Окно расшифровки (только на чтение) |
|
|
29
|
+
| **Результаты поиска** | ⚠️ Только заголовок строки | ✅ Заголовок + фрагмент текста с совпадением |
|
|
30
|
+
| **Пустые сессии** | ⚠️ Вперемешку с рабочими | ✅ Скрыты автоматически (кроме текущей сессии) |
|
|
31
|
+
| **Действия над пачкой строк** | ❌ Нет | ✅ Множественный выбор чекбоксами и диапазоны по Shift |
|
|
32
|
+
| **Скрыть лишнее** | ❌ Только необратимый архив | ✅ Обратимое скрытие в один клик |
|
|
33
|
+
| **Индикатор активной сессии** | ❌ Не показан | ✅ Пульсирующая точка активности в строке |
|
|
34
|
+
| **Переименование по месту** | ⚠️ Модальный диалог | ✅ Двойной клик или клавиша F2 прямо в строке |
|
|
35
|
+
|
|
36
|
+
> Штатные возможности — рабочие папки, поиск по содержимому диалогов, ответвление сессии — остаются на месте: плагин их переиспользует, а не переписывает.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Установка
|
|
41
|
+
|
|
42
|
+
Установка через CLI `dsh` для профиля `web`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
dsh plugin --profile web add @goodandready/dsh-session-control
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Перезапустите веб-профиль DeepSeek Harness для применения бандл-патча.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Как вернуть штатную панель
|
|
53
|
+
|
|
54
|
+
Чтобы в любой момент вернуть стандартную боковую панель:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
dsh plugin --profile web remove @goodandready/dsh-session-control
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Штатный ряд `ui-workspace` немедленно включается обратно, панель возвращается к исходному виду. Настройки закреплений и скрытий сохраняются на хосте и автоматически подхватятся при повторной установке плагина.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Возможности
|
|
65
|
+
|
|
66
|
+
### 📌 Закрепление сессий
|
|
67
|
+
Нужные диалоги выносятся в отдельную группу сверху панели. Закрепления сохраняются при перезагрузке, смене браузера и переходе на другое устройство, так как настройки хранятся на хосте.
|
|
68
|
+
|
|
69
|
+
### 🔍 Поиск с контекстным фрагментом
|
|
70
|
+
Ядро ищет по содержимому диалогов и возвращает кусок текста вокруг совпадения — плагин выводит его второй строкой под заголовком, поэтому сразу видно причину совпадения без необходимости открывать сессию.
|
|
71
|
+
|
|
72
|
+
### 🗄️ Архив по временным периодам
|
|
73
|
+
Сессии архива разбиты на сворачиваемые группы: *Сегодня*, *На этой неделе*, *В этом месяце*, *Раньше*. Свёрнутый период не рендерится в DOM, что исключает лаги интерфейса при тысячах старых сессий. При активном поиске периоды с совпадениями раскрываются автоматически.
|
|
74
|
+
|
|
75
|
+
### 📜 Расшифровка архивной сессии
|
|
76
|
+
Заархивированную сессию нельзя открыть в режиме беседы (ядро снимает выбор с таких сессий). Плагин предоставляет изолированное модальное окно просмотра расшифровки через маршрут `GET /dsh-session-control/transcript?session=<id>` только на чтение.
|
|
77
|
+
|
|
78
|
+
### 🧹 Скрытие пустых сессий
|
|
79
|
+
Сессии без сообщений непрерывно генерируются шедулерами, мессенджерами и канбан-досками. Они автоматически скрываются, сохраняя чистоту панели. Текущая активная сессия не скрывается никогда. Поведение настраивается в карточке плагина.
|
|
80
|
+
|
|
81
|
+
### 👁️ Обратимое скрытие
|
|
82
|
+
Собственный механизм скрытия, в отличие от необратимого ядрового архива: скрытая сессия восстанавливается одним кликом.
|
|
83
|
+
|
|
84
|
+
### ☑️ Множественный выбор и пакетные операции
|
|
85
|
+
Выбор строк чекбоксами при наведении и выбор диапазона через Shift+Click. Над выбранными строками доступны групповое скрытие, закрепление и разархивация видимости.
|
|
86
|
+
|
|
87
|
+
### ✏️ Быстрое переименование
|
|
88
|
+
Переименование сессии по двойному клику на заголовок или по нажатию клавиши `F2`.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Настройки
|
|
93
|
+
|
|
94
|
+
Карточка расположена в: **Настройки → Плагины → Настройки плагинов → Управление сессиями** (`dsh-session-control`):
|
|
95
|
+
|
|
96
|
+
| Поле | Тип | По умолчанию | Описание |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `pinned` | `string[]` | `[]` | Идентификаторы закреплённых сессий (порядок в массиве задаёт порядок в панели) |
|
|
99
|
+
| `hidden` | `string[]` | `[]` | Идентификаторы скрытых сессий |
|
|
100
|
+
| `hideBlank` | `boolean` | `true` | Автоматически скрывать сессии без сообщений |
|
|
101
|
+
|
|
102
|
+
Настройки хранятся на хосте и синхронизируются между всеми клиентами.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Архитектурные ограничения
|
|
107
|
+
|
|
108
|
+
* **Перенос диалога в другую рабочую папку не поддерживается:** Рабочая папка привязана к каталогу на диске, а принадлежность сессии выводится из её рабочего каталога (`cwd`). Реестр ядра строго отвергает несоответствие путей.
|
|
109
|
+
* **Возврат из архива не предусмотрен:** В API ядра есть только архивация, метод возврата отсутствует. Архивная сессия доступна для безопасного чтения через расшифровку.
|
|
110
|
+
* **Чтение устаревших журналов:** Часть архивных сессий раннего формата ядро не парсит; такие логи отображаются с информативным пояснением, а не с ложным «нет сообщений».
|
|
111
|
+
* **Удаление сессий отсутствует:** Удаление не предусмотрено в публичном API ядра, и плагин строго соблюдает контракт безопасности.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Архитектура и надежность
|
|
116
|
+
|
|
117
|
+
Плагин спроектирован по **двухчастной архитектуре** для гарантии отказоустойчивости:
|
|
118
|
+
|
|
119
|
+
1. **Сервисная половина (`uiWorkspace` провайдер):**
|
|
120
|
+
* Заменяемый модуль `ui-workspace` регистрирует обязательный сервис `uiWorkspace`, от которого зависят `dsh-client-ui-sidebar` и `dsh-client-ui-conversation`.
|
|
121
|
+
* Сервисная половина отдаёт сервис и корневые хуки без React-рендеринга и оверхеда.
|
|
122
|
+
2. **Интерфейсная половина (UI):**
|
|
123
|
+
* Дерево рабочих пространств, поиск, модальное окно расшифровки и карточка настроек.
|
|
124
|
+
* Полностью изолирована предохранителями (Error Boundaries): сбой вёрстки не ломает панель и окно диалога.
|
|
125
|
+
3. **Серверный маршрут:**
|
|
126
|
+
* `GET /dsh-session-control/transcript` безопасно парсит и отдаёт лог JSONL архивной сессии.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Языки интерфейса
|
|
131
|
+
|
|
132
|
+
Поставляется со строками на английском языке по умолчанию. Переводы (включая русский язык) подключаются через языковые пакеты (`@goodandready/dsh-russian-lang`).
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Совместимость
|
|
137
|
+
|
|
138
|
+
- DeepSeek Harness `0.1.2-rc.1` и `0.1.3-alpha.1`.
|
|
139
|
+
- Node.js `^20.19.0` или `>=22.12.0`.
|
|
140
|
+
- Полная совместимость с `@goodandready/dsh-lanmode`, `@goodandready/dsh-kanban`, `@goodandready/dsh-cron` и другими плагинами DSH.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Лицензия
|
|
145
|
+
|
|
146
|
+
MIT © GoodAndReady
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @goodandready/dsh-session-control
|
|
2
|
+
|
|
3
|
+
[English](../README.md) | [Русский](README.ru.md) | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
6
|
+
[](https://www.npmjs.com/package/@goodandready/dsh-session-control)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://goodandready.app)
|
|
9
|
+
|
|
10
|
+
**DeepSeek Harness** 侧边栏高级会话控制插件:置顶关键对话、带上下文匹配摘要的高级搜索、只读归档记录查看器、自动隐藏空白无用会话及多选批量操作。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 插件对侧边栏的改造
|
|
15
|
+
|
|
16
|
+
本插件**替换了侧边栏的主体内容**——即工作区与会话列表部分。侧边栏的其他原生部分完全保持原样:品牌标识、新建会话按钮、底部状态栏以及设置导航入口均不受影响。
|
|
17
|
+
|
|
18
|
+
替换是架构上的必然要求:Harness 核心将 `sidebar.workspaces` 插槽声明为 `kind: "single"`,不允许注册多个并列组件,且侧边栏主体没有其他扩展插槽。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 功能对比:原生侧边栏 vs dsh-session-control
|
|
23
|
+
|
|
24
|
+
| 功能特性 | 原生侧边栏 | `dsh-session-control` |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **会话置顶 (Pin)** | ❌ 无 | ✅ 顶部全局置顶区,统一管理 |
|
|
27
|
+
| **归档视图 (Archive)** | ❌ 完全不可见 | ✅ 独立归档分区,按时间段折叠聚合 |
|
|
28
|
+
| **阅读归档会话** | ❌ 无法打开 | ✅ 只读完整会话转录记录弹窗 |
|
|
29
|
+
| **内容搜索结果** | ⚠️ 仅显示标题 | ✅ 标题 + 匹配内容上下文摘要预览 |
|
|
30
|
+
| **空白会话** | ⚠️ 与活跃会话混杂 | ✅ 自动隐藏无消息会话(当前会话除外) |
|
|
31
|
+
| **批量操作** | ❌ 无 | ✅ 复选框多选及 Shift+Click 范围批量操作 |
|
|
32
|
+
| **可逆隐藏** | ❌ 仅支持不可逆归档 | ✅ 一键可逆隐藏与恢复 |
|
|
33
|
+
| **活跃运行指示** | ❌ 无指示 | ✅ 实时运行脉冲状态圆点 |
|
|
34
|
+
| **就地快速重命名** | ⚠️ 弹窗修改 | ✅ 双击标题或按 F2 直接在行内重命名 |
|
|
35
|
+
|
|
36
|
+
> 原生 Harness 核心功能(工作区目录、深度会话搜索、会话分支等)完全保留:插件复用并强化了核心能力,而非重复造轮子。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 安装方法
|
|
41
|
+
|
|
42
|
+
通过 `dsh` CLI 为 web profile 安装:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
dsh plugin --profile web add @goodandready/dsh-session-control
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
重启 DeepSeek Harness web profile 以应用 bundle patch。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 恢复原生侧边栏
|
|
53
|
+
|
|
54
|
+
随时可以通过卸载命令恢复默认侧边栏:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
dsh plugin --profile web remove @goodandready/dsh-session-control
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
原生 `ui-workspace` 模块将立即恢复启用,侧边栏恢复默认外观。所有置顶和隐藏配置安全保留在宿主存储中,重新安装插件后自动恢复。
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 核心功能
|
|
65
|
+
|
|
66
|
+
### 📌 会话置顶 (Pinned Sessions)
|
|
67
|
+
将高频核心对话置顶在侧边栏最上方的全局置顶区。由于配置存储在宿主端,置顶状态在页面刷新、更换浏览器或跨设备访问时均能持久保持。
|
|
68
|
+
|
|
69
|
+
### 🔍 上下文片段搜索 (Search with Snippets)
|
|
70
|
+
核心支持对话内容全文检索,`dsh-session-control` 在搜索结果中提取并展示匹配文本的上下文摘要行,无需打开会话即可快速确认匹配细节。
|
|
71
|
+
|
|
72
|
+
### 🗄️ 按时间段归档聚合 (Period-Based Archive)
|
|
73
|
+
归档会话按 *今天*、*本周*、*本月*、*更早* 进行分组折叠展示。折叠的时间段不会在 DOM 中渲染,即使存在数千个历史会话也不会造成界面卡顿。搜索时命中的时间段会自动展开。
|
|
74
|
+
|
|
75
|
+
### 📜 只读归档转录查看器 (Archived Transcript Viewer)
|
|
76
|
+
在核心中已归档的会话无法直接进入对话模式。插件通过独立服务端路由 `GET /dsh-session-control/transcript?session=<id>` 提供安全的只读转录弹窗,方便查阅历史对话与工具调用。
|
|
77
|
+
|
|
78
|
+
### 🧹 自动隐藏空白会话 (Hide Blank Sessions)
|
|
79
|
+
定时调度、消息网关及看板插件会持续创建无消息会话。插件默认自动隐藏空白会话,保持侧边栏整洁清爽。当前正在使用的会话即使为空也绝不会被隐藏。可在设置中自定义开启或关闭。
|
|
80
|
+
|
|
81
|
+
### 👁️ 可逆隐藏 (Reversible Hiding)
|
|
82
|
+
区别于核心单向的归档机制,插件提供轻量级的可逆隐藏功能,方便随时收起或恢复会话。
|
|
83
|
+
|
|
84
|
+
### ☑️ 多选与批量操作 (Batch Operations)
|
|
85
|
+
悬停复选框与 Shift 键区间连选,支持批量隐藏、批量取消隐藏及批量置顶。
|
|
86
|
+
|
|
87
|
+
### ✏️ 就地快速重命名 (In-Place Rename)
|
|
88
|
+
双击会话标题或按下 `F2` 快捷键即可直接在列表中重命名。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 配置项
|
|
93
|
+
|
|
94
|
+
前往 **设置 → 插件 → 插件设置 → 会话控制 (dsh-session-control)**:
|
|
95
|
+
|
|
96
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| `pinned` | `string[]` | `[]` | 置顶会话 ID 列表(数组顺序决定展示顺序) |
|
|
99
|
+
| `hidden` | `string[]` | `[]` | 可逆隐藏的会话 ID 列表 |
|
|
100
|
+
| `hideBlank` | `boolean` | `true` | 是否自动隐藏无消息的空白会话 |
|
|
101
|
+
|
|
102
|
+
所有设置均持久保存在宿主端并在所有客户端间同步。
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 设计约束说明
|
|
107
|
+
|
|
108
|
+
* **不支持跨工作区移动会话:** 工作区与本地磁盘目录绑定,会话归属由其工作目录 (`cwd`) 决定,核心注册表严格校验路径一致性。
|
|
109
|
+
* **不支持从归档恢复:** Harness 核心目前仅提供归档接口,未提供反归档 API。归档会话可通过转录查看器安全只读查阅。
|
|
110
|
+
* **早期日志格式兼容:** 早期格式的历史日志若无法被核心反序列化,插件会展示清晰说明提示,避免错误显示为“无消息”。
|
|
111
|
+
* **不支持物理硬删除:** 核心公共接口未开放硬删除方法,插件严格遵循安全规范。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 架构与可靠性
|
|
116
|
+
|
|
117
|
+
插件采用严谨的**双半区架构**设计以确保最高可靠性:
|
|
118
|
+
|
|
119
|
+
1. **服务半区 (`uiWorkspace` 提供者):**
|
|
120
|
+
* 被替换的原生 `ui-workspace` 负责导出核心服务 `uiWorkspace`,该服务是 `dsh-client-ui-sidebar` 和 `dsh-client-ui-conversation` 的强依赖项。
|
|
121
|
+
* 插件的服务半区以零 React 渲染、零额外逻辑的极轻量方式提供该服务与根 Hooks。
|
|
122
|
+
2. **界面半区 (UI & Views):**
|
|
123
|
+
* 包含工作区树、搜索列表、转录弹窗及设置卡片。
|
|
124
|
+
* 完全由错误边界 (Error Boundaries) 保护,即使 UI 发生异常也不会影响侧边栏其他部分及主聊天界面的正常运行。
|
|
125
|
+
3. **服务端路由:**
|
|
126
|
+
* 提供 `GET /dsh-session-control/transcript` 路由,安全解析并流式读取归档 JSONL 日志。
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 语言与本地化
|
|
131
|
+
|
|
132
|
+
插件核心内置英语文本。俄语及中文等多语言支持通过语言包扩展(如 `@goodandready/dsh-russian-lang`)。词典注册具备容错保护,不会因冲突引发异常。
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 兼容性
|
|
137
|
+
|
|
138
|
+
- 经验证支持 DeepSeek Harness `0.1.2-rc.1` 与 `0.1.3-alpha.1`。
|
|
139
|
+
- 支持 Node.js `^20.19.0` 或 `>=22.12.0`。
|
|
140
|
+
- 与 `@goodandready/dsh-lanmode`、`@goodandready/dsh-kanban`、`@goodandready/dsh-cron` 等 DSH 插件无缝协同工作。
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 许可证
|
|
145
|
+
|
|
146
|
+
MIT © GoodAndReady
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# DESIGN.md — dsh-session-control
|
|
2
|
+
|
|
3
|
+
Дизайн-контракт плагина. Источник истины о том, что видит пользователь и почему
|
|
4
|
+
именно так. Дополняет спецификацию
|
|
5
|
+
`docs/superpowers/specs/2026-09-05-dsh-session-control-design.md`: там — почему
|
|
6
|
+
такая архитектура, здесь — как это выглядит и ведёт себя.
|
|
7
|
+
|
|
8
|
+
## Product / Purpose
|
|
9
|
+
|
|
10
|
+
- **Назначение:** управление сессиями DSH в боковой панели — закрепление нужных
|
|
11
|
+
диалогов, перенос между проектами, переименование по месту, обратимое
|
|
12
|
+
скрытие, доступ к ядровому архиву.
|
|
13
|
+
- **Аудитория:** пользователь DSH, у которого накопилось больше диалогов, чем
|
|
14
|
+
помещается в голове: несколько проектов, десятки сессий, часть из них нужна
|
|
15
|
+
постоянно.
|
|
16
|
+
- **Статус:** выпуск `0.1.0`, работает на production.
|
|
17
|
+
|
|
18
|
+
## User Surfaces
|
|
19
|
+
|
|
20
|
+
- **Web/UI:** тело боковой панели — слот `sidebar.workspaces`. Плагин заменяет
|
|
21
|
+
ядровый блок «Рабочие папки» целиком. Остальная панель (бренд, «Новая
|
|
22
|
+
сессия», подвал, настройки) остаётся ядровой и не затрагивается.
|
|
23
|
+
- **DSH UI / settings / slots:**
|
|
24
|
+
- `sidebar.workspaces` (`single`/`root`) — наш список;
|
|
25
|
+
- `conversation.hero.workspace` (`single`/`root`) — выбор папки на пустом
|
|
26
|
+
экране беседы, обязательство замены;
|
|
27
|
+
- дочерние слоты `sidebar.workspaces.directoryFlow` и
|
|
28
|
+
`conversation.hero.workspace.directoryFlow` — объявляем, чтобы работал
|
|
29
|
+
выбор каталога;
|
|
30
|
+
- `settings.plugin.item` с ключом `dsh-session-control` — карточка настроек.
|
|
31
|
+
- **API:** один собственный маршрут — `GET /dsh-session-control/transcript?session=<id>`,
|
|
32
|
+
расшифровка архивной сессии. Понадобился потому, что заархивированную сессию
|
|
33
|
+
нельзя открыть в беседе: окно истории у клиента поднимается только для сессии
|
|
34
|
+
«на сцене», а архивная текущей быть не может. Все остальные действия идут в
|
|
35
|
+
ядровые `session/rename`, `session/create`, `workspace/archiveSession`.
|
|
36
|
+
- **CLI:** отсутствует.
|
|
37
|
+
- **Документация:** `README.md` (установка, что заменяет, как вернуть штатную
|
|
38
|
+
панель), `CHANGELOG.md`, этот файл, спецификация.
|
|
39
|
+
|
|
40
|
+
## Visual Direction
|
|
41
|
+
|
|
42
|
+
- **Атмосфера:** неотличимо от ядра. Плагин занимает место штатного блока, и
|
|
43
|
+
пользователь не должен замечать подмену — ни по отступам, ни по шрифтам, ни по
|
|
44
|
+
поведению при наведении. Всё новое (закрепление, разделы) выглядит так, будто
|
|
45
|
+
всегда было частью DSH.
|
|
46
|
+
- **Утверждённые референсы:** сама ядровая панель DSH — блок «Рабочие папки»,
|
|
47
|
+
строка сессии, меню `...`, карточки настроек «Консоль» и «Цикл агента».
|
|
48
|
+
Берём из них метрики, роли цветов, поведение раскрытия и вид меню.
|
|
49
|
+
- **Не копировать:** чужие плагины-переписыватели боковой панели, их разметку,
|
|
50
|
+
классы и ассеты. Референс — принципы ядра, а не чей-либо готовый интерфейс.
|
|
51
|
+
|
|
52
|
+
## Foundations
|
|
53
|
+
|
|
54
|
+
### Цвета и роли
|
|
55
|
+
|
|
56
|
+
Только переменные темы, ни одного литерального цвета. Набор снят с ядровых
|
|
57
|
+
модулей панели, а не придуман:
|
|
58
|
+
|
|
59
|
+
| Переменная | Роль у нас |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `--dsw-alias-label-primary` | заголовок сессии, название папки |
|
|
62
|
+
| `--dsw-alias-label-secondary` | возраст сессии, подписи разделов |
|
|
63
|
+
| `--dsw-alias-label-tertiary` | значки, шеврон раскрытия |
|
|
64
|
+
| `--dsw-alias-label-caption` | пояснения в пустых состояниях |
|
|
65
|
+
| `--dsw-alias-interactive-bg-hover` | подсветка строки при наведении |
|
|
66
|
+
| `--dsw-alias-state-business-primary` | активная сессия, индикатор `running` |
|
|
67
|
+
| `--dsw-alias-state-error-primary` | ошибка операции в строке |
|
|
68
|
+
| `--dsw-alias-border-l2`, `--dsw-alias-border-l4` | разделители, рамка карточки |
|
|
69
|
+
| `--dsw-alias-bg-layer-3` | фон карточки настроек |
|
|
70
|
+
| `--dsw-specific-sidebar-fill` | фон панели |
|
|
71
|
+
| `--dsw-alias-scrollbar-bg-l2`, `--dsw-alias-scrollbar-hover-l2` | полоса прокрутки |
|
|
72
|
+
| `--ds-ease-in-out` | все переходы |
|
|
73
|
+
|
|
74
|
+
### Отступы и метрики
|
|
75
|
+
|
|
76
|
+
Горизонтальный отступ берём у ядра — его задаёт `@deepseek-ai/dsh-client-ui-sidebar`,
|
|
77
|
+
который остаётся на месте:
|
|
78
|
+
|
|
79
|
+
```css
|
|
80
|
+
padding-inline: var(--dsh-sidebar-inline-padding);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Это единственный способ совпасть по вертикали с кнопкой «Новая сессия» над нами.
|
|
84
|
+
Своё число здесь — гарантированный рассинхрон при любой правке ядра.
|
|
85
|
+
|
|
86
|
+
Метрики списка `--dsh-session-list-edge-inset`,
|
|
87
|
+
`--dsh-session-list-scrollbar-offset`, `--dsh-session-list-scrollbar-width`
|
|
88
|
+
задавал заменяемый модуль, поэтому вместе с ним они исчезнут. Объявляем свои
|
|
89
|
+
под тем же смыслом и с теми же значениями, с префиксом `--dsc-`.
|
|
90
|
+
|
|
91
|
+
### Типографика
|
|
92
|
+
|
|
93
|
+
Наследуем от документа. Заголовок сессии — обычный вес, размер как в ядровой
|
|
94
|
+
строке; возраст — на ступень мельче и вторичным цветом. Своих шрифтов и
|
|
95
|
+
`font-family` не вводим, кроме `--ds-font-family-code`, если понадобится
|
|
96
|
+
показать идентификатор.
|
|
97
|
+
|
|
98
|
+
### Классы
|
|
99
|
+
|
|
100
|
+
Префикс `dsc-` у каждого класса без исключений. Стили живут в общем документе
|
|
101
|
+
рядом с чужими; класс без префикса однажды перекрасит чужую разметку, и искать
|
|
102
|
+
причину будут долго.
|
|
103
|
+
|
|
104
|
+
### Accessibility
|
|
105
|
+
|
|
106
|
+
- Заголовки разделов — `button` с `aria-expanded`, не `div` с обработчиком.
|
|
107
|
+
- Список сессий — навигация с клавиатуры: стрелки, Enter открывает, F2
|
|
108
|
+
переименовывает, Escape отменяет.
|
|
109
|
+
- Меню `...` открывается с клавиатуры и закрывается по Escape с возвратом
|
|
110
|
+
фокуса на кнопку.
|
|
111
|
+
- Индикатор `running` дублируется текстом для чтения с экрана, а не только
|
|
112
|
+
цветом.
|
|
113
|
+
- Переименование по месту — обычное поле с меткой, а не `contenteditable`.
|
|
114
|
+
|
|
115
|
+
## Components And States
|
|
116
|
+
|
|
117
|
+
### Компоненты
|
|
118
|
+
|
|
119
|
+
| Компонент | Назначение |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `SessionList` | корень блока: поиск, разделы, прокрутка |
|
|
122
|
+
| `PinnedGroup` | закреплённые, плоско, в порядке закрепления |
|
|
123
|
+
| `WorkspaceGroup` | папка и её сессии |
|
|
124
|
+
| `SessionRow` | строка: заголовок, возраст, индикатор, меню |
|
|
125
|
+
| `RowMenu` | закрепить/открепить, переименовать, ответвить, скрыть, архив |
|
|
126
|
+
| `CollapsedSection` | «Скрытые», «Архив» и периоды внутри архива |
|
|
127
|
+
| `ArchiveViewer` | расшифровка архивной сессии, только чтение |
|
|
128
|
+
| `HeroWorkspacePicker` | выбор папки на пустом экране беседы |
|
|
129
|
+
| `SettingsCard` | карточка в «Настройки → Плагины» |
|
|
130
|
+
|
|
131
|
+
### Состояния
|
|
132
|
+
|
|
133
|
+
| Состояние | Что показываем |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `loading` | скелет из трёх строк, без текста «Загрузка» |
|
|
136
|
+
| `empty` — нет папок | приглашение создать первую папку с кнопкой |
|
|
137
|
+
| `empty` — папка пуста | одна строка пояснения внутри папки |
|
|
138
|
+
| `empty` — поиск без результата | «Ничего не найдено» и подсказка искать по содержимому |
|
|
139
|
+
| `error` — операция не удалась | сообщение в самой строке цветом ошибки, действие не откатывается молча |
|
|
140
|
+
| `error` — настройки недоступны | закрепление и скрытие отключены, панель работает как обычный список; причина написана |
|
|
141
|
+
| `unreadable` — журнал раннего формата | расшифровка объясняет, что ядро отказывается читать, а не сообщает о пустоте |
|
|
142
|
+
| `success` | без всплывающих уведомлений: результат виден в самом списке |
|
|
143
|
+
|
|
144
|
+
**Статус снимка настроек важнее значения.** При `unavailable` карточка не рисует
|
|
145
|
+
пустую форму, которая с виду работает: она пишет, что пространство настроек
|
|
146
|
+
хосту неизвестно. Проверяем статус, а не значение.
|
|
147
|
+
|
|
148
|
+
### Формы, валидация и действия
|
|
149
|
+
|
|
150
|
+
- **Переименование:** двойной клик по заголовку → поле в строке. Enter
|
|
151
|
+
сохраняет, Escape отменяет, потеря фокуса сохраняет. Пустое имя не
|
|
152
|
+
сохраняется, строка остаётся в правке.
|
|
153
|
+
- **Скрытие:** обратимо, наше. Возврат — из раздела «Скрытые».
|
|
154
|
+
- **Архив:** пункт меню ведёт в ядровый архив и **необратим**. Спрашиваем
|
|
155
|
+
подтверждение и прямо пишем, что вернуть нельзя. Это единственное действие
|
|
156
|
+
плагина с подтверждением.
|
|
157
|
+
|
|
158
|
+
## User Flows
|
|
159
|
+
|
|
160
|
+
1. **Закрепить нужное.** Меню строки → «Закрепить». Сессия уходит в группу
|
|
161
|
+
сверху. Переживает перезагрузку, другой браузер и другое устройство.
|
|
162
|
+
2. **Найти старое.** Поиск в шапке блока: по заголовкам и по содержимому
|
|
163
|
+
диалогов. Результат — плоский список со сниппетами.
|
|
164
|
+
4. **Убрать лишнее, не потеряв.** Меню → «Скрыть». Раздел «Скрытые» внизу,
|
|
165
|
+
возврат одним нажатием.
|
|
166
|
+
5. **Назвать понятно.** Двойной клик по заголовку, правка на месте.
|
|
167
|
+
6. **Начать работу в проекте.** Кнопка «Новая сессия» ядра и выбор папки на
|
|
168
|
+
пустом экране беседы продолжают работать — это обязательства замены, а не
|
|
169
|
+
наша функциональность.
|
|
170
|
+
|
|
171
|
+
## Do / Don't
|
|
172
|
+
|
|
173
|
+
**Do**
|
|
174
|
+
|
|
175
|
+
- Брать горизонтальные отступы из `--dsh-sidebar-inline-padding`.
|
|
176
|
+
- Регистрировать в слоты только через `ctx.slots.inject(name, cb)`.
|
|
177
|
+
- Показывать отказ операции в интерфейсе, а не в консоли.
|
|
178
|
+
- Держать половину «сервис» без React и без нашей логики.
|
|
179
|
+
- Проверять статус снимка настроек перед отрисовкой формы.
|
|
180
|
+
|
|
181
|
+
**Don't**
|
|
182
|
+
|
|
183
|
+
- Не искать чужие узлы через `querySelector` и не клонировать ядровые кнопки:
|
|
184
|
+
это ровно тот способ, который у нас уже ломается в других плагинах.
|
|
185
|
+
- Не заводить свой раздел в боковом списке настроек — только карточка.
|
|
186
|
+
- Не использовать литеральные цвета и свои значения отступов панели.
|
|
187
|
+
- Не строить скрытие поверх ядрового архива: он необратим.
|
|
188
|
+
- Не предлагать перенос диалога между папками: модель этого не допускает.
|
|
189
|
+
- Не показывать всплывающих уведомлений об успехе.
|
|
190
|
+
- Не разворачивать разделы «Скрытые» и «Архив» по умолчанию.
|
|
191
|
+
|
|
192
|
+
## Locked Design Decisions
|
|
193
|
+
|
|
194
|
+
- **2026-09-05 — заменяем блок, а не дополняем.** Слот `sidebar.workspaces`
|
|
195
|
+
объявлен ядром как `kind: "single"`, второго регистранта не бывает, другого
|
|
196
|
+
слота в теле панели нет. Пересмотр — если ядро объявит слот списочным или
|
|
197
|
+
добавит точки расширения внутри списка.
|
|
198
|
+
- **2026-09-05 — две независимые половины.** Сервис `uiWorkspace` стоит в
|
|
199
|
+
обязательном `inject` у `dsh-client-ui-sidebar` и `dsh-client-ui-conversation`,
|
|
200
|
+
поэтому отказ нашей вёрстки не должен уносить приложение. Пересмотр — если
|
|
201
|
+
ядро перестанет требовать этот сервис.
|
|
202
|
+
- **2026-09-05 — закрепление общее на всю панель,** не внутри папки. Решение
|
|
203
|
+
владельца. Пересмотр — если папок станет столько, что закреплённые начнут
|
|
204
|
+
смешивать проекты.
|
|
205
|
+
- **2026-09-05 — своё скрытие вместо ядрового архива.**
|
|
206
|
+
`WorkspaceRegistry.archiveSession` только добавляет, метода возврата в API нет,
|
|
207
|
+
раздела архива в интерфейсе нет вовсе. Пересмотр — если ядро добавит возврат.
|
|
208
|
+
- **2026-09-05 — настройки карточкой `settings.plugin.item`,** без своего
|
|
209
|
+
раздела. Пересмотр — только по явной просьбе владельца.
|
|
210
|
+
- **2026-09-05 — переноса между папками не будет.** Рабочая папка — это каталог на хосте, а членство сессии выводится из её рабочего каталога: `attachSession` в реестре отвергает сессию, чей `cwd` не совпадает с путём папки. Перенести диалог в другую папку нельзя ни из клиента, ни из серверной половины — это не пробел в API, а устройство модели. Именно поэтому такого пункта нет и в ядровой панели.
|
|
211
|
+
Пересмотр — только если ядро перестанет выводить членство из рабочего
|
|
212
|
+
каталога. Обнаружено проверкой на песочнице: пункт меню возвращал
|
|
213
|
+
`workspace/move-invalid: the session is not accounted`.
|
|
214
|
+
- **2026-09-07 — расшифровку архива готовит серверная половина.** Со стороны
|
|
215
|
+
браузера архив не читается: `session/follow` требует потокового переносчика,
|
|
216
|
+
`session/page` отвечает «failed to observe session», а окно истории
|
|
217
|
+
поднимается только для текущей сессии. Запертое решение «своих маршрутов нет»
|
|
218
|
+
снято осознанно и только для чтения. Пересмотр — если ядро откроет чтение
|
|
219
|
+
чужого журнала с клиента.
|
|
220
|
+
- **2026-09-07 — плагин везёт только английский.** Переводы поставляет
|
|
221
|
+
отдельный языковой плагин и занимает то же пространство настроек раньше нас.
|
|
222
|
+
Собственный русский словарь ронял регистрацию, а вместе с ней всю половину
|
|
223
|
+
«интерфейс». Регистрация словаря защищена отдельно от остального.
|
|
224
|
+
- **2026-09-07 — текущая сессия не прячется никогда,** даже пустая. Иначе
|
|
225
|
+
исчезает строка, в которую человек как раз собирается писать.
|
|
226
|
+
- **2026-09-07 — архив разбит по календарным периодам,** а не по «минус
|
|
227
|
+
столько-то часов»: человек мыслит «сегодня» и «на этой неделе». Свёрнутый
|
|
228
|
+
период не рисуется — это же снимает вопрос отрисовки сотен строк.
|
|
229
|
+
- **2026-09-05 — автоназвание диалогов вне рамок первой версии.** Требует
|
|
230
|
+
вызовов модели и денег; переименование по месту закрывает ту же боль даром.
|
|
231
|
+
Пересмотр — после обкатки, отдельной задачей.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# dsh-session-control — дизайн
|
|
2
|
+
|
|
3
|
+
- Пакет: `@goodandready/dsh-session-control`
|
|
4
|
+
- Дата: 2026-09-05
|
|
5
|
+
- Ядро, под которое проектируем: DSH `0.1.2-rc.1`
|
|
6
|
+
- Статус: дизайн согласован, реализация не начата
|
|
7
|
+
|
|
8
|
+
## Задача
|
|
9
|
+
|
|
10
|
+
Дать управление сессиями в боковой панели: закреплять нужные диалоги, переносить
|
|
11
|
+
их между проектами, переименовывать по месту, находить старое и убирать лишнее
|
|
12
|
+
обратимо.
|
|
13
|
+
|
|
14
|
+
## Что уже умеет ядро
|
|
15
|
+
|
|
16
|
+
Проверено на живом production 2026-09-05, чтение без изменений.
|
|
17
|
+
|
|
18
|
+
| Возможность | Состояние |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Проекты | Есть — «Рабочие папки» (workspaces): создание, переименование, удаление |
|
|
21
|
+
| Переименование сессии | Есть — пункт меню и `session/rename` |
|
|
22
|
+
| Полнотекстовый поиск по истории | Есть и работает — `session/search` возвращает сниппеты из тела диалогов |
|
|
23
|
+
| Порядок сессий внутри папки | API есть — `workspace/insertSessionBefore`. **Внимание:** это только порядок внутри папки; вызов с чужим `workspaceId` отвергается |
|
|
24
|
+
| Признак «сессия работает» | Есть в данных — `SessionSummary.running`. Не показывается |
|
|
25
|
+
|
|
26
|
+
Это мы не переписываем, а переиспользуем.
|
|
27
|
+
|
|
28
|
+
## Чего нет
|
|
29
|
+
|
|
30
|
+
1. **Закрепление.** Ни метода, ни поля, ни признака нигде в API и в UI.
|
|
31
|
+
3. **Обзор без поиска.** Сессии видны только внутри раскрытой папки. Общего
|
|
32
|
+
списка нет.
|
|
33
|
+
4. **Возврат из архива.** Архив односторонний, см. ниже.
|
|
34
|
+
|
|
35
|
+
### Архив ядра необратим
|
|
36
|
+
|
|
37
|
+
`WorkspaceRegistry.archiveSession` только добавляет:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
archiveSession(sessionId) {
|
|
41
|
+
if (this.requireState().archivedSessionIds.includes(sessionId)) return;
|
|
42
|
+
...archivedSessionIds: [...state.archivedSessionIds, sessionId]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Метода «вернуть» в API нет. Плюс во всём модуле панели про архив есть ровно одна
|
|
47
|
+
строка — пункт меню `menu.archiveSession`; раздела с архивом не существует.
|
|
48
|
+
Сессия исчезает из интерфейса навсегда, хотя сама цела.
|
|
49
|
+
|
|
50
|
+
Поэтому наше «скрыть» строим отдельно и обратимо, а ядровый архив показываем
|
|
51
|
+
как отдельный раздел только на чтение.
|
|
52
|
+
|
|
53
|
+
## Ключевое ограничение: блок несущий
|
|
54
|
+
|
|
55
|
+
Тело боковой панели — единственный слот, и он не допускает второго регистранта.
|
|
56
|
+
Контракт `@deepseek-ai/dsh-client-ui-sidebar`:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
"sidebar.workspaces": { kind: "single", scope: "root" },
|
|
60
|
+
"sidebar.settings": { kind: "single", scope: "root" },
|
|
61
|
+
"sidebar.footer.action": { kind: "list", scope: "root" },
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`single` — значит «дополнить» нельзя, только заменить. А чтобы занять слот,
|
|
65
|
+
нужно освободить его, то есть выключить ядровый ряд `ui-workspace`.
|
|
66
|
+
|
|
67
|
+
Но тот же модуль отдаёт служебный сервис `uiWorkspace`, и он стоит в
|
|
68
|
+
**обязательном** `inject` у ядровых модулей:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
// dsh-client-ui-sidebar
|
|
72
|
+
const inject = ["slots", "layout", "uiWorkspace", "locale"]
|
|
73
|
+
// dsh-client-ui-conversation
|
|
74
|
+
const inject = ["slots", "sessions", "uiSession", "uiWorkspace", "locale", "settingsScope"]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Плюс он нужен `ui-agent-preset` и `ui-directory-picker-browse`. Просто выключить
|
|
78
|
+
`ui-workspace` нельзя: пропадёт `uiWorkspace`, и не поднимутся ни панель, ни
|
|
79
|
+
интерфейс беседы — экран будет пустой.
|
|
80
|
+
|
|
81
|
+
Значит, заменяя блок, мы обязаны отдать всё, что модуль отдавал.
|
|
82
|
+
|
|
83
|
+
## Пять обязательств замены
|
|
84
|
+
|
|
85
|
+
1. **`sidebar.workspaces`** — наш список. Ради этого всё.
|
|
86
|
+
2. **Сервис `uiWorkspace`** — около 120 строк, шесть методов
|
|
87
|
+
(`connectWorkspace`, `startSession`, `archiveSession`, `pickDirectory`,
|
|
88
|
+
`listDirectory`, `createDirectory`) плюс слежение за навигацией. Всё поверх
|
|
89
|
+
публичных `workspace/*`, `session/*` и `remote.directoryPicker`.
|
|
90
|
+
3. **Дочерние слоты** `sidebar.workspaces.directoryFlow` и
|
|
91
|
+
`conversation.hero.workspace.directoryFlow`, оба `single`/`root`. Без них
|
|
92
|
+
отвалится выбор каталога при создании папки.
|
|
93
|
+
4. **`conversation.hero.workspace`** — выбор папки на пустом экране беседы.
|
|
94
|
+
5. **Корневой хук** `ctx.slots.provideRoot({ hooks: { workspaces: workspaces.list } })` —
|
|
95
|
+
им пользуются чужие компоненты, воспроизвести как есть.
|
|
96
|
+
|
|
97
|
+
Объём при этом не «переписать модуль»: 125 КБ ядрового модуля — это React-вёрстка,
|
|
98
|
+
которую мы и так заменяем осознанно. Служебная часть небольшая и механическая.
|
|
99
|
+
|
|
100
|
+
## Устройство: две независимые половины
|
|
101
|
+
|
|
102
|
+
Плагин становится несущим, поэтому он обязан быть устроен так, чтобы его
|
|
103
|
+
поломка не гасила приложение. Один пакет, две половины, регистрируются
|
|
104
|
+
раздельно.
|
|
105
|
+
|
|
106
|
+
### Половина «сервис»
|
|
107
|
+
|
|
108
|
+
Только сервис `uiWorkspace`, объявления дочерних слотов и корневой хук. Без
|
|
109
|
+
React, без нашей логики, без настроек, без сети сверх ядровых вызовов. Её
|
|
110
|
+
единственная задача — никогда не падать. Пишется и проверяется первой.
|
|
111
|
+
|
|
112
|
+
Инъекции повторяют ядровые:
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
inject = ["slots", "sessions", "workspaces", "locale", "remote", "remote.directoryPicker"]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Половина «интерфейс»
|
|
119
|
+
|
|
120
|
+
Наш список: закрепление, перенос, скрытие, переименование по месту, поиск,
|
|
121
|
+
группировка. Регистрируется отдельно и целиком в `try/catch`.
|
|
122
|
+
|
|
123
|
+
**Смысл разделения:** если интерфейс сломается, слот `sidebar.workspaces`
|
|
124
|
+
останется пустым, но сервис жив — панель, настройки и беседа работают. Будь это
|
|
125
|
+
одна половина, любая опечатка в нашей вёрстке гасила бы весь DSH.
|
|
126
|
+
|
|
127
|
+
## Панель
|
|
128
|
+
|
|
129
|
+
Сверху вниз:
|
|
130
|
+
|
|
131
|
+
1. **Поиск** — то же поле, тот же `sessions.search(query, signal)` по содержимому.
|
|
132
|
+
2. **Закреплённые** — плоская группа, без папок, в порядке закрепления.
|
|
133
|
+
Закрепление общее на всю панель, не внутри папки.
|
|
134
|
+
3. **Папки** — как сейчас: папка, внутри её сессии.
|
|
135
|
+
4. **Скрытые** — свёрнутый раздел, наше, возврат одним нажатием.
|
|
136
|
+
5. **Архив** — свёрнутый раздел, показываем только если непустой. Открыть
|
|
137
|
+
можно, вернуть нельзя; подписать честно.
|
|
138
|
+
|
|
139
|
+
**Строка сессии:** заголовок, справа возраст, точка-индикатор при
|
|
140
|
+
`SessionSummary.running`, при наведении — `...`.
|
|
141
|
+
|
|
142
|
+
**Меню строки:** Закрепить / Открепить · Переименовать · Ответвить · Скрыть ·
|
|
143
|
+
В архив.
|
|
144
|
+
|
|
145
|
+
**Переноса между папками не будет.** Рабочая папка — это каталог на хосте, а членство сессии выводится из её
|
|
146
|
+
рабочего каталога: `attachSession` в реестре отвергает сессию, чей `cwd` не
|
|
147
|
+
совпадает с путём папки. Перенести диалог в другую папку нельзя ни из клиента,
|
|
148
|
+
ни из серверной половины — это не пробел в API, а устройство модели. Именно
|
|
149
|
+
поэтому такого пункта нет и в ядровой панели.
|
|
150
|
+
|
|
151
|
+
**Переименование:** двойной клик по заголовку, поле прямо в строке, без диалога.
|
|
152
|
+
|
|
153
|
+
**Настройки плагина:** карточка в «Настройки → Плагины → Настройки плагинов»
|
|
154
|
+
через слот `settings.plugin.item`, ключ карточки равен имени пространства
|
|
155
|
+
настроек. Своего раздела в боковом списке не заводим.
|
|
156
|
+
|
|
157
|
+
## Данные
|
|
158
|
+
|
|
159
|
+
Пространство настроек `dsh-session-control`, два поля:
|
|
160
|
+
|
|
161
|
+
| Поле | Тип | Смысл |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| `pinned` | `string[]` | sessionId закреплённых, порядок массива = порядок в панели |
|
|
164
|
+
| `hidden` | `string[]` | sessionId скрытых |
|
|
165
|
+
|
|
166
|
+
Состояние «свёрнуто/развёрнуто» в настройках не храним: это видовое состояние
|
|
167
|
+
одного браузера, и у ядра для него есть свой механизм (`createWorkspaceViewStore`).
|
|
168
|
+
Гонять его через серверные настройки незачем.
|
|
169
|
+
|
|
170
|
+
Настройки DSH серверные, поэтому закрепления переживают перезагрузку, смену
|
|
171
|
+
браузера и устройство. Своего хранилища не заводим.
|
|
172
|
+
|
|
173
|
+
Ссылка на исчезнувшую сессию — не ошибка: при отрисовке просто пропускаем
|
|
174
|
+
sessionId, которого нет в списке, и вычищаем при следующей записи.
|
|
175
|
+
|
|
176
|
+
## Потоки
|
|
177
|
+
|
|
178
|
+
Читаем те же источники, что ядровый модуль: `ctx.get("sessions")` и
|
|
179
|
+
`ctx.get("workspaces")` — их сторы дают список сессий, список папок и
|
|
180
|
+
`archivedSessionIds`. Свои поля читаем из настроек.
|
|
181
|
+
|
|
182
|
+
Порядок отрисовки: закреплённые вычитаем из папок, чтобы сессия не появлялась
|
|
183
|
+
дважды. Скрытые вычитаем отовсюду, кроме своего раздела.
|
|
184
|
+
|
|
185
|
+
Мутации идут прямо в ядровые методы: `session/rename`,
|
|
186
|
+
`workspace/insertSessionBefore`, `session/create`. Своих серверных маршрутов
|
|
187
|
+
плагин не добавляет — серверная половина нужна только чтобы объявить
|
|
188
|
+
пространство настроек.
|
|
189
|
+
|
|
190
|
+
## Обработка отказов
|
|
191
|
+
|
|
192
|
+
- Любая регистрация в слот — через `ctx.slots.inject(name, cb)`, чтобы имя,
|
|
193
|
+
которого нет, было безвредным.
|
|
194
|
+
- Регистрация локали — однократная, с учётом того, что клиентское дерево
|
|
195
|
+
применяется дважды за загрузку. Повторный `apply` не должен ни бросать, ни
|
|
196
|
+
дублировать эффекты.
|
|
197
|
+
- Отказ любого ядрового вызова показываем в строке, а не проглатываем: молчащий
|
|
198
|
+
`catch` уже дал нам два невидимых дефекта в других плагинах.
|
|
199
|
+
- Аварийный выход: `dsh plugin remove` возвращает ядровый ряд `ui-workspace` и
|
|
200
|
+
штатную панель.
|
|
201
|
+
|
|
202
|
+
## Чего не делаем
|
|
203
|
+
|
|
204
|
+
- **Автоназвание диалогов.** Требует вызова модели, денег и отдельного
|
|
205
|
+
обсуждения. Переименование по месту закрывает боль дешевле.
|
|
206
|
+
- **Отдельный режим «все диалоги по датам».** Папки уже группируют, поиск
|
|
207
|
+
работает. Вводить, только если после обкатки окажется мало.
|
|
208
|
+
- **Множественное выделение, экспорт, теги, цвета.** Не просили.
|
|
209
|
+
- **Свой раздел в боковом списке настроек.** Карточка.
|
|
210
|
+
|
|
211
|
+
## Проверки
|
|
212
|
+
|
|
213
|
+
Тестового контура нет, функциональная проверка только на production после
|
|
214
|
+
merge проверенного `main` и явного «ок» на deploy.
|
|
215
|
+
|
|
216
|
+
Обязательный набор, каждый пункт с доказательством в issue:
|
|
217
|
+
|
|
218
|
+
1. Панель рисуется, папки и сессии на месте.
|
|
219
|
+
2. Закрепление: закрепить, перезагрузить страницу, открыть в другом браузере —
|
|
220
|
+
осталось.
|
|
221
|
+
3. Перенос сессии в другую папку, порядок сохраняется после перезагрузки.
|
|
222
|
+
4. Скрыть и вернуть.
|
|
223
|
+
5. Переименование по двойному клику.
|
|
224
|
+
6. Поиск по содержимому находит старый диалог.
|
|
225
|
+
7. Создание папки с выбором каталога — проверяет обязательство №3.
|
|
226
|
+
8. Пустой экран беседы: выбор папки на месте — обязательство №4.
|
|
227
|
+
9. «Новая сессия» работает — обязательство №2.
|
|
228
|
+
10. Открытие беседы, отправка сообщения — интерфейс беседы жив.
|
|
229
|
+
11. Консоль при загрузке чистая, `Failed to load plugins` нет.
|
|
230
|
+
|
|
231
|
+
Пункты 7–10 — не формальность: это ровно то, что ломается при неполной замене.
|
|
232
|
+
|
|
233
|
+
## Порядок работ
|
|
234
|
+
|
|
235
|
+
1. Gitea-репозиторий, каркас, `cordis.patch.yml`, обезличенность с первого
|
|
236
|
+
коммита.
|
|
237
|
+
2. Половина «сервис» + проверка, что панель и беседа живы при выключенном
|
|
238
|
+
ядровом ряде и пустом слоте.
|
|
239
|
+
3. Половина «интерфейс»: список, папки, поиск.
|
|
240
|
+
4. Закрепление.
|
|
241
|
+
5. Перенос между папками.
|
|
242
|
+
6. Скрытие и раздел архива.
|
|
243
|
+
7. Переименование по месту.
|
|
244
|
+
8. Карточка настроек, README, документация.
|
|
245
|
+
9. Production-проверка по списку выше.
|
|
246
|
+
|
|
247
|
+
Доставка на прод до публикации: тарбол собирается из смерженного проверенного
|
|
248
|
+
`main` и кладётся в `~/.cache/dsh-artifacts/`. Ссылка в рабочий каталог DEV
|
|
249
|
+
недопустима ни на каком этапе. После публикации профиль переводится на обычную
|
|
250
|
+
версию из registry.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-session-control",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Session management for the DeepSeek Harness sidebar: pin conversations, search their contents, read archived transcripts and hide the noise",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"lib/",
|
|
15
15
|
"cordis.patch.yml",
|
|
16
16
|
"README.md",
|
|
17
|
+
"docs/",
|
|
17
18
|
"CHANGELOG.md",
|
|
18
19
|
"LICENSE"
|
|
19
20
|
],
|