@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.
Files changed (191) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +2 -5
  3. package/dist/app.js +119 -35
  4. package/dist/client.js +145 -136
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.js +119 -35
  7. package/dist/tailwind.css +1 -1
  8. package/package.json +5 -4
  9. package/src/api/schema.ts +0 -1
  10. package/src/app.ts +5 -20
  11. package/src/client/App.tsx +29 -31
  12. package/src/client/api.ts +7 -0
  13. package/src/client/components/BottomSheet.tsx +12 -2
  14. package/src/client/components/CopyButton.tsx +83 -0
  15. package/src/client/components/DetailsPanel.tsx +133 -0
  16. package/src/client/components/Drawer.tsx +15 -24
  17. package/src/client/components/FilterBar.tsx +16 -31
  18. package/src/client/components/Metric.tsx +33 -23
  19. package/src/client/components/PageContentSkeleton.tsx +1 -1
  20. package/src/client/components/Pagination.tsx +1 -1
  21. package/src/client/components/ParticipantAvatarStack.tsx +40 -36
  22. package/src/client/components/ProfileMenu.tsx +27 -93
  23. package/src/client/components/README.md +86 -77
  24. package/src/client/components/SegmentedNav.tsx +1 -1
  25. package/src/client/components/ShimmerText.tsx +2 -6
  26. package/src/client/components/ThemeSwitcher.tsx +19 -15
  27. package/src/client/components/charts/ActivityChart.tsx +173 -288
  28. package/src/client/components/charts/CacheWrites.tsx +63 -50
  29. package/src/client/components/charts/InputCacheChart.tsx +137 -131
  30. package/src/client/components/charts/README.md +4 -4
  31. package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
  32. package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
  33. package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
  34. package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
  35. package/src/client/components/layout/DashboardHeader.tsx +44 -90
  36. package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
  37. package/src/client/components/layout/VisualViewportShell.tsx +2 -2
  38. package/src/client/components/ui/accordion.tsx +77 -0
  39. package/src/client/components/ui/alert-dialog.tsx +89 -0
  40. package/src/client/components/ui/button.tsx +85 -0
  41. package/src/client/components/ui/chart.tsx +74 -0
  42. package/src/client/components/ui/checkbox.tsx +26 -0
  43. package/src/client/components/ui/collapsible.tsx +22 -0
  44. package/src/client/components/ui/field.tsx +49 -0
  45. package/src/client/components/ui/input.tsx +35 -0
  46. package/src/client/components/ui/native-select.tsx +57 -0
  47. package/src/client/components/ui/popover.tsx +67 -0
  48. package/src/client/components/ui/radio-group.tsx +32 -0
  49. package/src/client/components/ui/sheet.tsx +13 -3
  50. package/src/client/components/ui/skeleton.tsx +19 -0
  51. package/src/client/components/ui/table.tsx +80 -0
  52. package/src/client/components/ui/tabs.tsx +75 -0
  53. package/src/client/components/ui/textarea.tsx +17 -0
  54. package/src/client/components/ui/toast.tsx +215 -0
  55. package/src/client/components/ui/toggle-group.tsx +36 -0
  56. package/src/client/components/ui/toggle.tsx +37 -0
  57. package/src/client/components/ui/tooltip.tsx +168 -0
  58. package/src/client/components/ui/trace.tsx +96 -0
  59. package/src/client/conversations/ChatLayout.tsx +7 -3
  60. package/src/client/conversations/ConversationBrief.tsx +17 -6
  61. package/src/client/conversations/ConversationComposer.tsx +26 -12
  62. package/src/client/conversations/ConversationDetails.tsx +147 -62
  63. package/src/client/conversations/ConversationEventDetails.tsx +141 -0
  64. package/src/client/conversations/ConversationEventLog.tsx +150 -129
  65. package/src/client/conversations/ConversationHeader.tsx +8 -3
  66. package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
  67. package/src/client/conversations/ConversationHomeList.tsx +1 -1
  68. package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
  69. package/src/client/conversations/ConversationMeta.tsx +55 -88
  70. package/src/client/conversations/ConversationPage.tsx +220 -101
  71. package/src/client/conversations/ConversationSidebar.tsx +48 -29
  72. package/src/client/conversations/ConversationWatches.tsx +165 -0
  73. package/src/client/conversations/ConversationWorkspace.tsx +12 -22
  74. package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
  75. package/src/client/conversations/DetailsList.tsx +124 -0
  76. package/src/client/conversations/EventDetails.tsx +42 -8
  77. package/src/client/conversations/ForkConversationButton.tsx +27 -16
  78. package/src/client/conversations/ObjectCard.tsx +20 -8
  79. package/src/client/conversations/PendingMailboxStack.tsx +52 -33
  80. package/src/client/conversations/README.md +86 -13
  81. package/src/client/conversations/ShareConversationButton.tsx +25 -13
  82. package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
  83. package/src/client/conversations/ToolFrame.tsx +36 -52
  84. package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
  85. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
  86. package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
  87. package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
  88. package/src/client/conversations/TranscriptLoading.tsx +1 -1
  89. package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
  90. package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
  91. package/src/client/conversations/TranscriptMessageView.tsx +6 -1
  92. package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
  93. package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
  94. package/src/client/conversations/TranscriptToolView.tsx +1 -1
  95. package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
  96. package/src/client/conversations/TranscriptView.tsx +63 -27
  97. package/src/client/conversations/eventLog.ts +57 -0
  98. package/src/client/conversations/eventTime.ts +60 -0
  99. package/src/client/conversations/queries.ts +29 -0
  100. package/src/client/conversations/transcriptBottomPinning.ts +34 -0
  101. package/src/client/format.ts +5 -0
  102. package/src/client/pages/ConnectionsPage.tsx +2 -2
  103. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  104. package/src/client/pages/SettingsPage.tsx +9 -10
  105. package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
  106. package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
  107. package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
  108. package/src/client/pages/automations/AutomationDetails.tsx +193 -0
  109. package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
  110. package/src/client/pages/automations/AutomationEditor.tsx +41 -29
  111. package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
  112. package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
  113. package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
  114. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
  115. package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
  116. package/src/client/pages/automations/AutomationFilters.tsx +189 -74
  117. package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
  118. package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
  119. package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
  120. package/src/client/pages/automations/AutomationRow.tsx +12 -138
  121. package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
  122. package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
  123. package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
  124. package/src/client/pages/automations/AutomationStatus.tsx +201 -0
  125. package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
  126. package/src/client/pages/automations/AutomationsPage.tsx +80 -68
  127. package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
  128. package/src/client/pages/automations/README.md +57 -0
  129. package/src/client/pages/automations/TriggerFilters.tsx +22 -0
  130. package/src/client/pages/automations/automationFormat.ts +37 -23
  131. package/src/client/pages/code/CodeActivityChart.tsx +59 -101
  132. package/src/client/pages/code/CodePage.tsx +52 -55
  133. package/src/client/pages/code/RepositoryPage.tsx +6 -8
  134. package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
  135. package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
  136. package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
  137. package/src/client/pages/memory/MemoryDetails.tsx +218 -0
  138. package/src/client/pages/memory/MemoryPage.tsx +168 -112
  139. package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
  140. package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
  141. package/src/client/pages/memory/memoryRecord.ts +1 -1
  142. package/src/client/pages/people/ContributionGrid.tsx +23 -16
  143. package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
  144. package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
  145. package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
  146. package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
  147. package/src/client/pages/system/GuardianActivity.tsx +1 -2
  148. package/src/client/pages/system/PluginBarChart.tsx +106 -87
  149. package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
  150. package/src/client/pages/system/README.md +23 -0
  151. package/src/client/pages/system/SystemNavigation.tsx +5 -1
  152. package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
  153. package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
  154. package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
  155. package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
  156. package/src/client/pages/system/WorkspaceList.tsx +20 -30
  157. package/src/client/pages/system/WorkspacesPage.tsx +117 -119
  158. package/src/client/pages/system/sandboxSize.ts +8 -0
  159. package/src/client/pages/system/workspaceQueries.ts +53 -0
  160. package/src/client/pages/user/PluginUserPage.tsx +51 -2
  161. package/src/client/pages/user/pluginUserPageData.ts +1 -6
  162. package/src/client/styles.ts +4 -0
  163. package/src/client.tsx +7 -1
  164. package/src/mock-reporting/automation-collection.ts +4 -2
  165. package/src/mock-reporting/fixtures.ts +23 -7
  166. package/src/mock-reporting/routes.ts +12 -0
  167. package/src/mock-reporting/watches.ts +94 -0
  168. package/src/shell.ts +4 -9
  169. package/src/tailwind.css +0 -10
  170. package/src/client/components/Button.tsx +0 -118
  171. package/src/client/components/Field.tsx +0 -53
  172. package/src/client/components/SegmentedTabs.tsx +0 -119
  173. package/src/client/components/Select.tsx +0 -52
  174. package/src/client/components/Skeleton.tsx +0 -17
  175. package/src/client/components/TextInput.tsx +0 -58
  176. package/src/client/components/Tooltip.tsx +0 -216
  177. package/src/client/components/charts/useChartLayout.ts +0 -27
  178. package/src/client/components/ui/sheet.LICENSE +0 -21
  179. package/src/client/conversations/ConversationDetailsFrame.tsx +0 -119
  180. package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
  181. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
  182. package/src/client/pages/dev/ChartFixtures.tsx +0 -325
  183. package/src/client/pages/dev/ComponentsPage.tsx +0 -899
  184. package/src/client/pages/dev/ConversationFixture.tsx +0 -231
  185. package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
  186. package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
  187. package/src/client/pages/dev/SelectFixtures.tsx +0 -62
  188. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
  189. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
  190. package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
  191. package/src/client/pages/system/SnapshotSummary.tsx +0 -78
@@ -1,10 +1,9 @@
1
+ import { Bar, BarChart, CartesianGrid } from "recharts";
1
2
  import {
2
- ActivityChartDateLabels,
3
- ActivityChartGrid,
4
- ActivityChartTooltip,
5
- ActivityTooltipRows,
6
- ChartSvg,
7
- createActivityChartLayout,
3
+ activityChartMargin,
4
+ ActivityChartTooltipContent,
5
+ ActivityChartXAxis,
6
+ ActivityChartYAxis,
8
7
  } from "../../components/charts/ActivityChart";
9
8
  import { ChartLegend } from "../../components/charts/ChartLegend";
10
9
  import {
@@ -12,6 +11,7 @@ import {
12
11
  type TimeRangeDays,
13
12
  } from "../../components/controls/TimeRangeSelector";
14
13
  import { Card } from "../../components/layout/Card";
14
+ import { ChartContainer, ChartTooltip } from "../../components/ui/chart";
15
15
  import type { MemoryDay } from "./memoryDashboard";
16
16
 
17
17
  const series = [
@@ -19,6 +19,10 @@ const series = [
19
19
  { color: "var(--color-emerald-300)", key: "public", label: "Public" },
20
20
  ] as const;
21
21
 
22
+ const chartConfig = Object.fromEntries(
23
+ series.map((item) => [item.key, { color: item.color, label: item.label }]),
24
+ );
25
+
22
26
  /** Render viewer memory creation as a stacked personal/public timeline. */
23
27
  export function MemoryTimeline(props: {
24
28
  bucketUnit?: TimeRangeBucketUnit;
@@ -29,10 +33,6 @@ export function MemoryTimeline(props: {
29
33
  const bucketUnit = props.bucketUnit ?? "day";
30
34
 
31
35
  const days = props.days;
32
- const layout = createActivityChartLayout(200);
33
- const step =
34
- days.length > 0 ? layout.plotWidth / days.length : layout.plotWidth;
35
- const barWidth = Math.max(2, Math.min(13, step * 0.68));
36
36
  const totals = days.map((day) => day.personal + day.public);
37
37
  const maximum = Math.max(1, ...totals);
38
38
  const hasMemories = totals.some((total) => total > 0);
@@ -55,66 +55,43 @@ export function MemoryTimeline(props: {
55
55
  <ChartLegend ariaLabel="Memory visibility legend" items={series} />
56
56
 
57
57
  <div className="relative mt-3 overflow-hidden">
58
- <ChartSvg
59
- aria-label={`Memories learned during the last ${props.range === 1 ? "24 hours" : `${props.range} days`}`}
60
- className="min-h-40"
61
- layout={layout}
58
+ <ChartContainer
59
+ className="aspect-auto h-50 w-full"
60
+ config={chartConfig}
62
61
  >
63
- <ActivityChartGrid layout={layout} maximum={maximum} />
64
- {days.map((day, dayIndex) => {
65
- let stackedHeight = 0;
66
- const x = layout.left + dayIndex * step + (step - barWidth) / 2;
67
- const total = totals[dayIndex] ?? 0;
68
- return (
69
- <ActivityChartTooltip
70
- key={day.date}
71
- content={
72
- <ActivityTooltipRows
73
- rows={[
74
- ["personal", day.personal],
75
- ["public", day.public],
76
- ["total", total],
77
- ]}
78
- />
79
- }
80
- date={day.date}
81
- summary={`${day.personal} personal, ${day.public} public, ${total} total memories`}
82
- >
83
- <g tabIndex={0}>
84
- {series.map((item) => {
85
- const value = day[item.key];
86
- const segmentHeight = (value / maximum) * layout.plotHeight;
87
- stackedHeight += segmentHeight;
88
- return (
89
- <rect
90
- fill={item.color}
91
- height={segmentHeight}
92
- key={item.key}
93
- opacity={0.82}
94
- rx="1"
95
- width={barWidth}
96
- x={x}
97
- y={layout.top + layout.plotHeight - stackedHeight}
98
- />
99
- );
100
- })}
101
- <rect
102
- fill="transparent"
103
- height={layout.plotHeight}
104
- width={Math.max(barWidth, 8)}
105
- x={x - (Math.max(barWidth, 8) - barWidth) / 2}
106
- y={layout.top}
107
- />
108
- </g>
109
- </ActivityChartTooltip>
110
- );
111
- })}
112
- <ActivityChartDateLabels
113
- dates={days.map((day) => day.date)}
114
- layout={layout}
115
- xPosition={(index) => layout.left + index * step + step / 2}
116
- />
117
- </ChartSvg>
62
+ <BarChart
63
+ aria-label={`Memories learned during the last ${props.range === 1 ? "24 hours" : `${props.range} days`}`}
64
+ data={days}
65
+ margin={activityChartMargin}
66
+ role="img"
67
+ >
68
+ <CartesianGrid strokeDasharray="3 5" vertical={false} />
69
+ <ActivityChartYAxis maximum={maximum} />
70
+ <ActivityChartXAxis keys={days.map((day) => day.date)} />
71
+ <ChartTooltip
72
+ content={
73
+ <ActivityChartTooltipContent
74
+ rows={(day: MemoryDay) => [
75
+ ["personal", day.personal],
76
+ ["public", day.public],
77
+ ["total", day.personal + day.public],
78
+ ]}
79
+ />
80
+ }
81
+ />
82
+ {series.map((item) => (
83
+ <Bar
84
+ dataKey={item.key}
85
+ fill={`var(--color-${item.key})`}
86
+ fillOpacity={0.82}
87
+ isAnimationActive={false}
88
+ key={item.key}
89
+ maxBarSize={13}
90
+ stackId="visibility"
91
+ />
92
+ ))}
93
+ </BarChart>
94
+ </ChartContainer>
118
95
  {!hasMemories ? (
119
96
  <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
120
97
  No memories were learned in this period.
@@ -32,7 +32,7 @@ function formattedDate(value: string): string {
32
32
  }).format(new Date(value));
33
33
  }
34
34
 
35
- /** Project one viewer-visible memory API record into the dashboard drawer model. */
35
+ /** Project one viewer-visible memory API record into the dashboard details model. */
36
36
  export function memoryPageRecord(memory: MemoryRecord): PluginUserPageRecord {
37
37
  const learned =
38
38
  memory.origin === "automatic"
@@ -1,6 +1,11 @@
1
1
  import { ActivityTooltipRows } from "../../components/charts/ActivityChart";
2
2
  import { formatDuration } from "../../components/Duration";
3
- import { Tooltip } from "../../components/Tooltip";
3
+ import {
4
+ Tooltip,
5
+ TooltipContent,
6
+ TooltipTitle,
7
+ TooltipTrigger,
8
+ } from "../../components/ui/tooltip";
4
9
  import { getDashboardAgentName } from "../../agentName";
5
10
  import { formatCompactNumber, formatCostSummary } from "../../format";
6
11
  import { cn } from "../../styles";
@@ -79,8 +84,22 @@ export function ContributionGrid(props: { days: ContributionDay[] }) {
79
84
  {weeks.flatMap((week, weekIndex) =>
80
85
  week.map((day, dayIndex) =>
81
86
  day ? (
82
- <Tooltip
83
- content={
87
+ <Tooltip key={day.date}>
88
+ <TooltipTrigger
89
+ render={
90
+ <span
91
+ aria-label={`${day.date}: ${day.conversations} conversations, ${activityRuntime(day)}, ${formatCostSummary({ total: day.costUsd ?? 0 })} spend, ${formatCompactNumber(day.tokens ?? 0)} tokens`}
92
+ className={cn(
93
+ "size-3 border border-dashboard-overlay outline-none focus-visible:ring-2 focus-visible:ring-dashboard-focus",
94
+ activityClass(day.conversations, max),
95
+ )}
96
+ role="listitem"
97
+ tabIndex={0}
98
+ />
99
+ }
100
+ />
101
+ <TooltipContent>
102
+ <TooltipTitle>{formatDate(day.date)}</TooltipTitle>
84
103
  <ActivityTooltipRows
85
104
  rows={[
86
105
  ["conversations", day.conversations],
@@ -92,19 +111,7 @@ export function ContributionGrid(props: { days: ContributionDay[] }) {
92
111
  ["tokens", formatCompactNumber(day.tokens ?? 0)],
93
112
  ]}
94
113
  />
95
- }
96
- key={day.date}
97
- label={formatDate(day.date)}
98
- >
99
- <span
100
- aria-label={`${day.date}: ${day.conversations} conversations, ${activityRuntime(day)}, ${formatCostSummary({ total: day.costUsd ?? 0 })} spend, ${formatCompactNumber(day.tokens ?? 0)} tokens`}
101
- className={cn(
102
- "size-3 border border-dashboard-overlay outline-none focus-visible:ring-2 focus-visible:ring-dashboard-focus",
103
- activityClass(day.conversations, max),
104
- )}
105
- role="listitem"
106
- tabIndex={0}
107
- />
114
+ </TooltipContent>
108
115
  </Tooltip>
109
116
  ) : (
110
117
  <span
@@ -5,38 +5,21 @@ import {
5
5
  } from "../../components/controls/TimeRangeSelector";
6
6
  import type { PeopleActivityDayReport } from "@sentry/junior/api/schema";
7
7
 
8
+ import { Area, AreaChart, CartesianGrid } from "recharts";
8
9
  import {
9
10
  ActivityChartAverageLine,
10
- ActivityChartDateLabels,
11
- ActivityChartGrid,
12
11
  activityChartAverage,
13
- ActivityChartTooltip,
14
- ActivityTooltipRows,
15
- ChartSvg,
16
- createActivityChartLayout,
17
- type ActivityChartLayout,
12
+ activityChartMargin,
13
+ ActivityChartTooltipContent,
14
+ ActivityChartXAxis,
15
+ ActivityChartYAxis,
18
16
  } from "../../components/charts/ActivityChart";
19
17
  import { ChartLegend } from "../../components/charts/ChartLegend";
20
18
  import { Card } from "../../components/layout/Card";
21
19
  import { CardHeader } from "../../components/layout/CardHeader";
20
+ import { ChartContainer, ChartTooltip } from "../../components/ui/chart";
22
21
  import { formatActivityChartAverage } from "../../format";
23
22
 
24
- function chartPoint(
25
- day: PeopleActivityDayReport,
26
- index: number,
27
- count: number,
28
- maximum: number,
29
- layout: ActivityChartLayout,
30
- ) {
31
- return {
32
- x: layout.left + (index / Math.max(1, count - 1)) * layout.plotWidth,
33
- y:
34
- layout.top +
35
- layout.plotHeight -
36
- (day.activePeople / maximum) * layout.plotHeight,
37
- };
38
- }
39
-
40
23
  /** Plot distinct verified people with recorded conversation activity each day. */
41
24
  export function PeopleActivityChart(props: {
42
25
  bucketUnit?: TimeRangeBucketUnit;
@@ -46,20 +29,10 @@ export function PeopleActivityChart(props: {
46
29
  const bucketUnit = props.bucketUnit ?? "day";
47
30
  const chartTitle = `Active people per ${timeRangeBucketPerLabel(bucketUnit)}`;
48
31
 
49
- const layout = createActivityChartLayout(260);
50
32
  const values = props.days.map((day) => day.activePeople);
51
33
  const maximum = Math.max(1, ...values);
52
34
  const average = activityChartAverage(values);
53
- const points = props.days.map((day, index) =>
54
- chartPoint(day, index, props.days.length, maximum, layout),
55
- );
56
- const baseline = layout.top + layout.plotHeight;
57
- const line = points.map((point) => `${point.x},${point.y}`).join(" ");
58
- const area = points.length
59
- ? `M ${points[0]!.x} ${baseline} L ${points
60
- .map((point) => `${point.x} ${point.y}`)
61
- .join(" L ")} L ${points.at(-1)!.x} ${baseline} Z`
62
- : "";
35
+ const dense = props.days.length > 30;
63
36
  return (
64
37
  <Card>
65
38
  <CardHeader
@@ -80,83 +53,71 @@ export function PeopleActivityChart(props: {
80
53
  }
81
54
  />
82
55
  <div className="px-2 py-3 sm:px-4 sm:py-4">
83
- <ChartSvg
84
- aria-label={chartTitle}
85
- className="min-h-56 w-full overflow-visible"
86
- layout={layout}
87
- >
88
- <defs>
89
- <linearGradient id="people-area" x1="0" x2="0" y1="0" y2="1">
90
- <stop
91
- offset="0%"
92
- stopColor="var(--color-amber-500)"
93
- stopOpacity="0.3"
94
- />
95
- <stop
96
- offset="100%"
97
- stopColor="var(--color-amber-500)"
98
- stopOpacity="0"
99
- />
100
- </linearGradient>
101
- <linearGradient id="people-line" x1="0" x2="1" y1="0" y2="0">
102
- <stop offset="0%" stopColor="var(--color-amber-500)" />
103
- <stop offset="55%" stopColor="var(--color-amber-400)" />
104
- <stop offset="100%" stopColor="var(--color-cyan-400)" />
105
- </linearGradient>
106
- </defs>
107
- <ActivityChartGrid layout={layout} maximum={maximum} />
108
- {area ? <path d={area} fill="url(#people-area)" /> : null}
109
- {line ? (
110
- <polyline
111
- fill="none"
112
- points={line}
56
+ <ChartContainer className="aspect-auto h-65 w-full" config={{}}>
57
+ <AreaChart
58
+ aria-label={chartTitle}
59
+ data={props.days}
60
+ margin={activityChartMargin}
61
+ role="img"
62
+ >
63
+ <defs>
64
+ <linearGradient id="people-area" x1="0" x2="0" y1="0" y2="1">
65
+ <stop
66
+ offset="0%"
67
+ stopColor="var(--color-amber-500)"
68
+ stopOpacity="0.3"
69
+ />
70
+ <stop
71
+ offset="100%"
72
+ stopColor="var(--color-amber-500)"
73
+ stopOpacity="0"
74
+ />
75
+ </linearGradient>
76
+ <linearGradient id="people-line" x1="0" x2="1" y1="0" y2="0">
77
+ <stop offset="0%" stopColor="var(--color-amber-500)" />
78
+ <stop offset="55%" stopColor="var(--color-amber-400)" />
79
+ <stop offset="100%" stopColor="var(--color-cyan-400)" />
80
+ </linearGradient>
81
+ </defs>
82
+ <CartesianGrid strokeDasharray="3 5" vertical={false} />
83
+ <ActivityChartYAxis maximum={maximum} />
84
+ <ActivityChartXAxis keys={props.days.map((day) => day.date)} />
85
+ <ChartTooltip
86
+ content={
87
+ <ActivityChartTooltipContent
88
+ rows={(day: PeopleActivityDayReport) => [
89
+ ["active people", day.activePeople],
90
+ ["conversations", day.conversations],
91
+ ]}
92
+ />
93
+ }
94
+ />
95
+ <Area
96
+ activeDot={{ fill: "var(--color-amber-400)", r: 5 }}
97
+ dataKey="activePeople"
98
+ dot={{
99
+ fill: "var(--color-amber-400)",
100
+ fillOpacity: dense ? 0.35 : 0.75,
101
+ r: dense ? 3 : 4,
102
+ stroke: "none",
103
+ }}
104
+ fill="url(#people-area)"
105
+ fillOpacity={1}
106
+ isAnimationActive={false}
113
107
  stroke="url(#people-line)"
114
108
  strokeLinecap="round"
115
109
  strokeLinejoin="round"
116
- strokeWidth="2.5"
110
+ strokeWidth={2.5}
111
+ type="linear"
117
112
  />
118
- ) : null}
119
- {props.days.map((day, index) => {
120
- const point = points[index]!;
121
- return (
122
- <ActivityChartTooltip
123
- key={day.date}
124
- content={
125
- <ActivityTooltipRows
126
- rows={[
127
- ["active people", day.activePeople],
128
- ["conversations", day.conversations],
129
- ]}
130
- />
131
- }
132
- date={day.date}
133
- summary={`${day.activePeople} active people, ${day.conversations} conversations`}
134
- >
135
- <circle
136
- cx={point.x}
137
- cy={point.y}
138
- fill="var(--color-amber-400)"
139
- opacity={props.days.length > 30 ? 0.35 : 0.75}
140
- r={props.days.length > 30 ? 3 : 4}
141
- tabIndex={0}
142
- />
143
- </ActivityChartTooltip>
144
- );
145
- })}
146
- <ActivityChartAverageLine
147
- unit={timeRangeBucketAverageUnit(bucketUnit)}
148
- average={average}
149
- format={formatActivityChartAverage}
150
- layout={layout}
151
- maximum={maximum}
152
- stroke="var(--color-amber-400)"
153
- />
154
- <ActivityChartDateLabels
155
- dates={props.days.map((day) => day.date)}
156
- layout={layout}
157
- xPosition={(index) => points[index]?.x ?? layout.left}
158
- />
159
- </ChartSvg>
113
+ <ActivityChartAverageLine
114
+ unit={timeRangeBucketAverageUnit(bucketUnit)}
115
+ average={average}
116
+ format={formatActivityChartAverage}
117
+ stroke="var(--color-amber-400)"
118
+ />
119
+ </AreaChart>
120
+ </ChartContainer>
160
121
  </div>
161
122
  </Card>
162
123
  );
@@ -3,7 +3,7 @@ import type { CodeChangeSummaryReport } from "@sentry/junior/api/schema";
3
3
 
4
4
  import { EmptyTelemetry } from "../../components/EmptyTelemetry";
5
5
  import { FilterGroup } from "../../components/FilterBar";
6
- import { Select } from "../../components/Select";
6
+ import { NativeSelect } from "../../components/ui/native-select";
7
7
  import { Card } from "../../components/layout/Card";
8
8
  import { CodeChangeRow } from "../code/CodeChangeRow";
9
9
 
@@ -57,7 +57,7 @@ export function ProfileCodeChanges(props: {
57
57
  </div>
58
58
  <div className="flex flex-wrap gap-3">
59
59
  <FilterGroup className="w-36" label="State">
60
- <Select
60
+ <NativeSelect
61
61
  aria-label="Filter changes by state"
62
62
  onChange={(event) => setState(event.currentTarget.value)}
63
63
  value={state}
@@ -67,10 +67,10 @@ export function ProfileCodeChanges(props: {
67
67
  {option.label} ({count(option.value)})
68
68
  </option>
69
69
  ))}
70
- </Select>
70
+ </NativeSelect>
71
71
  </FilterGroup>
72
72
  <FilterGroup className="w-36" label="Opened">
73
- <Select
73
+ <NativeSelect
74
74
  aria-label="Filter changes by opened date"
75
75
  onChange={(event) => setOpenedDays(event.currentTarget.value)}
76
76
  value={openedDays}
@@ -80,7 +80,7 @@ export function ProfileCodeChanges(props: {
80
80
  {option.label}
81
81
  </option>
82
82
  ))}
83
- </Select>
83
+ </NativeSelect>
84
84
  </FilterGroup>
85
85
  </div>
86
86
  </div>
@@ -3,7 +3,7 @@ import type { BaselineSnapshotReport } from "@sentry/junior/api/schema";
3
3
 
4
4
  import { Card } from "../../components/layout/Card";
5
5
  import { formatMs, formatRelativeTime, formatTime } from "../../format";
6
- import { formatSandboxSize } from "./SandboxSizeSummary";
6
+ import { formatSandboxSize } from "./sandboxSize";
7
7
 
8
8
  /**
9
9
  * Show the base Sandbox: the default size and the baseline snapshot.
@@ -1,4 +1,4 @@
1
- import { useChartLayout } from "../../components/charts/useChartLayout";
1
+ import { Bar, BarChart, CartesianGrid } from "recharts";
2
2
  import {
3
3
  type TimeRangeBucketUnit,
4
4
  timeRangeBucketAverageUnit,
@@ -7,27 +7,28 @@ import type { ConversationMetricDay } from "@sentry/junior/api/schema";
7
7
 
8
8
  import {
9
9
  ActivityChartAverageLine,
10
- ActivityChartDateLabels,
11
- ActivityChartGrid,
12
10
  activityChartAverage,
13
- ActivityChartTooltip,
14
- ActivityTooltipRows,
15
- ChartSvg,
11
+ activityChartMargin,
12
+ ActivityChartTooltipContent,
13
+ ActivityChartXAxis,
14
+ ActivityChartYAxis,
16
15
  } from "../../components/charts/ActivityChart";
17
16
  import { ChartHeader } from "../../components/charts/ChartHeader";
18
17
  import { Card } from "../../components/layout/Card";
18
+ import { ChartContainer, ChartTooltip } from "../../components/ui/chart";
19
19
  import { formatActivityChartAverage, formatCompactNumber } from "../../format";
20
20
 
21
+ const chartConfig = {
22
+ conversations: { color: "var(--color-cyan-400)", label: "Conversations" },
23
+ };
24
+
21
25
  /** Plot root conversations with recorded activity each day or hour. */
22
26
  export function ConversationActivityChart(props: {
23
27
  bucketUnit?: TimeRangeBucketUnit;
24
28
  days: ConversationMetricDay[];
25
29
  }) {
26
30
  const bucketUnit = props.bucketUnit ?? "day";
27
- const { ref, layout } = useChartLayout(150);
28
31
  const maximum = Math.max(1, ...props.days.map((day) => day.conversations));
29
- const step = layout.plotWidth / Math.max(1, props.days.length);
30
- const barWidth = Math.max(2, Math.min(24, step * 0.68));
31
32
  const values = props.days.map((day) => day.conversations);
32
33
  const total = values.reduce((sum, value) => sum + value, 0);
33
34
  const average = activityChartAverage(values);
@@ -39,65 +40,46 @@ export function ConversationActivityChart(props: {
39
40
  title="Conversation activity"
40
41
  total={formatCompactNumber(total)}
41
42
  />
42
- <div className="px-2 py-2" ref={ref}>
43
- <ChartSvg
44
- aria-label={`Conversations per ${bucketUnit === "6hour" ? "6 hours" : bucketUnit}`}
45
- layout={layout}
43
+ <div className="px-2 py-2">
44
+ <ChartContainer
45
+ className="aspect-auto h-38 w-full"
46
+ config={chartConfig}
46
47
  >
47
- <ActivityChartGrid layout={layout} maximum={maximum} />
48
- {props.days.map((day, index) => {
49
- const x = layout.left + index * step + step / 2;
50
- const barHeight = (day.conversations / maximum) * layout.plotHeight;
51
- const renderedHeight = Math.max(
52
- day.conversations ? 2 : 0,
53
- barHeight,
54
- );
55
- return (
56
- <ActivityChartTooltip
57
- key={day.date}
58
- content={
59
- <ActivityTooltipRows
60
- rows={[["conversations", day.conversations]]}
61
- />
62
- }
63
- date={day.date}
64
- summary={`${day.conversations} conversations`}
65
- >
66
- <g tabIndex={0}>
67
- <rect
68
- fill="var(--color-cyan-400)"
69
- height={renderedHeight}
70
- opacity={day.conversations ? 0.78 : 0.08}
71
- rx="2"
72
- width={barWidth}
73
- x={x - barWidth / 2}
74
- y={layout.top + layout.plotHeight - renderedHeight}
75
- />
76
- <rect
77
- fill="transparent"
78
- height={layout.plotHeight}
79
- width={step}
80
- x={layout.left + index * step}
81
- y={layout.top}
82
- />
83
- </g>
84
- </ActivityChartTooltip>
85
- );
86
- })}
87
- <ActivityChartAverageLine
88
- average={average}
89
- format={formatActivityChartAverage}
90
- layout={layout}
91
- maximum={maximum}
92
- stroke="var(--color-cyan-400)"
93
- unit={timeRangeBucketAverageUnit(bucketUnit)}
94
- />
95
- <ActivityChartDateLabels
96
- dates={props.days.map((day) => day.date)}
97
- layout={layout}
98
- xPosition={(index) => layout.left + index * step + step / 2}
99
- />
100
- </ChartSvg>
48
+ <BarChart
49
+ aria-label={`Conversations per ${bucketUnit === "6hour" ? "6 hours" : bucketUnit}`}
50
+ data={props.days}
51
+ margin={activityChartMargin}
52
+ role="img"
53
+ >
54
+ <CartesianGrid strokeDasharray="3 5" vertical={false} />
55
+ <ActivityChartYAxis maximum={maximum} />
56
+ <ActivityChartXAxis keys={props.days.map((day) => day.date)} />
57
+ <ChartTooltip
58
+ content={
59
+ <ActivityChartTooltipContent
60
+ rows={(day: ConversationMetricDay) => [
61
+ ["conversations", day.conversations],
62
+ ]}
63
+ />
64
+ }
65
+ />
66
+ <Bar
67
+ dataKey="conversations"
68
+ fill="var(--color-conversations)"
69
+ fillOpacity={0.78}
70
+ isAnimationActive={false}
71
+ maxBarSize={24}
72
+ minPointSize={(value) => (value ? 2 : 0)}
73
+ radius={2}
74
+ />
75
+ <ActivityChartAverageLine
76
+ average={average}
77
+ format={formatActivityChartAverage}
78
+ stroke="var(--color-cyan-400)"
79
+ unit={timeRangeBucketAverageUnit(bucketUnit)}
80
+ />
81
+ </BarChart>
82
+ </ChartContainer>
101
83
  </div>
102
84
  </Card>
103
85
  );
@@ -128,10 +128,9 @@ export function GuardianActivity(props: {
128
128
  }
129
129
  date={day.date}
130
130
  summary={`${day.allow} allowed, ${day.ask} asked, ${day.deny} denied, ${formatCostSummary({ total: day.costUsd ?? 0 })}`}
131
- triggerClassName="h-full min-w-0 flex-1 items-end"
132
131
  >
133
132
  <button
134
- className="flex w-full min-w-0 flex-col justify-end overflow-hidden rounded-t-sm bg-dashboard-fill-soft focus-visible:outline-1 focus-visible:outline-dashboard-focus"
133
+ className="flex min-w-0 flex-1 flex-col justify-end overflow-hidden rounded-t-sm bg-dashboard-fill-soft focus-visible:outline-1 focus-visible:outline-dashboard-focus"
135
134
  style={{ height: `${height}%` }}
136
135
  type="button"
137
136
  >