@sentry/junior-dashboard 0.263.0 → 0.265.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/LICENSE +201 -0
- package/README.md +2 -5
- package/dist/app.js +119 -35
- package/dist/client.js +148 -135
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +9 -4
- package/src/api/schema.ts +0 -1
- package/src/app.ts +5 -20
- package/src/client/App.tsx +29 -31
- package/src/client/api.ts +7 -0
- package/src/client/components/BottomSheet.tsx +26 -18
- package/src/client/components/CopyButton.tsx +83 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +34 -33
- package/src/client/components/FilterBar.tsx +16 -31
- package/src/client/components/Metric.tsx +33 -23
- package/src/client/components/PageContentSkeleton.tsx +1 -1
- package/src/client/components/Pagination.tsx +1 -1
- package/src/client/components/ParticipantAvatarStack.tsx +40 -36
- package/src/client/components/ProfileMenu.tsx +27 -93
- package/src/client/components/README.md +101 -64
- package/src/client/components/SegmentedNav.tsx +1 -1
- package/src/client/components/ShimmerText.tsx +2 -6
- package/src/client/components/ThemeSwitcher.tsx +19 -15
- package/src/client/components/charts/ActivityChart.tsx +173 -288
- package/src/client/components/charts/CacheWrites.tsx +63 -50
- package/src/client/components/charts/InputCacheChart.tsx +137 -131
- package/src/client/components/charts/README.md +4 -4
- package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
- package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
- package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
- package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
- package/src/client/components/layout/DashboardHeader.tsx +44 -90
- package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
- package/src/client/components/layout/VisualViewportShell.tsx +2 -2
- package/src/client/components/ui/accordion.tsx +77 -0
- package/src/client/components/ui/alert-dialog.tsx +89 -0
- package/src/client/components/ui/button.tsx +85 -0
- package/src/client/components/ui/chart.tsx +74 -0
- package/src/client/components/ui/checkbox.tsx +26 -0
- package/src/client/components/ui/collapsible.tsx +22 -0
- package/src/client/components/ui/field.tsx +49 -0
- package/src/client/components/ui/input.tsx +35 -0
- package/src/client/components/ui/native-select.tsx +57 -0
- package/src/client/components/ui/popover.tsx +67 -0
- package/src/client/components/ui/radio-group.tsx +32 -0
- package/src/client/components/ui/sheet.tsx +151 -0
- package/src/client/components/ui/skeleton.tsx +19 -0
- package/src/client/components/ui/table.tsx +80 -0
- package/src/client/components/ui/tabs.tsx +75 -0
- package/src/client/components/ui/textarea.tsx +17 -0
- package/src/client/components/ui/toast.tsx +215 -0
- package/src/client/components/ui/toggle-group.tsx +36 -0
- package/src/client/components/ui/toggle.tsx +37 -0
- package/src/client/components/ui/tooltip.tsx +168 -0
- package/src/client/components/ui/trace.tsx +96 -0
- package/src/client/components/ui/useSheet.ts +74 -0
- package/src/client/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationChildAgents.tsx +87 -58
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +198 -95
- package/src/client/conversations/ConversationEventDetails.tsx +141 -0
- package/src/client/conversations/ConversationEventLog.tsx +150 -129
- package/src/client/conversations/ConversationHeader.tsx +8 -3
- package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
- package/src/client/conversations/ConversationMeta.tsx +111 -92
- package/src/client/conversations/ConversationPage.tsx +241 -110
- package/src/client/conversations/ConversationSidebar.tsx +48 -29
- package/src/client/conversations/ConversationUsageBreakdown.tsx +16 -3
- package/src/client/conversations/ConversationWatches.tsx +165 -0
- package/src/client/conversations/ConversationWorkspace.tsx +12 -22
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
- package/src/client/conversations/DetailsList.tsx +124 -0
- package/src/client/conversations/EventDetails.tsx +42 -8
- package/src/client/conversations/ForkConversationButton.tsx +27 -16
- package/src/client/conversations/ObjectCard.tsx +20 -8
- package/src/client/conversations/PendingMailboxStack.tsx +52 -33
- package/src/client/conversations/README.md +93 -19
- package/src/client/conversations/ShareConversationButton.tsx +28 -3
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
- package/src/client/conversations/ToolFrame.tsx +36 -52
- package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
- package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
- package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
- package/src/client/conversations/TranscriptLoading.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
- package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
- package/src/client/conversations/TranscriptSubagentView.tsx +3 -3
- package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
- package/src/client/conversations/TranscriptToolView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
- package/src/client/conversations/TranscriptView.tsx +63 -27
- package/src/client/conversations/eventLog.ts +57 -0
- package/src/client/conversations/eventTime.ts +60 -0
- package/src/client/conversations/queries.ts +29 -0
- package/src/client/conversations/transcriptBottomPinning.ts +34 -0
- package/src/client/format.ts +5 -0
- package/src/client/lib/utils.ts +1 -0
- package/src/client/pages/ConnectionsPage.tsx +2 -2
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/SettingsPage.tsx +9 -10
- package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
- package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
- package/src/client/pages/automations/AutomationDetails.tsx +193 -0
- package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
- package/src/client/pages/automations/AutomationEditor.tsx +41 -29
- package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
- package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
- package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
- package/src/client/pages/automations/AutomationFilters.tsx +189 -74
- package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
- package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
- package/src/client/pages/automations/AutomationRow.tsx +12 -138
- package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
- package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
- package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
- package/src/client/pages/automations/AutomationStatus.tsx +201 -0
- package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
- package/src/client/pages/automations/AutomationsPage.tsx +80 -68
- package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
- package/src/client/pages/automations/README.md +57 -0
- package/src/client/pages/automations/TriggerFilters.tsx +22 -0
- package/src/client/pages/automations/automationFormat.ts +37 -23
- package/src/client/pages/code/CodeActivityChart.tsx +59 -101
- package/src/client/pages/code/CodePage.tsx +52 -55
- package/src/client/pages/code/RepositoryPage.tsx +6 -8
- package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
- package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
- package/src/client/pages/memory/MemoryDetails.tsx +218 -0
- package/src/client/pages/memory/MemoryPage.tsx +168 -112
- package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
- package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/people/ContributionGrid.tsx +23 -16
- package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
- package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
- package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
- package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
- package/src/client/pages/system/GuardianActivity.tsx +1 -2
- package/src/client/pages/system/PluginBarChart.tsx +106 -87
- package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
- package/src/client/pages/system/README.md +23 -0
- package/src/client/pages/system/SystemNavigation.tsx +5 -1
- package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
- package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
- package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
- package/src/client/pages/system/WorkspaceList.tsx +20 -30
- package/src/client/pages/system/WorkspacesPage.tsx +117 -119
- package/src/client/pages/system/sandboxSize.ts +8 -0
- package/src/client/pages/system/workspaceQueries.ts +53 -0
- package/src/client/pages/user/PluginUserPage.tsx +51 -2
- package/src/client/pages/user/pluginUserPageData.ts +1 -6
- package/src/client/styles.ts +4 -0
- package/src/client.tsx +7 -1
- package/src/mock-reporting/automation-collection.ts +4 -2
- package/src/mock-reporting/fixtures.ts +23 -7
- package/src/mock-reporting/routes.ts +12 -0
- package/src/mock-reporting/watches.ts +94 -0
- package/src/shell.ts +4 -9
- package/src/tailwind.css +77 -10
- package/src/client/components/Button.tsx +0 -118
- package/src/client/components/Field.tsx +0 -53
- package/src/client/components/SegmentedTabs.tsx +0 -119
- package/src/client/components/Select.tsx +0 -52
- package/src/client/components/Sheet.LICENSE +0 -21
- package/src/client/components/Sheet.tsx +0 -141
- package/src/client/components/Skeleton.tsx +0 -17
- package/src/client/components/TextInput.tsx +0 -58
- package/src/client/components/Tooltip.tsx +0 -216
- package/src/client/components/charts/useChartLayout.ts +0 -27
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -91
- package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
- package/src/client/pages/dev/ChartFixtures.tsx +0 -325
- package/src/client/pages/dev/ComponentsPage.tsx +0 -899
- package/src/client/pages/dev/ConversationFixture.tsx +0 -231
- package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
- package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
- package/src/client/pages/dev/SelectFixtures.tsx +0 -62
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
- package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
- package/src/client/pages/system/SnapshotSummary.tsx +0 -78
|
@@ -1,138 +1,241 @@
|
|
|
1
|
-
import { ExternalLink } from "lucide-react";
|
|
2
1
|
import { useState, type ReactNode } from "react";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import { DetailsPanel, useDetailsPanel } from "../components/DetailsPanel";
|
|
4
|
+
import {
|
|
5
|
+
Tooltip,
|
|
6
|
+
TooltipContent,
|
|
7
|
+
TooltipTrigger,
|
|
8
|
+
} from "../components/ui/tooltip";
|
|
9
|
+
import {
|
|
10
|
+
Tabs,
|
|
11
|
+
TabsContent,
|
|
12
|
+
TabsList,
|
|
13
|
+
TabsTrigger,
|
|
14
|
+
} from "../components/ui/tabs";
|
|
8
15
|
import {
|
|
9
16
|
ConversationChildAgents,
|
|
10
17
|
useConversationChildAgents,
|
|
11
18
|
} from "./ConversationChildAgents";
|
|
19
|
+
import {
|
|
20
|
+
ConversationLinkedWork,
|
|
21
|
+
type ConversationLink,
|
|
22
|
+
} from "./ConversationLinkedWork";
|
|
12
23
|
import {
|
|
13
24
|
ConversationMemories,
|
|
14
25
|
useConversationMemories,
|
|
15
26
|
} from "./ConversationMemories";
|
|
27
|
+
import {
|
|
28
|
+
ConversationWatches,
|
|
29
|
+
useConversationWatches,
|
|
30
|
+
} from "./ConversationWatches";
|
|
16
31
|
import { ShareConversationButton } from "./ShareConversationButton";
|
|
17
32
|
import type { SubagentTranscriptTarget } from "./SubagentTranscriptDrawer";
|
|
18
33
|
|
|
19
34
|
type DetailsTab = "agents" | "summary" | "memories";
|
|
20
35
|
|
|
21
|
-
/**
|
|
36
|
+
/** Keep the tabs fixed while each panel scrolls to the frame edge. */
|
|
37
|
+
const detailsPanelClass =
|
|
38
|
+
"min-h-0 overflow-y-auto overscroll-contain rounded-none px-4 pb-[max(1rem,env(safe-area-inset-bottom))] focus-visible:-outline-offset-2";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Show conversation details: the brief, participants, and run facts first,
|
|
42
|
+
* then watches, linked work, and the usage breakdown. Share and Sentry links
|
|
43
|
+
* stay in the header so every tab can reach them.
|
|
44
|
+
*/
|
|
22
45
|
export function ConversationDetails(props: {
|
|
23
|
-
annotations: ReactNode;
|
|
24
46
|
brief: ReactNode;
|
|
25
47
|
conversationId: string;
|
|
26
48
|
identity: ReactNode;
|
|
27
|
-
|
|
49
|
+
links: ConversationLink[];
|
|
28
50
|
onClose(): void;
|
|
29
|
-
/** Open one child agent transcript.
|
|
51
|
+
/** Open one child agent transcript. A details sheet closes first. */
|
|
30
52
|
onOpenSubagent(target: SubagentTranscriptTarget): void;
|
|
53
|
+
overview: ReactNode;
|
|
31
54
|
sentryConversationUrl?: string;
|
|
32
|
-
|
|
55
|
+
usage: ReactNode;
|
|
33
56
|
}) {
|
|
34
57
|
const [tab, setTab] = useState<DetailsTab>("summary");
|
|
58
|
+
const panel = useDetailsPanel();
|
|
35
59
|
// Load both lists with details so each tab can show its count.
|
|
36
60
|
const memories = useConversationMemories(props.conversationId);
|
|
37
61
|
const agents = useConversationChildAgents(props.conversationId);
|
|
38
|
-
const
|
|
39
|
-
{ label: "Summary", value: "summary" },
|
|
40
|
-
{
|
|
41
|
-
count: memories.data?.memories.length,
|
|
42
|
-
label: "Memories",
|
|
43
|
-
value: "memories",
|
|
44
|
-
},
|
|
45
|
-
{ count: agents.data?.agents.length, label: "Subagents", value: "agents" },
|
|
46
|
-
] as const;
|
|
62
|
+
const watches = useConversationWatches(props.conversationId).data?.watches;
|
|
47
63
|
|
|
64
|
+
// People first, then run facts. Watches come before linked work: they say
|
|
65
|
+
// what can wake this agent later, so readers see them without another tab.
|
|
48
66
|
const sections = [
|
|
49
|
-
{ content: props.
|
|
50
|
-
{ content: props.
|
|
51
|
-
{
|
|
52
|
-
|
|
67
|
+
{ content: props.identity, key: "participants", title: "Participants" },
|
|
68
|
+
{ content: props.overview, key: "overview", title: undefined },
|
|
69
|
+
{
|
|
70
|
+
content: watches?.length ? (
|
|
71
|
+
<ConversationWatches watches={watches} />
|
|
72
|
+
) : null,
|
|
73
|
+
count: watches?.length,
|
|
74
|
+
key: "watching",
|
|
75
|
+
title: "Watching",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
content: props.links.length ? (
|
|
79
|
+
<ConversationLinkedWork links={props.links} />
|
|
80
|
+
) : null,
|
|
81
|
+
count: props.links.length,
|
|
82
|
+
key: "linked-work",
|
|
83
|
+
title: "Linked work",
|
|
84
|
+
},
|
|
85
|
+
{ content: props.usage, key: "usage", title: "Usage" },
|
|
53
86
|
].filter((section) => section.content != null);
|
|
54
87
|
|
|
55
88
|
return (
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
89
|
+
<DetailsPanel
|
|
90
|
+
label="Conversation details"
|
|
91
|
+
title={
|
|
92
|
+
<h2
|
|
93
|
+
aria-label="Conversation details"
|
|
94
|
+
className="m-0 text-base font-semibold text-dashboard-text"
|
|
95
|
+
>
|
|
96
|
+
Details
|
|
97
|
+
</h2>
|
|
98
|
+
}
|
|
99
|
+
actions={
|
|
100
|
+
<>
|
|
101
|
+
<ShareConversationButton
|
|
102
|
+
key={props.conversationId}
|
|
103
|
+
conversationId={props.conversationId}
|
|
104
|
+
layout="icon"
|
|
105
|
+
/>
|
|
106
|
+
{props.sentryConversationUrl ? (
|
|
107
|
+
<Tooltip>
|
|
108
|
+
<TooltipTrigger
|
|
109
|
+
render={
|
|
110
|
+
<a
|
|
111
|
+
aria-label="Open in Sentry"
|
|
112
|
+
className="grid size-9 place-items-center rounded-md text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-strong hover:text-dashboard-text focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashboard-focus"
|
|
113
|
+
href={props.sentryConversationUrl}
|
|
114
|
+
rel="noopener noreferrer"
|
|
115
|
+
target="_blank"
|
|
116
|
+
>
|
|
117
|
+
<SentryGlyph />
|
|
118
|
+
</a>
|
|
119
|
+
}
|
|
120
|
+
/>
|
|
121
|
+
<TooltipContent size="compact" side="bottom">
|
|
122
|
+
Open in Sentry
|
|
123
|
+
</TooltipContent>
|
|
124
|
+
</Tooltip>
|
|
125
|
+
) : null}
|
|
126
|
+
</>
|
|
127
|
+
}
|
|
128
|
+
onClose={props.onClose}
|
|
129
|
+
>
|
|
130
|
+
<Tabs
|
|
131
|
+
className="h-full min-h-0 grid-rows-[auto_minmax(0,1fr)] gap-0"
|
|
132
|
+
onValueChange={(value: DetailsTab) => setTab(value)}
|
|
62
133
|
value={tab}
|
|
63
134
|
>
|
|
64
|
-
|
|
135
|
+
<TabsList
|
|
136
|
+
activateOnFocus
|
|
137
|
+
aria-label="Conversation details"
|
|
138
|
+
className="m-4"
|
|
139
|
+
>
|
|
140
|
+
<TabsTrigger value="summary">
|
|
141
|
+
<span className="truncate">Summary</span>
|
|
142
|
+
</TabsTrigger>
|
|
143
|
+
<TabsTrigger value="memories">
|
|
144
|
+
<span className="truncate">Memories</span>
|
|
145
|
+
<TabCount count={memories.data?.memories.length} />
|
|
146
|
+
</TabsTrigger>
|
|
147
|
+
<TabsTrigger value="agents">
|
|
148
|
+
<span className="truncate">Subagents</span>
|
|
149
|
+
<TabCount count={agents.data?.agents.length} />
|
|
150
|
+
</TabsTrigger>
|
|
151
|
+
</TabsList>
|
|
152
|
+
<TabsContent className={detailsPanelClass} value="summary">
|
|
153
|
+
{props.brief != null || sections.length > 0 ? (
|
|
154
|
+
<div className="grid min-w-0 gap-3">
|
|
155
|
+
{props.brief != null ? (
|
|
156
|
+
<div className="min-w-0 break-words px-1 pb-1 font-sans text-sm leading-relaxed text-dashboard-text-muted">
|
|
157
|
+
{props.brief}
|
|
158
|
+
</div>
|
|
159
|
+
) : null}
|
|
160
|
+
{sections.map((section) => (
|
|
161
|
+
<DetailsSection
|
|
162
|
+
count={section.count}
|
|
163
|
+
key={section.key}
|
|
164
|
+
title={section.title}
|
|
165
|
+
>
|
|
166
|
+
{section.content}
|
|
167
|
+
</DetailsSection>
|
|
168
|
+
))}
|
|
169
|
+
</div>
|
|
170
|
+
) : (
|
|
171
|
+
<p className="m-0 font-sans text-sm leading-relaxed text-dashboard-text-muted">
|
|
172
|
+
No additional conversation details.
|
|
173
|
+
</p>
|
|
174
|
+
)}
|
|
175
|
+
</TabsContent>
|
|
176
|
+
<TabsContent className={detailsPanelClass} value="memories">
|
|
65
177
|
<ConversationMemories conversationId={props.conversationId} />
|
|
66
|
-
|
|
178
|
+
</TabsContent>
|
|
179
|
+
<TabsContent className={detailsPanelClass} value="agents">
|
|
67
180
|
<ConversationChildAgents
|
|
68
181
|
conversationId={props.conversationId}
|
|
69
182
|
onOpenAgent={(target) => {
|
|
70
|
-
|
|
183
|
+
// The panel stays beside the drawer. A modal sheet cannot.
|
|
184
|
+
if (!panel) props.onClose();
|
|
71
185
|
props.onOpenSubagent(target);
|
|
72
186
|
}}
|
|
73
187
|
/>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<ExternalLink
|
|
127
|
-
aria-hidden="true"
|
|
128
|
-
className="size-3.5 shrink-0"
|
|
129
|
-
/>
|
|
130
|
-
</ButtonLink>
|
|
131
|
-
) : null}
|
|
132
|
-
</div>
|
|
133
|
-
</footer>
|
|
134
|
-
) : null}
|
|
135
|
-
</SegmentedTabs>
|
|
136
|
-
</ConversationDetailsFrame>
|
|
188
|
+
</TabsContent>
|
|
189
|
+
</Tabs>
|
|
190
|
+
</DetailsPanel>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Show a panel's entry count. Zero and unknown counts are hidden. */
|
|
195
|
+
function TabCount(props: { count: number | undefined }) {
|
|
196
|
+
if (!props.count) return null;
|
|
197
|
+
return (
|
|
198
|
+
<span className="min-w-5 shrink-0 rounded-full bg-dashboard-fill-strong px-1.5 text-center text-xs font-semibold leading-5 tabular-nums">
|
|
199
|
+
{props.count.toLocaleString("en-US")}
|
|
200
|
+
</span>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Group one kind of conversation detail in a compact card. */
|
|
205
|
+
function DetailsSection(props: {
|
|
206
|
+
children: ReactNode;
|
|
207
|
+
count?: number;
|
|
208
|
+
title?: string;
|
|
209
|
+
}) {
|
|
210
|
+
return (
|
|
211
|
+
<section className="grid min-w-0 gap-2.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel p-3">
|
|
212
|
+
{props.title ? (
|
|
213
|
+
<h3 className="m-0 flex items-center gap-1.5 font-sans text-xs font-medium text-dashboard-text-muted">
|
|
214
|
+
{props.title}
|
|
215
|
+
{props.count ? (
|
|
216
|
+
<span className="tabular-nums text-dashboard-text-faint">
|
|
217
|
+
{props.count.toLocaleString("en-US")}
|
|
218
|
+
</span>
|
|
219
|
+
) : null}
|
|
220
|
+
</h3>
|
|
221
|
+
) : null}
|
|
222
|
+
<div className="min-w-0 break-words font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
223
|
+
{props.children}
|
|
224
|
+
</div>
|
|
225
|
+
</section>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Draw the Sentry glyph in the current text color, sized like a header icon. */
|
|
230
|
+
function SentryGlyph() {
|
|
231
|
+
return (
|
|
232
|
+
<svg
|
|
233
|
+
aria-hidden="true"
|
|
234
|
+
className="size-4"
|
|
235
|
+
fill="currentColor"
|
|
236
|
+
viewBox="0 0 24 24"
|
|
237
|
+
>
|
|
238
|
+
<path d="M13.91 2.505c-.873-1.448-2.972-1.448-3.844 0L6.904 7.92a15.478 15.478 0 0 1 8.53 12.811h-2.221A13.301 13.301 0 0 0 5.784 9.814l-2.926 5.06a7.65 7.65 0 0 1 4.435 5.848H2.194a.365.365 0 0 1-.298-.534l1.413-2.402a5.16 5.16 0 0 0-1.614-.913L.296 19.275a2.182 2.182 0 0 0 .812 2.999 2.24 2.24 0 0 0 1.086.288h6.983a9.322 9.322 0 0 0-3.845-8.318l1.11-1.922a11.47 11.47 0 0 1 4.95 10.24h5.915a17.242 17.242 0 0 0-7.885-15.28l2.244-3.845a.37.37 0 0 1 .504-.13c.255.14 9.75 16.708 9.928 16.9a.365.365 0 0 1-.327.543h-2.287c.029.612.029 1.223 0 1.831h2.297a2.206 2.206 0 0 0 1.922-3.31z" />
|
|
239
|
+
</svg>
|
|
137
240
|
);
|
|
138
241
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { ConversationReportEvent } from "@sentry/junior/api/schema";
|
|
2
|
+
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../styles";
|
|
5
|
+
import { DetailsPanel } from "../components/DetailsPanel";
|
|
6
|
+
import { Button } from "../components/ui/button";
|
|
7
|
+
import { Skeleton } from "../components/ui/skeleton";
|
|
8
|
+
import type { ConversationTranscript } from "../types";
|
|
9
|
+
import { useEventTime } from "./eventTime";
|
|
10
|
+
import { EventDetails } from "./EventDetails";
|
|
11
|
+
import { ForkConversationButton } from "./ForkConversationButton";
|
|
12
|
+
|
|
13
|
+
// Step buttons keep focus at the ends of the log, so style the ARIA state.
|
|
14
|
+
const stepButtonClass =
|
|
15
|
+
"data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:hover:!bg-transparent data-disabled:hover:!text-dashboard-text-muted";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Show the selected event without blocking the main conversation's event log.
|
|
19
|
+
* Without an event, show a loading state while a linked event loads.
|
|
20
|
+
* `previous` and `next` are the adjacent visible rows of the event log.
|
|
21
|
+
*/
|
|
22
|
+
export function ConversationEventDetails(props: {
|
|
23
|
+
conversation?: ConversationTranscript;
|
|
24
|
+
event?: ConversationReportEvent;
|
|
25
|
+
next?: number;
|
|
26
|
+
onClose(): void;
|
|
27
|
+
onSelect?(seq: number): void;
|
|
28
|
+
overlay?: boolean;
|
|
29
|
+
previous?: number;
|
|
30
|
+
}) {
|
|
31
|
+
const { conversation, event, onSelect } = props;
|
|
32
|
+
const time = useEventTime();
|
|
33
|
+
const timestamp = event ? (
|
|
34
|
+
<time
|
|
35
|
+
className="block break-words font-mono text-xs text-dashboard-text-muted"
|
|
36
|
+
dateTime={event.createdAt}
|
|
37
|
+
title={event.createdAt}
|
|
38
|
+
>
|
|
39
|
+
{time.formatTimestamp(event.createdAt)}
|
|
40
|
+
</time>
|
|
41
|
+
) : null;
|
|
42
|
+
const title = event ? (
|
|
43
|
+
<>
|
|
44
|
+
<h2 className="m-0 mb-1 break-words font-mono text-sm font-semibold text-dashboard-text">
|
|
45
|
+
{event.data.type}
|
|
46
|
+
</h2>
|
|
47
|
+
{timestamp}
|
|
48
|
+
</>
|
|
49
|
+
) : (
|
|
50
|
+
<div className="grid gap-2 py-0.5">
|
|
51
|
+
<Skeleton className="h-3.5 w-28" />
|
|
52
|
+
<Skeleton className="h-3 w-40" />
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
const actions =
|
|
56
|
+
onSelect && event ? (
|
|
57
|
+
<>
|
|
58
|
+
<Button
|
|
59
|
+
aria-label="Previous event"
|
|
60
|
+
className={stepButtonClass}
|
|
61
|
+
disabled={props.previous === undefined}
|
|
62
|
+
focusableWhenDisabled
|
|
63
|
+
onClick={() => {
|
|
64
|
+
if (props.previous !== undefined) onSelect(props.previous);
|
|
65
|
+
}}
|
|
66
|
+
size="icon"
|
|
67
|
+
title="Previous event"
|
|
68
|
+
>
|
|
69
|
+
<ChevronUp aria-hidden="true" size={15} />
|
|
70
|
+
</Button>
|
|
71
|
+
<Button
|
|
72
|
+
aria-label="Next event"
|
|
73
|
+
className={stepButtonClass}
|
|
74
|
+
disabled={props.next === undefined}
|
|
75
|
+
focusableWhenDisabled
|
|
76
|
+
onClick={() => {
|
|
77
|
+
if (props.next !== undefined) onSelect(props.next);
|
|
78
|
+
}}
|
|
79
|
+
size="icon"
|
|
80
|
+
title="Next event"
|
|
81
|
+
>
|
|
82
|
+
<ChevronDown aria-hidden="true" size={15} />
|
|
83
|
+
</Button>
|
|
84
|
+
</>
|
|
85
|
+
) : undefined;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<DetailsPanel
|
|
89
|
+
actions={actions}
|
|
90
|
+
label="Event details"
|
|
91
|
+
onClose={props.onClose}
|
|
92
|
+
overlay={props.overlay}
|
|
93
|
+
sheetTitle={event?.data.type}
|
|
94
|
+
title={title}
|
|
95
|
+
>
|
|
96
|
+
{/* Own the scroll here, so a new event starts at the top with closed
|
|
97
|
+
disclosures, and the sheet actions stay below the content. */}
|
|
98
|
+
<div
|
|
99
|
+
className={cn(
|
|
100
|
+
"h-full min-w-0 overflow-y-auto overscroll-contain p-4 pb-[max(1rem,env(safe-area-inset-bottom))]",
|
|
101
|
+
props.overlay && "xl:p-0",
|
|
102
|
+
)}
|
|
103
|
+
key={event?.seq ?? "loading"}
|
|
104
|
+
>
|
|
105
|
+
{event && conversation ? (
|
|
106
|
+
<>
|
|
107
|
+
<div className="mb-4 xl:hidden">{timestamp}</div>
|
|
108
|
+
{conversation.canFork &&
|
|
109
|
+
event.data.type === "message" &&
|
|
110
|
+
event.data.role === "assistant" &&
|
|
111
|
+
!event.data.redacted ? (
|
|
112
|
+
<div className="mb-3 flex items-center gap-2 text-sm">
|
|
113
|
+
<ForkConversationButton
|
|
114
|
+
conversationId={conversation.conversationId}
|
|
115
|
+
messageId={event.data.messageId}
|
|
116
|
+
/>
|
|
117
|
+
<span>Fork after this reply</span>
|
|
118
|
+
</div>
|
|
119
|
+
) : null}
|
|
120
|
+
<EventDetails event={event} />
|
|
121
|
+
</>
|
|
122
|
+
) : (
|
|
123
|
+
<EventDetailsLoading />
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</DetailsPanel>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Keep the body quiet while the event loads. The header placeholders carry the
|
|
132
|
+
* state, so the panel does not compete with the log skeleton.
|
|
133
|
+
*/
|
|
134
|
+
function EventDetailsLoading() {
|
|
135
|
+
return (
|
|
136
|
+
<div aria-busy="true" role="status">
|
|
137
|
+
<span className="sr-only">Loading event</span>
|
|
138
|
+
<Skeleton className="h-3 w-40 xl:hidden" />
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|