@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,33 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { memo, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
3
2
|
import { createPortal } from "react-dom";
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { Skeleton } from "../components/ui/skeleton";
|
|
5
|
+
import { Toggle } from "../components/ui/toggle";
|
|
6
|
+
import { useEventTime } from "./eventTime";
|
|
7
|
+
import { SearchInput } from "../components/SearchInput";
|
|
6
8
|
import { cn } from "../styles";
|
|
7
9
|
import type { ConversationTranscript } from "../types";
|
|
8
10
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
adjacentEventLogSeqs,
|
|
12
|
+
eventLogRows,
|
|
11
13
|
eventLogTone,
|
|
12
|
-
|
|
14
|
+
filterEventLogRows,
|
|
13
15
|
} from "./eventLog";
|
|
14
|
-
import {
|
|
15
|
-
import { ForkConversationButton } from "./ForkConversationButton";
|
|
16
|
+
import { ConversationEventDetails } from "./ConversationEventDetails";
|
|
16
17
|
import { HighlightText, useTranscriptSearch } from "./transcriptSearch";
|
|
17
18
|
|
|
18
19
|
const rowClass =
|
|
19
|
-
"grid grid-cols-[12ch_minmax(0,1fr)
|
|
20
|
+
"grid grid-cols-[12ch_minmax(0,1fr)] items-baseline gap-x-3 px-4 @min-[32rem]:grid-cols-[12ch_17ch_minmax(0,1fr)]";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Show every reporting event in sequence order, outside the transcript reducer.
|
|
23
|
-
* Pass `onSelectedSeqChange` to own
|
|
24
|
+
* Pass `onSelectedSeqChange` to own selection and render details beside the log.
|
|
25
|
+
* Standalone transcripts keep a local selection and show an overlay.
|
|
24
26
|
*/
|
|
25
27
|
export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
26
28
|
conversation: ConversationTranscript;
|
|
29
|
+
onSearchChange(value: string): void;
|
|
27
30
|
onSelectedSeqChange?(seq: number | undefined): void;
|
|
28
31
|
selectedSeq?: number;
|
|
29
32
|
}) {
|
|
30
33
|
const { conversation } = props;
|
|
34
|
+
const time = useEventTime();
|
|
31
35
|
const search = useTranscriptSearch();
|
|
32
36
|
const [localSelectedSeq, setLocalSelectedSeq] = useState<number>();
|
|
33
37
|
const selectedSeq = props.onSelectedSeqChange
|
|
@@ -35,31 +39,14 @@ export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
|
35
39
|
: localSelectedSeq;
|
|
36
40
|
const setSelectedSeq = props.onSelectedSeqChange ?? setLocalSelectedSeq;
|
|
37
41
|
const listRef = useRef<HTMLOListElement>(null);
|
|
38
|
-
//
|
|
42
|
+
// Clicked rows are visible. Reveal links and keyboard selections after layout.
|
|
39
43
|
const revealedSeqRef = useRef<number>(undefined);
|
|
40
|
-
const titleId = useId();
|
|
41
44
|
const events = conversation.events;
|
|
42
|
-
const rows = useMemo(
|
|
43
|
-
() =>
|
|
44
|
-
events.map((event) => ({
|
|
45
|
-
event,
|
|
46
|
-
model: eventLogModel(event),
|
|
47
|
-
usage: eventLogUsage(event),
|
|
48
|
-
summary: eventLogSummary(event.data).replace(/\s+/g, " ").slice(0, 300),
|
|
49
|
-
})),
|
|
50
|
-
[events],
|
|
51
|
-
);
|
|
45
|
+
const rows = useMemo(() => eventLogRows(events), [events]);
|
|
52
46
|
// Large payloads are serialized only for an active search or the open entry.
|
|
53
47
|
const visibleRows = useMemo(
|
|
54
|
-
() =>
|
|
55
|
-
|
|
56
|
-
? rows.filter(({ event, summary, usage }) =>
|
|
57
|
-
`${summary} ${usage} ${JSON.stringify(event)}`
|
|
58
|
-
.toLowerCase()
|
|
59
|
-
.includes(search.normalizedQuery),
|
|
60
|
-
)
|
|
61
|
-
: rows,
|
|
62
|
-
[rows, search.active, search.normalizedQuery],
|
|
48
|
+
() => filterEventLogRows(rows, search.normalizedQuery),
|
|
49
|
+
[rows, search.normalizedQuery],
|
|
63
50
|
);
|
|
64
51
|
// Resolve the selection from current data so polling cannot leave stale details.
|
|
65
52
|
const selected = events.find((event) => event.seq === selectedSeq);
|
|
@@ -72,7 +59,7 @@ export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
|
72
59
|
);
|
|
73
60
|
if (!row) return;
|
|
74
61
|
revealedSeqRef.current = selectedSeq;
|
|
75
|
-
row.scrollIntoView({ block: "
|
|
62
|
+
row.scrollIntoView({ block: "nearest" });
|
|
76
63
|
}, [selectedSeq, visibleRows]);
|
|
77
64
|
|
|
78
65
|
if (conversation.eventHistory.status === "expired") {
|
|
@@ -85,54 +72,89 @@ export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
|
85
72
|
|
|
86
73
|
return (
|
|
87
74
|
<section aria-label="Conversation event log" className="@container min-w-0">
|
|
88
|
-
<div className="
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
75
|
+
<div className="sticky top-0 z-10 bg-dashboard-bg">
|
|
76
|
+
<div className="flex flex-wrap items-center gap-2 border-b border-dashboard-border px-4 py-2">
|
|
77
|
+
<SearchInput
|
|
78
|
+
className="min-w-32 flex-1 @max-[28rem]:basis-full"
|
|
79
|
+
label="Search events"
|
|
80
|
+
onChange={props.onSearchChange}
|
|
81
|
+
placeholder="Search events…"
|
|
82
|
+
size="compact"
|
|
83
|
+
value={search.query}
|
|
84
|
+
/>
|
|
85
|
+
<div className="ml-auto flex items-center gap-3 whitespace-nowrap text-xs text-dashboard-text-muted @max-[28rem]:w-full @max-[28rem]:justify-between">
|
|
86
|
+
<span role="status">
|
|
87
|
+
{search.active
|
|
88
|
+
? `${visibleRows.length} of ${events.length} loaded events`
|
|
89
|
+
: `${events.length} loaded events`}
|
|
90
|
+
</span>
|
|
91
|
+
<Toggle
|
|
92
|
+
aria-label="Use local time"
|
|
93
|
+
onPressedChange={time.toggle}
|
|
94
|
+
pressed={time.local}
|
|
95
|
+
title={time.title}
|
|
96
|
+
>
|
|
97
|
+
<span className="max-w-32 truncate">{time.label}</span>
|
|
98
|
+
</Toggle>
|
|
99
|
+
</div>
|
|
108
100
|
</div>
|
|
101
|
+
<EventLogColumns />
|
|
102
|
+
</div>
|
|
103
|
+
<div className="font-mono text-xs">
|
|
109
104
|
<ol
|
|
110
105
|
aria-label="Events"
|
|
111
106
|
className="m-0 list-none divide-y divide-dashboard-border-subtle p-0"
|
|
112
107
|
ref={listRef}
|
|
113
108
|
>
|
|
114
|
-
{visibleRows.map(({ event, summary,
|
|
115
|
-
<li
|
|
109
|
+
{visibleRows.map(({ event, summary, metadata }, index) => (
|
|
110
|
+
<li
|
|
111
|
+
className="scroll-mt-32 scroll-mb-2"
|
|
112
|
+
data-event-seq={event.seq}
|
|
113
|
+
key={event.seq}
|
|
114
|
+
>
|
|
116
115
|
<button
|
|
117
|
-
aria-
|
|
116
|
+
aria-expanded={selectedSeq === event.seq}
|
|
118
117
|
aria-label={`Event ${event.seq}: ${event.data.type}`}
|
|
119
118
|
className={cn(
|
|
120
119
|
rowClass,
|
|
121
|
-
"min-h-11 w-full cursor-pointer border-0 bg-transparent py-2 text-left leading-5 hover:bg-dashboard-fill-hover active:bg-dashboard-fill-strong focus-visible:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus @min-[
|
|
122
|
-
selectedSeq === event.seq &&
|
|
120
|
+
"relative min-h-11 w-full cursor-pointer border-0 bg-transparent py-2 text-left leading-5 hover:bg-dashboard-fill-hover active:bg-dashboard-fill-strong focus-visible:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus @min-[32rem]:min-h-9 @min-[32rem]:py-1.5",
|
|
121
|
+
selectedSeq === event.seq &&
|
|
122
|
+
"bg-dashboard-fill-strong before:absolute before:inset-y-0 before:left-0 before:w-0.5 before:bg-dashboard-accent before:content-['']",
|
|
123
123
|
)}
|
|
124
124
|
onClick={() => {
|
|
125
125
|
revealedSeqRef.current = event.seq;
|
|
126
126
|
setSelectedSeq(event.seq);
|
|
127
127
|
}}
|
|
128
|
+
onKeyDown={(keyEvent) => {
|
|
129
|
+
if (
|
|
130
|
+
keyEvent.altKey ||
|
|
131
|
+
keyEvent.ctrlKey ||
|
|
132
|
+
keyEvent.metaKey ||
|
|
133
|
+
keyEvent.shiftKey ||
|
|
134
|
+
(keyEvent.key !== "ArrowUp" && keyEvent.key !== "ArrowDown")
|
|
135
|
+
)
|
|
136
|
+
return;
|
|
137
|
+
keyEvent.preventDefault();
|
|
138
|
+
const next =
|
|
139
|
+
visibleRows[
|
|
140
|
+
index + (keyEvent.key === "ArrowDown" ? 1 : -1)
|
|
141
|
+
];
|
|
142
|
+
if (!next) return;
|
|
143
|
+
setSelectedSeq(next.event.seq);
|
|
144
|
+
listRef.current
|
|
145
|
+
?.querySelector<HTMLButtonElement>(
|
|
146
|
+
`[data-event-seq="${next.event.seq}"] button`,
|
|
147
|
+
)
|
|
148
|
+
?.focus({ preventScroll: true });
|
|
149
|
+
}}
|
|
128
150
|
type="button"
|
|
129
151
|
>
|
|
130
152
|
<time
|
|
131
153
|
className="tabular-nums text-dashboard-text-muted"
|
|
132
154
|
dateTime={event.createdAt}
|
|
133
|
-
title={event.createdAt}
|
|
155
|
+
title={time.formatTimestamp(event.createdAt)}
|
|
134
156
|
>
|
|
135
|
-
{event.createdAt
|
|
157
|
+
{time.formatClock(event.createdAt)}
|
|
136
158
|
</time>
|
|
137
159
|
<span
|
|
138
160
|
className={cn("truncate", eventLogTone(event.data))}
|
|
@@ -140,50 +162,19 @@ export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
|
140
162
|
>
|
|
141
163
|
<HighlightText text={event.data.type} />
|
|
142
164
|
</span>
|
|
143
|
-
<span className="col-span-2 row-start-2 grid min-w-0 gap-
|
|
144
|
-
<span className="flex min-w-0 flex-wrap items-baseline gap-x-2">
|
|
145
|
-
<span
|
|
146
|
-
className={cn(
|
|
147
|
-
"break-all",
|
|
148
|
-
model
|
|
149
|
-
? "font-semibold text-violet-300"
|
|
150
|
-
: "text-dashboard-text-muted",
|
|
151
|
-
)}
|
|
152
|
-
>
|
|
153
|
-
<HighlightText
|
|
154
|
-
text={model?.modelId ?? "Model not recorded"}
|
|
155
|
-
/>
|
|
156
|
-
</span>
|
|
157
|
-
{model?.modelProfile ? (
|
|
158
|
-
<span className="text-dashboard-text-muted">
|
|
159
|
-
<HighlightText text={model.modelProfile} />
|
|
160
|
-
</span>
|
|
161
|
-
) : null}
|
|
162
|
-
{model?.reasoningLevel ? (
|
|
163
|
-
<span className="text-dashboard-text-muted">
|
|
164
|
-
<HighlightText
|
|
165
|
-
text={`${model.reasoningLevel} reasoning`}
|
|
166
|
-
/>
|
|
167
|
-
</span>
|
|
168
|
-
) : null}
|
|
169
|
-
</span>
|
|
165
|
+
<span className="col-span-2 row-start-2 grid min-w-0 gap-0.5 text-dashboard-text @min-[32rem]:col-span-1 @min-[32rem]:row-auto">
|
|
170
166
|
<span className="truncate" title={summary}>
|
|
171
167
|
<HighlightText text={summary} />
|
|
172
168
|
</span>
|
|
173
|
-
{
|
|
169
|
+
{metadata ? (
|
|
174
170
|
<span
|
|
175
|
-
className="text-dashboard-text-muted"
|
|
176
|
-
title=
|
|
171
|
+
className="truncate text-dashboard-text-muted"
|
|
172
|
+
title={metadata}
|
|
177
173
|
>
|
|
178
|
-
<HighlightText text={
|
|
174
|
+
<HighlightText text={metadata} />
|
|
179
175
|
</span>
|
|
180
176
|
) : null}
|
|
181
177
|
</span>
|
|
182
|
-
<ChevronRight
|
|
183
|
-
aria-hidden="true"
|
|
184
|
-
className="col-start-3 row-start-1 text-dashboard-text-muted @min-[48rem]:col-start-4"
|
|
185
|
-
size={14}
|
|
186
|
-
/>
|
|
187
178
|
</button>
|
|
188
179
|
</li>
|
|
189
180
|
))}
|
|
@@ -196,45 +187,75 @@ export const ConversationEventLog = memo(function ConversationEventLog(props: {
|
|
|
196
187
|
</p>
|
|
197
188
|
) : null}
|
|
198
189
|
</div>
|
|
199
|
-
{selected
|
|
190
|
+
{selected && !props.onSelectedSeqChange
|
|
200
191
|
? createPortal(
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<h2
|
|
206
|
-
className="m-0 break-words font-mono text-sm font-semibold text-dashboard-text"
|
|
207
|
-
id={titleId}
|
|
208
|
-
>
|
|
209
|
-
{selected.data.type}
|
|
210
|
-
</h2>
|
|
211
|
-
<p className="mb-0 mt-1 font-mono text-xs text-dashboard-text-muted">
|
|
212
|
-
{selected.createdAt}
|
|
213
|
-
</p>
|
|
214
|
-
</>
|
|
215
|
-
}
|
|
192
|
+
<ConversationEventDetails
|
|
193
|
+
overlay
|
|
194
|
+
conversation={conversation}
|
|
195
|
+
event={selected}
|
|
216
196
|
onClose={() => setSelectedSeq(undefined)}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
>
|
|
221
|
-
{conversation.canFork &&
|
|
222
|
-
selected.data.type === "message" &&
|
|
223
|
-
selected.data.role === "assistant" &&
|
|
224
|
-
!selected.data.redacted ? (
|
|
225
|
-
<div className="mb-3 flex items-center gap-2 text-sm">
|
|
226
|
-
<ForkConversationButton
|
|
227
|
-
conversationId={conversation.conversationId}
|
|
228
|
-
messageId={selected.data.messageId}
|
|
229
|
-
/>
|
|
230
|
-
<span>Fork after this reply</span>
|
|
231
|
-
</div>
|
|
232
|
-
) : null}
|
|
233
|
-
<EventDetails key={selected.seq} event={selected} />
|
|
234
|
-
</Drawer>,
|
|
197
|
+
onSelect={setSelectedSeq}
|
|
198
|
+
{...adjacentEventLogSeqs(visibleRows, selected.seq)}
|
|
199
|
+
/>,
|
|
235
200
|
document.body,
|
|
236
201
|
)
|
|
237
202
|
: null}
|
|
238
203
|
</section>
|
|
239
204
|
);
|
|
240
205
|
});
|
|
206
|
+
|
|
207
|
+
const loadingRowWidths = ["w-3/5", "w-2/5", "w-4/5", "w-1/2", "w-2/3", "w-1/3"];
|
|
208
|
+
|
|
209
|
+
/** Match the event log toolbar, columns, and rows while the transcript loads. */
|
|
210
|
+
export function ConversationEventLogLoading() {
|
|
211
|
+
return (
|
|
212
|
+
<section aria-busy="true" className="@container min-w-0">
|
|
213
|
+
<span className="sr-only" role="status">
|
|
214
|
+
Loading events
|
|
215
|
+
</span>
|
|
216
|
+
<div className="sticky top-0 z-10 bg-dashboard-bg">
|
|
217
|
+
<div className="flex flex-wrap items-center gap-2 border-b border-dashboard-border px-4 py-2">
|
|
218
|
+
<Skeleton className="h-9 min-w-32 flex-1 @max-[28rem]:basis-full" />
|
|
219
|
+
<Skeleton className="ml-auto h-3 w-24" />
|
|
220
|
+
</div>
|
|
221
|
+
<EventLogColumns />
|
|
222
|
+
</div>
|
|
223
|
+
<ol className="m-0 list-none divide-y divide-dashboard-border-subtle p-0">
|
|
224
|
+
{loadingRowWidths.map((width, index) => (
|
|
225
|
+
<li
|
|
226
|
+
className={cn(
|
|
227
|
+
rowClass,
|
|
228
|
+
"min-h-11 items-center gap-y-2 py-2 @min-[32rem]:min-h-9 @min-[32rem]:py-1.5",
|
|
229
|
+
)}
|
|
230
|
+
key={index}
|
|
231
|
+
>
|
|
232
|
+
<Skeleton className="h-3 w-[10ch]" />
|
|
233
|
+
<Skeleton className="h-3 w-[12ch]" />
|
|
234
|
+
<Skeleton
|
|
235
|
+
className={cn(
|
|
236
|
+
"col-span-2 row-start-2 h-3 @min-[32rem]:col-span-1 @min-[32rem]:row-auto",
|
|
237
|
+
width,
|
|
238
|
+
)}
|
|
239
|
+
/>
|
|
240
|
+
</li>
|
|
241
|
+
))}
|
|
242
|
+
</ol>
|
|
243
|
+
</section>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function EventLogColumns() {
|
|
248
|
+
return (
|
|
249
|
+
<div
|
|
250
|
+
aria-hidden="true"
|
|
251
|
+
className={cn(
|
|
252
|
+
rowClass,
|
|
253
|
+
"border-b border-dashboard-border py-2 font-mono text-xs text-dashboard-text-muted",
|
|
254
|
+
)}
|
|
255
|
+
>
|
|
256
|
+
<span>Time</span>
|
|
257
|
+
<span>Event</span>
|
|
258
|
+
<span className="hidden @min-[32rem]:block">Summary</span>
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
@@ -53,11 +53,16 @@ export function ConversationHeader(props: {
|
|
|
53
53
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
54
54
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
55
55
|
const mobileMenuRef = useRef<HTMLDivElement>(null);
|
|
56
|
-
const searchOpenVisible =
|
|
56
|
+
const searchOpenVisible =
|
|
57
|
+
props.view === "rich" && (searchOpen || props.search.length > 0);
|
|
57
58
|
const openMobileNavigation = useOpenMobileNavigation();
|
|
58
59
|
|
|
59
60
|
useEffect(() => {
|
|
60
|
-
if (
|
|
61
|
+
if (
|
|
62
|
+
wasDetailsOpen.current &&
|
|
63
|
+
!detailsOpen &&
|
|
64
|
+
document.activeElement === document.body
|
|
65
|
+
) {
|
|
61
66
|
const trigger = window.matchMedia("(max-width: 767px)").matches
|
|
62
67
|
? menuButtonRef.current
|
|
63
68
|
: detailsButtonRef.current;
|
|
@@ -182,7 +187,7 @@ export function ConversationHeader(props: {
|
|
|
182
187
|
<MobileHeaderLivePortal>{liveIndicator}</MobileHeaderLivePortal>
|
|
183
188
|
<header
|
|
184
189
|
className={cn(
|
|
185
|
-
"
|
|
190
|
+
"relative z-10 border-b border-dashboard-border bg-dashboard-bg",
|
|
186
191
|
!showMobileHeader && "hidden md:block",
|
|
187
192
|
)}
|
|
188
193
|
>
|
|
@@ -10,8 +10,13 @@ import {
|
|
|
10
10
|
X,
|
|
11
11
|
} from "lucide-react";
|
|
12
12
|
|
|
13
|
-
import { Button
|
|
14
|
-
import {
|
|
13
|
+
import { Button } from "../components/ui/button";
|
|
14
|
+
import {
|
|
15
|
+
Tooltip,
|
|
16
|
+
TooltipContent,
|
|
17
|
+
TooltipTrigger,
|
|
18
|
+
} from "../components/ui/tooltip";
|
|
19
|
+
import { ToggleGroup, ToggleGroupItem } from "../components/ui/toggle-group";
|
|
15
20
|
import { cn } from "../styles";
|
|
16
21
|
import type { TranscriptViewMode } from "./transcriptRenderModel";
|
|
17
22
|
import { ShareConversationButton } from "./ShareConversationButton";
|
|
@@ -43,17 +48,19 @@ export function ConversationHeaderActions(props: {
|
|
|
43
48
|
if (layout === "menu") {
|
|
44
49
|
return (
|
|
45
50
|
<div className="grid gap-0.5">
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
{props.view === "rich" ? (
|
|
52
|
+
<MenuActionButton
|
|
53
|
+
label={props.searchOpen ? "Hide search" : "Search transcript"}
|
|
54
|
+
onClick={props.onSearchClick}
|
|
55
|
+
pressed={props.searchOpen}
|
|
56
|
+
>
|
|
57
|
+
{props.searchOpen ? (
|
|
58
|
+
<X aria-hidden="true" size={16} strokeWidth={2} />
|
|
59
|
+
) : (
|
|
60
|
+
<Search aria-hidden="true" size={16} strokeWidth={2} />
|
|
61
|
+
)}
|
|
62
|
+
</MenuActionButton>
|
|
63
|
+
) : null}
|
|
57
64
|
<MenuActionButton
|
|
58
65
|
label="Conversation"
|
|
59
66
|
onClick={() => props.onViewChange("rich")}
|
|
@@ -88,10 +95,12 @@ export function ConversationHeaderActions(props: {
|
|
|
88
95
|
|
|
89
96
|
return (
|
|
90
97
|
<div className="flex shrink-0 items-center gap-0.5">
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
{props.view === "rich" ? (
|
|
99
|
+
<TranscriptSearchToggle
|
|
100
|
+
onClick={props.onSearchClick}
|
|
101
|
+
open={props.searchOpen}
|
|
102
|
+
/>
|
|
103
|
+
) : null}
|
|
95
104
|
{props.copyAction}
|
|
96
105
|
<ArchiveConversationButton {...props.archive} layout="bar" />
|
|
97
106
|
<Button
|
|
@@ -115,67 +124,55 @@ export function TranscriptViewToggle(props: {
|
|
|
115
124
|
value: TranscriptViewMode;
|
|
116
125
|
}) {
|
|
117
126
|
return (
|
|
118
|
-
<
|
|
127
|
+
<ToggleGroup
|
|
119
128
|
aria-label="Transcript view"
|
|
120
129
|
className={
|
|
121
130
|
props.labelled
|
|
122
|
-
? "inline-flex
|
|
123
|
-
: "inline-flex
|
|
131
|
+
? "inline-flex gap-6"
|
|
132
|
+
: "inline-flex gap-0.5 rounded-md border border-dashboard-border bg-dashboard-overlay-soft p-0.5"
|
|
124
133
|
}
|
|
125
|
-
|
|
134
|
+
onValueChange={([next]) => {
|
|
135
|
+
if (next) props.onChange(next);
|
|
136
|
+
}}
|
|
137
|
+
value={[props.value]}
|
|
126
138
|
>
|
|
127
|
-
<
|
|
128
|
-
active={props.value === "rich"}
|
|
129
|
-
label="Conversation"
|
|
130
|
-
labelled={props.labelled}
|
|
131
|
-
onClick={() => props.onChange("rich")}
|
|
132
|
-
>
|
|
139
|
+
<ViewModeItem label="Conversation" labelled={props.labelled} value="rich">
|
|
133
140
|
<MessagesSquare aria-hidden="true" size={14} strokeWidth={2} />
|
|
134
|
-
</
|
|
135
|
-
<
|
|
136
|
-
active={props.value === "raw"}
|
|
137
|
-
label="Event log"
|
|
138
|
-
labelled={props.labelled}
|
|
139
|
-
onClick={() => props.onChange("raw")}
|
|
140
|
-
>
|
|
141
|
+
</ViewModeItem>
|
|
142
|
+
<ViewModeItem label="Event log" labelled={props.labelled} value="raw">
|
|
141
143
|
<ScrollText aria-hidden="true" size={14} strokeWidth={2} />
|
|
142
|
-
</
|
|
143
|
-
</
|
|
144
|
+
</ViewModeItem>
|
|
145
|
+
</ToggleGroup>
|
|
144
146
|
);
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
function
|
|
148
|
-
active: boolean;
|
|
149
|
+
function ViewModeItem(props: {
|
|
149
150
|
children: ReactNode;
|
|
150
151
|
label: string;
|
|
151
152
|
labelled?: boolean;
|
|
152
|
-
|
|
153
|
+
value: TranscriptViewMode;
|
|
153
154
|
}) {
|
|
154
|
-
const
|
|
155
|
-
<
|
|
155
|
+
const item = (
|
|
156
|
+
<ToggleGroupItem
|
|
156
157
|
aria-label={props.label}
|
|
157
|
-
className={
|
|
158
|
-
"!no-underline",
|
|
158
|
+
className={
|
|
159
159
|
props.labelled
|
|
160
|
-
? "relative
|
|
161
|
-
: "
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
? "text-dashboard-text after:bg-dashboard-text-solid"
|
|
165
|
-
: "bg-dashboard-fill-hover text-dashboard-text"),
|
|
166
|
-
)}
|
|
167
|
-
onClick={props.onClick}
|
|
168
|
-
pressed={props.active}
|
|
169
|
-
variant="text"
|
|
160
|
+
? "relative h-11 min-w-0 gap-2 rounded-none border-0 px-0 text-sm hover:bg-transparent after:absolute after:inset-x-0 after:bottom-0 after:h-0.5 data-pressed:border-0 data-pressed:bg-transparent data-pressed:after:bg-dashboard-text-solid"
|
|
161
|
+
: "size-7 min-w-0 border-0 px-0 data-pressed:border-0 data-pressed:bg-dashboard-fill-hover"
|
|
162
|
+
}
|
|
163
|
+
value={props.value}
|
|
170
164
|
>
|
|
171
165
|
{props.children}
|
|
172
166
|
{props.labelled ? props.label : null}
|
|
173
|
-
</
|
|
167
|
+
</ToggleGroupItem>
|
|
174
168
|
);
|
|
175
169
|
return props.labelled ? (
|
|
176
|
-
|
|
170
|
+
item
|
|
177
171
|
) : (
|
|
178
|
-
<
|
|
172
|
+
<Tooltip>
|
|
173
|
+
<TooltipTrigger render={item} />
|
|
174
|
+
<TooltipContent size="compact">{props.label}</TooltipContent>
|
|
175
|
+
</Tooltip>
|
|
179
176
|
);
|
|
180
177
|
}
|
|
181
178
|
|
|
@@ -188,21 +185,26 @@ export function HeaderIconButton(props: {
|
|
|
188
185
|
pressed?: boolean;
|
|
189
186
|
}) {
|
|
190
187
|
return (
|
|
191
|
-
<
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
188
|
+
<Tooltip>
|
|
189
|
+
<TooltipTrigger
|
|
190
|
+
render={
|
|
191
|
+
<Button
|
|
192
|
+
ref={props.ref}
|
|
193
|
+
aria-label={props.label}
|
|
194
|
+
aria-pressed={props.pressed}
|
|
195
|
+
className={cn(
|
|
196
|
+
"text-dashboard-text-muted",
|
|
197
|
+
props.pressed && "bg-dashboard-fill-strong text-dashboard-text",
|
|
198
|
+
)}
|
|
199
|
+
onClick={props.onClick}
|
|
200
|
+
size="icon"
|
|
201
|
+
>
|
|
202
|
+
{props.children}
|
|
203
|
+
</Button>
|
|
204
|
+
}
|
|
205
|
+
/>
|
|
206
|
+
<TooltipContent size="compact">{props.label}</TooltipContent>
|
|
207
|
+
</Tooltip>
|
|
206
208
|
);
|
|
207
209
|
}
|
|
208
210
|
|
|
@@ -273,16 +275,22 @@ function ArchiveConversationButton(
|
|
|
273
275
|
);
|
|
274
276
|
}
|
|
275
277
|
return (
|
|
276
|
-
<
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
278
|
+
<Tooltip>
|
|
279
|
+
<TooltipTrigger
|
|
280
|
+
render={
|
|
281
|
+
<Button
|
|
282
|
+
focusableWhenDisabled
|
|
283
|
+
aria-label={label}
|
|
284
|
+
className="hidden shrink-0 text-dashboard-text-muted md:grid"
|
|
285
|
+
disabled={props.disabled}
|
|
286
|
+
onClick={props.onClick}
|
|
287
|
+
size="icon"
|
|
288
|
+
>
|
|
289
|
+
<Icon aria-hidden="true" size={15} strokeWidth={2} />
|
|
290
|
+
</Button>
|
|
291
|
+
}
|
|
292
|
+
/>
|
|
293
|
+
<TooltipContent size="compact">{label}</TooltipContent>
|
|
294
|
+
</Tooltip>
|
|
287
295
|
);
|
|
288
296
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
visualStatusForConversation,
|
|
10
10
|
} from "../format";
|
|
11
11
|
import { EmptyTelemetry } from "../components/EmptyTelemetry";
|
|
12
|
-
import { Skeleton } from "../components/
|
|
12
|
+
import { Skeleton } from "../components/ui/skeleton";
|
|
13
13
|
import {
|
|
14
14
|
conversationParticipants,
|
|
15
15
|
ParticipantAvatarStack,
|