@sentry/junior-dashboard 0.264.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 +145 -136
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +5 -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 +12 -2
- package/src/client/components/CopyButton.tsx +83 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +15 -24
- 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 +86 -77
- 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 +13 -3
- 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/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +147 -62
- 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 +55 -88
- package/src/client/conversations/ConversationPage.tsx +220 -101
- package/src/client/conversations/ConversationSidebar.tsx +48 -29
- 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 +86 -13
- package/src/client/conversations/ShareConversationButton.tsx +25 -13
- 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/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/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 +0 -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/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/components/ui/sheet.LICENSE +0 -21
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -119
- 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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@dashboard/lib/utils";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Collapsible,
|
|
6
|
+
CollapsibleContent,
|
|
7
|
+
CollapsibleTrigger,
|
|
8
|
+
} from "@dashboard/components/ui/collapsible";
|
|
9
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
10
|
+
|
|
11
|
+
function TraceIcon({ className, ...props }: React.ComponentProps<"span">) {
|
|
12
|
+
return (
|
|
13
|
+
<span
|
|
14
|
+
data-slot="trace-icon"
|
|
15
|
+
aria-hidden="true"
|
|
16
|
+
className={cn(
|
|
17
|
+
"flex size-4 shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Keep one trace row and its details in one open state. */
|
|
26
|
+
function TraceCollapsible({
|
|
27
|
+
className,
|
|
28
|
+
...props
|
|
29
|
+
}: Omit<React.ComponentProps<typeof Collapsible>, "open"> &
|
|
30
|
+
(
|
|
31
|
+
| { open?: undefined }
|
|
32
|
+
| {
|
|
33
|
+
open: boolean;
|
|
34
|
+
onOpenChange: NonNullable<
|
|
35
|
+
React.ComponentProps<typeof Collapsible>["onOpenChange"]
|
|
36
|
+
>;
|
|
37
|
+
}
|
|
38
|
+
)) {
|
|
39
|
+
return (
|
|
40
|
+
<Collapsible
|
|
41
|
+
data-slot="trace-collapsible"
|
|
42
|
+
className={cn("flex w-full min-w-0 flex-col", className)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Render the row button with a chevron that turns while open. */
|
|
49
|
+
function TraceCollapsibleTrigger({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<typeof CollapsibleTrigger>) {
|
|
54
|
+
return (
|
|
55
|
+
<CollapsibleTrigger
|
|
56
|
+
data-slot="trace-collapsible-trigger"
|
|
57
|
+
className={cn(
|
|
58
|
+
"group/trace-collapsible-trigger flex w-fit max-w-full min-w-0 cursor-pointer items-center gap-1 rounded-md px-2 py-1.5 text-left text-dashboard-text-muted transition-colors outline-none hover:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus disabled:cursor-default disabled:hover:bg-transparent",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
<TraceIcon className="transition-transform group-aria-expanded/trace-collapsible-trigger:rotate-90 motion-reduce:transition-none">
|
|
65
|
+
<ChevronRightIcon className="size-3 text-dashboard-text-faint" />
|
|
66
|
+
</TraceIcon>
|
|
67
|
+
</CollapsibleTrigger>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Reveal row details in a card under the row title. */
|
|
72
|
+
function TraceCollapsibleContent({
|
|
73
|
+
className,
|
|
74
|
+
children,
|
|
75
|
+
...props
|
|
76
|
+
}: React.ComponentProps<typeof CollapsibleContent>) {
|
|
77
|
+
return (
|
|
78
|
+
<CollapsibleContent
|
|
79
|
+
data-slot="trace-collapsible-content"
|
|
80
|
+
className="h-(--collapsible-panel-height) min-w-0 overflow-hidden transition-[height] duration-200 data-ending-style:h-0 data-starting-style:h-0 motion-reduce:transition-none"
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
<div
|
|
84
|
+
data-slot="trace-collapsible-content-inner"
|
|
85
|
+
className={cn(
|
|
86
|
+
"mt-1.5 ml-6 min-w-0 overflow-hidden rounded-md border border-dashboard-border-subtle bg-dashboard-surface-panel",
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
{children}
|
|
91
|
+
</div>
|
|
92
|
+
</CollapsibleContent>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { TraceCollapsible, TraceCollapsibleTrigger, TraceCollapsibleContent };
|
|
@@ -5,12 +5,14 @@ import { cn } from "../styles";
|
|
|
5
5
|
/**
|
|
6
6
|
* Own the conversation chat frame: one scroll region above one dock.
|
|
7
7
|
*
|
|
8
|
-
* Pages pass `scroll
|
|
8
|
+
* Pages pass `header`, `scroll`, and `dock`. Do not invent footer padding, safe-area,
|
|
9
9
|
* or shell height math outside this component and `ComposerDock`.
|
|
10
10
|
*/
|
|
11
11
|
export function ChatLayout(props: {
|
|
12
12
|
className?: string;
|
|
13
13
|
dock?: ReactNode;
|
|
14
|
+
/** Keep conversation chrome outside the scrolling content. */
|
|
15
|
+
header?: ReactNode;
|
|
14
16
|
/** When true, keep a taller minimum scroll region for long transcripts. */
|
|
15
17
|
scrollMinTall?: boolean;
|
|
16
18
|
scroll: ReactNode;
|
|
@@ -20,11 +22,13 @@ export function ChatLayout(props: {
|
|
|
20
22
|
return (
|
|
21
23
|
<div
|
|
22
24
|
className={cn(
|
|
23
|
-
"grid h-full min-h-0 min-w-0 grid-rows-[
|
|
24
|
-
props.scrollMinTall &&
|
|
25
|
+
"grid h-full min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)_minmax(0,auto)]",
|
|
26
|
+
props.scrollMinTall &&
|
|
27
|
+
"grid-rows-[auto_minmax(7rem,1fr)_minmax(0,auto)]",
|
|
25
28
|
props.className,
|
|
26
29
|
)}
|
|
27
30
|
>
|
|
31
|
+
<div className="min-w-0">{props.header}</div>
|
|
28
32
|
<div
|
|
29
33
|
aria-label={props.scrollAriaLabel}
|
|
30
34
|
className={cn(
|
|
@@ -4,7 +4,13 @@ import type { ConversationDetailReport } from "@sentry/junior/api/schema";
|
|
|
4
4
|
|
|
5
5
|
import { Detail, DetailList } from "../components/DetailList";
|
|
6
6
|
import { StatusChip, type StatusChipTone } from "../components/StatusChip";
|
|
7
|
+
import {
|
|
8
|
+
Collapsible,
|
|
9
|
+
CollapsibleContent,
|
|
10
|
+
CollapsibleTrigger,
|
|
11
|
+
} from "../components/ui/collapsible";
|
|
7
12
|
import { formatTime } from "../format";
|
|
13
|
+
import { cn, dashboardDisclosureChevronClass } from "../styles";
|
|
8
14
|
import { MentionPeopleProvider, renderMentionText } from "./transcriptMentions";
|
|
9
15
|
|
|
10
16
|
type ConversationBriefReport = NonNullable<ConversationDetailReport["brief"]>;
|
|
@@ -160,16 +166,21 @@ function BriefContentView(props: {
|
|
|
160
166
|
|
|
161
167
|
function BriefSection(props: { children: ReactNode; title: string }) {
|
|
162
168
|
return (
|
|
163
|
-
<
|
|
164
|
-
<
|
|
169
|
+
<Collapsible className="min-w-0 border-t border-dashboard-border pt-3">
|
|
170
|
+
<CollapsibleTrigger className="group flex w-full cursor-pointer items-center gap-2 text-left text-sm font-medium text-dashboard-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus">
|
|
165
171
|
<ChevronRight
|
|
166
172
|
aria-hidden="true"
|
|
167
|
-
className=
|
|
173
|
+
className={cn(
|
|
174
|
+
dashboardDisclosureChevronClass,
|
|
175
|
+
"text-dashboard-text-muted",
|
|
176
|
+
)}
|
|
168
177
|
/>
|
|
169
178
|
{props.title}
|
|
170
|
-
</
|
|
171
|
-
<
|
|
172
|
-
|
|
179
|
+
</CollapsibleTrigger>
|
|
180
|
+
<CollapsibleContent hiddenUntilFound>
|
|
181
|
+
<div className="mt-3">{props.children}</div>
|
|
182
|
+
</CollapsibleContent>
|
|
183
|
+
</Collapsible>
|
|
173
184
|
);
|
|
174
185
|
}
|
|
175
186
|
|
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
} from "react";
|
|
13
13
|
import { ImagePlus, Send, Square } from "lucide-react";
|
|
14
14
|
|
|
15
|
-
import { Button } from "../components/
|
|
15
|
+
import { Button } from "../components/ui/button";
|
|
16
|
+
import {
|
|
17
|
+
Tooltip,
|
|
18
|
+
TooltipContent,
|
|
19
|
+
TooltipTrigger,
|
|
20
|
+
} from "../components/ui/tooltip";
|
|
16
21
|
import { useDashboardOnline } from "../connection";
|
|
17
22
|
import { cn, dashboardComposerSurfaceClass } from "../styles";
|
|
18
23
|
|
|
@@ -447,16 +452,25 @@ export const ConversationComposer = memo(function ConversationComposer(
|
|
|
447
452
|
)}
|
|
448
453
|
>
|
|
449
454
|
<div className="flex min-w-0 items-center gap-3">
|
|
450
|
-
<
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
455
|
+
<Tooltip>
|
|
456
|
+
<TooltipTrigger
|
|
457
|
+
render={
|
|
458
|
+
<Button
|
|
459
|
+
focusableWhenDisabled
|
|
460
|
+
aria-label="Attach images"
|
|
461
|
+
disabled={sendLocked || readingImages}
|
|
462
|
+
onClick={() => fileInputRef.current?.click()}
|
|
463
|
+
size="icon"
|
|
464
|
+
type="button"
|
|
465
|
+
>
|
|
466
|
+
<ImagePlus aria-hidden="true" size={16} />
|
|
467
|
+
</Button>
|
|
468
|
+
}
|
|
469
|
+
/>
|
|
470
|
+
<TooltipContent size="compact">
|
|
471
|
+
Attach images (up to 3 MB total)
|
|
472
|
+
</TooltipContent>
|
|
473
|
+
</Tooltip>
|
|
460
474
|
{props.footerStart}
|
|
461
475
|
{props.footerStart ? null : (
|
|
462
476
|
<div className="hidden min-w-0 font-sans text-xs leading-relaxed text-dashboard-text-muted md:block">
|
|
@@ -488,7 +502,7 @@ export const ConversationComposer = memo(function ConversationComposer(
|
|
|
488
502
|
readingImages
|
|
489
503
|
}
|
|
490
504
|
title={sendLocked ? "Sending message" : props.submitLabel}
|
|
491
|
-
|
|
505
|
+
variant="primary"
|
|
492
506
|
type="submit"
|
|
493
507
|
>
|
|
494
508
|
<Send aria-hidden="true" size={14} />
|
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
import { useState, type ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { DetailsPanel, useDetailsPanel } from "../components/DetailsPanel";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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";
|
|
9
15
|
import {
|
|
10
16
|
ConversationChildAgents,
|
|
11
17
|
useConversationChildAgents,
|
|
12
18
|
} from "./ConversationChildAgents";
|
|
19
|
+
import {
|
|
20
|
+
ConversationLinkedWork,
|
|
21
|
+
type ConversationLink,
|
|
22
|
+
} from "./ConversationLinkedWork";
|
|
13
23
|
import {
|
|
14
24
|
ConversationMemories,
|
|
15
25
|
useConversationMemories,
|
|
16
26
|
} from "./ConversationMemories";
|
|
27
|
+
import {
|
|
28
|
+
ConversationWatches,
|
|
29
|
+
useConversationWatches,
|
|
30
|
+
} from "./ConversationWatches";
|
|
17
31
|
import { ShareConversationButton } from "./ShareConversationButton";
|
|
18
32
|
import type { SubagentTranscriptTarget } from "./SubagentTranscriptDrawer";
|
|
19
33
|
|
|
20
34
|
type DetailsTab = "agents" | "summary" | "memories";
|
|
21
35
|
|
|
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
|
+
|
|
22
40
|
/**
|
|
23
|
-
* Show conversation details: the brief and facts first,
|
|
24
|
-
*
|
|
25
|
-
* header so every tab can reach them.
|
|
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.
|
|
26
44
|
*/
|
|
27
45
|
export function ConversationDetails(props: {
|
|
28
|
-
annotations: ReactNode;
|
|
29
46
|
brief: ReactNode;
|
|
30
47
|
conversationId: string;
|
|
31
48
|
identity: ReactNode;
|
|
49
|
+
links: ConversationLink[];
|
|
32
50
|
onClose(): void;
|
|
33
51
|
/** Open one child agent transcript. A details sheet closes first. */
|
|
34
52
|
onOpenSubagent(target: SubagentTranscriptTarget): void;
|
|
@@ -37,29 +55,47 @@ export function ConversationDetails(props: {
|
|
|
37
55
|
usage: ReactNode;
|
|
38
56
|
}) {
|
|
39
57
|
const [tab, setTab] = useState<DetailsTab>("summary");
|
|
40
|
-
const panel =
|
|
58
|
+
const panel = useDetailsPanel();
|
|
41
59
|
// Load both lists with details so each tab can show its count.
|
|
42
60
|
const memories = useConversationMemories(props.conversationId);
|
|
43
61
|
const agents = useConversationChildAgents(props.conversationId);
|
|
44
|
-
const
|
|
45
|
-
{ label: "Summary", value: "summary" },
|
|
46
|
-
{
|
|
47
|
-
count: memories.data?.memories.length,
|
|
48
|
-
label: "Memories",
|
|
49
|
-
value: "memories",
|
|
50
|
-
},
|
|
51
|
-
{ count: agents.data?.agents.length, label: "Subagents", value: "agents" },
|
|
52
|
-
] as const;
|
|
62
|
+
const watches = useConversationWatches(props.conversationId).data?.watches;
|
|
53
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.
|
|
54
66
|
const sections = [
|
|
55
|
-
{ content: props.overview, key: "overview", title: undefined },
|
|
56
67
|
{ content: props.identity, key: "participants", title: "Participants" },
|
|
57
|
-
{ content: props.
|
|
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
|
+
},
|
|
58
85
|
{ content: props.usage, key: "usage", title: "Usage" },
|
|
59
86
|
].filter((section) => section.content != null);
|
|
60
87
|
|
|
61
88
|
return (
|
|
62
|
-
<
|
|
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
|
+
}
|
|
63
99
|
actions={
|
|
64
100
|
<>
|
|
65
101
|
<ShareConversationButton
|
|
@@ -68,32 +104,79 @@ export function ConversationDetails(props: {
|
|
|
68
104
|
layout="icon"
|
|
69
105
|
/>
|
|
70
106
|
{props.sentryConversationUrl ? (
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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>
|
|
82
125
|
) : null}
|
|
83
126
|
</>
|
|
84
127
|
}
|
|
85
128
|
onClose={props.onClose}
|
|
86
129
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
label="Conversation details"
|
|
91
|
-
onChange={setTab}
|
|
130
|
+
<Tabs
|
|
131
|
+
className="h-full min-h-0 grid-rows-[auto_minmax(0,1fr)] gap-0"
|
|
132
|
+
onValueChange={(value: DetailsTab) => setTab(value)}
|
|
92
133
|
value={tab}
|
|
93
134
|
>
|
|
94
|
-
|
|
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">
|
|
95
177
|
<ConversationMemories conversationId={props.conversationId} />
|
|
96
|
-
|
|
178
|
+
</TabsContent>
|
|
179
|
+
<TabsContent className={detailsPanelClass} value="agents">
|
|
97
180
|
<ConversationChildAgents
|
|
98
181
|
conversationId={props.conversationId}
|
|
99
182
|
onOpenAgent={(target) => {
|
|
@@ -102,36 +185,38 @@ export function ConversationDetails(props: {
|
|
|
102
185
|
props.onOpenSubagent(target);
|
|
103
186
|
}}
|
|
104
187
|
/>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
) : (
|
|
119
|
-
<p className="m-0 font-sans text-sm leading-relaxed text-dashboard-text-muted">
|
|
120
|
-
No additional conversation details.
|
|
121
|
-
</p>
|
|
122
|
-
)}
|
|
123
|
-
</SegmentedTabs>
|
|
124
|
-
</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>
|
|
125
201
|
);
|
|
126
202
|
}
|
|
127
203
|
|
|
128
204
|
/** Group one kind of conversation detail in a compact card. */
|
|
129
|
-
function DetailsSection(props: {
|
|
205
|
+
function DetailsSection(props: {
|
|
206
|
+
children: ReactNode;
|
|
207
|
+
count?: number;
|
|
208
|
+
title?: string;
|
|
209
|
+
}) {
|
|
130
210
|
return (
|
|
131
211
|
<section className="grid min-w-0 gap-2.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel p-3">
|
|
132
212
|
{props.title ? (
|
|
133
|
-
<h3 className="m-0 font-sans text-xs font-medium text-dashboard-text-muted">
|
|
213
|
+
<h3 className="m-0 flex items-center gap-1.5 font-sans text-xs font-medium text-dashboard-text-muted">
|
|
134
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}
|
|
135
220
|
</h3>
|
|
136
221
|
) : null}
|
|
137
222
|
<div className="min-w-0 break-words font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
@@ -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
|
+
}
|