spiracha 1.0.0 → 1.1.1
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/AGENTS.md +31 -1
- package/README.md +61 -7
- package/apps/ui/AGENTS.md +70 -0
- package/apps/ui/README.md +72 -0
- package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +1 -0
- package/apps/ui/dist/client/assets/analytics-CqWZmyV6.js +1 -0
- package/apps/ui/dist/client/assets/checkbox-DXM4lkJq.js +1 -0
- package/apps/ui/dist/client/assets/data-table-DnPYMPCD.js +4 -0
- package/apps/ui/dist/client/assets/delete-confirm-dialog-CcZaRX33.js +11 -0
- package/apps/ui/dist/client/assets/download-DOwxk-cG.js +1 -0
- package/apps/ui/dist/client/assets/es2015-Bm0kEzx2.js +41 -0
- package/apps/ui/dist/client/assets/formatters-C12LmYaa.js +1 -0
- package/apps/ui/dist/client/assets/index-DdJ7ahIt.js +22 -0
- package/apps/ui/dist/client/assets/input-CEsI7EpI.js +1 -0
- package/apps/ui/dist/client/assets/metric-card-9jwBF7rG.js +1 -0
- package/apps/ui/dist/client/assets/page-header-Dr_h1CVv.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-uyNGnpjH.js +1 -0
- package/apps/ui/dist/client/assets/projects._project-zoM8d2nH.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-D1CWVN-O.js +1 -0
- package/apps/ui/dist/client/assets/projects.index-DukMuny6.js +1 -0
- package/apps/ui/dist/client/assets/routes-Gr2Wwh83.js +1 -0
- package/apps/ui/dist/client/assets/select-CFim44gT.js +1 -0
- package/apps/ui/dist/client/assets/settings-DqhyDxo2.js +1 -0
- package/apps/ui/dist/client/assets/styles-CMrP9Jb4.css +1 -0
- package/apps/ui/dist/client/assets/threads._threadId-DT75NiBa.js +1 -0
- package/apps/ui/dist/client/assets/threads._threadId-Df5VXIuZ.js +7 -0
- package/apps/ui/dist/client/favicon.ico +0 -0
- package/apps/ui/dist/client/logo192.png +0 -0
- package/apps/ui/dist/client/logo512.png +0 -0
- package/apps/ui/dist/client/manifest.json +25 -0
- package/apps/ui/dist/client/robots.txt +3 -0
- package/apps/ui/dist/server/assets/__23tanstack-start-plugin-adapters-BzCA6dXo.js +5 -0
- package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-C0V305Nt.js +99 -0
- package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +6 -0
- package/apps/ui/dist/server/assets/analytics-BMxW_bZL.js +139 -0
- package/apps/ui/dist/server/assets/button-CmTDnzOn.js +46 -0
- package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +19 -0
- package/apps/ui/dist/server/assets/codex-queries-CAF6HYiG.js +109 -0
- package/apps/ui/dist/server/assets/codex-server-BFZq2Y2O.js +2062 -0
- package/apps/ui/dist/server/assets/data-table-Cdct823O.js +189 -0
- package/apps/ui/dist/server/assets/delete-confirm-dialog-CWqcTXTF.js +139 -0
- package/apps/ui/dist/server/assets/download-C5rkk_Bo.js +289 -0
- package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +91 -0
- package/apps/ui/dist/server/assets/input-B4tEzctc.js +46 -0
- package/apps/ui/dist/server/assets/loading-panel-DbLdvjtR.js +27 -0
- package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +23 -0
- package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +13 -0
- package/apps/ui/dist/server/assets/page-header-CxdZM86z.js +25 -0
- package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +31 -0
- package/apps/ui/dist/server/assets/projects._project-CJ7l0ynC.js +18 -0
- package/apps/ui/dist/server/assets/projects._project-CLSohrBp.js +26 -0
- package/apps/ui/dist/server/assets/projects._project-CcJLp_A8.js +337 -0
- package/apps/ui/dist/server/assets/projects.index-CaplpeMy.js +26 -0
- package/apps/ui/dist/server/assets/projects.index-srtogpuF.js +172 -0
- package/apps/ui/dist/server/assets/router-C_w-haH6.js +307 -0
- package/apps/ui/dist/server/assets/routes-BhbxvJE7.js +34 -0
- package/apps/ui/dist/server/assets/routes-CPe-ppmC.js +169 -0
- package/apps/ui/dist/server/assets/select-GW76p-ld.js +76 -0
- package/apps/ui/dist/server/assets/settings-MvWDgc1u.js +100 -0
- package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +52 -0
- package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +13 -0
- package/apps/ui/dist/server/assets/start-HeKLHD9b.js +4 -0
- package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +26 -0
- package/apps/ui/dist/server/assets/threads._threadId-Ba7vv6-K.js +18 -0
- package/apps/ui/dist/server/assets/threads._threadId-euyNckhj.js +1059 -0
- package/apps/ui/dist/server/assets/utils-C_uf36nf.js +8 -0
- package/apps/ui/dist/server/server.js +5678 -0
- package/package.json +53 -7
- package/src/export-chats.ts +4 -18
- package/src/lib/claude-exporter.ts +1 -1
- package/src/lib/codex-analytics.ts +100 -0
- package/src/lib/codex-browser-db.ts +605 -0
- package/src/lib/codex-browser-export.ts +429 -0
- package/src/lib/codex-browser-types.ts +224 -0
- package/src/lib/codex-exporter-cli.ts +6 -1
- package/src/lib/codex-exporter-db.ts +19 -20
- package/src/lib/codex-exporter-transcript.ts +158 -34
- package/src/lib/codex-exporter-types.ts +8 -0
- package/src/lib/codex-thread-cache.ts +58 -0
- package/src/lib/codex-thread-parser.ts +604 -0
- package/src/lib/interactive-cli.ts +10 -25
- package/src/lib/model-label.ts +24 -0
- package/src/lib/native-open.ts +54 -0
- package/src/lib/path-transforms.ts +46 -0
- package/src/lib/shared.ts +15 -1
- package/src/lib/sqlite-error.ts +14 -0
- package/src/lib/sqlite-retry.ts +53 -0
- package/src/lib/ui-cache.ts +96 -0
- package/src/lib/ui-export-files.ts +77 -0
- package/src/mcp-server.ts +1 -0
- package/src/spiracha.ts +16 -4
- package/src/ui-cli.ts +310 -0
|
@@ -0,0 +1,1059 @@
|
|
|
1
|
+
import { t as applyPathTransforms$1 } from "./path-transforms-DL2IwtYd.js";
|
|
2
|
+
import { t as cn } from "./utils-C_uf36nf.js";
|
|
3
|
+
import { t as Button } from "./button-CmTDnzOn.js";
|
|
4
|
+
import { n as useSettings } from "./settings-store-DpEJEQ7M.js";
|
|
5
|
+
import { a as threadSnapshotQueryOptions, c as deleteThreadFn, o as threadTranscriptQueryOptions, u as exportThreadFn } from "./codex-queries-CAF6HYiG.js";
|
|
6
|
+
import { t as Route } from "./threads._threadId-Ba7vv6-K.js";
|
|
7
|
+
import { t as Checkbox$1 } from "./checkbox-C0hovF41.js";
|
|
8
|
+
import { t as MetricCard } from "./metric-card-ByEeLu0r.js";
|
|
9
|
+
import { t as PageHeader } from "./page-header-CxdZM86z.js";
|
|
10
|
+
import { a as formatModelLabel, i as formatList, n as formatBytes, r as formatDateTime, s as formatTokens, t as formatBooleanLabel } from "./formatters-FJaGZgJk.js";
|
|
11
|
+
import { t as DeleteConfirmDialog } from "./delete-confirm-dialog-CWqcTXTF.js";
|
|
12
|
+
import { n as downloadUrlFile, r as ExportDialog, t as downloadTextFile } from "./download-C5rkk_Bo.js";
|
|
13
|
+
import { useEffect, useRef, useState } from "react";
|
|
14
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
|
|
17
|
+
import { Check, Copy, Download, Trash2 } from "lucide-react";
|
|
18
|
+
import { cva } from "class-variance-authority";
|
|
19
|
+
import { Slot, Tabs } from "radix-ui";
|
|
20
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
21
|
+
//#region src/components/json-panel.tsx
|
|
22
|
+
function JsonPanel({ title, value }) {
|
|
23
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
24
|
+
className: "rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]",
|
|
25
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
26
|
+
className: "font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]",
|
|
27
|
+
children: title
|
|
28
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
29
|
+
className: "mt-4 overflow-x-auto rounded-2xl border border-[var(--border)] bg-[var(--code-background)] p-4 text-[var(--code-foreground)] text-xs leading-5",
|
|
30
|
+
children: JSON.stringify(value, null, 2)
|
|
31
|
+
})]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/components/metadata-section.tsx
|
|
36
|
+
function MetadataSection({ items, title }) {
|
|
37
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
38
|
+
className: "rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]",
|
|
39
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
40
|
+
className: "font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]",
|
|
41
|
+
children: title
|
|
42
|
+
}), /* @__PURE__ */ jsx("dl", {
|
|
43
|
+
className: "mt-4 space-y-3",
|
|
44
|
+
children: items.map((item) => /* @__PURE__ */ jsxs("div", {
|
|
45
|
+
className: "grid gap-1 sm:grid-cols-[11rem_1fr] sm:items-start",
|
|
46
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
47
|
+
className: "font-medium text-[var(--muted-foreground)] text-xs uppercase tracking-[0.14em]",
|
|
48
|
+
children: item.label
|
|
49
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
50
|
+
className: "min-w-0 text-sm leading-6",
|
|
51
|
+
children: item.value
|
|
52
|
+
})]
|
|
53
|
+
}, item.label))
|
|
54
|
+
})]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/components/ui/badge.tsx
|
|
59
|
+
var badgeVariants = cva("inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", {
|
|
60
|
+
defaultVariants: { variant: "default" },
|
|
61
|
+
variants: { variant: {
|
|
62
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
63
|
+
destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
|
|
64
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
65
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
66
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
67
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90"
|
|
68
|
+
} }
|
|
69
|
+
});
|
|
70
|
+
function Badge({ className, variant = "default", asChild = false, ...props }) {
|
|
71
|
+
return /* @__PURE__ */ jsx(asChild ? Slot.Root : "span", {
|
|
72
|
+
"data-slot": "badge",
|
|
73
|
+
"data-variant": variant,
|
|
74
|
+
className: cn(badgeVariants({ variant }), className),
|
|
75
|
+
...props
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/lib/path-utils.ts
|
|
80
|
+
var applyPathTransforms = (text, settings, projectPath) => {
|
|
81
|
+
return applyPathTransforms$1(text, {
|
|
82
|
+
...settings,
|
|
83
|
+
projectPath
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/components/transcript-view.tsx
|
|
88
|
+
var isCommentaryMessage = (event) => event.kind === "message" && event.role === "assistant" && event.phase === "commentary";
|
|
89
|
+
var shouldShowEvent = (event, showToolCalls, showExtraEvents, showCommentary) => {
|
|
90
|
+
if (isCommentaryMessage(event) && !showCommentary) return false;
|
|
91
|
+
if (event.kind === "message") return !event.isHiddenByDefault || showExtraEvents;
|
|
92
|
+
if (event.kind === "tool_call" || event.kind === "tool_output") return showToolCalls;
|
|
93
|
+
return showExtraEvents;
|
|
94
|
+
};
|
|
95
|
+
var getEventTone = (event) => {
|
|
96
|
+
if (event.kind === "message" && event.role === "assistant") return "border-[var(--accent)]/40 bg-[var(--panel)]";
|
|
97
|
+
if (event.kind === "message" && event.role === "user") return "border-[var(--border)] bg-[var(--panel-secondary)]";
|
|
98
|
+
if (event.kind === "tool_call" || event.kind === "tool_output") return "border-[var(--border)] bg-[var(--code-background)]";
|
|
99
|
+
return "border-[var(--border)] bg-[var(--panel-secondary)]/80";
|
|
100
|
+
};
|
|
101
|
+
var getMessageTitle = (event, assistantModel) => {
|
|
102
|
+
const modelLabel = formatModelLabel(event.model ?? assistantModel);
|
|
103
|
+
if (event.variant === "agent_message") return event.role === "assistant" ? modelLabel : "Assistant update";
|
|
104
|
+
return event.role === "assistant" ? modelLabel : "User";
|
|
105
|
+
};
|
|
106
|
+
var getNonMessageTitle = (event) => {
|
|
107
|
+
switch (event.kind) {
|
|
108
|
+
case "tool_call": return `Tool call: ${event.name}`;
|
|
109
|
+
case "tool_output": return "Tool output";
|
|
110
|
+
case "task_started": return "Task started";
|
|
111
|
+
case "task_complete": return "Task complete";
|
|
112
|
+
case "token_count": return "Token update";
|
|
113
|
+
case "reasoning": return "Reasoning";
|
|
114
|
+
case "web_search": return "Web search";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var getEventTitle = (event, assistantModel) => event.kind === "message" ? getMessageTitle(event, assistantModel) : getNonMessageTitle(event);
|
|
118
|
+
var copyToClipboard = async (text) => {
|
|
119
|
+
if (typeof navigator !== "undefined" && navigator.clipboard) try {
|
|
120
|
+
await navigator.clipboard.writeText(text);
|
|
121
|
+
return true;
|
|
122
|
+
} catch {}
|
|
123
|
+
return false;
|
|
124
|
+
};
|
|
125
|
+
var getEventMarkdownBody = (event, transform) => {
|
|
126
|
+
switch (event.kind) {
|
|
127
|
+
case "message": return transform(event.text || "No text content");
|
|
128
|
+
case "tool_call": return [event.command ?? event.name, event.workdir].filter((value) => Boolean(value)).map(transform).join("\n\n");
|
|
129
|
+
case "tool_output": return transform(event.summary || event.outputText || "");
|
|
130
|
+
case "task_started": return `Context window: ${event.modelContextWindow ?? "n/a"}\n\nCollaboration mode: ${event.collaborationModeKind ?? "n/a"}`;
|
|
131
|
+
case "task_complete": return `Duration: ${event.durationMs ?? "n/a"} ms\n\nFirst token: ${event.timeToFirstTokenMs ?? "n/a"} ms`;
|
|
132
|
+
case "token_count": return JSON.stringify(event.rateLimits, null, 2);
|
|
133
|
+
case "reasoning": return event.summary.join(" ") || "Reasoning content is not directly available.";
|
|
134
|
+
case "web_search": return [`Phase: ${event.phase}${event.status ? ` · ${event.status}` : ""}`, event.query ? transform(event.query) : null].filter(Boolean).join("\n\n");
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
var getEventMarkdown = (event, assistantModel, transform) => `## ${getEventTitle(event, assistantModel)}\n\n${getEventMarkdownBody(event, transform)}`;
|
|
138
|
+
var renderMessageBody = (event, t) => /* @__PURE__ */ jsx("p", {
|
|
139
|
+
className: "min-w-0 whitespace-pre-wrap break-words text-sm leading-6 [overflow-wrap:anywhere]",
|
|
140
|
+
children: t(event.text || "No text content")
|
|
141
|
+
});
|
|
142
|
+
var renderToolCallBody = (event, t) => /* @__PURE__ */ jsxs("div", {
|
|
143
|
+
className: "space-y-2 text-sm",
|
|
144
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "min-w-0 break-words font-medium [overflow-wrap:anywhere]",
|
|
146
|
+
children: t(event.command ?? event.name)
|
|
147
|
+
}), event.workdir ? /* @__PURE__ */ jsx("p", {
|
|
148
|
+
className: "font-mono text-[var(--muted-foreground)] text-xs",
|
|
149
|
+
children: t(event.workdir)
|
|
150
|
+
}) : null]
|
|
151
|
+
});
|
|
152
|
+
var renderToolOutputBody = (event, t) => /* @__PURE__ */ jsx("p", {
|
|
153
|
+
className: "min-w-0 whitespace-pre-wrap break-words text-sm leading-6 [overflow-wrap:anywhere]",
|
|
154
|
+
children: t(event.summary || event.outputText || "")
|
|
155
|
+
});
|
|
156
|
+
var renderTaskStartedBody = (event) => /* @__PURE__ */ jsxs("div", {
|
|
157
|
+
className: "text-[var(--muted-foreground)] text-sm",
|
|
158
|
+
children: [
|
|
159
|
+
"Context window: ",
|
|
160
|
+
event.modelContextWindow ?? "n/a",
|
|
161
|
+
" · Collaboration mode: ",
|
|
162
|
+
event.collaborationModeKind ?? "n/a"
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
var renderTaskCompleteBody = (event) => /* @__PURE__ */ jsxs("div", {
|
|
166
|
+
className: "text-[var(--muted-foreground)] text-sm",
|
|
167
|
+
children: [
|
|
168
|
+
"Duration: ",
|
|
169
|
+
event.durationMs ?? "n/a",
|
|
170
|
+
" ms · First token: ",
|
|
171
|
+
event.timeToFirstTokenMs ?? "n/a",
|
|
172
|
+
" ms"
|
|
173
|
+
]
|
|
174
|
+
});
|
|
175
|
+
var renderTokenCountBody = (event) => /* @__PURE__ */ jsx("pre", {
|
|
176
|
+
className: "overflow-x-auto text-xs leading-5",
|
|
177
|
+
children: JSON.stringify(event.rateLimits, null, 2)
|
|
178
|
+
});
|
|
179
|
+
var renderReasoningBody = (event) => /* @__PURE__ */ jsxs("div", {
|
|
180
|
+
className: "space-y-2 text-sm",
|
|
181
|
+
children: [/* @__PURE__ */ jsx("p", { children: event.summary.join(" ") || "Reasoning content is not directly available." }), event.hasEncryptedContent ? /* @__PURE__ */ jsx("p", {
|
|
182
|
+
className: "text-[var(--muted-foreground)] text-xs",
|
|
183
|
+
children: "Encrypted reasoning payload captured."
|
|
184
|
+
}) : null]
|
|
185
|
+
});
|
|
186
|
+
var renderWebSearchBody = (event) => /* @__PURE__ */ jsxs("div", {
|
|
187
|
+
className: "space-y-2 text-sm",
|
|
188
|
+
children: [/* @__PURE__ */ jsxs("p", { children: [
|
|
189
|
+
"Phase: ",
|
|
190
|
+
event.phase,
|
|
191
|
+
event.status ? ` · ${event.status}` : ""
|
|
192
|
+
] }), event.query ? /* @__PURE__ */ jsx("p", {
|
|
193
|
+
className: "text-[var(--muted-foreground)]",
|
|
194
|
+
children: event.query
|
|
195
|
+
}) : null]
|
|
196
|
+
});
|
|
197
|
+
var renderEventBody = (event, t) => {
|
|
198
|
+
switch (event.kind) {
|
|
199
|
+
case "message": return renderMessageBody(event, t);
|
|
200
|
+
case "tool_call": return renderToolCallBody(event, t);
|
|
201
|
+
case "tool_output": return renderToolOutputBody(event, t);
|
|
202
|
+
case "task_started": return renderTaskStartedBody(event);
|
|
203
|
+
case "task_complete": return renderTaskCompleteBody(event);
|
|
204
|
+
case "token_count": return renderTokenCountBody(event);
|
|
205
|
+
case "reasoning": return renderReasoningBody(event);
|
|
206
|
+
case "web_search": return renderWebSearchBody(event);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
function TranscriptEventCard({ assistantModel, copied, event, isSelected, showRawJson, transform, onCopy, onSelectionChange }) {
|
|
210
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
211
|
+
className: cn("min-w-0 overflow-hidden rounded-xl border p-3.5 shadow-[var(--panel-shadow)]", isSelected && "ring-2 ring-[var(--accent)]/35", getEventTone(event)),
|
|
212
|
+
children: [
|
|
213
|
+
/* @__PURE__ */ jsxs("div", {
|
|
214
|
+
className: "flex flex-wrap items-start justify-between gap-2",
|
|
215
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
216
|
+
className: "flex min-w-0 flex-1 flex-wrap items-center gap-2",
|
|
217
|
+
children: [
|
|
218
|
+
/* @__PURE__ */ jsx(Checkbox$1, {
|
|
219
|
+
"aria-label": `Select ${getEventTitle(event, assistantModel)}`,
|
|
220
|
+
checked: isSelected,
|
|
221
|
+
onCheckedChange: (checked) => onSelectionChange(event, checked === true)
|
|
222
|
+
}),
|
|
223
|
+
/* @__PURE__ */ jsx("h3", {
|
|
224
|
+
className: "min-w-0 break-words font-semibold text-sm [overflow-wrap:anywhere]",
|
|
225
|
+
children: getEventTitle(event, assistantModel)
|
|
226
|
+
}),
|
|
227
|
+
event.kind === "message" && event.phase ? /* @__PURE__ */ jsx(Badge, {
|
|
228
|
+
variant: "outline",
|
|
229
|
+
children: event.phase
|
|
230
|
+
}) : null,
|
|
231
|
+
event.kind !== "message" ? /* @__PURE__ */ jsx(Badge, {
|
|
232
|
+
variant: "outline",
|
|
233
|
+
children: event.kind.replaceAll("_", " ")
|
|
234
|
+
}) : null
|
|
235
|
+
]
|
|
236
|
+
}), event.timestamp ? /* @__PURE__ */ jsx("p", {
|
|
237
|
+
className: "shrink-0 text-[var(--muted-foreground)] text-xs",
|
|
238
|
+
suppressHydrationWarning: true,
|
|
239
|
+
children: formatDateTime(event.timestamp)
|
|
240
|
+
}) : null]
|
|
241
|
+
}),
|
|
242
|
+
/* @__PURE__ */ jsx("div", {
|
|
243
|
+
className: "mt-2.5 min-w-0",
|
|
244
|
+
children: renderEventBody(event, transform)
|
|
245
|
+
}),
|
|
246
|
+
/* @__PURE__ */ jsx("div", {
|
|
247
|
+
className: "mt-3 flex justify-end",
|
|
248
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
249
|
+
"aria-label": "Copy message",
|
|
250
|
+
className: "text-[var(--muted-foreground)] hover:bg-[var(--panel-secondary)] hover:text-[var(--foreground)]",
|
|
251
|
+
size: "icon-xs",
|
|
252
|
+
variant: "ghost",
|
|
253
|
+
onClick: () => onCopy(event),
|
|
254
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "text-[var(--accent)]" }) : /* @__PURE__ */ jsx(Copy, {})
|
|
255
|
+
})
|
|
256
|
+
}),
|
|
257
|
+
showRawJson ? /* @__PURE__ */ jsx("pre", {
|
|
258
|
+
className: "mt-3 overflow-x-auto rounded-lg border border-[var(--border)] bg-[var(--code-background)] p-3 text-[var(--code-foreground)] text-xs leading-5",
|
|
259
|
+
children: JSON.stringify(event.raw, null, 2)
|
|
260
|
+
}) : null
|
|
261
|
+
]
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
function TranscriptView({ assistantModel, events, projectPath, showCommentary, showExtraEvents, showRawJson, showToolCalls }) {
|
|
265
|
+
const { settings } = useSettings();
|
|
266
|
+
const visibleEvents = events.filter((event) => shouldShowEvent(event, showToolCalls, showExtraEvents, showCommentary));
|
|
267
|
+
const [copiedEventKeys, setCopiedEventKeys] = useState([]);
|
|
268
|
+
const [copiedSelection, setCopiedSelection] = useState(false);
|
|
269
|
+
const [copyErrorMessage, setCopyErrorMessage] = useState(null);
|
|
270
|
+
const [selectedEventKeys, setSelectedEventKeys] = useState([]);
|
|
271
|
+
const parentRef = useRef(null);
|
|
272
|
+
const timeoutIdsRef = useRef([]);
|
|
273
|
+
const useVirtualList = visibleEvents.length > 40;
|
|
274
|
+
const getEventKey = (event) => {
|
|
275
|
+
if (event.kind === "tool_call") return `${event.kind}-${event.sequence}-${event.callId ?? event.timestamp ?? event.name}`;
|
|
276
|
+
if (event.kind === "tool_output") return `${event.kind}-${event.sequence}-${event.callId ?? event.timestamp ?? "output"}`;
|
|
277
|
+
if (event.kind === "message") return `${event.kind}-${event.sequence}-${event.variant}-${event.timestamp ?? event.role}`;
|
|
278
|
+
return `${event.kind}-${event.sequence}-${event.timestamp ?? "event"}`;
|
|
279
|
+
};
|
|
280
|
+
const transform = (text) => applyPathTransforms(text, settings, projectPath);
|
|
281
|
+
const visibleEventKeys = visibleEvents.map(getEventKey);
|
|
282
|
+
const visibleEventKeySet = new Set(visibleEventKeys);
|
|
283
|
+
const selectedEventKeySet = new Set(selectedEventKeys);
|
|
284
|
+
const virtualizer = useVirtualizer({
|
|
285
|
+
count: useVirtualList ? visibleEvents.length : 0,
|
|
286
|
+
estimateSize: () => 160,
|
|
287
|
+
getItemKey: (index) => getEventKey(visibleEvents[index]) ?? `event-${index}`,
|
|
288
|
+
getScrollElement: () => parentRef.current,
|
|
289
|
+
measureElement: (element) => element.getBoundingClientRect().height,
|
|
290
|
+
overscan: 8
|
|
291
|
+
});
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
return () => {
|
|
294
|
+
for (const timeoutId of timeoutIdsRef.current) window.clearTimeout(timeoutId);
|
|
295
|
+
timeoutIdsRef.current = [];
|
|
296
|
+
};
|
|
297
|
+
}, []);
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
setSelectedEventKeys((current) => {
|
|
300
|
+
const next = current.filter((key) => visibleEventKeySet.has(key));
|
|
301
|
+
return next.length === current.length ? current : next;
|
|
302
|
+
});
|
|
303
|
+
}, [visibleEventKeySet]);
|
|
304
|
+
const scheduleTimeout = (callback, delayMs) => {
|
|
305
|
+
const timeoutId = window.setTimeout(() => {
|
|
306
|
+
timeoutIdsRef.current = timeoutIdsRef.current.filter((entry) => entry !== timeoutId);
|
|
307
|
+
callback();
|
|
308
|
+
}, delayMs);
|
|
309
|
+
timeoutIdsRef.current.push(timeoutId);
|
|
310
|
+
};
|
|
311
|
+
const showCopyFailure = () => {
|
|
312
|
+
setCopyErrorMessage("Copy failed");
|
|
313
|
+
scheduleTimeout(() => {
|
|
314
|
+
setCopyErrorMessage((current) => current === "Copy failed" ? null : current);
|
|
315
|
+
}, 1500);
|
|
316
|
+
};
|
|
317
|
+
const visibleSelectedKeys = selectedEventKeys.filter((key) => visibleEventKeySet.has(key));
|
|
318
|
+
const allVisibleSelected = visibleEvents.length > 0 && visibleSelectedKeys.length === visibleEvents.length;
|
|
319
|
+
const handleSelectionChange = (event, checked) => {
|
|
320
|
+
const key = getEventKey(event);
|
|
321
|
+
setSelectedEventKeys((current) => checked ? current.includes(key) ? current : [...current, key] : current.filter((entry) => entry !== key));
|
|
322
|
+
};
|
|
323
|
+
const handleToggleAllVisible = (checked) => {
|
|
324
|
+
setSelectedEventKeys((current) => checked ? [...new Set([...current.filter((key) => !visibleEventKeySet.has(key)), ...visibleEventKeys])] : current.filter((key) => !visibleEventKeySet.has(key)));
|
|
325
|
+
};
|
|
326
|
+
const handleCopyEvent = async (event) => {
|
|
327
|
+
if (!await copyToClipboard(getEventMarkdown(event, assistantModel, transform))) {
|
|
328
|
+
showCopyFailure();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const key = getEventKey(event);
|
|
332
|
+
setCopiedEventKeys((current) => [...new Set([...current, key])]);
|
|
333
|
+
scheduleTimeout(() => {
|
|
334
|
+
setCopiedEventKeys((current) => current.filter((entry) => entry !== key));
|
|
335
|
+
}, 1500);
|
|
336
|
+
};
|
|
337
|
+
const handleCopySelected = async () => {
|
|
338
|
+
const selectedEvents = visibleEvents.filter((event) => visibleSelectedKeys.includes(getEventKey(event)));
|
|
339
|
+
if (selectedEvents.length === 0) return;
|
|
340
|
+
if (!await copyToClipboard(selectedEvents.map((event) => getEventMarkdown(event, assistantModel, transform)).join("\n\n"))) {
|
|
341
|
+
showCopyFailure();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
setCopiedSelection(true);
|
|
345
|
+
scheduleTimeout(() => {
|
|
346
|
+
setCopiedSelection(false);
|
|
347
|
+
}, 1500);
|
|
348
|
+
};
|
|
349
|
+
if (useVirtualList) return /* @__PURE__ */ jsxs("div", {
|
|
350
|
+
ref: parentRef,
|
|
351
|
+
className: "h-[70vh] overflow-auto rounded-xl border border-[var(--border)] bg-[var(--panel)] p-3",
|
|
352
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
353
|
+
className: "sticky top-0 z-10 mb-3 flex items-center justify-between rounded-xl border border-[var(--border)] bg-[var(--panel)]/95 px-3 py-2 backdrop-blur",
|
|
354
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
355
|
+
className: "flex items-center gap-2",
|
|
356
|
+
children: [
|
|
357
|
+
/* @__PURE__ */ jsx(Checkbox$1, {
|
|
358
|
+
"aria-label": "Select visible messages",
|
|
359
|
+
checked: allVisibleSelected,
|
|
360
|
+
onCheckedChange: (checked) => handleToggleAllVisible(checked === true)
|
|
361
|
+
}),
|
|
362
|
+
/* @__PURE__ */ jsxs("span", {
|
|
363
|
+
className: "text-[var(--muted-foreground)] text-sm",
|
|
364
|
+
children: [visibleSelectedKeys.length, " selected"]
|
|
365
|
+
}),
|
|
366
|
+
copyErrorMessage ? /* @__PURE__ */ jsx("span", {
|
|
367
|
+
className: "text-[var(--destructive)] text-sm",
|
|
368
|
+
children: copyErrorMessage
|
|
369
|
+
}) : null
|
|
370
|
+
]
|
|
371
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
372
|
+
"aria-label": "Copy selected messages",
|
|
373
|
+
className: "hover:bg-[var(--panel-secondary)] hover:text-[var(--foreground)]",
|
|
374
|
+
disabled: visibleSelectedKeys.length === 0,
|
|
375
|
+
size: "sm",
|
|
376
|
+
variant: "outline",
|
|
377
|
+
onClick: () => void handleCopySelected(),
|
|
378
|
+
children: [copiedSelection ? /* @__PURE__ */ jsx(Check, { className: "text-[var(--accent)]" }) : /* @__PURE__ */ jsx(Copy, {}), copiedSelection ? "Copied" : "Copy"]
|
|
379
|
+
})]
|
|
380
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
381
|
+
className: "relative w-full",
|
|
382
|
+
style: { height: `${virtualizer.getTotalSize()}px` },
|
|
383
|
+
children: virtualizer.getVirtualItems().map((item) => /* @__PURE__ */ jsx("div", {
|
|
384
|
+
ref: virtualizer.measureElement,
|
|
385
|
+
"data-index": item.index,
|
|
386
|
+
className: "absolute top-0 left-0 w-full pb-3.5",
|
|
387
|
+
style: { transform: `translateY(${item.start}px)` },
|
|
388
|
+
children: /* @__PURE__ */ jsx(TranscriptEventCard, {
|
|
389
|
+
assistantModel,
|
|
390
|
+
copied: copiedEventKeys.includes(getEventKey(visibleEvents[item.index])),
|
|
391
|
+
event: visibleEvents[item.index],
|
|
392
|
+
isSelected: selectedEventKeySet.has(getEventKey(visibleEvents[item.index])),
|
|
393
|
+
showRawJson,
|
|
394
|
+
transform,
|
|
395
|
+
onCopy: (event) => void handleCopyEvent(event),
|
|
396
|
+
onSelectionChange: handleSelectionChange
|
|
397
|
+
})
|
|
398
|
+
}, item.key))
|
|
399
|
+
})]
|
|
400
|
+
});
|
|
401
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
402
|
+
className: "space-y-3.5",
|
|
403
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
404
|
+
className: "sticky top-0 z-10 flex items-center justify-between rounded-xl border border-[var(--border)] bg-[var(--panel)]/95 px-3 py-2 backdrop-blur",
|
|
405
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
406
|
+
className: "flex items-center gap-2",
|
|
407
|
+
children: [
|
|
408
|
+
/* @__PURE__ */ jsx(Checkbox$1, {
|
|
409
|
+
"aria-label": "Select visible messages",
|
|
410
|
+
checked: allVisibleSelected,
|
|
411
|
+
onCheckedChange: (checked) => handleToggleAllVisible(checked === true)
|
|
412
|
+
}),
|
|
413
|
+
/* @__PURE__ */ jsxs("span", {
|
|
414
|
+
className: "text-[var(--muted-foreground)] text-sm",
|
|
415
|
+
children: [visibleSelectedKeys.length, " selected"]
|
|
416
|
+
}),
|
|
417
|
+
copyErrorMessage ? /* @__PURE__ */ jsx("span", {
|
|
418
|
+
className: "text-[var(--destructive)] text-sm",
|
|
419
|
+
children: copyErrorMessage
|
|
420
|
+
}) : null
|
|
421
|
+
]
|
|
422
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
423
|
+
"aria-label": "Copy selected messages",
|
|
424
|
+
className: "hover:bg-[var(--panel-secondary)] hover:text-[var(--foreground)]",
|
|
425
|
+
disabled: visibleSelectedKeys.length === 0,
|
|
426
|
+
size: "sm",
|
|
427
|
+
variant: "outline",
|
|
428
|
+
onClick: () => void handleCopySelected(),
|
|
429
|
+
children: [copiedSelection ? /* @__PURE__ */ jsx(Check, { className: "text-[var(--accent)]" }) : /* @__PURE__ */ jsx(Copy, {}), copiedSelection ? "Copied" : "Copy"]
|
|
430
|
+
})]
|
|
431
|
+
}), visibleEvents.map((event) => /* @__PURE__ */ jsx(TranscriptEventCard, {
|
|
432
|
+
assistantModel,
|
|
433
|
+
copied: copiedEventKeys.includes(getEventKey(event)),
|
|
434
|
+
event,
|
|
435
|
+
isSelected: selectedEventKeySet.has(getEventKey(event)),
|
|
436
|
+
showRawJson,
|
|
437
|
+
transform,
|
|
438
|
+
onCopy: (selectedEvent) => void handleCopyEvent(selectedEvent),
|
|
439
|
+
onSelectionChange: handleSelectionChange
|
|
440
|
+
}, getEventKey(event)))]
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/components/ui/tabs.tsx
|
|
445
|
+
function Tabs$1({ className, orientation = "horizontal", ...props }) {
|
|
446
|
+
return /* @__PURE__ */ jsx(Tabs.Root, {
|
|
447
|
+
"data-slot": "tabs",
|
|
448
|
+
"data-orientation": orientation,
|
|
449
|
+
orientation,
|
|
450
|
+
className: cn("group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", className),
|
|
451
|
+
...props
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
var tabsListVariants = cva("group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:min-h-11 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", {
|
|
455
|
+
defaultVariants: { variant: "default" },
|
|
456
|
+
variants: { variant: {
|
|
457
|
+
default: "bg-muted",
|
|
458
|
+
line: "gap-1 bg-transparent"
|
|
459
|
+
} }
|
|
460
|
+
});
|
|
461
|
+
function TabsList({ className, variant = "default", ...props }) {
|
|
462
|
+
return /* @__PURE__ */ jsx(Tabs.List, {
|
|
463
|
+
"data-slot": "tabs-list",
|
|
464
|
+
"data-variant": variant,
|
|
465
|
+
className: cn(tabsListVariants({ variant }), className),
|
|
466
|
+
...props
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function TabsTrigger({ className, ...props }) {
|
|
470
|
+
return /* @__PURE__ */ jsx(Tabs.Trigger, {
|
|
471
|
+
"data-slot": "tabs-trigger",
|
|
472
|
+
className: cn("relative inline-flex h-auto min-h-9 min-w-0 flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-3 py-2 font-medium text-foreground/60 text-sm transition-all hover:text-foreground focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground", "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=horizontal]/tabs:after:bottom-[-3px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", className),
|
|
473
|
+
...props
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
function TabsContent({ className, ...props }) {
|
|
477
|
+
return /* @__PURE__ */ jsx(Tabs.Content, {
|
|
478
|
+
"data-slot": "tabs-content",
|
|
479
|
+
className: cn("flex-1 outline-none", className),
|
|
480
|
+
...props
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/routes/threads.$threadId.tsx?tsr-split=component
|
|
485
|
+
var buildThreadItems = (snapshot) => {
|
|
486
|
+
return [
|
|
487
|
+
{
|
|
488
|
+
label: "Thread ID",
|
|
489
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
490
|
+
"data-mono": "true",
|
|
491
|
+
children: snapshot.thread.id
|
|
492
|
+
})
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
label: "Project",
|
|
496
|
+
value: snapshot.project
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
label: "CWD",
|
|
500
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
501
|
+
"data-mono": "true",
|
|
502
|
+
children: snapshot.thread.cwd
|
|
503
|
+
})
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
label: "Created",
|
|
507
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
508
|
+
suppressHydrationWarning: true,
|
|
509
|
+
children: formatDateTime(snapshot.thread.created_at_ms ?? snapshot.thread.created_at * 1e3)
|
|
510
|
+
})
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
label: "Updated",
|
|
514
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
515
|
+
suppressHydrationWarning: true,
|
|
516
|
+
children: formatDateTime(snapshot.thread.updated_at_ms ?? snapshot.thread.updated_at * 1e3)
|
|
517
|
+
})
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
label: "Session started",
|
|
521
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
522
|
+
suppressHydrationWarning: true,
|
|
523
|
+
children: formatDateTime(snapshot.transcript?.sessionMeta.timestamp ?? null)
|
|
524
|
+
})
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
label: "Rollout size",
|
|
528
|
+
value: formatBytes(snapshot.rollout.fileSizeBytes)
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
label: "Archived",
|
|
532
|
+
value: formatBooleanLabel(Boolean(snapshot.thread.archived))
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
label: "Archived at",
|
|
536
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
537
|
+
suppressHydrationWarning: true,
|
|
538
|
+
children: formatDateTime(snapshot.thread.archived_at ? snapshot.thread.archived_at * 1e3 : null)
|
|
539
|
+
})
|
|
540
|
+
}
|
|
541
|
+
];
|
|
542
|
+
};
|
|
543
|
+
var buildRuntimeItems = (snapshot) => {
|
|
544
|
+
return [
|
|
545
|
+
{
|
|
546
|
+
label: "Source",
|
|
547
|
+
value: snapshot.thread.source
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
label: "Originator",
|
|
551
|
+
value: snapshot.transcript?.sessionMeta.originator ?? "n/a"
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
label: "Model provider",
|
|
555
|
+
value: snapshot.thread.model_provider
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
label: "Reasoning effort",
|
|
559
|
+
value: snapshot.thread.reasoning_effort ?? "n/a"
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
label: "CLI version",
|
|
563
|
+
value: snapshot.thread.cli_version
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
label: "Approval mode",
|
|
567
|
+
value: snapshot.thread.approval_mode
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
label: "Memory mode",
|
|
571
|
+
value: snapshot.thread.memory_mode
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
label: "Has user event",
|
|
575
|
+
value: formatBooleanLabel(Boolean(snapshot.thread.has_user_event))
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
};
|
|
579
|
+
var buildGitItems = (snapshot) => {
|
|
580
|
+
return [
|
|
581
|
+
{
|
|
582
|
+
label: "Git branch",
|
|
583
|
+
value: snapshot.thread.git_branch ?? "n/a"
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
label: "Git SHA",
|
|
587
|
+
value: snapshot.thread.git_sha ?? "n/a"
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
label: "Git remote",
|
|
591
|
+
value: snapshot.thread.git_origin_url ?? "n/a"
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
label: "Agent nickname",
|
|
595
|
+
value: snapshot.thread.agent_nickname ?? "n/a"
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
label: "Agent role",
|
|
599
|
+
value: snapshot.thread.agent_role ?? "n/a"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
label: "Agent path",
|
|
603
|
+
value: snapshot.thread.agent_path ?? "n/a"
|
|
604
|
+
}
|
|
605
|
+
];
|
|
606
|
+
};
|
|
607
|
+
var buildRelationItems = (snapshot) => {
|
|
608
|
+
const parentThreadValue = snapshot.relations.parentThreadId ? /* @__PURE__ */ jsx(Link, {
|
|
609
|
+
className: "text-[var(--accent)]",
|
|
610
|
+
params: { threadId: snapshot.relations.parentThreadId },
|
|
611
|
+
to: "/threads/$threadId",
|
|
612
|
+
children: snapshot.relations.parentThreadId
|
|
613
|
+
}) : "n/a";
|
|
614
|
+
const childThreadValue = snapshot.relations.childEdges.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
615
|
+
className: "flex flex-wrap gap-2",
|
|
616
|
+
children: snapshot.relations.childEdges.map((edge) => /* @__PURE__ */ jsx(Link, {
|
|
617
|
+
className: "rounded-full border border-[var(--border)] px-3 py-1 text-[var(--accent)] text-xs",
|
|
618
|
+
params: { threadId: edge.child_thread_id },
|
|
619
|
+
to: "/threads/$threadId",
|
|
620
|
+
children: edge.child_thread_id
|
|
621
|
+
}, edge.child_thread_id))
|
|
622
|
+
}) : "n/a";
|
|
623
|
+
return [
|
|
624
|
+
{
|
|
625
|
+
label: "Parent thread",
|
|
626
|
+
value: parentThreadValue
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
label: "Child threads",
|
|
630
|
+
value: childThreadValue
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
label: "First user message",
|
|
634
|
+
value: snapshot.thread.first_user_message || "n/a"
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
label: "Preview",
|
|
638
|
+
value: snapshot.thread.preview || "n/a"
|
|
639
|
+
}
|
|
640
|
+
];
|
|
641
|
+
};
|
|
642
|
+
var buildTranscriptStatsItems = (snapshot) => {
|
|
643
|
+
if (!snapshot.transcript) {
|
|
644
|
+
if (snapshot.transcriptState === "missing") return [
|
|
645
|
+
{
|
|
646
|
+
label: "Transcript load",
|
|
647
|
+
value: "Transcript file missing from disk"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
label: "Rollout path",
|
|
651
|
+
value: snapshot.thread.rollout_path
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
label: "Preview mode",
|
|
655
|
+
value: "Export and transcript browsing are unavailable until the file exists again."
|
|
656
|
+
}
|
|
657
|
+
];
|
|
658
|
+
return [
|
|
659
|
+
{
|
|
660
|
+
label: "Transcript load",
|
|
661
|
+
value: "Deferred for oversized rollout"
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
label: "Rollout size",
|
|
665
|
+
value: formatBytes(snapshot.rollout.fileSizeBytes)
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
label: "Preview mode",
|
|
669
|
+
value: "Load the transcript manually to inspect it."
|
|
670
|
+
}
|
|
671
|
+
];
|
|
672
|
+
}
|
|
673
|
+
return [
|
|
674
|
+
{
|
|
675
|
+
label: "Event kinds",
|
|
676
|
+
value: formatList([...new Set(snapshot.transcript.events.map((event) => event.kind))])
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
label: "Stats scope",
|
|
680
|
+
value: snapshot.transcript.statsArePartial ? "Preview only" : "Full transcript"
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
label: "Tool calls",
|
|
684
|
+
value: String(snapshot.transcript.stats.toolCallCount)
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
label: "Exec calls",
|
|
688
|
+
value: String(snapshot.transcript.stats.execCommandCount)
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
label: "Web search events",
|
|
692
|
+
value: String(snapshot.transcript.stats.webSearchEventCount)
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
label: "Assistant messages",
|
|
696
|
+
value: String(snapshot.transcript.stats.assistantMessageCount)
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
label: "Commentary updates",
|
|
700
|
+
value: String(snapshot.transcript.stats.commentaryCount)
|
|
701
|
+
}
|
|
702
|
+
];
|
|
703
|
+
};
|
|
704
|
+
function TranscriptControls({ rawJsonDisabled = false, showCommentary, showExtraEvents, showRawJson, showToolCalls, onShowCommentaryChange, onShowExtraEventsChange, onShowRawJsonChange, onShowToolCallsChange }) {
|
|
705
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
706
|
+
className: "flex flex-wrap gap-4 rounded-xl border border-[var(--border)] bg-[var(--panel)] px-4 py-3 shadow-[var(--panel-shadow)]",
|
|
707
|
+
children: [
|
|
708
|
+
/* @__PURE__ */ jsxs("div", {
|
|
709
|
+
className: "flex items-center gap-2 text-sm",
|
|
710
|
+
children: [/* @__PURE__ */ jsx(Checkbox$1, {
|
|
711
|
+
checked: showToolCalls,
|
|
712
|
+
onCheckedChange: (checked) => onShowToolCallsChange(checked === true)
|
|
713
|
+
}), /* @__PURE__ */ jsx("span", { children: "Show tool calls" })]
|
|
714
|
+
}),
|
|
715
|
+
/* @__PURE__ */ jsxs("div", {
|
|
716
|
+
className: "flex items-center gap-2 text-sm",
|
|
717
|
+
children: [/* @__PURE__ */ jsx(Checkbox$1, {
|
|
718
|
+
checked: showCommentary,
|
|
719
|
+
onCheckedChange: (checked) => onShowCommentaryChange(checked === true)
|
|
720
|
+
}), /* @__PURE__ */ jsx("span", { children: "Show commentary" })]
|
|
721
|
+
}),
|
|
722
|
+
/* @__PURE__ */ jsxs("div", {
|
|
723
|
+
className: "flex items-center gap-2 text-sm",
|
|
724
|
+
children: [/* @__PURE__ */ jsx(Checkbox$1, {
|
|
725
|
+
checked: showExtraEvents,
|
|
726
|
+
onCheckedChange: (checked) => onShowExtraEventsChange(checked === true)
|
|
727
|
+
}), /* @__PURE__ */ jsx("span", { children: "Show extra events" })]
|
|
728
|
+
}),
|
|
729
|
+
/* @__PURE__ */ jsxs("div", {
|
|
730
|
+
className: "flex items-center gap-2 text-sm",
|
|
731
|
+
children: [/* @__PURE__ */ jsx(Checkbox$1, {
|
|
732
|
+
checked: showRawJson,
|
|
733
|
+
disabled: rawJsonDisabled,
|
|
734
|
+
onCheckedChange: (checked) => onShowRawJsonChange(checked === true)
|
|
735
|
+
}), /* @__PURE__ */ jsx("span", { children: "Raw JSON" })]
|
|
736
|
+
})
|
|
737
|
+
]
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
function ThreadMetadataPanels({ snapshot }) {
|
|
741
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
742
|
+
className: "grid gap-4 xl:grid-cols-[1.1fr_0.9fr]",
|
|
743
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
744
|
+
className: "space-y-4",
|
|
745
|
+
children: [
|
|
746
|
+
/* @__PURE__ */ jsx(MetadataSection, {
|
|
747
|
+
items: buildThreadItems(snapshot),
|
|
748
|
+
title: "Thread"
|
|
749
|
+
}),
|
|
750
|
+
/* @__PURE__ */ jsx(MetadataSection, {
|
|
751
|
+
items: buildRuntimeItems(snapshot),
|
|
752
|
+
title: "Runtime"
|
|
753
|
+
}),
|
|
754
|
+
/* @__PURE__ */ jsx(MetadataSection, {
|
|
755
|
+
items: buildGitItems(snapshot),
|
|
756
|
+
title: "Git and agent"
|
|
757
|
+
})
|
|
758
|
+
]
|
|
759
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
760
|
+
className: "space-y-4",
|
|
761
|
+
children: [
|
|
762
|
+
/* @__PURE__ */ jsx(MetadataSection, {
|
|
763
|
+
items: buildRelationItems(snapshot),
|
|
764
|
+
title: "Relations and summary"
|
|
765
|
+
}),
|
|
766
|
+
/* @__PURE__ */ jsxs("section", {
|
|
767
|
+
className: "rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]",
|
|
768
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
769
|
+
className: "font-semibold text-[var(--muted-foreground)] text-sm uppercase tracking-[0.18em]",
|
|
770
|
+
children: "Available tools"
|
|
771
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
772
|
+
className: "mt-4 space-y-3",
|
|
773
|
+
children: snapshot.availableTools.map((tool) => /* @__PURE__ */ jsxs("div", {
|
|
774
|
+
className: "rounded-xl border border-[var(--border)] bg-[var(--panel-secondary)] p-3.5",
|
|
775
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
776
|
+
className: "flex flex-wrap items-center gap-2",
|
|
777
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
778
|
+
className: "font-medium font-mono text-sm",
|
|
779
|
+
children: tool.name
|
|
780
|
+
}), tool.namespace ? /* @__PURE__ */ jsx(Badge, {
|
|
781
|
+
variant: "outline",
|
|
782
|
+
children: tool.namespace
|
|
783
|
+
}) : null]
|
|
784
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
785
|
+
className: "mt-1.5 text-[var(--muted-foreground)] text-sm",
|
|
786
|
+
children: tool.description || "No description."
|
|
787
|
+
})]
|
|
788
|
+
}, `${tool.name}-${tool.namespace ?? "global"}`))
|
|
789
|
+
})]
|
|
790
|
+
}),
|
|
791
|
+
/* @__PURE__ */ jsx(MetadataSection, {
|
|
792
|
+
items: buildTranscriptStatsItems(snapshot),
|
|
793
|
+
title: "Transcript stats"
|
|
794
|
+
})
|
|
795
|
+
]
|
|
796
|
+
})]
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
function ThreadRawPanels({ snapshot }) {
|
|
800
|
+
if (!snapshot.transcript) return /* @__PURE__ */ jsx("div", {
|
|
801
|
+
className: "rounded-xl border border-[var(--border)] bg-[var(--panel)] px-5 py-4 text-sm",
|
|
802
|
+
children: snapshot.transcriptState === "missing" ? "The rollout JSONL file is missing from disk, so raw transcript payloads are unavailable." : "Raw transcript payloads are deferred for oversized rollouts. Use Export if you only need the full thread contents, or load the transcript manually from the Transcript tab."
|
|
803
|
+
});
|
|
804
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
805
|
+
className: "space-y-4",
|
|
806
|
+
children: [
|
|
807
|
+
/* @__PURE__ */ jsx(JsonPanel, {
|
|
808
|
+
title: "Session meta",
|
|
809
|
+
value: snapshot.transcript.sessionMeta
|
|
810
|
+
}),
|
|
811
|
+
/* @__PURE__ */ jsx(JsonPanel, {
|
|
812
|
+
title: "Turn contexts",
|
|
813
|
+
value: snapshot.transcript.turnContexts
|
|
814
|
+
}),
|
|
815
|
+
/* @__PURE__ */ jsx(JsonPanel, {
|
|
816
|
+
title: "Sandbox policy",
|
|
817
|
+
value: snapshot.thread.sandbox_policy
|
|
818
|
+
})
|
|
819
|
+
]
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
function DeferredTranscriptNotice({ fileSizeBytes, missing, pending, onLoad }) {
|
|
823
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
824
|
+
className: "rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]",
|
|
825
|
+
children: [
|
|
826
|
+
/* @__PURE__ */ jsx("h3", {
|
|
827
|
+
className: "font-semibold text-base",
|
|
828
|
+
children: missing ? "Transcript file missing" : "This is a very big thread"
|
|
829
|
+
}),
|
|
830
|
+
/* @__PURE__ */ jsx("p", {
|
|
831
|
+
className: "mt-2 text-[var(--muted-foreground)] text-sm leading-6",
|
|
832
|
+
children: missing ? "The rollout JSONL referenced by this thread is no longer present on disk. Export may still work if the file is restored, but transcript browsing is unavailable right now." : `Spiracha skipped loading the transcript automatically because the rollout file is ${formatBytes(fileSizeBytes)}. Export still works immediately. If you need to inspect the thread here, load a bounded preview manually.`
|
|
833
|
+
}),
|
|
834
|
+
missing ? null : /* @__PURE__ */ jsx("div", {
|
|
835
|
+
className: "mt-4",
|
|
836
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
837
|
+
disabled: pending,
|
|
838
|
+
variant: "outline",
|
|
839
|
+
onClick: onLoad,
|
|
840
|
+
children: pending ? "Loading preview..." : "Load transcript preview"
|
|
841
|
+
})
|
|
842
|
+
})
|
|
843
|
+
]
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
function ThreadDetailPage() {
|
|
847
|
+
const navigate = useNavigate();
|
|
848
|
+
const queryClient = useQueryClient();
|
|
849
|
+
const params = Route.useParams();
|
|
850
|
+
const snapshot = useSuspenseQuery(threadSnapshotQueryOptions(params.threadId)).data;
|
|
851
|
+
const { settings } = useSettings();
|
|
852
|
+
const [shouldLoadTranscript, setShouldLoadTranscript] = useState(!snapshot.rollout.shouldDeferTranscriptLoad);
|
|
853
|
+
const [showToolCalls, setShowToolCalls] = useState(false);
|
|
854
|
+
const [showCommentary, setShowCommentary] = useState(false);
|
|
855
|
+
const [showExtraEvents, setShowExtraEvents] = useState(false);
|
|
856
|
+
const [showRawJson, setShowRawJson] = useState(false);
|
|
857
|
+
const [exportOpen, setExportOpen] = useState(false);
|
|
858
|
+
const [deleteOpen, setDeleteOpen] = useState(false);
|
|
859
|
+
const transcriptQuery = useQuery({
|
|
860
|
+
...threadTranscriptQueryOptions(params.threadId),
|
|
861
|
+
enabled: shouldLoadTranscript && snapshot.transcript === null
|
|
862
|
+
});
|
|
863
|
+
const transcript = snapshot.transcript ?? transcriptQuery.data ?? null;
|
|
864
|
+
const viewSnapshot = {
|
|
865
|
+
...snapshot,
|
|
866
|
+
transcript
|
|
867
|
+
};
|
|
868
|
+
const exportThreadMutation = useMutation({
|
|
869
|
+
mutationFn: async (options) => {
|
|
870
|
+
console.info("[spiracha:export-ui] request", {
|
|
871
|
+
outputFormat: options.outputFormat,
|
|
872
|
+
project: snapshot.project,
|
|
873
|
+
selectedThreadCount: 1,
|
|
874
|
+
selectedThreadIds: [snapshot.thread.id]
|
|
875
|
+
});
|
|
876
|
+
const download = await exportThreadFn({ data: {
|
|
877
|
+
...options,
|
|
878
|
+
...settings,
|
|
879
|
+
threadId: snapshot.thread.id
|
|
880
|
+
} });
|
|
881
|
+
console.info("[spiracha:export-ui] response", {
|
|
882
|
+
downloadUrl: download.mode === "download_url" ? download.downloadUrl : null,
|
|
883
|
+
fileName: download.fileName,
|
|
884
|
+
mode: download.mode,
|
|
885
|
+
project: snapshot.project,
|
|
886
|
+
selectedThreadCount: 1
|
|
887
|
+
});
|
|
888
|
+
if (download.mode === "download") {
|
|
889
|
+
downloadTextFile(download.fileName, download.content, download.mimeType);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
await downloadUrlFile(download.fileName, download.downloadUrl);
|
|
893
|
+
},
|
|
894
|
+
onError: (error) => {
|
|
895
|
+
console.error("[spiracha:export-ui] failed", {
|
|
896
|
+
error: error instanceof Error ? error.message : String(error),
|
|
897
|
+
project: snapshot.project,
|
|
898
|
+
selectedThreadCount: 1,
|
|
899
|
+
selectedThreadIds: [snapshot.thread.id]
|
|
900
|
+
});
|
|
901
|
+
},
|
|
902
|
+
onSuccess: async () => {
|
|
903
|
+
setExportOpen(false);
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
const deleteThreadMutation = useMutation({
|
|
907
|
+
mutationFn: (input) => deleteThreadFn({ data: {
|
|
908
|
+
deleteSessionFiles: input.deleteSessionFiles,
|
|
909
|
+
threadId: snapshot.thread.id
|
|
910
|
+
} }),
|
|
911
|
+
onSuccess: async () => {
|
|
912
|
+
await Promise.all([
|
|
913
|
+
queryClient.invalidateQueries({ queryKey: ["analytics"] }),
|
|
914
|
+
queryClient.invalidateQueries({ queryKey: ["dashboard"] }),
|
|
915
|
+
queryClient.invalidateQueries({ queryKey: ["project-threads", snapshot.project] }),
|
|
916
|
+
queryClient.invalidateQueries({ queryKey: ["projects"] })
|
|
917
|
+
]);
|
|
918
|
+
navigate({
|
|
919
|
+
params: { project: snapshot.project },
|
|
920
|
+
to: "/projects/$project"
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
925
|
+
className: "space-y-5",
|
|
926
|
+
children: [
|
|
927
|
+
/* @__PURE__ */ jsx(PageHeader, {
|
|
928
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button, {
|
|
929
|
+
className: "rounded-full",
|
|
930
|
+
variant: "outline",
|
|
931
|
+
onClick: () => setExportOpen(true),
|
|
932
|
+
children: [/* @__PURE__ */ jsx(Download, { className: "mr-2 size-4" }), "Export"]
|
|
933
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
934
|
+
className: "rounded-full border-[var(--destructive)]/20 text-[var(--destructive)]",
|
|
935
|
+
variant: "outline",
|
|
936
|
+
onClick: () => setDeleteOpen(true),
|
|
937
|
+
children: [/* @__PURE__ */ jsx(Trash2, { className: "mr-2 size-4" }), "Delete"]
|
|
938
|
+
})] }),
|
|
939
|
+
eyebrow: snapshot.project,
|
|
940
|
+
subtitle: snapshot.thread.preview,
|
|
941
|
+
title: snapshot.thread.title
|
|
942
|
+
}),
|
|
943
|
+
/* @__PURE__ */ jsxs("div", {
|
|
944
|
+
className: "grid gap-3 md:grid-cols-2 xl:grid-cols-4",
|
|
945
|
+
children: [
|
|
946
|
+
/* @__PURE__ */ jsx(MetricCard, {
|
|
947
|
+
label: "Model",
|
|
948
|
+
value: snapshot.thread.model ?? "unknown"
|
|
949
|
+
}),
|
|
950
|
+
/* @__PURE__ */ jsx(MetricCard, {
|
|
951
|
+
label: "Tokens",
|
|
952
|
+
value: formatTokens(snapshot.thread.tokens_used)
|
|
953
|
+
}),
|
|
954
|
+
/* @__PURE__ */ jsx(MetricCard, {
|
|
955
|
+
label: "Updated",
|
|
956
|
+
value: /* @__PURE__ */ jsx("span", {
|
|
957
|
+
suppressHydrationWarning: true,
|
|
958
|
+
children: formatDateTime(snapshot.thread.updated_at_ms ?? snapshot.thread.updated_at * 1e3)
|
|
959
|
+
})
|
|
960
|
+
}),
|
|
961
|
+
/* @__PURE__ */ jsx(MetricCard, {
|
|
962
|
+
label: "Thread source",
|
|
963
|
+
value: snapshot.thread.thread_source ?? transcript?.sessionMeta.threadSource ?? "n/a"
|
|
964
|
+
})
|
|
965
|
+
]
|
|
966
|
+
}),
|
|
967
|
+
/* @__PURE__ */ jsxs(Tabs$1, {
|
|
968
|
+
className: "space-y-4",
|
|
969
|
+
defaultValue: "transcript",
|
|
970
|
+
children: [
|
|
971
|
+
/* @__PURE__ */ jsxs(TabsList, {
|
|
972
|
+
className: "grid w-fit min-w-[24rem] grid-cols-3 rounded-full border border-[var(--border)] bg-[var(--panel)] p-1",
|
|
973
|
+
children: [
|
|
974
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
975
|
+
className: "rounded-full px-5 text-sm",
|
|
976
|
+
value: "transcript",
|
|
977
|
+
children: "Transcript"
|
|
978
|
+
}),
|
|
979
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
980
|
+
className: "rounded-full px-5 text-sm",
|
|
981
|
+
value: "metadata",
|
|
982
|
+
children: "Metadata"
|
|
983
|
+
}),
|
|
984
|
+
/* @__PURE__ */ jsx(TabsTrigger, {
|
|
985
|
+
className: "rounded-full px-5 text-sm",
|
|
986
|
+
value: "raw",
|
|
987
|
+
children: "Raw"
|
|
988
|
+
})
|
|
989
|
+
]
|
|
990
|
+
}),
|
|
991
|
+
/* @__PURE__ */ jsxs(TabsContent, {
|
|
992
|
+
className: "space-y-3",
|
|
993
|
+
value: "transcript",
|
|
994
|
+
children: [
|
|
995
|
+
/* @__PURE__ */ jsx(TranscriptControls, {
|
|
996
|
+
rawJsonDisabled: !transcript?.rawIncluded,
|
|
997
|
+
showCommentary,
|
|
998
|
+
showExtraEvents,
|
|
999
|
+
showRawJson,
|
|
1000
|
+
showToolCalls,
|
|
1001
|
+
onShowCommentaryChange: setShowCommentary,
|
|
1002
|
+
onShowExtraEventsChange: setShowExtraEvents,
|
|
1003
|
+
onShowRawJsonChange: setShowRawJson,
|
|
1004
|
+
onShowToolCallsChange: setShowToolCalls
|
|
1005
|
+
}),
|
|
1006
|
+
transcript ? /* @__PURE__ */ jsx(TranscriptView, {
|
|
1007
|
+
assistantModel: snapshot.thread.model,
|
|
1008
|
+
events: transcript.events,
|
|
1009
|
+
projectPath: snapshot.thread.cwd,
|
|
1010
|
+
showCommentary,
|
|
1011
|
+
showExtraEvents,
|
|
1012
|
+
showRawJson: showRawJson && transcript.rawIncluded,
|
|
1013
|
+
showToolCalls
|
|
1014
|
+
}) : /* @__PURE__ */ jsx(DeferredTranscriptNotice, {
|
|
1015
|
+
fileSizeBytes: snapshot.rollout.fileSizeBytes,
|
|
1016
|
+
missing: snapshot.transcriptState === "missing",
|
|
1017
|
+
pending: transcriptQuery.isFetching,
|
|
1018
|
+
onLoad: () => setShouldLoadTranscript(true)
|
|
1019
|
+
}),
|
|
1020
|
+
transcriptQuery.isError ? /* @__PURE__ */ jsxs("p", {
|
|
1021
|
+
className: "text-[var(--destructive)] text-sm",
|
|
1022
|
+
children: [
|
|
1023
|
+
"Failed to load transcript preview:",
|
|
1024
|
+
" ",
|
|
1025
|
+
transcriptQuery.error instanceof Error ? transcriptQuery.error.message : "Unknown error"
|
|
1026
|
+
]
|
|
1027
|
+
}) : null
|
|
1028
|
+
]
|
|
1029
|
+
}),
|
|
1030
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
1031
|
+
value: "metadata",
|
|
1032
|
+
children: /* @__PURE__ */ jsx(ThreadMetadataPanels, { snapshot: viewSnapshot })
|
|
1033
|
+
}),
|
|
1034
|
+
/* @__PURE__ */ jsx(TabsContent, {
|
|
1035
|
+
value: "raw",
|
|
1036
|
+
children: /* @__PURE__ */ jsx(ThreadRawPanels, { snapshot: viewSnapshot })
|
|
1037
|
+
})
|
|
1038
|
+
]
|
|
1039
|
+
}),
|
|
1040
|
+
/* @__PURE__ */ jsx(DeleteConfirmDialog, {
|
|
1041
|
+
confirmLabel: deleteThreadMutation.isPending ? "Deleting..." : "Delete thread",
|
|
1042
|
+
description: "Delete this thread from the Codex database. Enable Delete Session files if you also want to remove the rollout JSONL from disk.",
|
|
1043
|
+
open: deleteOpen,
|
|
1044
|
+
showDeleteSessionFilesOption: true,
|
|
1045
|
+
title: "Delete this thread from Codex DB?",
|
|
1046
|
+
onConfirm: ({ deleteSessionFiles }) => deleteThreadMutation.mutate({ deleteSessionFiles }),
|
|
1047
|
+
onOpenChange: setDeleteOpen
|
|
1048
|
+
}),
|
|
1049
|
+
/* @__PURE__ */ jsx(ExportDialog, {
|
|
1050
|
+
open: exportOpen,
|
|
1051
|
+
pending: exportThreadMutation.isPending,
|
|
1052
|
+
onExport: (options) => exportThreadMutation.mutate(options),
|
|
1053
|
+
onOpenChange: setExportOpen
|
|
1054
|
+
})
|
|
1055
|
+
]
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
//#endregion
|
|
1059
|
+
export { ThreadDetailPage as component };
|