@michengai/dsh-archive-manager 0.1.6 → 0.1.7
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/lib/client.js +377 -266
- package/lib/projcache.js +5 -1
- package/lib/workspace.js +65 -5
- package/package.json +45 -45
package/lib/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
|
-
id: "@michengai/dsh-archive-manager",
|
|
2
|
+
id: "@michengai/dsh-archive-manager",
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
@@ -36,14 +36,14 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
|
-
* 客户端通过 `ctx.remote.$mount` 注册 workspaceRegistry 的远程方法。
|
|
40
|
-
* 调用走 typert gateway SRC 路径,不影响既有 `/api/workspace.*` 网关。
|
|
39
|
+
* 客户端通过 `ctx.remote.$mount` 注册 workspaceRegistry 的远程方法。
|
|
40
|
+
* 调用走 typert gateway SRC 路径,不影响既有 `/api/workspace.*` 网关。
|
|
41
41
|
*/
|
|
42
42
|
const ARCHIVE_MANAGER_REMOTE = {
|
|
43
|
-
package: "@michengai/dsh-archive-manager",
|
|
43
|
+
package: "@michengai/dsh-archive-manager",
|
|
44
44
|
descriptors: [
|
|
45
45
|
{
|
|
46
|
-
id: "@michengai/dsh-archive-manager#workspaceRegistry/unarchiveSession",
|
|
46
|
+
id: "@michengai/dsh-archive-manager#workspaceRegistry/unarchiveSession",
|
|
47
47
|
service: "workspaceRegistry",
|
|
48
48
|
namespace: "workspaceRegistry",
|
|
49
49
|
method: "unarchiveSession",
|
|
@@ -56,13 +56,13 @@ window.__ModuleLoader__.load({
|
|
|
56
56
|
}],
|
|
57
57
|
result: {
|
|
58
58
|
mode: "strict",
|
|
59
|
-
typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedSessionIds",
|
|
59
|
+
typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedSessionIds",
|
|
60
60
|
schema: archivedSetSchema
|
|
61
61
|
},
|
|
62
|
-
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
62
|
+
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
id: "@michengai/dsh-archive-manager#workspaceRegistry/deleteSession",
|
|
65
|
+
id: "@michengai/dsh-archive-manager#workspaceRegistry/deleteSession",
|
|
66
66
|
service: "workspaceRegistry",
|
|
67
67
|
namespace: "workspaceRegistry",
|
|
68
68
|
method: "deleteSession",
|
|
@@ -75,28 +75,28 @@ window.__ModuleLoader__.load({
|
|
|
75
75
|
}],
|
|
76
76
|
result: {
|
|
77
77
|
mode: "strict",
|
|
78
|
-
typeSymbol: "@michengai/dsh-archive-manager/types#Deleted",
|
|
78
|
+
typeSymbol: "@michengai/dsh-archive-manager/types#Deleted",
|
|
79
79
|
schema: deletedSchema
|
|
80
80
|
},
|
|
81
|
-
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
81
|
+
sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
|
|
82
82
|
}
|
|
83
83
|
]
|
|
84
84
|
};
|
|
85
85
|
/** Archived-session row presentation classes (theme-token driven). */
|
|
86
|
-
const ARCHIVED_CLASSES = {
|
|
87
|
-
row: "dsham_archivedRow",
|
|
88
|
-
title: "dsham_archivedTitle",
|
|
89
|
-
badge: "dsham_archivedBadge",
|
|
90
|
-
content: "dsham_archiveCardContent",
|
|
91
|
-
meta: "dsham_archiveCardMeta",
|
|
92
|
-
actions: "dsham_archiveCardActions",
|
|
93
|
-
unarchive: "dsham_archiveCardUnarchive",
|
|
94
|
-
delete: "dsham_archiveCardDelete"
|
|
95
|
-
};
|
|
96
|
-
const ARCHIVED_CSS = ".YDXeBa_sessionRow.dsham_archivedRow{box-sizing:border-box;cursor:default;min-height:64px;height:auto;background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;gap:12px;margin:8px 0;padding:10px 16px}.YDXeBa_sessionRow.dsham_archivedRow:hover{background:var(--dsw-alias-button-elevated-fill);border-color:var(--dsw-alias-border-l3)}.YDXeBa_searchResultRow.dsham_archivedRow{background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;margin:8px 0;padding:10px 16px}.dsham_archivedTitle{color:var(--dsw-alias-label-primary);font-weight:600}.dsham_archivedBadge{display:none}.dsham_archiveCardContent{min-width:0;flex:1;flex-direction:column;gap:2px;display:flex}.dsham_archiveCardMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.YDXeBa_sessionRow.dsham_archivedRow>.YDXeBa_time,.YDXeBa_sessionRow.dsham_archivedRow>.YDXeBa_rowActions{display:none}.dsham_archiveCardActions{align-items:center;gap:12px;display:inline-flex}.dsham_archiveCardActions button{cursor:pointer;border:none;flex:none}.dsham_archiveCardDelete{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border-radius:8px;align-items:center;justify-content:center;display:inline-flex}.dsham_archiveCardDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_archiveCardUnarchive{height:32px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2)!important;border-radius:10px;padding:0 12px;font-size:13px;font-weight:600;line-height:20px}.dsham_archiveCardUnarchive:hover{background:var(--dsw-alias-interactive-bg-hover)}";
|
|
86
|
+
const ARCHIVED_CLASSES = {
|
|
87
|
+
row: "dsham_archivedRow",
|
|
88
|
+
title: "dsham_archivedTitle",
|
|
89
|
+
badge: "dsham_archivedBadge",
|
|
90
|
+
content: "dsham_archiveCardContent",
|
|
91
|
+
meta: "dsham_archiveCardMeta",
|
|
92
|
+
actions: "dsham_archiveCardActions",
|
|
93
|
+
unarchive: "dsham_archiveCardUnarchive",
|
|
94
|
+
delete: "dsham_archiveCardDelete"
|
|
95
|
+
};
|
|
96
|
+
const ARCHIVED_CSS = ".YDXeBa_sessionRow.dsham_archivedRow{box-sizing:border-box;cursor:default;min-height:64px;height:auto;background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;gap:12px;margin:8px 0;padding:10px 16px}.YDXeBa_sessionRow.dsham_archivedRow:hover{background:var(--dsw-alias-button-elevated-fill);border-color:var(--dsw-alias-border-l3)}.YDXeBa_searchResultRow.dsham_archivedRow{background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;margin:8px 0;padding:10px 16px}.dsham_archivedTitle{color:var(--dsw-alias-label-primary);font-weight:600}.dsham_archivedBadge{display:none}.dsham_archiveCardContent{min-width:0;flex:1;flex-direction:column;gap:2px;display:flex}.dsham_archiveCardMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.YDXeBa_sessionRow.dsham_archivedRow>.YDXeBa_time,.YDXeBa_sessionRow.dsham_archivedRow>.YDXeBa_rowActions{display:none}.dsham_archiveCardActions{align-items:center;gap:12px;display:inline-flex}.dsham_archiveCardActions button{cursor:pointer;border:none;flex:none}.dsham_archiveCardDelete{width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border-radius:8px;align-items:center;justify-content:center;display:inline-flex}.dsham_archiveCardDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_archiveCardUnarchive{height:32px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-button-elevated-fill);border:1px solid var(--dsw-alias-border-l2)!important;border-radius:10px;padding:0 12px;font-size:13px;font-weight:600;line-height:20px}.dsham_archiveCardUnarchive:hover{background:var(--dsw-alias-interactive-bg-hover)}";
|
|
97
97
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/Archived.module.css") + "]") === null) {
|
|
98
98
|
const tag = document.createElement("style");
|
|
99
|
-
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
99
|
+
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
100
100
|
tag.dataset.pluginCss = "dsh-archive-manager/Archived.module.css";
|
|
101
101
|
tag.textContent = ARCHIVED_CSS;
|
|
102
102
|
document.head.appendChild(tag);
|
|
@@ -200,6 +200,83 @@ window.__ModuleLoader__.load({
|
|
|
200
200
|
return session.origin !== "subagent" && (!archived.has(session.id) || showArchived === true) && (!session.blank || session.id === current);
|
|
201
201
|
}
|
|
202
202
|
/**
|
|
203
|
+
* 单个删除按钮的目标:先子代理(含多层),再本会话。fork 分支不是 subagent,不会被收进来。
|
|
204
|
+
*/
|
|
205
|
+
function collectSessionAndDescendantIds(sessionId, byId) {
|
|
206
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) return [];
|
|
207
|
+
const catalog = byId ?? {};
|
|
208
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const session of Object.values(catalog)) {
|
|
210
|
+
if (session === void 0 || session.origin !== "subagent" || typeof session.id !== "string" || session.id.length === 0) continue;
|
|
211
|
+
const parentId = session.parentId;
|
|
212
|
+
if (typeof parentId !== "string") continue;
|
|
213
|
+
const list = childrenByParent.get(parentId) ?? [];
|
|
214
|
+
list.push(session.id);
|
|
215
|
+
childrenByParent.set(parentId, list);
|
|
216
|
+
}
|
|
217
|
+
const ordered = [];
|
|
218
|
+
const seen = /* @__PURE__ */ new Set();
|
|
219
|
+
const visit = (id) => {
|
|
220
|
+
for (const childId of childrenByParent.get(id) ?? []) {
|
|
221
|
+
if (seen.has(childId)) continue;
|
|
222
|
+
seen.add(childId);
|
|
223
|
+
visit(childId);
|
|
224
|
+
ordered.push(childId);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
visit(sessionId);
|
|
228
|
+
if (!seen.has(sessionId)) ordered.push(sessionId);
|
|
229
|
+
return ordered;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 归档设置页“删除全部”的目标:全部已归档 id,加上这些父会话下仍在目录中的 subagent 子会话。
|
|
233
|
+
* 每棵树都是先子后父,避免级联删除后再删子会话时被当成未知。
|
|
234
|
+
*/
|
|
235
|
+
function collectArchivedDeleteAllIds(archivedSessionIds, byId) {
|
|
236
|
+
const catalog = byId ?? {};
|
|
237
|
+
const ids = [];
|
|
238
|
+
const seenArchived = /* @__PURE__ */ new Set();
|
|
239
|
+
for (const id of archivedSessionIds ?? []) {
|
|
240
|
+
if (typeof id !== "string" || id.length === 0 || seenArchived.has(id)) continue;
|
|
241
|
+
seenArchived.add(id);
|
|
242
|
+
ids.push(id);
|
|
243
|
+
}
|
|
244
|
+
const result = [];
|
|
245
|
+
const seen = /* @__PURE__ */ new Set();
|
|
246
|
+
const addTree = (id) => {
|
|
247
|
+
for (const item of collectSessionAndDescendantIds(id, catalog)) {
|
|
248
|
+
if (seen.has(item)) continue;
|
|
249
|
+
seen.add(item);
|
|
250
|
+
result.push(item);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
for (const id of ids) {
|
|
254
|
+
if (catalog[id]?.origin === "subagent") continue;
|
|
255
|
+
addTree(id);
|
|
256
|
+
}
|
|
257
|
+
for (const id of ids) {
|
|
258
|
+
if (seen.has(id)) continue;
|
|
259
|
+
addTree(id);
|
|
260
|
+
}
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
/** 识别删除路径上“会话已不存在”的稳定标记;保留旧文案作兜底。 */
|
|
264
|
+
function isUnknownSessionError(reason) {
|
|
265
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
266
|
+
return message.includes("UNKNOWN_SESSION") || message.includes("no such session");
|
|
267
|
+
}
|
|
268
|
+
/** 把删除失败转成当前语言的用户文案。 */
|
|
269
|
+
function formatDeleteError(reason, t) {
|
|
270
|
+
if (isUnknownSessionError(reason)) return t("deleteSession.unknown");
|
|
271
|
+
const detail = reason instanceof Error ? reason.message : String(reason);
|
|
272
|
+
return t("deleteSession.failed", { detail });
|
|
273
|
+
}
|
|
274
|
+
function formatUnarchiveError(reason, t) {
|
|
275
|
+
if (isUnknownSessionError(reason)) return t("archives.unarchiveUnknown");
|
|
276
|
+
const detail = reason instanceof Error ? reason.message : String(reason);
|
|
277
|
+
return t("archives.unarchiveFailed", { detail });
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
203
280
|
* A blank session is the selected Workspace's provisional New Session row;
|
|
204
281
|
* its canonical title never enters search (blank rows are query-excluded)
|
|
205
282
|
* and the renderer localizes its display label.
|
|
@@ -442,7 +519,7 @@ window.__ModuleLoader__.load({
|
|
|
442
519
|
const tagId$2 = "@deepseek-ai/dsh-client-ui-workspace/Rows.module.css";
|
|
443
520
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
444
521
|
const tag = document.createElement("style");
|
|
445
|
-
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
522
|
+
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
446
523
|
tag.dataset.pluginCss = tagId$2;
|
|
447
524
|
tag.textContent = css$2;
|
|
448
525
|
document.head.appendChild(tag);
|
|
@@ -492,21 +569,21 @@ window.__ModuleLoader__.load({
|
|
|
492
569
|
* except workspace Rename/Delete and session Rename/Fork/Archive; the session
|
|
493
570
|
* and workspace hover cards are suppressed while a menu is open.
|
|
494
571
|
*/
|
|
495
|
-
/** Row display title: blank rows show the localized New Session label. */
|
|
496
|
-
function displayTitle(node, t) {
|
|
497
|
-
return node.blank ? t("session.new") : node.title ?? node.displayTitle ?? "";
|
|
572
|
+
/** Row display title: blank rows show the localized New Session label. */
|
|
573
|
+
function displayTitle(node, t) {
|
|
574
|
+
return node.blank ? t("session.new") : node.title ?? node.displayTitle ?? "";
|
|
498
575
|
}
|
|
499
576
|
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
500
|
-
function timeLabel(updatedAt, now, t) {
|
|
501
|
-
const { unit, n } = relativeTime(updatedAt, now);
|
|
502
|
-
return unit === "now" ? t("time.now") : t(`time.${unit}`, { n });
|
|
503
|
-
}
|
|
504
|
-
/** 归档页使用绝对时间,方便在历史记录中准确辨识会话。 */
|
|
505
|
-
function archiveTimeLabel(updatedAt, t) {
|
|
506
|
-
const date = new Date(updatedAt);
|
|
507
|
-
const time = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
508
|
-
return t("archives.timestamp", { date: t("date.ymd", { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate() }), time });
|
|
509
|
-
}
|
|
577
|
+
function timeLabel(updatedAt, now, t) {
|
|
578
|
+
const { unit, n } = relativeTime(updatedAt, now);
|
|
579
|
+
return unit === "now" ? t("time.now") : t(`time.${unit}`, { n });
|
|
580
|
+
}
|
|
581
|
+
/** 归档页使用绝对时间,方便在历史记录中准确辨识会话。 */
|
|
582
|
+
function archiveTimeLabel(updatedAt, t) {
|
|
583
|
+
const date = new Date(updatedAt);
|
|
584
|
+
const time = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
585
|
+
return t("archives.timestamp", { date: t("date.ymd", { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate() }), time });
|
|
586
|
+
}
|
|
510
587
|
/** Hover-card variant: distances wrap in the ago template; the now bucket stays bare (no "now ago"). */
|
|
511
588
|
function hoverTimeLabel(updatedAt, now, t) {
|
|
512
589
|
const { unit, n } = relativeTime(updatedAt, now);
|
|
@@ -890,42 +967,42 @@ window.__ModuleLoader__.load({
|
|
|
890
967
|
className: ARCHIVED_CLASSES.badge,
|
|
891
968
|
children: t("archived.badge")
|
|
892
969
|
}),
|
|
893
|
-
archived ? (0, react_jsx_runtime.jsxs)("span", {
|
|
894
|
-
className: ARCHIVED_CLASSES.content,
|
|
895
|
-
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
896
|
-
className: clsx(Rows_module_css_default.title, ARCHIVED_CLASSES.title),
|
|
897
|
-
children: title
|
|
898
|
-
}), !row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
899
|
-
className: ARCHIVED_CLASSES.meta,
|
|
900
|
-
children: timeLabel(row.updatedAt, now, t)
|
|
901
|
-
})]
|
|
902
|
-
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
903
|
-
className: Rows_module_css_default.title,
|
|
904
|
-
children: title
|
|
905
|
-
}),
|
|
906
|
-
!row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
907
|
-
className: Rows_module_css_default.time,
|
|
908
|
-
children: timeLabel(row.updatedAt, now, t)
|
|
909
|
-
}),
|
|
910
|
-
archived && !row.blank && (0, react_jsx_runtime.jsxs)("span", {
|
|
911
|
-
className: ARCHIVED_CLASSES.actions,
|
|
912
|
-
onClick: (e) => {
|
|
913
|
-
e.stopPropagation();
|
|
914
|
-
},
|
|
915
|
-
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
916
|
-
type: "button",
|
|
917
|
-
className: ARCHIVED_CLASSES.delete,
|
|
918
|
-
"aria-label": t("menu.deleteSession"),
|
|
919
|
-
onClick: () => onDeleteSession(node.id, row.title),
|
|
920
|
-
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
|
|
921
|
-
}), (0, react_jsx_runtime.jsx)("button", {
|
|
922
|
-
type: "button",
|
|
923
|
-
className: ARCHIVED_CLASSES.unarchive,
|
|
924
|
-
onClick: () => onUnarchive(node.id),
|
|
925
|
-
children: t("menu.unarchive")
|
|
926
|
-
})]
|
|
927
|
-
}),
|
|
928
|
-
!row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
970
|
+
archived ? (0, react_jsx_runtime.jsxs)("span", {
|
|
971
|
+
className: ARCHIVED_CLASSES.content,
|
|
972
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
973
|
+
className: clsx(Rows_module_css_default.title, ARCHIVED_CLASSES.title),
|
|
974
|
+
children: title
|
|
975
|
+
}), !row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
976
|
+
className: ARCHIVED_CLASSES.meta,
|
|
977
|
+
children: timeLabel(row.updatedAt, now, t)
|
|
978
|
+
})]
|
|
979
|
+
}) : (0, react_jsx_runtime.jsx)("span", {
|
|
980
|
+
className: Rows_module_css_default.title,
|
|
981
|
+
children: title
|
|
982
|
+
}),
|
|
983
|
+
!row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
984
|
+
className: Rows_module_css_default.time,
|
|
985
|
+
children: timeLabel(row.updatedAt, now, t)
|
|
986
|
+
}),
|
|
987
|
+
archived && !row.blank && (0, react_jsx_runtime.jsxs)("span", {
|
|
988
|
+
className: ARCHIVED_CLASSES.actions,
|
|
989
|
+
onClick: (e) => {
|
|
990
|
+
e.stopPropagation();
|
|
991
|
+
},
|
|
992
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
993
|
+
type: "button",
|
|
994
|
+
className: ARCHIVED_CLASSES.delete,
|
|
995
|
+
"aria-label": t("menu.deleteSession"),
|
|
996
|
+
onClick: () => onDeleteSession(node.id, row.title),
|
|
997
|
+
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {})
|
|
998
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
999
|
+
type: "button",
|
|
1000
|
+
className: ARCHIVED_CLASSES.unarchive,
|
|
1001
|
+
onClick: () => onUnarchive(node.id),
|
|
1002
|
+
children: t("menu.unarchive")
|
|
1003
|
+
})]
|
|
1004
|
+
}),
|
|
1005
|
+
!row.blank && (0, react_jsx_runtime.jsx)("span", {
|
|
929
1006
|
className: Rows_module_css_default.rowActions,
|
|
930
1007
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
931
1008
|
open: menuOpen,
|
|
@@ -974,7 +1051,7 @@ window.__ModuleLoader__.load({
|
|
|
974
1051
|
const tagId$1 = "@deepseek-ai/dsh-client-ui-workspace/WorkspacePicker.module.css";
|
|
975
1052
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
976
1053
|
const tag = document.createElement("style");
|
|
977
|
-
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
1054
|
+
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
978
1055
|
tag.dataset.pluginCss = tagId$1;
|
|
979
1056
|
tag.textContent = css$1;
|
|
980
1057
|
document.head.appendChild(tag);
|
|
@@ -1144,7 +1221,7 @@ window.__ModuleLoader__.load({
|
|
|
1144
1221
|
const tagId = "@deepseek-ai/dsh-client-ui-workspace/WorkspaceBrowser.module.css";
|
|
1145
1222
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1146
1223
|
const tag = document.createElement("style");
|
|
1147
|
-
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
1224
|
+
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
1148
1225
|
tag.dataset.pluginCss = tagId;
|
|
1149
1226
|
tag.textContent = css;
|
|
1150
1227
|
document.head.appendChild(tag);
|
|
@@ -1300,8 +1377,8 @@ window.__ModuleLoader__.load({
|
|
|
1300
1377
|
changed: orderChanged || timestampsChanged
|
|
1301
1378
|
};
|
|
1302
1379
|
}
|
|
1303
|
-
/** Grouping and ordering controls for the workspace browser. */
|
|
1304
|
-
function ViewOptionsMenu({ groupBy, orderBy, onGroupPick, onOrderPick, t }) {
|
|
1380
|
+
/** Grouping and ordering controls for the workspace browser. */
|
|
1381
|
+
function ViewOptionsMenu({ groupBy, orderBy, onGroupPick, onOrderPick, t }) {
|
|
1305
1382
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1306
1383
|
return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
1307
1384
|
open,
|
|
@@ -1339,12 +1416,12 @@ window.__ModuleLoader__.load({
|
|
|
1339
1416
|
id: "updated",
|
|
1340
1417
|
label: t("orderBy.updated")
|
|
1341
1418
|
},
|
|
1342
|
-
],
|
|
1343
|
-
selectedIds: [groupBy, orderBy],
|
|
1344
|
-
onSelect: (id) => {
|
|
1345
|
-
if (id === "workspace" || id === "flat") onGroupPick(id);
|
|
1346
|
-
else if (id === "manual" || id === "updated") onOrderPick(id);
|
|
1347
|
-
setOpen(false);
|
|
1419
|
+
],
|
|
1420
|
+
selectedIds: [groupBy, orderBy],
|
|
1421
|
+
onSelect: (id) => {
|
|
1422
|
+
if (id === "workspace" || id === "flat") onGroupPick(id);
|
|
1423
|
+
else if (id === "manual" || id === "updated") onOrderPick(id);
|
|
1424
|
+
setOpen(false);
|
|
1348
1425
|
},
|
|
1349
1426
|
align: "end",
|
|
1350
1427
|
dense: true,
|
|
@@ -1830,6 +1907,7 @@ window.__ModuleLoader__.load({
|
|
|
1830
1907
|
const workspaces = useWorkspaces((state) => state.items);
|
|
1831
1908
|
const workspacePhase = useWorkspaces((state) => state.phase);
|
|
1832
1909
|
const archivedSessionIds = useWorkspaces((state) => state.archivedSessionIds);
|
|
1910
|
+
const sessionSnapshot = useSessions((s) => s);
|
|
1833
1911
|
const directoryFlowAvailable = useDirectoryFlow((occupied) => occupied);
|
|
1834
1912
|
const groupBy = useStore((s) => s.groupBy);
|
|
1835
1913
|
const orderBy = useStore((s) => s.orderBy);
|
|
@@ -2069,13 +2147,25 @@ window.__ModuleLoader__.load({
|
|
|
2069
2147
|
if (deletingSession || deleteSessionTarget === null) return;
|
|
2070
2148
|
setDeletingSession(true);
|
|
2071
2149
|
setDeleteSessionError(null);
|
|
2072
|
-
|
|
2150
|
+
const rootId = deleteSessionTarget.sessionId;
|
|
2151
|
+
const ids = collectSessionAndDescendantIds(rootId, sessionSnapshot.byId);
|
|
2152
|
+
(async () => {
|
|
2153
|
+
for (const id of ids) {
|
|
2154
|
+
try {
|
|
2155
|
+
await deleteSession(id);
|
|
2156
|
+
} catch (reason) {
|
|
2157
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
2158
|
+
if (id !== rootId && isUnknownSessionError(reason)) continue;
|
|
2159
|
+
throw reason;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
})().then(() => {
|
|
2073
2163
|
setDeletingSession(false);
|
|
2074
2164
|
setDeleteSessionTarget(null);
|
|
2075
|
-
setDeleteSessionCommittedId(
|
|
2165
|
+
setDeleteSessionCommittedId(rootId);
|
|
2076
2166
|
}).catch((reason) => {
|
|
2077
2167
|
setDeletingSession(false);
|
|
2078
|
-
setDeleteSessionError(reason
|
|
2168
|
+
setDeleteSessionError(formatDeleteError(reason, t));
|
|
2079
2169
|
});
|
|
2080
2170
|
};
|
|
2081
2171
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2149,17 +2239,17 @@ window.__ModuleLoader__.load({
|
|
|
2149
2239
|
}),
|
|
2150
2240
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
2151
2241
|
className: clsx(WorkspaceBrowser_module_css_default.headerActions, wide && searchExpanded && WorkspaceBrowser_module_css_default.headerActionsHidden),
|
|
2152
|
-
children: [wide && (0, react_jsx_runtime.jsx)(ViewOptionsMenu, {
|
|
2153
|
-
groupBy,
|
|
2154
|
-
orderBy,
|
|
2155
|
-
onGroupPick: (mode) => {
|
|
2156
|
-
actions.setGroupBy(mode);
|
|
2157
|
-
},
|
|
2158
|
-
onOrderPick: (mode) => {
|
|
2159
|
-
actions.setOrderBy(mode);
|
|
2160
|
-
},
|
|
2161
|
-
t
|
|
2162
|
-
}), directoryFlowAvailable && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
2242
|
+
children: [wide && (0, react_jsx_runtime.jsx)(ViewOptionsMenu, {
|
|
2243
|
+
groupBy,
|
|
2244
|
+
orderBy,
|
|
2245
|
+
onGroupPick: (mode) => {
|
|
2246
|
+
actions.setGroupBy(mode);
|
|
2247
|
+
},
|
|
2248
|
+
onOrderPick: (mode) => {
|
|
2249
|
+
actions.setOrderBy(mode);
|
|
2250
|
+
},
|
|
2251
|
+
t
|
|
2252
|
+
}), directoryFlowAvailable && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
2163
2253
|
label: t("workspace.add"),
|
|
2164
2254
|
side: "bottom",
|
|
2165
2255
|
delayMs: 500,
|
|
@@ -2451,104 +2541,114 @@ window.__ModuleLoader__.load({
|
|
|
2451
2541
|
}
|
|
2452
2542
|
})
|
|
2453
2543
|
]
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
//#endregion
|
|
2457
|
-
//#region dsh-archive-manager: settings section
|
|
2458
|
-
const ARCHIVE_SETTINGS_CSS = ".dsham_settings{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;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px}.dsham_settings h2{margin:0;font-size:20px;font-weight:650;letter-spacing:-.2px;line-height:28px}.dsham_settingsIntro{margin:4px 0 0;max-width:42em;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.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{min-width:132px;height:32px;padding:0 28px 0 11px;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}.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}@media(max-width:720px){.dsham_settings{width:100%;margin:28px auto 48px;padding:0 16px}.dsham_settingsHeader{margin-bottom:28px}.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}}";
|
|
2459
|
-
const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
|
|
2460
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
|
|
2461
|
-
const tag = document.createElement("style");
|
|
2462
|
-
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
2463
|
-
tag.dataset.pluginCss = "dsh-archive-manager/ArchiveSettings.layout.css";
|
|
2464
|
-
tag.textContent = ARCHIVE_SETTINGS_LAYOUT_OVERRIDE;
|
|
2465
|
-
document.head.appendChild(tag);
|
|
2466
|
-
}
|
|
2467
|
-
/** 管理设置页中的归档会话,数据直接订阅 DSH 的会话与工作区投影。 */
|
|
2468
|
-
function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, t }) {
|
|
2469
|
-
const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
|
|
2470
|
-
const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
|
|
2471
|
-
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
2472
|
-
const [busy, setBusy] = (0, react.useState)(false);
|
|
2473
|
-
const [error, setError] = (0, react.useState)(null);
|
|
2474
|
-
const [query, setQuery] = (0, react.useState)("");
|
|
2475
|
-
const [project, setProject] = (0, react.useState)("all");
|
|
2476
|
-
const groups = (0, react.useMemo)(() => {
|
|
2477
|
-
|
|
2478
|
-
const
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
const
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
//#endregion
|
|
2547
|
+
//#region dsh-archive-manager: settings section
|
|
2548
|
+
const ARCHIVE_SETTINGS_CSS = ".dsham_settings{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;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px}.dsham_settings h2{margin:0;font-size:20px;font-weight:650;letter-spacing:-.2px;line-height:28px}.dsham_settingsIntro{margin:4px 0 0;max-width:42em;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.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{min-width:132px;height:32px;padding:0 28px 0 11px;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}.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}@media(max-width:720px){.dsham_settings{width:100%;margin:28px auto 48px;padding:0 16px}.dsham_settingsHeader{margin-bottom:28px}.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}}";
|
|
2549
|
+
const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
|
|
2550
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
|
|
2551
|
+
const tag = document.createElement("style");
|
|
2552
|
+
tag.dataset.plugin = "@michengai/dsh-archive-manager";
|
|
2553
|
+
tag.dataset.pluginCss = "dsh-archive-manager/ArchiveSettings.layout.css";
|
|
2554
|
+
tag.textContent = ARCHIVE_SETTINGS_LAYOUT_OVERRIDE;
|
|
2555
|
+
document.head.appendChild(tag);
|
|
2556
|
+
}
|
|
2557
|
+
/** 管理设置页中的归档会话,数据直接订阅 DSH 的会话与工作区投影。 */
|
|
2558
|
+
function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, t }) {
|
|
2559
|
+
const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
|
|
2560
|
+
const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
|
|
2561
|
+
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
2562
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
2563
|
+
const [error, setError] = (0, react.useState)(null);
|
|
2564
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
2565
|
+
const [project, setProject] = (0, react.useState)("all");
|
|
2566
|
+
const groups = (0, react.useMemo)(() => {
|
|
2567
|
+
// 列表仍隐藏 subagent;“删除全部”另用 collectArchivedDeleteAllIds 收父会话和子会话。
|
|
2568
|
+
const archived = new Set(workspaceState.archivedSessionIds);
|
|
2569
|
+
const byWorkspace = workspaceState.items.map((workspace) => ({
|
|
2570
|
+
title: workspace.title,
|
|
2571
|
+
ids: new Set(workspace.sessionIds),
|
|
2572
|
+
sessions: []
|
|
2573
|
+
}));
|
|
2574
|
+
const ungrouped = [];
|
|
2575
|
+
for (const id of workspaceState.archivedSessionIds) {
|
|
2576
|
+
const session = sessions.byId[id];
|
|
2577
|
+
if (session === void 0 || session.origin === "subagent" || !archived.has(id)) continue;
|
|
2578
|
+
const group = byWorkspace.find((workspace) => workspace.ids.has(id));
|
|
2579
|
+
(group === void 0 ? ungrouped : group.sessions).push(session);
|
|
2580
|
+
}
|
|
2581
|
+
const result = byWorkspace.filter((group) => group.sessions.length > 0);
|
|
2582
|
+
if (ungrouped.length > 0) result.push({ title: t("group.ungrouped"), sessions: ungrouped });
|
|
2583
|
+
return result;
|
|
2584
|
+
}, [sessions, workspaceState, t]);
|
|
2585
|
+
const filteredGroups = (0, react.useMemo)(() => {
|
|
2586
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
2587
|
+
return groups.filter((group) => project === "all" || project === group.title).map((group) => ({
|
|
2588
|
+
...group,
|
|
2589
|
+
sessions: group.sessions.filter((session) => normalizedQuery === "" || displayTitle(session, t).toLocaleLowerCase().includes(normalizedQuery))
|
|
2590
|
+
})).filter((group) => group.sessions.length > 0);
|
|
2591
|
+
}, [groups, project, query, t]);
|
|
2592
|
+
const archivedSessionIds = (0, react.useMemo)(() => groups.flatMap((group) => group.sessions.map((session) => session.id)), [groups]);
|
|
2593
|
+
const deleteAllSessionIds = (0, react.useMemo)(() => collectArchivedDeleteAllIds(workspaceState.archivedSessionIds, sessions.byId), [workspaceState.archivedSessionIds, sessions]);
|
|
2594
|
+
const onUnarchive = (sessionId) => {
|
|
2595
|
+
setError(null);
|
|
2596
|
+
unarchiveSession(sessionId).catch((reason) => {
|
|
2597
|
+
setError(formatUnarchiveError(reason, t));
|
|
2598
|
+
});
|
|
2599
|
+
};
|
|
2600
|
+
const closeDelete = () => {
|
|
2601
|
+
if (!busy) setDeleteTarget(null);
|
|
2602
|
+
};
|
|
2603
|
+
const confirmDelete = async () => {
|
|
2604
|
+
if (busy || deleteTarget === null) return;
|
|
2605
|
+
setBusy(true);
|
|
2606
|
+
setError(null);
|
|
2607
|
+
try {
|
|
2608
|
+
for (const id of deleteTarget.all ? deleteAllSessionIds : collectSessionAndDescendantIds(deleteTarget.id, sessions.byId)) {
|
|
2609
|
+
try {
|
|
2610
|
+
await deleteSession(id);
|
|
2611
|
+
} catch (reason) {
|
|
2612
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
2613
|
+
if ((deleteTarget.all || id !== deleteTarget.id) && isUnknownSessionError(reason)) continue;
|
|
2614
|
+
throw reason;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
setDeleteTarget(null);
|
|
2618
|
+
} catch (reason) {
|
|
2619
|
+
setError(formatDeleteError(reason, t));
|
|
2620
|
+
} finally {
|
|
2621
|
+
setBusy(false);
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
2625
|
+
className: "dsham_settings",
|
|
2626
|
+
"aria-label": t("archives.title"),
|
|
2627
|
+
children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS }), (0, react_jsx_runtime.jsxs)("header", { className: "dsham_settingsHeader", children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })] }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || deleteAllSessionIds.length === 0, onClick: () => setDeleteTarget({ all: true }), 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)("select", { className: "dsham_settingsFilter", value: project, onChange: (event) => setProject(event.target.value), "aria-label": t("archives.projectFilter"), children: [(0, react_jsx_runtime.jsx)("option", { value: "all", children: t("archives.allProjects") }), groups.map((group) => (0, react_jsx_runtime.jsx)("option", { value: group.title, children: group.title }, group.title))] })] }), 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) => (0, react_jsx_runtime.jsxs)("section", {
|
|
2628
|
+
className: "dsham_settingsGroup",
|
|
2629
|
+
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.jsx)("span", { className: "dsham_settingsCount", children: t("archives.sessionCount", { n: group.sessions.length }) })] }), (0, react_jsx_runtime.jsx)("div", {
|
|
2630
|
+
className: "dsham_settingsList",
|
|
2631
|
+
children: group.sessions.map((session) => (0, react_jsx_runtime.jsxs)("article", {
|
|
2632
|
+
className: "dsham_settingsRow",
|
|
2633
|
+
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", {
|
|
2634
|
+
className: "dsham_settingsActions",
|
|
2635
|
+
children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy, onClick: () => onUnarchive(session.id), children: t("menu.unarchive") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy, "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget(session), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
|
|
2636
|
+
})]
|
|
2637
|
+
}, session.id))
|
|
2638
|
+
})]
|
|
2639
|
+
}, group.title)), error !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }), (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
2640
|
+
open: deleteTarget !== null,
|
|
2641
|
+
onClose: closeDelete,
|
|
2642
|
+
closeLabel: t("close"),
|
|
2643
|
+
title: deleteTarget?.all ? t("archives.deleteAllTitle") : t("deleteSession.title"),
|
|
2644
|
+
...deleteTarget === null ? {} : { description: deleteTarget.all ? t("archives.deleteAllDesc", { n: deleteAllSessionIds.length }) : t("deleteSession.desc", { name: displayTitle(deleteTarget, t) }) },
|
|
2645
|
+
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", disabled: busy, onClick: confirmDelete, children: deleteTarget?.all ? t("archives.deleteAll") : t("deleteSession.title") })] }),
|
|
2646
|
+
children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.all ? t("archives.deleteAllPending") : t("deleteSession.pending") })
|
|
2647
|
+
})]
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
//#endregion
|
|
2651
|
+
//#region lib/types/client/locales.js
|
|
2552
2652
|
/**
|
|
2553
2653
|
* `workspace` namespace dictionaries: the browsing region (section header,
|
|
2554
2654
|
* search, tree rows, dialogs) and the pick/add flow. Runtime failure
|
|
@@ -2561,7 +2661,7 @@ window.__ModuleLoader__.load({
|
|
|
2561
2661
|
"section.workspaces": "工作区",
|
|
2562
2662
|
"section.sessions": "会话",
|
|
2563
2663
|
"viewOptions.label": "视图选项",
|
|
2564
|
-
"groupBy.label": "分组方式",
|
|
2664
|
+
"groupBy.label": "分组方式",
|
|
2565
2665
|
"groupBy.workspace": "按工作区",
|
|
2566
2666
|
"groupBy.flat": "单列表",
|
|
2567
2667
|
"orderBy.label": "排序方式",
|
|
@@ -2584,23 +2684,27 @@ window.__ModuleLoader__.load({
|
|
|
2584
2684
|
"menu.unarchive": "取消归档",
|
|
2585
2685
|
"menu.deleteSession": "删除会话",
|
|
2586
2686
|
"archived.badge": "已归档",
|
|
2587
|
-
"archived.notOpenable": "已归档,取消归档后可继续对话",
|
|
2588
|
-
"archives.title": "已归档的聊天",
|
|
2589
|
-
"archives.description": "管理已归档的会话。",
|
|
2590
|
-
"archives.empty": "暂无已归档会话",
|
|
2591
|
-
"archives.emptyFiltered": "没有匹配的已归档聊天",
|
|
2592
|
-
"archives.searchPlaceholder": "搜索已归档聊天",
|
|
2593
|
-
"archives.projectFilter": "按项目筛选",
|
|
2594
|
-
"archives.allProjects": "所有项目",
|
|
2595
|
-
"archives.sessionCount": "{n} 个聊天",
|
|
2596
|
-
"archives.timestamp": "{date},{time}",
|
|
2597
|
-
"archives.deleteAll": "全部删除",
|
|
2598
|
-
"archives.deleteAllTitle": "删除全部已归档聊天",
|
|
2599
|
-
"archives.deleteAllDesc": "将永久删除全部 {n}
|
|
2687
|
+
"archived.notOpenable": "已归档,取消归档后可继续对话",
|
|
2688
|
+
"archives.title": "已归档的聊天",
|
|
2689
|
+
"archives.description": "管理已归档的会话。",
|
|
2690
|
+
"archives.empty": "暂无已归档会话",
|
|
2691
|
+
"archives.emptyFiltered": "没有匹配的已归档聊天",
|
|
2692
|
+
"archives.searchPlaceholder": "搜索已归档聊天",
|
|
2693
|
+
"archives.projectFilter": "按项目筛选",
|
|
2694
|
+
"archives.allProjects": "所有项目",
|
|
2695
|
+
"archives.sessionCount": "{n} 个聊天",
|
|
2696
|
+
"archives.timestamp": "{date},{time}",
|
|
2697
|
+
"archives.deleteAll": "全部删除",
|
|
2698
|
+
"archives.deleteAllTitle": "删除全部已归档聊天",
|
|
2699
|
+
"archives.deleteAllDesc": "将永久删除全部 {n} 个已归档聊天及其子代理和记录,此操作不可恢复。",
|
|
2600
2700
|
"archives.deleteAllPending": "正在删除已归档聊天…",
|
|
2601
|
-
"
|
|
2602
|
-
"
|
|
2603
|
-
"deleteSession.
|
|
2701
|
+
"archives.unarchiveUnknown": "会话已不存在,无法取消归档。",
|
|
2702
|
+
"archives.unarchiveFailed": "取消归档失败:{detail}",
|
|
2703
|
+
"deleteSession.title": "删除会话",
|
|
2704
|
+
"deleteSession.desc": "将永久删除会话“{name}”及其子代理和全部记录(对话内容、统计、缓存),此操作不可恢复。",
|
|
2705
|
+
"deleteSession.pending": "正在删除会话…",
|
|
2706
|
+
"deleteSession.unknown": "会话已不存在或已被删除。",
|
|
2707
|
+
"deleteSession.failed": "删除会话失败:{detail}",
|
|
2604
2708
|
"picker.loading": "正在加载工作区…",
|
|
2605
2709
|
"conflict.named": "已存在名为“{name}”的工作区。",
|
|
2606
2710
|
"folderError.title": "无法打开文件夹",
|
|
@@ -2646,7 +2750,7 @@ window.__ModuleLoader__.load({
|
|
|
2646
2750
|
"section.workspaces": "Workspaces",
|
|
2647
2751
|
"section.sessions": "Sessions",
|
|
2648
2752
|
"viewOptions.label": "View options",
|
|
2649
|
-
"groupBy.label": "Group by",
|
|
2753
|
+
"groupBy.label": "Group by",
|
|
2650
2754
|
"groupBy.workspace": "WorkSpace",
|
|
2651
2755
|
"groupBy.flat": "In one list",
|
|
2652
2756
|
"orderBy.label": "Order by",
|
|
@@ -2669,23 +2773,27 @@ window.__ModuleLoader__.load({
|
|
|
2669
2773
|
"menu.unarchive": "Unarchive",
|
|
2670
2774
|
"menu.deleteSession": "Delete session",
|
|
2671
2775
|
"archived.badge": "Archived",
|
|
2672
|
-
"archived.notOpenable": "This session is archived. Unarchive it to continue the conversation.",
|
|
2673
|
-
"archives.title": "Archived chats",
|
|
2674
|
-
"archives.description": "Manage archived sessions.",
|
|
2675
|
-
"archives.empty": "No archived sessions.",
|
|
2676
|
-
"archives.emptyFiltered": "No archived chats match your filters.",
|
|
2677
|
-
"archives.searchPlaceholder": "Search archived chats",
|
|
2678
|
-
"archives.projectFilter": "Filter by project",
|
|
2679
|
-
"archives.allProjects": "All projects",
|
|
2680
|
-
"archives.sessionCount": "{n} chats",
|
|
2681
|
-
"archives.timestamp": "{date}, {time}",
|
|
2682
|
-
"archives.deleteAll": "Delete all",
|
|
2683
|
-
"archives.deleteAllTitle": "Delete all archived chats",
|
|
2684
|
-
"archives.deleteAllDesc": "This permanently deletes all {n} archived chats and their records. This cannot be undone.",
|
|
2776
|
+
"archived.notOpenable": "This session is archived. Unarchive it to continue the conversation.",
|
|
2777
|
+
"archives.title": "Archived chats",
|
|
2778
|
+
"archives.description": "Manage archived sessions.",
|
|
2779
|
+
"archives.empty": "No archived sessions.",
|
|
2780
|
+
"archives.emptyFiltered": "No archived chats match your filters.",
|
|
2781
|
+
"archives.searchPlaceholder": "Search archived chats",
|
|
2782
|
+
"archives.projectFilter": "Filter by project",
|
|
2783
|
+
"archives.allProjects": "All projects",
|
|
2784
|
+
"archives.sessionCount": "{n} chats",
|
|
2785
|
+
"archives.timestamp": "{date}, {time}",
|
|
2786
|
+
"archives.deleteAll": "Delete all",
|
|
2787
|
+
"archives.deleteAllTitle": "Delete all archived chats",
|
|
2788
|
+
"archives.deleteAllDesc": "This permanently deletes all {n} archived chats, their child agents, and their records. This cannot be undone.",
|
|
2685
2789
|
"archives.deleteAllPending": "Deleting archived chats…",
|
|
2686
|
-
"
|
|
2687
|
-
"
|
|
2688
|
-
"deleteSession.
|
|
2790
|
+
"archives.unarchiveUnknown": "This session no longer exists, so it cannot be unarchived.",
|
|
2791
|
+
"archives.unarchiveFailed": "Could not unarchive the session: {detail}",
|
|
2792
|
+
"deleteSession.title": "Delete session",
|
|
2793
|
+
"deleteSession.desc": "This permanently deletes session “{name}”, its child agents, and all of its records (conversation, stats, cache). This cannot be undone.",
|
|
2794
|
+
"deleteSession.pending": "Deleting session…",
|
|
2795
|
+
"deleteSession.unknown": "This session no longer exists or was already deleted.",
|
|
2796
|
+
"deleteSession.failed": "Could not delete the session: {detail}",
|
|
2689
2797
|
"picker.loading": "Loading workspaces…",
|
|
2690
2798
|
"conflict.named": "A workspace named “{name}” already exists.",
|
|
2691
2799
|
"folderError.title": "Couldn’t open folder",
|
|
@@ -2768,7 +2876,7 @@ window.__ModuleLoader__.load({
|
|
|
2768
2876
|
* framework's global hooks.
|
|
2769
2877
|
* @param ctx - client root context.
|
|
2770
2878
|
*/
|
|
2771
|
-
function applyWorkspaceBrowser(ctx) {
|
|
2879
|
+
function applyWorkspaceBrowser(ctx) {
|
|
2772
2880
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
2773
2881
|
zh,
|
|
2774
2882
|
en
|
|
@@ -2784,19 +2892,19 @@ window.__ModuleLoader__.load({
|
|
|
2784
2892
|
});
|
|
2785
2893
|
const browserFlowSource = flowSource("sidebar.workspaces.directoryFlow");
|
|
2786
2894
|
const pickerFlowSource = flowSource("conversation.hero.workspace.directoryFlow");
|
|
2787
|
-
const unarchiveSession = async (sessionId) => {
|
|
2788
|
-
const registry = ctx.get("remote.workspaceRegistry");
|
|
2789
|
-
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
2790
|
-
const result = await registry.unarchiveSession(sessionId);
|
|
2791
|
-
if (!result.ok) throw new Error(result.error.message);
|
|
2792
|
-
};
|
|
2793
|
-
const deleteSession = async (sessionId) => {
|
|
2794
|
-
const registry = ctx.get("remote.workspaceRegistry");
|
|
2795
|
-
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
2796
|
-
const result = await registry.deleteSession(sessionId);
|
|
2797
|
-
if (!result.ok) throw new Error(result.error.message);
|
|
2798
|
-
};
|
|
2799
|
-
const browserInjected = () => ({
|
|
2895
|
+
const unarchiveSession = async (sessionId) => {
|
|
2896
|
+
const registry = ctx.get("remote.workspaceRegistry");
|
|
2897
|
+
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
2898
|
+
const result = await registry.unarchiveSession(sessionId);
|
|
2899
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
2900
|
+
};
|
|
2901
|
+
const deleteSession = async (sessionId) => {
|
|
2902
|
+
const registry = ctx.get("remote.workspaceRegistry");
|
|
2903
|
+
if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
|
|
2904
|
+
const result = await registry.deleteSession(sessionId);
|
|
2905
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
2906
|
+
};
|
|
2907
|
+
const browserInjected = () => ({
|
|
2800
2908
|
startSession: (workspaceId) => {
|
|
2801
2909
|
ctx.workspaces.startSession(workspaceId);
|
|
2802
2910
|
},
|
|
@@ -2831,8 +2939,8 @@ window.__ModuleLoader__.load({
|
|
|
2831
2939
|
archiveSession: async (sessionId) => {
|
|
2832
2940
|
await ctx.workspaces.archiveSession(sessionId);
|
|
2833
2941
|
},
|
|
2834
|
-
unarchiveSession,
|
|
2835
|
-
deleteSession,
|
|
2942
|
+
unarchiveSession,
|
|
2943
|
+
deleteSession,
|
|
2836
2944
|
insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
|
|
2837
2945
|
await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
|
|
2838
2946
|
},
|
|
@@ -2853,7 +2961,7 @@ window.__ModuleLoader__.load({
|
|
|
2853
2961
|
inject: browserInjected,
|
|
2854
2962
|
locale: NS
|
|
2855
2963
|
}, WorkspaceBrowser));
|
|
2856
|
-
ctx.slots.inject("conversation.hero.workspace", () => ctx.slots.register({
|
|
2964
|
+
ctx.slots.inject("conversation.hero.workspace", () => ctx.slots.register({
|
|
2857
2965
|
name: "conversation.hero.workspace",
|
|
2858
2966
|
children: { "conversation.hero.workspace.directoryFlow": {
|
|
2859
2967
|
kind: "single",
|
|
@@ -2861,28 +2969,31 @@ window.__ModuleLoader__.load({
|
|
|
2861
2969
|
} },
|
|
2862
2970
|
inject: pickerInjected,
|
|
2863
2971
|
locale: NS
|
|
2864
|
-
}, WorkspacePicker));
|
|
2865
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
2866
|
-
name: "settings.section",
|
|
2867
|
-
id: "archived-sessions",
|
|
2868
|
-
order: 18,
|
|
2869
|
-
label: () => ctx.locale.bind(NS)("archived.badge"),
|
|
2870
|
-
icon: "archive",
|
|
2871
|
-
locale: NS,
|
|
2872
|
-
inject: () => ({
|
|
2873
|
-
sessionStore: ctx.sessions.list,
|
|
2874
|
-
workspaceStore: ctx.workspaces.list,
|
|
2875
|
-
unarchiveSession,
|
|
2876
|
-
deleteSession,
|
|
2877
|
-
t: ctx.locale.bind(NS)
|
|
2878
|
-
})
|
|
2879
|
-
}, ArchivedSessionsSection));
|
|
2880
|
-
}
|
|
2972
|
+
}, WorkspacePicker));
|
|
2973
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
2974
|
+
name: "settings.section",
|
|
2975
|
+
id: "archived-sessions",
|
|
2976
|
+
order: 18,
|
|
2977
|
+
label: () => ctx.locale.bind(NS)("archived.badge"),
|
|
2978
|
+
icon: "archive",
|
|
2979
|
+
locale: NS,
|
|
2980
|
+
inject: () => ({
|
|
2981
|
+
sessionStore: ctx.sessions.list,
|
|
2982
|
+
workspaceStore: ctx.workspaces.list,
|
|
2983
|
+
unarchiveSession,
|
|
2984
|
+
deleteSession,
|
|
2985
|
+
t: ctx.locale.bind(NS)
|
|
2986
|
+
})
|
|
2987
|
+
}, ArchivedSessionsSection));
|
|
2988
|
+
}
|
|
2881
2989
|
//#endregion
|
|
2882
2990
|
/** Pure derivation surface for dsh-archive-manager self-tests (no-op for the runtime). */
|
|
2883
|
-
exports.__test = {
|
|
2884
|
-
displayTitle,
|
|
2885
|
-
sessionVisible,
|
|
2991
|
+
exports.__test = {
|
|
2992
|
+
displayTitle,
|
|
2993
|
+
sessionVisible,
|
|
2994
|
+
collectArchivedDeleteAllIds,
|
|
2995
|
+
collectSessionAndDescendantIds,
|
|
2996
|
+
isUnknownSessionError,
|
|
2886
2997
|
deriveGroups,
|
|
2887
2998
|
deriveFlat,
|
|
2888
2999
|
deriveSearchResults,
|
|
@@ -2895,4 +3006,4 @@ window.__ModuleLoader__.load({
|
|
|
2895
3006
|
exports.inject = inject;
|
|
2896
3007
|
return module.exports;
|
|
2897
3008
|
}
|
|
2898
|
-
});
|
|
3009
|
+
});
|
package/lib/projcache.js
CHANGED
|
@@ -34,7 +34,11 @@ var ArchiveProjectionCache = class extends SessionProjectionCache {
|
|
|
34
34
|
this.writeTail = Promise.allSettled([this.writeTail, task]).then(() => void 0);
|
|
35
35
|
return task;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* 墓碑正确性依赖:super.write(session) 一次整体写入,返回后不再有后续异步落盘。
|
|
39
|
+
* 若上游改成多阶段异步,(C) 补删会漏掉后续写入,deletedSessionIds 挡不住复活。
|
|
40
|
+
*/
|
|
41
|
+
async writeCore(session) {
|
|
38
42
|
if (this.deletedSessionIds.has(session.id)) return;
|
|
39
43
|
await super.write(session);
|
|
40
44
|
if (this.deletedSessionIds.has(session.id)) await this.requireTable().delete(session.id);
|
package/lib/workspace.js
CHANGED
|
@@ -17,6 +17,10 @@ import { sessionDir } from "@deepseek-ai/dsh-spill-local";
|
|
|
17
17
|
*
|
|
18
18
|
* 所有可失败的持久化清理都先于不可逆的物理删除。此前任一步失败时,
|
|
19
19
|
* 转录仍保留,可重试删除;物理删除失败时记录残留路径供人工排查。
|
|
20
|
+
* 物理删除成功后必须遗忘父类内存索引(headers / sessionPaths /
|
|
21
|
+
* invalidSessionPaths)并打上删除墓碑:父类 sessionKnown 以 headers.has
|
|
22
|
+
* 短路,indexHeaders 只增不减,stale list() 否则会把已删 id 救活,
|
|
23
|
+
* 进而被 archiveSession 重新写回 archivedSessionIds。
|
|
20
24
|
* `unarchiveSession` 与 `deleteSession` 通过 Typert Remote 暴露给浏览器,并注册到 typert.local,避免生产环境只靠 SRC 扫描时 404。
|
|
21
25
|
*/
|
|
22
26
|
function markRemoteMethod(instance, method) {
|
|
@@ -33,6 +37,17 @@ function markRemoteMethod(instance, method) {
|
|
|
33
37
|
};
|
|
34
38
|
Remote(method)(void 0, context);
|
|
35
39
|
}
|
|
40
|
+
function unknownSessionMessage(sessionId) {
|
|
41
|
+
return `cannot delete session "${sessionId}": unknown (UNKNOWN_SESSION)`;
|
|
42
|
+
}
|
|
43
|
+
var ArchiveUnknownSessionError = class extends Error {
|
|
44
|
+
sessionId;
|
|
45
|
+
constructor(sessionId) {
|
|
46
|
+
super(unknownSessionMessage(sessionId));
|
|
47
|
+
this.sessionId = sessionId;
|
|
48
|
+
this.name = "ArchiveUnknownSessionError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
36
51
|
const sessionIdSchema = {
|
|
37
52
|
parse(value) {
|
|
38
53
|
if (typeof value !== "string" || value.length === 0) throw new TypeError(`sessionId must be a non-empty string, got ${String(value)}`);
|
|
@@ -121,6 +136,12 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
121
136
|
"sessionProjectionCache",
|
|
122
137
|
"typert"
|
|
123
138
|
];
|
|
139
|
+
/** 本进程内已物理删除的会话;阻止父类把 stale list() 重新编入索引。 */
|
|
140
|
+
deletedSessionIds = /* @__PURE__ */ new Set();
|
|
141
|
+
/** 墓碑插入顺序,用于在上限处淘汰最旧项。 */
|
|
142
|
+
deletedSessionOrder = [];
|
|
143
|
+
/** 墓碑上限:足够挡住 stale list(),又避免长驻进程无限增长。 */
|
|
144
|
+
deletedSessionTombstoneLimit = 4096;
|
|
124
145
|
constructor(ctx) {
|
|
125
146
|
super(ctx);
|
|
126
147
|
this.typertRemote = bindTypertRemote(this, this.name);
|
|
@@ -155,7 +176,7 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
155
176
|
* directory, workspace accounting, archive marker, projection cache row).
|
|
156
177
|
* @param sessionId - the session to delete.
|
|
157
178
|
* @returns `{ deleted: true }` after durability.
|
|
158
|
-
* @throws {@link
|
|
179
|
+
* @throws {@link ArchiveUnknownSessionError} when the session is unknown.
|
|
159
180
|
*/
|
|
160
181
|
async deleteSession(sessionId) {
|
|
161
182
|
return this.enqueueOperation(() => this.deleteSessionCore(sessionId));
|
|
@@ -163,7 +184,7 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
163
184
|
/** The serialized core deletion body (also used by the cascade path, which
|
|
164
185
|
* already holds the operation chain — it must never re-enqueue). */
|
|
165
186
|
async deleteSessionCore(sessionId) {
|
|
166
|
-
if (!await this.sessionKnown(sessionId)) throw new
|
|
187
|
+
if (!await this.sessionKnown(sessionId)) throw new ArchiveUnknownSessionError(sessionId);
|
|
167
188
|
const sessions = this.ctx.get("sessions");
|
|
168
189
|
const live = sessions?.get(sessionId);
|
|
169
190
|
if (live !== void 0) {
|
|
@@ -192,9 +213,48 @@ var ArchiveWorkspaceRegistry = class extends WorkspaceRegistry {
|
|
|
192
213
|
await this.removeFromWorkspaceAccounts(sessionId);
|
|
193
214
|
if (projCache !== void 0) await projCache.delete(sessionId);
|
|
194
215
|
await this.deleteDescendants(sessionId);
|
|
195
|
-
await this.cleanSpill(sessionId);
|
|
196
|
-
await this.removeTranscriptDirectory(sessionId);
|
|
197
|
-
|
|
216
|
+
await this.cleanSpill(sessionId);
|
|
217
|
+
await this.removeTranscriptDirectory(sessionId);
|
|
218
|
+
// 物理删除已成功:此时再清父类索引。失败时保留索引,便于重试。
|
|
219
|
+
this.forgetIndexedSession(sessionId);
|
|
220
|
+
return { deleted: true };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 从父类内存索引中遗忘已删除会话,并阻止后续 indexHeaders 把它加回。
|
|
224
|
+
* 实时会话以同 id 重新出现时(自定义 id 复用)会撤掉墓碑。
|
|
225
|
+
*/
|
|
226
|
+
forgetIndexedSession(sessionId) {
|
|
227
|
+
if (!this.deletedSessionIds.has(sessionId)) {
|
|
228
|
+
this.deletedSessionIds.add(sessionId);
|
|
229
|
+
this.deletedSessionOrder.push(sessionId);
|
|
230
|
+
}
|
|
231
|
+
while (this.deletedSessionOrder.length > this.deletedSessionTombstoneLimit) {
|
|
232
|
+
const oldest = this.deletedSessionOrder.shift();
|
|
233
|
+
if (oldest !== void 0 && oldest !== sessionId) this.deletedSessionIds.delete(oldest);
|
|
234
|
+
}
|
|
235
|
+
this.headers.delete(sessionId);
|
|
236
|
+
this.sessionPaths.delete(sessionId);
|
|
237
|
+
this.invalidSessionPaths.delete(sessionId);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* 已删除会话对归档/删除入口都视为未知。实时复用同一 id 时撤墓碑,
|
|
241
|
+
* 避免挡住新会话。
|
|
242
|
+
*/
|
|
243
|
+
async sessionKnown(id) {
|
|
244
|
+
if (this.ctx.get("sessions")?.get(id) !== void 0) {
|
|
245
|
+
this.deletedSessionIds.delete(id);
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
if (this.deletedSessionIds.has(id)) return false;
|
|
249
|
+
return super.sessionKnown(id);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 父类 indexHeaders 只增不减;跳过墓碑 id,避免 stale persistence.list()
|
|
253
|
+
* 把已删除会话重新编入 headers。
|
|
254
|
+
*/
|
|
255
|
+
async indexHeader(header) {
|
|
256
|
+
if (this.deletedSessionIds.has(header.id)) return;
|
|
257
|
+
return super.indexHeader(header);
|
|
198
258
|
}
|
|
199
259
|
/** 为未处于实时状态的持久化会话发布相同的移除事件。 */
|
|
200
260
|
async publishColdSessionRemoval(sessionId, sessions) {
|
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "@michengai/dsh-archive-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "NPM-installable DSH Web plugin for managing archived sessions.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@11.21.0",
|
|
7
|
-
"engines": {
|
|
8
|
-
"node": "^22.19.0 || >=24.0.0"
|
|
9
|
-
},
|
|
10
|
-
"main": "lib/index.js",
|
|
11
|
-
"exports": {
|
|
12
|
-
".": "./lib/index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@11.21.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
9
|
+
},
|
|
10
|
+
"main": "lib/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./lib/index.js",
|
|
13
13
|
"./workspace": "./lib/workspace.js",
|
|
14
14
|
"./projcache": "./lib/projcache.js",
|
|
15
15
|
"./client": "./lib/client.js",
|
|
16
|
-
"./package.json": "./package.json"
|
|
17
|
-
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
18
|
"files": [
|
|
19
19
|
"lib",
|
|
20
20
|
"assets",
|
|
21
21
|
"cordis.patch.yml",
|
|
22
22
|
"README.md",
|
|
23
23
|
"README.zh-CN.md",
|
|
24
|
-
"LICENSE"
|
|
25
|
-
],
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "node scripts/check-package.mjs",
|
|
28
28
|
"test": "node --test test/*.test.mjs",
|
|
@@ -44,42 +44,42 @@
|
|
|
44
44
|
"react-dom": "18.3.1"
|
|
45
45
|
},
|
|
46
46
|
"license": "Apache-2.0",
|
|
47
|
-
"dsh": {
|
|
48
|
-
"bundle": {
|
|
49
|
-
"patch": "./cordis.patch.yml"
|
|
50
|
-
},
|
|
51
|
-
"client": {
|
|
52
|
-
"inject": [
|
|
53
|
-
"@deepseek-ai/dsh-client-locale",
|
|
54
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-conversation",
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
57
|
-
"@deepseek-ai/dsh-typert-registry",
|
|
58
|
-
"@deepseek-ai/dsh-client-connection"
|
|
59
|
-
],
|
|
60
|
-
"platform": "web"
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"repository": {
|
|
64
|
-
"type": "git",
|
|
47
|
+
"dsh": {
|
|
48
|
+
"bundle": {
|
|
49
|
+
"patch": "./cordis.patch.yml"
|
|
50
|
+
},
|
|
51
|
+
"client": {
|
|
52
|
+
"inject": [
|
|
53
|
+
"@deepseek-ai/dsh-client-locale",
|
|
54
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
57
|
+
"@deepseek-ai/dsh-typert-registry",
|
|
58
|
+
"@deepseek-ai/dsh-client-connection"
|
|
59
|
+
],
|
|
60
|
+
"platform": "web"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"repository": {
|
|
64
|
+
"type": "git",
|
|
65
65
|
"url": "git+https://github.com/MichengAI/dsh-archive-manager.git"
|
|
66
66
|
},
|
|
67
67
|
"homepage": "https://github.com/MichengAI/dsh-archive-manager#readme",
|
|
68
68
|
"bugs": {
|
|
69
69
|
"url": "https://github.com/MichengAI/dsh-archive-manager/issues"
|
|
70
|
-
},
|
|
71
|
-
"keywords": [
|
|
72
|
-
"deepseek",
|
|
73
|
-
"dsh",
|
|
74
|
-
"dsh-plugin",
|
|
75
|
-
"archive",
|
|
76
|
-
"session-management",
|
|
77
|
-
"cordis",
|
|
78
|
-
"typert",
|
|
79
|
-
"workspace",
|
|
80
|
-
"nodejs",
|
|
81
|
-
"esm"
|
|
82
|
-
],
|
|
70
|
+
},
|
|
71
|
+
"keywords": [
|
|
72
|
+
"deepseek",
|
|
73
|
+
"dsh",
|
|
74
|
+
"dsh-plugin",
|
|
75
|
+
"archive",
|
|
76
|
+
"session-management",
|
|
77
|
+
"cordis",
|
|
78
|
+
"typert",
|
|
79
|
+
"workspace",
|
|
80
|
+
"nodejs",
|
|
81
|
+
"esm"
|
|
82
|
+
],
|
|
83
83
|
"publishConfig": {
|
|
84
84
|
"access": "public",
|
|
85
85
|
"registry": "https://registry.npmjs.org/"
|