dsh-archived-chats 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![DSH Insights health](https://dsh-insights.com/badge/Ultronen/dsh-archived-chats.svg)](https://dsh-insights.com/p/Ultronen/dsh-archived-chats/)
1
2
  <p align="center">
2
3
  <img src="assets/brand/session-archive-banner.png" alt="Session Archive: local-first archived-chat center for DeepSeek Harness" width="100%">
3
4
  </p>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/lib/client.js CHANGED
@@ -297,7 +297,7 @@ window.__ModuleLoader__.load({
297
297
  "retention.preview": "预览清理",
298
298
  "retention.apply": "应用所选清理",
299
299
  "retention.empty": "当前策略没有清理候选",
300
- "retention.saved": "策略已保存,不会自动执行清理",
300
+ "retention.saved": "策略已保存",
301
301
  "retention.note": "保留数量按每个原会话分别计算;保存策略不会删除任何数据,必须再预览并应用清理。",
302
302
  "retention.snapshotCandidate": "历史快照清理",
303
303
  "retention.trashCandidate": "永久删除回收站会话",
@@ -345,8 +345,7 @@ window.__ModuleLoader__.load({
345
345
  "lineage.diagnostic.cycle": "检测到循环",
346
346
  "lineage.diagnostic.delegation-depth-mismatch": "委派深度不一致",
347
347
  "action.import": "导入备份",
348
- "action.export": "导出备份",
349
- "action.more": "更多",
348
+ "action.export": "全部导出",
350
349
  "delete.all": "全部移至回收站",
351
350
  "import.action": "导入备份",
352
351
  "import.title": "导入归档备份",
@@ -366,7 +365,8 @@ window.__ModuleLoader__.load({
366
365
  "import.done": "备份恢复完成",
367
366
  "export.all": "全部导出",
368
367
  "export.selected": "导出选中项",
369
- "export.row": "导出备份",
368
+ "export.row": "导出本条",
369
+ "menu.row": "更多聊天操作",
370
370
  "export.started": "已开始下载备份",
371
371
  "search.placeholder": "搜索标题、标签、备注和聊天内容",
372
372
  "search.loading": "正在搜索聊天内容…",
@@ -406,11 +406,10 @@ window.__ModuleLoader__.load({
406
406
  "sort.newest": "最新优先",
407
407
  "sort.oldest": "最早优先",
408
408
  "sort.title": "按标题",
409
- "selection.visible": "选择当前结果",
409
+ "selection.projectVisible": "全选当前显示的聊天",
410
410
  "selection.group": "选择此项目",
411
411
  "selection.start": "批量选择",
412
412
  "selection.done": "完成",
413
- "selection.clear": "清除",
414
413
  "bulk.unarchive": "取消归档",
415
414
  "bulk.delete": "移至回收站",
416
415
  "group.noProject": "未分组",
@@ -601,7 +600,7 @@ window.__ModuleLoader__.load({
601
600
  "retention.preview": "Preview cleanup",
602
601
  "retention.apply": "Apply selected cleanup",
603
602
  "retention.empty": "The current policy has no cleanup candidates",
604
- "retention.saved": "Policy saved; no cleanup ran automatically",
603
+ "retention.saved": "Policy saved",
605
604
  "retention.note": "The retention count applies separately to each original chat. Saving never deletes data; preview and apply cleanup separately.",
606
605
  "retention.snapshotCandidate": "Historical snapshot cleanup",
607
606
  "retention.trashCandidate": "Permanently delete recycled chat",
@@ -649,8 +648,7 @@ window.__ModuleLoader__.load({
649
648
  "lineage.diagnostic.cycle": "Cycle detected",
650
649
  "lineage.diagnostic.delegation-depth-mismatch": "Delegation depth mismatch",
651
650
  "action.import": "Import backup",
652
- "action.export": "Export backup",
653
- "action.more": "More",
651
+ "action.export": "Export all",
654
652
  "delete.all": "Move all to Recycle Bin",
655
653
  "import.action": "Import backup",
656
654
  "import.title": "Import archived backup",
@@ -670,7 +668,8 @@ window.__ModuleLoader__.load({
670
668
  "import.done": "Backup restore completed",
671
669
  "export.all": "Export all",
672
670
  "export.selected": "Export selected",
673
- "export.row": "Export backup",
671
+ "export.row": "Export this chat",
672
+ "menu.row": "More chat actions",
674
673
  "export.started": "Backup download started",
675
674
  "search.placeholder": "Search titles, tags, notes, and conversation content",
676
675
  "search.loading": "Searching conversation content…",
@@ -710,11 +709,10 @@ window.__ModuleLoader__.load({
710
709
  "sort.newest": "Newest first",
711
710
  "sort.oldest": "Oldest first",
712
711
  "sort.title": "Title",
713
- "selection.visible": "Select visible chats",
712
+ "selection.projectVisible": "Select all visible chats",
714
713
  "selection.group": "Select this project",
715
714
  "selection.start": "Select multiple",
716
715
  "selection.done": "Done",
717
- "selection.clear": "Clear",
718
716
  "bulk.unarchive": "Unarchive",
719
717
  "bulk.delete": "Move to Recycle Bin",
720
718
  "group.noProject": "Ungrouped",
@@ -1219,6 +1217,14 @@ window.__ModuleLoader__.load({
1219
1217
  .dac-menu-item.dac-danger{color:var(--dsw-alias-state-error-primary)}
1220
1218
  .dac-menu-item.dac-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger)}
1221
1219
  .dac-list{display:flex;flex-direction:column;gap:8px}
1220
+ .dac-group>.dac-list{gap:0}
1221
+ .dac-group>.dac-list>.dac-row{border:0;border-bottom:1px solid var(--dsw-alias-border-l2);border-radius:0}
1222
+ .dac-row-more{position:relative;flex:none}
1223
+ .dac-row-more>summary{list-style:none}
1224
+ .dac-row-more>summary::-webkit-details-marker{display:none}
1225
+ .dac-row-more[open]{z-index:41}
1226
+ .dac-row-menu{top:34px;min-width:180px;max-width:calc(100vw - 48px)}
1227
+ .dac-row-menu .dac-menu-item{display:flex;align-items:center;gap:8px}
1222
1228
  .dac-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:12px 16px;transition:border-color .15s,background .15s}
1223
1229
  .dac-row:hover{border-color:var(--dsw-alias-border-l1,var(--dsw-alias-border-l2))}
1224
1230
  .dac-row.dac-selected{border-color:color-mix(in srgb,var(--dsw-alias-interactive-primary,#6e6ef7) 45%,var(--dsw-alias-border-l2));background:color-mix(in srgb,var(--dsw-alias-interactive-primary,#6e6ef7) 6%,var(--dsw-alias-bg-layer-1))}
@@ -2814,7 +2820,48 @@ window.__ModuleLoader__.load({
2814
2820
  ] }, group.key);
2815
2821
  }
2816
2822
 
2817
- function GroupSection({ group, t, collapsed, onToggleCollapsed, menuOpen, onToggleMenu, onUnarchive, onDelete, onExport, onPreview, contentHits, busy, selectionMode, selected, onToggleSelected, stats, metadataStatus, onEditMetadata }) {
2823
+ function ChatActionMenu({ t, title, children }) {
2824
+ const menuRef = _react.useRef(null);
2825
+ _react.useEffect(() => {
2826
+ const closeOutside = (event) => {
2827
+ const menu = menuRef.current;
2828
+ if (menu?.open && !menu.contains(event.target)) menu.open = false;
2829
+ };
2830
+ document.addEventListener("pointerdown", closeOutside);
2831
+ document.addEventListener("focusin", closeOutside);
2832
+ return () => {
2833
+ document.removeEventListener("pointerdown", closeOutside);
2834
+ document.removeEventListener("focusin", closeOutside);
2835
+ };
2836
+ }, []);
2837
+ return (0, jsx.jsxs)("details", {
2838
+ ref: menuRef,
2839
+ className: "dac-row-more",
2840
+ onKeyDown: (event) => {
2841
+ if (event.key === "Escape" && menuRef.current?.open) {
2842
+ event.preventDefault();
2843
+ event.stopPropagation();
2844
+ menuRef.current.open = false;
2845
+ menuRef.current.querySelector("summary")?.focus();
2846
+ }
2847
+ },
2848
+ children: [
2849
+ (0, jsx.jsx)("summary", { className: "dac-iconbtn", "aria-label": `${t("menu.row")}: ${title}`, title: t("menu.row"), children: (0, jsx.jsx)(IconDots, {}) }),
2850
+ (0, jsx.jsx)("div", {
2851
+ className: "dac-menu dac-row-menu",
2852
+ onClickCapture: (event) => {
2853
+ if (event.target.closest("button:not(:disabled)") && menuRef.current) {
2854
+ menuRef.current.open = false;
2855
+ menuRef.current.querySelector("summary")?.focus();
2856
+ }
2857
+ },
2858
+ children
2859
+ })
2860
+ ]
2861
+ });
2862
+ }
2863
+
2864
+ function GroupSection({ group, t, collapsed, onToggleCollapsed, menuOpen, onToggleMenu, onUnarchive, onDelete, onPermanentDelete, onExport, onPreview, contentHits, busy, selectionMode, selected, onToggleSelected, stats, metadataStatus, onEditMetadata }) {
2818
2865
  const wrapRef = _react.useRef(null);
2819
2866
  _react.useEffect(() => {
2820
2867
  if (!menuOpen) return void 0;
@@ -2823,9 +2870,10 @@ window.__ModuleLoader__.load({
2823
2870
  return () => document.removeEventListener("mousedown", onDown);
2824
2871
  }, [menuOpen, onToggleMenu]);
2825
2872
  const ids = group.selectionIds;
2826
- const selectedInGroup = ids.filter((id) => selected.has(id)).length;
2827
- const allSelected = ids.length > 0 && selectedInGroup === ids.length;
2828
- const someSelected = selectedInGroup > 0 && !allSelected;
2873
+ const visibleIds = group.items.map((session) => session.id);
2874
+ const selectedCount = visibleIds.filter((id) => selected.has(id)).length;
2875
+ const allSelected = visibleIds.length > 0 && selectedCount === visibleIds.length;
2876
+ const partiallySelected = selectedCount > 0 && !allSelected;
2829
2877
  const groupBusy = ids.some((id) => busy[id] === true);
2830
2878
  return (0, jsx.jsxs)("div", {
2831
2879
  className: "dac-group",
@@ -2837,13 +2885,6 @@ window.__ModuleLoader__.load({
2837
2885
  (0, jsx.jsxs)("div", {
2838
2886
  className: "dac-group-left",
2839
2887
  children: [
2840
- selectionMode && (0, jsx.jsx)(SelectionCheckbox, {
2841
- checked: allSelected,
2842
- indeterminate: someSelected,
2843
- disabled: groupBusy,
2844
- ariaLabel: selectProjectLabel(t, group.title),
2845
- onChange: (checked) => onToggleSelected(ids, checked)
2846
- }),
2847
2888
  (0, jsx.jsxs)("button", {
2848
2889
  type: "button",
2849
2890
  className: "dac-group-toggle",
@@ -2861,6 +2902,16 @@ window.__ModuleLoader__.load({
2861
2902
  (0, jsx.jsxs)("div", {
2862
2903
  className: "dac-group-side",
2863
2904
  children: [
2905
+ visibleIds.length > 0 && (0, jsx.jsx)(SelectionCheckbox, {
2906
+ checked: allSelected,
2907
+ indeterminate: partiallySelected,
2908
+ disabled: visibleIds.length === 0 || visibleIds.some((id) => busy[id] === true),
2909
+ ariaLabel: `${t("selection.projectVisible")}${isZh(t) ? ":" : ": "}${group.title}`,
2910
+ onChange: (checked) => {
2911
+ onToggleSelected(visibleIds, checked);
2912
+ if (checked && collapsed) onToggleCollapsed(group.key);
2913
+ }
2914
+ }),
2864
2915
  (0, jsx.jsx)("span", { className: "dac-count", children: chatsCount(t, group.items.length) }),
2865
2916
  (0, jsx.jsx)("button", {
2866
2917
  type: "button",
@@ -2879,6 +2930,7 @@ window.__ModuleLoader__.load({
2879
2930
  type: "button",
2880
2931
  className: "dac-menu-item",
2881
2932
  role: "menuitem",
2933
+ disabled: groupBusy,
2882
2934
  onClick: () => { onToggleMenu(null); onUnarchive(ids); },
2883
2935
  children: t("menu.unarchiveAll")
2884
2936
  }),
@@ -2886,8 +2938,17 @@ window.__ModuleLoader__.load({
2886
2938
  type: "button",
2887
2939
  className: "dac-menu-item dac-danger",
2888
2940
  role: "menuitem",
2941
+ disabled: groupBusy,
2889
2942
  onClick: () => { onToggleMenu(null); onDelete(ids, group.title); },
2890
2943
  children: t("menu.deleteAll")
2944
+ }),
2945
+ (0, jsx.jsx)("button", {
2946
+ type: "button",
2947
+ className: "dac-menu-item dac-danger",
2948
+ role: "menuitem",
2949
+ disabled: groupBusy,
2950
+ onClick: () => { onToggleMenu(null); onPermanentDelete(ids); },
2951
+ children: t("menu.deletePermanently")
2891
2952
  })
2892
2953
  ]
2893
2954
  })
@@ -2939,37 +3000,38 @@ window.__ModuleLoader__.load({
2939
3000
  onClick: () => onPreview(session),
2940
3001
  children: (0, jsx.jsx)(IconEye, {})
2941
3002
  }),
3003
+ (0, jsx.jsx)("button", {
3004
+ type: "button",
3005
+ className: "dac-iconbtn dac-danger",
3006
+ "aria-label": t("confirm.permanent.confirm"),
3007
+ title: t("confirm.permanent.confirm"),
3008
+ disabled: busy[session.id] === true,
3009
+ onClick: () => onPermanentDelete([session.id]),
3010
+ children: (0, jsx.jsx)(IconTrash, {})
3011
+ }),
3012
+ (0, jsx.jsx)("button", {
3013
+ type: "button", className: "dac-unarchive",
3014
+ disabled: busy[session.id] === true,
3015
+ onClick: () => onUnarchive([session.id]), children: t("chat.unarchive")
3016
+ }),
3017
+ (0, jsx.jsxs)(ChatActionMenu, { t, title: session.title ?? t("chat.untitled"), children: [
2942
3018
  (0, jsx.jsx)("button", {
2943
3019
  type: "button",
2944
- className: "dac-iconbtn",
3020
+ className: "dac-menu-item",
2945
3021
  "aria-label": t("tag.edit"),
2946
3022
  disabled: busy[session.id] === true || metadataStatus === "unavailable",
2947
3023
  onClick: () => onEditMetadata(session),
2948
- children: (0, jsx.jsx)(IconEdit, {})
2949
- }),
2950
- (0, jsx.jsx)("button", {
2951
- type: "button",
2952
- className: "dac-iconbtn",
2953
- "aria-label": t("export.row"),
2954
- disabled: busy[session.id] === true,
2955
- onClick: () => onExport([session.id]),
2956
- children: (0, jsx.jsx)(IconDownload, {})
2957
- }),
2958
- (0, jsx.jsx)("button", {
2959
- type: "button",
2960
- className: "dac-iconbtn dac-danger",
2961
- "aria-label": t("menu.deleteAll"),
2962
- disabled: busy[session.id] === true,
2963
- onClick: () => onDelete([session.id], null),
2964
- children: (0, jsx.jsx)(IconTrash, {})
3024
+ children: t("tag.edit")
2965
3025
  }),
2966
- (0, jsx.jsx)("button", {
2967
- type: "button",
2968
- className: "dac-unarchive",
2969
- disabled: busy[session.id] === true,
2970
- onClick: () => onUnarchive([session.id]),
2971
- children: t("chat.unarchive")
2972
- })
3026
+ (0, jsx.jsx)("button", {
3027
+ type: "button",
3028
+ className: "dac-menu-item",
3029
+ "aria-label": t("export.row"),
3030
+ disabled: busy[session.id] === true,
3031
+ onClick: () => onExport([session.id]),
3032
+ children: t("export.row")
3033
+ }),
3034
+ ] })
2973
3035
  ]
2974
3036
  })
2975
3037
  ]
@@ -3097,6 +3159,7 @@ window.__ModuleLoader__.load({
3097
3159
  const [selected, setSelected] = _react.useState(() => new Set());
3098
3160
  const [busy, setBusy] = _react.useState(false);
3099
3161
  const [message, setMessage] = _react.useState(null);
3162
+ const [messageKind, setMessageKind] = _react.useState("error");
3100
3163
  const [detailsMode, setDetailsMode] = _react.useState(null);
3101
3164
  const previewReturnFocusRef = _react.useRef(null);
3102
3165
  const detailsReturnFocusRef = _react.useRef(null);
@@ -3111,15 +3174,20 @@ window.__ModuleLoader__.load({
3111
3174
  } catch (error) { if (!signal?.aborted) setState({ status: "error", value: null, error }); }
3112
3175
  }, []);
3113
3176
  _react.useEffect(() => { const controller = new AbortController(); void load(controller.signal); return () => controller.abort(); }, [load]);
3177
+ _react.useEffect(() => {
3178
+ if (message === null || messageKind !== "ok") return undefined;
3179
+ const timer = setTimeout(() => setMessage(null), 3000);
3180
+ return () => clearTimeout(timer);
3181
+ }, [message, messageKind]);
3114
3182
  const setNumber = (key, value, nullable) => setDraft((current) => ({ ...current, [key]: nullable && value === "" ? null : Number(value) }));
3115
3183
  const save = async () => {
3116
- setBusy(true); setMessage(null);
3117
- try { const result = await saveRetentionPolicy(draft); setDraft({ ...(result.policy ?? draft) }); setMessage(t("retention.saved")); await load(); }
3184
+ setBusy(true); setMessage(null); setMessageKind("error");
3185
+ try { const result = await saveRetentionPolicy(draft); setDraft({ ...(result.policy ?? draft) }); setMessageKind("ok"); setMessage(t("retention.saved")); await load(); }
3118
3186
  catch (error) { setMessage(String(error.message ?? error)); }
3119
3187
  finally { setBusy(false); }
3120
3188
  };
3121
3189
  const openPreview = async () => {
3122
- setBusy(true); setMessage(null);
3190
+ setBusy(true); setMessage(null); setMessageKind("error");
3123
3191
  try { previewReturnFocusRef.current = document.activeElement; const value = await previewRetention(); setPreview(value); setSelected(defaultRetentionSelection(value.candidates)); }
3124
3192
  catch (error) { setMessage(String(error.message ?? error)); }
3125
3193
  finally { setBusy(false); }
@@ -3129,7 +3197,7 @@ window.__ModuleLoader__.load({
3129
3197
  setDetailsMode(mode);
3130
3198
  };
3131
3199
  const applySelected = async () => {
3132
- setBusy(true);
3200
+ setBusy(true); setMessage(null); setMessageKind("error");
3133
3201
  try { const result = await applyRetention(preview.token, preview.nonce, [...selected]); const applied = result?.applied ?? []; const failed = result?.failed ?? []; setMessage(`${t("retention.result")}: ${applied.length} / ${failed.length}${failed.length > 0 ? ` · ${failed.map((item) => `${item.key}: ${item.reason}`).join(" · ")}` : ""}`); setPreview(null); setSelected(new Set()); await load(); }
3134
3202
  catch (error) { const body = error.body ?? {}; const applied = body.applied ?? []; const failed = body.failed ?? []; setMessage(failed.length > 0 ? `${t("retention.result")}: ${applied.length} / ${failed.length} · ${failed.map((item) => `${item.key}: ${item.reason}`).join(" · ")}` : String(error.message ?? error)); setPreview(null); setSelected(new Set()); }
3135
3203
  finally { setBusy(false); }
@@ -3151,7 +3219,7 @@ window.__ModuleLoader__.load({
3151
3219
  (0, jsx.jsx)("strong", { children: display }),
3152
3220
  mode && (0, jsx.jsx)("button", { type: "button", className: "dac-insights-open", style: { alignSelf: "center" }, "aria-label": t(actionLabel), disabled: count === 0, onClick: () => openDetails(mode), children: t("insights.details") })
3153
3221
  ] }, label)) }),
3154
- message && (0, jsx.jsx)("div", { className: "dac-notice", role: "status", children: message }),
3222
+ message && (0, jsx.jsx)("div", { className: messageKind === "ok" ? "dac-toast" : "dac-notice", role: "status", children: message }),
3155
3223
  draft && (0, jsx.jsxs)("div", { className: "dac-policy", children: [
3156
3224
  (0, jsx.jsxs)("label", { children: [(0, jsx.jsx)("span", { children: t("retention.history") }), (0, jsx.jsx)("input", { type: "number", min: 0, max: 20, value: draft.historicalSnapshotsPerSession, onChange: (event) => setNumber("historicalSnapshotsPerSession", event.target.value, false) })] }),
3157
3225
  (0, jsx.jsxs)("label", { children: [(0, jsx.jsx)("span", { children: t("retention.snapshotAge") }), (0, jsx.jsx)("input", { type: "number", min: 1, max: 3650, placeholder: t("retention.disabled"), value: draft.historicalSnapshotMaxAgeDays ?? "", onChange: (event) => setNumber("historicalSnapshotMaxAgeDays", event.target.value, true) })] }),
@@ -3657,8 +3725,7 @@ window.__ModuleLoader__.load({
3657
3725
  const [metaBusy, setMetaBusy] = _react.useState(false);
3658
3726
  const [importPreview, setImportPreview] = _react.useState(null);
3659
3727
  const [importBusy, setImportBusy] = _react.useState(false);
3660
- const [selectionMode, setSelectionMode] = _react.useState(false);
3661
- const [actionMenu, setActionMenu] = _react.useState(null);
3728
+ const selectionMode = selected.size > 0;
3662
3729
  const [contentSearch, setContentSearch] = _react.useState({ status: "idle", query: "", hits: [], skipped: [] });
3663
3730
  const [preview, setPreview] = _react.useState(null);
3664
3731
  const [previewBusy, setPreviewBusy] = _react.useState(false);
@@ -3667,8 +3734,6 @@ window.__ModuleLoader__.load({
3667
3734
  const [workspaceArchiveRefresh, setWorkspaceArchiveRefresh] = _react.useState(0);
3668
3735
  const pageRef = _react.useRef(null);
3669
3736
  const pageHeadingRef = _react.useRef(null);
3670
- const actionMenuRef = _react.useRef(null);
3671
- const moreActionRef = _react.useRef(null);
3672
3737
  const deleteReturnFocusRef = _react.useRef(null);
3673
3738
  const trashReturnFocusRef = _react.useRef(null);
3674
3739
  const metaReturnFocusRef = _react.useRef(null);
@@ -3714,25 +3779,6 @@ window.__ModuleLoader__.load({
3714
3779
  previewRequestRef.current.controller?.abort();
3715
3780
  }, []);
3716
3781
 
3717
- _react.useEffect(() => {
3718
- if (actionMenu === null) return undefined;
3719
- const onDown = (event) => {
3720
- if (actionMenuRef.current && !actionMenuRef.current.contains(event.target)) setActionMenu(null);
3721
- };
3722
- document.addEventListener("mousedown", onDown);
3723
- return () => {
3724
- document.removeEventListener("mousedown", onDown);
3725
- };
3726
- }, [actionMenu]);
3727
-
3728
- const closeActionMenuFromKeyboard = (event) => {
3729
- if (event.key !== "Escape" || actionMenu === null) return;
3730
- event.preventDefault();
3731
- event.stopPropagation();
3732
- setActionMenu(null);
3733
- moreActionRef.current?.focus?.();
3734
- };
3735
-
3736
3782
  _react.useEffect(() => {
3737
3783
  if (sessions === null || loadError !== null) return undefined;
3738
3784
  return markArchiveDialog(pageRef.current);
@@ -3867,9 +3913,8 @@ window.__ModuleLoader__.load({
3867
3913
  const toggleSelected = (ids, checked) => {
3868
3914
  setSelected((current) => setVisibleSelection(current, ids, checked));
3869
3915
  };
3870
- const finishSelectionMode = () => {
3916
+ const clearArchiveSelection = () => {
3871
3917
  setSelected(new Set());
3872
- setSelectionMode(false);
3873
3918
  };
3874
3919
 
3875
3920
  const markBusy = (ids, value) => {
@@ -3990,7 +4035,7 @@ window.__ModuleLoader__.load({
3990
4035
  });
3991
4036
  };
3992
4037
 
3993
- const unarchive = async (ids, finishSelectionAfter = false) => {
4038
+ const unarchive = async (ids) => {
3994
4039
  if (ids.length === 0) return;
3995
4040
  markBusy(ids, true);
3996
4041
  setNotice(null);
@@ -3998,7 +4043,6 @@ window.__ModuleLoader__.load({
3998
4043
  await post("/unarchive-all", { sessionIds: ids });
3999
4044
  setSessions((prev) => (prev ?? []).filter((s) => !ids.includes(s.id)));
4000
4045
  setSelected((current) => setVisibleSelection(current, ids, false));
4001
- if (finishSelectionAfter) finishSelectionMode();
4002
4046
  } catch (error) {
4003
4047
  setNotice({ kind: "error", text: String(error.message ?? error) });
4004
4048
  } finally {
@@ -4006,7 +4050,7 @@ window.__ModuleLoader__.load({
4006
4050
  }
4007
4051
  };
4008
4052
 
4009
- const runDelete = async (ids, finishSelectionAfter = false) => {
4053
+ const runDelete = async (ids) => {
4010
4054
  if (ids.length === 0) return;
4011
4055
  markBusy(ids, true);
4012
4056
  setNotice(null);
@@ -4023,7 +4067,6 @@ window.__ModuleLoader__.load({
4023
4067
  }
4024
4068
  if (failed.length > 0) setNotice({ kind: "error", text: failureText(t, failed) });
4025
4069
  else if (trashed.length > 0) setNotice({ kind: "ok", text: t("trash.moved"), action: "undo-trash", ids: trashed });
4026
- if (finishSelectionAfter && failed.length === 0 && trashed.length === ids.length) finishSelectionMode();
4027
4070
  } catch (error) {
4028
4071
  const trashed = uniqueSessionIds(error.body?.trashed);
4029
4072
  const failed = error.body?.failed ?? [];
@@ -4045,7 +4088,7 @@ window.__ModuleLoader__.load({
4045
4088
  }
4046
4089
  };
4047
4090
 
4048
- const runPermanentDelete = async (ids, finishSelectionAfter = false) => {
4091
+ const runPermanentDelete = async (ids) => {
4049
4092
  if (ids.length === 0) return;
4050
4093
  markBusy(ids, true);
4051
4094
  setNotice(null);
@@ -4061,7 +4104,6 @@ window.__ModuleLoader__.load({
4061
4104
  }
4062
4105
  if (failed.length > 0) setNotice({ kind: "error", text: failureText(t, failed) });
4063
4106
  else if (deleted.length > 0) setNotice({ kind: "ok", text: t("trash.purged") });
4064
- if (finishSelectionAfter && failed.length === 0 && deleted.length === ids.length) finishSelectionMode();
4065
4107
  } catch (error) {
4066
4108
  const deleted = uniqueSessionIds(error.body?.deleted);
4067
4109
  const failed = error.body?.failed ?? [];
@@ -4103,7 +4145,7 @@ window.__ModuleLoader__.load({
4103
4145
  if (ids.length === 0) return;
4104
4146
  deleteReturnFocusRef.current = document.activeElement;
4105
4147
  if (selectedScope) {
4106
- setConfirm({ title: t("confirm.deleteSelected.title"), body: deleteSelectedBody(t, ids.length), ids, finishSelectionAfter: true });
4148
+ setConfirm({ title: t("confirm.deleteSelected.title"), body: deleteSelectedBody(t, ids.length), ids });
4107
4149
  } else if (ids.length === 1 && groupName === null) {
4108
4150
  setConfirm({ title: t("confirm.deleteOne.title"), body: t("confirm.deleteOne.body"), ids });
4109
4151
  } else if (groupName !== null && groupName !== void 0) {
@@ -4113,7 +4155,7 @@ window.__ModuleLoader__.load({
4113
4155
  }
4114
4156
  };
4115
4157
 
4116
- const askPermanentDelete = (ids, finishSelectionAfter = false) => {
4158
+ const askPermanentDelete = (ids) => {
4117
4159
  if (ids.length === 0) return;
4118
4160
  deleteReturnFocusRef.current = document.activeElement;
4119
4161
  setConfirm({
@@ -4121,7 +4163,6 @@ window.__ModuleLoader__.load({
4121
4163
  body: `${t("confirm.permanent.body")} ${isZh(t) ? `共 ${ids.length} 个聊天。` : `${ids.length} chats selected.`}`,
4122
4164
  ids,
4123
4165
  permanent: true,
4124
- finishSelectionAfter,
4125
4166
  });
4126
4167
  };
4127
4168
 
@@ -4364,13 +4405,8 @@ window.__ModuleLoader__.load({
4364
4405
  }
4365
4406
 
4366
4407
  const allIds = (sessions ?? []).map((s) => s.id);
4367
- const visibleIds = groups.flatMap((group) => group.items.map((session) => session.id));
4368
4408
  const selectedIds = allIds.filter((id) => selected.has(id));
4369
- const selectedVisibleCount = visibleIds.filter((id) => selected.has(id)).length;
4370
- const allVisibleSelected = visibleIds.length > 0 && selectedVisibleCount === visibleIds.length;
4371
- const someVisibleSelected = selectedVisibleCount > 0 && !allVisibleSelected;
4372
4409
  const selectedBusy = selectedIds.some((id) => busy[id] === true);
4373
- const visibleBusy = visibleIds.some((id) => busy[id] === true);
4374
4410
  const allBusy = allIds.some((id) => busy[id] === true);
4375
4411
  const filtering = query.trim() !== "" || projectFilter !== "all" || typeFilter !== "all" || tagFilter !== "";
4376
4412
  const trashGroups = groupTrashSessions(trash.sessions);
@@ -4389,54 +4425,28 @@ window.__ModuleLoader__.load({
4389
4425
  className: "dac-head",
4390
4426
  children: [
4391
4427
  (0, jsx.jsx)("h2", { ref: pageHeadingRef, tabIndex: -1, className: "dac-title", children: t("page.title") }),
4392
- pageMode === "archived" && (0, jsx.jsxs)("div", {
4393
- ref: actionMenuRef,
4394
- className: "dac-head-actions",
4395
- onKeyDown: closeActionMenuFromKeyboard,
4396
- children: [
4397
- (0, jsx.jsx)("div", { className: "dac-action-wrap", children:
4398
- (0, jsx.jsxs)("button", {
4399
- type: "button",
4400
- className: "dac-action-trigger",
4401
- onClick: openWorkspaceArchive,
4402
- children: [(0, jsx.jsx)(IconArchive, { size: 15 }), (0, jsx.jsx)("span", { children: t("workspaceArchive.settingsAction") })]
4403
- })
4404
- }),
4405
- (0, jsx.jsx)("div", { className: "dac-action-wrap", children:
4406
- (0, jsx.jsxs)("button", {
4407
- type: "button",
4408
- className: "dac-action-trigger",
4409
- disabled: importBusy,
4410
- onClick: () => importInputRef.current?.click?.(),
4411
- children: [(0, jsx.jsx)(IconUpload, {}), (0, jsx.jsx)("span", { children: t("action.import") })]
4412
- })
4428
+ pageMode === "archived" && (0, jsx.jsxs)("div", {
4429
+ className: "dac-head-actions",
4430
+ children: [
4431
+ (0, jsx.jsxs)("button", {
4432
+ type: "button",
4433
+ className: "dac-action-trigger",
4434
+ onClick: openWorkspaceArchive,
4435
+ children: [(0, jsx.jsx)(IconArchive, { size: 15 }), (0, jsx.jsx)("span", { children: t("workspaceArchive.settingsAction") })]
4413
4436
  }),
4414
- (0, jsx.jsx)("div", { className: "dac-action-wrap", children:
4415
- (0, jsx.jsxs)("button", {
4416
- type: "button",
4417
- className: "dac-action-trigger",
4418
- disabled: allIds.length === 0 || allBusy,
4419
- onClick: () => { const didExport = exportSessions(selectedIds.length > 0 ? selectedIds : allIds); if (didExport && selectedIds.length > 0) finishSelectionMode(); },
4420
- children: [(0, jsx.jsx)(IconDownload, {}), (0, jsx.jsx)("span", { children: t("action.export") })]
4421
- })
4422
- }),
4423
- (0, jsx.jsxs)("div", { className: "dac-action-wrap", children: [
4424
- (0, jsx.jsxs)("button", {
4425
- ref: moreActionRef,
4426
- type: "button",
4427
- className: "dac-action-trigger",
4428
- disabled: allIds.length === 0,
4429
- "aria-controls": "dac-more-actions",
4430
- "aria-expanded": actionMenu === "more",
4431
- onClick: () => setActionMenu((current) => current === "more" ? null : "more"),
4432
- children: [(0, jsx.jsx)(IconDots, {}), (0, jsx.jsx)("span", { children: t("action.more") })]
4433
- }),
4434
- actionMenu === "more" && (0, jsx.jsxs)("div", { id: "dac-more-actions", className: "dac-action-menu", children: [
4435
- (0, jsx.jsx)("button", { type: "button", className: "dac-action-menu-item dac-danger", onClick: () => { setActionMenu(null); askDelete(allIds, void 0); }, children: t("delete.all") }),
4436
- (0, jsx.jsx)("button", { type: "button", className: "dac-action-menu-item dac-danger", onClick: () => { setActionMenu(null); askPermanentDelete(allIds); }, children: t("menu.deletePermanently") })
4437
- ] })
4438
- ] })
4439
- ]
4437
+ (0, jsx.jsxs)("button", {
4438
+ type: "button",
4439
+ className: "dac-action-trigger",
4440
+ disabled: importBusy,
4441
+ onClick: () => importInputRef.current?.click?.(),
4442
+ children: [(0, jsx.jsx)(IconUpload, {}), (0, jsx.jsx)("span", { children: t("action.import") })]
4443
+ }),
4444
+ (0, jsx.jsxs)("button", {
4445
+ type: "button",
4446
+ className: "dac-action-trigger",
4447
+ disabled: allIds.length === 0 || allBusy,
4448
+ onClick: () => exportSessions(allIds),
4449
+ children: [(0, jsx.jsx)(IconDownload, {}), (0, jsx.jsx)("span", { children: t("action.export") })]
4440
4450
  }),
4441
4451
  (0, jsx.jsx)("input", {
4442
4452
  ref: importInputRef,
@@ -4444,9 +4454,11 @@ window.__ModuleLoader__.load({
4444
4454
  accept: ".zip,application/zip",
4445
4455
  hidden: true,
4446
4456
  onChange: (event) => { const file = event.target.files?.[0]; void importFile(file); event.target.value = ""; }
4447
- }),
4457
+ })
4448
4458
  ]
4449
- }),
4459
+ })
4460
+ ]
4461
+ }),
4450
4462
  (0, jsx.jsxs)("div", { className: "dac-tabs", role: "tablist", "aria-label": t("page.title"), children: [
4451
4463
  (0, jsx.jsx)("button", { type: "button", role: "tab", className: "dac-tab", "aria-selected": pageMode === "archived", onClick: () => setPageMode("archived"), children: t("tab.archived") }),
4452
4464
  (0, jsx.jsx)("button", { type: "button", role: "tab", className: "dac-tab", "aria-selected": pageMode === "history", onClick: () => setPageMode("history"), children: t("tab.history") }),
@@ -4483,7 +4495,7 @@ window.__ModuleLoader__.load({
4483
4495
  type: "text",
4484
4496
  placeholder: t("search.placeholder"),
4485
4497
  value: query,
4486
- onChange: (e) => setQuery(e.target.value)
4498
+ onChange: (e) => { clearArchiveSelection(); setQuery(e.target.value); }
4487
4499
  }),
4488
4500
  contentSearch.status === "loading" && (0, jsx.jsx)("span", { className: "dac-search-state", role: "status", children: t("search.loading") }),
4489
4501
  contentSearch.status === "error" && (0, jsx.jsx)("span", { className: "dac-search-state dac-error", role: "status", children: t("search.error") })
@@ -4494,7 +4506,7 @@ window.__ModuleLoader__.load({
4494
4506
  children: [
4495
4507
  (0, jsx.jsx)(Select, {
4496
4508
  value: typeFilter,
4497
- onChange: setTypeFilter,
4509
+ onChange: (value) => { clearArchiveSelection(); setTypeFilter(value); },
4498
4510
  ariaLabel: t("filter.allChats"),
4499
4511
  options: [
4500
4512
  { value: "all", label: t("filter.allChats") },
@@ -4504,7 +4516,7 @@ window.__ModuleLoader__.load({
4504
4516
  }),
4505
4517
  (0, jsx.jsx)(Select, {
4506
4518
  value: projectFilter,
4507
- onChange: setProjectFilter,
4519
+ onChange: (value) => { clearArchiveSelection(); setProjectFilter(value); },
4508
4520
  ariaLabel: t("filter.allProjects"),
4509
4521
  options: [
4510
4522
  { value: "all", label: t("filter.allProjects") },
@@ -4513,7 +4525,7 @@ window.__ModuleLoader__.load({
4513
4525
  }),
4514
4526
  (0, jsx.jsx)(Select, {
4515
4527
  value: tagFilter,
4516
- onChange: setTagFilter,
4528
+ onChange: (value) => { clearArchiveSelection(); setTagFilter(value); },
4517
4529
  ariaLabel: t("tag.filter"),
4518
4530
  options: [
4519
4531
  { value: "", label: t("tag.filter") },
@@ -4530,30 +4542,6 @@ window.__ModuleLoader__.load({
4530
4542
  { value: "title", label: t("sort.title") }
4531
4543
  ]
4532
4544
  }),
4533
- (visibleIds.length > 0 || selectionMode) && (0, jsx.jsxs)("div", {
4534
- className: "dac-selection-controls",
4535
- children: [
4536
- selectionMode && visibleIds.length > 0 && (0, jsx.jsxs)("label", {
4537
- className: "dac-selection-toggle",
4538
- children: [
4539
- (0, jsx.jsx)(SelectionCheckbox, {
4540
- checked: allVisibleSelected,
4541
- indeterminate: someVisibleSelected,
4542
- disabled: visibleBusy,
4543
- ariaLabel: t("selection.visible"),
4544
- onChange: (checked) => toggleSelected(visibleIds, checked)
4545
- }),
4546
- (0, jsx.jsx)("span", { children: t("selection.visible") })
4547
- ]
4548
- }),
4549
- (0, jsx.jsx)("button", {
4550
- type: "button",
4551
- className: "dac-selection-mode",
4552
- onClick: selectionMode ? finishSelectionMode : () => setSelectionMode(true),
4553
- children: t(selectionMode ? "selection.done" : "selection.start")
4554
- })
4555
- ]
4556
- })
4557
4545
  ]
4558
4546
  }),
4559
4547
  selectedIds.length > 0 && (0, jsx.jsxs)("div", {
@@ -4569,14 +4557,14 @@ window.__ModuleLoader__.load({
4569
4557
  type: "button",
4570
4558
  className: "dac-bulk-btn",
4571
4559
  disabled: selectedBusy,
4572
- onClick: () => { if (exportSessions(selectedIds)) finishSelectionMode(); },
4560
+ onClick: () => exportSessions(selectedIds),
4573
4561
  children: t("export.selected")
4574
4562
  }),
4575
4563
  (0, jsx.jsx)("button", {
4576
4564
  type: "button",
4577
4565
  className: "dac-bulk-btn",
4578
4566
  disabled: selectedBusy,
4579
- onClick: () => unarchive(selectedIds, true),
4567
+ onClick: () => unarchive(selectedIds),
4580
4568
  children: t("bulk.unarchive")
4581
4569
  }),
4582
4570
  (0, jsx.jsx)("button", {
@@ -4590,15 +4578,9 @@ window.__ModuleLoader__.load({
4590
4578
  type: "button",
4591
4579
  className: "dac-bulk-btn dac-danger",
4592
4580
  disabled: selectedBusy,
4593
- onClick: () => askPermanentDelete(selectedIds, true),
4594
- children: t("menu.deletePermanently")
4581
+ onClick: () => askPermanentDelete(selectedIds),
4582
+ children: t("confirm.permanent.confirm")
4595
4583
  }),
4596
- (0, jsx.jsx)("button", {
4597
- type: "button",
4598
- className: "dac-bulk-btn",
4599
- onClick: () => setSelected(new Set()),
4600
- children: t("selection.clear")
4601
- })
4602
4584
  ]
4603
4585
  })
4604
4586
  ]
@@ -4639,6 +4621,7 @@ window.__ModuleLoader__.load({
4639
4621
  onToggleMenu: setMenuFor,
4640
4622
  onUnarchive: unarchive,
4641
4623
  onDelete: askDelete,
4624
+ onPermanentDelete: askPermanentDelete,
4642
4625
  onExport: exportSessions,
4643
4626
  onPreview: openPreview,
4644
4627
  contentHits,
@@ -4750,7 +4733,7 @@ window.__ModuleLoader__.load({
4750
4733
  busy: confirm.ids.some((id) => busy[id] === true),
4751
4734
  returnFocus: deleteReturnFocusRef.current,
4752
4735
  fallbackFocusRef: pageHeadingRef,
4753
- onConfirm: () => confirm.permanent ? runPermanentDelete(confirm.ids, confirm.finishSelectionAfter === true) : runDelete(confirm.ids, confirm.finishSelectionAfter === true),
4736
+ onConfirm: () => confirm.permanent ? runPermanentDelete(confirm.ids) : runDelete(confirm.ids),
4754
4737
  onCancel: () => setConfirm(null)
4755
4738
  }),
4756
4739
  trashConfirm !== null && (0, jsx.jsx)(ConfirmDialog, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-archived-chats",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "DeepSeek Harness 会话档案:从设置中安全批量归档工作区会话,按工作区浏览和全文搜索归档聊天,原生只读预览对话、工具活动和已存储图片,管理标签备注与 ZIP 备份恢复;保留历史版本并恢复为副本,提供可撤销回收站、空间分账、保留策略和来源与分支。所有数据留在本机。 Session Archive for DeepSeek Harness: safely bulk-archive workspace chats from Settings; browse and full-text search archived chats; natively preview conversations, tool activity, and stored images read-only; manage tags, notes, and ZIP backup restore; retain local History with restore-as-copy, an undoable Recycle Bin, storage accounting, retention policies, and Origins & Branches. All data stays local.",
5
5
  "license": "MIT",
6
6
  "author": "Ultronen",