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
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import type { ThreadEvent, ThreadTranscriptStats } from '@spiracha/lib/codex-browser-types';
|
|
2
|
+
import type { GrokSessionTranscript } from '@spiracha/lib/grok-exporter-types';
|
|
3
|
+
import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
|
4
|
+
import { createFileRoute, Link, useNavigate } from '@tanstack/react-router';
|
|
5
|
+
import { Download, Trash2 } from 'lucide-react';
|
|
6
|
+
import { useMemo, useState } from 'react';
|
|
7
|
+
import { Breadcrumbs } from '#/components/breadcrumbs';
|
|
8
|
+
import { DeleteConfirmDialog } from '#/components/delete-confirm-dialog';
|
|
9
|
+
import { ExportDialog } from '#/components/export-dialog';
|
|
10
|
+
import { JsonPanel } from '#/components/json-panel';
|
|
11
|
+
import { LoadingPanel } from '#/components/loading-panel';
|
|
12
|
+
import { MetadataSection } from '#/components/metadata-section';
|
|
13
|
+
import { MetricCard } from '#/components/metric-card';
|
|
14
|
+
import { PageHeader } from '#/components/page-header';
|
|
15
|
+
import { ReloadErrorPanel } from '#/components/reload-error-panel';
|
|
16
|
+
import { DEFAULT_SHOW_USER_MESSAGES, TranscriptView } from '#/components/transcript-view';
|
|
17
|
+
import { Button } from '#/components/ui/button';
|
|
18
|
+
import { Checkbox } from '#/components/ui/checkbox';
|
|
19
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '#/components/ui/tabs';
|
|
20
|
+
import { downloadTextFile, downloadUrlFile } from '#/lib/download';
|
|
21
|
+
import { formatDateTime, formatList, formatNumber } from '#/lib/formatters';
|
|
22
|
+
import { grokSessionDetailQueryOptions } from '#/lib/grok-queries';
|
|
23
|
+
import { deleteGrokSessionFn, exportGrokSessionFn } from '#/lib/grok-server';
|
|
24
|
+
import { getGrokThreadTranscriptStats, grokTranscriptToThreadEvents } from '#/lib/grok-transcript-events';
|
|
25
|
+
|
|
26
|
+
type ExportDialogOptions = {
|
|
27
|
+
includeCommentary: boolean;
|
|
28
|
+
includeMetadata: boolean;
|
|
29
|
+
includeTools: boolean;
|
|
30
|
+
outputFormat: 'md' | 'txt';
|
|
31
|
+
zipArchive: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type TranscriptControlsProps = {
|
|
35
|
+
rawJsonDisabled?: boolean;
|
|
36
|
+
showCommentary: boolean;
|
|
37
|
+
showExtraEvents: boolean;
|
|
38
|
+
showRawJson: boolean;
|
|
39
|
+
showToolCalls: boolean;
|
|
40
|
+
showUserMessages: boolean;
|
|
41
|
+
onShowCommentaryChange: (checked: boolean) => void;
|
|
42
|
+
onShowExtraEventsChange: (checked: boolean) => void;
|
|
43
|
+
onShowRawJsonChange: (checked: boolean) => void;
|
|
44
|
+
onShowToolCallsChange: (checked: boolean) => void;
|
|
45
|
+
onShowUserMessagesChange: (checked: boolean) => void;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const GrokSessionDetailErrorComponent = ({ error }: { error: Error }) => {
|
|
49
|
+
return <ReloadErrorPanel description={error.message} title="Failed to load Grok session" />;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const buildSessionMetadata = (detail: GrokSessionTranscript) => [
|
|
53
|
+
{ label: 'Session ID', value: <span data-mono="true">{detail.session.sessionId}</span> },
|
|
54
|
+
{
|
|
55
|
+
label: 'Workspace',
|
|
56
|
+
value: (
|
|
57
|
+
<Link
|
|
58
|
+
className="text-[var(--accent)]"
|
|
59
|
+
params={{ workspaceKey: detail.session.workspaceKey }}
|
|
60
|
+
to="/grok/$workspaceKey"
|
|
61
|
+
>
|
|
62
|
+
{detail.session.workspaceLabel}
|
|
63
|
+
</Link>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
{ label: 'Worktree', value: detail.session.worktree },
|
|
67
|
+
{ label: 'Session directory', value: detail.session.sessionDir },
|
|
68
|
+
{ label: 'Agent', value: detail.session.agentName ?? 'unknown' },
|
|
69
|
+
{ label: 'Model', value: detail.session.modelLabel ?? detail.session.currentModelId ?? 'unknown' },
|
|
70
|
+
{ label: 'Git branch', value: detail.session.gitBranch ?? 'unknown' },
|
|
71
|
+
{ label: 'Head commit', value: detail.session.headCommit ?? 'unknown' },
|
|
72
|
+
{ label: 'Created', value: <span suppressHydrationWarning>{formatDateTime(detail.session.createdAtMs)}</span> },
|
|
73
|
+
{ label: 'Updated', value: <span suppressHydrationWarning>{formatDateTime(detail.session.lastActiveAtMs)}</span> },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const buildTranscriptStatsItems = (
|
|
77
|
+
detail: GrokSessionTranscript,
|
|
78
|
+
events: ThreadEvent[],
|
|
79
|
+
stats: ThreadTranscriptStats,
|
|
80
|
+
) => [
|
|
81
|
+
{ label: 'Event kinds', value: formatList([...new Set(events.map((event) => event.kind))]) },
|
|
82
|
+
{ label: 'Messages', value: formatNumber(stats.messageCount) },
|
|
83
|
+
{ label: 'User messages', value: formatNumber(stats.userMessageCount) },
|
|
84
|
+
{ label: 'Assistant messages', value: formatNumber(stats.assistantMessageCount) },
|
|
85
|
+
{ label: 'Reasoning events', value: formatNumber(events.filter((event) => event.kind === 'reasoning').length) },
|
|
86
|
+
{ label: 'Final answers', value: formatNumber(stats.finalAnswerCount) },
|
|
87
|
+
{ label: 'Tool calls', value: formatNumber(stats.toolCallCount) },
|
|
88
|
+
{ label: 'Tool outputs', value: formatNumber(stats.toolOutputCount) },
|
|
89
|
+
{ label: 'Renderable parts', value: formatNumber(detail.renderablePartCount) },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const GrokTranscriptControls = ({
|
|
93
|
+
rawJsonDisabled = false,
|
|
94
|
+
showCommentary,
|
|
95
|
+
showExtraEvents,
|
|
96
|
+
showRawJson,
|
|
97
|
+
showToolCalls,
|
|
98
|
+
showUserMessages,
|
|
99
|
+
onShowCommentaryChange,
|
|
100
|
+
onShowExtraEventsChange,
|
|
101
|
+
onShowRawJsonChange,
|
|
102
|
+
onShowToolCallsChange,
|
|
103
|
+
onShowUserMessagesChange,
|
|
104
|
+
}: TranscriptControlsProps) => {
|
|
105
|
+
return (
|
|
106
|
+
<div className="flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]">
|
|
107
|
+
<div className="flex items-center gap-2 text-sm">
|
|
108
|
+
<Checkbox
|
|
109
|
+
checked={showToolCalls}
|
|
110
|
+
id="grok-transcript-show-tool-calls"
|
|
111
|
+
onCheckedChange={(checked) => onShowToolCallsChange(checked === true)}
|
|
112
|
+
/>
|
|
113
|
+
<label htmlFor="grok-transcript-show-tool-calls">Show tool calls</label>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex items-center gap-2 text-sm">
|
|
116
|
+
<Checkbox
|
|
117
|
+
checked={showCommentary}
|
|
118
|
+
id="grok-transcript-show-commentary"
|
|
119
|
+
onCheckedChange={(checked) => onShowCommentaryChange(checked === true)}
|
|
120
|
+
/>
|
|
121
|
+
<label htmlFor="grok-transcript-show-commentary">Show commentary</label>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="flex items-center gap-2 text-sm">
|
|
124
|
+
<Checkbox
|
|
125
|
+
checked={showExtraEvents}
|
|
126
|
+
id="grok-transcript-show-extra-events"
|
|
127
|
+
onCheckedChange={(checked) => onShowExtraEventsChange(checked === true)}
|
|
128
|
+
/>
|
|
129
|
+
<label htmlFor="grok-transcript-show-extra-events">Show extra events</label>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="flex items-center gap-2 text-sm">
|
|
132
|
+
<Checkbox
|
|
133
|
+
checked={showRawJson}
|
|
134
|
+
disabled={rawJsonDisabled}
|
|
135
|
+
id="grok-transcript-show-raw-json"
|
|
136
|
+
onCheckedChange={(checked) => onShowRawJsonChange(checked === true)}
|
|
137
|
+
/>
|
|
138
|
+
<label htmlFor="grok-transcript-show-raw-json">Raw JSON</label>
|
|
139
|
+
</div>
|
|
140
|
+
<div className="flex items-center gap-2 text-sm">
|
|
141
|
+
<Checkbox
|
|
142
|
+
checked={showUserMessages}
|
|
143
|
+
id="grok-transcript-show-user-messages"
|
|
144
|
+
onCheckedChange={(checked) => onShowUserMessagesChange(checked === true)}
|
|
145
|
+
/>
|
|
146
|
+
<label htmlFor="grok-transcript-show-user-messages">User</label>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const GrokRawPanels = ({ detail, events }: { detail: GrokSessionTranscript; events: ThreadEvent[] }) => {
|
|
153
|
+
return (
|
|
154
|
+
<div className="space-y-4">
|
|
155
|
+
<JsonPanel title="Session summary" value={detail.session} />
|
|
156
|
+
<JsonPanel title="Grok transcript entries" value={detail.entries} />
|
|
157
|
+
<JsonPanel title="Transcript events" value={events} />
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const GrokSessionDetailPage = () => {
|
|
163
|
+
const navigate = useNavigate();
|
|
164
|
+
const queryClient = useQueryClient();
|
|
165
|
+
const detail = useSuspenseQuery(grokSessionDetailQueryOptions(Route.useParams().sessionId)).data;
|
|
166
|
+
const [deleteOpen, setDeleteOpen] = useState(false);
|
|
167
|
+
const [pendingExport, setPendingExport] = useState(false);
|
|
168
|
+
const [showToolCalls, setShowToolCalls] = useState(false);
|
|
169
|
+
const [showCommentary, setShowCommentary] = useState(false);
|
|
170
|
+
const [showExtraEvents, setShowExtraEvents] = useState(false);
|
|
171
|
+
const [showRawJson, setShowRawJson] = useState(false);
|
|
172
|
+
const [showUserMessages, setShowUserMessages] = useState(DEFAULT_SHOW_USER_MESSAGES);
|
|
173
|
+
const transcriptEvents = useMemo(() => grokTranscriptToThreadEvents(detail), [detail]);
|
|
174
|
+
const transcriptStats = useMemo(() => getGrokThreadTranscriptStats(transcriptEvents), [transcriptEvents]);
|
|
175
|
+
|
|
176
|
+
const exportSessionMutation = useMutation({
|
|
177
|
+
mutationFn: async (options: ExportDialogOptions) => {
|
|
178
|
+
const download = await exportGrokSessionFn({
|
|
179
|
+
data: {
|
|
180
|
+
includeCommentary: options.includeCommentary,
|
|
181
|
+
includeMetadata: options.includeMetadata,
|
|
182
|
+
includeTools: options.includeTools,
|
|
183
|
+
outputFormat: options.outputFormat,
|
|
184
|
+
sessionId: detail.session.sessionId,
|
|
185
|
+
zipArchive: options.zipArchive,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
if (download.mode === 'download') {
|
|
189
|
+
downloadTextFile(download.fileName, download.content, download.mimeType);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await downloadUrlFile(download.fileName, download.downloadUrl);
|
|
194
|
+
},
|
|
195
|
+
onSuccess: () => {
|
|
196
|
+
setPendingExport(false);
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const deleteSessionMutation = useMutation({
|
|
201
|
+
mutationFn: () => deleteGrokSessionFn({ data: { sessionId: detail.session.sessionId } }),
|
|
202
|
+
onSuccess: async () => {
|
|
203
|
+
await Promise.all([
|
|
204
|
+
queryClient.invalidateQueries({ queryKey: ['grok-workspaces'] }),
|
|
205
|
+
queryClient.invalidateQueries({ queryKey: ['grok-sessions', detail.session.workspaceKey] }),
|
|
206
|
+
queryClient.invalidateQueries({ queryKey: ['grok-session', detail.session.sessionId] }),
|
|
207
|
+
]);
|
|
208
|
+
navigate({
|
|
209
|
+
params: { workspaceKey: detail.session.workspaceKey },
|
|
210
|
+
to: '/grok/$workspaceKey',
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div className="space-y-6">
|
|
217
|
+
<PageHeader
|
|
218
|
+
actions={
|
|
219
|
+
<>
|
|
220
|
+
<Button
|
|
221
|
+
className="rounded-full"
|
|
222
|
+
type="button"
|
|
223
|
+
variant="outline"
|
|
224
|
+
onClick={() => setPendingExport(true)}
|
|
225
|
+
>
|
|
226
|
+
<Download className="mr-2 size-4" />
|
|
227
|
+
Export
|
|
228
|
+
</Button>
|
|
229
|
+
<Button
|
|
230
|
+
className="rounded-full border-[var(--destructive)]/20 text-[var(--destructive)]"
|
|
231
|
+
type="button"
|
|
232
|
+
variant="outline"
|
|
233
|
+
onClick={() => setDeleteOpen(true)}
|
|
234
|
+
>
|
|
235
|
+
<Trash2 className="mr-2 size-4" />
|
|
236
|
+
Delete
|
|
237
|
+
</Button>
|
|
238
|
+
</>
|
|
239
|
+
}
|
|
240
|
+
breadcrumb={
|
|
241
|
+
<Breadcrumbs
|
|
242
|
+
items={[
|
|
243
|
+
{ label: 'Grok', to: '/grok' },
|
|
244
|
+
{
|
|
245
|
+
label: detail.session.workspaceLabel,
|
|
246
|
+
params: { workspaceKey: detail.session.workspaceKey },
|
|
247
|
+
to: '/grok/$workspaceKey',
|
|
248
|
+
},
|
|
249
|
+
{ label: detail.session.title },
|
|
250
|
+
]}
|
|
251
|
+
/>
|
|
252
|
+
}
|
|
253
|
+
eyebrow="Grok session"
|
|
254
|
+
subtitle="Session detail for the selected local Grok CLI conversation."
|
|
255
|
+
title={detail.session.title}
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
259
|
+
<MetricCard label="Messages" value={formatNumber(detail.session.messageCount)} />
|
|
260
|
+
<MetricCard label="Tool calls" value={formatNumber(detail.session.toolCallCount)} />
|
|
261
|
+
<MetricCard label="Reasoning" value={formatNumber(detail.session.reasoningCount)} />
|
|
262
|
+
<MetricCard label="Renderable parts" value={formatNumber(detail.renderablePartCount)} />
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<Tabs className="space-y-4" defaultValue="transcript">
|
|
266
|
+
<TabsList className="grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1">
|
|
267
|
+
<TabsTrigger className="rounded-full px-5 text-sm" value="transcript">
|
|
268
|
+
Transcript
|
|
269
|
+
</TabsTrigger>
|
|
270
|
+
<TabsTrigger className="rounded-full px-5 text-sm" value="metadata">
|
|
271
|
+
Metadata
|
|
272
|
+
</TabsTrigger>
|
|
273
|
+
<TabsTrigger className="rounded-full px-5 text-sm" value="raw">
|
|
274
|
+
Raw
|
|
275
|
+
</TabsTrigger>
|
|
276
|
+
</TabsList>
|
|
277
|
+
|
|
278
|
+
<TabsContent className="space-y-3" value="transcript">
|
|
279
|
+
<GrokTranscriptControls
|
|
280
|
+
rawJsonDisabled={Boolean(detail.rawPayloadsOmitted) || transcriptEvents.length === 0}
|
|
281
|
+
showCommentary={showCommentary}
|
|
282
|
+
showExtraEvents={showExtraEvents}
|
|
283
|
+
showRawJson={showRawJson}
|
|
284
|
+
showToolCalls={showToolCalls}
|
|
285
|
+
showUserMessages={showUserMessages}
|
|
286
|
+
onShowCommentaryChange={setShowCommentary}
|
|
287
|
+
onShowExtraEventsChange={setShowExtraEvents}
|
|
288
|
+
onShowRawJsonChange={setShowRawJson}
|
|
289
|
+
onShowToolCallsChange={setShowToolCalls}
|
|
290
|
+
onShowUserMessagesChange={setShowUserMessages}
|
|
291
|
+
/>
|
|
292
|
+
{transcriptEvents.length > 0 ? (
|
|
293
|
+
<TranscriptView
|
|
294
|
+
assistantModel={detail.session.modelLabel ?? detail.session.currentModelId}
|
|
295
|
+
events={transcriptEvents}
|
|
296
|
+
projectPath={detail.session.worktree}
|
|
297
|
+
showCommentary={showCommentary}
|
|
298
|
+
showExtraEvents={showExtraEvents}
|
|
299
|
+
showRawJson={showRawJson && !detail.rawPayloadsOmitted}
|
|
300
|
+
showToolCalls={showToolCalls}
|
|
301
|
+
showUserMessages={showUserMessages}
|
|
302
|
+
/>
|
|
303
|
+
) : (
|
|
304
|
+
<section className="rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]">
|
|
305
|
+
<h3 className="font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]">
|
|
306
|
+
Transcript
|
|
307
|
+
</h3>
|
|
308
|
+
<p className="mt-4 text-[var(--muted-foreground)] text-sm">
|
|
309
|
+
No renderable Grok transcript content was found for this session.
|
|
310
|
+
</p>
|
|
311
|
+
</section>
|
|
312
|
+
)}
|
|
313
|
+
</TabsContent>
|
|
314
|
+
|
|
315
|
+
<TabsContent value="metadata">
|
|
316
|
+
<div className="grid gap-4 xl:grid-cols-[0.95fr_1.05fr]">
|
|
317
|
+
<MetadataSection items={buildSessionMetadata(detail)} title="Session metadata" />
|
|
318
|
+
<MetadataSection
|
|
319
|
+
items={buildTranscriptStatsItems(detail, transcriptEvents, transcriptStats)}
|
|
320
|
+
title="Transcript stats"
|
|
321
|
+
/>
|
|
322
|
+
</div>
|
|
323
|
+
</TabsContent>
|
|
324
|
+
|
|
325
|
+
<TabsContent value="raw">
|
|
326
|
+
{detail.rawPayloadsOmitted ? (
|
|
327
|
+
<section className="mb-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] p-4 text-[var(--muted-foreground)] text-sm">
|
|
328
|
+
Raw Grok payloads are omitted from the default session view to keep large transcripts
|
|
329
|
+
reloadable.
|
|
330
|
+
</section>
|
|
331
|
+
) : null}
|
|
332
|
+
<GrokRawPanels detail={detail} events={transcriptEvents} />
|
|
333
|
+
</TabsContent>
|
|
334
|
+
</Tabs>
|
|
335
|
+
|
|
336
|
+
<ExportDialog
|
|
337
|
+
errorMessage={
|
|
338
|
+
exportSessionMutation.isError
|
|
339
|
+
? exportSessionMutation.error instanceof Error
|
|
340
|
+
? exportSessionMutation.error.message
|
|
341
|
+
: 'Export failed'
|
|
342
|
+
: null
|
|
343
|
+
}
|
|
344
|
+
open={pendingExport}
|
|
345
|
+
pending={exportSessionMutation.isPending}
|
|
346
|
+
title={`Export ${detail.session.title}`}
|
|
347
|
+
onExport={(options) => exportSessionMutation.mutate(options)}
|
|
348
|
+
onOpenChange={(open) => {
|
|
349
|
+
setPendingExport(open);
|
|
350
|
+
if (!open) {
|
|
351
|
+
exportSessionMutation.reset();
|
|
352
|
+
}
|
|
353
|
+
}}
|
|
354
|
+
/>
|
|
355
|
+
|
|
356
|
+
<DeleteConfirmDialog
|
|
357
|
+
confirmLabel={deleteSessionMutation.isPending ? 'Deleting...' : 'Delete session'}
|
|
358
|
+
description="Permanently delete this Grok session from local history. This removes the session directory and transcript files."
|
|
359
|
+
errorMessage={
|
|
360
|
+
deleteSessionMutation.isError
|
|
361
|
+
? deleteSessionMutation.error instanceof Error
|
|
362
|
+
? deleteSessionMutation.error.message
|
|
363
|
+
: 'Session delete failed'
|
|
364
|
+
: null
|
|
365
|
+
}
|
|
366
|
+
open={deleteOpen}
|
|
367
|
+
title="Delete this Grok session?"
|
|
368
|
+
onConfirm={() => deleteSessionMutation.mutate()}
|
|
369
|
+
onOpenChange={(open) => {
|
|
370
|
+
setDeleteOpen(open);
|
|
371
|
+
if (!open) {
|
|
372
|
+
deleteSessionMutation.reset();
|
|
373
|
+
}
|
|
374
|
+
}}
|
|
375
|
+
/>
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
export const Route = createFileRoute('/grok-sessions/$sessionId')({
|
|
381
|
+
component: GrokSessionDetailPage,
|
|
382
|
+
errorComponent: GrokSessionDetailErrorComponent,
|
|
383
|
+
loader: ({ context, params }) =>
|
|
384
|
+
context.queryClient.ensureQueryData(grokSessionDetailQueryOptions(params.sessionId)),
|
|
385
|
+
pendingComponent: () => (
|
|
386
|
+
<LoadingPanel description="Loading the Grok transcript and session metadata." title="Loading session" />
|
|
387
|
+
),
|
|
388
|
+
});
|