@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
|
@@ -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
|
-
}
|