@michengai/dsh-archive-manager 0.1.40 → 0.1.42
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 +26 -5
- package/README.zh-CN.md +26 -5
- package/lib/client.js +233 -31
- package/lib/workspace.js +51 -0
- 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.42 - 2026-09-14
|
|
8
|
+
|
|
9
|
+
- Add Archived / Unarchived tabs with Archived selected by default. Select sessions across projects for batch archiving, or archive all chats in a project or ungrouped section from its menu.
|
|
10
|
+
- Unify session action labels and make batch archive buttons and menu actions easier to identify. Keep the confirmation dialog open with an error on archive failure for direct retries, and clear stale feedback.
|
|
11
|
+
- Add the Typert `workspaceRegistry/archiveSessions` endpoint accepting `{ sessionIds: string[] }` to archive sessions across projects, with deduplication, idempotency, and whole-batch rejection for invalid sessions.
|
|
12
|
+
|
|
13
|
+
## 0.1.41 - 2026-09-14
|
|
14
|
+
|
|
15
|
+
- Improve the archived-session bulk action bar with grouped selection details and actions, fixing wrapped button labels in narrow content areas.
|
|
16
|
+
- Simplify the bar when nothing is selected and show cross-filter selection counts and scope separately so hidden selections remain clear.
|
|
17
|
+
|
|
7
18
|
## 0.1.40 - 2026-09-13
|
|
8
19
|
|
|
9
20
|
- Open archived sessions in the native DSH conversation view and continue chatting while keeping them archived, or restore and open them.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
以下发布说明会持续保留;新增版本时不再删除较早记录。
|
|
6
6
|
|
|
7
|
+
## 0.1.42 - 2026-09-14
|
|
8
|
+
|
|
9
|
+
- 新增「已归档 / 未归档」页签,默认展示已归档;支持跨项目选择会话批量归档,也可通过项目或未分组菜单归档该组全部聊天。
|
|
10
|
+
- 统一会话操作文案,提升批量归档按钮与菜单的辨识度;归档失败时保留确认框并显示错误,可直接重试,同时清理过期提示。
|
|
11
|
+
- 新增 Typert `workspaceRegistry/archiveSessions` 接口,接受 `{ sessionIds: string[] }` 批量归档跨项目会话,支持去重与幂等,无效会话导致整批拒绝。
|
|
12
|
+
|
|
13
|
+
## 0.1.41 - 2026-09-14
|
|
14
|
+
|
|
15
|
+
- 优化归档会话的批量操作栏:选择摘要与操作分组显示,修复窄内容区内按钮文字折行的问题。
|
|
16
|
+
- 未选择时简化操作栏;跨筛选选择的数量和影响范围独立显示,避免遗漏不可见的已选会话。
|
|
17
|
+
|
|
7
18
|
## 0.1.40 - 2026-09-13
|
|
8
19
|
|
|
9
20
|
- 支持直接打开归档会话,在 DSH 原生页面查看完整对话并保持归档继续聊天,也可恢复后打开。
|
package/README.md
CHANGED
|
@@ -76,16 +76,22 @@ Restart DSH Web, then hard-refresh your browser with `Ctrl+Shift+R`. Open **Sett
|
|
|
76
76
|
| Archive a workspace | Open the workspace menu and choose the option to archive its chats |
|
|
77
77
|
| Find an archive | Open **Settings → Archived sessions**, then search titles or filter by project |
|
|
78
78
|
| Change the order | Sort by update time, creation time, or title |
|
|
79
|
-
| Restore one chat | Click **
|
|
79
|
+
| Restore one chat | Click **Restore** beside the session |
|
|
80
|
+
| Archive a project or ungrouped chats | On **Unarchived**, open the group’s **…** menu and confirm archiving all chats in that group, regardless of search filters |
|
|
81
|
+
| Archive across projects | Switch to **Unarchived**, select sessions across projects, then click **Archive** and confirm |
|
|
80
82
|
| Restore or delete in bulk | Select chats and use the bulk actions, or use the project menu or page-wide actions |
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
The page opens on **Archived**, with **Unarchived** on the right. Switching tabs clears selections; changing search or project filters preserves them. Check the hidden selection count before applying bulk actions, or clear your selection first.
|
|
85
|
+
|
|
86
|
+
**Restore all / Delete all** remain on the Archived tab and apply to archived sessions across all projects, regardless of filters. Unarchived excludes subagents and blank placeholders and supports sorting by update time or title.
|
|
87
|
+
|
|
88
|
+
Developer API: Typert `workspaceRegistry/archiveSessions` accepts `{ sessionIds: string[] }` and returns `{ archivedSessionIds, archivedSessionIdsAdded }`. Call `registry.archiveSessions(ids)` and check the Typert `ok` result; the method uses the existing host authentication and RPC channel. The host validates and deduplicates the batch before one state write. Already archived IDs are idempotent; unknown sessions return `UNKNOWN_SESSION` without a partial archive.
|
|
83
89
|
|
|
84
90
|
### View and continue archived conversations
|
|
85
91
|
|
|
86
92
|
Available starting with `0.1.40`:
|
|
87
93
|
|
|
88
|
-
- **
|
|
94
|
+
- **Open session**: open the native DSH session to view messages, attachments, and tool details. Continue chatting while keeping the session archived.
|
|
89
95
|
- **Restore and open**: unarchive the session and open it to resume work.
|
|
90
96
|
|
|
91
97
|
## Updates
|
|
@@ -139,9 +145,24 @@ Restart DSH Web and hard-refresh your browser afterward. Edit [src](src), not th
|
|
|
139
145
|
|
|
140
146
|
</details>
|
|
141
147
|
|
|
142
|
-
##
|
|
148
|
+
## DSH product ecosystem
|
|
149
|
+
|
|
150
|
+
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.
|
|
151
|
+
|
|
152
|
+
| Plugin | What you can do |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | Organize projects and conversations, search tasks, and navigate chat turns |
|
|
155
|
+
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | Choose and summon specialists for your task |
|
|
156
|
+
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | Find, enable, create, and import local skills |
|
|
157
|
+
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | Search, restore, or clean up archived conversations |
|
|
158
|
+
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | Send tasks and receive replies through messaging platforms |
|
|
159
|
+
| [Automation](https://github.com/MichengAI/dsh-automation) | Schedule tasks and review each run |
|
|
160
|
+
| [BTW](https://github.com/MichengAI/dsh-btw) | Ask side questions without interrupting the main task |
|
|
161
|
+
| [Simplify](https://github.com/MichengAI/dsh-simplify) | Use `/simplify` to improve code within your Git changes |
|
|
162
|
+
| [PUA](https://github.com/MichengAI/dsh-pua) | Guide the Agent to try new approaches after failures, investigate causes, and verify results before completion |
|
|
163
|
+
| [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 |
|
|
164
|
+
| [Codex Pet](https://github.com/MichengAI/dsh-codex-pet) | View conversation notifications and respond to tool approvals and questions through a desktop pet |
|
|
143
165
|
|
|
144
|
-
[DSH Codex UI](https://github.com/MichengAI/dsh-codex-ui) provides project and conversation management. For a desktop workbench, see [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop).
|
|
145
166
|
|
|
146
167
|
## License
|
|
147
168
|
|
package/README.zh-CN.md
CHANGED
|
@@ -76,16 +76,22 @@ dsh plugin --profile web add @michengai/dsh-archive-manager@latest --registry=ht
|
|
|
76
76
|
| 归档整个工作区 | 打开工作区菜单,选择归档该工作区的会话 |
|
|
77
77
|
| 查找归档 | 打开「设置 → 归档会话」,搜索标题或按项目筛选 |
|
|
78
78
|
| 调整排列顺序 | 按更新时间、创建时间或标题排序 |
|
|
79
|
-
| 恢复一条会话 |
|
|
79
|
+
| 恢复一条会话 | 点击会话右侧的「恢复」 |
|
|
80
|
+
| 归档某个项目或未分组 | 在「未归档」页点击分组右侧“…”菜单,确认归档该组全部聊天;不受搜索筛选影响 |
|
|
81
|
+
| 跨项目批量归档 | 切换到「未归档」,跨项目勾选会话后点击「归档」并确认 |
|
|
80
82
|
| 批量恢复或删除 | 勾选会话后使用批量操作;也可使用项目菜单或页面顶部的全部操作 |
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
页面默认打开「已归档」,右侧「未归档」用于批量归档。切换 TAB 会清空选择;切换搜索或项目筛选会保留已选会话。批量操作前留意隐藏的已选数量,或先清空选择。
|
|
85
|
+
|
|
86
|
+
「全部恢复 / 全部删除」仅在已归档页显示,作用于所有项目的归档会话,不受筛选影响。未归档页排除子代理和空白占位会话,支持按更新时间或标题排序。
|
|
87
|
+
|
|
88
|
+
开发接口:Typert `workspaceRegistry/archiveSessions` 接收 `{ sessionIds: string[] }`,返回 `{ archivedSessionIds, archivedSessionIdsAdded }`。客户端调用 `registry.archiveSessions(ids)` 并检查 Typert 的 `ok` 结果;接口复用宿主连接的认证与调用通道。宿主先校验、去重,再一次写入;已归档项幂等跳过,未知会话返回 `UNKNOWN_SESSION` 且不部分归档。
|
|
83
89
|
|
|
84
90
|
### 查看并继续归档对话
|
|
85
91
|
|
|
86
92
|
从 `0.1.40` 起支持以下操作:
|
|
87
93
|
|
|
88
|
-
-
|
|
94
|
+
- **打开会话**:打开 DSH 原生会话页,查看消息、附件和工具详情;可直接继续聊天,保持归档状态。
|
|
89
95
|
- **恢复并打开**:取消归档后进入原会话,继续工作。
|
|
90
96
|
|
|
91
97
|
## 更新
|
|
@@ -139,9 +145,24 @@ dsh plugin --profile web add .
|
|
|
139
145
|
|
|
140
146
|
</details>
|
|
141
147
|
|
|
142
|
-
##
|
|
148
|
+
## DSH 产品生态
|
|
149
|
+
|
|
150
|
+
想使用桌面工作台,可下载 [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop/releases);已有 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 环境,可按各项目 README 按需安装。以下列出 11 个自研插件;桌面端实际随附范围以对应版本的发行说明和内置清单为准。
|
|
151
|
+
|
|
152
|
+
| 插件 | 你可以用它做什么 |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| [Codex UI](https://github.com/MichengAI/dsh-codex-ui) | 整理项目与会话、搜索任务、跳转对话轮次 |
|
|
155
|
+
| [Agency Agents](https://github.com/MichengAI/dsh-agency-agents) | 按任务选择并召唤专业角色 |
|
|
156
|
+
| [Skills Manager](https://github.com/MichengAI/dsh-skills-manager) | 统一查找、启停、创建和导入本机技能 |
|
|
157
|
+
| [Archive Manager](https://github.com/MichengAI/dsh-archive-manager) | 搜索、恢复或清理已归档会话 |
|
|
158
|
+
| [IM Connect](https://github.com/MichengAI/dsh-im-connect) | 从消息平台下任务、收回复 |
|
|
159
|
+
| [Automation](https://github.com/MichengAI/dsh-automation) | 按计划执行任务,查看每次运行的结果 |
|
|
160
|
+
| [BTW](https://github.com/MichengAI/dsh-btw) | 在当前上下文中临时旁问,不打断主任务 |
|
|
161
|
+
| [Simplify](https://github.com/MichengAI/dsh-simplify) | 用 `/simplify` 整理 Git 改动范围内的代码 |
|
|
162
|
+
| [PUA](https://github.com/MichengAI/dsh-pua) | 引导 Agent 在失败时换方法、查原因,并在完成前验证结果 |
|
|
163
|
+
| [Code Review](https://github.com/MichengAI/dsh-code-review) | 用 `/review` 发起独立 Agent 代码审查,在当前会话接收报告 |
|
|
164
|
+
| [Codex Pet](https://github.com/MichengAI/dsh-codex-pet) | 通过桌面宠物查看会话提醒、处理工具审批和问题回答 |
|
|
143
165
|
|
|
144
|
-
[DSH Codex UI](https://github.com/MichengAI/dsh-codex-ui) 提供项目与会话管理界面;想使用桌面工作台,可查看 [DSH Codex Desktop](https://github.com/MichengAI/dsh-codex-desktop)。
|
|
145
166
|
|
|
146
167
|
## 许可证
|
|
147
168
|
|
package/lib/client.js
CHANGED
|
@@ -505,6 +505,12 @@
|
|
|
505
505
|
return value;
|
|
506
506
|
}
|
|
507
507
|
};
|
|
508
|
+
const archiveSessionIdsSchema = {
|
|
509
|
+
parse(value) {
|
|
510
|
+
if (!Array.isArray(value)) throw new TypeError("sessionIds must be an array");
|
|
511
|
+
return [...new Set(value.map((id) => sessionIdSchema.parse(id)))];
|
|
512
|
+
}
|
|
513
|
+
};
|
|
508
514
|
const archivedWorkspaceBatchSchema = {
|
|
509
515
|
parse(value) {
|
|
510
516
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
|
|
@@ -591,6 +597,25 @@
|
|
|
591
597
|
},
|
|
592
598
|
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
593
599
|
},
|
|
600
|
+
{
|
|
601
|
+
id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveSessions",
|
|
602
|
+
service: "workspaceRegistry",
|
|
603
|
+
namespace: "workspaceRegistry",
|
|
604
|
+
method: "archiveSessions",
|
|
605
|
+
invocation: { kind: "direct" },
|
|
606
|
+
parameters: [{
|
|
607
|
+
name: "sessionIds",
|
|
608
|
+
wire: "sessionIds",
|
|
609
|
+
source: "json",
|
|
610
|
+
codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchiveSessionIds", schema: archiveSessionIdsSchema }
|
|
611
|
+
}],
|
|
612
|
+
result: {
|
|
613
|
+
mode: "strict",
|
|
614
|
+
typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
|
|
615
|
+
schema: archivedWorkspaceBatchSchema
|
|
616
|
+
},
|
|
617
|
+
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
618
|
+
},
|
|
594
619
|
{
|
|
595
620
|
id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveWorkspaceSessions",
|
|
596
621
|
service: "workspaceRegistry",
|
|
@@ -729,9 +754,27 @@
|
|
|
729
754
|
function GithubMark16() {
|
|
730
755
|
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" }) });
|
|
731
756
|
}
|
|
732
|
-
function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, viewState, close, t }) {
|
|
757
|
+
function ArchivedSessionsSection({ archiveSessions, sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, viewState, close, t }) {
|
|
733
758
|
const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
|
|
734
759
|
const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
|
|
760
|
+
const [archiveTab, setArchiveTab] = (0, react.useState)("archived");
|
|
761
|
+
const isArchived = archiveTab === "archived";
|
|
762
|
+
const [archiveTarget, setArchiveTarget] = (0, react.useState)(null);
|
|
763
|
+
const [archiveGroup, setArchiveGroup] = (0, react.useState)(null);
|
|
764
|
+
const requestArchive = (ids, group = null) => {
|
|
765
|
+
setError(null);
|
|
766
|
+
setNotice(null);
|
|
767
|
+
setArchiveGroup(group);
|
|
768
|
+
setArchiveTarget(ids);
|
|
769
|
+
};
|
|
770
|
+
const closeArchive = () => {
|
|
771
|
+
if (!busy) {
|
|
772
|
+
setArchiveTarget(null);
|
|
773
|
+
setArchiveGroup(null);
|
|
774
|
+
setError(null);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
const archiveBusy = (0, react.useRef)(false);
|
|
735
778
|
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
736
779
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
737
780
|
const [error, setError] = (0, react.useState)(null);
|
|
@@ -771,7 +814,39 @@
|
|
|
771
814
|
(0, react.useEffect)(() => {
|
|
772
815
|
if (viewState) Object.assign(viewState, { query, project, sortBy });
|
|
773
816
|
}, [query, project, sortBy, viewState]);
|
|
774
|
-
const
|
|
817
|
+
const eligibleIds = (0, react.useMemo)(() => isArchived ? workspaceState.archivedSessionIds : unarchivedSessionIds(sessions.byId, workspaceState.archivedSessionIds), [isArchived, sessions.byId, workspaceState.archivedSessionIds]);
|
|
818
|
+
const groups = (0, react.useMemo)(() => deriveArchivedGroups(sessions.byId, workspaceState.items, eligibleIds, t("group.ungrouped")), [sessions.byId, workspaceState, eligibleIds, t]);
|
|
819
|
+
const switchTab = (tab) => {
|
|
820
|
+
if (busy || unarchivingSessionIdsRef.current.size > 0) return;
|
|
821
|
+
setArchiveTab(tab);
|
|
822
|
+
setSelectedSessionIds([]);
|
|
823
|
+
setArchiveTarget(null);
|
|
824
|
+
setArchiveGroup(null);
|
|
825
|
+
setError(null);
|
|
826
|
+
setNotice(null);
|
|
827
|
+
setProject("all");
|
|
828
|
+
setQuery("");
|
|
829
|
+
if (tab === "unarchived" && sortBy === "created") setSortBy("updated");
|
|
830
|
+
};
|
|
831
|
+
const confirmArchive = async () => {
|
|
832
|
+
if (archiveBusy.current || busy || !archiveTarget?.length) return;
|
|
833
|
+
archiveBusy.current = true;
|
|
834
|
+
setBusy(true);
|
|
835
|
+
setError(null);
|
|
836
|
+
setNotice(null);
|
|
837
|
+
try {
|
|
838
|
+
const result = await archiveSessions(archiveTarget);
|
|
839
|
+
setSelectedSessionIds((ids) => ids.filter((id) => !result.archivedSessionIds.includes(id)));
|
|
840
|
+
setNotice(t("archives.archiveSuccess", { n: result.archivedSessionIdsAdded.length }));
|
|
841
|
+
setArchiveTarget(null);
|
|
842
|
+
setArchiveGroup(null);
|
|
843
|
+
} catch (reason) {
|
|
844
|
+
setError(t("archives.archiveBatchFailed", { detail: reason instanceof Error ? reason.message : String(reason) }));
|
|
845
|
+
} finally {
|
|
846
|
+
archiveBusy.current = false;
|
|
847
|
+
setBusy(false);
|
|
848
|
+
}
|
|
849
|
+
};
|
|
775
850
|
const sortedGroups = (0, react.useMemo)(() => sortArchivedGroups(groups, sortBy, createdAtById, t), [groups, sortBy, createdAtById, t]);
|
|
776
851
|
(0, react.useEffect)(() => {
|
|
777
852
|
let cancelled = false;
|
|
@@ -801,8 +876,8 @@
|
|
|
801
876
|
const allBatchTarget = { scope: "all" };
|
|
802
877
|
const allBatchSessionIds = (0, react.useMemo)(() => deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, allBatchTarget), [workspaceState.archivedSessionIds, workspaceState.items]);
|
|
803
878
|
(0, react.useEffect)(() => {
|
|
804
|
-
setSelectedSessionIds((current) => pruneArchivedSelection(current,
|
|
805
|
-
}, [
|
|
879
|
+
setSelectedSessionIds((current) => pruneArchivedSelection(current, eligibleIds));
|
|
880
|
+
}, [eligibleIds]);
|
|
806
881
|
const toggleSessionSelection = (sessionId, checked) => {
|
|
807
882
|
setSelectedSessionIds((current) => toggleArchivedSelection(current, [sessionId], checked));
|
|
808
883
|
};
|
|
@@ -892,46 +967,90 @@
|
|
|
892
967
|
return (0, react_jsx_runtime.jsxs)("section", {
|
|
893
968
|
className: "dsham_settings",
|
|
894
969
|
"aria-label": t("archives.title"),
|
|
895
|
-
children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS + ARCHIVE_SETTINGS_SELECTION_CSS + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS }), (0, react_jsx_runtime.jsxs)("
|
|
970
|
+
children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_TABS_CSS + ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS + ARCHIVE_SETTINGS_SELECTION_CSS + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS + ARCHIVE_WORKSPACE_CONFIRM_CSS }), (0, react_jsx_runtime.jsxs)("div", {
|
|
971
|
+
// 设置页使用独立布局容器,避免被 Codex UI 的会话 header 页签适配器识别。
|
|
896
972
|
className: "dsham_settingsHeader",
|
|
897
973
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
898
974
|
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") })]
|
|
899
|
-
}), (0, react_jsx_runtime.
|
|
975
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
976
|
+
className: "dsham_archiveTabs",
|
|
977
|
+
role: "tablist",
|
|
978
|
+
"aria-label": t("archives.title"),
|
|
979
|
+
children: ["archived", "unarchived"].map((tab) => (0, react_jsx_runtime.jsx)("button", {
|
|
980
|
+
type: "button",
|
|
981
|
+
role: "tab",
|
|
982
|
+
className: "dsham_archiveTab",
|
|
983
|
+
id: "dsham-tab-" + tab,
|
|
984
|
+
"aria-controls": "dsham-archive-panel",
|
|
985
|
+
"aria-selected": archiveTab === tab,
|
|
986
|
+
tabIndex: archiveTab === tab ? 0 : -1,
|
|
987
|
+
disabled: busy || unarchivingSessionIds.size > 0,
|
|
988
|
+
onClick: () => switchTab(tab),
|
|
989
|
+
onKeyDown: (event) => {
|
|
990
|
+
if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
|
|
991
|
+
event.preventDefault();
|
|
992
|
+
const next = event.key === "Home" ? "archived" : event.key === "End" ? "unarchived" : isArchived ? "unarchived" : "archived";
|
|
993
|
+
switchTab(next);
|
|
994
|
+
event.currentTarget.parentElement.querySelector("#dsham-tab-" + next)?.focus();
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
children: t("archives.tab." + tab)
|
|
998
|
+
}, tab))
|
|
999
|
+
}), isArchived && (0, react_jsx_runtime.jsxs)("div", {
|
|
900
1000
|
className: "dsham_settingsHeaderActions",
|
|
901
1001
|
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")] })]
|
|
902
1002
|
})]
|
|
903
|
-
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1003
|
+
}), (0, react_jsx_runtime.jsxs)("div", { id: "dsham-archive-panel", role: "tabpanel", "aria-labelledby": "dsham-tab-" + archiveTab, children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
904
1004
|
className: "dsham_settingsToolbar",
|
|
905
1005
|
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
906
1006
|
className: "dsham_settingsSearch",
|
|
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") })]
|
|
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") })]
|
|
909
|
-
}), groups.length > 0 && (0, react_jsx_runtime.
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1007
|
+
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(isArchived ? "archives.searchPlaceholder" : "archives.searchUnarchived"), "aria-label": t(isArchived ? "archives.searchPlaceholder" : "archives.searchUnarchived") })]
|
|
1008
|
+
}), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, ...isArchived ? [{ 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") })]
|
|
1009
|
+
}), groups.length > 0 && (0, react_jsx_runtime.jsx)(ArchiveSelectionToolbar, {
|
|
1010
|
+
selectedCount: selectedSessionIds.length,
|
|
1011
|
+
hiddenCount: selectedSessionIds.length - selectedVisibleCount,
|
|
1012
|
+
allVisibleSelected,
|
|
1013
|
+
selectedVisibleCount,
|
|
1014
|
+
visibleCount: visibleSessionIds.length,
|
|
1015
|
+
busy,
|
|
1016
|
+
t,
|
|
1017
|
+
onToggle: toggleVisibleSelection,
|
|
1018
|
+
onClear: () => setSelectedSessionIds([]),
|
|
1019
|
+
onArchive: isArchived ? void 0 : () => requestArchive([...selectedSessionIds]),
|
|
1020
|
+
onRestore: onSelectedUnarchive,
|
|
1021
|
+
onDelete: () => setDeleteTarget({ kind: "batch", target: { scope: "sessions", sessionIds: selectedSessionIds }, count: selectedSessionIds.length })
|
|
1022
|
+
}), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t(isArchived ? "archives.empty" : "archives.emptyUnarchived") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
|
|
916
1023
|
const target = archivedBatchTargetForGroup(group.key);
|
|
917
|
-
const
|
|
1024
|
+
const groupSessionIds = groups.find((item) => item.key === group.key)?.sessions.map((session) => session.id) ?? [];
|
|
1025
|
+
const count = isArchived ? deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length : groupSessionIds.length;
|
|
918
1026
|
return (0, react_jsx_runtime.jsxs)("section", {
|
|
919
1027
|
className: "dsham_settingsGroup",
|
|
920
1028
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
921
1029
|
className: "dsham_settingsGroupHeading",
|
|
922
|
-
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 })] })]
|
|
1030
|
+
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, onArchive: isArchived ? void 0 : () => requestArchive(groupSessionIds, group), onRestore: () => onBatchUnarchive(target), onDelete: () => setDeleteTarget({ kind: "batch", target, title: group.title, count }), t })] })]
|
|
923
1031
|
}), (0, react_jsx_runtime.jsx)("div", {
|
|
924
1032
|
className: "dsham_settingsList",
|
|
925
1033
|
children: group.sessions.map((session) => (0, react_jsx_runtime.jsxs)("article", {
|
|
926
1034
|
className: "dsham_settingsRow",
|
|
927
1035
|
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", {
|
|
928
1036
|
className: "dsham_settingsActions",
|
|
929
|
-
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session), children: t("archives.
|
|
1037
|
+
children: !isArchived ? [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy, onClick: () => viewConversation(session), children: t("archives.openSession") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy, onClick: () => requestArchive([session.id]), children: t("archives.archiveSelected") })] : [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session), children: t("archives.openSession") }), (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("archives.restore") }), (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, {}) })]
|
|
930
1038
|
})]
|
|
931
1039
|
}, session.id))
|
|
932
1040
|
})]
|
|
933
1041
|
}, group.key);
|
|
934
|
-
}), 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.
|
|
1042
|
+
}), archiveTarget === null && 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.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1043
|
+
open: archiveTarget !== null,
|
|
1044
|
+
onClose: closeArchive,
|
|
1045
|
+
closeLabel: t("close"),
|
|
1046
|
+
title: t("archives.archiveTitle", { n: archiveTarget?.length ?? 0 }),
|
|
1047
|
+
description: archiveGroup === null ? t("archives.archiveSelectedDesc") : t(archiveGroup.key === ARCHIVE_UNGROUPED_KEY ? "archives.archiveUngroupedDesc" : "archives.archiveProjectDesc", { name: archiveGroup.title, n: archiveTarget?.length ?? 0 }),
|
|
1048
|
+
footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1049
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeArchive, children: t("cancel") }),
|
|
1050
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_archiveWorkspaceConfirm", disabled: busy, onClick: confirmArchive, children: t("archives.archiveSelected") })
|
|
1051
|
+
] }),
|
|
1052
|
+
children: error !== null ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }) : busy ? t("archiveWorkspace.pending") : null
|
|
1053
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
935
1054
|
open: deleteTarget !== null,
|
|
936
1055
|
onClose: closeDelete,
|
|
937
1056
|
closeLabel: t("close"),
|
|
@@ -939,7 +1058,7 @@
|
|
|
939
1058
|
...deleteDialogDescription === void 0 ? {} : { description: deleteDialogDescription },
|
|
940
1059
|
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 })] }),
|
|
941
1060
|
children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.kind === "batch" ? t("archives.deleteBatchPending") : t("deleteSession.pending") })
|
|
942
|
-
})]
|
|
1061
|
+
})] })]
|
|
943
1062
|
});
|
|
944
1063
|
}
|
|
945
1064
|
function r(e) {
|
|
@@ -3164,12 +3283,13 @@
|
|
|
3164
3283
|
]
|
|
3165
3284
|
});
|
|
3166
3285
|
}
|
|
3286
|
+
const ARCHIVE_TABS_CSS = ".dsham_archiveTabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_archiveTab{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;padding:12px 2px;border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:600;cursor:pointer}.dsham_archiveTab[aria-selected=true]{border-bottom-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary)}.dsham_archiveTab:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_archiveTab:disabled{opacity:.5;cursor:not-allowed}@container(max-width:520px){.dsham_archiveTabs{gap:16px}}";
|
|
3167
3287
|
const ARCHIVE_SETTINGS_CSS = ".dsham_settings{container-type:inline-size;min-width:0;box-sizing:border-box;width:min(100%,760px);margin:0 auto;padding:0 0 32px;color:var(--dsw-alias-label-primary)}.dsham_settingsHeader{display:flex;flex-direction:column;align-items:stretch;gap:16px;margin-bottom:16px}.dsham_settings h2{margin:0;font-size:24px;line-height:32px;font-weight:600;letter-spacing:-.4px;white-space:nowrap}.dsham_settingsIntro{margin:12px 0 0;max-width:42em;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}.dsham_settingsDanger{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:0 12px;color:var(--dsw-alias-state-error-primary);background:transparent;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsDanger:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)}.dsham_settingsToolbar{display:flex;gap:8px;margin-bottom:16px}.dsham_settingsSearch{display:flex;align-items:center;gap:8px;min-width:0;flex:1;height:32px;padding:0 12px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px}.dsham_settingsSearch:focus-within{border-color:var(--dsw-alias-label-tertiary)}.dsham_settingsSearch input{width:100%;min-width:0;padding:0;color:var(--dsw-alias-label-primary);background:transparent;border:0;outline:0;font:inherit;font-size:12px}.dsham_settingsSearch input::placeholder{color:var(--dsw-alias-label-tertiary)}.dsham_settingsFilter{position:relative;min-width:168px;flex:none}.dsham_selectTrigger{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:32px;padding:0 10px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;line-height:20px;text-align:left}.dsham_selectTrigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectTrigger:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_selectTrigger[aria-expanded='true']{border-color:var(--dsw-alias-state-success-primary)}.dsham_selectValue{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsham_selectCaret{flex:none;width:12px;height:12px;color:var(--dsw-alias-label-tertiary)}.dsham_selectMenu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;box-sizing:border-box;min-width:100%;max-height:280px;overflow:auto;padding:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv3)}.dsham_selectOption{box-sizing:border-box;display:flex;align-items:center;width:100%;min-height:32px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;text-align:left;cursor:pointer}.dsham_selectOption:hover,.dsham_selectOption[data-active='true']{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectOption[aria-selected='true']{color:var(--dsw-alias-state-success-primary)}.dsham_settingsGroup{margin:0 0 20px}.dsham_settingsGroupHeading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.dsham_settingsGroupTitle{display:flex;align-items:center;gap:8px;min-width:0;margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.dsham_settingsGroupTitle svg{flex:none;color:var(--dsw-alias-label-secondary)}.dsham_settingsCount{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsham_settingsList{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-button-elevated-fill))}.dsham_settingsRow{display:flex;align-items:center;gap:12px;min-height:60px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsRow:last-child{border-bottom:0}.dsham_settingsContent{min-width:0;flex:1}.dsham_settingsTitle{overflow:hidden;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.dsham_settingsMeta{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.dsham_settingsActions{display:flex;align-items:center;gap:8px}.dsham_settingsAction{min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsAction:hover{filter:brightness(1.12)}.dsham_settingsDelete{display:flex;align-items:center;justify-content:center;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsEmpty{padding:28px 8px;color:var(--dsw-alias-label-secondary);text-align:center}.dsham_settingsError{margin-top:10px;color:var(--dsw-alias-state-error-primary);font-size:12px}@container(max-width:520px){.dsham_settingsHeader{margin-bottom:16px}.dsham_settingsToolbar{flex-wrap:wrap;margin-bottom:28px}.dsham_settingsSearch{flex-basis:100%}.dsham_settingsFilter{flex:1;min-width:0}.dsham_settingsGroup{margin-bottom:32px}.dsham_settingsRow{padding:10px 12px}.dsham_settingsActions{gap:4px}}";
|
|
3168
3288
|
const ARCHIVE_SETTINGS_BATCH_CSS = ".dsham_settingsHeaderActions,.dsham_settingsGroupMeta{display:flex;align-items:center;gap:8px;flex:none;flex-wrap:wrap}.dsham_settingsRestoreAll{display:inline-flex;align-items:center;min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsRestoreAll:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsRestoreAll:disabled,.dsham_settingsDanger:disabled,.dsham_settingsGroupMenu:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsGroupMenu{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsGroupMenu:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsStatus{margin-top:10px;color:var(--dsw-alias-label-secondary);font-size:12px}@media(max-width:720px){.dsham_settingsHeader{flex-direction:column}.dsham_settingsHeaderActions{align-self:flex-start}}";
|
|
3169
3289
|
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}}";
|
|
3170
3290
|
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}";
|
|
3171
3291
|
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}";
|
|
3172
|
-
const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{
|
|
3292
|
+
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_settingsSelectionArchive,.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}";
|
|
3173
3293
|
const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
|
|
3174
3294
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
|
|
3175
3295
|
const tag = document.createElement("style");
|
|
@@ -3309,6 +3429,32 @@
|
|
|
3309
3429
|
}) : null]
|
|
3310
3430
|
});
|
|
3311
3431
|
}
|
|
3432
|
+
function ArchiveSelectionToolbar({ selectedCount, hiddenCount, allVisibleSelected, selectedVisibleCount, visibleCount, busy, t, onToggle, onClear, onRestore, onDelete, onArchive }) {
|
|
3433
|
+
const hasSelection = selectedCount > 0;
|
|
3434
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
3435
|
+
className: "dsham_settingsSelection",
|
|
3436
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
3437
|
+
className: "dsham_settingsSelectionMain",
|
|
3438
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
3439
|
+
className: "dsham_settingsSelectionSummary",
|
|
3440
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
3441
|
+
className: "dsham_settingsSelectionToggle",
|
|
3442
|
+
title: t("archives.selectAllFiltered"),
|
|
3443
|
+
children: [(0, react_jsx_runtime.jsx)(ArchiveSelectionCheckbox, {
|
|
3444
|
+
checked: allVisibleSelected,
|
|
3445
|
+
indeterminate: selectedVisibleCount > 0 && !allVisibleSelected,
|
|
3446
|
+
disabled: busy || visibleCount === 0,
|
|
3447
|
+
label: t("archives.selectAllFiltered"),
|
|
3448
|
+
onChange: (event) => onToggle(event.target.checked)
|
|
3449
|
+
}), (0, react_jsx_runtime.jsx)("span", { "aria-live": "polite", children: hasSelection ? t("archives.selectedCount", { n: selectedCount }) : t("archives.selectAllFiltered") })]
|
|
3450
|
+
}), 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") })]
|
|
3451
|
+
}), hasSelection && (0, react_jsx_runtime.jsxs)("div", {
|
|
3452
|
+
className: "dsham_settingsSelectionActions",
|
|
3453
|
+
children: onArchive ? [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction dsham_settingsSelectionArchive", disabled: busy, onClick: onArchive, children: t("archives.archiveSelected") })] : [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction", disabled: busy, "aria-label": t("archives.restoreSelected"), onClick: onRestore, children: t("archives.restore") }), (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") })]
|
|
3454
|
+
})]
|
|
3455
|
+
}), hiddenCount > 0 && (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsSelectionScope", role: "status", children: t("archives.selectionHiddenHint", { hidden: hiddenCount }) })]
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3312
3458
|
function ArchiveSelectionCheckbox({ checked, indeterminate = false, disabled = false, label, onChange }) {
|
|
3313
3459
|
const inputRef = (0, react.useRef)(null);
|
|
3314
3460
|
(0, react.useEffect)(() => {
|
|
@@ -3324,10 +3470,15 @@
|
|
|
3324
3470
|
onChange
|
|
3325
3471
|
});
|
|
3326
3472
|
}
|
|
3327
|
-
function ArchivedGroupActions({ group, busy, onRestore, onDelete, t }) {
|
|
3473
|
+
function ArchivedGroupActions({ group, busy, onArchive, onRestore, onDelete, t }) {
|
|
3328
3474
|
const [open, setOpen] = (0, react.useState)(false);
|
|
3329
3475
|
const ungrouped = group.key === ARCHIVE_UNGROUPED_KEY;
|
|
3330
|
-
const items = [{
|
|
3476
|
+
const items = onArchive ? [{
|
|
3477
|
+
id: "archive",
|
|
3478
|
+
label: t(ungrouped ? "archives.archiveUngrouped" : "archives.archiveProject"),
|
|
3479
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }),
|
|
3480
|
+
danger: true
|
|
3481
|
+
}] : [{
|
|
3331
3482
|
id: "restore",
|
|
3332
3483
|
label: t(ungrouped ? "archives.restoreUngrouped" : "archives.restoreProject"),
|
|
3333
3484
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
|
|
@@ -3343,7 +3494,9 @@
|
|
|
3343
3494
|
items,
|
|
3344
3495
|
onSelect: (id) => {
|
|
3345
3496
|
setOpen(false);
|
|
3346
|
-
if (
|
|
3497
|
+
if (busy) return;
|
|
3498
|
+
if (id === "archive") onArchive?.();
|
|
3499
|
+
else if (id === "restore") onRestore();
|
|
3347
3500
|
else if (id === "delete") onDelete();
|
|
3348
3501
|
},
|
|
3349
3502
|
portal: true,
|
|
@@ -3407,6 +3560,10 @@
|
|
|
3407
3560
|
};
|
|
3408
3561
|
return { kind: "notice", message: t("archives.deleteSuccess", { n: deleted }) };
|
|
3409
3562
|
}
|
|
3563
|
+
function unarchivedSessionIds(byId, archivedSessionIds) {
|
|
3564
|
+
const archived = new Set(archivedSessionIds);
|
|
3565
|
+
return Object.values(byId).filter((session) => session.origin !== "subagent" && !session.blank && !archived.has(session.id)).map((session) => session.id);
|
|
3566
|
+
}
|
|
3410
3567
|
function deriveArchivedGroups(byId, items, archivedSessionIds, ungroupedLabel) {
|
|
3411
3568
|
const byWorkspace = items.map((workspace) => ({
|
|
3412
3569
|
key: workspace.workspaceId,
|
|
@@ -3448,11 +3605,28 @@
|
|
|
3448
3605
|
});
|
|
3449
3606
|
}
|
|
3450
3607
|
const zh = {
|
|
3451
|
-
"archives.
|
|
3608
|
+
"archives.archiveProject": "\u5F52\u6863\u8BE5\u9879\u76EE\u7684\u5168\u90E8\u804A\u5929",
|
|
3609
|
+
"archives.archiveUngrouped": "\u5F52\u6863\u5168\u90E8\u672A\u5206\u7EC4\u804A\u5929",
|
|
3610
|
+
"archives.archiveProjectDesc": "\u5C06\u201C{name}\u201D\u4E2D\u5168\u90E8 {n} \u6761\u672A\u5F52\u6863\u4F1A\u8BDD\u5F52\u6863\uFF0C\u4E0D\u53D7\u5F53\u524D\u641C\u7D22\u7B5B\u9009\u5F71\u54CD\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u6062\u590D\u3002",
|
|
3611
|
+
"archives.archiveUngroupedDesc": "\u5C06\u5168\u90E8 {n} \u6761\u672A\u5206\u7EC4\u7684\u672A\u5F52\u6863\u4F1A\u8BDD\u5F52\u6863\uFF0C\u4E0D\u53D7\u5F53\u524D\u641C\u7D22\u7B5B\u9009\u5F71\u54CD\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u6062\u590D\u3002",
|
|
3612
|
+
"archives.tab.archived": "\u5DF2\u5F52\u6863",
|
|
3613
|
+
"archives.tab.unarchived": "\u672A\u5F52\u6863",
|
|
3614
|
+
"archives.searchUnarchived": "\u641C\u7D22\u672A\u5F52\u6863\u804A\u5929",
|
|
3615
|
+
"archives.emptyUnarchived": "\u6682\u65E0\u672A\u5F52\u6863\u4F1A\u8BDD\u3002",
|
|
3616
|
+
"archives.archiveSelected": "\u5F52\u6863",
|
|
3617
|
+
"archives.openSession": "\u6253\u5F00\u4F1A\u8BDD",
|
|
3618
|
+
"archives.archiveSelectedDesc": "\u5C06\u9009\u4E2D\u7684\u4F1A\u8BDD\u5F52\u6863\uFF0C\u5305\u542B\u5F53\u524D\u7B5B\u9009\u9690\u85CF\u7684\u5DF2\u9009\u9879\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u67E5\u770B\u548C\u6062\u590D\u3002",
|
|
3619
|
+
"archives.archiveSuccess": "\u5DF2\u5F52\u6863 {n} \u6761\u4F1A\u8BDD\u3002",
|
|
3620
|
+
"archives.archiveBatchFailed": "\u6279\u91CF\u5F52\u6863\u5931\u8D25\uFF1A{detail}",
|
|
3452
3621
|
"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",
|
|
3453
3622
|
"archives.sessionNotRetained": "\u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6216\u6062\u590D\u540E\u518D\u8BD5\u3002",
|
|
3454
3623
|
"archives.restoreOpen": "\u6062\u590D\u5E76\u6253\u5F00",
|
|
3455
|
-
"archives.
|
|
3624
|
+
"archives.selectedCount": "\u5DF2\u9009 {n} \u6761",
|
|
3625
|
+
"archives.selectionHiddenHint": "\u542B\u5F53\u524D\u672A\u663E\u793A\u7684 {hidden} \u6761\uFF0C\u4E5F\u5C06\u53C2\u4E0E\u64CD\u4F5C",
|
|
3626
|
+
"archives.clearSelectionShort": "\u6E05\u7A7A",
|
|
3627
|
+
"archives.restore": "\u6062\u590D",
|
|
3628
|
+
"archives.archiveTitle": "\u5F52\u6863 {n} \u4E2A\u804A\u5929\uFF1F",
|
|
3629
|
+
"archives.deleteSelectionShort": "\u5220\u9664",
|
|
3456
3630
|
"archives.clearSelection": "\u6E05\u7A7A\u9009\u62E9",
|
|
3457
3631
|
"group.ungrouped": "\u672A\u5206\u7EC4",
|
|
3458
3632
|
"session.new": "\u65B0\u4F1A\u8BDD",
|
|
@@ -3483,7 +3657,7 @@
|
|
|
3483
3657
|
"archived.badge": "\u5F52\u6863\u4F1A\u8BDD",
|
|
3484
3658
|
"archived.notOpenable": "\u5DF2\u5F52\u6863\uFF0C\u53D6\u6D88\u5F52\u6863\u540E\u53EF\u7EE7\u7EED\u5BF9\u8BDD",
|
|
3485
3659
|
"archives.title": "\u5F52\u6863\u4F1A\u8BDD",
|
|
3486
|
-
"archives.description": "\u7BA1\u7406\u5DF2\u5F52\u6863\
|
|
3660
|
+
"archives.description": "\u7BA1\u7406\u5DF2\u5F52\u6863\u4F1A\u8BDD\uFF0C\u6216\u9009\u62E9\u672A\u5F52\u6863\u4F1A\u8BDD\u8FDB\u884C\u6279\u91CF\u5F52\u6863\u3002",
|
|
3487
3661
|
"archives.viewProject": "GitHub",
|
|
3488
3662
|
"archives.feedback": "\u95EE\u9898\u53CD\u9988",
|
|
3489
3663
|
"archives.empty": "\u6682\u65E0\u5DF2\u5F52\u6863\u4F1A\u8BDD",
|
|
@@ -3579,11 +3753,28 @@
|
|
|
3579
3753
|
"time.ago": "{t}\u524D"
|
|
3580
3754
|
};
|
|
3581
3755
|
const en = {
|
|
3582
|
-
"archives.
|
|
3756
|
+
"archives.archiveProject": "Archive all chats in this project",
|
|
3757
|
+
"archives.archiveUngrouped": "Archive all ungrouped chats",
|
|
3758
|
+
"archives.archiveProjectDesc": "Archive all {n} unarchived sessions in \u201C{name}\u201D, regardless of the current search filter. You can restore them in Archived.",
|
|
3759
|
+
"archives.archiveUngroupedDesc": "Archive all {n} ungrouped, unarchived sessions, regardless of the current search filter. You can restore them in Archived.",
|
|
3760
|
+
"archives.tab.archived": "Archived",
|
|
3761
|
+
"archives.tab.unarchived": "Unarchived",
|
|
3762
|
+
"archives.searchUnarchived": "Search unarchived chats",
|
|
3763
|
+
"archives.emptyUnarchived": "No unarchived sessions.",
|
|
3764
|
+
"archives.archiveSelected": "Archive",
|
|
3765
|
+
"archives.openSession": "Open session",
|
|
3766
|
+
"archives.archiveSelectedDesc": "Archive the selected sessions, including selections hidden by the current filter. You can view and restore them in Archived.",
|
|
3767
|
+
"archives.archiveSuccess": "Archived {n} sessions.",
|
|
3768
|
+
"archives.archiveBatchFailed": "Batch archive failed: {detail}",
|
|
3583
3769
|
"archives.navigationUnavailable": "This host cannot keep the conversation archived while opening it. Use \u201CRestore and open\u201D instead.",
|
|
3584
3770
|
"archives.sessionNotRetained": "The host did not keep the requested conversation open. Try again, or restore it before opening.",
|
|
3585
3771
|
"archives.restoreOpen": "Restore and open",
|
|
3586
|
-
"archives.
|
|
3772
|
+
"archives.selectedCount": "{n} selected",
|
|
3773
|
+
"archives.selectionHiddenHint": "Includes {hidden} hidden by the current filter; actions apply to them too.",
|
|
3774
|
+
"archives.clearSelectionShort": "Clear",
|
|
3775
|
+
"archives.restore": "Restore",
|
|
3776
|
+
"archives.archiveTitle": "Archive {n} chats?",
|
|
3777
|
+
"archives.deleteSelectionShort": "Delete",
|
|
3587
3778
|
"archives.clearSelection": "Clear selection",
|
|
3588
3779
|
"group.ungrouped": "Ungrouped",
|
|
3589
3780
|
"session.new": "New Session",
|
|
@@ -3614,7 +3805,7 @@
|
|
|
3614
3805
|
"archived.badge": "Archived sessions",
|
|
3615
3806
|
"archived.notOpenable": "This session is archived. Unarchive it to continue the conversation.",
|
|
3616
3807
|
"archives.title": "Archived sessions",
|
|
3617
|
-
"archives.description": "Manage archived sessions.",
|
|
3808
|
+
"archives.description": "Manage archived sessions or select unarchived sessions to archive in bulk.",
|
|
3618
3809
|
"archives.viewProject": "GitHub",
|
|
3619
3810
|
"archives.feedback": "Issues",
|
|
3620
3811
|
"archives.empty": "No archived sessions.",
|
|
@@ -3803,6 +3994,14 @@
|
|
|
3803
3994
|
if (!result.ok) throw new Error(result.error.message);
|
|
3804
3995
|
await refreshSessionList();
|
|
3805
3996
|
};
|
|
3997
|
+
const archiveSessions = async (sessionIds) => {
|
|
3998
|
+
const registry = ctx.get("remote.workspaceRegistry");
|
|
3999
|
+
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
4000
|
+
const result = await registry.archiveSessions(sessionIds);
|
|
4001
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
4002
|
+
await refreshSessionList();
|
|
4003
|
+
return result.value;
|
|
4004
|
+
};
|
|
3806
4005
|
const unarchiveSessions = async (target) => {
|
|
3807
4006
|
const registry = ctx.get("remote.workspaceRegistry");
|
|
3808
4007
|
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
@@ -3901,6 +4100,7 @@
|
|
|
3901
4100
|
icon: "archive",
|
|
3902
4101
|
locale: NS,
|
|
3903
4102
|
inject: () => ({
|
|
4103
|
+
archiveSessions,
|
|
3904
4104
|
sessionStore: bindObservable(ctx.sessions.list),
|
|
3905
4105
|
workspaceStore: bindObservable(ctx.workspaces.list),
|
|
3906
4106
|
unarchiveSession,
|
|
@@ -3915,6 +4115,8 @@
|
|
|
3915
4115
|
}, ArchivedSessionsSection));
|
|
3916
4116
|
}
|
|
3917
4117
|
exports.__test = {
|
|
4118
|
+
unarchivedSessionIds,
|
|
4119
|
+
ArchiveSelectionToolbar,
|
|
3918
4120
|
ArchivedSessionsSection,
|
|
3919
4121
|
zh,
|
|
3920
4122
|
displayTitle,
|
package/lib/workspace.js
CHANGED
|
@@ -118,6 +118,12 @@ const unarchivedBatchSchema = {
|
|
|
118
118
|
return value;
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
|
+
const archiveSessionIdsSchema = {
|
|
122
|
+
parse(value) {
|
|
123
|
+
if (!Array.isArray(value)) throw new TypeError("sessionIds must be an array");
|
|
124
|
+
return [...new Set(value.map((id) => sessionIdSchema.parse(id)))];
|
|
125
|
+
}
|
|
126
|
+
};
|
|
121
127
|
const archivedWorkspaceBatchSchema = {
|
|
122
128
|
parse(value) {
|
|
123
129
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
@@ -249,6 +255,35 @@ const ARCHIVE_MANAGER_INVOCATIONS = [
|
|
|
249
255
|
column: 1
|
|
250
256
|
}
|
|
251
257
|
},
|
|
258
|
+
{
|
|
259
|
+
id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveSessions",
|
|
260
|
+
service: "workspaceRegistry",
|
|
261
|
+
namespace: "workspaceRegistry",
|
|
262
|
+
method: "archiveSessions",
|
|
263
|
+
invocation: { kind: "direct" },
|
|
264
|
+
parameters: [
|
|
265
|
+
{
|
|
266
|
+
name: "sessionIds",
|
|
267
|
+
wire: "sessionIds",
|
|
268
|
+
source: "json",
|
|
269
|
+
codec: {
|
|
270
|
+
mode: "strict",
|
|
271
|
+
typeSymbol: "@michengai/dsh-archive-manager/types#ArchiveSessionIds",
|
|
272
|
+
schema: archiveSessionIdsSchema
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
result: {
|
|
277
|
+
mode: "strict",
|
|
278
|
+
typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
|
|
279
|
+
schema: archivedWorkspaceBatchSchema
|
|
280
|
+
},
|
|
281
|
+
sourceLocation: {
|
|
282
|
+
file: "@michengai/dsh-archive-manager/lib/workspace.js",
|
|
283
|
+
line: 1,
|
|
284
|
+
column: 1
|
|
285
|
+
}
|
|
286
|
+
},
|
|
252
287
|
{
|
|
253
288
|
id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveWorkspaceSessions",
|
|
254
289
|
service: "workspaceRegistry",
|
|
@@ -365,6 +400,7 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
365
400
|
markRemoteMethod(this, "deleteSession");
|
|
366
401
|
markRemoteMethod(this, "unarchiveSessions");
|
|
367
402
|
markRemoteMethod(this, "archiveWorkspaceSessions");
|
|
403
|
+
markRemoteMethod(this, "archiveSessions");
|
|
368
404
|
markRemoteMethod(this, "deleteArchivedSessions");
|
|
369
405
|
markRemoteMethod(this, "archivedSessionMetadata");
|
|
370
406
|
registerHostRemote(this.ctx);
|
|
@@ -494,6 +530,21 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
494
530
|
return { archivedSessionIds: [...next.archivedSessionIds] };
|
|
495
531
|
});
|
|
496
532
|
}
|
|
533
|
+
/** 按明确 ID 跨工作区归档;先校验整批,单次写入,重复归档不新增标记。 */
|
|
534
|
+
async archiveSessions(sessionIds) {
|
|
535
|
+
return this.enqueueOperation(async () => {
|
|
536
|
+
const ids = archiveSessionIdsSchema.parse(sessionIds);
|
|
537
|
+
const state = this.requireState();
|
|
538
|
+
const archived = new Set(state.archivedSessionIds);
|
|
539
|
+
const archivedSessionIdsAdded = ids.filter((id) => !archived.has(id));
|
|
540
|
+
for (const id of archivedSessionIdsAdded) {
|
|
541
|
+
if (!await this.sessionKnown(id)) throw new ArchiveUnknownSessionError(id);
|
|
542
|
+
}
|
|
543
|
+
const next = { ...state, archivedSessionIds: [...state.archivedSessionIds, ...archivedSessionIdsAdded] };
|
|
544
|
+
if (archivedSessionIdsAdded.length > 0) await this.setState(next);
|
|
545
|
+
return { archivedSessionIds: [...next.archivedSessionIds], archivedSessionIdsAdded };
|
|
546
|
+
});
|
|
547
|
+
}
|
|
497
548
|
/**
|
|
498
549
|
* 将一个工作区内所有会话加入归档集合。先确认所有待归档会话仍存在,
|
|
499
550
|
* 再执行单次状态写入,因此未知会话不会导致项目只归档一部分。
|