@sentry/junior-dashboard 0.263.0 → 0.265.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +2 -5
- package/dist/app.js +119 -35
- package/dist/client.js +148 -135
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +9 -4
- package/src/api/schema.ts +0 -1
- package/src/app.ts +5 -20
- package/src/client/App.tsx +29 -31
- package/src/client/api.ts +7 -0
- package/src/client/components/BottomSheet.tsx +26 -18
- package/src/client/components/CopyButton.tsx +83 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +34 -33
- package/src/client/components/FilterBar.tsx +16 -31
- package/src/client/components/Metric.tsx +33 -23
- package/src/client/components/PageContentSkeleton.tsx +1 -1
- package/src/client/components/Pagination.tsx +1 -1
- package/src/client/components/ParticipantAvatarStack.tsx +40 -36
- package/src/client/components/ProfileMenu.tsx +27 -93
- package/src/client/components/README.md +101 -64
- package/src/client/components/SegmentedNav.tsx +1 -1
- package/src/client/components/ShimmerText.tsx +2 -6
- package/src/client/components/ThemeSwitcher.tsx +19 -15
- package/src/client/components/charts/ActivityChart.tsx +173 -288
- package/src/client/components/charts/CacheWrites.tsx +63 -50
- package/src/client/components/charts/InputCacheChart.tsx +137 -131
- package/src/client/components/charts/README.md +4 -4
- package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
- package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
- package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
- package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
- package/src/client/components/layout/DashboardHeader.tsx +44 -90
- package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
- package/src/client/components/layout/VisualViewportShell.tsx +2 -2
- package/src/client/components/ui/accordion.tsx +77 -0
- package/src/client/components/ui/alert-dialog.tsx +89 -0
- package/src/client/components/ui/button.tsx +85 -0
- package/src/client/components/ui/chart.tsx +74 -0
- package/src/client/components/ui/checkbox.tsx +26 -0
- package/src/client/components/ui/collapsible.tsx +22 -0
- package/src/client/components/ui/field.tsx +49 -0
- package/src/client/components/ui/input.tsx +35 -0
- package/src/client/components/ui/native-select.tsx +57 -0
- package/src/client/components/ui/popover.tsx +67 -0
- package/src/client/components/ui/radio-group.tsx +32 -0
- package/src/client/components/ui/sheet.tsx +151 -0
- package/src/client/components/ui/skeleton.tsx +19 -0
- package/src/client/components/ui/table.tsx +80 -0
- package/src/client/components/ui/tabs.tsx +75 -0
- package/src/client/components/ui/textarea.tsx +17 -0
- package/src/client/components/ui/toast.tsx +215 -0
- package/src/client/components/ui/toggle-group.tsx +36 -0
- package/src/client/components/ui/toggle.tsx +37 -0
- package/src/client/components/ui/tooltip.tsx +168 -0
- package/src/client/components/ui/trace.tsx +96 -0
- package/src/client/components/ui/useSheet.ts +74 -0
- package/src/client/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationChildAgents.tsx +87 -58
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +198 -95
- package/src/client/conversations/ConversationEventDetails.tsx +141 -0
- package/src/client/conversations/ConversationEventLog.tsx +150 -129
- package/src/client/conversations/ConversationHeader.tsx +8 -3
- package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
- package/src/client/conversations/ConversationMeta.tsx +111 -92
- package/src/client/conversations/ConversationPage.tsx +241 -110
- package/src/client/conversations/ConversationSidebar.tsx +48 -29
- package/src/client/conversations/ConversationUsageBreakdown.tsx +16 -3
- package/src/client/conversations/ConversationWatches.tsx +165 -0
- package/src/client/conversations/ConversationWorkspace.tsx +12 -22
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
- package/src/client/conversations/DetailsList.tsx +124 -0
- package/src/client/conversations/EventDetails.tsx +42 -8
- package/src/client/conversations/ForkConversationButton.tsx +27 -16
- package/src/client/conversations/ObjectCard.tsx +20 -8
- package/src/client/conversations/PendingMailboxStack.tsx +52 -33
- package/src/client/conversations/README.md +93 -19
- package/src/client/conversations/ShareConversationButton.tsx +28 -3
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
- package/src/client/conversations/ToolFrame.tsx +36 -52
- package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
- package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
- package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
- package/src/client/conversations/TranscriptLoading.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
- package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
- package/src/client/conversations/TranscriptSubagentView.tsx +3 -3
- package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
- package/src/client/conversations/TranscriptToolView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
- package/src/client/conversations/TranscriptView.tsx +63 -27
- package/src/client/conversations/eventLog.ts +57 -0
- package/src/client/conversations/eventTime.ts +60 -0
- package/src/client/conversations/queries.ts +29 -0
- package/src/client/conversations/transcriptBottomPinning.ts +34 -0
- package/src/client/format.ts +5 -0
- package/src/client/lib/utils.ts +1 -0
- package/src/client/pages/ConnectionsPage.tsx +2 -2
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/SettingsPage.tsx +9 -10
- package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
- package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
- package/src/client/pages/automations/AutomationDetails.tsx +193 -0
- package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
- package/src/client/pages/automations/AutomationEditor.tsx +41 -29
- package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
- package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
- package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
- package/src/client/pages/automations/AutomationFilters.tsx +189 -74
- package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
- package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
- package/src/client/pages/automations/AutomationRow.tsx +12 -138
- package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
- package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
- package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
- package/src/client/pages/automations/AutomationStatus.tsx +201 -0
- package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
- package/src/client/pages/automations/AutomationsPage.tsx +80 -68
- package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
- package/src/client/pages/automations/README.md +57 -0
- package/src/client/pages/automations/TriggerFilters.tsx +22 -0
- package/src/client/pages/automations/automationFormat.ts +37 -23
- package/src/client/pages/code/CodeActivityChart.tsx +59 -101
- package/src/client/pages/code/CodePage.tsx +52 -55
- package/src/client/pages/code/RepositoryPage.tsx +6 -8
- package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
- package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
- package/src/client/pages/memory/MemoryDetails.tsx +218 -0
- package/src/client/pages/memory/MemoryPage.tsx +168 -112
- package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
- package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/people/ContributionGrid.tsx +23 -16
- package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
- package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
- package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
- package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
- package/src/client/pages/system/GuardianActivity.tsx +1 -2
- package/src/client/pages/system/PluginBarChart.tsx +106 -87
- package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
- package/src/client/pages/system/README.md +23 -0
- package/src/client/pages/system/SystemNavigation.tsx +5 -1
- package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
- package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
- package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
- package/src/client/pages/system/WorkspaceList.tsx +20 -30
- package/src/client/pages/system/WorkspacesPage.tsx +117 -119
- package/src/client/pages/system/sandboxSize.ts +8 -0
- package/src/client/pages/system/workspaceQueries.ts +53 -0
- package/src/client/pages/user/PluginUserPage.tsx +51 -2
- package/src/client/pages/user/pluginUserPageData.ts +1 -6
- package/src/client/styles.ts +4 -0
- package/src/client.tsx +7 -1
- package/src/mock-reporting/automation-collection.ts +4 -2
- package/src/mock-reporting/fixtures.ts +23 -7
- package/src/mock-reporting/routes.ts +12 -0
- package/src/mock-reporting/watches.ts +94 -0
- package/src/shell.ts +4 -9
- package/src/tailwind.css +77 -10
- package/src/client/components/Button.tsx +0 -118
- package/src/client/components/Field.tsx +0 -53
- package/src/client/components/SegmentedTabs.tsx +0 -119
- package/src/client/components/Select.tsx +0 -52
- package/src/client/components/Sheet.LICENSE +0 -21
- package/src/client/components/Sheet.tsx +0 -141
- package/src/client/components/Skeleton.tsx +0 -17
- package/src/client/components/TextInput.tsx +0 -58
- package/src/client/components/Tooltip.tsx +0 -216
- package/src/client/components/charts/useChartLayout.ts +0 -27
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -91
- package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
- package/src/client/pages/dev/ChartFixtures.tsx +0 -325
- package/src/client/pages/dev/ComponentsPage.tsx +0 -899
- package/src/client/pages/dev/ConversationFixture.tsx +0 -231
- package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
- package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
- package/src/client/pages/dev/SelectFixtures.tsx +0 -62
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
- package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
- package/src/client/pages/system/SnapshotSummary.tsx +0 -78
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
Bot,
|
|
4
|
+
CircleDot,
|
|
5
|
+
Eye,
|
|
6
|
+
FolderGit2,
|
|
7
|
+
GitPullRequest,
|
|
8
|
+
Hash,
|
|
9
|
+
LockKeyhole,
|
|
10
|
+
type LucideIcon,
|
|
11
|
+
Package,
|
|
12
|
+
Rocket,
|
|
13
|
+
Tag,
|
|
14
|
+
Timer,
|
|
15
|
+
Users,
|
|
16
|
+
} from "lucide-react";
|
|
17
|
+
import type { ReactNode } from "react";
|
|
18
|
+
import {
|
|
19
|
+
conversationWatchesReportSchema,
|
|
20
|
+
type ConversationWatch,
|
|
21
|
+
} from "@sentry/junior/api/schema";
|
|
22
|
+
|
|
23
|
+
import { formatTime } from "../format";
|
|
24
|
+
import { fetchDashboardJson } from "../http";
|
|
25
|
+
import { DetailsList, DetailsRow, DetailsRowLine } from "./DetailsList";
|
|
26
|
+
|
|
27
|
+
/** Load the active Watches that one Conversation owns. */
|
|
28
|
+
export function useConversationWatches(conversationId: string) {
|
|
29
|
+
return useQuery({
|
|
30
|
+
queryFn: ({ signal }) =>
|
|
31
|
+
fetchDashboardJson(
|
|
32
|
+
conversationWatchesReportSchema,
|
|
33
|
+
`/api/conversations/${encodeURIComponent(conversationId)}/watches`,
|
|
34
|
+
signal,
|
|
35
|
+
),
|
|
36
|
+
queryKey: ["conversation", conversationId, "watches"],
|
|
37
|
+
retry: false,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Show what can wake this Conversation: each active Watch with the resource,
|
|
43
|
+
* the reason, and the event types that deliver to it.
|
|
44
|
+
*/
|
|
45
|
+
export function ConversationWatches(props: { watches: ConversationWatch[] }) {
|
|
46
|
+
return (
|
|
47
|
+
<DetailsList
|
|
48
|
+
itemKey={(watch) => watch.id}
|
|
49
|
+
items={props.watches}
|
|
50
|
+
renderItem={(watch) => <WatchRow watch={watch} />}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const resourceIcons: Partial<Record<string, LucideIcon>> = {
|
|
56
|
+
channel: Hash,
|
|
57
|
+
deployment: Rocket,
|
|
58
|
+
deployment_source: Rocket,
|
|
59
|
+
issue: CircleDot,
|
|
60
|
+
pull_request: GitPullRequest,
|
|
61
|
+
release_source: Tag,
|
|
62
|
+
repository: FolderGit2,
|
|
63
|
+
team: Users,
|
|
64
|
+
timer: Timer,
|
|
65
|
+
workspace_snapshot: Package,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Show one Watch in up to three short lines: the resource and time left, the
|
|
70
|
+
* reason it exists, then the event types that wake the Conversation.
|
|
71
|
+
*/
|
|
72
|
+
function WatchRow(props: { watch: ConversationWatch }) {
|
|
73
|
+
const { watch } = props;
|
|
74
|
+
const Icon = watch.redacted
|
|
75
|
+
? LockKeyhole
|
|
76
|
+
: (resourceIcons[watch.resourceType] ?? Eye);
|
|
77
|
+
const isTimer = watch.firesAt !== undefined;
|
|
78
|
+
const until = watch.firesAt ?? watch.expiresAt;
|
|
79
|
+
const resource = `${watch.namespace} ${watch.resourceType.replaceAll("_", " ")}`;
|
|
80
|
+
// A timer has one event, and the time already says when it fires.
|
|
81
|
+
const events = isTimer ? [] : watch.events;
|
|
82
|
+
const filters = Object.entries(watch.match ?? {});
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<DetailsRow
|
|
86
|
+
icon={<Icon aria-hidden="true" className="size-3.5" />}
|
|
87
|
+
meta={
|
|
88
|
+
<time dateTime={until}>
|
|
89
|
+
{isTimer
|
|
90
|
+
? `in ${formatRemaining(until)}`
|
|
91
|
+
: `${formatRemaining(until)} left`}
|
|
92
|
+
</time>
|
|
93
|
+
}
|
|
94
|
+
title={watch.label ?? `Private ${resource}`}
|
|
95
|
+
tooltip={`${isTimer ? "Fires" : "Expires"} ${formatTime(until, {
|
|
96
|
+
dateStyle: "medium",
|
|
97
|
+
timeStyle: "short",
|
|
98
|
+
})}`}
|
|
99
|
+
>
|
|
100
|
+
{watch.intent ? (
|
|
101
|
+
<DetailsRowLine className="line-clamp-2 break-words">
|
|
102
|
+
{watch.intent}
|
|
103
|
+
</DetailsRowLine>
|
|
104
|
+
) : null}
|
|
105
|
+
{events.length > 0 || filters.length > 0 || watch.childAgent ? (
|
|
106
|
+
<DetailsRowLine className="flex flex-wrap items-center gap-1 pt-0.5">
|
|
107
|
+
{events.map((event) => (
|
|
108
|
+
<WatchChip key={event}>
|
|
109
|
+
{watchEventLabel(event, watch.resourceType)}
|
|
110
|
+
</WatchChip>
|
|
111
|
+
))}
|
|
112
|
+
{filters.map(([key, value]) => (
|
|
113
|
+
<WatchChip filter key={`match:${key}`}>
|
|
114
|
+
{key.replaceAll("_", " ")}: {[value].flat().join(", ")}
|
|
115
|
+
</WatchChip>
|
|
116
|
+
))}
|
|
117
|
+
{watch.childAgent ? (
|
|
118
|
+
<span className="inline-flex min-w-0 items-center gap-1 px-0.5">
|
|
119
|
+
<Bot aria-hidden="true" className="size-3 shrink-0" />
|
|
120
|
+
<span className="truncate">
|
|
121
|
+
via {watch.childAgent.name ?? "subagent"}
|
|
122
|
+
</span>
|
|
123
|
+
</span>
|
|
124
|
+
) : null}
|
|
125
|
+
</DetailsRowLine>
|
|
126
|
+
) : null}
|
|
127
|
+
</DetailsRow>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Label one event type, or a dashed match filter that narrows the events. */
|
|
132
|
+
function WatchChip(props: { children: ReactNode; filter?: boolean }) {
|
|
133
|
+
return (
|
|
134
|
+
<span
|
|
135
|
+
className={
|
|
136
|
+
props.filter
|
|
137
|
+
? "min-w-0 truncate rounded-md border border-dashed border-dashboard-border px-1.5 py-px text-xs leading-4 text-dashboard-text-muted"
|
|
138
|
+
: "min-w-0 truncate rounded-md bg-dashboard-fill-soft px-1.5 py-px text-xs leading-4 text-dashboard-text-subtle"
|
|
139
|
+
}
|
|
140
|
+
>
|
|
141
|
+
{props.children}
|
|
142
|
+
</span>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Turn an event type into a short trigger label. The row already names the
|
|
148
|
+
* resource, so `pull_request.checks.failed` reads as "checks failed".
|
|
149
|
+
*/
|
|
150
|
+
function watchEventLabel(event: string, resourceType: string): string {
|
|
151
|
+
const parts = event.split(".");
|
|
152
|
+
const rest =
|
|
153
|
+
parts[0] === resourceType && parts.length > 1 ? parts.slice(1) : parts;
|
|
154
|
+
return rest.join(" ").replaceAll("_", " ");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Format the time until a Watch fires or expires as one compact unit. */
|
|
158
|
+
function formatRemaining(value: string): string {
|
|
159
|
+
const ms = Math.max(0, Date.parse(value) - Date.now());
|
|
160
|
+
const minutes = Math.round(ms / 60_000);
|
|
161
|
+
if (minutes < 60) return `${Math.max(1, minutes)}m`;
|
|
162
|
+
const hours = Math.round(minutes / 60);
|
|
163
|
+
if (hours < 48) return `${hours}h`;
|
|
164
|
+
return `${Math.round(hours / 24)}d`;
|
|
165
|
+
}
|
|
@@ -5,8 +5,8 @@ import { useNavigate, useParams } from "react-router";
|
|
|
5
5
|
|
|
6
6
|
import { useConversationsData } from "../api";
|
|
7
7
|
import { ConversationSidebar } from "./ConversationSidebar";
|
|
8
|
-
import { ToggleButton } from "../components/Button";
|
|
9
8
|
import { SearchInput } from "../components/SearchInput";
|
|
9
|
+
import { ToggleGroup, ToggleGroupItem } from "../components/ui/toggle-group";
|
|
10
10
|
import { pageCount, pageItems, PagePagination } from "../components/Pagination";
|
|
11
11
|
import { ConversationHomeList } from "./ConversationHomeList";
|
|
12
12
|
import { ConversationComposer } from "./ConversationComposer";
|
|
@@ -171,7 +171,6 @@ function NewConversationView(props: {
|
|
|
171
171
|
): Promise<void>;
|
|
172
172
|
}) {
|
|
173
173
|
const [visibility, setVisibility] = useState<"private" | "public">("public");
|
|
174
|
-
const isPublic = visibility === "public";
|
|
175
174
|
|
|
176
175
|
return (
|
|
177
176
|
<section
|
|
@@ -185,32 +184,23 @@ function NewConversationView(props: {
|
|
|
185
184
|
draftId="new"
|
|
186
185
|
error={props.error}
|
|
187
186
|
footerStart={
|
|
188
|
-
<
|
|
187
|
+
<ToggleGroup
|
|
189
188
|
aria-label="Conversation visibility"
|
|
190
|
-
className="inline-flex shrink-0
|
|
191
|
-
|
|
189
|
+
className="inline-flex shrink-0 gap-0.5 rounded-lg border border-dashboard-border bg-dashboard-surface-panel p-0.5"
|
|
190
|
+
onValueChange={([next]) => {
|
|
191
|
+
if (next) setVisibility(next);
|
|
192
|
+
}}
|
|
193
|
+
value={[visibility]}
|
|
192
194
|
>
|
|
193
|
-
<
|
|
194
|
-
className="min-w-20 gap-1.5"
|
|
195
|
-
onClick={() => setVisibility("public")}
|
|
196
|
-
pressed={isPublic}
|
|
197
|
-
type="button"
|
|
198
|
-
variant="segment"
|
|
199
|
-
>
|
|
195
|
+
<ToggleGroupItem className="min-w-20 gap-1.5" value="public">
|
|
200
196
|
<Globe2 aria-hidden="true" className="size-3" />
|
|
201
197
|
Public
|
|
202
|
-
</
|
|
203
|
-
<
|
|
204
|
-
className="min-w-20 gap-1.5"
|
|
205
|
-
onClick={() => setVisibility("private")}
|
|
206
|
-
pressed={!isPublic}
|
|
207
|
-
type="button"
|
|
208
|
-
variant="segment"
|
|
209
|
-
>
|
|
198
|
+
</ToggleGroupItem>
|
|
199
|
+
<ToggleGroupItem className="min-w-20 gap-1.5" value="private">
|
|
210
200
|
<LockKeyhole aria-hidden="true" className="size-3" />
|
|
211
201
|
Private
|
|
212
|
-
</
|
|
213
|
-
</
|
|
202
|
+
</ToggleGroupItem>
|
|
203
|
+
</ToggleGroup>
|
|
214
204
|
}
|
|
215
205
|
label="Start a conversation"
|
|
216
206
|
restoreDraftOnError
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Skeleton } from "../components/
|
|
1
|
+
import { Skeleton } from "../components/ui/skeleton";
|
|
2
2
|
import { dashboardContainerClass, cn } from "../styles";
|
|
3
3
|
import { ChatLayout } from "./ChatLayout";
|
|
4
4
|
import { ConversationHomeListLoading } from "./ConversationHomeList";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ChevronRight } from "lucide-react";
|
|
2
|
+
import { useState, type ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn, dashboardDisclosureChevronClass } from "../styles";
|
|
5
|
+
|
|
6
|
+
/** Rows a details list shows before the reader asks for the rest. */
|
|
7
|
+
const COLLAPSED_ROW_COUNT = 3;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Show rows in a details card. Rows are split by thin rules. A list with more
|
|
11
|
+
* than three rows shows the first three and a row that shows the rest.
|
|
12
|
+
*/
|
|
13
|
+
export function DetailsList<T>(props: {
|
|
14
|
+
items: readonly T[];
|
|
15
|
+
itemKey(item: T): string;
|
|
16
|
+
renderItem(item: T): ReactNode;
|
|
17
|
+
}) {
|
|
18
|
+
const [expanded, setExpanded] = useState(false);
|
|
19
|
+
const hidden = props.items.length - COLLAPSED_ROW_COUNT;
|
|
20
|
+
const visible =
|
|
21
|
+
expanded || hidden <= 0
|
|
22
|
+
? props.items
|
|
23
|
+
: props.items.slice(0, COLLAPSED_ROW_COUNT);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
// Rows own their padding, so hover fills reach the rules. The negative
|
|
27
|
+
// margin keeps the first and last text on the card padding.
|
|
28
|
+
<ul className="-my-2.5 grid list-none p-0">
|
|
29
|
+
{visible.map((item) => (
|
|
30
|
+
<li
|
|
31
|
+
className="border-t border-dashboard-border-subtle first:border-t-0"
|
|
32
|
+
key={props.itemKey(item)}
|
|
33
|
+
>
|
|
34
|
+
{props.renderItem(item)}
|
|
35
|
+
</li>
|
|
36
|
+
))}
|
|
37
|
+
{hidden > 0 ? (
|
|
38
|
+
<li className="border-t border-dashboard-border-subtle">
|
|
39
|
+
<button
|
|
40
|
+
aria-expanded={expanded}
|
|
41
|
+
className="group -mx-2 flex min-h-8 items-center gap-2.5 rounded-md px-2 py-2 text-left text-xs font-medium leading-4 text-dashboard-text-muted transition-colors hover:bg-dashboard-surface-hover hover:text-dashboard-text focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus"
|
|
42
|
+
onClick={() => setExpanded((value) => !value)}
|
|
43
|
+
type="button"
|
|
44
|
+
>
|
|
45
|
+
<span className="grid size-4 shrink-0 place-items-center">
|
|
46
|
+
<ChevronRight
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
className={dashboardDisclosureChevronClass}
|
|
49
|
+
/>
|
|
50
|
+
</span>
|
|
51
|
+
{expanded ? "Show fewer" : `Show ${hidden} more`}
|
|
52
|
+
</button>
|
|
53
|
+
</li>
|
|
54
|
+
) : null}
|
|
55
|
+
</ul>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Lay out one details list row: an icon, a title, and short trailing text on
|
|
61
|
+
* the first line, then optional lines under the title. A row with `href`
|
|
62
|
+
* opens that link in a new tab.
|
|
63
|
+
*/
|
|
64
|
+
export function DetailsRow(props: {
|
|
65
|
+
children?: ReactNode;
|
|
66
|
+
href?: string;
|
|
67
|
+
icon: ReactNode;
|
|
68
|
+
meta?: ReactNode;
|
|
69
|
+
title: ReactNode;
|
|
70
|
+
/** Native hover text with detail that the row does not show. */
|
|
71
|
+
tooltip?: string;
|
|
72
|
+
}) {
|
|
73
|
+
const className = cn(
|
|
74
|
+
"-mx-2 grid min-w-0 grid-cols-[auto_minmax(0,1fr)_auto] items-start gap-x-2.5 gap-y-1 rounded-md px-2 py-2.5 text-inherit no-underline",
|
|
75
|
+
props.href &&
|
|
76
|
+
"group transition-colors hover:bg-dashboard-surface-hover focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus",
|
|
77
|
+
);
|
|
78
|
+
const content = (
|
|
79
|
+
<>
|
|
80
|
+
<span className="grid size-5 shrink-0 place-items-center text-dashboard-text-muted">
|
|
81
|
+
{props.icon}
|
|
82
|
+
</span>
|
|
83
|
+
<span className="min-w-0 truncate text-sm font-medium leading-5 text-dashboard-text group-hover:underline">
|
|
84
|
+
{props.title}
|
|
85
|
+
</span>
|
|
86
|
+
<span className="shrink-0 text-xs leading-5 tabular-nums text-dashboard-text-muted">
|
|
87
|
+
{props.meta}
|
|
88
|
+
</span>
|
|
89
|
+
{props.children}
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
return props.href ? (
|
|
93
|
+
<a
|
|
94
|
+
className={className}
|
|
95
|
+
href={props.href}
|
|
96
|
+
rel="noreferrer"
|
|
97
|
+
target="_blank"
|
|
98
|
+
title={props.tooltip}
|
|
99
|
+
>
|
|
100
|
+
{content}
|
|
101
|
+
</a>
|
|
102
|
+
) : (
|
|
103
|
+
<div className={className} title={props.tooltip}>
|
|
104
|
+
{content}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Place one extra line under the title of a `DetailsRow`. */
|
|
110
|
+
export function DetailsRowLine(props: {
|
|
111
|
+
children: ReactNode;
|
|
112
|
+
className?: string;
|
|
113
|
+
}) {
|
|
114
|
+
return (
|
|
115
|
+
<div
|
|
116
|
+
className={cn(
|
|
117
|
+
"col-start-2 col-end-4 min-w-0 text-xs leading-4 text-dashboard-text-muted",
|
|
118
|
+
props.className,
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{props.children}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { ConversationReportEvent } from "@sentry/junior/api/schema";
|
|
2
|
-
import
|
|
2
|
+
import { ChevronRight } from "lucide-react";
|
|
3
|
+
import { useState, type ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { HighlightedCode } from "../code";
|
|
6
|
+
import { CopyButton } from "../components/CopyButton";
|
|
7
|
+
import {
|
|
8
|
+
Collapsible,
|
|
9
|
+
CollapsibleContent,
|
|
10
|
+
CollapsibleTrigger,
|
|
11
|
+
} from "../components/ui/collapsible";
|
|
12
|
+
import { dashboardDisclosureChevronClass } from "../styles";
|
|
3
13
|
|
|
4
14
|
import { RedactedMarker } from "./TranscriptRedacted";
|
|
5
15
|
import { TranscriptText } from "./TranscriptText";
|
|
@@ -178,14 +188,38 @@ export function EventDetails({ event }: { event: ConversationReportEvent }) {
|
|
|
178
188
|
</DetailSection>
|
|
179
189
|
) : null}
|
|
180
190
|
{content}
|
|
181
|
-
<
|
|
182
|
-
|
|
191
|
+
<RawEventJson event={event} />
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function RawEventJson({ event }: { event: ConversationReportEvent }) {
|
|
197
|
+
const [open, setOpen] = useState(false);
|
|
198
|
+
const json = open ? JSON.stringify(event, null, 2) : "";
|
|
199
|
+
return (
|
|
200
|
+
<div className="relative min-w-0 border-t border-dashboard-border pt-4">
|
|
201
|
+
<Collapsible onOpenChange={setOpen} open={open}>
|
|
202
|
+
<CollapsibleTrigger className="group flex min-h-9 cursor-pointer items-center gap-1 pr-12 text-xs text-dashboard-text-muted hover:text-dashboard-text focus-visible:outline-2 focus-visible:outline-dashboard-focus">
|
|
203
|
+
<ChevronRight
|
|
204
|
+
aria-hidden="true"
|
|
205
|
+
className={dashboardDisclosureChevronClass}
|
|
206
|
+
/>
|
|
183
207
|
Raw JSON
|
|
184
|
-
</
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
</CollapsibleTrigger>
|
|
209
|
+
<CollapsibleContent>
|
|
210
|
+
<div className="absolute right-0 top-4">
|
|
211
|
+
<CopyButton getText={() => json} label="Copy JSON" />
|
|
212
|
+
</div>
|
|
213
|
+
<div
|
|
214
|
+
aria-label="Raw event JSON"
|
|
215
|
+
className="mt-3 max-h-80 min-w-0 overflow-auto overscroll-contain rounded-lg border border-dashboard-border bg-dashboard-surface-panel p-3 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus"
|
|
216
|
+
role="region"
|
|
217
|
+
tabIndex={0}
|
|
218
|
+
>
|
|
219
|
+
<HighlightedCode code={json} language="json" />
|
|
220
|
+
</div>
|
|
221
|
+
</CollapsibleContent>
|
|
222
|
+
</Collapsible>
|
|
189
223
|
</div>
|
|
190
224
|
);
|
|
191
225
|
}
|
|
@@ -2,10 +2,15 @@ import { GitFork } from "lucide-react";
|
|
|
2
2
|
import { useId, useState } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { useNavigate } from "react-router";
|
|
5
|
-
import { Button } from "../components/
|
|
5
|
+
import { Button } from "../components/ui/button";
|
|
6
|
+
import { Textarea } from "../components/ui/textarea";
|
|
7
|
+
import { Field, FieldLabel } from "../components/ui/field";
|
|
8
|
+
import {
|
|
9
|
+
Tooltip,
|
|
10
|
+
TooltipContent,
|
|
11
|
+
TooltipTrigger,
|
|
12
|
+
} from "../components/ui/tooltip";
|
|
6
13
|
import { Drawer } from "../components/Drawer";
|
|
7
|
-
import { Field } from "../components/Field";
|
|
8
|
-
import { TextArea } from "../components/TextInput";
|
|
9
14
|
import { DashboardApiError } from "../http";
|
|
10
15
|
import { conversationPath } from "./conversationRoutes";
|
|
11
16
|
import { useForkConversation } from "./queries";
|
|
@@ -17,14 +22,20 @@ export function ForkConversationButton(props: ForkTarget) {
|
|
|
17
22
|
const [open, setOpen] = useState(false);
|
|
18
23
|
return (
|
|
19
24
|
<>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
<Tooltip>
|
|
26
|
+
<TooltipTrigger
|
|
27
|
+
render={
|
|
28
|
+
<Button
|
|
29
|
+
size="icon"
|
|
30
|
+
aria-label="Fork after this message"
|
|
31
|
+
onClick={() => setOpen(true)}
|
|
32
|
+
>
|
|
33
|
+
<GitFork size={14} aria-hidden="true" />
|
|
34
|
+
</Button>
|
|
35
|
+
}
|
|
36
|
+
/>
|
|
37
|
+
<TooltipContent size="compact">Fork after this message</TooltipContent>
|
|
38
|
+
</Tooltip>
|
|
28
39
|
{open
|
|
29
40
|
? createPortal(
|
|
30
41
|
<ForkConversationDialog
|
|
@@ -82,12 +93,12 @@ function ForkConversationDialog(props: ForkTarget & { onClose(): void }) {
|
|
|
82
93
|
<p className="m-0">
|
|
83
94
|
Creates a fork of the conversation from this point.
|
|
84
95
|
</p>
|
|
85
|
-
<Field
|
|
86
|
-
<
|
|
96
|
+
<Field>
|
|
97
|
+
<FieldLabel htmlFor={inputId}>Message</FieldLabel>
|
|
98
|
+
<Textarea
|
|
87
99
|
id={inputId}
|
|
88
|
-
prose
|
|
89
100
|
autoFocus
|
|
90
|
-
className="min-h-28"
|
|
101
|
+
className="min-h-28 leading-relaxed"
|
|
91
102
|
placeholder="Message Junior…"
|
|
92
103
|
value={message}
|
|
93
104
|
disabled={fork.isPending}
|
|
@@ -112,7 +123,7 @@ function ForkConversationDialog(props: ForkTarget & { onClose(): void }) {
|
|
|
112
123
|
</p>
|
|
113
124
|
) : null}
|
|
114
125
|
<Button
|
|
115
|
-
|
|
126
|
+
variant="primary"
|
|
116
127
|
type="submit"
|
|
117
128
|
disabled={!message.trim() || fork.isPending}
|
|
118
129
|
>
|
|
@@ -3,10 +3,16 @@ import {
|
|
|
3
3
|
objectFactFields,
|
|
4
4
|
objectPresentation,
|
|
5
5
|
} from "@sentry/junior-plugin-api";
|
|
6
|
-
import { ArrowUpRight } from "lucide-react";
|
|
6
|
+
import { ArrowUpRight, ChevronRight } from "lucide-react";
|
|
7
7
|
import { ObjectIcon } from "../components/ObjectIcon";
|
|
8
8
|
import { AutomationCard } from "../components/AutomationCard";
|
|
9
9
|
import { StatusChip } from "../components/StatusChip";
|
|
10
|
+
import {
|
|
11
|
+
Collapsible,
|
|
12
|
+
CollapsibleContent,
|
|
13
|
+
CollapsibleTrigger,
|
|
14
|
+
} from "../components/ui/collapsible";
|
|
15
|
+
import { dashboardDisclosureChevronClass } from "../styles";
|
|
10
16
|
|
|
11
17
|
/** Show the latest saved annotation. Opening a source is always explicit. */
|
|
12
18
|
export function ObjectCard({ card }: { card: OwnedObjectAnnotation }) {
|
|
@@ -78,14 +84,20 @@ export function ObjectCard({ card }: { card: OwnedObjectAnnotation }) {
|
|
|
78
84
|
)}
|
|
79
85
|
{fields.length > 0 && facts(fields)}
|
|
80
86
|
{card.description && (
|
|
81
|
-
<
|
|
82
|
-
<
|
|
87
|
+
<Collapsible className="min-w-0 border-t border-dashboard-border pt-3">
|
|
88
|
+
<CollapsibleTrigger className="group flex cursor-pointer items-center gap-1 text-xs font-medium text-dashboard-text-muted focus-visible:outline-dashboard-focus">
|
|
89
|
+
<ChevronRight
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
className={dashboardDisclosureChevronClass}
|
|
92
|
+
/>
|
|
83
93
|
More details
|
|
84
|
-
</
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
</CollapsibleTrigger>
|
|
95
|
+
<CollapsibleContent hiddenUntilFound>
|
|
96
|
+
<p className="mb-0 mt-3 whitespace-pre-wrap break-words text-sm text-dashboard-text-muted">
|
|
97
|
+
{card.description}
|
|
98
|
+
</p>
|
|
99
|
+
</CollapsibleContent>
|
|
100
|
+
</Collapsible>
|
|
89
101
|
)}
|
|
90
102
|
{card.warning && (
|
|
91
103
|
<p className="m-0 break-words text-xs text-amber-500">
|
|
@@ -15,7 +15,11 @@ import {
|
|
|
15
15
|
type LucideIcon,
|
|
16
16
|
} from "lucide-react";
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Tooltip,
|
|
20
|
+
TooltipContent,
|
|
21
|
+
TooltipTrigger,
|
|
22
|
+
} from "../components/ui/tooltip";
|
|
19
23
|
import { actorLabel } from "../format";
|
|
20
24
|
import type { ConversationMailboxMessage } from "./conversationOutbox";
|
|
21
25
|
import {
|
|
@@ -44,13 +48,18 @@ function pendingDeliveryMeta(
|
|
|
44
48
|
|
|
45
49
|
function PendingMetaIcon(props: { children: ReactElement; label: string }) {
|
|
46
50
|
return (
|
|
47
|
-
<Tooltip
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
<Tooltip>
|
|
52
|
+
<TooltipTrigger
|
|
53
|
+
render={
|
|
54
|
+
<span
|
|
55
|
+
aria-label={props.label}
|
|
56
|
+
className="inline-flex text-dashboard-text-muted"
|
|
57
|
+
>
|
|
58
|
+
{props.children}
|
|
59
|
+
</span>
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
<TooltipContent>{props.label}</TooltipContent>
|
|
54
63
|
</Tooltip>
|
|
55
64
|
);
|
|
56
65
|
}
|
|
@@ -69,10 +78,15 @@ function PendingMetaIcons(props: {
|
|
|
69
78
|
return (
|
|
70
79
|
<TranscriptHeadingMeta className="flex min-w-0 items-center justify-end gap-1.5 text-xs leading-none text-dashboard-text-muted">
|
|
71
80
|
{props.showSlack ? (
|
|
72
|
-
<Tooltip
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
<Tooltip>
|
|
82
|
+
<TooltipTrigger
|
|
83
|
+
render={
|
|
84
|
+
<span className="inline-flex text-dashboard-text-muted">
|
|
85
|
+
<SlackMark className="size-3.5" />
|
|
86
|
+
</span>
|
|
87
|
+
}
|
|
88
|
+
/>
|
|
89
|
+
<TooltipContent>Slack</TooltipContent>
|
|
76
90
|
</Tooltip>
|
|
77
91
|
) : null}
|
|
78
92
|
<PendingMetaIcon label={delivery.label}>
|
|
@@ -84,28 +98,33 @@ function PendingMetaIcons(props: {
|
|
|
84
98
|
/>
|
|
85
99
|
</PendingMetaIcon>
|
|
86
100
|
{props.onCancel ? (
|
|
87
|
-
<Tooltip
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
<Tooltip>
|
|
102
|
+
<TooltipTrigger
|
|
103
|
+
render={
|
|
104
|
+
<button
|
|
105
|
+
aria-label={
|
|
106
|
+
props.cancelError
|
|
107
|
+
? "Could not remove. Try again."
|
|
108
|
+
: "Remove queued message"
|
|
109
|
+
}
|
|
110
|
+
className="inline-flex size-5 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-hover hover:text-amber-50 focus-visible:outline focus-visible:outline-1 focus-visible:outline-amber-200/55 disabled:cursor-default disabled:opacity-50"
|
|
111
|
+
disabled={props.cancelDisabled}
|
|
112
|
+
onClick={props.onCancel}
|
|
113
|
+
type="button"
|
|
114
|
+
>
|
|
115
|
+
{props.cancelPending ? (
|
|
116
|
+
<LoaderCircle
|
|
117
|
+
aria-hidden="true"
|
|
118
|
+
className="animate-spin"
|
|
119
|
+
size={13}
|
|
120
|
+
/>
|
|
121
|
+
) : (
|
|
122
|
+
<X aria-hidden="true" size={14} strokeWidth={2.2} />
|
|
123
|
+
)}
|
|
124
|
+
</button>
|
|
93
125
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
onClick={props.onCancel}
|
|
97
|
-
type="button"
|
|
98
|
-
>
|
|
99
|
-
{props.cancelPending ? (
|
|
100
|
-
<LoaderCircle
|
|
101
|
-
aria-hidden="true"
|
|
102
|
-
className="animate-spin"
|
|
103
|
-
size={13}
|
|
104
|
-
/>
|
|
105
|
-
) : (
|
|
106
|
-
<X aria-hidden="true" size={14} strokeWidth={2.2} />
|
|
107
|
-
)}
|
|
108
|
-
</button>
|
|
126
|
+
/>
|
|
127
|
+
<TooltipContent>Remove queued message</TooltipContent>
|
|
109
128
|
</Tooltip>
|
|
110
129
|
) : null}
|
|
111
130
|
</TranscriptHeadingMeta>
|