@michengai/dsh-archive-manager 0.1.39 → 0.1.41
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/CHANGELOG.md +11 -0
- package/CHANGELOG.zh-CN.md +11 -0
- package/README.md +76 -122
- package/README.zh-CN.md +78 -124
- package/lib/archive-experience.js +68 -0
- package/lib/client.js +171 -188
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
Published release notes are retained below; new versions are added without removing earlier entries.
|
|
6
6
|
|
|
7
|
+
## 0.1.41 - 2026-09-14
|
|
8
|
+
|
|
9
|
+
- Improve the archived-session bulk action bar with grouped selection details and actions, fixing wrapped button labels in narrow content areas.
|
|
10
|
+
- Simplify the bar when nothing is selected and show cross-filter selection counts and scope separately so hidden selections remain clear.
|
|
11
|
+
|
|
12
|
+
## 0.1.40 - 2026-09-13
|
|
13
|
+
|
|
14
|
+
- Open archived sessions in the native DSH conversation view and continue chatting while keeping them archived, or restore and open them.
|
|
15
|
+
- Close the settings overlay after an archived conversation opens successfully; keep settings open and show an error on failure, with a restore-and-open alternative when host navigation adaptation is unavailable.
|
|
16
|
+
- Localize archive navigation errors and avoid unnecessary warnings when older hosts load or open archives successfully.
|
|
17
|
+
|
|
7
18
|
## 0.1.39 - 2026-09-11
|
|
8
19
|
|
|
9
20
|
- Preserve the Codex UI workspace list, styling, and dedicated interactions when both plugins are installed, while keeping archive management available.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
以下发布说明会持续保留;新增版本时不再删除较早记录。
|
|
6
6
|
|
|
7
|
+
## 0.1.41 - 2026-09-14
|
|
8
|
+
|
|
9
|
+
- 优化归档会话的批量操作栏:选择摘要与操作分组显示,修复窄内容区内按钮文字折行的问题。
|
|
10
|
+
- 未选择时简化操作栏;跨筛选选择的数量和影响范围独立显示,避免遗漏不可见的已选会话。
|
|
11
|
+
|
|
12
|
+
## 0.1.40 - 2026-09-13
|
|
13
|
+
|
|
14
|
+
- 支持直接打开归档会话,在 DSH 原生页面查看完整对话并保持归档继续聊天,也可恢复后打开。
|
|
15
|
+
- 修复查看归档后设置遮罩未关闭的问题:打开成功后关闭设置,失败时保留设置并显示错误;宿主导航适配不可用时可恢复后打开。
|
|
16
|
+
- 归档打开失败提示随界面语言显示,旧宿主正常加载和打开归档时不再产生多余警告。
|
|
17
|
+
|
|
7
18
|
## 0.1.39 - 2026-09-11
|
|
8
19
|
|
|
9
20
|
- 修复与 Codex UI 共存时抢占其工作区列表的问题,保留 Codex UI 的列表样式和专属交互,同时提供归档管理入口。
|
package/README.md
CHANGED
|
@@ -14,196 +14,150 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/@michengai/dsh-archive-manager)
|
|
15
15
|
[](https://www.npmjs.com/package/@michengai/dsh-archive-manager)
|
|
16
16
|
[](https://github.com/MichengAI/dsh-archive-manager)
|
|
17
|
-
[](https://nodejs.org/)
|
|
18
17
|
</div>
|
|
19
18
|
|
|
20
19
|
> DSH Archive Manager is a community-maintained DeepSeek Harness (DSH) plugin, not an official DeepSeek AI product.
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## What you can do
|
|
23
22
|
|
|
24
|
-
Put inactive conversations away and find them again when needed
|
|
23
|
+
Put inactive conversations away and find them again when needed, keeping everyday task lists tidy.
|
|
25
24
|
|
|
26
|
-
- **
|
|
27
|
-
- **Find past work**: search titles, filter by project, and sort by time or title
|
|
28
|
-
- **
|
|
29
|
-
- **Clean up records**: permanently delete
|
|
25
|
+
- **Archive conversations**: put away one chat or all unarchived chats in a workspace.
|
|
26
|
+
- **Find past work**: search titles, filter by project, and sort by time or title.
|
|
27
|
+
- **Restore tasks**: restore one chat, selected chats, a project group, or all archives.
|
|
28
|
+
- **Clean up records**: permanently delete unwanted archived conversations after confirmation.
|
|
30
29
|
|
|
31
30
|
## Screenshots
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
Archive a chat from the sidebar session menu:
|
|
34
33
|
|
|
35
34
|

|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
Find, restore, and clean up chats in **Settings → Archived sessions**:
|
|
38
37
|
|
|
39
38
|

|
|
40
39
|
|
|
41
|
-
## DSH product ecosystem
|
|
42
|
-
|
|
43
|
-
For a ready-to-use workbench, download [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop/releases). If you already use [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), install any of these eight plugins individually. The desktop app includes all eight.
|
|
44
|
-
|
|
45
|
-
| Plugin | What you can do |
|
|
46
|
-
| --- | --- |
|
|
47
|
-
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | Organize projects and conversations, search tasks, and navigate chat turns |
|
|
48
|
-
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | Send tasks and receive replies through your usual messenger |
|
|
49
|
-
| [Automation](https://github.com/MichengAI/dsh-automation) | Schedule tasks and review each run |
|
|
50
|
-
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | Find, enable, create, and import local skills |
|
|
51
|
-
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | Search, restore, or clean up archived conversations |
|
|
52
|
-
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | Choose and summon specialists for your task |
|
|
53
|
-
| [BTW](https://github.com/MichengAI/dsh-btw) | Ask side questions without interrupting the main task |
|
|
54
|
-
| [Simplify](https://github.com/MichengAI/dsh-simplify) | Use /simplify to improve code within your Git changes |
|
|
55
|
-
|
|
56
40
|
## Prerequisites
|
|
57
41
|
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
- Examples use the `web` profile; replace it with the target profile.
|
|
62
|
-
- Source installation and development require Node.js 22+ and pnpm. npm installation does not require running `pnpm install` separately.
|
|
63
|
-
|
|
64
|
-
DSH peer dependencies accept only `0.1.0-rc.8 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2`; other RCs and stable versions are not automatically accepted. Development dependencies are pinned to `0.1.5-rc.2`.
|
|
42
|
+
- A working [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web installation with `dsh` available in your terminal.
|
|
43
|
+
- Supported DSH versions: `0.1.0-rc.8`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, and `0.1.5-rc.2`. Other versions are not currently supported.
|
|
44
|
+
- Node.js matching `^22.19.0 || >=24.0.0`. Source installation also requires pnpm.
|
|
65
45
|
|
|
66
46
|
## Installation
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- `pnpm test`: build and run local tests, excluding `test/fixtures`; local host links may affect dependency resolution.
|
|
71
|
-
- `pnpm test:matrix`: build, then install five isolated host versions and run the full matrix.
|
|
72
|
-
- `pnpm test:latest`: build, then run the real storage regressions against the isolated latest baseline.
|
|
73
|
-
- `pnpm test:compat`: validate legacy cache migration and run the full matrix.
|
|
74
|
-
|
|
75
|
-
Do not run `test/fixtures/*.mjs` directly. Fixtures validate the isolated entry point, dependency versions, and resolved paths before loading the host. If local dependencies have drifted, run `pnpm install --frozen-lockfile` to restore declared development dependencies. This does not guarantee removal of undeclared packages or host links under `test/node_modules`; use the isolated commands for compatibility acceptance.
|
|
48
|
+
Examples use the `web` profile. Replace it with the profile you actually use.
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
| --- | --- | --- |
|
|
79
|
-
| `0.1.0-rc.8` | `4.0.1` | 153 passed |
|
|
80
|
-
| `0.1.1-rc.2` | `4.0.1` | 153 passed, plus legacy cache migration validation |
|
|
81
|
-
| `0.1.2-rc.1` | `4.0.2` | 153 passed |
|
|
82
|
-
| `0.1.5-rc.1` | `4.0.2` | 162 passed |
|
|
83
|
-
| `0.1.5-rc.2` | `4.0.2` | 162 passed |
|
|
50
|
+
### Ask an agent to install it
|
|
84
51
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
The installation commands below use the official npm registry.
|
|
88
|
-
|
|
89
|
-
### Ask an agent to install it (recommended)
|
|
90
|
-
|
|
91
|
-
Send the prompt below to any agent that can run terminal commands on your computer. Replace `web` with your actual profile. Once installed, use the plugin in DSH.
|
|
52
|
+
Send this prompt to an agent that can run terminal commands on your computer:
|
|
92
53
|
|
|
93
54
|
```text
|
|
94
|
-
Install the
|
|
55
|
+
Install the latest @michengai/dsh-archive-manager into my local DSH web profile using the official npm registry. Check the plugin configuration afterward, then explain how to reload DSH and open archived session management.
|
|
95
56
|
```
|
|
96
57
|
|
|
97
|
-
### Install
|
|
58
|
+
### Install manually
|
|
98
59
|
|
|
99
|
-
Run
|
|
60
|
+
Run in PowerShell:
|
|
100
61
|
|
|
101
62
|
```powershell
|
|
102
63
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
103
64
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
65
|
+
|
|
104
66
|
dsh plugin --profile web add @michengai/dsh-archive-manager@latest --registry=https://registry.npmjs.org/
|
|
105
|
-
dsh --profile web --dump-config
|
|
106
67
|
```
|
|
107
68
|
|
|
108
|
-
|
|
69
|
+
Restart DSH Web, then hard-refresh your browser with `Ctrl+Shift+R`. Open **Settings → Archived sessions** to get started.
|
|
109
70
|
|
|
110
|
-
|
|
71
|
+
## Usage
|
|
111
72
|
|
|
112
|
-
|
|
73
|
+
| Goal | Action |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| Archive one chat | Open its sidebar menu and choose **Archive session** |
|
|
76
|
+
| Archive a workspace | Open the workspace menu and choose the option to archive its chats |
|
|
77
|
+
| Find an archive | Open **Settings → Archived sessions**, then search titles or filter by project |
|
|
78
|
+
| Change the order | Sort by update time, creation time, or title |
|
|
79
|
+
| Restore one chat | Click **Unarchive** beside the session |
|
|
80
|
+
| Restore or delete in bulk | Select chats and use the bulk actions, or use the project menu or page-wide actions |
|
|
113
81
|
|
|
114
|
-
|
|
82
|
+
Selections persist when filters change. Check the hidden selection count before applying bulk actions, or clear your selection first.
|
|
115
83
|
|
|
116
|
-
|
|
84
|
+
### View and continue archived conversations
|
|
117
85
|
|
|
118
|
-
|
|
86
|
+
Available starting with `0.1.40`:
|
|
119
87
|
|
|
120
|
-
|
|
88
|
+
- **View conversation**: open the native DSH session to view messages, attachments, and tool details. Continue chatting while keeping the session archived.
|
|
89
|
+
- **Restore and open**: unarchive the session and open it to resume work.
|
|
121
90
|
|
|
122
91
|
## Updates
|
|
123
92
|
|
|
124
|
-
|
|
93
|
+
Click **Check for updates** in the archive management page header. DSH CLI or Desktop environments with automatic update support can update directly; other environments provide a manual command for the current profile. You can also rerun the installation command above.
|
|
125
94
|
|
|
126
|
-
##
|
|
95
|
+
## FAQ
|
|
127
96
|
|
|
128
|
-
|
|
129
|
-
2. Open **Settings → Archived sessions** to inspect sessions by workspace.
|
|
130
|
-
3. Search by title, sort by update time, creation time, or title, or filter the list by project.
|
|
131
|
-
4. Select **Unarchive** to restore one session, or select **Restore all** in the page header.
|
|
132
|
-
5. Open a project heading's action menu to restore or delete all of that project's archived chats.
|
|
133
|
-
6. Use the delete icon to remove one session permanently, then confirm the deletion. **It cannot be undone.**
|
|
97
|
+
### Why is the entry missing after installation?
|
|
134
98
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
## Data handling limits
|
|
138
|
-
|
|
139
|
-
- Deletion always requires confirmation.
|
|
140
|
-
- It removes workspace records, archive markers, and the projection cache. For the official JSONL backend, a validated layout also allows removal of the session-owned directory and its contents, including attachments. Other backends or unknown layouts only lose the located transcript artifact, never its parent directory.
|
|
141
|
-
- Project containers and storage roots are retained. Deletion refuses symbolic links or Windows junctions at the official layout's project/session directory levels and keeps the operation retryable.
|
|
142
|
-
- Layout validation uses the official backend's initialized absolute root, so a relative root remains stable across host working-directory changes. If that field is unavailable, only an absolute configured root is accepted. An unverified official JSONL layout emits a warning with the session ID and artifact path before falling back to artifact-only deletion.
|
|
143
|
-
- Directory validation is not a cross-process filesystem lock: do not concurrently move or replace storage directories or change directory links during deletion. Storage paths writable by untrusted processes are not a security isolation boundary.
|
|
144
|
-
- A live session finishes writing before cleanup to prevent data truncation.
|
|
145
|
-
- The official `ui-workspace` remains enabled, so the home picker and navigation follow the host. The archive sidebar uses priority `-0.5`, ahead of the official default but behind Codex UI 1.1.2 at `-1`. Codex UI retains its list, styling, and interactions; archive management remains available in settings. Unloading Codex UI restores the archive sidebar; unloading the archive frontend restores the official sidebar. Backend workspace and projection services still use the archive implementations.
|
|
146
|
-
- The sidebar directory entry reuses official leaf components through a separate child slot and follows their registration lifetimes. It does not redeclare official directory slots. Custom directory extensions with additional child slots should use the official home entry; this adapter does not transfer their child-slot permissions.
|
|
147
|
-
- Install through the DSH profile instead of manually composing the patch.
|
|
148
|
-
|
|
149
|
-
## Secondary development
|
|
150
|
-
|
|
151
|
-
### Install from source
|
|
152
|
-
|
|
153
|
-
Use this for debugging or unpublished changes. The cloned directory becomes the plugin source path:
|
|
99
|
+
Restart DSH Web and hard-refresh your browser. Make sure you installed into the profile you are using. If the entry is still missing, run:
|
|
154
100
|
|
|
155
101
|
```powershell
|
|
156
102
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
157
103
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
158
|
-
|
|
159
|
-
git clone https://github.com/MichengAI/dsh-archive-manager.git
|
|
160
|
-
Set-Location .\dsh-archive-manager
|
|
161
|
-
pnpm install --frozen-lockfile
|
|
162
|
-
pnpm build
|
|
163
|
-
dsh plugin --profile web add .
|
|
104
|
+
|
|
164
105
|
dsh --profile web --dump-config
|
|
165
106
|
```
|
|
166
107
|
|
|
167
|
-
|
|
108
|
+
The configuration should include `workspace-archive-manager` and `ui-workspace-archive-manager`. If you previously set the official `ui-workspace` to `disabled: true` in your profile's `cordis.patch.yml`, remove that disabling override and restart.
|
|
168
109
|
|
|
169
|
-
|
|
110
|
+
### How is archiving different from deletion?
|
|
170
111
|
|
|
171
|
-
|
|
172
|
-
- [src\workspace.js](src/workspace.js): archived-session and workspace service.
|
|
173
|
-
- [src\projcache.js](src/projcache.js): session projection cache.
|
|
174
|
-
- [src\client.js](src/client.js): Settings page and archive UI.
|
|
175
|
-
- `test\*.test.mjs`: host, client, Remote, and styling coverage.
|
|
112
|
+
Archiving puts a conversation away so you can restore it later. **Permanent deletion cannot be undone** and may also remove that session's attachments. It does not delete your project working directory. Deletion requires confirmation.
|
|
176
113
|
|
|
177
|
-
|
|
114
|
+
### Can I use it with Codex UI?
|
|
178
115
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
pnpm test
|
|
183
|
-
pnpm pack:check
|
|
184
|
-
dsh plugin --profile web add .
|
|
185
|
-
```
|
|
116
|
+
Yes. [Codex UI](https://github.com/MichengAI/dsh-codex-ui) keeps its sidebar appearance and interactions. Archive management remains available in **Settings → Archived sessions**.
|
|
117
|
+
|
|
118
|
+
For other problems, open an [issue](https://github.com/MichengAI/dsh-archive-manager/issues) with your DSH and plugin versions, reproduction steps, and error details.
|
|
186
119
|
|
|
187
|
-
|
|
120
|
+
## Install from source
|
|
188
121
|
|
|
189
|
-
|
|
122
|
+
<details>
|
|
123
|
+
<summary>Expand for development or testing unreleased changes</summary>
|
|
124
|
+
|
|
125
|
+
Run these commands in a directory of your choice. For local changes that have not been pushed, use the existing working copy.
|
|
190
126
|
|
|
191
127
|
```powershell
|
|
192
128
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
193
129
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
194
|
-
|
|
130
|
+
|
|
131
|
+
git clone https://github.com/MichengAI/dsh-archive-manager.git
|
|
132
|
+
Set-Location .\dsh-archive-manager
|
|
133
|
+
pnpm install --frozen-lockfile
|
|
134
|
+
pnpm build
|
|
135
|
+
dsh plugin --profile web add .
|
|
195
136
|
```
|
|
196
137
|
|
|
197
|
-
|
|
138
|
+
Restart DSH Web and hard-refresh your browser afterward. Edit [src](src), not the generated `lib` directory. Run `pnpm test` to validate changes or `pnpm verify` for the full checks.
|
|
198
139
|
|
|
199
|
-
|
|
140
|
+
</details>
|
|
200
141
|
|
|
201
|
-
|
|
142
|
+
## DSH product ecosystem
|
|
202
143
|
|
|
203
|
-
|
|
144
|
+
For a desktop workbench, download [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop/releases). Existing [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) installations can add plugins as needed by following each project's README. Below are 11 first-party plugins; consult the corresponding desktop release notes and bundled catalog for what that version includes.
|
|
204
145
|
|
|
205
|
-
|
|
146
|
+
| Plugin | What you can do |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | Organize projects and conversations, search tasks, and navigate chat turns |
|
|
149
|
+
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | Choose and summon specialists for your task |
|
|
150
|
+
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | Find, enable, create, and import local skills |
|
|
151
|
+
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | Search, restore, or clean up archived conversations |
|
|
152
|
+
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | Send tasks and receive replies through messaging platforms |
|
|
153
|
+
| [Automation](https://github.com/MichengAI/dsh-automation) | Schedule tasks and review each run |
|
|
154
|
+
| [BTW](https://github.com/MichengAI/dsh-btw) | Ask side questions without interrupting the main task |
|
|
155
|
+
| [Simplify](https://github.com/MichengAI/dsh-simplify) | Use `/simplify` to improve code within your Git changes |
|
|
156
|
+
| [PUA](https://github.com/MichengAI/dsh-pua) | Guide the Agent to try new approaches after failures, investigate causes, and verify results before completion |
|
|
157
|
+
| [Code Review](https://github.com/MichengAI/dsh-code-review) | Use `/review` to request an independent Agent code review and receive the report in the current conversation |
|
|
158
|
+
| [Codex Pet](https://github.com/MichengAI/dsh-codex-pet) | View conversation notifications and respond to tool approvals and questions through a desktop pet |
|
|
206
159
|
|
|
207
|
-
Recovery checks the npm package name, version, and tag commit first. An exact match skips dependency installation, full builds, and republishing, and only synchronizes the Release. Unpublished old tags must still satisfy their original cooling policy. Exact-version and latest propagation each have a two-minute deadline; a lagging latest tag does not create a non-Latest Release prematurely.
|
|
208
160
|
|
|
209
|
-
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
Licensed under [Apache License 2.0](LICENSE).
|
package/README.zh-CN.md
CHANGED
|
@@ -14,196 +14,150 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/@michengai/dsh-archive-manager)
|
|
15
15
|
[](https://www.npmjs.com/package/@michengai/dsh-archive-manager)
|
|
16
16
|
[](https://github.com/MichengAI/dsh-archive-manager)
|
|
17
|
-
[](https://nodejs.org/)
|
|
18
17
|
</div>
|
|
19
18
|
|
|
20
19
|
> DSH Archive Manager 是社区维护的 DeepSeek Harness(DSH)插件,并非 DeepSeek AI 官方产品。
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## 你可以用它做什么
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
把暂时不用的会话收起来,需要时再找回,让日常任务列表更清爽。
|
|
25
24
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
25
|
+
- **归档会话**:收起单条聊天,或整个工作区的未归档聊天。
|
|
26
|
+
- **找回历史**:搜索会话标题,按项目筛选,按时间或标题排序。
|
|
27
|
+
- **恢复任务**:恢复单条、选中的会话、整个项目或全部归档会话。
|
|
28
|
+
- **清理记录**:确认后永久删除不再需要的归档会话。
|
|
30
29
|
|
|
31
30
|
## 界面预览
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
从侧栏会话菜单归档聊天:
|
|
34
33
|
|
|
35
34
|

|
|
36
35
|
|
|
37
|
-
在「设置 →
|
|
36
|
+
在「设置 → 归档会话」集中查找、恢复和清理:
|
|
38
37
|
|
|
39
|
-
;已有 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 环境,可按需独立安装以下 8 个自研插件。桌面端已随附这些插件。
|
|
44
|
-
|
|
45
|
-
| 插件 | 你可以用它做什么 |
|
|
46
|
-
| --- | --- |
|
|
47
|
-
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | 整理项目与会话、搜索任务、跳转对话轮次 |
|
|
48
|
-
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | 从微信、飞书、钉钉等消息平台下任务、收回复 |
|
|
49
|
-
| [Automation](https://github.com/MichengAI/dsh-automation) | 按计划执行任务,查看每次运行的结果 |
|
|
50
|
-
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | 统一查找、启停、创建和导入本机技能 |
|
|
51
|
-
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | 搜索、恢复或清理已归档会话 |
|
|
52
|
-
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | 按任务选择并召唤专业角色 |
|
|
53
|
-
| [BTW](https://github.com/MichengAI/dsh-btw) | 在当前上下文中临时旁问,不打断主任务 |
|
|
54
|
-
| [Simplify](https://github.com/MichengAI/dsh-simplify) | 用 /simplify 整理 Git 改动范围内的代码 |
|
|
38
|
+

|
|
55
39
|
|
|
56
40
|
## 前置条件
|
|
57
41
|
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
- 以下示例使用 `web` profile;请替换为实际目标 profile。
|
|
62
|
-
- 从源码安装或二次开发需要 Node.js 22+ 与 pnpm;仅从 npm 安装无需另外执行 `pnpm install`。
|
|
63
|
-
|
|
64
|
-
DSH peer 依赖仅接受 `0.1.0-rc.8 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2`;不自动接纳其他 RC 或正式版。开发依赖固定为 `0.1.5-rc.2`。
|
|
42
|
+
- 已能正常使用 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web,并可在终端运行 `dsh`。
|
|
43
|
+
- 当前支持 DSH `0.1.0-rc.8`、`0.1.1-rc.2`、`0.1.2-rc.1`、`0.1.5-rc.1`、`0.1.5-rc.2`;其他版本暂未纳入支持范围。
|
|
44
|
+
- Node.js 版本需满足 `^22.19.0 || >=24.0.0`;从源码安装还需要 pnpm。
|
|
65
45
|
|
|
66
46
|
## 安装
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- `pnpm test`:构建并执行本地常规测试,不包含 `test/fixtures`;实际依赖可能受本机宿主链接影响。
|
|
71
|
-
- `pnpm test:matrix`:先构建,再隔离安装五个宿主版本,执行完整矩阵。
|
|
72
|
-
- `pnpm test:latest`:先构建,再隔离运行最新版的 真实存储回归。
|
|
73
|
-
- `pnpm test:compat`:旧缓存迁移验证加完整矩阵。
|
|
74
|
-
|
|
75
|
-
不要直接执行 `test/fixtures/*.mjs`;夹具会在加载宿主前检查隔离入口、依赖版本及实际路径。若本机依赖脱节,可先执行 `pnpm install --frozen-lockfile` 恢复声明的开发依赖;它不保证清理未声明包或 `test/node_modules` 的宿主链接,兼容性验收以隔离命令为准。
|
|
48
|
+
以下示例使用 `web` profile,请替换为你实际使用的 profile。
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
| --- | --- | --- |
|
|
79
|
-
| `0.1.0-rc.8` | `4.0.1` | 153 项通过 |
|
|
80
|
-
| `0.1.1-rc.2` | `4.0.1` | 153 项通过,另含旧缓存迁移验证 |
|
|
81
|
-
| `0.1.2-rc.1` | `4.0.2` | 153 项通过 |
|
|
82
|
-
| `0.1.5-rc.1` | `4.0.2` | 162 项通过 |
|
|
83
|
-
| `0.1.5-rc.2` | `4.0.2` | 162 项通过 |
|
|
50
|
+
### 让 Agent 帮你安装
|
|
84
51
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
以下安装命令使用官方 npm 源。
|
|
88
|
-
|
|
89
|
-
### 让 Agent 帮你安装(推荐)
|
|
90
|
-
|
|
91
|
-
把下面这段话发给任意能够执行本机终端命令的 Agent。将 `web` 替换为实际使用的 profile;安装完成后,在 DSH 中使用本插件。
|
|
52
|
+
把下面这段话发给能执行本机终端命令的 Agent:
|
|
92
53
|
|
|
93
54
|
```text
|
|
94
|
-
请将
|
|
55
|
+
请将 @michengai/dsh-archive-manager 最新版安装到本机 DSH 的 web profile,使用官方 npm 源。安装后检查插件配置,并告诉我如何重新加载 DSH、进入归档会话管理页。
|
|
95
56
|
```
|
|
96
57
|
|
|
97
|
-
###
|
|
58
|
+
### 手动安装
|
|
98
59
|
|
|
99
|
-
|
|
60
|
+
在 PowerShell 中执行:
|
|
100
61
|
|
|
101
62
|
```powershell
|
|
102
63
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
103
64
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
65
|
+
|
|
104
66
|
dsh plugin --profile web add @michengai/dsh-archive-manager@latest --registry=https://registry.npmjs.org/
|
|
105
|
-
dsh --profile web --dump-config
|
|
106
67
|
```
|
|
107
68
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
配置输出中应包含 `workspace-archive-manager` 与 `ui-workspace-archive-manager`,同时官方 `ui-workspace` 保持启用。安装后重启 DSH Web 并在浏览器硬刷新;请勿手工复制客户端文件,否则设置页和归档菜单不会被挂载。
|
|
111
|
-
|
|
112
|
-
### 已安装环境升级检查
|
|
113
|
-
|
|
114
|
-
升级会重新读取插件包内的补丁,无需手工迁移旧 bundle。但 Profile 自己的 `cordis.patch.yml` 在 bundle 之后应用:若曾手工将 `ui-workspace` 设为 `disabled: true`,请移除该覆盖或改为 `disabled: false`,保留其余配置。
|
|
69
|
+
安装后重启 DSH Web,并按 `Ctrl+Shift+R` 硬刷新浏览器。打开「设置 → 归档会话」即可使用。
|
|
115
70
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
侧栏添加目录依赖官方 `sidebar.workspaces.directoryFlow` 插槽。源槽不存在或没有目录组件时,该入口会隐藏,不会因此记录同步错误;可使用官方首页入口。带额外子插槽的目录扩展暂不支持镜像,会输出警告。
|
|
71
|
+
## 使用
|
|
119
72
|
|
|
120
|
-
|
|
73
|
+
| 你想做什么 | 操作 |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| 归档一条会话 | 打开侧栏会话菜单,选择「归档会话」 |
|
|
76
|
+
| 归档整个工作区 | 打开工作区菜单,选择归档该工作区的会话 |
|
|
77
|
+
| 查找归档 | 打开「设置 → 归档会话」,搜索标题或按项目筛选 |
|
|
78
|
+
| 调整排列顺序 | 按更新时间、创建时间或标题排序 |
|
|
79
|
+
| 恢复一条会话 | 点击会话右侧的「取消归档」 |
|
|
80
|
+
| 批量恢复或删除 | 勾选会话后使用批量操作;也可使用项目菜单或页面顶部的全部操作 |
|
|
121
81
|
|
|
122
|
-
|
|
82
|
+
切换筛选条件会保留已选会话。批量操作前留意隐藏的已选数量,或先清空选择。
|
|
123
83
|
|
|
124
|
-
|
|
84
|
+
### 查看并继续归档对话
|
|
125
85
|
|
|
126
|
-
|
|
86
|
+
从 `0.1.40` 起支持以下操作:
|
|
127
87
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
3. 按标题搜索,按更新时间、创建时间或标题排序,或按项目筛选列表。
|
|
131
|
-
4. 点击「取消归档」恢复单个会话,或在顶部点击「全部恢复」。
|
|
132
|
-
5. 打开项目标题右侧菜单,可恢复或删除该项目的全部已归档聊天。
|
|
133
|
-
6. 点击删除图标永久移除单个会话;删除前确认提示。**删除无法撤销。**
|
|
88
|
+
- **查看对话**:打开 DSH 原生会话页,查看消息、附件和工具详情;可直接继续聊天,保持归档状态。
|
|
89
|
+
- **恢复并打开**:取消归档后进入原会话,继续工作。
|
|
134
90
|
|
|
135
|
-
|
|
91
|
+
## 更新
|
|
136
92
|
|
|
137
|
-
|
|
93
|
+
在归档管理页标题处点击「检查更新」。支持自动更新的 DSH CLI 或 Desktop 环境可直接更新;其他环境会提供适用于当前 profile 的手动命令。也可重新执行上面的安装命令。
|
|
138
94
|
|
|
139
|
-
|
|
140
|
-
- 删除会移除工作区记录、归档标记和投影缓存。官方 JSONL 后端在目录布局校验通过后,会一并删除会话专属目录及其中的附件等内容;其他后端或未知布局仅删除定位到的转录工件,不删除其父目录。
|
|
141
|
-
- 不清理项目分组目录或存储根目录。官方布局的项目/会话目录若为符号链接或 Windows junction,会拒绝删除并保留可重试状态。
|
|
142
|
-
- 布局校验优先使用官方后端初始化时确定的绝对根路径,相对路径配置不再受宿主工作目录变化影响;若无法取得该字段,则只接受绝对路径配置。官方 JSONL 布局校验失败时,会记录包含会话 ID 和工件路径的警告,再降级为仅删除工件。
|
|
143
|
-
- 目录校验不是跨进程文件锁:删除期间不要由其他进程迁移、替换存储目录或改写目录链接。插件不将可被不可信进程改写的存储路径视为安全隔离边界。
|
|
144
|
-
- 正在写入的会话会在完成写入后清理,避免截断数据。
|
|
145
|
-
- 本插件保留官方 `ui-workspace`,首页工作区选择器与导航服务跟随宿主;以优先级 `-0.5` 覆盖官方默认侧栏;与 Codex UI 1.1.2 共存时保留其优先级 `-1` 的列表、样式与交互,归档管理仍在设置页提供。卸载 Codex UI 后恢复归档侧栏;卸载归档前端后恢复官方侧栏。后端工作区和会话投影服务仍由归档实现提供。
|
|
146
|
-
- 侧栏目录入口复用官方目录叶组件,使用独立子插槽,随官方条目加载、卸载同步;不重复声明官方目录插槽。自定义目录扩展若声明额外子插槽,应使用官方首页入口;当前适配不转移其子插槽权限。
|
|
147
|
-
- 请仅通过 DSH profile 安装,避免手工拼接补丁配置。
|
|
95
|
+
## 常见问题
|
|
148
96
|
|
|
149
|
-
|
|
97
|
+
### 安装后找不到入口?
|
|
150
98
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
适用于调试或使用未发布改动。克隆后的目录会直接作为插件安装路径:
|
|
99
|
+
先重启 DSH Web 并硬刷新浏览器,确认安装到了当前使用的 profile。仍未显示时,执行:
|
|
154
100
|
|
|
155
101
|
```powershell
|
|
156
102
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
157
103
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
158
|
-
|
|
159
|
-
git clone https://github.com/MichengAI/dsh-archive-manager.git
|
|
160
|
-
Set-Location .\dsh-archive-manager
|
|
161
|
-
pnpm install --frozen-lockfile
|
|
162
|
-
pnpm build
|
|
163
|
-
dsh plugin --profile web add .
|
|
104
|
+
|
|
164
105
|
dsh --profile web --dump-config
|
|
165
106
|
```
|
|
166
107
|
|
|
167
|
-
|
|
108
|
+
配置中应包含 `workspace-archive-manager` 和 `ui-workspace-archive-manager`。若曾在 profile 的 `cordis.patch.yml` 中手动将官方 `ui-workspace` 设为 `disabled: true`,请移除该禁用覆盖,再重启。
|
|
168
109
|
|
|
169
|
-
|
|
110
|
+
### 归档和删除有什么区别?
|
|
170
111
|
|
|
171
|
-
|
|
172
|
-
- [src\workspace.js](src/workspace.js):归档会话和工作区服务实现。
|
|
173
|
-
- [src\projcache.js](src/projcache.js):会话投影缓存实现。
|
|
174
|
-
- [src\client.js](src/client.js):设置页和归档会话界面。
|
|
175
|
-
- `test\*.test.mjs`:Host、客户端、Remote 和样式边界测试。
|
|
112
|
+
归档只是收起会话,可以恢复。**永久删除无法撤销**,并可能一并清理该会话的附件;不会删除你的项目工作目录。删除前会要求确认。
|
|
176
113
|
|
|
177
|
-
|
|
114
|
+
### 可以和 Codex UI 一起使用吗?
|
|
178
115
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
pnpm test
|
|
183
|
-
pnpm pack:check
|
|
184
|
-
dsh plugin --profile web add .
|
|
185
|
-
```
|
|
116
|
+
可以。保留 [Codex UI](https://github.com/MichengAI/dsh-codex-ui) 的侧栏样式和交互,归档管理仍在「设置 → 归档会话」中。
|
|
117
|
+
|
|
118
|
+
遇到其他问题,请提交 [Issue](https://github.com/MichengAI/dsh-archive-manager/issues),附上 DSH 与插件版本、复现步骤和错误信息。
|
|
186
119
|
|
|
187
|
-
|
|
120
|
+
## 从源码安装
|
|
188
121
|
|
|
189
|
-
|
|
122
|
+
<details>
|
|
123
|
+
<summary>开发或测试未发布改动时展开</summary>
|
|
124
|
+
|
|
125
|
+
在你选择的源码目录中执行以下命令。未推送的本地改动需使用已有工作副本。
|
|
190
126
|
|
|
191
127
|
```powershell
|
|
192
128
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
193
129
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
194
|
-
|
|
130
|
+
|
|
131
|
+
git clone https://github.com/MichengAI/dsh-archive-manager.git
|
|
132
|
+
Set-Location .\dsh-archive-manager
|
|
133
|
+
pnpm install --frozen-lockfile
|
|
134
|
+
pnpm build
|
|
135
|
+
dsh plugin --profile web add .
|
|
195
136
|
```
|
|
196
137
|
|
|
197
|
-
|
|
138
|
+
完成后重启 DSH Web 并硬刷新浏览器。修改 [src](src) 中的源码,不直接编辑生成目录 `lib`;使用 `pnpm test` 验证修改,使用 `pnpm verify` 执行完整检查。
|
|
198
139
|
|
|
199
|
-
|
|
140
|
+
</details>
|
|
200
141
|
|
|
201
|
-
|
|
142
|
+
## DSH 产品生态
|
|
202
143
|
|
|
203
|
-
|
|
144
|
+
想使用桌面工作台,可下载 [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop/releases);已有 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 环境,可按各项目 README 按需安装。以下列出 11 个自研插件;桌面端实际随附范围以对应版本的发行说明和内置清单为准。
|
|
204
145
|
|
|
205
|
-
|
|
146
|
+
| 插件 | 你可以用它做什么 |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | 整理项目与会话、搜索任务、跳转对话轮次 |
|
|
149
|
+
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | 按任务选择并召唤专业角色 |
|
|
150
|
+
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | 统一查找、启停、创建和导入本机技能 |
|
|
151
|
+
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | 搜索、恢复或清理已归档会话 |
|
|
152
|
+
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | 从消息平台下任务、收回复 |
|
|
153
|
+
| [Automation](https://github.com/MichengAI/dsh-automation) | 按计划执行任务,查看每次运行的结果 |
|
|
154
|
+
| [BTW](https://github.com/MichengAI/dsh-btw) | 在当前上下文中临时旁问,不打断主任务 |
|
|
155
|
+
| [Simplify](https://github.com/MichengAI/dsh-simplify) | 用 `/simplify` 整理 Git 改动范围内的代码 |
|
|
156
|
+
| [PUA](https://github.com/MichengAI/dsh-pua) | 引导 Agent 在失败时换方法、查原因,并在完成前验证结果 |
|
|
157
|
+
| [Code Review](https://github.com/MichengAI/dsh-code-review) | 用 `/review` 发起独立 Agent 代码审查,在当前会话接收报告 |
|
|
158
|
+
| [Codex Pet](https://github.com/MichengAI/dsh-codex-pet) | 通过桌面宠物查看会话提醒、处理工具审批和问题回答 |
|
|
206
159
|
|
|
207
|
-
恢复已发布版本时,流程先校验 npm 包名、版本和标签提交:一致则跳过依赖安装、完整构建和再次发布,仅补同步 Release;未发布的旧标签仍需满足原标签的冷却策略,不能通过重试绕过。精确版本及 latest 传播分别最多等待两分钟,latest 落后时不提前创建非 Latest Release。
|
|
208
160
|
|
|
209
|
-
|
|
161
|
+
## 许可证
|
|
162
|
+
|
|
163
|
+
本项目采用 [Apache License 2.0](LICENSE)。
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
class ArchiveNavigationError extends Error {
|
|
2
|
+
constructor(code) {
|
|
3
|
+
super(code);
|
|
4
|
+
this.name = "ArchiveNavigationError";
|
|
5
|
+
this.code = code;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function formatArchiveNavigationError(error, t) {
|
|
9
|
+
return error instanceof ArchiveNavigationError ? t(`archives.${error.code}`) : String(error?.message ?? error);
|
|
10
|
+
}
|
|
11
|
+
async function openArchivedConversation(actions, sessionId, restore, isActive = () => true) {
|
|
12
|
+
if (restore) await actions.restore(sessionId);
|
|
13
|
+
if (isActive()) await actions.open(sessionId);
|
|
14
|
+
}
|
|
15
|
+
function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened = () => {
|
|
16
|
+
}, warn = (...args) => console.warn(...args) } = {}) {
|
|
17
|
+
let allowed;
|
|
18
|
+
const original = navigation?.clearArchivedCurrent;
|
|
19
|
+
const descriptor = navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent");
|
|
20
|
+
const wrapped = function(...args) {
|
|
21
|
+
const current = sessions.list.getSnapshot().current;
|
|
22
|
+
if (current === allowed && current !== void 0 && workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
|
|
23
|
+
allowed = void 0;
|
|
24
|
+
return original.apply(this, args);
|
|
25
|
+
};
|
|
26
|
+
const ownsWrapper = () => navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent")?.value === wrapped;
|
|
27
|
+
if (typeof original === "function") {
|
|
28
|
+
try {
|
|
29
|
+
navigation.clearArchivedCurrent = wrapped;
|
|
30
|
+
if (!ownsWrapper()) throw new Error("\u5BFC\u822A\u65B9\u6CD5\u672A\u63A5\u7EB3\u5F52\u6863\u9002\u914D");
|
|
31
|
+
} catch (error) {
|
|
32
|
+
warn("archive-manager: \u65E0\u6CD5\u9002\u914D\u5F52\u6863\u5BFC\u822A\uFF0C\u8BF7\u6062\u590D\u4F1A\u8BDD\u540E\u6253\u5F00\u3002", error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
open(id) {
|
|
37
|
+
allowed = id;
|
|
38
|
+
try {
|
|
39
|
+
if (typeof original === "function" && !ownsWrapper() && workspaces.list.getSnapshot().archivedSessionIds.includes(id)) {
|
|
40
|
+
warn("archive-manager: \u5F52\u6863\u5BFC\u822A\u9002\u914D\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u66FF\u6362\u3002");
|
|
41
|
+
throw new ArchiveNavigationError("navigationUnavailable");
|
|
42
|
+
}
|
|
43
|
+
sessions.open(id);
|
|
44
|
+
if (sessions.list.getSnapshot().current !== id) {
|
|
45
|
+
warn("archive-manager: \u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\u3002", { cleanupAvailable: typeof original === "function" });
|
|
46
|
+
throw new ArchiveNavigationError("sessionNotRetained");
|
|
47
|
+
}
|
|
48
|
+
onOpened();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
allowed = void 0;
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
dispose() {
|
|
55
|
+
allowed = void 0;
|
|
56
|
+
if (!navigation || Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent")?.value !== wrapped) return;
|
|
57
|
+
if (descriptor) Object.defineProperty(navigation, "clearArchivedCurrent", descriptor);
|
|
58
|
+
else delete navigation.clearArchivedCurrent;
|
|
59
|
+
original.call(navigation);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
ArchiveNavigationError,
|
|
65
|
+
allowArchivedNavigation,
|
|
66
|
+
formatArchiveNavigationError,
|
|
67
|
+
openArchivedConversation
|
|
68
|
+
};
|
package/lib/client.js
CHANGED
|
@@ -1,4 +1,68 @@
|
|
|
1
1
|
(() => {
|
|
2
|
+
// src/archive-experience.js
|
|
3
|
+
var ArchiveNavigationError = class extends Error {
|
|
4
|
+
constructor(code) {
|
|
5
|
+
super(code);
|
|
6
|
+
this.name = "ArchiveNavigationError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
function formatArchiveNavigationError(error, t) {
|
|
11
|
+
return error instanceof ArchiveNavigationError ? t(`archives.${error.code}`) : String(error?.message ?? error);
|
|
12
|
+
}
|
|
13
|
+
async function openArchivedConversation(actions, sessionId, restore, isActive = () => true) {
|
|
14
|
+
if (restore) await actions.restore(sessionId);
|
|
15
|
+
if (isActive()) await actions.open(sessionId);
|
|
16
|
+
}
|
|
17
|
+
function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened = () => {
|
|
18
|
+
}, warn = (...args) => console.warn(...args) } = {}) {
|
|
19
|
+
let allowed;
|
|
20
|
+
const original = navigation?.clearArchivedCurrent;
|
|
21
|
+
const descriptor = navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent");
|
|
22
|
+
const wrapped = function(...args) {
|
|
23
|
+
const current = sessions.list.getSnapshot().current;
|
|
24
|
+
if (current === allowed && current !== void 0 && workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
|
|
25
|
+
allowed = void 0;
|
|
26
|
+
return original.apply(this, args);
|
|
27
|
+
};
|
|
28
|
+
const ownsWrapper = () => navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent")?.value === wrapped;
|
|
29
|
+
if (typeof original === "function") {
|
|
30
|
+
try {
|
|
31
|
+
navigation.clearArchivedCurrent = wrapped;
|
|
32
|
+
if (!ownsWrapper()) throw new Error("\u5BFC\u822A\u65B9\u6CD5\u672A\u63A5\u7EB3\u5F52\u6863\u9002\u914D");
|
|
33
|
+
} catch (error) {
|
|
34
|
+
warn("archive-manager: \u65E0\u6CD5\u9002\u914D\u5F52\u6863\u5BFC\u822A\uFF0C\u8BF7\u6062\u590D\u4F1A\u8BDD\u540E\u6253\u5F00\u3002", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
open(id) {
|
|
39
|
+
allowed = id;
|
|
40
|
+
try {
|
|
41
|
+
if (typeof original === "function" && !ownsWrapper() && workspaces.list.getSnapshot().archivedSessionIds.includes(id)) {
|
|
42
|
+
warn("archive-manager: \u5F52\u6863\u5BFC\u822A\u9002\u914D\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u66FF\u6362\u3002");
|
|
43
|
+
throw new ArchiveNavigationError("navigationUnavailable");
|
|
44
|
+
}
|
|
45
|
+
sessions.open(id);
|
|
46
|
+
if (sessions.list.getSnapshot().current !== id) {
|
|
47
|
+
warn("archive-manager: \u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\u3002", { cleanupAvailable: typeof original === "function" });
|
|
48
|
+
throw new ArchiveNavigationError("sessionNotRetained");
|
|
49
|
+
}
|
|
50
|
+
onOpened();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
allowed = void 0;
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
dispose() {
|
|
57
|
+
allowed = void 0;
|
|
58
|
+
if (!navigation || Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent")?.value !== wrapped) return;
|
|
59
|
+
if (descriptor) Object.defineProperty(navigation, "clearArchivedCurrent", descriptor);
|
|
60
|
+
else delete navigation.clearArchivedCurrent;
|
|
61
|
+
original.call(navigation);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
2
66
|
// src/directory-flow-slot.js
|
|
3
67
|
function mirrorDirectoryFlow(ctx, target) {
|
|
4
68
|
const source = "sidebar.workspaces.directoryFlow";
|
|
@@ -665,20 +729,48 @@
|
|
|
665
729
|
function GithubMark16() {
|
|
666
730
|
return (0, react_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": true, focusable: "false", children: (0, react_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M8 0a8 8 0 0 0-2.53 15.59c.4.074.547-.173.547-.385 0-.19-.007-.693-.01-1.36-2.226.484-2.695-1.073-2.695-1.073-.364-.924-.89-1.17-.89-1.17-.726-.496.055-.486.055-.486.803.056 1.225.824 1.225.824.714 1.223 1.872.87 2.328.665.072-.517.28-.87.508-1.07-1.777-.202-3.645-.888-3.645-3.956 0-.874.31-1.588.823-2.148-.083-.202-.357-1.017.078-2.12 0 0 .672-.215 2.2.82A7.65 7.65 0 0 1 8 4.8c.68.003 1.365.092 2.004.27 1.527-1.035 2.197-.82 2.197-.82.437 1.103.162 1.918.08 2.12.513.56.822 1.274.822 2.148 0 3.076-1.872 3.752-3.654 3.95.288.248.544.735.544 1.482 0 1.07-.01 1.932-.01 2.195 0 .214.144.463.55.384A8.001 8.001 0 0 0 8 0Z" }) });
|
|
667
731
|
}
|
|
668
|
-
function
|
|
732
|
+
function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, viewState, close, t }) {
|
|
669
733
|
const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
|
|
670
734
|
const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
|
|
671
735
|
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
672
736
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
673
737
|
const [error, setError] = (0, react.useState)(null);
|
|
674
738
|
const [notice, setNotice] = (0, react.useState)(null);
|
|
675
|
-
const [query, setQuery] = (0, react.useState)("");
|
|
676
|
-
const [project, setProject] = (0, react.useState)("all");
|
|
677
|
-
const [sortBy, setSortBy] = (0, react.useState)("updated");
|
|
739
|
+
const [query, setQuery] = (0, react.useState)(viewState?.query ?? "");
|
|
740
|
+
const [project, setProject] = (0, react.useState)(viewState?.project ?? "all");
|
|
741
|
+
const [sortBy, setSortBy] = (0, react.useState)(viewState?.sortBy ?? "updated");
|
|
678
742
|
const [createdAtById, setCreatedAtById] = (0, react.useState)({});
|
|
679
743
|
const [unarchivingSessionIds, setUnarchivingSessionIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
680
744
|
const unarchivingSessionIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
681
745
|
const [selectedSessionIds, setSelectedSessionIds] = (0, react.useState)([]);
|
|
746
|
+
const navigationActive = (0, react.useRef)(true);
|
|
747
|
+
const navigationPending = (0, react.useRef)(false);
|
|
748
|
+
(0, react.useEffect)(() => {
|
|
749
|
+
navigationActive.current = true;
|
|
750
|
+
return () => {
|
|
751
|
+
navigationActive.current = false;
|
|
752
|
+
};
|
|
753
|
+
}, []);
|
|
754
|
+
const viewConversation = async (session, restore = false) => {
|
|
755
|
+
if (busy || navigationPending.current) return;
|
|
756
|
+
navigationPending.current = true;
|
|
757
|
+
setBusy(true);
|
|
758
|
+
setError(null);
|
|
759
|
+
try {
|
|
760
|
+
await openArchivedConversation({ restore: unarchiveSession, open: async (id) => {
|
|
761
|
+
await openConversation(id);
|
|
762
|
+
if (navigationActive.current) close?.();
|
|
763
|
+
} }, session.id, restore, () => navigationActive.current);
|
|
764
|
+
} catch (reason) {
|
|
765
|
+
if (navigationActive.current) setError(formatArchiveNavigationError(reason, t));
|
|
766
|
+
} finally {
|
|
767
|
+
navigationPending.current = false;
|
|
768
|
+
if (navigationActive.current) setBusy(false);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
(0, react.useEffect)(() => {
|
|
772
|
+
if (viewState) Object.assign(viewState, { query, project, sortBy });
|
|
773
|
+
}, [query, project, sortBy, viewState]);
|
|
682
774
|
const groups = (0, react.useMemo)(() => deriveArchivedGroups(sessions.byId, workspaceState.items, workspaceState.archivedSessionIds, t("group.ungrouped")), [sessions.byId, workspaceState, t]);
|
|
683
775
|
const sortedGroups = (0, react.useMemo)(() => sortArchivedGroups(groups, sortBy, createdAtById, t), [groups, sortBy, createdAtById, t]);
|
|
684
776
|
(0, react.useEffect)(() => {
|
|
@@ -814,12 +906,18 @@
|
|
|
814
906
|
className: "dsham_settingsSearch",
|
|
815
907
|
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })]
|
|
816
908
|
}), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, { value: "created", label: t("archives.sortCreated") }, { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })]
|
|
817
|
-
}), groups.length > 0 && (0, react_jsx_runtime.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
909
|
+
}), groups.length > 0 && (0, react_jsx_runtime.jsx)(ArchiveSelectionToolbar, {
|
|
910
|
+
selectedCount: selectedSessionIds.length,
|
|
911
|
+
hiddenCount: selectedSessionIds.length - selectedVisibleCount,
|
|
912
|
+
allVisibleSelected,
|
|
913
|
+
selectedVisibleCount,
|
|
914
|
+
visibleCount: visibleSessionIds.length,
|
|
915
|
+
busy,
|
|
916
|
+
t,
|
|
917
|
+
onToggle: toggleVisibleSelection,
|
|
918
|
+
onClear: () => setSelectedSessionIds([]),
|
|
919
|
+
onRestore: onSelectedUnarchive,
|
|
920
|
+
onDelete: () => setDeleteTarget({ kind: "batch", target: { scope: "sessions", sessionIds: selectedSessionIds }, count: selectedSessionIds.length })
|
|
823
921
|
}), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
|
|
824
922
|
const target = archivedBatchTargetForGroup(group.key);
|
|
825
923
|
const count = deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length;
|
|
@@ -834,7 +932,7 @@
|
|
|
834
932
|
className: "dsham_settingsRow",
|
|
835
933
|
children: [(0, react_jsx_runtime.jsx)(ArchiveSelectionCheckbox, { checked: selectedSessionIdSet.has(session.id), disabled: busy, label: t("archives.selectSession", { name: displayTitle(session, t) }), onChange: (event) => toggleSessionSelection(session.id, event.target.checked) }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsContent", children: [(0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsTitle", children: displayTitle(session, t) }), (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsMeta", children: archiveTimeLabel(session.updatedAt, t) })] }), (0, react_jsx_runtime.jsxs)("div", {
|
|
836
934
|
className: "dsham_settingsActions",
|
|
837
|
-
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => onUnarchive(session.id), children: t("menu.unarchive") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy || unarchivingSessionIds.has(session.id), "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget({ kind: "session", session }), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
|
|
935
|
+
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session), children: t("archives.viewConversation") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session, true), children: t("archives.restoreOpen") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => onUnarchive(session.id), children: t("menu.unarchive") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy || unarchivingSessionIds.has(session.id), "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget({ kind: "session", session }), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
|
|
838
936
|
})]
|
|
839
937
|
}, session.id))
|
|
840
938
|
})]
|
|
@@ -3077,7 +3175,7 @@
|
|
|
3077
3175
|
const ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS = ".dsham_settingsTitleRow{display:flex;align-items:center;gap:8px 12px;min-width:0;flex-wrap:wrap}.dsham_settingsLinks{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.dsham_settingsExternalLink{display:inline-flex;align-items:center;gap:5px;min-height:28px;padding:0 8px;color:var(--dsw-alias-label-secondary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;font-size:12px;font-weight:500;line-height:18px;text-decoration:none;white-space:nowrap}.dsham_settingsExternalLink:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsExternalLink:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_settingsExternalLink svg{flex:none}@media(max-width:720px){.dsham_settingsTitleRow{flex-wrap:wrap}}";
|
|
3078
3176
|
const ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS = ".dsham_settingsDeleteConfirm{color:var(--dsw-alias-state-error-primary)!important;background:transparent!important;border-color:var(--dsw-alias-state-error-primary)!important}.dsham_settingsDeleteConfirm:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)!important}.dsham_settingsDeleteConfirm:focus-visible{outline:2px solid var(--dsw-alias-state-error-secondary);outline-offset:2px}.dsham_settingsDeleteConfirm:disabled{cursor:not-allowed;opacity:.5}";
|
|
3079
3177
|
const ARCHIVE_WORKSPACE_CONFIRM_CSS = ".dsham_archiveWorkspaceConfirm{color:var(--dsw-alias-state-error-primary)!important;background:transparent!important;border-color:var(--dsw-alias-state-error-primary)!important}.dsham_archiveWorkspaceConfirm:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)!important}.dsham_archiveWorkspaceConfirm:focus-visible{outline:2px solid var(--dsw-alias-state-error-secondary);outline-offset:2px}.dsham_archiveWorkspaceConfirm:disabled{cursor:not-allowed;opacity:.5}";
|
|
3080
|
-
const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{
|
|
3178
|
+
const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{margin:-8px 0 16px;padding:4px 0 12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsSelectionMain{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px;min-height:36px}.dsham_settingsSelectionSummary{display:flex;flex-wrap:wrap;align-items:center;gap:12px;min-width:0;max-width:100%}.dsham_settingsSelectionToggle{display:inline-flex;flex:none;white-space:nowrap;align-items:center;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;cursor:pointer}.dsham_settingsCheckbox{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#3b82f6)}.dsham_settingsSelectionClear{flex:none;min-height:28px;padding:0 0 0 12px;border:0;border-left:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-state-business-primary,#3b82f6);font:inherit;font-size:13px;white-space:nowrap;cursor:pointer}.dsham_settingsSelectionClear:hover:not(:disabled){text-decoration:underline}.dsham_settingsSelectionActions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px;max-width:100%;margin-left:auto}.dsham_settingsSelectionAction{flex:none;white-space:nowrap;min-height:32px;padding:0 14px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsSelectionAction:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsSelectionDelete{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.dsham_settingsSelectionScope{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow-wrap:anywhere}.dsham_settingsSelectionAction:disabled,.dsham_settingsSelectionClear:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsSelectionAction:focus-visible,.dsham_settingsSelectionClear:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#3b82f6);outline-offset:3px}";
|
|
3081
3179
|
const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
|
|
3082
3180
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
|
|
3083
3181
|
const tag = document.createElement("style");
|
|
@@ -3217,6 +3315,32 @@
|
|
|
3217
3315
|
}) : null]
|
|
3218
3316
|
});
|
|
3219
3317
|
}
|
|
3318
|
+
function ArchiveSelectionToolbar({ selectedCount, hiddenCount, allVisibleSelected, selectedVisibleCount, visibleCount, busy, t, onToggle, onClear, onRestore, onDelete }) {
|
|
3319
|
+
const hasSelection = selectedCount > 0;
|
|
3320
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
3321
|
+
className: "dsham_settingsSelection",
|
|
3322
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
3323
|
+
className: "dsham_settingsSelectionMain",
|
|
3324
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
3325
|
+
className: "dsham_settingsSelectionSummary",
|
|
3326
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
3327
|
+
className: "dsham_settingsSelectionToggle",
|
|
3328
|
+
title: t("archives.selectAllFiltered"),
|
|
3329
|
+
children: [(0, react_jsx_runtime.jsx)(ArchiveSelectionCheckbox, {
|
|
3330
|
+
checked: allVisibleSelected,
|
|
3331
|
+
indeterminate: selectedVisibleCount > 0 && !allVisibleSelected,
|
|
3332
|
+
disabled: busy || visibleCount === 0,
|
|
3333
|
+
label: t("archives.selectAllFiltered"),
|
|
3334
|
+
onChange: (event) => onToggle(event.target.checked)
|
|
3335
|
+
}), (0, react_jsx_runtime.jsx)("span", { "aria-live": "polite", children: hasSelection ? t("archives.selectedCount", { n: selectedCount }) : t("archives.selectAllFiltered") })]
|
|
3336
|
+
}), hasSelection && (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionClear", disabled: busy, "aria-label": t("archives.clearSelection"), onClick: onClear, children: t("archives.clearSelectionShort") })]
|
|
3337
|
+
}), hasSelection && (0, react_jsx_runtime.jsxs)("div", {
|
|
3338
|
+
className: "dsham_settingsSelectionActions",
|
|
3339
|
+
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction", disabled: busy, "aria-label": t("archives.restoreSelected"), onClick: onRestore, children: t("archives.restoreSelectionShort") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction dsham_settingsSelectionDelete", disabled: busy, "aria-label": t("archives.deleteSelected"), onClick: onDelete, children: t("archives.deleteSelectionShort") })]
|
|
3340
|
+
})]
|
|
3341
|
+
}), hiddenCount > 0 && (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsSelectionScope", role: "status", children: t("archives.selectionHiddenHint", { hidden: hiddenCount }) })]
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3220
3344
|
function ArchiveSelectionCheckbox({ checked, indeterminate = false, disabled = false, label, onChange }) {
|
|
3221
3345
|
const inputRef = (0, react.useRef)(null);
|
|
3222
3346
|
(0, react.useEffect)(() => {
|
|
@@ -3355,172 +3479,17 @@
|
|
|
3355
3479
|
return compareText(left.title, right.title) || compareText(left.key, right.key);
|
|
3356
3480
|
});
|
|
3357
3481
|
}
|
|
3358
|
-
function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, t }) {
|
|
3359
|
-
const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
|
|
3360
|
-
const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
|
|
3361
|
-
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
3362
|
-
const [busy, setBusy] = (0, react.useState)(false);
|
|
3363
|
-
const [error, setError] = (0, react.useState)(null);
|
|
3364
|
-
const [notice, setNotice] = (0, react.useState)(null);
|
|
3365
|
-
const [query, setQuery] = (0, react.useState)("");
|
|
3366
|
-
const [project, setProject] = (0, react.useState)("all");
|
|
3367
|
-
const [sortBy, setSortBy] = (0, react.useState)("updated");
|
|
3368
|
-
const [createdAtById, setCreatedAtById] = (0, react.useState)({});
|
|
3369
|
-
const [unarchivingSessionIds, setUnarchivingSessionIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
3370
|
-
const unarchivingSessionIdsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
3371
|
-
const [selectedSessionIds, setSelectedSessionIds] = (0, react.useState)([]);
|
|
3372
|
-
const groups = (0, react.useMemo)(() => deriveArchivedGroups(sessions.byId, workspaceState.items, workspaceState.archivedSessionIds, t("group.ungrouped")), [sessions.byId, workspaceState, t]);
|
|
3373
|
-
const sortedGroups = (0, react.useMemo)(() => sortArchivedGroups(groups, sortBy, createdAtById, t), [groups, sortBy, createdAtById, t]);
|
|
3374
|
-
(0, react.useEffect)(() => {
|
|
3375
|
-
let cancelled = false;
|
|
3376
|
-
archivedSessionMetadata().then((result) => {
|
|
3377
|
-
if (cancelled) return;
|
|
3378
|
-
setCreatedAtById(Object.fromEntries(result.items.map((item) => [item.sessionId, item.createdAt])));
|
|
3379
|
-
}).catch(() => {
|
|
3380
|
-
if (!cancelled) setCreatedAtById({});
|
|
3381
|
-
});
|
|
3382
|
-
return () => {
|
|
3383
|
-
cancelled = true;
|
|
3384
|
-
};
|
|
3385
|
-
}, [archivedSessionMetadata, workspaceState.archivedSessionIds]);
|
|
3386
|
-
(0, react.useEffect)(() => {
|
|
3387
|
-
if (project === "all") return;
|
|
3388
|
-
if (groups.some((group) => group.key === project)) return;
|
|
3389
|
-
setProject("all");
|
|
3390
|
-
}, [groups, project]);
|
|
3391
|
-
const filteredGroups = (0, react.useMemo)(() => {
|
|
3392
|
-
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
3393
|
-
return sortedGroups.filter((group) => project === "all" || project === group.key).map((group) => ({
|
|
3394
|
-
...group,
|
|
3395
|
-
sessions: group.sessions.filter((session) => normalizedQuery === "" || displayTitle(session, t).toLocaleLowerCase().includes(normalizedQuery))
|
|
3396
|
-
})).filter((group) => group.sessions.length > 0);
|
|
3397
|
-
}, [sortedGroups, project, query, t]);
|
|
3398
|
-
const visibleSessionIds = (0, react.useMemo)(() => archivedSessionIdsInGroups(filteredGroups), [filteredGroups]);
|
|
3399
|
-
const selectedSessionIdSet = (0, react.useMemo)(() => new Set(selectedSessionIds), [selectedSessionIds]);
|
|
3400
|
-
const selectedVisibleCount = visibleSessionIds.filter((sessionId) => selectedSessionIdSet.has(sessionId)).length;
|
|
3401
|
-
const allVisibleSelected = visibleSessionIds.length > 0 && selectedVisibleCount === visibleSessionIds.length;
|
|
3402
|
-
const allBatchTarget = { scope: "all" };
|
|
3403
|
-
const allBatchSessionIds = (0, react.useMemo)(() => deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, allBatchTarget), [workspaceState.archivedSessionIds, workspaceState.items]);
|
|
3404
|
-
(0, react.useEffect)(() => {
|
|
3405
|
-
setSelectedSessionIds((current) => pruneArchivedSelection(current, workspaceState.archivedSessionIds));
|
|
3406
|
-
}, [workspaceState.archivedSessionIds]);
|
|
3407
|
-
const toggleSessionSelection = (sessionId, checked) => {
|
|
3408
|
-
setSelectedSessionIds((current) => toggleArchivedSelection(current, [sessionId], checked));
|
|
3409
|
-
};
|
|
3410
|
-
const toggleVisibleSelection = (checked) => {
|
|
3411
|
-
setSelectedSessionIds((current) => toggleArchivedSelection(current, visibleSessionIds, checked));
|
|
3412
|
-
};
|
|
3413
|
-
const onUnarchive = (sessionId) => {
|
|
3414
|
-
if (busy || unarchivingSessionIdsRef.current.has(sessionId)) return;
|
|
3415
|
-
unarchivingSessionIdsRef.current.add(sessionId);
|
|
3416
|
-
setUnarchivingSessionIds(new Set(unarchivingSessionIdsRef.current));
|
|
3417
|
-
setError(null);
|
|
3418
|
-
setNotice(null);
|
|
3419
|
-
unarchiveSession(sessionId).catch((reason) => {
|
|
3420
|
-
setError(formatUnarchiveError(reason, t));
|
|
3421
|
-
}).finally(() => {
|
|
3422
|
-
unarchivingSessionIdsRef.current.delete(sessionId);
|
|
3423
|
-
setUnarchivingSessionIds(new Set(unarchivingSessionIdsRef.current));
|
|
3424
|
-
});
|
|
3425
|
-
};
|
|
3426
|
-
const onBatchUnarchive = async (target) => {
|
|
3427
|
-
if (busy) return;
|
|
3428
|
-
setBusy(true);
|
|
3429
|
-
setError(null);
|
|
3430
|
-
setNotice(null);
|
|
3431
|
-
try {
|
|
3432
|
-
const result = await unarchiveSessions(target);
|
|
3433
|
-
setNotice(t("archives.restoreSuccess", { n: result.unarchivedSessionIds.length }));
|
|
3434
|
-
return result;
|
|
3435
|
-
} catch (reason) {
|
|
3436
|
-
setError(t("archives.restoreBatchFailed", { detail: reason instanceof Error ? reason.message : String(reason) }));
|
|
3437
|
-
} finally {
|
|
3438
|
-
setBusy(false);
|
|
3439
|
-
}
|
|
3440
|
-
};
|
|
3441
|
-
const onSelectedUnarchive = async () => {
|
|
3442
|
-
if (selectedSessionIds.length === 0) return;
|
|
3443
|
-
const result = await onBatchUnarchive({ scope: "sessions", sessionIds: selectedSessionIds });
|
|
3444
|
-
if (result !== void 0) {
|
|
3445
|
-
const restored = new Set(result.unarchivedSessionIds);
|
|
3446
|
-
setSelectedSessionIds((current) => current.filter((sessionId) => !restored.has(sessionId)));
|
|
3447
|
-
}
|
|
3448
|
-
};
|
|
3449
|
-
const closeDelete = () => {
|
|
3450
|
-
if (!busy) setDeleteTarget(null);
|
|
3451
|
-
};
|
|
3452
|
-
(0, react.useEffect)(() => {
|
|
3453
|
-
if (deleteTarget === null) return;
|
|
3454
|
-
const onKeyDown = (event) => {
|
|
3455
|
-
if (event.key !== "Escape") return;
|
|
3456
|
-
event.preventDefault();
|
|
3457
|
-
event.stopPropagation();
|
|
3458
|
-
if (typeof event.stopImmediatePropagation === "function") event.stopImmediatePropagation();
|
|
3459
|
-
if (!busy) setDeleteTarget(null);
|
|
3460
|
-
};
|
|
3461
|
-
window.addEventListener("keydown", onKeyDown, true);
|
|
3462
|
-
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
3463
|
-
}, [deleteTarget, busy]);
|
|
3464
|
-
const confirmDelete = async () => {
|
|
3465
|
-
if (busy || deleteTarget === null) return;
|
|
3466
|
-
setBusy(true);
|
|
3467
|
-
setError(null);
|
|
3468
|
-
setNotice(null);
|
|
3469
|
-
try {
|
|
3470
|
-
if (deleteTarget.kind === "batch") {
|
|
3471
|
-
const result = await deleteArchivedSessions(deleteTarget.target);
|
|
3472
|
-
const feedback = archivedDeleteFeedback(result, t);
|
|
3473
|
-
if (feedback.kind === "error") setError(feedback.message);
|
|
3474
|
-
else setNotice(feedback.message);
|
|
3475
|
-
if (deleteTarget.target.scope === "sessions") {
|
|
3476
|
-
const completed = /* @__PURE__ */ new Set([...result.deletedSessionIds, ...result.skippedSessionIds]);
|
|
3477
|
-
setSelectedSessionIds((current) => current.filter((sessionId) => !completed.has(sessionId)));
|
|
3478
|
-
}
|
|
3479
|
-
} else {
|
|
3480
|
-
await deleteSession(deleteTarget.session.id);
|
|
3481
|
-
}
|
|
3482
|
-
setDeleteTarget(null);
|
|
3483
|
-
} catch (reason) {
|
|
3484
|
-
setError(formatDeleteError(reason, t));
|
|
3485
|
-
} finally {
|
|
3486
|
-
setBusy(false);
|
|
3487
|
-
}
|
|
3488
|
-
};
|
|
3489
|
-
const batchScope = deleteTarget?.kind === "batch" ? deleteTarget.target.scope : null;
|
|
3490
|
-
const deleteDialogTitle = batchScope === "all" ? t("archives.deleteAllTitle") : batchScope === "ungrouped" ? t("archives.deleteUngroupedTitle") : batchScope === "workspace" ? t("archives.deleteProjectTitle", { name: deleteTarget.title }) : batchScope === "sessions" ? t("archives.deleteSelectedTitle") : t("deleteSession.title");
|
|
3491
|
-
const deleteDialogDescription = deleteTarget === null ? void 0 : batchScope === "all" ? t("archives.deleteAllDesc", { n: deleteTarget.count }) : batchScope === "ungrouped" ? t("archives.deleteUngroupedDesc", { n: deleteTarget.count }) : batchScope === "workspace" ? t("archives.deleteProjectDesc", { name: deleteTarget.title, n: deleteTarget.count }) : batchScope === "sessions" ? t("archives.deleteSelectedDesc", { n: deleteTarget.count }) : t("deleteSession.desc", { name: displayTitle(deleteTarget.session, t) });
|
|
3492
|
-
const deleteConfirmLabel = batchScope === "all" ? t("archives.deleteAll") : batchScope === "ungrouped" ? t("archives.deleteUngroupedConfirm") : batchScope === "workspace" ? t("archives.deleteProjectConfirm") : batchScope === "sessions" ? t("archives.deleteSelectedConfirm") : t("deleteSession.title");
|
|
3493
|
-
return (0, react_jsx_runtime.jsxs)("section", {
|
|
3494
|
-
className: "dsham_settings",
|
|
3495
|
-
"aria-label": t("archives.title"),
|
|
3496
|
-
children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsTitleRow", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsLinks", children: [(0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager", target: "_blank", rel: "noreferrer", "aria-label": t("archives.viewProject"), children: [(0, react_jsx_runtime.jsx)(GithubMark16, {}), t("archives.viewProject")] }), (0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager/issues", target: "_blank", rel: "noreferrer", "aria-label": t("archives.feedback"), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, {}), t("archives.feedback")] })] })] }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsHeaderActions", children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsRestoreAll", disabled: busy || allBatchSessionIds.length === 0, onClick: () => onBatchUnarchive(allBatchTarget), children: t("archives.restoreAll") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || allBatchSessionIds.length === 0, onClick: () => setDeleteTarget({ kind: "batch", target: allBatchTarget, title: t("archives.allProjects"), count: allBatchSessionIds.length }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })] })] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsToolbar", children: [(0, react_jsx_runtime.jsxs)("label", { className: "dsham_settingsSearch", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })] }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, { value: "created", label: t("archives.sortCreated") }, { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })] }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
|
|
3497
|
-
const target = archivedBatchTargetForGroup(group.key);
|
|
3498
|
-
const count = deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length;
|
|
3499
|
-
return (0, react_jsx_runtime.jsxs)("section", {
|
|
3500
|
-
className: "dsham_settingsGroup",
|
|
3501
|
-
children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsGroupHeading", children: [(0, react_jsx_runtime.jsxs)("h3", { className: "dsham_settingsGroupTitle", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, {}), group.title] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsGroupMeta", children: [(0, react_jsx_runtime.jsx)("span", { className: "dsham_settingsCount", children: t("archives.sessionCount", { n: count }) }), (0, react_jsx_runtime.jsx)(ArchivedGroupActions, { group, busy, onRestore: () => onBatchUnarchive(target), onDelete: () => setDeleteTarget({ kind: "batch", target, title: group.title, count }), t })] })] }), (0, react_jsx_runtime.jsx)("div", {
|
|
3502
|
-
className: "dsham_settingsList",
|
|
3503
|
-
children: group.sessions.map((session) => (0, react_jsx_runtime.jsxs)("article", {
|
|
3504
|
-
className: "dsham_settingsRow",
|
|
3505
|
-
children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsContent", children: [(0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsTitle", children: displayTitle(session, t) }), (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsMeta", children: archiveTimeLabel(session.updatedAt, t) })] }), (0, react_jsx_runtime.jsxs)("div", {
|
|
3506
|
-
className: "dsham_settingsActions",
|
|
3507
|
-
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => onUnarchive(session.id), children: t("menu.unarchive") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy || unarchivingSessionIds.has(session.id), "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget({ kind: "session", session }), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
|
|
3508
|
-
})]
|
|
3509
|
-
}, session.id))
|
|
3510
|
-
})]
|
|
3511
|
-
}, group.key);
|
|
3512
|
-
}), error !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }), notice !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsStatus", role: "status", children: notice }), (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
3513
|
-
open: deleteTarget !== null,
|
|
3514
|
-
onClose: closeDelete,
|
|
3515
|
-
closeLabel: t("close"),
|
|
3516
|
-
title: deleteDialogTitle,
|
|
3517
|
-
...deleteDialogDescription === void 0 ? {} : { description: deleteDialogDescription },
|
|
3518
|
-
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_settingsDeleteConfirm", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
|
|
3519
|
-
children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.kind === "batch" ? t("archives.deleteBatchPending") : t("deleteSession.pending") })
|
|
3520
|
-
})]
|
|
3521
|
-
});
|
|
3522
|
-
}
|
|
3523
3482
|
const zh = {
|
|
3483
|
+
"archives.viewConversation": "\u67E5\u770B\u5BF9\u8BDD",
|
|
3484
|
+
"archives.navigationUnavailable": "\u5F53\u524D\u5BBF\u4E3B\u65E0\u6CD5\u4FDD\u6301\u5F52\u6863\u5BF9\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u201C\u6062\u590D\u5E76\u6253\u5F00\u201D\u3002",
|
|
3485
|
+
"archives.sessionNotRetained": "\u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6216\u6062\u590D\u540E\u518D\u8BD5\u3002",
|
|
3486
|
+
"archives.restoreOpen": "\u6062\u590D\u5E76\u6253\u5F00",
|
|
3487
|
+
"archives.selectedCount": "\u5DF2\u9009 {n} \u6761",
|
|
3488
|
+
"archives.selectionHiddenHint": "\u542B\u5F53\u524D\u672A\u663E\u793A\u7684 {hidden} \u6761\uFF0C\u4E5F\u5C06\u53C2\u4E0E\u64CD\u4F5C",
|
|
3489
|
+
"archives.clearSelectionShort": "\u6E05\u7A7A",
|
|
3490
|
+
"archives.restoreSelectionShort": "\u6062\u590D",
|
|
3491
|
+
"archives.deleteSelectionShort": "\u5220\u9664",
|
|
3492
|
+
"archives.clearSelection": "\u6E05\u7A7A\u9009\u62E9",
|
|
3524
3493
|
"group.ungrouped": "\u672A\u5206\u7EC4",
|
|
3525
3494
|
"session.new": "\u65B0\u4F1A\u8BDD",
|
|
3526
3495
|
"section.workspaces": "\u5DE5\u4F5C\u533A",
|
|
@@ -3535,7 +3504,6 @@
|
|
|
3535
3504
|
"sessions.expand": "\u5C55\u5F00\u5176\u4F59 {n} \u4E2A\u4F1A\u8BDD",
|
|
3536
3505
|
"sessions.collapse": "\u6536\u8D77",
|
|
3537
3506
|
"empty.none": "\u6682\u65E0\u4F1A\u8BDD",
|
|
3538
|
-
"empty.noMatches": "\u65E0\u5339\u914D\u7ED3\u679C",
|
|
3539
3507
|
"workspace.add": "\u6DFB\u52A0\u5DE5\u4F5C\u533A",
|
|
3540
3508
|
"search.sessions.aria": "\u641C\u7D22\u4F1A\u8BDD",
|
|
3541
3509
|
"search.placeholder": "\u641C\u7D22\u4F1A\u8BDD\u2026",
|
|
@@ -3566,7 +3534,6 @@
|
|
|
3566
3534
|
"archives.sessionCount": "{n} \u4E2A\u804A\u5929",
|
|
3567
3535
|
"archives.selectAllFiltered": "\u5168\u9009\u5F53\u524D\u7B5B\u9009\u7ED3\u679C",
|
|
3568
3536
|
"archives.selectSession": "\u9009\u62E9\u4F1A\u8BDD\u201C{name}\u201D",
|
|
3569
|
-
"archives.selectedCount": "\u5DF2\u9009 {n} \u4E2A",
|
|
3570
3537
|
"archives.restoreSelected": "\u6062\u590D\u6240\u9009",
|
|
3571
3538
|
"archives.deleteSelected": "\u5220\u9664\u6240\u9009",
|
|
3572
3539
|
"archives.timestamp": "{date}\uFF0C{time}",
|
|
@@ -3585,7 +3552,6 @@
|
|
|
3585
3552
|
"archives.deleteSelectedTitle": "\u5220\u9664\u6240\u9009\u5DF2\u5F52\u6863\u804A\u5929",
|
|
3586
3553
|
"archives.deleteSelectedDesc": "\u5C06\u6C38\u4E45\u5220\u9664\u6240\u9009\u7684 {n} \u4E2A\u5DF2\u5F52\u6863\u804A\u5929\u53CA\u5176\u5B50\u4EE3\u7406\uFF08\u542B\u6B63\u5728\u8FD0\u884C\u7684\uFF09\u548C\u8BB0\u5F55\u3002\u5176\u4ED6\u804A\u5929\u4E0D\u4F1A\u53D7\u5F71\u54CD\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
3587
3554
|
"archives.deleteSelectedConfirm": "\u5220\u9664\u6240\u9009\u804A\u5929",
|
|
3588
|
-
"archives.deleteAllPending": "\u6B63\u5728\u5220\u9664\u5DF2\u5F52\u6863\u804A\u5929\u2026",
|
|
3589
3555
|
"archives.deleteProjectTitle": "\u5220\u9664\u201C{name}\u201D\u4E2D\u7684\u5DF2\u5F52\u6863\u804A\u5929",
|
|
3590
3556
|
"archives.deleteProjectDesc": "\u5C06\u6C38\u4E45\u5220\u9664\u201C{name}\u201D\u4E2D\u7684 {n} \u4E2A\u5DF2\u5F52\u6863\u804A\u5929\u53CA\u5176\u5B50\u4EE3\u7406\u548C\u8BB0\u5F55\u3002\u9879\u76EE\u76EE\u5F55\u548C\u672A\u5F52\u6863\u804A\u5929\u4E0D\u4F1A\u53D7\u5F71\u54CD\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
|
|
3591
3557
|
"archives.deleteProjectConfirm": "\u5220\u9664\u8BE5\u9879\u76EE\u7684\u5168\u90E8\u804A\u5929",
|
|
@@ -3626,8 +3592,6 @@
|
|
|
3626
3592
|
"archiveWorkspace.desc": "\u8FD9\u4F1A\u5C06\u201C{name}\u201D\u4E2D\u7684\u804A\u5929\u5F52\u6863\u3002\u4E4B\u540E\u4F60\u53EF\u4EE5\u5728\u5DF2\u5F52\u6863\u7684\u804A\u5929\u4E2D\u627E\u5230\u5B83\u4EEC\u3002",
|
|
3627
3593
|
"archiveWorkspace.confirm": "\u5168\u90E8\u5F52\u6863",
|
|
3628
3594
|
"archiveWorkspace.pending": "\u6B63\u5728\u5F52\u6863\u804A\u5929\u2026",
|
|
3629
|
-
"sessions.count.one": "{n} \u4E2A\u4F1A\u8BDD",
|
|
3630
|
-
"sessions.count.other": "{n} \u4E2A\u4F1A\u8BDD",
|
|
3631
3595
|
"actions.workspace.aria": "\u5DE5\u4F5C\u533A\u201C{name}\u201D\u7684\u64CD\u4F5C",
|
|
3632
3596
|
"actions.session.aria": "\u4F1A\u8BDD\u201C{name}\u201D\u7684\u64CD\u4F5C",
|
|
3633
3597
|
"actions.newSession.aria": "\u5728\u201C{name}\u201D\u4E2D\u65B0\u5EFA\u4F1A\u8BDD",
|
|
@@ -3651,6 +3615,16 @@
|
|
|
3651
3615
|
"time.ago": "{t}\u524D"
|
|
3652
3616
|
};
|
|
3653
3617
|
const en = {
|
|
3618
|
+
"archives.viewConversation": "View conversation",
|
|
3619
|
+
"archives.navigationUnavailable": "This host cannot keep the conversation archived while opening it. Use \u201CRestore and open\u201D instead.",
|
|
3620
|
+
"archives.sessionNotRetained": "The host did not keep the requested conversation open. Try again, or restore it before opening.",
|
|
3621
|
+
"archives.restoreOpen": "Restore and open",
|
|
3622
|
+
"archives.selectedCount": "{n} selected",
|
|
3623
|
+
"archives.selectionHiddenHint": "Includes {hidden} hidden by the current filter; actions apply to them too.",
|
|
3624
|
+
"archives.clearSelectionShort": "Clear",
|
|
3625
|
+
"archives.restoreSelectionShort": "Restore",
|
|
3626
|
+
"archives.deleteSelectionShort": "Delete",
|
|
3627
|
+
"archives.clearSelection": "Clear selection",
|
|
3654
3628
|
"group.ungrouped": "Ungrouped",
|
|
3655
3629
|
"session.new": "New Session",
|
|
3656
3630
|
"section.workspaces": "Workspaces",
|
|
@@ -3665,7 +3639,6 @@
|
|
|
3665
3639
|
"sessions.expand": "Show {n} more sessions",
|
|
3666
3640
|
"sessions.collapse": "Show less",
|
|
3667
3641
|
"empty.none": "No sessions yet",
|
|
3668
|
-
"empty.noMatches": "No matches",
|
|
3669
3642
|
"workspace.add": "Add workspace",
|
|
3670
3643
|
"search.sessions.aria": "Search sessions",
|
|
3671
3644
|
"search.placeholder": "Search sessions...",
|
|
@@ -3696,7 +3669,6 @@
|
|
|
3696
3669
|
"archives.sessionCount": "{n} chats",
|
|
3697
3670
|
"archives.selectAllFiltered": "Select all results",
|
|
3698
3671
|
"archives.selectSession": "Select chat {name}",
|
|
3699
|
-
"archives.selectedCount": "{n} selected",
|
|
3700
3672
|
"archives.restoreSelected": "Restore selected",
|
|
3701
3673
|
"archives.deleteSelected": "Delete selected",
|
|
3702
3674
|
"archives.timestamp": "{date}, {time}",
|
|
@@ -3715,7 +3687,6 @@
|
|
|
3715
3687
|
"archives.deleteSelectedTitle": "Delete selected archived chats",
|
|
3716
3688
|
"archives.deleteSelectedDesc": "This permanently deletes the selected {n} archived chats, their child agents (including any that are still running), and their records. Other chats are not affected. This cannot be undone.",
|
|
3717
3689
|
"archives.deleteSelectedConfirm": "Delete selected chats",
|
|
3718
|
-
"archives.deleteAllPending": "Deleting archived chats\u2026",
|
|
3719
3690
|
"archives.deleteProjectTitle": "Delete archived chats in {name}",
|
|
3720
3691
|
"archives.deleteProjectDesc": "This permanently deletes the {n} archived chats in {name}, their child agents, and their records. The project directory and unarchived chats are not affected. This cannot be undone.",
|
|
3721
3692
|
"archives.deleteProjectConfirm": "Delete all project chats",
|
|
@@ -3756,8 +3727,6 @@
|
|
|
3756
3727
|
"archiveWorkspace.desc": "This archives the chats in \u201C{name}\u201D. You can find them later in Archived chats.",
|
|
3757
3728
|
"archiveWorkspace.confirm": "Archive all",
|
|
3758
3729
|
"archiveWorkspace.pending": "Archiving chats\u2026",
|
|
3759
|
-
"sessions.count.one": "{n} session",
|
|
3760
|
-
"sessions.count.other": "{n} sessions",
|
|
3761
3730
|
"actions.workspace.aria": "Workspace actions for {name}",
|
|
3762
3731
|
"actions.session.aria": "Session actions for {name}",
|
|
3763
3732
|
"actions.newSession.aria": "New session in {name}",
|
|
@@ -3828,6 +3797,15 @@
|
|
|
3828
3797
|
createIcon: createPluginUpdateIcon
|
|
3829
3798
|
}), "dsh-archive-manager: plugin update ui");
|
|
3830
3799
|
const uiWorkspaceAt = () => ctx.get("uiWorkspace");
|
|
3800
|
+
const archiveViewState = {};
|
|
3801
|
+
let archiveNavigation;
|
|
3802
|
+
ctx.effect(() => {
|
|
3803
|
+
archiveNavigation = allowArchivedNavigation(uiWorkspaceAt(), ctx.sessions, ctx.workspaces, {
|
|
3804
|
+
onOpened: () => ctx.get("layout")?.selectPanel(null)
|
|
3805
|
+
});
|
|
3806
|
+
return () => archiveNavigation.dispose();
|
|
3807
|
+
}, "archive-manager: explicit archived navigation");
|
|
3808
|
+
const openConversation = (sessionId) => archiveNavigation.open(sessionId);
|
|
3831
3809
|
const searchSessions = async (query, signal) => {
|
|
3832
3810
|
const result = await ctx.sessions.search(query, signal);
|
|
3833
3811
|
if (!result.ok) throw new Error(result.error.message);
|
|
@@ -3970,11 +3948,16 @@
|
|
|
3970
3948
|
unarchiveSessions,
|
|
3971
3949
|
deleteArchivedSessions,
|
|
3972
3950
|
archivedSessionMetadata,
|
|
3951
|
+
openConversation,
|
|
3952
|
+
viewState: archiveViewState,
|
|
3973
3953
|
t: ctx.locale.bind(NS)
|
|
3974
3954
|
})
|
|
3975
|
-
},
|
|
3955
|
+
}, ArchivedSessionsSection));
|
|
3976
3956
|
}
|
|
3977
3957
|
exports.__test = {
|
|
3958
|
+
ArchiveSelectionToolbar,
|
|
3959
|
+
ArchivedSessionsSection,
|
|
3960
|
+
zh,
|
|
3978
3961
|
displayTitle,
|
|
3979
3962
|
sessionVisible,
|
|
3980
3963
|
indexSubagentDescendants,
|