spiracha 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/apps/ui/README.md +20 -2
- package/apps/ui/package.json +2 -2
- package/apps/ui/src/components/antigravity-conversations-table.tsx +50 -11
- package/apps/ui/src/components/antigravity-workspaces-table.tsx +1 -1
- package/apps/ui/src/components/app-shell.tsx +1 -0
- package/apps/ui/src/components/breadcrumbs.tsx +21 -3
- package/apps/ui/src/components/claude-code-sessions-table.tsx +42 -6
- package/apps/ui/src/components/claude-code-workspaces-table.tsx +3 -1
- package/apps/ui/src/components/cursor-threads-table.tsx +10 -47
- package/apps/ui/src/components/data-table.tsx +7 -5
- package/apps/ui/src/components/export-dialog.tsx +2 -12
- package/apps/ui/src/components/grok-sessions-table.tsx +146 -0
- package/apps/ui/src/components/grok-workspaces-table.tsx +53 -0
- package/apps/ui/src/components/kiro-sessions-table.tsx +40 -4
- package/apps/ui/src/components/opencode-sessions-table.tsx +40 -4
- package/apps/ui/src/components/page-header.tsx +15 -9
- package/apps/ui/src/components/qoder-sessions-table.tsx +23 -2
- package/apps/ui/src/components/qoder-workspaces-table.tsx +5 -1
- package/apps/ui/src/components/selection-actions-toolbar.tsx +80 -0
- package/apps/ui/src/components/threads-table.tsx +9 -46
- package/apps/ui/src/components/transcript-view.tsx +148 -52
- package/apps/ui/src/components/ui/select.tsx +1 -1
- package/apps/ui/src/lib/antigravity-conversation-state.ts +17 -4
- package/apps/ui/src/lib/antigravity-server.ts +152 -7
- package/apps/ui/src/lib/antigravity-transcript-events.ts +11 -3
- package/apps/ui/src/lib/claude-code-queries.ts +8 -0
- package/apps/ui/src/lib/claude-code-server.ts +175 -7
- package/apps/ui/src/lib/claude-code-transcript-events.ts +8 -1
- package/apps/ui/src/lib/codex-queries.ts +19 -3
- package/apps/ui/src/lib/codex-server.ts +135 -53
- package/apps/ui/src/lib/cursor-server.ts +48 -5
- package/apps/ui/src/lib/formatters.ts +3 -5
- package/apps/ui/src/lib/grok-queries.ts +22 -0
- package/apps/ui/src/lib/grok-server.ts +169 -0
- package/apps/ui/src/lib/grok-transcript-events.ts +149 -0
- package/apps/ui/src/lib/kiro-server.ts +85 -7
- package/apps/ui/src/lib/opencode-server.ts +85 -7
- package/apps/ui/src/lib/qoder-server.ts +67 -11
- package/apps/ui/src/lib/route-search.ts +114 -0
- package/apps/ui/src/lib/source-session-export-server.ts +86 -3
- package/apps/ui/src/lib/thread-transcript-load.ts +15 -0
- package/apps/ui/src/lib/workspace-delete-navigation.ts +12 -0
- package/apps/ui/src/routeTree.gen.ts +107 -0
- package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +76 -10
- package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +269 -31
- package/apps/ui/src/routes/api.v1.conversations.$source.$id.ts +4 -0
- package/apps/ui/src/routes/api.v1.conversations.delete.ts +12 -0
- package/apps/ui/src/routes/api.v1.conversations.export.ts +12 -0
- package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +143 -17
- package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +165 -26
- package/apps/ui/src/routes/cursor-threads.$composerId.tsx +2 -2
- package/apps/ui/src/routes/cursor.$workspaceKey.tsx +11 -1
- package/apps/ui/src/routes/grok-sessions.$sessionId.tsx +388 -0
- package/apps/ui/src/routes/grok.$workspaceKey.tsx +301 -0
- package/apps/ui/src/routes/grok.index.tsx +48 -0
- package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +66 -15
- package/apps/ui/src/routes/kiro.$workspaceKey.tsx +199 -32
- package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +105 -19
- package/apps/ui/src/routes/opencode.$workspaceKey.tsx +236 -44
- package/apps/ui/src/routes/opencode.index.tsx +16 -3
- package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +7 -4
- package/apps/ui/src/routes/qoder.$workspaceKey.tsx +75 -28
- package/apps/ui/src/routes/threads.$threadId.tsx +548 -64
- package/package.json +19 -18
- package/src/client.ts +134 -1
- package/src/lib/antigravity-db.ts +208 -32
- package/src/lib/antigravity-exporter-types.ts +3 -0
- package/src/lib/antigravity-projects.ts +201 -0
- package/src/lib/claude-code-db.ts +498 -32
- package/src/lib/claude-code-exporter-types.ts +5 -0
- package/src/lib/claude-code-transcript-phase.ts +60 -1
- package/src/lib/claude-code-transcript.ts +8 -5
- package/src/lib/codex-browser-export.ts +23 -27
- package/src/lib/codex-thread-cache.ts +41 -13
- package/src/lib/codex-thread-parser.ts +86 -35
- package/src/lib/codex-transcript-filter.ts +31 -0
- package/src/lib/codex-transcript-renderer.ts +39 -19
- package/src/lib/concurrency.ts +41 -0
- package/src/lib/conversation-api.ts +399 -19
- package/src/lib/conversation-data/antigravity-adapter.ts +21 -2
- package/src/lib/conversation-data/claude-code-adapter.ts +37 -6
- package/src/lib/conversation-data/codex-adapter.ts +28 -4
- package/src/lib/conversation-data/cursor-adapter.ts +35 -1
- package/src/lib/conversation-data/grok-adapter.ts +210 -0
- package/src/lib/conversation-data/index.ts +76 -1
- package/src/lib/conversation-data/kiro-adapter.ts +41 -7
- package/src/lib/conversation-data/opencode-adapter.ts +24 -2
- package/src/lib/conversation-data/qoder-adapter.ts +44 -19
- package/src/lib/conversation-data/types.ts +43 -0
- package/src/lib/conversation-zip-export.ts +57 -0
- package/src/lib/cursor-db.ts +34 -5
- package/src/lib/grok-db.ts +1026 -0
- package/src/lib/grok-exporter-types.ts +110 -0
- package/src/lib/grok-transcript-phase.ts +52 -0
- package/src/lib/grok-transcript.ts +154 -0
- package/src/lib/kiro-db.ts +52 -1
- package/src/lib/model-label.ts +11 -3
- package/src/lib/opencode-db.ts +598 -55
- package/src/lib/opencode-transcript.ts +8 -5
- package/src/lib/shared.ts +12 -0
- package/src/lib/transcript-load-limiter.ts +82 -0
- package/src/lib/ui-cache.ts +43 -17
- package/src/lib/ui-export-archive.ts +60 -0
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import type { ClaudeCodeSessionSummary, ClaudeCodeWorkspaceGroup } from '@spiracha/lib/claude-code-exporter-types';
|
|
2
|
-
import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
|
|
3
|
-
import { createFileRoute } from '@tanstack/react-router';
|
|
4
|
-
import { useDeferredValue, useState } from 'react';
|
|
2
|
+
import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
|
3
|
+
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
|
4
|
+
import { useDeferredValue, useMemo, useState } from 'react';
|
|
5
5
|
import { ClaudeCodeSessionsTable } from '#/components/claude-code-sessions-table';
|
|
6
|
+
import { DeleteConfirmDialog } from '#/components/delete-confirm-dialog';
|
|
6
7
|
import { ExportDialog } from '#/components/export-dialog';
|
|
7
8
|
import { ListSearchInput } from '#/components/list-search-input';
|
|
8
9
|
import { LoadingPanel } from '#/components/loading-panel';
|
|
9
10
|
import { PageHeader } from '#/components/page-header';
|
|
10
11
|
import { ReloadErrorPanel } from '#/components/reload-error-panel';
|
|
11
12
|
import { claudeCodeSessionsQueryOptions, claudeCodeWorkspacesQueryOptions } from '#/lib/claude-code-queries';
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
deleteClaudeCodeSessionFn,
|
|
15
|
+
deleteClaudeCodeSessionsFn,
|
|
16
|
+
exportClaudeCodeSessionFn,
|
|
17
|
+
exportClaudeCodeSessionsFn,
|
|
18
|
+
} from '#/lib/claude-code-server';
|
|
13
19
|
import { downloadTextFile, downloadUrlFile } from '#/lib/download';
|
|
14
20
|
import { matchesTextQuery } from '#/lib/text-filter';
|
|
21
|
+
import { isWorkspaceEmptiedByDelete } from '#/lib/workspace-delete-navigation';
|
|
15
22
|
|
|
16
23
|
type ExportDialogOptions = {
|
|
17
24
|
includeCommentary: boolean;
|
|
@@ -21,6 +28,15 @@ type ExportDialogOptions = {
|
|
|
21
28
|
zipArchive: boolean;
|
|
22
29
|
};
|
|
23
30
|
|
|
31
|
+
type PendingSessionDelete = {
|
|
32
|
+
sessions: ClaudeCodeSessionSummary[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type PendingSessionExport = {
|
|
36
|
+
label: string;
|
|
37
|
+
sessionIds: string[];
|
|
38
|
+
};
|
|
39
|
+
|
|
24
40
|
const findWorkspaceOrThrow = (workspaces: ClaudeCodeWorkspaceGroup[], workspaceKey: string) => {
|
|
25
41
|
const workspace = workspaces.find((candidate) => candidate.key === workspaceKey);
|
|
26
42
|
if (!workspace) {
|
|
@@ -30,6 +46,36 @@ const findWorkspaceOrThrow = (workspaces: ClaudeCodeWorkspaceGroup[], workspaceK
|
|
|
30
46
|
return workspace;
|
|
31
47
|
};
|
|
32
48
|
|
|
49
|
+
const buildSessionExport = (selectedSessions: ClaudeCodeSessionSummary[]): PendingSessionExport => ({
|
|
50
|
+
label: selectedSessions.length === 1 ? selectedSessions[0]!.title : `${selectedSessions.length} selected sessions`,
|
|
51
|
+
sessionIds: selectedSessions.map((session) => session.sessionId),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const getDeleteConfirmLabel = (pendingDelete: PendingSessionDelete | null, isPending: boolean) => {
|
|
55
|
+
if (isPending) {
|
|
56
|
+
return 'Deleting...';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return pendingDelete && pendingDelete.sessions.length > 1 ? 'Delete sessions' : 'Delete session';
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getDeleteDescription = (pendingDelete: PendingSessionDelete | null) => {
|
|
63
|
+
if (!pendingDelete) {
|
|
64
|
+
return 'Permanently delete the selected Claude Code sessions from disk.';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (pendingDelete.sessions.length === 1) {
|
|
68
|
+
return `Permanently delete "${pendingDelete.sessions[0]!.title}" from Claude Code history. This removes the session JSONL file from disk.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return `Permanently delete ${pendingDelete.sessions.length} selected Claude Code sessions from disk.`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getDeleteTitle = (pendingDelete: PendingSessionDelete | null) =>
|
|
75
|
+
pendingDelete && pendingDelete.sessions.length > 1
|
|
76
|
+
? `Delete ${pendingDelete.sessions.length} Claude Code sessions?`
|
|
77
|
+
: 'Delete this Claude Code session?';
|
|
78
|
+
|
|
33
79
|
export const Route = createFileRoute('/claude-code/$workspaceKey')({
|
|
34
80
|
component: ClaudeCodeWorkspacePage,
|
|
35
81
|
errorComponent: ClaudeCodeWorkspaceErrorComponent,
|
|
@@ -48,12 +94,15 @@ function ClaudeCodeWorkspaceErrorComponent({ error }: { error: Error }) {
|
|
|
48
94
|
}
|
|
49
95
|
|
|
50
96
|
function ClaudeCodeWorkspacePage() {
|
|
97
|
+
const navigate = useNavigate({ from: Route.fullPath });
|
|
51
98
|
const params = Route.useParams();
|
|
99
|
+
const queryClient = useQueryClient();
|
|
52
100
|
const workspaces = useSuspenseQuery(claudeCodeWorkspacesQueryOptions()).data;
|
|
53
101
|
const workspace = findWorkspaceOrThrow(workspaces, params.workspaceKey);
|
|
54
102
|
const sessions = useSuspenseQuery(claudeCodeSessionsQueryOptions(workspace.key)).data;
|
|
55
103
|
const [searchInput, setSearchInput] = useState('');
|
|
56
|
-
const [
|
|
104
|
+
const [pendingDelete, setPendingDelete] = useState<PendingSessionDelete | null>(null);
|
|
105
|
+
const [pendingExport, setPendingExport] = useState<PendingSessionExport | null>(null);
|
|
57
106
|
const deferredSearch = useDeferredValue(searchInput);
|
|
58
107
|
|
|
59
108
|
const exportMutation = useMutation({
|
|
@@ -62,16 +111,28 @@ function ClaudeCodeWorkspacePage() {
|
|
|
62
111
|
throw new Error('No Claude Code session selected for export');
|
|
63
112
|
}
|
|
64
113
|
|
|
65
|
-
const download =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
const download =
|
|
115
|
+
pendingExport.sessionIds.length === 1
|
|
116
|
+
? await exportClaudeCodeSessionFn({
|
|
117
|
+
data: {
|
|
118
|
+
includeCommentary: options.includeCommentary,
|
|
119
|
+
includeMetadata: options.includeMetadata,
|
|
120
|
+
includeTools: options.includeTools,
|
|
121
|
+
outputFormat: options.outputFormat,
|
|
122
|
+
sessionId: pendingExport.sessionIds[0]!,
|
|
123
|
+
zipArchive: options.zipArchive,
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
: await exportClaudeCodeSessionsFn({
|
|
127
|
+
data: {
|
|
128
|
+
includeCommentary: options.includeCommentary,
|
|
129
|
+
includeMetadata: options.includeMetadata,
|
|
130
|
+
includeTools: options.includeTools,
|
|
131
|
+
outputFormat: options.outputFormat,
|
|
132
|
+
sessionIds: pendingExport.sessionIds,
|
|
133
|
+
zipArchive: options.zipArchive,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
75
136
|
if (download.mode === 'download') {
|
|
76
137
|
downloadTextFile(download.fileName, download.content, download.mimeType);
|
|
77
138
|
return;
|
|
@@ -84,16 +145,62 @@ function ClaudeCodeWorkspacePage() {
|
|
|
84
145
|
},
|
|
85
146
|
});
|
|
86
147
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
session.
|
|
94
|
-
|
|
95
|
-
|
|
148
|
+
const deleteMutation = useMutation({
|
|
149
|
+
mutationFn: async (sessionIds: string[]) =>
|
|
150
|
+
sessionIds.length === 1
|
|
151
|
+
? deleteClaudeCodeSessionFn({ data: { sessionId: sessionIds[0]! } })
|
|
152
|
+
: deleteClaudeCodeSessionsFn({ data: { sessionIds } }),
|
|
153
|
+
onSuccess: async (_result, sessionIds) => {
|
|
154
|
+
const workspaceEmptied = isWorkspaceEmptiedByDelete(sessions, sessionIds, (session) => session.sessionId);
|
|
155
|
+
setPendingDelete(null);
|
|
156
|
+
if (workspaceEmptied) {
|
|
157
|
+
await navigate({ to: '/claude-code' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await Promise.all([
|
|
161
|
+
queryClient.invalidateQueries({ queryKey: ['claude-code-workspaces'] }),
|
|
162
|
+
queryClient.invalidateQueries({ queryKey: ['claude-code-sessions', workspace.key] }),
|
|
163
|
+
...sessionIds.map((sessionId) =>
|
|
164
|
+
queryClient.invalidateQueries({ queryKey: ['claude-code-session', sessionId] }),
|
|
165
|
+
),
|
|
166
|
+
]);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const visibleSessions = useMemo(
|
|
171
|
+
() =>
|
|
172
|
+
sessions.filter((session) =>
|
|
173
|
+
matchesTextQuery(deferredSearch, [
|
|
174
|
+
session.title,
|
|
175
|
+
session.sessionId,
|
|
176
|
+
session.model,
|
|
177
|
+
session.version,
|
|
178
|
+
session.gitBranch,
|
|
179
|
+
session.filePath,
|
|
180
|
+
]),
|
|
181
|
+
),
|
|
182
|
+
[deferredSearch, sessions],
|
|
96
183
|
);
|
|
184
|
+
const visibleSessionsById = useMemo(
|
|
185
|
+
() => new Map(visibleSessions.map((session) => [session.sessionId, session])),
|
|
186
|
+
[visibleSessions],
|
|
187
|
+
);
|
|
188
|
+
const lookupSelectedSessions = (sessionIds: string[]) =>
|
|
189
|
+
sessionIds
|
|
190
|
+
.map((sessionId) => visibleSessionsById.get(sessionId) ?? null)
|
|
191
|
+
.filter((session): session is ClaudeCodeSessionSummary => session !== null);
|
|
192
|
+
const openExportForSessions = (selectedSessions: ClaudeCodeSessionSummary[]) => {
|
|
193
|
+
if (selectedSessions.length === 0) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
setPendingExport(buildSessionExport(selectedSessions));
|
|
198
|
+
};
|
|
199
|
+
const openDeleteForSessions = (selectedSessions: ClaudeCodeSessionSummary[]) => {
|
|
200
|
+
if (selectedSessions.length > 0) {
|
|
201
|
+
setPendingDelete({ sessions: selectedSessions });
|
|
202
|
+
}
|
|
203
|
+
};
|
|
97
204
|
|
|
98
205
|
return (
|
|
99
206
|
<div className="space-y-6">
|
|
@@ -110,12 +217,19 @@ function ClaudeCodeWorkspacePage() {
|
|
|
110
217
|
title={workspace.label}
|
|
111
218
|
/>
|
|
112
219
|
|
|
113
|
-
<ClaudeCodeSessionsTable
|
|
220
|
+
<ClaudeCodeSessionsTable
|
|
221
|
+
sessions={visibleSessions}
|
|
222
|
+
onDeleteSession={(session) => openDeleteForSessions([session])}
|
|
223
|
+
onDeleteSessions={(sessionIds) => openDeleteForSessions(lookupSelectedSessions(sessionIds))}
|
|
224
|
+
onExportSession={(session) => openExportForSessions([session])}
|
|
225
|
+
onExportSessions={(sessionIds) => openExportForSessions(lookupSelectedSessions(sessionIds))}
|
|
226
|
+
/>
|
|
114
227
|
|
|
115
228
|
<ExportDialog
|
|
229
|
+
forceZipArchive={pendingExport ? pendingExport.sessionIds.length > 1 : false}
|
|
116
230
|
open={pendingExport !== null}
|
|
117
231
|
pending={exportMutation.isPending}
|
|
118
|
-
title={pendingExport ? `Export ${pendingExport.
|
|
232
|
+
title={pendingExport ? `Export ${pendingExport.label}` : 'Export session'}
|
|
119
233
|
onExport={(options) => exportMutation.mutate(options)}
|
|
120
234
|
onOpenChange={(open) => {
|
|
121
235
|
if (!open) {
|
|
@@ -130,6 +244,31 @@ function ClaudeCodeWorkspacePage() {
|
|
|
130
244
|
{exportMutation.error instanceof Error ? exportMutation.error.message : 'Session export failed'}
|
|
131
245
|
</p>
|
|
132
246
|
) : null}
|
|
247
|
+
|
|
248
|
+
<DeleteConfirmDialog
|
|
249
|
+
confirmLabel={getDeleteConfirmLabel(pendingDelete, deleteMutation.isPending)}
|
|
250
|
+
description={getDeleteDescription(pendingDelete)}
|
|
251
|
+
errorMessage={
|
|
252
|
+
deleteMutation.isError
|
|
253
|
+
? deleteMutation.error instanceof Error
|
|
254
|
+
? deleteMutation.error.message
|
|
255
|
+
: 'Session delete failed'
|
|
256
|
+
: null
|
|
257
|
+
}
|
|
258
|
+
open={pendingDelete !== null}
|
|
259
|
+
title={getDeleteTitle(pendingDelete)}
|
|
260
|
+
onConfirm={() => {
|
|
261
|
+
if (pendingDelete) {
|
|
262
|
+
deleteMutation.mutate(pendingDelete.sessions.map((session) => session.sessionId));
|
|
263
|
+
}
|
|
264
|
+
}}
|
|
265
|
+
onOpenChange={(open) => {
|
|
266
|
+
if (!open) {
|
|
267
|
+
setPendingDelete(null);
|
|
268
|
+
deleteMutation.reset();
|
|
269
|
+
}
|
|
270
|
+
}}
|
|
271
|
+
/>
|
|
133
272
|
</div>
|
|
134
273
|
);
|
|
135
274
|
}
|
|
@@ -12,7 +12,7 @@ import { MetadataSection } from '#/components/metadata-section';
|
|
|
12
12
|
import { MetricCard } from '#/components/metric-card';
|
|
13
13
|
import { PageHeader } from '#/components/page-header';
|
|
14
14
|
import { ReloadErrorPanel } from '#/components/reload-error-panel';
|
|
15
|
-
import { TranscriptView } from '#/components/transcript-view';
|
|
15
|
+
import { DEFAULT_SHOW_USER_MESSAGES, TranscriptView } from '#/components/transcript-view';
|
|
16
16
|
import { Button } from '#/components/ui/button';
|
|
17
17
|
import { Checkbox } from '#/components/ui/checkbox';
|
|
18
18
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '#/components/ui/tabs';
|
|
@@ -221,7 +221,7 @@ const CursorThreadDetailPage = () => {
|
|
|
221
221
|
const [showCommentary, setShowCommentary] = useState(false);
|
|
222
222
|
const [showExtraEvents, setShowExtraEvents] = useState(false);
|
|
223
223
|
const [showRawJson, setShowRawJson] = useState(false);
|
|
224
|
-
const [showUserMessages, setShowUserMessages] = useState(
|
|
224
|
+
const [showUserMessages, setShowUserMessages] = useState(DEFAULT_SHOW_USER_MESSAGES);
|
|
225
225
|
const transcriptEvents = useMemo(
|
|
226
226
|
() => (detail.transcript ? cursorTranscriptToThreadEvents(detail.transcript) : []),
|
|
227
227
|
[detail.transcript],
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from '#/lib/cursor-server';
|
|
22
22
|
import { downloadTextFile, downloadUrlFile } from '#/lib/download';
|
|
23
23
|
import { matchesTextQuery } from '#/lib/text-filter';
|
|
24
|
+
import { isWorkspaceEmptiedByDelete } from '#/lib/workspace-delete-navigation';
|
|
24
25
|
|
|
25
26
|
type PendingCursorDelete =
|
|
26
27
|
| { kind: 'threads'; threads: CursorThreadSummary[] }
|
|
@@ -153,8 +154,17 @@ const CursorWorkspacePage = () => {
|
|
|
153
154
|
return;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
|
|
157
|
+
const workspaceEmptied = isWorkspaceEmptiedByDelete(
|
|
158
|
+
threads,
|
|
159
|
+
target.threads.map((thread) => thread.composerId),
|
|
160
|
+
(thread) => thread.composerId,
|
|
161
|
+
);
|
|
157
162
|
setPendingDelete(null);
|
|
163
|
+
if (workspaceEmptied) {
|
|
164
|
+
await navigate({ to: '/cursor' });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await invalidateWorkspaceQueries();
|
|
158
168
|
},
|
|
159
169
|
});
|
|
160
170
|
|