@michengai/dsh-archive-manager 0.1.42 → 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,28 +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 archiveSessionIdsSchema = {
509
- parse(value) {
510
- if (!Array.isArray(value)) throw new TypeError("sessionIds must be an array");
511
- return [...new Set(value.map((id) => sessionIdSchema.parse(id)))];
512
- }
513
- };
514
- const archivedWorkspaceBatchSchema = {
515
- parse(value) {
516
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
517
- if (!Array.isArray(value.archivedSessionIds) || value.archivedSessionIds.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIds must be a string array");
518
- if (!Array.isArray(value.archivedSessionIdsAdded) || value.archivedSessionIdsAdded.some((id) => typeof id !== "string")) throw new TypeError("archivedSessionIdsAdded must be a string array");
519
- return value;
520
- }
521
- };
522
499
  const deletedBatchSchema = {
523
500
  parse(value) {
524
501
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError("result must be an object");
@@ -578,63 +555,6 @@
578
555
  },
579
556
  sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
580
557
  },
581
- {
582
- id: "@michengai/dsh-archive-manager#workspaceRegistry/unarchiveSessions",
583
- service: "workspaceRegistry",
584
- namespace: "workspaceRegistry",
585
- method: "unarchiveSessions",
586
- invocation: { kind: "direct" },
587
- parameters: [{
588
- name: "target",
589
- wire: "target",
590
- source: "json",
591
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedBatchTarget", schema: archivedBatchTargetSchema }
592
- }],
593
- result: {
594
- mode: "strict",
595
- typeSymbol: "@michengai/dsh-archive-manager/types#UnarchivedBatch",
596
- schema: unarchivedBatchSchema
597
- },
598
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
599
- },
600
- {
601
- id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveSessions",
602
- service: "workspaceRegistry",
603
- namespace: "workspaceRegistry",
604
- method: "archiveSessions",
605
- invocation: { kind: "direct" },
606
- parameters: [{
607
- name: "sessionIds",
608
- wire: "sessionIds",
609
- source: "json",
610
- codec: { mode: "strict", typeSymbol: "@michengai/dsh-archive-manager/types#ArchiveSessionIds", schema: archiveSessionIdsSchema }
611
- }],
612
- result: {
613
- mode: "strict",
614
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
615
- schema: archivedWorkspaceBatchSchema
616
- },
617
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
618
- },
619
- {
620
- id: "@michengai/dsh-archive-manager#workspaceRegistry/archiveWorkspaceSessions",
621
- service: "workspaceRegistry",
622
- namespace: "workspaceRegistry",
623
- method: "archiveWorkspaceSessions",
624
- invocation: { kind: "direct" },
625
- parameters: [{
626
- name: "workspaceId",
627
- wire: "workspaceId",
628
- source: "json",
629
- codec: { mode: "strict", typeSymbol: "@deepseek-ai/dsh-workspace/types#WorkspaceId", schema: workspaceIdSchema }
630
- }],
631
- result: {
632
- mode: "strict",
633
- typeSymbol: "@michengai/dsh-archive-manager/types#ArchivedWorkspaceBatch",
634
- schema: archivedWorkspaceBatchSchema
635
- },
636
- sourceLocation: { file: "@michengai/dsh-archive-manager/lib/workspace.js", line: 1, column: 1 }
637
- },
638
558
  {
639
559
  id: "@michengai/dsh-archive-manager#workspaceRegistry/deleteArchivedSessions",
640
560
  service: "workspaceRegistry",
@@ -711,7 +631,23 @@
711
631
  return indexed;
712
632
  }
713
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
+ }
714
649
  function createWorkspaceViewStore() {
650
+ migrateWorkspaceViewPersist();
715
651
  return (0, _deepseek_ai_dsh_client_store.defineStore)({
716
652
  init: () => ({
717
653
  groupBy: "workspace",
@@ -721,7 +657,7 @@
721
657
  sessionOrderByAccount: {},
722
658
  sessionUpdatedAtByAccount: {}
723
659
  }),
724
- persist: "dsh.workspace.view.v5",
660
+ persist: WORKSPACE_VIEW_PERSIST_KEY,
725
661
  actions: {
726
662
  setGroupBy: (d, mode) => {
727
663
  d.groupBy = mode;
@@ -754,7 +690,7 @@
754
690
  function GithubMark16() {
755
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" }) });
756
692
  }
757
- function ArchivedSessionsSection({ archiveSessions, 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 }) {
758
694
  const sessions = (0, react.useSyncExternalStore)(sessionStore.subscribe, sessionStore.getSnapshot);
759
695
  const workspaceState = (0, react.useSyncExternalStore)(workspaceStore.subscribe, workspaceStore.getSnapshot);
760
696
  const [archiveTab, setArchiveTab] = (0, react.useState)("archived");
@@ -799,13 +735,29 @@
799
735
  navigationPending.current = true;
800
736
  setBusy(true);
801
737
  setError(null);
738
+ let restored = false;
802
739
  try {
803
- await openArchivedConversation({ restore: unarchiveSession, open: async (id) => {
804
- await openConversation(id);
805
- if (navigationActive.current) close?.();
806
- } }, 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);
807
753
  } catch (reason) {
808
- 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));
809
761
  } finally {
810
762
  navigationPending.current = false;
811
763
  if (navigationActive.current) setBusy(false);
@@ -836,7 +788,10 @@
836
788
  setNotice(null);
837
789
  try {
838
790
  const result = await archiveSessions(archiveTarget);
839
- setSelectedSessionIds((ids) => ids.filter((id) => !result.archivedSessionIds.includes(id)));
791
+ setSelectedSessionIds([]);
792
+ setArchiveTab("archived");
793
+ setProject("all");
794
+ setQuery("");
840
795
  setNotice(t("archives.archiveSuccess", { n: result.archivedSessionIdsAdded.length }));
841
796
  setArchiveTarget(null);
842
797
  setArchiveGroup(null);
@@ -1629,7 +1584,7 @@
1629
1584
  })]
1630
1585
  });
1631
1586
  }
1632
- 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 }) {
1633
1588
  const row = node;
1634
1589
  const title = displayTitle(node, t);
1635
1590
  const selected = node.id === currentId;
@@ -1637,6 +1592,12 @@
1637
1592
  const statuses = sessionStatuses(node, t);
1638
1593
  const showStatus = statuses[0].state !== "done" || row.completed;
1639
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]);
1640
1601
  const sessionMenuItems = archived ? [
1641
1602
  {
1642
1603
  id: "unarchive",
@@ -1674,6 +1635,7 @@
1674
1635
  ];
1675
1636
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
1676
1637
  anchor: (0, react_jsx_runtime.jsxs)("div", {
1638
+ ref: rowRef,
1677
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),
1678
1640
  role: "treeitem",
1679
1641
  "aria-selected": selected,
@@ -2123,7 +2085,7 @@
2123
2085
  const rect = e.currentTarget.getBoundingClientRect();
2124
2086
  return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
2125
2087
  }
2126
- 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 }) {
2127
2089
  const list = useSessions((s) => s);
2128
2090
  const pendingInteractions = useSessionPendingInteraction((s) => s);
2129
2091
  const current = list.current;
@@ -2135,6 +2097,7 @@
2135
2097
  const previousOrderBy = (0, react.useRef)(orderBy);
2136
2098
  useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
2137
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 ?? "";
2138
2101
  (0, react.useEffect)(() => {
2139
2102
  if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
2140
2103
  setGroupExpanded(currentGroup, true);
@@ -2144,6 +2107,14 @@
2144
2107
  setGroupExpanded,
2145
2108
  groupExpansion
2146
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
+ ]);
2147
2118
  const expandedGroups = (0, react.useMemo)(() => Object.entries(groupExpansion).filter(([, expanded]) => expanded).map(([key]) => key), [groupExpansion]);
2148
2119
  const ungroupedSessionIds = (0, react.useMemo)(() => {
2149
2120
  const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
@@ -2170,7 +2141,7 @@
2170
2141
  orderBy,
2171
2142
  sortByRecency: orderBy === "updated" && (previousOrder === void 0 || switchedToUpdated)
2172
2143
  });
2173
- 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);
2174
2145
  }
2175
2146
  }, [
2176
2147
  list,
@@ -2205,6 +2176,17 @@
2205
2176
  expandedGroups,
2206
2177
  sessionOrderByAccount
2207
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
+ ]);
2208
2190
  const now = Date.now();
2209
2191
  const commitSessionDrag = (activeDrag, over) => {
2210
2192
  if (sessionDropCommitted.current) return;
@@ -2351,6 +2333,9 @@
2351
2333
  onArchive: onSessionArchive,
2352
2334
  onUnarchive: onSessionUnarchive,
2353
2335
  onDeleteSession: onSessionDelete,
2336
+ onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
2337
+ onSessionRevealed?.(node.id);
2338
+ } : void 0,
2354
2339
  drag: {
2355
2340
  start: () => {
2356
2341
  sessionDropCommitted.current = false;
@@ -2404,7 +2389,7 @@
2404
2389
  ]
2405
2390
  });
2406
2391
  }
2407
- 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 }) {
2408
2393
  const list = useSessions((s) => s);
2409
2394
  const pendingInteractions = useSessionPendingInteraction((s) => s);
2410
2395
  const baseRows = (0, react.useMemo)(() => deriveFlat(list, archivedSessionIds, pendingInteractions, showArchived), [
@@ -2494,6 +2479,9 @@
2494
2479
  onArchive: onSessionArchive,
2495
2480
  onUnarchive: onSessionUnarchive,
2496
2481
  onDeleteSession: onSessionDelete,
2482
+ onReveal: node.id === revealSessionId ? () => {
2483
+ onSessionRevealed?.(node.id);
2484
+ } : void 0,
2497
2485
  flat: true,
2498
2486
  drag: {
2499
2487
  start: () => {
@@ -2594,6 +2582,18 @@
2594
2582
  }), (0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })]
2595
2583
  });
2596
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
+ };
2597
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 }) {
2598
2598
  const useEffectiveSessionPendingInteraction = useSessionPendingInteraction ?? useEmptySessionPendingInteraction;
2599
2599
  const workspaces = useWorkspaces((state) => state.items);
@@ -2608,6 +2608,11 @@
2608
2608
  const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
2609
2609
  const sessionUpdatedAtByAccount = useStore((s) => s.sessionUpdatedAtByAccount);
2610
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
+ };
2611
2616
  const [archivedToast, setArchivedToast] = (0, react.useState)(null);
2612
2617
  const archivedToastSeq = (0, react.useRef)(0);
2613
2618
  const showArchivedToast2 = (text) => {
@@ -3015,7 +3020,12 @@
3015
3020
  children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
3016
3021
  useSessions,
3017
3022
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3018
- open: guardedOpen,
3023
+ open: (sessionId) => {
3024
+ setRevealSessionId(sessionId);
3025
+ setQuery("");
3026
+ setSearchExpanded(false);
3027
+ guardedOpen(sessionId);
3028
+ },
3019
3029
  workspaces,
3020
3030
  archivedSessionIds,
3021
3031
  showArchived,
@@ -3027,6 +3037,8 @@
3027
3037
  useSessions,
3028
3038
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3029
3039
  open: guardedOpen,
3040
+ revealSessionId,
3041
+ onSessionRevealed: acknowledgeSessionReveal,
3030
3042
  forkSession,
3031
3043
  onSessionRename,
3032
3044
  onSessionArchive,
@@ -3046,6 +3058,8 @@
3046
3058
  }) : (0, react_jsx_runtime.jsx)(SessionTree, {
3047
3059
  useSessions,
3048
3060
  useSessionPendingInteraction: useEffectiveSessionPendingInteraction,
3061
+ revealSessionId,
3062
+ onSessionRevealed: acknowledgeSessionReveal,
3049
3063
  onSessionRename,
3050
3064
  onSessionArchive,
3051
3065
  onSessionUnarchive,
@@ -3908,8 +3922,7 @@
3908
3922
  "workspaces",
3909
3923
  "locale",
3910
3924
  "remote",
3911
- "typert",
3912
- ...hasSplitClientStore ? ["uiWorkspace"] : []
3925
+ "typert"
3913
3926
  ];
3914
3927
  function bindObservable(source) {
3915
3928
  return {
@@ -3917,18 +3930,58 @@
3917
3930
  subscribe: source.subscribe.bind(source)
3918
3931
  };
3919
3932
  }
3920
- async function archiveWorkspaceSessionsAndRefresh(registry, workspaceId, refresh) {
3921
- const result = await registry.archiveWorkspaceSessions(workspaceId);
3922
- if (!result.ok) throw new Error(result.error.message);
3923
- await refresh();
3924
- 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
+ };
3925
3978
  }
3926
3979
  async function apply(ctx) {
3980
+ applyWorkspaceBrowser(ctx);
3927
3981
  const remote = ctx.get("remote");
3928
3982
  let disposeRemote = () => {
3929
3983
  };
3930
3984
  if (remote !== void 0) disposeRemote = await remote.$mount(ARCHIVE_MANAGER_REMOTE);
3931
- applyWorkspaceBrowser(ctx);
3932
3985
  return async () => {
3933
3986
  await disposeRemote();
3934
3987
  };
@@ -3950,13 +4003,24 @@
3950
4003
  const uiWorkspaceAt = () => ctx.get("uiWorkspace");
3951
4004
  const archiveViewState = {};
3952
4005
  let archiveNavigation;
3953
- ctx.effect(() => {
3954
- archiveNavigation = allowArchivedNavigation(uiWorkspaceAt(), ctx.sessions, ctx.workspaces, {
3955
- 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?.()
3956
4010
  });
3957
4011
  return () => archiveNavigation.dispose();
3958
- }, "archive-manager: explicit archived navigation");
3959
- 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
+ };
3960
4024
  const searchSessions = async (query, signal) => {
3961
4025
  const result = await ctx.sessions.search(query, signal);
3962
4026
  if (!result.ok) throw new Error(result.error.message);
@@ -3975,17 +4039,16 @@
3975
4039
  console.warn("archive-manager: restored archived sessions but session list refresh failed:", error);
3976
4040
  }
3977
4041
  };
4042
+ const unarchiveOne = createUnarchiveSession(ctx.workspaces, () => ctx.get("remote.workspaceRegistry"));
3978
4043
  const unarchiveSession = async (sessionId) => {
3979
- const registry = ctx.get("remote.workspaceRegistry");
3980
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3981
- const result = await registry.unarchiveSession(sessionId);
3982
- if (!result.ok) throw new Error(result.error.message);
4044
+ await unarchiveOne(sessionId);
3983
4045
  await refreshSessionList();
3984
4046
  };
3985
4047
  const archiveWorkspaceSessions = async (workspaceId) => {
3986
- const registry = ctx.get("remote.workspaceRegistry");
3987
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
3988
- 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);
3989
4052
  };
3990
4053
  const deleteSession = async (sessionId) => {
3991
4054
  const registry = ctx.get("remote.workspaceRegistry");
@@ -3994,21 +4057,11 @@
3994
4057
  if (!result.ok) throw new Error(result.error.message);
3995
4058
  await refreshSessionList();
3996
4059
  };
3997
- const archiveSessions = async (sessionIds) => {
3998
- const registry = ctx.get("remote.workspaceRegistry");
3999
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
4000
- const result = await registry.archiveSessions(sessionIds);
4001
- if (!result.ok) throw new Error(result.error.message);
4002
- await refreshSessionList();
4003
- return result.value;
4004
- };
4060
+ const archiveSessions = async (sessionIds) => archiveSessionsViaOfficial(ctx.workspaces, sessionIds, refreshSessionList);
4005
4061
  const unarchiveSessions = async (target) => {
4006
- const registry = ctx.get("remote.workspaceRegistry");
4007
- if (registry === void 0) throw new Error("archive-manager remote service is unavailable");
4008
- const result = await registry.unarchiveSessions(target);
4009
- if (!result.ok) throw new Error(result.error.message);
4010
- await refreshSessionList();
4011
- 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);
4012
4065
  };
4013
4066
  const deleteArchivedSessions = async (target) => {
4014
4067
  const registry = ctx.get("remote.workspaceRegistry");
@@ -4079,18 +4132,28 @@
4079
4132
  createWorkspace: (input) => ctx.workspaces.create(input),
4080
4133
  hooks: { directoryFlow: browserFlowSource }
4081
4134
  });
4082
- ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
4083
- name: "sidebar.workspaces",
4084
- // 显示顺序:Codex UI(-1)→ 归档(-0.5)→ 官方默认(0)。
4085
- priority: -0.5,
4086
- children: { [DIRECTORY_FLOW_SLOT]: {
4087
- kind: "single",
4088
- scope: "root"
4089
- } },
4090
- store: createWorkspaceViewStore(),
4091
- inject: browserInjected,
4092
- locale: NS
4093
- }, 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
+ });
4094
4157
  mirrorDirectoryFlow(ctx, DIRECTORY_FLOW_SLOT);
4095
4158
  ctx.slots.inject("settings.section", () => ctx.slots.register({
4096
4159
  name: "settings.section",
@@ -4109,6 +4172,7 @@
4109
4172
  deleteArchivedSessions,
4110
4173
  archivedSessionMetadata,
4111
4174
  openConversation,
4175
+ focusSessionWorkspace,
4112
4176
  viewState: archiveViewState,
4113
4177
  t: ctx.locale.bind(NS)
4114
4178
  })
@@ -4136,8 +4200,13 @@
4136
4200
  archiveableWorkspaceSessionCount,
4137
4201
  archiveWorkspaceDialogTarget,
4138
4202
  archiveWorkspaceDialogFailureState,
4139
- archiveWorkspaceSessionsAndRefresh,
4203
+ archiveSessionsViaOfficial,
4204
+ unarchiveSessionsViaOfficial,
4205
+ createUnarchiveSession,
4140
4206
  createWorkspaceViewStore,
4207
+ migrateWorkspaceViewPersist,
4208
+ WORKSPACE_VIEW_PERSIST_KEY,
4209
+ LEGACY_WORKSPACE_VIEW_PERSIST_KEY,
4141
4210
  bindObservable,
4142
4211
  hasSplitClientStore,
4143
4212
  groupByWorkspace,