@sentry/junior-dashboard 0.264.0 → 0.266.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 (213) 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/ActorAvatar.tsx +10 -22
  14. package/src/client/components/BottomSheet.tsx +12 -2
  15. package/src/client/components/ConnectionBanner.tsx +4 -3
  16. package/src/client/components/CopyButton.tsx +82 -0
  17. package/src/client/components/DetailsPanel.tsx +133 -0
  18. package/src/client/components/Drawer.tsx +15 -24
  19. package/src/client/components/EmptyTelemetry.tsx +5 -3
  20. package/src/client/components/FilterBar.tsx +16 -31
  21. package/src/client/components/ImageAttachment.tsx +49 -63
  22. package/src/client/components/InlineError.tsx +8 -4
  23. package/src/client/components/Metric.tsx +33 -23
  24. package/src/client/components/Notice.tsx +19 -26
  25. package/src/client/components/PageContentSkeleton.tsx +1 -1
  26. package/src/client/components/Pagination.tsx +27 -27
  27. package/src/client/components/ParticipantAvatarStack.tsx +43 -39
  28. package/src/client/components/ProfileMenu.tsx +27 -93
  29. package/src/client/components/README.md +103 -78
  30. package/src/client/components/SearchInput.tsx +51 -41
  31. package/src/client/components/SegmentedNav.tsx +1 -1
  32. package/src/client/components/ShimmerText.tsx +2 -6
  33. package/src/client/components/ThemeSwitcher.tsx +19 -15
  34. package/src/client/components/VersionDriftBanner.tsx +9 -7
  35. package/src/client/components/charts/ActivityChart.tsx +173 -288
  36. package/src/client/components/charts/CacheWrites.tsx +59 -37
  37. package/src/client/components/charts/InputCacheChart.tsx +137 -131
  38. package/src/client/components/charts/README.md +4 -4
  39. package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
  40. package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
  41. package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
  42. package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
  43. package/src/client/components/layout/DashboardHeader.tsx +44 -90
  44. package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
  45. package/src/client/components/layout/VisualViewportShell.tsx +2 -2
  46. package/src/client/components/ui/accordion.tsx +77 -0
  47. package/src/client/components/ui/alert-dialog.tsx +89 -0
  48. package/src/client/components/ui/alert.tsx +64 -0
  49. package/src/client/components/ui/avatar.tsx +53 -0
  50. package/src/client/components/ui/button.tsx +86 -0
  51. package/src/client/components/ui/chart.tsx +74 -0
  52. package/src/client/components/ui/checkbox.tsx +26 -0
  53. package/src/client/components/ui/collapsible.tsx +22 -0
  54. package/src/client/components/ui/dialog.tsx +66 -0
  55. package/src/client/components/ui/empty.tsx +32 -0
  56. package/src/client/components/ui/field.tsx +49 -0
  57. package/src/client/components/ui/input-group.tsx +96 -0
  58. package/src/client/components/ui/input.tsx +35 -0
  59. package/src/client/components/ui/native-select.tsx +57 -0
  60. package/src/client/components/ui/pagination.tsx +60 -0
  61. package/src/client/components/ui/popover.tsx +67 -0
  62. package/src/client/components/ui/radio-group.tsx +32 -0
  63. package/src/client/components/ui/sheet.tsx +13 -3
  64. package/src/client/components/ui/skeleton.tsx +19 -0
  65. package/src/client/components/ui/spinner.tsx +21 -0
  66. package/src/client/components/ui/table.tsx +101 -0
  67. package/src/client/components/ui/tabs.tsx +75 -0
  68. package/src/client/components/ui/textarea.tsx +17 -0
  69. package/src/client/components/ui/toast.tsx +215 -0
  70. package/src/client/components/ui/toggle-group.tsx +36 -0
  71. package/src/client/components/ui/toggle.tsx +37 -0
  72. package/src/client/components/ui/tooltip.tsx +168 -0
  73. package/src/client/components/ui/trace.tsx +96 -0
  74. package/src/client/conversations/ChatLayout.tsx +7 -3
  75. package/src/client/conversations/ConversationBrief.tsx +17 -6
  76. package/src/client/conversations/ConversationChildAgents.tsx +5 -5
  77. package/src/client/conversations/ConversationComposer.tsx +26 -12
  78. package/src/client/conversations/ConversationDetails.tsx +147 -62
  79. package/src/client/conversations/ConversationEventDetails.tsx +141 -0
  80. package/src/client/conversations/ConversationEventLog.tsx +150 -129
  81. package/src/client/conversations/ConversationHeader.tsx +13 -7
  82. package/src/client/conversations/ConversationHeaderActions.tsx +97 -93
  83. package/src/client/conversations/ConversationHomeList.tsx +1 -1
  84. package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
  85. package/src/client/conversations/ConversationMeta.tsx +55 -88
  86. package/src/client/conversations/ConversationPage.tsx +220 -101
  87. package/src/client/conversations/ConversationSidebar.tsx +49 -29
  88. package/src/client/conversations/ConversationUsageBreakdown.tsx +108 -65
  89. package/src/client/conversations/ConversationWatches.tsx +173 -0
  90. package/src/client/conversations/ConversationWorkspace.tsx +12 -22
  91. package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
  92. package/src/client/conversations/DetailsList.tsx +139 -0
  93. package/src/client/conversations/EventDetails.tsx +42 -8
  94. package/src/client/conversations/EventUsage.tsx +99 -74
  95. package/src/client/conversations/ForkConversationButton.tsx +27 -16
  96. package/src/client/conversations/ObjectCard.tsx +20 -8
  97. package/src/client/conversations/PendingMailboxStack.tsx +52 -38
  98. package/src/client/conversations/README.md +89 -13
  99. package/src/client/conversations/ShareConversationButton.tsx +28 -17
  100. package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
  101. package/src/client/conversations/ToolFrame.tsx +36 -52
  102. package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
  103. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
  104. package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
  105. package/src/client/conversations/TranscriptEventView.tsx +35 -16
  106. package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
  107. package/src/client/conversations/TranscriptLoading.tsx +1 -1
  108. package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
  109. package/src/client/conversations/TranscriptMessageContextView.tsx +35 -18
  110. package/src/client/conversations/TranscriptMessageView.tsx +6 -1
  111. package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
  112. package/src/client/conversations/TranscriptStructuredEventView.tsx +25 -15
  113. package/src/client/conversations/TranscriptSummary.tsx +34 -2
  114. package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
  115. package/src/client/conversations/TranscriptToolView.tsx +1 -1
  116. package/src/client/conversations/TranscriptTurnContextView.tsx +144 -65
  117. package/src/client/conversations/TranscriptView.tsx +63 -27
  118. package/src/client/conversations/eventLog.ts +58 -0
  119. package/src/client/conversations/eventTime.ts +60 -0
  120. package/src/client/conversations/queries.ts +29 -0
  121. package/src/client/conversations/transcriptBottomPinning.ts +34 -0
  122. package/src/client/conversations/transcriptMentions.tsx +18 -5
  123. package/src/client/format.ts +5 -0
  124. package/src/client/pages/ConnectionsPage.tsx +2 -2
  125. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  126. package/src/client/pages/SettingsPage.tsx +9 -10
  127. package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
  128. package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
  129. package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
  130. package/src/client/pages/automations/AutomationDetails.tsx +193 -0
  131. package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
  132. package/src/client/pages/automations/AutomationEditor.tsx +41 -29
  133. package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
  134. package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
  135. package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
  136. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
  137. package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
  138. package/src/client/pages/automations/AutomationFilters.tsx +189 -74
  139. package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
  140. package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
  141. package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
  142. package/src/client/pages/automations/AutomationRow.tsx +12 -138
  143. package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
  144. package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
  145. package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
  146. package/src/client/pages/automations/AutomationStatus.tsx +201 -0
  147. package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
  148. package/src/client/pages/automations/AutomationsPage.tsx +80 -68
  149. package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
  150. package/src/client/pages/automations/README.md +57 -0
  151. package/src/client/pages/automations/TriggerFilters.tsx +22 -0
  152. package/src/client/pages/automations/automationFormat.ts +37 -23
  153. package/src/client/pages/code/CodeActivityChart.tsx +59 -101
  154. package/src/client/pages/code/CodePage.tsx +52 -55
  155. package/src/client/pages/code/RepositoryPage.tsx +6 -8
  156. package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
  157. package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
  158. package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
  159. package/src/client/pages/memory/MemoryDetails.tsx +218 -0
  160. package/src/client/pages/memory/MemoryPage.tsx +168 -112
  161. package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
  162. package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
  163. package/src/client/pages/memory/memoryRecord.ts +1 -1
  164. package/src/client/pages/people/ContributionGrid.tsx +23 -16
  165. package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
  166. package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
  167. package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
  168. package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
  169. package/src/client/pages/system/GuardianActivity.tsx +1 -2
  170. package/src/client/pages/system/PluginBarChart.tsx +106 -87
  171. package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
  172. package/src/client/pages/system/README.md +23 -0
  173. package/src/client/pages/system/SystemNavigation.tsx +5 -1
  174. package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
  175. package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
  176. package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
  177. package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
  178. package/src/client/pages/system/WorkspaceList.tsx +20 -30
  179. package/src/client/pages/system/WorkspacesPage.tsx +117 -119
  180. package/src/client/pages/system/sandboxSize.ts +8 -0
  181. package/src/client/pages/system/workspaceQueries.ts +53 -0
  182. package/src/client/pages/user/PluginUserPage.tsx +51 -2
  183. package/src/client/pages/user/pluginUserPageData.ts +1 -6
  184. package/src/client/styles.ts +4 -0
  185. package/src/client.tsx +7 -1
  186. package/src/mock-reporting/automation-collection.ts +4 -2
  187. package/src/mock-reporting/fixtures.ts +23 -7
  188. package/src/mock-reporting/routes.ts +12 -0
  189. package/src/mock-reporting/watches.ts +94 -0
  190. package/src/shell.ts +4 -9
  191. package/src/tailwind.css +0 -10
  192. package/src/client/components/Button.tsx +0 -118
  193. package/src/client/components/Field.tsx +0 -53
  194. package/src/client/components/SegmentedTabs.tsx +0 -119
  195. package/src/client/components/Select.tsx +0 -52
  196. package/src/client/components/Skeleton.tsx +0 -17
  197. package/src/client/components/TextInput.tsx +0 -58
  198. package/src/client/components/Tooltip.tsx +0 -216
  199. package/src/client/components/charts/useChartLayout.ts +0 -27
  200. package/src/client/components/ui/sheet.LICENSE +0 -21
  201. package/src/client/conversations/ConversationDetailsFrame.tsx +0 -119
  202. package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
  203. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
  204. package/src/client/pages/dev/ChartFixtures.tsx +0 -325
  205. package/src/client/pages/dev/ComponentsPage.tsx +0 -899
  206. package/src/client/pages/dev/ConversationFixture.tsx +0 -231
  207. package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
  208. package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
  209. package/src/client/pages/dev/SelectFixtures.tsx +0 -62
  210. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
  211. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
  212. package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
  213. package/src/client/pages/system/SnapshotSummary.tsx +0 -78
@@ -1,216 +0,0 @@
1
- import * as HoverCard from "@radix-ui/react-hover-card";
2
- import {
3
- type ReactElement,
4
- type ReactNode,
5
- type RefObject,
6
- useEffect,
7
- useId,
8
- useRef,
9
- useState,
10
- } from "react";
11
-
12
- import { cn } from "../styles";
13
-
14
- type TooltipProps = {
15
- align?: "center" | "left" | "right";
16
- children: ReactElement;
17
- className?: string;
18
- content: ReactNode;
19
- focusable?: boolean;
20
- label?: ReactNode;
21
- placement?: "above" | "below";
22
- triggerClassName?: string;
23
- };
24
-
25
- const VIEWPORT_GAP = 8;
26
- const ANCHOR_GAP = 10;
27
- const CLOSE_DELAY_MS = 150;
28
- const TOOLTIP_MEDIA_QUERY =
29
- "(min-width: 768px) and (hover: hover) and (pointer: fine)";
30
-
31
- /** SVG hosts cannot wrap triggers in an HTML span without collapsing geometry. */
32
- const SVG_TRIGGER_TAGS = new Set([
33
- "circle",
34
- "ellipse",
35
- "g",
36
- "line",
37
- "path",
38
- "polygon",
39
- "polyline",
40
- "rect",
41
- "svg",
42
- "text",
43
- ]);
44
-
45
- function isSvgTrigger(element: ReactElement): boolean {
46
- return typeof element.type === "string" && SVG_TRIGGER_TAGS.has(element.type);
47
- }
48
-
49
- /** Show selectable dashboard details beside an element. */
50
- export function Tooltip({
51
- align = "center",
52
- children,
53
- className,
54
- content,
55
- focusable = false,
56
- label,
57
- placement = "above",
58
- triggerClassName,
59
- }: TooltipProps) {
60
- const tooltipId = useId();
61
- const rootRef = useRef<Element | null>(null);
62
- const touchStartedOpenRef = useRef<boolean | null>(null);
63
- const suppressOpenUntilRef = useRef(0);
64
- const [open, setOpen] = useState(false);
65
- const [available, setAvailable] = useState(() =>
66
- typeof window === "undefined"
67
- ? false
68
- : window.matchMedia(TOOLTIP_MEDIA_QUERY).matches,
69
- );
70
- const svgTrigger = isSvgTrigger(children);
71
-
72
- useEffect(() => {
73
- const media = window.matchMedia(TOOLTIP_MEDIA_QUERY);
74
- const syncAvailable = () => {
75
- setAvailable(media.matches);
76
- if (!media.matches) setOpen(false);
77
- };
78
- syncAvailable();
79
- media.addEventListener("change", syncAvailable);
80
- return () => media.removeEventListener("change", syncAvailable);
81
- }, []);
82
-
83
- useEffect(() => {
84
- if (!open) return;
85
- const onPointerDown = (event: PointerEvent) => {
86
- const root = rootRef.current;
87
- const target = event.target;
88
- if (!(target instanceof Node)) return;
89
- // Keep interactions inside the trigger or content from auto-closing.
90
- if (root?.contains(target)) return;
91
- const contentNode = document.getElementById(tooltipId);
92
- if (contentNode?.contains(target)) return;
93
- suppressOpenUntilRef.current = performance.now() + CLOSE_DELAY_MS + 50;
94
- setOpen(false);
95
- };
96
- document.addEventListener("pointerdown", onPointerDown, true);
97
- return () => {
98
- document.removeEventListener("pointerdown", onPointerDown, true);
99
- };
100
- }, [open, tooltipId]);
101
-
102
- // Keep chart/layout wrappers even when tooltips are off.
103
- if (!available) {
104
- if (svgTrigger) return children;
105
- return (
106
- <span className={cn("inline-flex", triggerClassName)}>{children}</span>
107
- );
108
- }
109
-
110
- const card = (
111
- <HoverCard.Root
112
- closeDelay={CLOSE_DELAY_MS}
113
- onOpenChange={(nextOpen) => {
114
- // Touch toggle/outside close can race HoverCard's hover reopen.
115
- if (nextOpen && performance.now() < suppressOpenUntilRef.current) {
116
- return;
117
- }
118
- setOpen(nextOpen);
119
- }}
120
- open={open}
121
- openDelay={0}
122
- >
123
- <HoverCard.Trigger
124
- aria-describedby={open ? tooltipId : undefined}
125
- asChild
126
- onBlur={focusable ? () => setOpen(false) : undefined}
127
- onFocus={focusable ? () => setOpen(true) : undefined}
128
- onPointerCancel={() => {
129
- touchStartedOpenRef.current = null;
130
- }}
131
- onPointerDown={(event) => {
132
- if (event.pointerType === "touch") {
133
- touchStartedOpenRef.current = open;
134
- }
135
- }}
136
- onPointerUp={(event) => {
137
- if (
138
- event.pointerType !== "touch" ||
139
- touchStartedOpenRef.current === null
140
- ) {
141
- return;
142
- }
143
- const nextOpen = !touchStartedOpenRef.current;
144
- if (!nextOpen) {
145
- suppressOpenUntilRef.current =
146
- performance.now() + CLOSE_DELAY_MS + 50;
147
- }
148
- setOpen(nextOpen);
149
- touchStartedOpenRef.current = null;
150
- }}
151
- ref={
152
- svgTrigger
153
- ? (node) => {
154
- rootRef.current = node;
155
- }
156
- : undefined
157
- }
158
- >
159
- {children}
160
- </HoverCard.Trigger>
161
- <HoverCard.Portal>
162
- <HoverCard.Content
163
- align={
164
- align === "left" ? "start" : align === "right" ? "end" : "center"
165
- }
166
- className={cn(
167
- "z-50 select-text outline-none",
168
- className ??
169
- "min-w-36 max-w-lg rounded-md border border-dashboard-border-emphasis bg-dashboard-surface-raised px-3 py-2 font-sans text-xs leading-relaxed text-dashboard-text-muted shadow-2xl shadow-black/70",
170
- )}
171
- collisionPadding={VIEWPORT_GAP}
172
- hideWhenDetached
173
- id={tooltipId}
174
- role="tooltip"
175
- side={placement === "above" ? "top" : "bottom"}
176
- sideOffset={ANCHOR_GAP}
177
- >
178
- {label ? (
179
- <div className="mb-1 font-semibold text-dashboard-text-muted">
180
- {label}
181
- </div>
182
- ) : null}
183
- {content}
184
- </HoverCard.Content>
185
- </HoverCard.Portal>
186
- </HoverCard.Root>
187
- );
188
-
189
- if (svgTrigger) return card;
190
-
191
- return (
192
- <span
193
- className={cn("inline-flex", triggerClassName)}
194
- ref={rootRef as RefObject<HTMLSpanElement | null>}
195
- >
196
- {card}
197
- </span>
198
- );
199
- }
200
-
201
- /** Compact label tooltip for icon-only controls such as header actions. */
202
- export function IconButtonTooltip(props: {
203
- children: ReactElement;
204
- label: string;
205
- placement?: "above" | "below";
206
- }) {
207
- return (
208
- <Tooltip
209
- className="min-w-0 rounded-md border border-dashboard-border-emphasis bg-dashboard-surface-raised px-2.5 py-1.5 font-sans text-xs leading-none text-dashboard-text shadow-2xl shadow-black/70"
210
- content={props.label}
211
- placement={props.placement ?? "above"}
212
- >
213
- <span className="inline-flex">{props.children}</span>
214
- </Tooltip>
215
- );
216
- }
@@ -1,27 +0,0 @@
1
- import { useLayoutEffect, useRef, useState } from "react";
2
- import { createActivityChartLayout } from "./ActivityChart";
3
-
4
- /** Keep chart height and label spacing fixed as its container width changes. */
5
- export function useChartLayout(height: number, left = 64) {
6
- const ref = useRef<HTMLDivElement>(null);
7
- const [width, setWidth] = useState(400);
8
- useLayoutEffect(() => {
9
- const container = ref.current;
10
- if (!container) return;
11
- const observer = new ResizeObserver(([entry]) => {
12
- if (entry && entry.contentRect.width > 0)
13
- setWidth(entry.contentRect.width);
14
- });
15
- observer.observe(container);
16
- return () => observer.disconnect();
17
- }, []);
18
- return {
19
- ref,
20
- layout: createActivityChartLayout(height, {
21
- width,
22
- left,
23
- top: 18,
24
- bottom: 28,
25
- }),
26
- };
27
- }
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 shadcn
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,119 +0,0 @@
1
- import { X } from "lucide-react";
2
- import {
3
- useEffect,
4
- useId,
5
- useRef,
6
- useSyncExternalStore,
7
- type ReactNode,
8
- } from "react";
9
-
10
- import { Button } from "../components/Button";
11
- import { BottomSheet } from "../components/BottomSheet";
12
-
13
- const panelMediaQuery = "(min-width: 1280px)";
14
- // Open details by default only when the chat column stays wide beside them.
15
- const defaultOpenMediaQuery = "(min-width: 1440px)";
16
-
17
- function useMediaQuery(query: string): boolean {
18
- return useSyncExternalStore(
19
- (onChange) => {
20
- const media = window.matchMedia(query);
21
- media.addEventListener("change", onChange);
22
- return () => media.removeEventListener("change", onChange);
23
- },
24
- () => window.matchMedia(query).matches,
25
- () => false,
26
- );
27
- }
28
-
29
- /**
30
- * Report whether the viewport has room to show details as a panel beside the
31
- * chat. Narrower viewports show details in a bottom sheet.
32
- */
33
- export function useConversationDetailsPanel(): boolean {
34
- return useMediaQuery(panelMediaQuery);
35
- }
36
-
37
- /** Report whether details should open beside the chat without a request. */
38
- export function useConversationDetailsDefaultOpen(): boolean {
39
- return useMediaQuery(defaultOpenMediaQuery);
40
- }
41
-
42
- /** Keep details beside the chat when space permits, or in a modal bottom sheet. */
43
- export function ConversationDetailsFrame(props: {
44
- actions?: ReactNode;
45
- children: ReactNode;
46
- onClose(): void;
47
- }) {
48
- const titleId = useId();
49
- const panel = useConversationDetailsPanel();
50
- const closeRef = useRef<HTMLButtonElement>(null);
51
- const wasPanel = useRef(panel);
52
- useEffect(() => {
53
- if (
54
- panel &&
55
- !wasPanel.current &&
56
- !document.querySelector("[data-dashboard-modal]")
57
- ) {
58
- closeRef.current?.focus({ preventScroll: true });
59
- }
60
- wasPanel.current = panel;
61
- }, [panel]);
62
- if (!panel) {
63
- return (
64
- <BottomSheet label="Conversation details" onClose={props.onClose}>
65
- {props.children}
66
- {/* After the tabs, so the sheet still opens with the first tab focused. */}
67
- {props.actions ? (
68
- <div className="flex justify-end gap-1 border-t border-dashboard-border px-4 py-2">
69
- {props.actions}
70
- </div>
71
- ) : null}
72
- </BottomSheet>
73
- );
74
- }
75
-
76
- return (
77
- <aside
78
- aria-labelledby={titleId}
79
- className="grid min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)] border-l border-dashboard-border bg-dashboard-bg-elevated"
80
- onKeyDown={(event) => {
81
- if (
82
- event.key === "Escape" &&
83
- !event.defaultPrevented &&
84
- !document.querySelector("[data-dashboard-modal]")
85
- ) {
86
- event.preventDefault();
87
- props.onClose();
88
- }
89
- }}
90
- >
91
- <header className="flex items-center justify-between gap-3 border-b border-dashboard-border py-2 pl-5 pr-3">
92
- {/* Keep actions beside the title. The panel opens under the pointer
93
- that clicked the chat's Details button, so that corner holds only
94
- Close. */}
95
- <div className="flex min-w-0 items-center gap-3">
96
- <h2
97
- aria-label="Conversation details"
98
- className="m-0 text-base font-semibold text-dashboard-text"
99
- id={titleId}
100
- >
101
- Details
102
- </h2>
103
- {props.actions ? (
104
- <div className="flex items-center gap-0.5">{props.actions}</div>
105
- ) : null}
106
- </div>
107
- <Button
108
- ref={closeRef}
109
- aria-label="Close conversation details"
110
- onClick={props.onClose}
111
- size="icon"
112
- >
113
- <X aria-hidden="true" size={15} />
114
- </Button>
115
- </header>
116
- <div className="min-h-0">{props.children}</div>
117
- </aside>
118
- );
119
- }
@@ -1,65 +0,0 @@
1
- import { useState } from "react";
2
- import { Check, Copy } from "lucide-react";
3
-
4
- import { Button } from "../components/Button";
5
- import { IconButtonTooltip } from "../components/Tooltip";
6
-
7
- /** Copy an available Markdown document while exposing clipboard result state. */
8
- export function CopyMarkdownButton(props: {
9
- getMarkdown?: () => Promise<string> | string;
10
- /** `menu` shows icon + label for mobile overflow lists. */
11
- layout?: "icon" | "menu";
12
- }) {
13
- const [status, setStatus] = useState<
14
- "copied" | "copying" | "failed" | "idle"
15
- >("idle");
16
- const label =
17
- status === "copied"
18
- ? "Copied"
19
- : status === "copying"
20
- ? "Preparing Markdown"
21
- : status === "failed"
22
- ? "Copy failed"
23
- : "Copy as Markdown";
24
- const Icon = status === "copied" ? Check : Copy;
25
-
26
- async function copyMarkdown() {
27
- if (!props.getMarkdown) return;
28
-
29
- try {
30
- setStatus("copying");
31
- await navigator.clipboard.writeText(await props.getMarkdown());
32
- setStatus("copied");
33
- } catch {
34
- setStatus("failed");
35
- }
36
- }
37
-
38
- if (props.layout === "menu") {
39
- return (
40
- <button
41
- aria-label={label}
42
- className="flex w-full cursor-pointer items-center gap-2.5 rounded-md border-0 bg-transparent px-2.5 py-2 text-left text-sm font-semibold text-dashboard-text transition-colors hover:bg-dashboard-fill-strong focus-visible:bg-dashboard-fill-strong focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
43
- disabled={!props.getMarkdown || status === "copying"}
44
- onClick={() => void copyMarkdown()}
45
- type="button"
46
- >
47
- <Icon aria-hidden="true" size={16} strokeWidth={2} />
48
- <span>{label}</span>
49
- </button>
50
- );
51
- }
52
-
53
- return (
54
- <IconButtonTooltip label={label}>
55
- <Button
56
- aria-label={label}
57
- disabled={!props.getMarkdown || status === "copying"}
58
- onClick={() => void copyMarkdown()}
59
- size="icon"
60
- >
61
- <Icon aria-hidden="true" size={15} strokeWidth={2} />
62
- </Button>
63
- </IconButtonTooltip>
64
- );
65
- }
@@ -1,219 +0,0 @@
1
- import { AutomationLifecycle } from "./AutomationLifecycle";
2
- import { automationOutcomeLabel } from "./automationOutcomes";
3
- import {
4
- automationEventConditions,
5
- automationEventNames,
6
- automationScheduleLabel,
7
- formatAutomationRunTime,
8
- } from "./automationFormat";
9
- import type { AutomationSummary } from "@sentry/junior/api/schema";
10
- import { Link, useLocation } from "react-router";
11
- import { Detail, DetailList } from "../../components/DetailList";
12
- import { Drawer } from "../../components/Drawer";
13
- import {
14
- timeRangeLabel,
15
- type TimeRangeDays,
16
- } from "../../components/controls/TimeRangeSelector";
17
- import { conversationPath } from "../../conversations/conversationRoutes";
18
- import { formatTime, peoplePath } from "../../format";
19
- import { TranscriptText } from "../../conversations/TranscriptText";
20
- import { cn, dashboardInlineLinkClass } from "../../styles";
21
-
22
- /** Show one automation's instruction and metadata in a right-side slide-out. */
23
- export function AutomationDetailsDrawer(props: {
24
- onClose(): void;
25
- range: TimeRangeDays;
26
- automation: AutomationSummary | undefined;
27
- }) {
28
- const location = useLocation();
29
- if (!props.automation) return null;
30
-
31
- const { automation } = props;
32
- const createdBy = automation.createdByEmail ? (
33
- <Link
34
- className={cn("font-semibold", dashboardInlineLinkClass)}
35
- to={peoplePath(automation.createdByEmail)}
36
- >
37
- {automation.ownedByViewer ? "you" : automation.createdBy}
38
- </Link>
39
- ) : automation.ownedByViewer ? (
40
- "you"
41
- ) : (
42
- automation.createdBy
43
- );
44
- const conditions =
45
- automation.kind === "event" ? automationEventConditions(automation) : "";
46
- const details =
47
- automation.kind === "scheduled"
48
- ? [
49
- { label: "Schedule", value: automationScheduleLabel(automation) },
50
- {
51
- label: "Next run",
52
- value:
53
- automation.status === "active" && automation.nextRunAt
54
- ? formatAutomationRunTime(automation.nextRunAt)
55
- : "None",
56
- },
57
- ]
58
- : [
59
- { label: "Watching", value: automation.resource },
60
- { label: "Events", value: automationEventNames(automation) },
61
- ...(conditions ? [{ label: "Conditions", value: conditions }] : []),
62
- ];
63
- const titleId = "automation-details-drawer-title";
64
-
65
- return (
66
- <Drawer
67
- closeLabel="Close automation details"
68
- header={
69
- <>
70
- <h2
71
- className="m-0 font-display text-lg font-medium tracking-normal text-dashboard-text"
72
- id={titleId}
73
- >
74
- {automation.title}
75
- </h2>
76
- <div className="mt-1 break-words text-sm text-dashboard-text-muted">
77
- Created by {createdBy} · {automation.destination.label}
78
- </div>
79
- </>
80
- }
81
- onClose={props.onClose}
82
- openKey={`${automation.kind}:${automation.id}`}
83
- titleId={titleId}
84
- >
85
- <section className="grid gap-5">
86
- <div>
87
- <h3 className="mt-0 mb-2 text-sm font-medium text-dashboard-text-muted">
88
- What to do
89
- </h3>
90
- <TranscriptText
91
- mentions={automation.mentions ?? []}
92
- text={automation.instruction}
93
- />
94
- </div>
95
- <AutomationLifecycle
96
- key={`${automation.kind}:${automation.id}`}
97
- automation={automation}
98
- editPath={`/automations/${encodeURIComponent(automation.id)}/edit${location.search}`}
99
- />
100
- <DetailList>
101
- {details.map((detail) => (
102
- <Detail key={detail.label} label={detail.label}>
103
- {detail.value}
104
- </Detail>
105
- ))}
106
- <Detail label="On success">
107
- {automation.outcomes.length === 0 ? (
108
- "No message on success"
109
- ) : (
110
- <ol className="m-0 pl-4">
111
- {automation.outcomes.map((outcome, index) => (
112
- <li key={index}>
113
- {automationOutcomeLabel(outcome, automation.destination)}
114
- </li>
115
- ))}
116
- </ol>
117
- )}
118
- </Detail>
119
- </DetailList>
120
- <div className="flex flex-wrap gap-x-5">
121
- {automation.totalRuns > 0 &&
122
- automation.lastRunStatus !== "failed" &&
123
- automation.lastRunStatus !== "blocked" ? (
124
- <Link
125
- className="w-fit py-2 text-sm text-dashboard-text underline underline-offset-2"
126
- to={`/automations/${encodeURIComponent(automation.id)}/executions`}
127
- >
128
- View all runs
129
- </Link>
130
- ) : null}
131
- <Link
132
- className="w-fit py-2 text-sm text-dashboard-text underline underline-offset-2"
133
- to={`/automations/${encodeURIComponent(automation.id)}/versions${location.search}`}
134
- >
135
- Version history
136
- </Link>
137
- </div>
138
- <details>
139
- <summary className="cursor-pointer py-3 text-sm text-dashboard-text-muted hover:text-dashboard-text focus-visible:outline focus-visible:outline-dashboard-focus">
140
- More details
141
- </summary>
142
- <DetailList>
143
- <Detail label="Connected accounts">
144
- {automation.credentialMode === "creator" ? (
145
- <>
146
- Uses accounts connected by{" "}
147
- {automation.ownedByViewer ? "you" : automation.createdBy}.
148
- </>
149
- ) : (
150
- "Junior’s accounts only"
151
- )}
152
- </Detail>
153
- <Detail label="Created">{formatDate(automation.createdAt)}</Detail>
154
- <Detail label="Run history">
155
- <AutomationExecutionSummary
156
- range={props.range}
157
- automation={automation}
158
- />
159
- </Detail>
160
- </DetailList>
161
- </details>
162
- </section>
163
- </Drawer>
164
- );
165
- }
166
-
167
- function AutomationExecutionSummary(props: {
168
- range: TimeRangeDays;
169
- automation: Pick<
170
- AutomationSummary,
171
- "id" | "kind" | "lastConversationId" | "lastRunAt" | "runs" | "totalRuns"
172
- >;
173
- }) {
174
- const { range, automation } = props;
175
- const runCount = automation.runs[range];
176
- const rangeLabel = timeRangeLabel(range);
177
- const executionsPath = `/automations/${encodeURIComponent(automation.id)}/executions`;
178
- return (
179
- <div className="text-sm text-dashboard-text-muted">
180
- {automation.totalRuns > 0 ? (
181
- <Link className={dashboardInlineLinkClass} to={executionsPath}>
182
- {runCount} runs / {rangeLabel}
183
- </Link>
184
- ) : (
185
- <span className="text-dashboard-text">
186
- {runCount} runs / {rangeLabel}
187
- </span>
188
- )}
189
- <span className="mx-2 opacity-45">·</span>
190
- <span>
191
- {automation.totalRuns > 0 ? (
192
- <Link className={dashboardInlineLinkClass} to={executionsPath}>
193
- {automation.totalRuns} total
194
- </Link>
195
- ) : (
196
- <>{automation.totalRuns} total</>
197
- )}
198
- {automation.lastRunAt ? " · Last run " : " · Never run"}
199
- {automation.lastRunAt && automation.lastConversationId ? (
200
- <Link
201
- className={dashboardInlineLinkClass}
202
- to={conversationPath(automation.lastConversationId)}
203
- >
204
- {formatAutomationRunTime(automation.lastRunAt)}
205
- </Link>
206
- ) : automation.lastRunAt ? (
207
- formatAutomationRunTime(automation.lastRunAt)
208
- ) : null}
209
- </span>
210
- </div>
211
- );
212
- }
213
-
214
- function formatDate(value: string): string {
215
- return formatTime(value, {
216
- dateStyle: "medium",
217
- timeStyle: "short",
218
- });
219
- }