@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/CHANGELOG.md +18 -0
- package/CHANGELOG.zh-CN.md +18 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/cordis.patch.yml +7 -0
- package/lib/archive-experience.js +7 -2
- package/lib/client.js +216 -147
- package/lib/workspace.js +143 -239
- package/package.json +41 -41
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)
|
|
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:
|
|
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({
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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)
|
|
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(
|
|
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:
|
|
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
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
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
|
-
|
|
3954
|
-
archiveNavigation = allowArchivedNavigation(
|
|
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
|
-
}
|
|
3959
|
-
|
|
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
|
-
|
|
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
|
|
3987
|
-
|
|
3988
|
-
|
|
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
|
|
4007
|
-
|
|
4008
|
-
|
|
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", () =>
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
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
|
-
|
|
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,
|