@michengai/dsh-archive-manager 0.1.41 → 0.1.43

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 CHANGED
@@ -11,11 +11,15 @@
11
11
  return error instanceof ArchiveNavigationError ? t(`archives.${error.code}`) : String(error?.message ?? error);
12
12
  }
13
13
  async function openArchivedConversation(actions, sessionId, restore, isActive = () => true) {
14
- if (restore) await actions.restore(sessionId);
14
+ if (restore) {
15
+ if (typeof actions.prepare === "function") await actions.prepare(sessionId);
16
+ if (!isActive()) return;
17
+ await actions.restore(sessionId);
18
+ }
15
19
  if (isActive()) await actions.open(sessionId);
16
20
  }
17
21
  function allowArchivedNavigation(navigation, sessions, workspaces, { onOpened = () => {
18
- }, warn = (...args) => console.warn(...args) } = {}) {
22
+ }, beginNavigation, warn = (...args) => console.warn(...args) } = {}) {
19
23
  let allowed;
20
24
  const original = navigation?.clearArchivedCurrent;
21
25
  const descriptor = navigation && Object.getOwnPropertyDescriptor(navigation, "clearArchivedCurrent");
@@ -42,6 +46,7 @@
42
46
  warn("archive-manager: \u5F52\u6863\u5BFC\u822A\u9002\u914D\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u66FF\u6362\u3002");
43
47
  throw new ArchiveNavigationError("navigationUnavailable");
44
48
  }
49
+ beginNavigation?.();
45
50
  sessions.open(id);
46
51
  if (sessions.list.getSnapshot().current !== id) {
47
52
  warn("archive-manager: \u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\u3002", { cleanupAvailable: typeof original === "function" });
@@ -468,12 +473,6 @@
468
473
  return value;
469
474
  }
470
475
  };
471
- const workspaceIdSchema = {
472
- parse(value) {
473
- if (typeof value !== "string" || value.length === 0) throw new TypeError(`workspaceId must be a non-empty string, got ${String(value)}`);
474
- return value;
475
- }
476
- };
477
476
  const archivedSetSchema = {
478
477
  parse(value) {
479
478
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
@@ -497,22 +496,6 @@
497
496
  throw new TypeError("target.scope must be all, ungrouped, workspace with a non-empty workspaceId, or sessions with non-empty sessionIds");
498
497
  }
499
498
  };
500
- const unarchivedBatchSchema = {
501
- parse(value) {
502
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
503
- if (!Array.isArray(value.archivedSessionIds) || value.archivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIds must be a string array");
504
- if (!Array.isArray(value.unarchivedSessionIds) || value.unarchivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("unarchivedSessionIds must be a string array");
505
- return value;
506
- }
507
- };
508
- const archivedWorkspaceBatchSchema = {
509
- parse(value) {
510
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
511
- if (!Array.isArray(value.archivedSessionIds) || value.archivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIds must be a string array");
512
- if (!Array.isArray(value.archivedSessionIdsAdded) || value.archivedSessionIdsAdded.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIdsAdded must be a string array");
513
- return value;
514
- }
515
- };
516
499
  const deletedBatchSchema = {
517
500
  parse(value) {
518
501
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
@@ -572,44 +555,6 @@
572
555
  },
573
556
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
574
557
  },
575
- {
576
- id: "@michengai/dsh-archive-manager#workspaceRegistry/unarchiveSessions",
577
- service: "workspaceRegistry",
578
- namespace: "workspaceRegistry",
579
- method: "unarchiveSessions",
580
- invocation: { kind: "direct" },
581
- parameters: [{
582
- name: "target",
583
- wire: "target",
584
- source: "json",
585
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedBatchTarget", schema: archivedBatchTargetSchema }
586
- }],
587
- result: {
588
- mode: "strict",
589
- typeSymbol: "@michengai/dsh-archive-manager/types#UnarchivedBatch",
590
- schema: unarchivedBatchSchema
591
- },
592
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
593
- },
594
- {
595
- id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveWorkspaceSessions",
596
- service: "workspaceRegistry",
597
- namespace: "workspaceRegistry",
598
- method: "archiveWorkspaceSessions",
599
- invocation: { kind: "direct" },
600
- parameters: [{
601
- name: "workspaceId",
602
- wire: "workspaceId",
603
- source: "json",
604
- codec: { mode: "strict", typeSymbol: "@deepseek-ai/dsh-workspace/types#WorkspaceId", schema: workspaceIdSchema }
605
- }],
606
- result: {
607
- mode: "strict",
608
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
609
- schema: archivedWorkspaceBatchSchema
610
- },
611
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
612
- },
613
558
  {
614
559
  id: "@michengai/dsh-archive-manager#workspaceRegistry/deleteArchivedSessions",
615
560
  service: "workspaceRegistry",
@@ -686,7 +631,23 @@
686
631
  return indexed;
687
632
  }
688
633
  const FLAT_SESSION_ORDER_KEY = "__flat_session_order__";
634
+ const WORKSPACE_VIEW_PERSIST_KEY = "dsh.archive-manager.workspace.view.v1";
635
+ const LEGACY_WORKSPACE_VIEW_PERSIST_KEY = "dsh.workspace.view.v5";
636
+ function migrateWorkspaceViewPersist(storage) {
637
+ const target = storage ?? globalThis.localStorage;
638
+ if (target == null || typeof target.getItem !== "function" || typeof target.setItem !== "function") return false;
639
+ try {
640
+ if (target.getItem(WORKSPACE_VIEW_PERSIST_KEY) != null) return false;
641
+ const legacy = target.getItem(LEGACY_WORKSPACE_VIEW_PERSIST_KEY);
642
+ if (legacy == null) return false;
643
+ target.setItem(WORKSPACE_VIEW_PERSIST_KEY, legacy);
644
+ return true;
645
+ } catch {
646
+ return false;
647
+ }
648
+ }
689
649
  function createWorkspaceViewStore() {
650
+ migrateWorkspaceViewPersist();
690
651
  return (0, _deepseek_ai_dsh_client_store.defineStore)({
691
652
  init: () => ({
692
653
  groupBy: "workspace",
@@ -696,7 +657,7 @@
696
657
  sessionOrderByAccount: {},
697
658
  sessionUpdatedAtByAccount: {}
698
659
  }),
699
- persist: "dsh.workspace.view.v5",
660
+ persist: WORKSPACE_VIEW_PERSIST_KEY,
700
661
  actions: {
701
662
  setGroupBy: (d, mode) => {
702
663
  d.groupBy = mode;
@@ -729,9 +690,27 @@
729
690
  function GithubMark16() {
730
691
  return (0, react_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", width: 16, height: 16, "aria-hidden": true, focusable: "false", children: (0, react_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M8 0a8 8 0 0 0-2.53 15.59c.4.074.547-.173.547-.385 0-.19-.007-.693-.01-1.36-2.226.484-2.695-1.073-2.695-1.073-.364-.924-.89-1.17-.89-1.17-.726-.496.055-.486.055-.486.803.056 1.225.824 1.225.824.714 1.223 1.872.87 2.328.665.072-.517.28-.87.508-1.07-1.777-.202-3.645-.888-3.645-3.956 0-.874.31-1.588.823-2.148-.083-.202-.357-1.017.078-2.12 0 0 .672-.215 2.2.82A7.65 7.65 0 0 1 8 4.8c.68.003 1.365.092 2.004.27 1.527-1.035 2.197-.82 2.197-.82.437 1.103.162 1.918.08 2.12.513.56.822 1.274.822 2.148 0 3.076-1.872 3.752-3.654 3.95.288.248.544.735.544 1.482 0 1.07-.01 1.932-.01 2.195 0 .214.144.463.55.384A8.001 8.001 0 0 0 8 0Z" }) });
731
692
  }
732
- function ArchivedSessionsSection({ sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, viewState, close, t }) {
693
+ function ArchivedSessionsSection({ archiveSessions, sessionStore, workspaceStore, unarchiveSession, deleteSession, unarchiveSessions, deleteArchivedSessions, archivedSessionMetadata, openConversation, focusSessionWorkspace, viewState, close, t }) {
733
694
  const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
734
695
  const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
696
+ const [archiveTab, setArchiveTab] = (0, react.useState)("archived");
697
+ const isArchived = archiveTab === "archived";
698
+ const [archiveTarget, setArchiveTarget] = (0, react.useState)(null);
699
+ const [archiveGroup, setArchiveGroup] = (0, react.useState)(null);
700
+ const requestArchive = (ids, group = null) => {
701
+ setError(null);
702
+ setNotice(null);
703
+ setArchiveGroup(group);
704
+ setArchiveTarget(ids);
705
+ };
706
+ const closeArchive = () => {
707
+ if (!busy) {
708
+ setArchiveTarget(null);
709
+ setArchiveGroup(null);
710
+ setError(null);
711
+ }
712
+ };
713
+ const archiveBusy = (0, react.useRef)(false);
735
714
  const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
736
715
  const [busy, setBusy] = (0, react.useState)(false);
737
716
  const [error, setError] = (0, react.useState)(null);
@@ -756,13 +735,29 @@
756
735
  navigationPending.current = true;
757
736
  setBusy(true);
758
737
  setError(null);
738
+ let restored = false;
759
739
  try {
760
- await openArchivedConversation({ restore: unarchiveSession, open: async (id) => {
761
- await openConversation(id);
762
- if (navigationActive.current) close?.();
763
- } }, session.id, restore, () => navigationActive.current);
740
+ await openArchivedConversation({
741
+ prepare: restore ? async (id) => {
742
+ await focusSessionWorkspace?.(id);
743
+ } : void 0,
744
+ restore: async (id) => {
745
+ await unarchiveSession(id);
746
+ restored = true;
747
+ },
748
+ open: async (id) => {
749
+ await openConversation(id);
750
+ if (navigationActive.current) close?.();
751
+ }
752
+ }, session.id, restore, () => navigationActive.current);
764
753
  } catch (reason) {
765
- if (navigationActive.current) setError(formatArchiveNavigationError(reason, t));
754
+ if (!navigationActive.current) return;
755
+ if (restored) {
756
+ setArchiveTab("unarchived");
757
+ const group = groups.find((item) => item.sessions.some((row) => row.id === session.id));
758
+ if (group !== void 0) setProject(group.key);
759
+ }
760
+ setError(formatArchiveNavigationError(reason, t));
766
761
  } finally {
767
762
  navigationPending.current = false;
768
763
  if (navigationActive.current) setBusy(false);
@@ -771,7 +766,42 @@
771
766
  (0, react.useEffect)(() => {
772
767
  if (viewState) Object.assign(viewState, { query, project, sortBy });
773
768
  }, [query, project, sortBy, viewState]);
774
- const groups = (0, react.useMemo)(() => deriveArchivedGroups(sessions.byId, workspaceState.items, workspaceState.archivedSessionIds, t("group.ungrouped")), [sessions.byId, workspaceState, t]);
769
+ const eligibleIds = (0, react.useMemo)(() => isArchived ? workspaceState.archivedSessionIds : unarchivedSessionIds(sessions.byId, workspaceState.archivedSessionIds), [isArchived, sessions.byId, workspaceState.archivedSessionIds]);
770
+ const groups = (0, react.useMemo)(() => deriveArchivedGroups(sessions.byId, workspaceState.items, eligibleIds, t("group.ungrouped")), [sessions.byId, workspaceState, eligibleIds, t]);
771
+ const switchTab = (tab) => {
772
+ if (busy || unarchivingSessionIdsRef.current.size > 0) return;
773
+ setArchiveTab(tab);
774
+ setSelectedSessionIds([]);
775
+ setArchiveTarget(null);
776
+ setArchiveGroup(null);
777
+ setError(null);
778
+ setNotice(null);
779
+ setProject("all");
780
+ setQuery("");
781
+ if (tab === "unarchived" && sortBy === "created") setSortBy("updated");
782
+ };
783
+ const confirmArchive = async () => {
784
+ if (archiveBusy.current || busy || !archiveTarget?.length) return;
785
+ archiveBusy.current = true;
786
+ setBusy(true);
787
+ setError(null);
788
+ setNotice(null);
789
+ try {
790
+ const result = await archiveSessions(archiveTarget);
791
+ setSelectedSessionIds([]);
792
+ setArchiveTab("archived");
793
+ setProject("all");
794
+ setQuery("");
795
+ setNotice(t("archives.archiveSuccess", { n: result.archivedSessionIdsAdded.length }));
796
+ setArchiveTarget(null);
797
+ setArchiveGroup(null);
798
+ } catch (reason) {
799
+ setError(t("archives.archiveBatchFailed", { detail: reason instanceof Error ? reason.message : String(reason) }));
800
+ } finally {
801
+ archiveBusy.current = false;
802
+ setBusy(false);
803
+ }
804
+ };
775
805
  const sortedGroups = (0, react.useMemo)(() => sortArchivedGroups(groups, sortBy, createdAtById, t), [groups, sortBy, createdAtById, t]);
776
806
  (0, react.useEffect)(() => {
777
807
  let cancelled = false;
@@ -801,8 +831,8 @@
801
831
  const allBatchTarget = { scope: "all" };
802
832
  const allBatchSessionIds = (0, react.useMemo)(() => deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, allBatchTarget), [workspaceState.archivedSessionIds, workspaceState.items]);
803
833
  (0, react.useEffect)(() => {
804
- setSelectedSessionIds((current) => pruneArchivedSelection(current, workspaceState.archivedSessionIds));
805
- }, [workspaceState.archivedSessionIds]);
834
+ setSelectedSessionIds((current) => pruneArchivedSelection(current, eligibleIds));
835
+ }, [eligibleIds]);
806
836
  const toggleSessionSelection = (sessionId, checked) => {
807
837
  setSelectedSessionIds((current) => toggleArchivedSelection(current, [sessionId], checked));
808
838
  };
@@ -892,20 +922,45 @@
892
922
  return (0, react_jsx_runtime.jsxs)("section", {
893
923
  className: "dsham_settings",
894
924
  "aria-label": t("archives.title"),
895
- children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS + ARCHIVE_SETTINGS_SELECTION_CSS + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS }), (0, react_jsx_runtime.jsxs)("header", {
925
+ children: [(0, react_jsx_runtime.jsx)("style", { children: ARCHIVE_TABS_CSS + ARCHIVE_SETTINGS_CSS + ARCHIVE_SETTINGS_BATCH_CSS + ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS + ARCHIVE_SETTINGS_SELECTION_CSS + ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS + ARCHIVE_WORKSPACE_CONFIRM_CSS }), (0, react_jsx_runtime.jsxs)("div", {
926
+ // 设置页使用独立布局容器,避免被 Codex UI 的会话 header 页签适配器识别。
896
927
  className: "dsham_settingsHeader",
897
928
  children: [(0, react_jsx_runtime.jsxs)("div", {
898
929
  children: [(0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsTitleRow", children: [(0, react_jsx_runtime.jsx)("h2", { children: t("archives.title") }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsLinks", children: [(0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager", target: "_blank", rel: "noreferrer", "aria-label": t("archives.viewProject"), children: [(0, react_jsx_runtime.jsx)(GithubMark16, {}), t("archives.viewProject")] }), (0, react_jsx_runtime.jsxs)("a", { className: "dsham_settingsExternalLink", href: "https://github.com/MichengAI/dsh-archive-manager/issues", target: "_blank", rel: "noreferrer", "aria-label": t("archives.feedback"), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconListPenOutline16, {}), t("archives.feedback")] })] })] }), (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsIntro", children: t("archives.description") })]
899
- }), (0, react_jsx_runtime.jsxs)("div", {
930
+ }), (0, react_jsx_runtime.jsx)("div", {
931
+ className: "dsham_archiveTabs",
932
+ role: "tablist",
933
+ "aria-label": t("archives.title"),
934
+ children: ["archived", "unarchived"].map((tab) => (0, react_jsx_runtime.jsx)("button", {
935
+ type: "button",
936
+ role: "tab",
937
+ className: "dsham_archiveTab",
938
+ id: "dsham-tab-" + tab,
939
+ "aria-controls": "dsham-archive-panel",
940
+ "aria-selected": archiveTab === tab,
941
+ tabIndex: archiveTab === tab ? 0 : -1,
942
+ disabled: busy || unarchivingSessionIds.size > 0,
943
+ onClick: () => switchTab(tab),
944
+ onKeyDown: (event) => {
945
+ if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) {
946
+ event.preventDefault();
947
+ const next = event.key === "Home" ? "archived" : event.key === "End" ? "unarchived" : isArchived ? "unarchived" : "archived";
948
+ switchTab(next);
949
+ event.currentTarget.parentElement.querySelector("#dsham-tab-" + next)?.focus();
950
+ }
951
+ },
952
+ children: t("archives.tab." + tab)
953
+ }, tab))
954
+ }), isArchived && (0, react_jsx_runtime.jsxs)("div", {
900
955
  className: "dsham_settingsHeaderActions",
901
956
  children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsRestoreAll", disabled: busy || allBatchSessionIds.length === 0, onClick: () => onBatchUnarchive(allBatchTarget), children: t("archives.restoreAll") }), (0, react_jsx_runtime.jsxs)("button", { type: "button", className: "dsham_settingsDanger", disabled: busy || allBatchSessionIds.length === 0, onClick: () => setDeleteTarget({ kind: "batch", target: allBatchTarget, title: t("archives.allProjects"), count: allBatchSessionIds.length }), children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}), t("archives.deleteAll")] })]
902
957
  })]
903
- }), (0, react_jsx_runtime.jsxs)("div", {
958
+ }), (0, react_jsx_runtime.jsxs)("div", { id: "dsham-archive-panel", role: "tabpanel", "aria-labelledby": "dsham-tab-" + archiveTab, children: [(0, react_jsx_runtime.jsxs)("div", {
904
959
  className: "dsham_settingsToolbar",
905
960
  children: [(0, react_jsx_runtime.jsxs)("label", {
906
961
  className: "dsham_settingsSearch",
907
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t("archives.searchPlaceholder"), "aria-label": t("archives.searchPlaceholder") })]
908
- }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, { value: "created", label: t("archives.sortCreated") }, { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })]
962
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, {}), (0, react_jsx_runtime.jsx)("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t(isArchived ? "archives.searchPlaceholder" : "archives.searchUnarchived"), "aria-label": t(isArchived ? "archives.searchPlaceholder" : "archives.searchUnarchived") })]
963
+ }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-sort-filter", value: sortBy, options: [{ value: "updated", label: t("archives.sortUpdated") }, ...isArchived ? [{ value: "created", label: t("archives.sortCreated") }] : [], { value: "alphabetical", label: t("archives.sortAlphabetical") }], onChange: setSortBy, "aria-label": t("archives.sortBy") }), (0, react_jsx_runtime.jsx)(ArchiveProjectSelect, { id: "dsham-project-filter", value: project, options: [{ value: "all", label: t("archives.allProjects") }, ...sortedGroups.map((group) => ({ value: group.key, label: group.title }))], onChange: setProject, "aria-label": t("archives.projectFilter") })]
909
964
  }), groups.length > 0 && (0, react_jsx_runtime.jsx)(ArchiveSelectionToolbar, {
910
965
  selectedCount: selectedSessionIds.length,
911
966
  hiddenCount: selectedSessionIds.length - selectedVisibleCount,
@@ -916,28 +971,41 @@
916
971
  t,
917
972
  onToggle: toggleVisibleSelection,
918
973
  onClear: () => setSelectedSessionIds([]),
974
+ onArchive: isArchived ? void 0 : () => requestArchive([...selectedSessionIds]),
919
975
  onRestore: onSelectedUnarchive,
920
976
  onDelete: () => setDeleteTarget({ kind: "batch", target: { scope: "sessions", sessionIds: selectedSessionIds }, count: selectedSessionIds.length })
921
- }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.empty") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
977
+ }), groups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t(isArchived ? "archives.empty" : "archives.emptyUnarchived") }) : filteredGroups.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsEmpty", children: t("archives.emptyFiltered") }) : filteredGroups.map((group) => {
922
978
  const target = archivedBatchTargetForGroup(group.key);
923
- const count = deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length;
979
+ const groupSessionIds = groups.find((item) => item.key === group.key)?.sessions.map((session) => session.id) ?? [];
980
+ const count = isArchived ? deriveArchivedBatchIds(workspaceState.archivedSessionIds, workspaceState.items, target).length : groupSessionIds.length;
924
981
  return (0, react_jsx_runtime.jsxs)("section", {
925
982
  className: "dsham_settingsGroup",
926
983
  children: [(0, react_jsx_runtime.jsxs)("div", {
927
984
  className: "dsham_settingsGroupHeading",
928
- children: [(0, react_jsx_runtime.jsxs)("h3", { className: "dsham_settingsGroupTitle", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, {}), group.title] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsGroupMeta", children: [(0, react_jsx_runtime.jsx)("span", { className: "dsham_settingsCount", children: t("archives.sessionCount", { n: count }) }), (0, react_jsx_runtime.jsx)(ArchivedGroupActions, { group, busy, onRestore: () => onBatchUnarchive(target), onDelete: () => setDeleteTarget({ kind: "batch", target, title: group.title, count }), t })] })]
985
+ children: [(0, react_jsx_runtime.jsxs)("h3", { className: "dsham_settingsGroupTitle", children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, {}), group.title] }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsGroupMeta", children: [(0, react_jsx_runtime.jsx)("span", { className: "dsham_settingsCount", children: t("archives.sessionCount", { n: count }) }), (0, react_jsx_runtime.jsx)(ArchivedGroupActions, { group, busy, onArchive: isArchived ? void 0 : () => requestArchive(groupSessionIds, group), onRestore: () => onBatchUnarchive(target), onDelete: () => setDeleteTarget({ kind: "batch", target, title: group.title, count }), t })] })]
929
986
  }), (0, react_jsx_runtime.jsx)("div", {
930
987
  className: "dsham_settingsList",
931
988
  children: group.sessions.map((session) => (0, react_jsx_runtime.jsxs)("article", {
932
989
  className: "dsham_settingsRow",
933
990
  children: [(0, react_jsx_runtime.jsx)(ArchiveSelectionCheckbox, { checked: selectedSessionIdSet.has(session.id), disabled: busy, label: t("archives.selectSession", { name: displayTitle(session, t) }), onChange: (event) => toggleSessionSelection(session.id, event.target.checked) }), (0, react_jsx_runtime.jsxs)("div", { className: "dsham_settingsContent", children: [(0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsTitle", children: displayTitle(session, t) }), (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsMeta", children: archiveTimeLabel(session.updatedAt, t) })] }), (0, react_jsx_runtime.jsxs)("div", {
934
991
  className: "dsham_settingsActions",
935
- children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session), children: t("archives.viewConversation") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session, true), children: t("archives.restoreOpen") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => onUnarchive(session.id), children: t("menu.unarchive") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy || unarchivingSessionIds.has(session.id), "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget({ kind: "session", session }), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
992
+ children: !isArchived ? [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy, onClick: () => viewConversation(session), children: t("archives.openSession") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy, onClick: () => requestArchive([session.id]), children: t("archives.archiveSelected") })] : [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session), children: t("archives.openSession") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => viewConversation(session, true), children: t("archives.restoreOpen") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsAction", disabled: busy || unarchivingSessionIds.has(session.id), onClick: () => onUnarchive(session.id), children: t("archives.restore") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsDelete", disabled: busy || unarchivingSessionIds.has(session.id), "aria-label": t("menu.deleteSession"), onClick: () => setDeleteTarget({ kind: "session", session }), children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) })]
936
993
  })]
937
994
  }, session.id))
938
995
  })]
939
996
  }, group.key);
940
- }), error !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }), notice !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsStatus", role: "status", children: notice }), (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
997
+ }), archiveTarget === null && error !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }), notice !== null && (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsStatus", role: "status", children: notice }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
998
+ open: archiveTarget !== null,
999
+ onClose: closeArchive,
1000
+ closeLabel: t("close"),
1001
+ title: t("archives.archiveTitle", { n: archiveTarget?.length ?? 0 }),
1002
+ description: archiveGroup === null ? t("archives.archiveSelectedDesc") : t(archiveGroup.key === ARCHIVE_UNGROUPED_KEY ? "archives.archiveUngroupedDesc" : "archives.archiveProjectDesc", { name: archiveGroup.title, n: archiveTarget?.length ?? 0 }),
1003
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1004
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeArchive, children: t("cancel") }),
1005
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_archiveWorkspaceConfirm", disabled: busy, onClick: confirmArchive, children: t("archives.archiveSelected") })
1006
+ ] }),
1007
+ children: error !== null ? (0, react_jsx_runtime.jsx)("div", { className: "dsham_settingsError", role: "alert", children: error }) : busy ? t("archiveWorkspace.pending") : null
1008
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
941
1009
  open: deleteTarget !== null,
942
1010
  onClose: closeDelete,
943
1011
  closeLabel: t("close"),
@@ -945,7 +1013,7 @@
945
1013
  ...deleteDialogDescription === void 0 ? {} : { description: deleteDialogDescription },
946
1014
  footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", disabled: busy, onClick: closeDelete, children: t("cancel") }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { variant: "outline", className: "dsham_settingsDeleteConfirm", disabled: busy, onClick: confirmDelete, children: deleteConfirmLabel })] }),
947
1015
  children: busy && (0, react_jsx_runtime.jsx)("div", { role: "status", children: deleteTarget?.kind === "batch" ? t("archives.deleteBatchPending") : t("deleteSession.pending") })
948
- })]
1016
+ })] })]
949
1017
  });
950
1018
  }
951
1019
  function r(e) {
@@ -1516,7 +1584,7 @@
1516
1584
  })]
1517
1585
  });
1518
1586
  }
1519
- function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onUnarchive, onDeleteSession, drag, flat = false, t }) {
1587
+ function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onUnarchive, onDeleteSession, onReveal, drag, flat = false, t }) {
1520
1588
  const row = node;
1521
1589
  const title = displayTitle(node, t);
1522
1590
  const selected = node.id === currentId;
@@ -1524,6 +1592,12 @@
1524
1592
  const statuses = sessionStatuses(node, t);
1525
1593
  const showStatus = statuses[0].state !== "done" || row.completed;
1526
1594
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
1595
+ const rowRef = (0, react.useRef)(null);
1596
+ (0, react.useEffect)(() => {
1597
+ if (onReveal === void 0) return;
1598
+ rowRef.current?.scrollIntoView({ block: "nearest" });
1599
+ onReveal();
1600
+ }, [onReveal]);
1527
1601
  const sessionMenuItems = archived ? [
1528
1602
  {
1529
1603
  id: "unarchive",
@@ -1561,6 +1635,7 @@
1561
1635
  ];
1562
1636
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
1563
1637
  anchor: (0, react_jsx_runtime.jsxs)("div", {
1638
+ ref: rowRef,
1564
1639
  className: clsx(Rows_module_css_default.sessionRow, selected && Rows_module_css_default.selected, menuOpen && Rows_module_css_default.menuOpen, flat && !showStatus && Rows_module_css_default.flatSessionRowWithoutStatus, archived && ARCHIVED_CLASSES.row, drag?.marker === "before" && Rows_module_css_default.dropBefore, drag?.marker === "after" && Rows_module_css_default.dropAfter),
1565
1640
  role: "treeitem",
1566
1641
  "aria-selected": selected,
@@ -2010,7 +2085,7 @@
2010
2085
  const rect = e.currentTarget.getBoundingClientRect();
2011
2086
  return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
2012
2087
  }
2013
- function SessionTree({ useSessions, useSessionPendingInteraction, startSession, open, forkSession, workspaces, archivedSessionIds, showArchived, onRenameRequest, onArchiveRequest, onDeleteRequest, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t }) {
2088
+ function SessionTree({ useSessions, useSessionPendingInteraction, startSession, open, forkSession, workspaces, archivedSessionIds, showArchived, onRenameRequest, onArchiveRequest, onDeleteRequest, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t, revealSessionId, onSessionRevealed }) {
2014
2089
  const list = useSessions((s) => s);
2015
2090
  const pendingInteractions = useSessionPendingInteraction((s) => s);
2016
2091
  const current = list.current;
@@ -2022,6 +2097,7 @@
2022
2097
  const previousOrderBy = (0, react.useRef)(orderBy);
2023
2098
  useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
2024
2099
  const currentGroup = current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(current))?.workspaceId ?? "";
2100
+ const revealGroup = revealSessionId === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(revealSessionId))?.workspaceId ?? "";
2025
2101
  (0, react.useEffect)(() => {
2026
2102
  if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
2027
2103
  setGroupExpanded(currentGroup, true);
@@ -2031,6 +2107,14 @@
2031
2107
  setGroupExpanded,
2032
2108
  groupExpansion
2033
2109
  ]);
2110
+ (0, react.useEffect)(() => {
2111
+ if (revealGroup === void 0 || groupExpansion[revealGroup] === true) return;
2112
+ setGroupExpanded(revealGroup, true);
2113
+ }, [
2114
+ groupExpansion,
2115
+ revealGroup,
2116
+ setGroupExpanded
2117
+ ]);
2034
2118
  const expandedGroups = (0, react.useMemo)(() => Object.entries(groupExpansion).filter(([, expanded]) => expanded).map(([key]) => key), [groupExpansion]);
2035
2119
  const ungroupedSessionIds = (0, react.useMemo)(() => {
2036
2120
  const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
@@ -2057,7 +2141,7 @@
2057
2141
  orderBy,
2058
2142
  sortByRecency: orderBy === "updated" && (previousOrder === void 0 || switchedToUpdated)
2059
2143
  });
2060
- if (next.changed) syncSessionOrderAccount(key, next.order.map((id) => id), next.updatedAt);
2144
+ if (next.changed && typeof syncSessionOrderAccount === "function") syncSessionOrderAccount(key, next.order.map((id) => id), next.updatedAt);
2061
2145
  }
2062
2146
  }, [
2063
2147
  list,
@@ -2092,6 +2176,17 @@
2092
2176
  expandedGroups,
2093
2177
  sessionOrderByAccount
2094
2178
  ]);
2179
+ (0, react.useEffect)(() => {
2180
+ if (revealSessionId === void 0 || revealGroup === void 0) return;
2181
+ const group = groups.find((candidate) => candidate.key === revealGroup);
2182
+ if (group === void 0 || !group.expanded || !group.sessions.some((row) => row.id === revealSessionId)) return;
2183
+ if (group.sessions.slice(0, COLLAPSED_SESSION_LIMIT).some((row) => row.id === revealSessionId)) return;
2184
+ setExpandedSessionGroups((keys) => keys.includes(revealGroup) ? keys : [...keys, revealGroup]);
2185
+ }, [
2186
+ groups,
2187
+ revealGroup,
2188
+ revealSessionId
2189
+ ]);
2095
2190
  const now = Date.now();
2096
2191
  const commitSessionDrag = (activeDrag, over) => {
2097
2192
  if (sessionDropCommitted.current) return;
@@ -2238,6 +2333,9 @@
2238
2333
  onArchive: onSessionArchive,
2239
2334
  onUnarchive: onSessionUnarchive,
2240
2335
  onDeleteSession: onSessionDelete,
2336
+ onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
2337
+ onSessionRevealed?.(node.id);
2338
+ } : void 0,
2241
2339
  drag: {
2242
2340
  start: () => {
2243
2341
  sessionDropCommitted.current = false;
@@ -2291,7 +2389,7 @@
2291
2389
  ]
2292
2390
  });
2293
2391
  }
2294
- function FlatList({ useSessions, useSessionPendingInteraction, open, forkSession, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, archivedSessionIds, showArchived, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t }) {
2392
+ function FlatList({ useSessions, useSessionPendingInteraction, open, forkSession, onSessionRename, onSessionArchive, onSessionUnarchive, onSessionDelete, archivedSessionIds, showArchived, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t, revealSessionId, onSessionRevealed }) {
2295
2393
  const list = useSessions((s) => s);
2296
2394
  const pendingInteractions = useSessionPendingInteraction((s) => s);
2297
2395
  const baseRows = (0, react.useMemo)(() => deriveFlat(list, archivedSessionIds, pendingInteractions, showArchived), [
@@ -2381,6 +2479,9 @@
2381
2479
  onArchive: onSessionArchive,
2382
2480
  onUnarchive: onSessionUnarchive,
2383
2481
  onDeleteSession: onSessionDelete,
2482
+ onReveal: node.id === revealSessionId ? () => {
2483
+ onSessionRevealed?.(node.id);
2484
+ } : void 0,
2384
2485
  flat: true,
2385
2486
  drag: {
2386
2487
  start: () => {
@@ -2481,6 +2582,18 @@
2481
2582
  }), (0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })]
2482
2583
  });
2483
2584
  }
2585
+ const sidebarReveal = {
2586
+ listeners: /* @__PURE__ */ new Set(),
2587
+ request(sessionId) {
2588
+ for (const listener of this.listeners) listener(sessionId);
2589
+ },
2590
+ subscribe(listener) {
2591
+ this.listeners.add(listener);
2592
+ return () => {
2593
+ this.listeners.delete(listener);
2594
+ };
2595
+ }
2596
+ };
2484
2597
  function WorkspaceBrowser({ wide, expandSidebar, useSessions, useSessionPendingInteraction, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, archiveWorkspaceSessions, unarchiveSession, deleteSession, insertSessionBefore, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, renderSlot, t }) {
2485
2598
  const useEffectiveSessionPendingInteraction = useSessionPendingInteraction ?? useEmptySessionPendingInteraction;
2486
2599
  const workspaces = useWorkspaces((state) => state.items);
@@ -2495,6 +2608,11 @@
2495
2608
  const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
2496
2609
  const sessionUpdatedAtByAccount = useStore((s) => s.sessionUpdatedAtByAccount);
2497
2610
  const archivedSet = (0, react.useMemo)(() => new Set(archivedSessionIds), [archivedSessionIds]);
2611
+ const [revealSessionId, setRevealSessionId] = (0, react.useState)(void 0);
2612
+ (0, react.useEffect)(() => sidebarReveal.subscribe((sessionId) => setRevealSessionId(sessionId)), []);
2613
+ const acknowledgeSessionReveal = (sessionId) => {
2614
+ setRevealSessionId((current) => current === sessionId ? void 0 : current);
2615
+ };
2498
2616
  const [archivedToast, setArchivedToast] = (0, react.useState)(null);
2499
2617
  const archivedToastSeq = (0, react.useRef)(0);
2500
2618
  const showArchivedToast2 = (text) => {
@@ -2902,7 +3020,12 @@
2902
3020
  children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
2903
3021
  useSessions,
2904
3022
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
2905
- open: guardedOpen,
3023
+ open: (sessionId) => {
3024
+ setRevealSessionId(sessionId);
3025
+ setQuery("");
3026
+ setSearchExpanded(false);
3027
+ guardedOpen(sessionId);
3028
+ },
2906
3029
  workspaces,
2907
3030
  archivedSessionIds,
2908
3031
  showArchived,
@@ -2914,6 +3037,8 @@
2914
3037
  useSessions,
2915
3038
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
2916
3039
  open: guardedOpen,
3040
+ revealSessionId,
3041
+ onSessionRevealed: acknowledgeSessionReveal,
2917
3042
  forkSession,
2918
3043
  onSessionRename,
2919
3044
  onSessionArchive,
@@ -2933,6 +3058,8 @@
2933
3058
  }) : (0, react_jsx_runtime.jsx)(SessionTree, {
2934
3059
  useSessions,
2935
3060
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3061
+ revealSessionId,
3062
+ onSessionRevealed: acknowledgeSessionReveal,
2936
3063
  onSessionRename,
2937
3064
  onSessionArchive,
2938
3065
  onSessionUnarchive,
@@ -3170,12 +3297,13 @@
3170
3297
  ]
3171
3298
  });
3172
3299
  }
3300
+ const ARCHIVE_TABS_CSS = ".dsham_archiveTabs{display:flex;gap:24px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_archiveTab{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;padding:12px 2px;border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:600;cursor:pointer}.dsham_archiveTab[aria-selected=true]{border-bottom-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary)}.dsham_archiveTab:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_archiveTab:disabled{opacity:.5;cursor:not-allowed}@container(max-width:520px){.dsham_archiveTabs{gap:16px}}";
3173
3301
  const ARCHIVE_SETTINGS_CSS = ".dsham_settings{container-type:inline-size;min-width:0;box-sizing:border-box;width:min(100%,760px);margin:0 auto;padding:0 0 32px;color:var(--dsw-alias-label-primary)}.dsham_settingsHeader{display:flex;flex-direction:column;align-items:stretch;gap:16px;margin-bottom:16px}.dsham_settings h2{margin:0;font-size:24px;line-height:32px;font-weight:600;letter-spacing:-.4px;white-space:nowrap}.dsham_settingsIntro{margin:12px 0 0;max-width:42em;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}.dsham_settingsDanger{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:0 12px;color:var(--dsw-alias-state-error-primary);background:transparent;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsDanger:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)}.dsham_settingsToolbar{display:flex;gap:8px;margin-bottom:16px}.dsham_settingsSearch{display:flex;align-items:center;gap:8px;min-width:0;flex:1;height:32px;padding:0 12px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px}.dsham_settingsSearch:focus-within{border-color:var(--dsw-alias-label-tertiary)}.dsham_settingsSearch input{width:100%;min-width:0;padding:0;color:var(--dsw-alias-label-primary);background:transparent;border:0;outline:0;font:inherit;font-size:12px}.dsham_settingsSearch input::placeholder{color:var(--dsw-alias-label-tertiary)}.dsham_settingsFilter{position:relative;min-width:168px;flex:none}.dsham_selectTrigger{box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:32px;padding:0 10px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-button-elevated-fill));border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;line-height:20px;text-align:left}.dsham_selectTrigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectTrigger:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_selectTrigger[aria-expanded='true']{border-color:var(--dsw-alias-state-success-primary)}.dsham_selectValue{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsham_selectCaret{flex:none;width:12px;height:12px;color:var(--dsw-alias-label-tertiary)}.dsham_selectMenu{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;box-sizing:border-box;min-width:100%;max-height:280px;overflow:auto;padding:4px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv3)}.dsham_selectOption{box-sizing:border-box;display:flex;align-items:center;width:100%;min-height:32px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;text-align:left;cursor:pointer}.dsham_selectOption:hover,.dsham_selectOption[data-active='true']{background:var(--dsw-alias-interactive-bg-hover)}.dsham_selectOption[aria-selected='true']{color:var(--dsw-alias-state-success-primary)}.dsham_settingsGroup{margin:0 0 20px}.dsham_settingsGroupHeading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 14px}.dsham_settingsGroupTitle{display:flex;align-items:center;gap:8px;min-width:0;margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600}.dsham_settingsGroupTitle svg{flex:none;color:var(--dsw-alias-label-secondary)}.dsham_settingsCount{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}.dsham_settingsList{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-button-elevated-fill))}.dsham_settingsRow{display:flex;align-items:center;gap:12px;min-height:60px;padding:10px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsRow:last-child{border-bottom:0}.dsham_settingsContent{min-width:0;flex:1}.dsham_settingsTitle{overflow:hidden;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:18px;text-overflow:ellipsis;white-space:nowrap}.dsham_settingsMeta{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.dsham_settingsActions{display:flex;align-items:center;gap:8px}.dsham_settingsAction{min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsAction:hover{filter:brightness(1.12)}.dsham_settingsDelete{display:flex;align-items:center;justify-content:center;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsDelete:hover{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsEmpty{padding:28px 8px;color:var(--dsw-alias-label-secondary);text-align:center}.dsham_settingsError{margin-top:10px;color:var(--dsw-alias-state-error-primary);font-size:12px}@container(max-width:520px){.dsham_settingsHeader{margin-bottom:16px}.dsham_settingsToolbar{flex-wrap:wrap;margin-bottom:28px}.dsham_settingsSearch{flex-basis:100%}.dsham_settingsFilter{flex:1;min-width:0}.dsham_settingsGroup{margin-bottom:32px}.dsham_settingsRow{padding:10px 12px}.dsham_settingsActions{gap:4px}}";
3174
3302
  const ARCHIVE_SETTINGS_BATCH_CSS = ".dsham_settingsHeaderActions,.dsham_settingsGroupMeta{display:flex;align-items:center;gap:8px;flex:none;flex-wrap:wrap}.dsham_settingsRestoreAll{display:inline-flex;align-items:center;min-height:32px;padding:0 12px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsRestoreAll:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsRestoreAll:disabled,.dsham_settingsDanger:disabled,.dsham_settingsGroupMenu:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsGroupMenu{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;color:var(--dsw-alias-label-tertiary);background:transparent;border:0;border-radius:8px;cursor:pointer}.dsham_settingsGroupMenu:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsStatus{margin-top:10px;color:var(--dsw-alias-label-secondary);font-size:12px}@media(max-width:720px){.dsham_settingsHeader{flex-direction:column}.dsham_settingsHeaderActions{align-self:flex-start}}";
3175
3303
  const ARCHIVE_SETTINGS_EXTERNAL_LINK_CSS = ".dsham_settingsTitleRow{display:flex;align-items:center;gap:8px 12px;min-width:0;flex-wrap:wrap}.dsham_settingsLinks{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.dsham_settingsExternalLink{display:inline-flex;align-items:center;gap:5px;min-height:28px;padding:0 8px;color:var(--dsw-alias-label-secondary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;font-size:12px;font-weight:500;line-height:18px;text-decoration:none;white-space:nowrap}.dsham_settingsExternalLink:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsExternalLink:focus-visible{outline:2px solid var(--dsw-alias-state-success-primary);outline-offset:2px}.dsham_settingsExternalLink svg{flex:none}@media(max-width:720px){.dsham_settingsTitleRow{flex-wrap:wrap}}";
3176
3304
  const ARCHIVE_SETTINGS_DELETE_CONFIRM_CSS = ".dsham_settingsDeleteConfirm{color:var(--dsw-alias-state-error-primary)!important;background:transparent!important;border-color:var(--dsw-alias-state-error-primary)!important}.dsham_settingsDeleteConfirm:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)!important}.dsham_settingsDeleteConfirm:focus-visible{outline:2px solid var(--dsw-alias-state-error-secondary);outline-offset:2px}.dsham_settingsDeleteConfirm:disabled{cursor:not-allowed;opacity:.5}";
3177
3305
  const ARCHIVE_WORKSPACE_CONFIRM_CSS = ".dsham_archiveWorkspaceConfirm{color:var(--dsw-alias-state-error-primary)!important;background:transparent!important;border-color:var(--dsw-alias-state-error-primary)!important}.dsham_archiveWorkspaceConfirm:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 20%,transparent)!important}.dsham_archiveWorkspaceConfirm:focus-visible{outline:2px solid var(--dsw-alias-state-error-secondary);outline-offset:2px}.dsham_archiveWorkspaceConfirm:disabled{cursor:not-allowed;opacity:.5}";
3178
- const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{margin:-8px 0 16px;padding:4px 0 12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsSelectionMain{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px;min-height:36px}.dsham_settingsSelectionSummary{display:flex;flex-wrap:wrap;align-items:center;gap:12px;min-width:0;max-width:100%}.dsham_settingsSelectionToggle{display:inline-flex;flex:none;white-space:nowrap;align-items:center;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;cursor:pointer}.dsham_settingsCheckbox{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#3b82f6)}.dsham_settingsSelectionClear{flex:none;min-height:28px;padding:0 0 0 12px;border:0;border-left:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-state-business-primary,#3b82f6);font:inherit;font-size:13px;white-space:nowrap;cursor:pointer}.dsham_settingsSelectionClear:hover:not(:disabled){text-decoration:underline}.dsham_settingsSelectionActions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px;max-width:100%;margin-left:auto}.dsham_settingsSelectionAction{flex:none;white-space:nowrap;min-height:32px;padding:0 14px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsSelectionAction:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsSelectionDelete{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.dsham_settingsSelectionScope{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow-wrap:anywhere}.dsham_settingsSelectionAction:disabled,.dsham_settingsSelectionClear:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsSelectionAction:focus-visible,.dsham_settingsSelectionClear:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#3b82f6);outline-offset:3px}";
3306
+ const ARCHIVE_SETTINGS_SELECTION_CSS = ".dsham_settingsSelection{margin:-8px 0 16px;padding:4px 0 12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dsham_settingsSelectionMain{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px;min-height:36px}.dsham_settingsSelectionSummary{display:flex;flex-wrap:wrap;align-items:center;gap:12px;min-width:0;max-width:100%}.dsham_settingsSelectionToggle{display:inline-flex;flex:none;white-space:nowrap;align-items:center;gap:12px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;cursor:pointer}.dsham_settingsCheckbox{flex:none;width:16px;height:16px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#3b82f6)}.dsham_settingsSelectionClear{flex:none;min-height:28px;padding:0 0 0 12px;border:0;border-left:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-state-business-primary,#3b82f6);font:inherit;font-size:13px;white-space:nowrap;cursor:pointer}.dsham_settingsSelectionClear:hover:not(:disabled){text-decoration:underline}.dsham_settingsSelectionActions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px;max-width:100%;margin-left:auto}.dsham_settingsSelectionAction{flex:none;white-space:nowrap;min-height:32px;padding:0 14px;color:var(--dsw-alias-label-primary);background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;cursor:pointer;font:inherit;font-size:13px;font-weight:500}.dsham_settingsSelectionAction:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dsham_settingsSelectionArchive,.dsham_settingsSelectionDelete{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.dsham_settingsSelectionScope{margin:8px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow-wrap:anywhere}.dsham_settingsSelectionAction:disabled,.dsham_settingsSelectionClear:disabled{cursor:not-allowed;opacity:.5}.dsham_settingsSelectionAction:focus-visible,.dsham_settingsSelectionClear:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#3b82f6);outline-offset:3px}";
3179
3307
  const ARCHIVE_SETTINGS_LAYOUT_OVERRIDE = ".dsham_settings{margin:0 auto!important}@media(max-width:720px){.dsham_settings{margin:0 auto!important}}";
3180
3308
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-archive-manager/ArchiveSettings.layout.css") + "]") === null) {
3181
3309
  const tag = document.createElement("style");
@@ -3315,7 +3443,7 @@
3315
3443
  }) : null]
3316
3444
  });
3317
3445
  }
3318
- function ArchiveSelectionToolbar({ selectedCount, hiddenCount, allVisibleSelected, selectedVisibleCount, visibleCount, busy, t, onToggle, onClear, onRestore, onDelete }) {
3446
+ function ArchiveSelectionToolbar({ selectedCount, hiddenCount, allVisibleSelected, selectedVisibleCount, visibleCount, busy, t, onToggle, onClear, onRestore, onDelete, onArchive }) {
3319
3447
  const hasSelection = selectedCount > 0;
3320
3448
  return (0, react_jsx_runtime.jsxs)("div", {
3321
3449
  className: "dsham_settingsSelection",
@@ -3336,7 +3464,7 @@
3336
3464
  }), hasSelection && (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionClear", disabled: busy, "aria-label": t("archives.clearSelection"), onClick: onClear, children: t("archives.clearSelectionShort") })]
3337
3465
  }), hasSelection && (0, react_jsx_runtime.jsxs)("div", {
3338
3466
  className: "dsham_settingsSelectionActions",
3339
- children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction", disabled: busy, "aria-label": t("archives.restoreSelected"), onClick: onRestore, children: t("archives.restoreSelectionShort") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction dsham_settingsSelectionDelete", disabled: busy, "aria-label": t("archives.deleteSelected"), onClick: onDelete, children: t("archives.deleteSelectionShort") })]
3467
+ children: onArchive ? [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction dsham_settingsSelectionArchive", disabled: busy, onClick: onArchive, children: t("archives.archiveSelected") })] : [(0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction", disabled: busy, "aria-label": t("archives.restoreSelected"), onClick: onRestore, children: t("archives.restore") }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: "dsham_settingsSelectionAction dsham_settingsSelectionDelete", disabled: busy, "aria-label": t("archives.deleteSelected"), onClick: onDelete, children: t("archives.deleteSelectionShort") })]
3340
3468
  })]
3341
3469
  }), hiddenCount > 0 && (0, react_jsx_runtime.jsx)("p", { className: "dsham_settingsSelectionScope", role: "status", children: t("archives.selectionHiddenHint", { hidden: hiddenCount }) })]
3342
3470
  });
@@ -3356,10 +3484,15 @@
3356
3484
  onChange
3357
3485
  });
3358
3486
  }
3359
- function ArchivedGroupActions({ group, busy, onRestore, onDelete, t }) {
3487
+ function ArchivedGroupActions({ group, busy, onArchive, onRestore, onDelete, t }) {
3360
3488
  const [open, setOpen] = (0, react.useState)(false);
3361
3489
  const ungrouped = group.key === ARCHIVE_UNGROUPED_KEY;
3362
- const items = [{
3490
+ const items = onArchive ? [{
3491
+ id: "archive",
3492
+ label: t(ungrouped ? "archives.archiveUngrouped" : "archives.archiveProject"),
3493
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 }),
3494
+ danger: true
3495
+ }] : [{
3363
3496
  id: "restore",
3364
3497
  label: t(ungrouped ? "archives.restoreUngrouped" : "archives.restoreProject"),
3365
3498
  icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16, {})
@@ -3375,7 +3508,9 @@
3375
3508
  items,
3376
3509
  onSelect: (id) => {
3377
3510
  setOpen(false);
3378
- if (id === "restore") onRestore();
3511
+ if (busy) return;
3512
+ if (id === "archive") onArchive?.();
3513
+ else if (id === "restore") onRestore();
3379
3514
  else if (id === "delete") onDelete();
3380
3515
  },
3381
3516
  portal: true,
@@ -3439,6 +3574,10 @@
3439
3574
  };
3440
3575
  return { kind: "notice", message: t("archives.deleteSuccess", { n: deleted }) };
3441
3576
  }
3577
+ function unarchivedSessionIds(byId, archivedSessionIds) {
3578
+ const archived = new Set(archivedSessionIds);
3579
+ return Object.values(byId).filter((session) => session.origin !== "subagent" && !session.blank && !archived.has(session.id)).map((session) => session.id);
3580
+ }
3442
3581
  function deriveArchivedGroups(byId, items, archivedSessionIds, ungroupedLabel) {
3443
3582
  const byWorkspace = items.map((workspace) => ({
3444
3583
  key: workspace.workspaceId,
@@ -3480,14 +3619,27 @@
3480
3619
  });
3481
3620
  }
3482
3621
  const zh = {
3483
- "archives.viewConversation": "\u67E5\u770B\u5BF9\u8BDD",
3622
+ "archives.archiveProject": "\u5F52\u6863\u8BE5\u9879\u76EE\u7684\u5168\u90E8\u804A\u5929",
3623
+ "archives.archiveUngrouped": "\u5F52\u6863\u5168\u90E8\u672A\u5206\u7EC4\u804A\u5929",
3624
+ "archives.archiveProjectDesc": "\u5C06\u201C{name}\u201D\u4E2D\u5168\u90E8 {n} \u6761\u672A\u5F52\u6863\u4F1A\u8BDD\u5F52\u6863\uFF0C\u4E0D\u53D7\u5F53\u524D\u641C\u7D22\u7B5B\u9009\u5F71\u54CD\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u6062\u590D\u3002",
3625
+ "archives.archiveUngroupedDesc": "\u5C06\u5168\u90E8 {n} \u6761\u672A\u5206\u7EC4\u7684\u672A\u5F52\u6863\u4F1A\u8BDD\u5F52\u6863\uFF0C\u4E0D\u53D7\u5F53\u524D\u641C\u7D22\u7B5B\u9009\u5F71\u54CD\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u6062\u590D\u3002",
3626
+ "archives.tab.archived": "\u5DF2\u5F52\u6863",
3627
+ "archives.tab.unarchived": "\u672A\u5F52\u6863",
3628
+ "archives.searchUnarchived": "\u641C\u7D22\u672A\u5F52\u6863\u804A\u5929",
3629
+ "archives.emptyUnarchived": "\u6682\u65E0\u672A\u5F52\u6863\u4F1A\u8BDD\u3002",
3630
+ "archives.archiveSelected": "\u5F52\u6863",
3631
+ "archives.openSession": "\u6253\u5F00\u4F1A\u8BDD",
3632
+ "archives.archiveSelectedDesc": "\u5C06\u9009\u4E2D\u7684\u4F1A\u8BDD\u5F52\u6863\uFF0C\u5305\u542B\u5F53\u524D\u7B5B\u9009\u9690\u85CF\u7684\u5DF2\u9009\u9879\u3002\u4E4B\u540E\u53EF\u5728\u201C\u5DF2\u5F52\u6863\u201D\u4E2D\u67E5\u770B\u548C\u6062\u590D\u3002",
3633
+ "archives.archiveSuccess": "\u5DF2\u5F52\u6863 {n} \u6761\u4F1A\u8BDD\u3002",
3634
+ "archives.archiveBatchFailed": "\u6279\u91CF\u5F52\u6863\u5931\u8D25\uFF1A{detail}",
3484
3635
  "archives.navigationUnavailable": "\u5F53\u524D\u5BBF\u4E3B\u65E0\u6CD5\u4FDD\u6301\u5F52\u6863\u5BF9\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u201C\u6062\u590D\u5E76\u6253\u5F00\u201D\u3002",
3485
3636
  "archives.sessionNotRetained": "\u5BBF\u4E3B\u672A\u4FDD\u7559\u76EE\u6807\u4F1A\u8BDD\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6216\u6062\u590D\u540E\u518D\u8BD5\u3002",
3486
3637
  "archives.restoreOpen": "\u6062\u590D\u5E76\u6253\u5F00",
3487
3638
  "archives.selectedCount": "\u5DF2\u9009 {n} \u6761",
3488
3639
  "archives.selectionHiddenHint": "\u542B\u5F53\u524D\u672A\u663E\u793A\u7684 {hidden} \u6761\uFF0C\u4E5F\u5C06\u53C2\u4E0E\u64CD\u4F5C",
3489
3640
  "archives.clearSelectionShort": "\u6E05\u7A7A",
3490
- "archives.restoreSelectionShort": "\u6062\u590D",
3641
+ "archives.restore": "\u6062\u590D",
3642
+ "archives.archiveTitle": "\u5F52\u6863 {n} \u4E2A\u804A\u5929\uFF1F",
3491
3643
  "archives.deleteSelectionShort": "\u5220\u9664",
3492
3644
  "archives.clearSelection": "\u6E05\u7A7A\u9009\u62E9",
3493
3645
  "group.ungrouped": "\u672A\u5206\u7EC4",
@@ -3519,7 +3671,7 @@
3519
3671
  "archived.badge": "\u5F52\u6863\u4F1A\u8BDD",
3520
3672
  "archived.notOpenable": "\u5DF2\u5F52\u6863\uFF0C\u53D6\u6D88\u5F52\u6863\u540E\u53EF\u7EE7\u7EED\u5BF9\u8BDD",
3521
3673
  "archives.title": "\u5F52\u6863\u4F1A\u8BDD",
3522
- "archives.description": "\u7BA1\u7406\u5DF2\u5F52\u6863\u7684\u4F1A\u8BDD\u3002",
3674
+ "archives.description": "\u7BA1\u7406\u5DF2\u5F52\u6863\u4F1A\u8BDD\uFF0C\u6216\u9009\u62E9\u672A\u5F52\u6863\u4F1A\u8BDD\u8FDB\u884C\u6279\u91CF\u5F52\u6863\u3002",
3523
3675
  "archives.viewProject": "GitHub",
3524
3676
  "archives.feedback": "\u95EE\u9898\u53CD\u9988",
3525
3677
  "archives.empty": "\u6682\u65E0\u5DF2\u5F52\u6863\u4F1A\u8BDD",
@@ -3615,14 +3767,27 @@
3615
3767
  "time.ago": "{t}\u524D"
3616
3768
  };
3617
3769
  const en = {
3618
- "archives.viewConversation": "View conversation",
3770
+ "archives.archiveProject": "Archive all chats in this project",
3771
+ "archives.archiveUngrouped": "Archive all ungrouped chats",
3772
+ "archives.archiveProjectDesc": "Archive all {n} unarchived sessions in \u201C{name}\u201D, regardless of the current search filter. You can restore them in Archived.",
3773
+ "archives.archiveUngroupedDesc": "Archive all {n} ungrouped, unarchived sessions, regardless of the current search filter. You can restore them in Archived.",
3774
+ "archives.tab.archived": "Archived",
3775
+ "archives.tab.unarchived": "Unarchived",
3776
+ "archives.searchUnarchived": "Search unarchived chats",
3777
+ "archives.emptyUnarchived": "No unarchived sessions.",
3778
+ "archives.archiveSelected": "Archive",
3779
+ "archives.openSession": "Open session",
3780
+ "archives.archiveSelectedDesc": "Archive the selected sessions, including selections hidden by the current filter. You can view and restore them in Archived.",
3781
+ "archives.archiveSuccess": "Archived {n} sessions.",
3782
+ "archives.archiveBatchFailed": "Batch archive failed: {detail}",
3619
3783
  "archives.navigationUnavailable": "This host cannot keep the conversation archived while opening it. Use \u201CRestore and open\u201D instead.",
3620
3784
  "archives.sessionNotRetained": "The host did not keep the requested conversation open. Try again, or restore it before opening.",
3621
3785
  "archives.restoreOpen": "Restore and open",
3622
3786
  "archives.selectedCount": "{n} selected",
3623
3787
  "archives.selectionHiddenHint": "Includes {hidden} hidden by the current filter; actions apply to them too.",
3624
3788
  "archives.clearSelectionShort": "Clear",
3625
- "archives.restoreSelectionShort": "Restore",
3789
+ "archives.restore": "Restore",
3790
+ "archives.archiveTitle": "Archive {n} chats?",
3626
3791
  "archives.deleteSelectionShort": "Delete",
3627
3792
  "archives.clearSelection": "Clear selection",
3628
3793
  "group.ungrouped": "Ungrouped",
@@ -3654,7 +3819,7 @@
3654
3819
  "archived.badge": "Archived sessions",
3655
3820
  "archived.notOpenable": "This session is archived. Unarchive it to continue the conversation.",
3656
3821
  "archives.title": "Archived sessions",
3657
- "archives.description": "Manage archived sessions.",
3822
+ "archives.description": "Manage archived sessions or select unarchived sessions to archive in bulk.",
3658
3823
  "archives.viewProject": "GitHub",
3659
3824
  "archives.feedback": "Issues",
3660
3825
  "archives.empty": "No archived sessions.",
@@ -3757,8 +3922,7 @@
3757
3922
  "workspaces",
3758
3923
  "locale",
3759
3924
  "remote",
3760
- "typert",
3761
- ...hasSplitClientStore ? ["uiWorkspace"] : []
3925
+ "typert"
3762
3926
  ];
3763
3927
  function bindObservable(source) {
3764
3928
  return {
@@ -3766,18 +3930,58 @@
3766
3930
  subscribe: source.subscribe.bind(source)
3767
3931
  };
3768
3932
  }
3769
- async function archiveWorkspaceSessionsAndRefresh(registry, workspaceId, refresh) {
3770
- const result = await registry.archiveWorkspaceSessions(workspaceId);
3771
- if (!result.ok) throw new Error(result.error.message);
3772
- await refresh();
3773
- return result.value;
3933
+ function createUnarchiveSession(workspaces, getRegistry) {
3934
+ return async (sessionId) => {
3935
+ if (typeof workspaces?.unarchiveSession === "function") {
3936
+ await workspaces.unarchiveSession(sessionId);
3937
+ return;
3938
+ }
3939
+ const registry = typeof getRegistry === "function" ? getRegistry() : getRegistry;
3940
+ if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3941
+ const result = await registry.unarchiveSession(sessionId);
3942
+ if (!result.ok) throw new Error(result.error.message);
3943
+ return result.value;
3944
+ };
3945
+ }
3946
+ async function archiveSessionsViaOfficial(workspaces, sessionIds, refresh) {
3947
+ const before = new Set(workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []);
3948
+ const seen = /* @__PURE__ */ new Set();
3949
+ for (const sessionId of sessionIds) {
3950
+ if (typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) continue;
3951
+ seen.add(sessionId);
3952
+ await workspaces.archiveSession(sessionId);
3953
+ }
3954
+ if (typeof refresh === "function") await refresh();
3955
+ const archivedSessionIds = [...workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []];
3956
+ return {
3957
+ archivedSessionIds,
3958
+ archivedSessionIdsAdded: archivedSessionIds.filter((id) => !before.has(id))
3959
+ };
3960
+ }
3961
+ async function unarchiveSessionsViaOfficial(workspaces, sessionIds, refresh, unarchive) {
3962
+ const before = new Set(workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []);
3963
+ const seen = /* @__PURE__ */ new Set();
3964
+ const run = typeof unarchive === "function" ? unarchive : (id) => workspaces.unarchiveSession(id);
3965
+ let archivedSessionIds;
3966
+ for (const sessionId of sessionIds) {
3967
+ if (typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) continue;
3968
+ seen.add(sessionId);
3969
+ const value = await run(sessionId);
3970
+ if (Array.isArray(value?.archivedSessionIds)) archivedSessionIds = [...value.archivedSessionIds];
3971
+ }
3972
+ if (typeof refresh === "function") await refresh();
3973
+ archivedSessionIds ??= [...workspaces.list?.getSnapshot?.()?.archivedSessionIds ?? []];
3974
+ return {
3975
+ archivedSessionIds,
3976
+ unarchivedSessionIds: [...before].filter((id) => !archivedSessionIds.includes(id))
3977
+ };
3774
3978
  }
3775
3979
  async function apply(ctx) {
3980
+ applyWorkspaceBrowser(ctx);
3776
3981
  const remote = ctx.get("remote");
3777
3982
  let disposeRemote = () => {
3778
3983
  };
3779
3984
  if (remote !== void 0) disposeRemote = await remote.$mount(ARCHIVE_MANAGER_REMOTE);
3780
- applyWorkspaceBrowser(ctx);
3781
3985
  return async () => {
3782
3986
  await disposeRemote();
3783
3987
  };
@@ -3799,13 +4003,24 @@
3799
4003
  const uiWorkspaceAt = () => ctx.get("uiWorkspace");
3800
4004
  const archiveViewState = {};
3801
4005
  let archiveNavigation;
3802
- ctx.effect(() => {
3803
- archiveNavigation = allowArchivedNavigation(uiWorkspaceAt(), ctx.sessions, ctx.workspaces, {
3804
- onOpened: () => ctx.get("layout")?.selectPanel(null)
4006
+ const attachArchiveNavigation = (navigation) => {
4007
+ archiveNavigation = allowArchivedNavigation(navigation, ctx.sessions, ctx.workspaces, {
4008
+ onOpened: () => ctx.get("layout")?.selectPanel(null),
4009
+ beginNavigation: () => ctx.get("layout")?.beginNavigation?.()
3805
4010
  });
3806
4011
  return () => archiveNavigation.dispose();
3807
- }, "archive-manager: explicit archived navigation");
3808
- const openConversation = (sessionId) => archiveNavigation.open(sessionId);
4012
+ };
4013
+ if (typeof ctx.inject === "function") ctx.inject(["uiWorkspace"], (ready) => attachArchiveNavigation(ready.uiWorkspace ?? uiWorkspaceAt()));
4014
+ else ctx.effect(() => attachArchiveNavigation(uiWorkspaceAt()), "archive-manager: explicit archived navigation");
4015
+ const openConversation = (sessionId) => {
4016
+ if (archiveNavigation === void 0) throw new ArchiveNavigationError("navigationUnavailable");
4017
+ sidebarReveal.request(sessionId);
4018
+ return archiveNavigation.open(sessionId);
4019
+ };
4020
+ const focusSessionWorkspace = async (sessionId) => {
4021
+ ctx.get("layout")?.beginNavigation?.();
4022
+ sidebarReveal.request(sessionId);
4023
+ };
3809
4024
  const searchSessions = async (query, signal) => {
3810
4025
  const result = await ctx.sessions.search(query, signal);
3811
4026
  if (!result.ok) throw new Error(result.error.message);
@@ -3824,17 +4039,16 @@
3824
4039
  console.warn("archive-manager: restored archived sessions but session list refresh failed:", error);
3825
4040
  }
3826
4041
  };
4042
+ const unarchiveOne = createUnarchiveSession(ctx.workspaces, () => ctx.get("remote.workspaceRegistry"));
3827
4043
  const unarchiveSession = async (sessionId) => {
3828
- const registry = ctx.get("remote.workspaceRegistry");
3829
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3830
- const result = await registry.unarchiveSession(sessionId);
3831
- if (!result.ok) throw new Error(result.error.message);
4044
+ await unarchiveOne(sessionId);
3832
4045
  await refreshSessionList();
3833
4046
  };
3834
4047
  const archiveWorkspaceSessions = async (workspaceId) => {
3835
- const registry = ctx.get("remote.workspaceRegistry");
3836
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3837
- return archiveWorkspaceSessionsAndRefresh(registry, workspaceId, refreshSessionList);
4048
+ const items = ctx.workspaces.list?.getSnapshot?.()?.items ?? [];
4049
+ const workspace = items.find((item) => item.workspaceId === workspaceId);
4050
+ if (workspace === void 0) throw new Error(`unknown workspace "${workspaceId}"`);
4051
+ return archiveSessionsViaOfficial(ctx.workspaces, workspace.sessionIds, refreshSessionList);
3838
4052
  };
3839
4053
  const deleteSession = async (sessionId) => {
3840
4054
  const registry = ctx.get("remote.workspaceRegistry");
@@ -3843,13 +4057,11 @@
3843
4057
  if (!result.ok) throw new Error(result.error.message);
3844
4058
  await refreshSessionList();
3845
4059
  };
4060
+ const archiveSessions = async (sessionIds) => archiveSessionsViaOfficial(ctx.workspaces, sessionIds, refreshSessionList);
3846
4061
  const unarchiveSessions = async (target) => {
3847
- const registry = ctx.get("remote.workspaceRegistry");
3848
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3849
- const result = await registry.unarchiveSessions(target);
3850
- if (!result.ok) throw new Error(result.error.message);
3851
- await refreshSessionList();
3852
- return result.value;
4062
+ const snapshot = ctx.workspaces.list?.getSnapshot?.() ?? { archivedSessionIds: [], items: [] };
4063
+ const sessionIds = deriveArchivedBatchIds(snapshot.archivedSessionIds, snapshot.items, target);
4064
+ return unarchiveSessionsViaOfficial(ctx.workspaces, sessionIds, refreshSessionList, unarchiveOne);
3853
4065
  };
3854
4066
  const deleteArchivedSessions = async (target) => {
3855
4067
  const registry = ctx.get("remote.workspaceRegistry");
@@ -3920,18 +4132,28 @@
3920
4132
  createWorkspace: (input) => ctx.workspaces.create(input),
3921
4133
  hooks: { directoryFlow: browserFlowSource }
3922
4134
  });
3923
- ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
3924
- name: "sidebar.workspaces",
3925
- // 显示顺序:Codex UI(-1)→ 归档(-0.5)→ 官方默认(0)。
3926
- priority: -0.5,
3927
- children: { [DIRECTORY_FLOW_SLOT]: {
3928
- kind: "single",
3929
- scope: "root"
3930
- } },
3931
- store: createWorkspaceViewStore(),
3932
- inject: browserInjected,
3933
- locale: NS
3934
- }, WorkspaceBrowser));
4135
+ ctx.slots.inject("sidebar.workspaces", () => {
4136
+ const common = {
4137
+ name: "sidebar.workspaces",
4138
+ // 低于官方 0,高于 Codex -1:必须盖住官方三项菜单才能露出删除。
4139
+ priority: -0.5,
4140
+ store: createWorkspaceViewStore(),
4141
+ inject: browserInjected,
4142
+ locale: NS
4143
+ };
4144
+ try {
4145
+ return ctx.slots.register({
4146
+ ...common,
4147
+ children: { [DIRECTORY_FLOW_SLOT]: {
4148
+ kind: "single",
4149
+ scope: "root"
4150
+ } }
4151
+ }, WorkspaceBrowser);
4152
+ } catch (error) {
4153
+ console.warn("archive-manager: sidebar registration with directory child failed, retrying without children", error);
4154
+ return ctx.slots.register(common, WorkspaceBrowser);
4155
+ }
4156
+ });
3935
4157
  mirrorDirectoryFlow(ctx, DIRECTORY_FLOW_SLOT);
3936
4158
  ctx.slots.inject("settings.section", () => ctx.slots.register({
3937
4159
  name: "settings.section",
@@ -3941,6 +4163,7 @@
3941
4163
  icon: "archive",
3942
4164
  locale: NS,
3943
4165
  inject: () => ({
4166
+ archiveSessions,
3944
4167
  sessionStore: bindObservable(ctx.sessions.list),
3945
4168
  workspaceStore: bindObservable(ctx.workspaces.list),
3946
4169
  unarchiveSession,
@@ -3949,12 +4172,14 @@
3949
4172
  deleteArchivedSessions,
3950
4173
  archivedSessionMetadata,
3951
4174
  openConversation,
4175
+ focusSessionWorkspace,
3952
4176
  viewState: archiveViewState,
3953
4177
  t: ctx.locale.bind(NS)
3954
4178
  })
3955
4179
  }, ArchivedSessionsSection));
3956
4180
  }
3957
4181
  exports.__test = {
4182
+ unarchivedSessionIds,
3958
4183
  ArchiveSelectionToolbar,
3959
4184
  ArchivedSessionsSection,
3960
4185
  zh,
@@ -3975,8 +4200,13 @@
3975
4200
  archiveableWorkspaceSessionCount,
3976
4201
  archiveWorkspaceDialogTarget,
3977
4202
  archiveWorkspaceDialogFailureState,
3978
- archiveWorkspaceSessionsAndRefresh,
4203
+ archiveSessionsViaOfficial,
4204
+ unarchiveSessionsViaOfficial,
4205
+ createUnarchiveSession,
3979
4206
  createWorkspaceViewStore,
4207
+ migrateWorkspaceViewPersist,
4208
+ WORKSPACE_VIEW_PERSIST_KEY,
4209
+ LEGACY_WORKSPACE_VIEW_PERSIST_KEY,
3980
4210
  bindObservable,
3981
4211
  hasSplitClientStore,
3982
4212
  groupByWorkspace,