@sentry/junior-dashboard 0.279.0 → 0.280.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/dist/app.js +100 -31
- package/dist/client-manifest.json +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +100 -31
- package/dist/public/chunks/AdminPage-YEKZOKH6.js +1 -0
- package/dist/public/chunks/AutomationPage-5GS3IUAK.js +3 -0
- package/dist/public/chunks/AutomationRunsPage-IPYEZU4L.js +1 -0
- package/dist/public/chunks/AutomationsPage-HSAYPC3C.js +1 -0
- package/dist/public/chunks/CodePage-IPFKXWFB.js +1 -0
- package/dist/public/chunks/ConnectionsPage-5XUEK6FS.js +1 -0
- package/dist/public/chunks/DrawerContent-RYYS57ZH.js +1 -0
- package/dist/public/chunks/LocationDetailPage-27ETUGR6.js +1 -0
- package/dist/public/chunks/LocationsPage-WOKMQAGM.js +1 -0
- package/dist/public/chunks/McpSettingsPage-3RT7JPEK.js +1 -0
- package/dist/public/chunks/PeoplePage-6YQLDOUL.js +1 -0
- package/dist/public/chunks/PersonProfilePage-GYMAZFIK.js +1 -0
- package/dist/public/chunks/PersonalTokensPage-4BCR744B.js +1 -0
- package/dist/public/chunks/PluginUserPage-IP6WL7MD.js +1 -0
- package/dist/public/chunks/RepositoryPage-TRD44LQ3.js +1 -0
- package/dist/public/chunks/SettingsPage-4E3SLQ36.js +1 -0
- package/dist/public/chunks/SystemPage-C3XBKXKW.js +1 -0
- package/dist/public/chunks/WorkspaceFormPage-2LS2X67Q.js +2 -0
- package/dist/public/chunks/WorkspacesPage-5WB3CUTX.js +1 -0
- package/dist/public/chunks/chunk-2BCS5AQL.js +1 -0
- package/dist/public/chunks/chunk-3Y3ICT6K.js +1 -0
- package/dist/public/chunks/chunk-4AKUZW6R.js +1 -0
- package/dist/public/chunks/chunk-4UGZNVJM.js +1 -0
- package/dist/public/chunks/chunk-564V7YHG.js +1 -0
- package/dist/public/chunks/chunk-5GZDBGAR.js +1 -0
- package/dist/public/chunks/chunk-5ISD3QSC.js +1 -0
- package/dist/public/chunks/chunk-5LNGNJ3P.js +1 -0
- package/dist/public/chunks/chunk-5ZA674OU.js +1 -0
- package/dist/public/chunks/chunk-6FSJ3W2W.js +1 -0
- package/dist/public/chunks/chunk-6GQL3EVJ.js +1 -0
- package/dist/public/chunks/chunk-6PHJZPWD.js +19 -0
- package/dist/public/chunks/chunk-A2QWWA7O.js +41 -0
- package/dist/public/chunks/chunk-AYXSBMWA.js +1 -0
- package/dist/public/chunks/chunk-B5ZN3SWQ.js +14 -0
- package/dist/public/chunks/chunk-B6HGRBBX.js +1 -0
- package/dist/public/chunks/chunk-BKOACADP.js +1 -0
- package/dist/public/chunks/chunk-CQY6BYUB.js +1 -0
- package/dist/public/chunks/chunk-CR2ICLSH.js +1 -0
- package/dist/public/chunks/chunk-DZB4LPI6.js +1 -0
- package/dist/public/chunks/chunk-EFEREKX4.js +1 -0
- package/dist/public/chunks/chunk-FE522UEA.js +1 -0
- package/dist/public/chunks/chunk-FVX42XAD.js +1 -0
- package/dist/public/chunks/chunk-G5NWYJTV.js +1 -0
- package/dist/public/chunks/chunk-GMZIELFU.js +1 -0
- package/dist/public/chunks/chunk-GO45RQDL.js +1 -0
- package/dist/public/chunks/chunk-GOTQ4D6W.js +1 -0
- package/dist/public/chunks/chunk-GRMX2HX6.js +1 -0
- package/dist/public/chunks/chunk-GYRM5ZIV.js +1 -0
- package/dist/public/chunks/chunk-H2ZU34CP.js +1 -0
- package/dist/public/chunks/chunk-H76B43OQ.js +1 -0
- package/dist/public/chunks/chunk-HCLRG2EC.js +1 -0
- package/dist/public/chunks/chunk-IA6BJDKN.js +1 -0
- package/dist/public/chunks/chunk-IZWTFLCC.js +1 -0
- package/dist/public/chunks/chunk-J37MXUED.js +1 -0
- package/dist/public/chunks/chunk-JAZABOBA.js +0 -0
- package/dist/public/chunks/chunk-L2H3ZWZN.js +1 -0
- package/dist/public/chunks/chunk-LBELRO5P.js +1 -0
- package/dist/public/chunks/chunk-LKW7MULF.js +1 -0
- package/dist/public/chunks/chunk-MEHLQZDG.js +3 -0
- package/dist/public/chunks/chunk-MTBUV2GJ.js +1 -0
- package/dist/public/chunks/chunk-N4AONKD3.js +1 -0
- package/dist/public/chunks/chunk-NQMDED2R.js +2 -0
- package/dist/public/chunks/chunk-NQOJAZFQ.js +1 -0
- package/dist/public/chunks/chunk-PEQNT5MO.js +1 -0
- package/dist/public/chunks/chunk-PQPAPPEI.js +1 -0
- package/dist/public/chunks/chunk-PXHLJIOT.js +1 -0
- package/dist/public/chunks/chunk-QNA7F7QB.js +1 -0
- package/dist/public/chunks/chunk-QOX5RMLM.js +1 -0
- package/dist/public/chunks/chunk-QPBLZSNX.js +1 -0
- package/dist/public/chunks/chunk-R4ORRE77.js +1 -0
- package/dist/public/chunks/chunk-RHIXHBS5.js +1 -0
- package/dist/public/chunks/chunk-RHMZNAJX.js +1 -0
- package/dist/public/chunks/chunk-SDKV3OID.js +1 -0
- package/dist/public/chunks/chunk-SIOW7OOW.js +1 -0
- package/dist/public/chunks/chunk-SUX4QBVF.js +1 -0
- package/dist/public/chunks/chunk-T4M53QQT.js +1 -0
- package/dist/public/chunks/chunk-TFJOEF3Q.js +1 -0
- package/dist/public/chunks/chunk-UAH6DWNS.js +1 -0
- package/dist/public/chunks/chunk-UJD3MBMB.js +1 -0
- package/dist/public/chunks/chunk-UK2V4UBT.js +1 -0
- package/dist/public/chunks/chunk-UT6HZPGL.js +1 -0
- package/dist/public/chunks/chunk-UZ44J2LH.js +1 -0
- package/dist/public/chunks/chunk-V5UDSIRA.js +63 -0
- package/dist/public/chunks/chunk-VC6IS564.js +1 -0
- package/dist/public/chunks/chunk-VFEBNOPX.js +1 -0
- package/dist/public/chunks/chunk-VXT3FIVL.js +55 -0
- package/dist/public/chunks/chunk-WJ3OXMVX.js +1 -0
- package/dist/public/chunks/chunk-WRXMQB7X.js +25 -0
- package/dist/public/chunks/chunk-WZVO3HG4.js +1 -0
- package/dist/public/chunks/chunk-XNR4RZEC.js +1 -0
- package/dist/public/chunks/chunk-YPMUHXYT.js +147 -0
- package/dist/public/chunks/chunk-YYNMZ4WC.js +1 -0
- package/dist/public/chunks/chunk-Z2JPRGFT.js +1 -0
- package/dist/public/chunks/chunk-ZIJ6SJDN.js +1 -0
- package/dist/public/chunks/code-highlighter-Q7WC7AEF.js +12 -0
- package/dist/public/client-TYC7JKRE.js +91 -0
- package/dist/tailwind.css +1 -1
- package/package.json +5 -4
- package/src/api/schema.ts +1 -1
- package/src/assets.ts +4 -1
- package/src/client/App.tsx +100 -23
- package/src/client/TracedRoutes.tsx +8 -4
- package/src/client/code-highlighter.ts +26 -0
- package/src/client/code.tsx +2 -22
- package/src/client/components/Drawer.tsx +12 -71
- package/src/client/components/DrawerContent.tsx +76 -0
- package/src/client/components/ProfileMenu.tsx +12 -3
- package/src/client/components/UserInputForm.tsx +28 -5
- package/src/client/conversations/ConversationCatchUp.tsx +107 -0
- package/src/client/conversations/ConversationContextTags.tsx +110 -0
- package/src/client/conversations/ConversationEventLog.tsx +213 -71
- package/src/client/conversations/ConversationHeader.tsx +35 -41
- package/src/client/conversations/ConversationHeaderActions.tsx +34 -38
- package/src/client/conversations/ConversationHomeList.tsx +41 -3
- package/src/client/conversations/ConversationMeta.tsx +6 -12
- package/src/client/conversations/ConversationOverflowMenu.tsx +15 -4
- package/src/client/conversations/ConversationPage.tsx +188 -150
- package/src/client/conversations/ShareConversationButton.tsx +5 -50
- package/src/client/conversations/SlackMark.tsx +16 -4
- package/src/client/conversations/TranscriptRows.tsx +24 -11
- package/src/client/conversations/TranscriptView.tsx +21 -13
- package/src/client/conversations/conversationArchive.ts +22 -0
- package/src/client/conversations/conversationCatchUpState.ts +96 -0
- package/src/client/conversations/conversationMemory.ts +8 -1
- package/src/client/conversations/queries.ts +11 -3
- package/src/client/conversations/{transcriptUnreadHistory.ts → transcriptAroundHistory.ts} +48 -42
- package/src/client/conversations/transcriptBottomPinning.ts +27 -3
- package/src/client/conversations/transcriptReadPosition.ts +24 -2
- package/src/client/conversations/turnContext.ts +1 -1
- package/src/client/pages/admin/AdminPage.tsx +1 -1
- package/src/client/pages/admin/ServiceAccountsSection.tsx +1 -1
- package/src/client/pages/memory/memoryDashboard.ts +1 -1
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/settings/ConnectionsPage.tsx +1 -1
- package/src/client/pages/user/PluginUserPage.tsx +0 -6
- package/src/client/pages/user/pluginUserPageRoutes.ts +5 -0
- package/src/client/primaryNav.ts +1 -1
- package/src/client/review-desk/reviewItemFormat.ts +1 -1
- package/src/mock-reporting/fixtures.ts +43 -0
- package/src/mock-reporting/routes.ts +40 -3
- package/src/public-assets.ts +28 -20
- package/src/shell.ts +12 -2
- package/dist/public/client-ZCZXZBTL.js +0 -460
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.280.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@base-ui/react": "^1.9.0",
|
|
26
26
|
"@sentry/browser": "11.10.0",
|
|
27
27
|
"@tanstack/react-query": "^5.100.14",
|
|
28
|
+
"@tanstack/react-virtual": "^3.14.26",
|
|
28
29
|
"@uidotdev/usehooks": "2.4.1",
|
|
29
30
|
"class-variance-authority": "^0.7.1",
|
|
30
31
|
"cn": "^0.5.0",
|
|
@@ -41,10 +42,10 @@
|
|
|
41
42
|
"remark-gfm": "^4.0.1",
|
|
42
43
|
"shiki": "4.1.0",
|
|
43
44
|
"zod": "^4.5.4",
|
|
44
|
-
"@sentry/junior-plugin-api": "0.
|
|
45
|
+
"@sentry/junior-plugin-api": "0.280.0"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|
|
47
|
-
"@sentry/junior": "0.
|
|
48
|
+
"@sentry/junior": "0.280.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"tw-animate-css": "^1.4.0",
|
|
66
67
|
"typescript": "^7.0.2",
|
|
67
68
|
"vitest": "^4.1.12",
|
|
68
|
-
"@sentry/junior": "0.
|
|
69
|
+
"@sentry/junior": "0.280.0"
|
|
69
70
|
},
|
|
70
71
|
"scripts": {
|
|
71
72
|
"build": "pnpm --filter @sentry/junior-plugin-api build && pnpm --filter @sentry/junior build && pnpm run build:client && pnpm run build:css && pnpm run build:server",
|
package/src/api/schema.ts
CHANGED
package/src/assets.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DashboardClientManifest } from "./public-assets";
|
|
2
|
+
|
|
3
|
+
export const dashboardClientManifest: DashboardClientManifest | undefined =
|
|
4
|
+
undefined;
|
|
2
5
|
export const dashboardPublicAssets: Readonly<Record<string, string>> = {};
|
|
3
6
|
export const dashboardTailwindAsset = "";
|
|
4
7
|
export const dashboardAvatarHeaderAsset = "";
|
package/src/client/App.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
1
|
+
import { lazy, useEffect, useMemo, useState, type ReactNode } from "react";
|
|
2
2
|
import { Navigate, Route, useLocation, useParams } from "react-router";
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -36,41 +36,20 @@ import { ConversationWorkspaceLoading } from "./conversations/ConversationWorksp
|
|
|
36
36
|
import { useConversationData } from "./conversations/queries";
|
|
37
37
|
import { readConversationStartPosition } from "./conversations/transcriptReadPosition";
|
|
38
38
|
import { codeRepositoryTabs } from "./pages/code/codeRepositoryRoutes";
|
|
39
|
-
import { RepositoryPage } from "./pages/code/RepositoryPage";
|
|
40
|
-
import { CodePage } from "./pages/code/CodePage";
|
|
41
|
-
import { LocationDetailPage } from "./pages/locations/LocationDetailPage";
|
|
42
|
-
import { LocationsPage } from "./pages/locations/LocationsPage";
|
|
43
|
-
import { PeoplePage } from "./pages/people/PeoplePage";
|
|
44
|
-
import { PersonProfilePage } from "./pages/people/PersonProfilePage";
|
|
45
|
-
import { ConnectionsPage } from "./pages/settings/ConnectionsPage";
|
|
46
|
-
import { McpSettingsPage } from "./pages/settings/McpSettingsPage";
|
|
47
|
-
import { PersonalTokensPage } from "./pages/settings/PersonalTokensPage";
|
|
48
|
-
import { SettingsPage } from "./pages/settings/SettingsPage";
|
|
49
39
|
import { SettingsPageLayout } from "./pages/settings/SettingsPageLayout";
|
|
50
|
-
import { SystemPage } from "./pages/system/SystemPage";
|
|
51
40
|
import {
|
|
52
41
|
SystemPageLayout,
|
|
53
42
|
SystemRouteLoading,
|
|
54
43
|
} from "./pages/system/SystemPageLayout";
|
|
55
|
-
import { WorkspaceFormPage } from "./pages/system/WorkspaceFormPage";
|
|
56
|
-
import { WorkspacesPage } from "./pages/system/WorkspacesPage";
|
|
57
44
|
import {
|
|
58
45
|
isMemoryLibraryPath,
|
|
59
46
|
MemoryRouteLoading,
|
|
60
47
|
} from "./pages/memory/MemoryPageLayout";
|
|
61
|
-
import { AutomationPage } from "./pages/automations/AutomationPage";
|
|
62
|
-
import { AutomationRunsPage } from "./pages/automations/AutomationRunsPage";
|
|
63
|
-
import { AutomationsPage } from "./pages/automations/AutomationsPage";
|
|
64
48
|
import {
|
|
65
49
|
AutomationsPageLayout,
|
|
66
50
|
hasAutomationDetailsFrame,
|
|
67
51
|
AutomationsRouteLoading,
|
|
68
52
|
} from "./pages/automations/AutomationsPageLayout";
|
|
69
|
-
import {
|
|
70
|
-
MemoryPermalinkRoute,
|
|
71
|
-
PluginUserPageRoute,
|
|
72
|
-
} from "./pages/user/PluginUserPage";
|
|
73
|
-
import { AdminPage } from "./pages/admin/AdminPage";
|
|
74
53
|
import { buildPrimaryNavItems } from "./primaryNav";
|
|
75
54
|
import { TracedRoutes } from "./TracedRoutes";
|
|
76
55
|
import {
|
|
@@ -80,6 +59,104 @@ import {
|
|
|
80
59
|
import { useReviewItemCountData } from "./review-desk/queries";
|
|
81
60
|
import type { DashboardCoreData } from "./types";
|
|
82
61
|
|
|
62
|
+
// Pages outside the conversation workspace load on first visit, so the
|
|
63
|
+
// workspace's first paint skips their code and the chart library.
|
|
64
|
+
const RepositoryPage = lazy(() =>
|
|
65
|
+
import("./pages/code/RepositoryPage").then((module) => ({
|
|
66
|
+
default: module.RepositoryPage,
|
|
67
|
+
})),
|
|
68
|
+
);
|
|
69
|
+
const CodePage = lazy(() =>
|
|
70
|
+
import("./pages/code/CodePage").then((module) => ({
|
|
71
|
+
default: module.CodePage,
|
|
72
|
+
})),
|
|
73
|
+
);
|
|
74
|
+
const LocationDetailPage = lazy(() =>
|
|
75
|
+
import("./pages/locations/LocationDetailPage").then((module) => ({
|
|
76
|
+
default: module.LocationDetailPage,
|
|
77
|
+
})),
|
|
78
|
+
);
|
|
79
|
+
const LocationsPage = lazy(() =>
|
|
80
|
+
import("./pages/locations/LocationsPage").then((module) => ({
|
|
81
|
+
default: module.LocationsPage,
|
|
82
|
+
})),
|
|
83
|
+
);
|
|
84
|
+
const PeoplePage = lazy(() =>
|
|
85
|
+
import("./pages/people/PeoplePage").then((module) => ({
|
|
86
|
+
default: module.PeoplePage,
|
|
87
|
+
})),
|
|
88
|
+
);
|
|
89
|
+
const PersonProfilePage = lazy(() =>
|
|
90
|
+
import("./pages/people/PersonProfilePage").then((module) => ({
|
|
91
|
+
default: module.PersonProfilePage,
|
|
92
|
+
})),
|
|
93
|
+
);
|
|
94
|
+
const ConnectionsPage = lazy(() =>
|
|
95
|
+
import("./pages/settings/ConnectionsPage").then((module) => ({
|
|
96
|
+
default: module.ConnectionsPage,
|
|
97
|
+
})),
|
|
98
|
+
);
|
|
99
|
+
const McpSettingsPage = lazy(() =>
|
|
100
|
+
import("./pages/settings/McpSettingsPage").then((module) => ({
|
|
101
|
+
default: module.McpSettingsPage,
|
|
102
|
+
})),
|
|
103
|
+
);
|
|
104
|
+
const PersonalTokensPage = lazy(() =>
|
|
105
|
+
import("./pages/settings/PersonalTokensPage").then((module) => ({
|
|
106
|
+
default: module.PersonalTokensPage,
|
|
107
|
+
})),
|
|
108
|
+
);
|
|
109
|
+
const SettingsPage = lazy(() =>
|
|
110
|
+
import("./pages/settings/SettingsPage").then((module) => ({
|
|
111
|
+
default: module.SettingsPage,
|
|
112
|
+
})),
|
|
113
|
+
);
|
|
114
|
+
const SystemPage = lazy(() =>
|
|
115
|
+
import("./pages/system/SystemPage").then((module) => ({
|
|
116
|
+
default: module.SystemPage,
|
|
117
|
+
})),
|
|
118
|
+
);
|
|
119
|
+
const WorkspaceFormPage = lazy(() =>
|
|
120
|
+
import("./pages/system/WorkspaceFormPage").then((module) => ({
|
|
121
|
+
default: module.WorkspaceFormPage,
|
|
122
|
+
})),
|
|
123
|
+
);
|
|
124
|
+
const WorkspacesPage = lazy(() =>
|
|
125
|
+
import("./pages/system/WorkspacesPage").then((module) => ({
|
|
126
|
+
default: module.WorkspacesPage,
|
|
127
|
+
})),
|
|
128
|
+
);
|
|
129
|
+
const AutomationPage = lazy(() =>
|
|
130
|
+
import("./pages/automations/AutomationPage").then((module) => ({
|
|
131
|
+
default: module.AutomationPage,
|
|
132
|
+
})),
|
|
133
|
+
);
|
|
134
|
+
const AutomationRunsPage = lazy(() =>
|
|
135
|
+
import("./pages/automations/AutomationRunsPage").then((module) => ({
|
|
136
|
+
default: module.AutomationRunsPage,
|
|
137
|
+
})),
|
|
138
|
+
);
|
|
139
|
+
const AutomationsPage = lazy(() =>
|
|
140
|
+
import("./pages/automations/AutomationsPage").then((module) => ({
|
|
141
|
+
default: module.AutomationsPage,
|
|
142
|
+
})),
|
|
143
|
+
);
|
|
144
|
+
const MemoryPermalinkRoute = lazy(() =>
|
|
145
|
+
import("./pages/user/PluginUserPage").then((module) => ({
|
|
146
|
+
default: module.MemoryPermalinkRoute,
|
|
147
|
+
})),
|
|
148
|
+
);
|
|
149
|
+
const PluginUserPageRoute = lazy(() =>
|
|
150
|
+
import("./pages/user/PluginUserPage").then((module) => ({
|
|
151
|
+
default: module.PluginUserPageRoute,
|
|
152
|
+
})),
|
|
153
|
+
);
|
|
154
|
+
const AdminPage = lazy(() =>
|
|
155
|
+
import("./pages/admin/AdminPage").then((module) => ({
|
|
156
|
+
default: module.AdminPage,
|
|
157
|
+
})),
|
|
158
|
+
);
|
|
159
|
+
|
|
83
160
|
const dashboardNoise = {
|
|
84
161
|
backgroundImage:
|
|
85
162
|
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitchTiles'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.7'/%3E%3C/svg%3E\")",
|
|
@@ -295,7 +372,7 @@ export function DashboardShell() {
|
|
|
295
372
|
/>
|
|
296
373
|
}
|
|
297
374
|
/>
|
|
298
|
-
<TracedRoutes>
|
|
375
|
+
<TracedRoutes fallback={<LoadingView label="Loading page" />}>
|
|
299
376
|
<Route element={<LegacyAutomationsRedirect />} path="/tasks" />
|
|
300
377
|
<Route element={<LegacyAutomationsRedirect />} path="/tasks/*" />
|
|
301
378
|
<Route
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useLayoutEffect, type ReactNode } from "react";
|
|
1
|
+
import { Suspense, useLayoutEffect, type ReactNode } from "react";
|
|
2
2
|
import {
|
|
3
3
|
createRoutesFromChildren,
|
|
4
4
|
matchRoutes,
|
|
@@ -23,9 +23,13 @@ function routePattern(matches: RouteMatch[]): string {
|
|
|
23
23
|
/**
|
|
24
24
|
* Render `<Route>` children like `<Routes>`, and name the open pageload or
|
|
25
25
|
* navigation span after the route pattern that matched, such as
|
|
26
|
-
* `/conversations/:conversationId`.
|
|
26
|
+
* `/conversations/:conversationId`. Lazy pages show `fallback` while their
|
|
27
|
+
* code loads.
|
|
27
28
|
*/
|
|
28
|
-
export function TracedRoutes(props: {
|
|
29
|
+
export function TracedRoutes(props: {
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
fallback: ReactNode;
|
|
32
|
+
}) {
|
|
29
33
|
const location = useLocation();
|
|
30
34
|
const routes = createRoutesFromChildren(props.children);
|
|
31
35
|
const element = useRoutes(routes, location);
|
|
@@ -36,5 +40,5 @@ export function TracedRoutes(props: { children: ReactNode }) {
|
|
|
36
40
|
useLayoutEffect(() => {
|
|
37
41
|
if (pattern) nameRouteSpan(pattern);
|
|
38
42
|
}, [location.key, pattern]);
|
|
39
|
-
return element
|
|
43
|
+
return <Suspense fallback={props.fallback}>{element}</Suspense>;
|
|
40
44
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBundledHighlighter,
|
|
3
|
+
createSingletonShorthands,
|
|
4
|
+
guessEmbeddedLanguages,
|
|
5
|
+
} from "shiki/core";
|
|
6
|
+
|
|
7
|
+
import { dashboardCodeLanguages } from "./code-languages";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Highlight code with the dashboard languages and themes. Themes, grammars,
|
|
11
|
+
* and the engine load in on-demand chunks.
|
|
12
|
+
*/
|
|
13
|
+
export const { codeToHtml } = createSingletonShorthands(
|
|
14
|
+
createBundledHighlighter({
|
|
15
|
+
langs: dashboardCodeLanguages,
|
|
16
|
+
themes: {
|
|
17
|
+
"github-dark": () => import("shiki/themes/github-dark.mjs"),
|
|
18
|
+
"github-light": () => import("shiki/themes/github-light.mjs"),
|
|
19
|
+
},
|
|
20
|
+
engine: async () => {
|
|
21
|
+
const { createOnigurumaEngine } = await import("shiki/engine/oniguruma");
|
|
22
|
+
return createOnigurumaEngine(import("shiki/wasm"));
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
{ guessEmbeddedLanguages },
|
|
26
|
+
);
|
package/src/client/code.tsx
CHANGED
|
@@ -1,35 +1,13 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import type { BundledLanguage } from "shiki/bundle/web";
|
|
4
|
-
import {
|
|
5
|
-
createBundledHighlighter,
|
|
6
|
-
createSingletonShorthands,
|
|
7
|
-
guessEmbeddedLanguages,
|
|
8
|
-
} from "shiki/core";
|
|
9
4
|
|
|
10
5
|
import { cn } from "./styles";
|
|
11
|
-
import { dashboardCodeLanguages } from "./code-languages";
|
|
12
6
|
import {
|
|
13
7
|
buildSearchDecorations,
|
|
14
8
|
useTranscriptSearch,
|
|
15
9
|
} from "./conversations/transcriptSearch";
|
|
16
10
|
|
|
17
|
-
// Split themes, language grammars, and the engine into on-demand chunks.
|
|
18
|
-
const { codeToHtml } = createSingletonShorthands(
|
|
19
|
-
createBundledHighlighter({
|
|
20
|
-
langs: dashboardCodeLanguages,
|
|
21
|
-
themes: {
|
|
22
|
-
"github-dark": () => import("shiki/themes/github-dark.mjs"),
|
|
23
|
-
"github-light": () => import("shiki/themes/github-light.mjs"),
|
|
24
|
-
},
|
|
25
|
-
engine: async () => {
|
|
26
|
-
const { createOnigurumaEngine } = await import("shiki/engine/oniguruma");
|
|
27
|
-
return createOnigurumaEngine(import("shiki/wasm"));
|
|
28
|
-
},
|
|
29
|
-
}),
|
|
30
|
-
{ guessEmbeddedLanguages },
|
|
31
|
-
);
|
|
32
|
-
|
|
33
11
|
declare const shikiHtmlBrand: unique symbol;
|
|
34
12
|
|
|
35
13
|
/** HTML generated by Shiki and safe to pass to the highlighted code shell. */
|
|
@@ -51,6 +29,8 @@ export function HighlightedCode(props: {
|
|
|
51
29
|
const decorations = normalizedQuery
|
|
52
30
|
? buildSearchDecorations(props.code, normalizedQuery)
|
|
53
31
|
: [];
|
|
32
|
+
// Shiki loads on the first code block, not with the dashboard entry.
|
|
33
|
+
const { codeToHtml } = await import("./code-highlighter");
|
|
54
34
|
return (await codeToHtml(props.code, {
|
|
55
35
|
decorations: decorations.length ? decorations : undefined,
|
|
56
36
|
lang: props.language,
|
|
@@ -1,82 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useRef, type ReactNode } from "react";
|
|
1
|
+
import { lazy, Suspense } from "react";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import { Button } from "./ui/button";
|
|
6
|
-
import {
|
|
7
|
-
Drawer as DrawerRoot,
|
|
8
|
-
DrawerClose,
|
|
9
|
-
DrawerContent as DrawerPopup,
|
|
10
|
-
} from "./ui/drawer";
|
|
11
|
-
import { useSheet } from "./ui/useSheet";
|
|
3
|
+
import type { DrawerProps } from "./DrawerContent";
|
|
12
4
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
onClose(): void;
|
|
21
|
-
openKey: string;
|
|
22
|
-
width?: "default" | "wide" | "narrow";
|
|
23
|
-
} & ({ titleId: string; label?: never } | { label: string; titleId?: never });
|
|
5
|
+
// A drawer opens only after a click, so base-ui's drawer loads with the first
|
|
6
|
+
// open instead of with the page.
|
|
7
|
+
const DrawerContent = lazy(() =>
|
|
8
|
+
import("./DrawerContent").then((module) => ({
|
|
9
|
+
default: module.DrawerContent,
|
|
10
|
+
})),
|
|
11
|
+
);
|
|
24
12
|
|
|
25
13
|
/**
|
|
26
14
|
* Render a side drawer with shared modal focus and dismissal behavior.
|
|
27
15
|
* A drawer opened from inside another drawer stacks in front of it.
|
|
28
16
|
*/
|
|
29
17
|
export function Drawer(props: DrawerProps) {
|
|
30
|
-
return <DrawerContent key={props.openKey} {...props} />;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function DrawerContent(props: DrawerProps) {
|
|
34
|
-
const closeRef = useRef<HTMLButtonElement>(null);
|
|
35
|
-
const sheet = useSheet(props.onClose, closeRef);
|
|
36
18
|
return (
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
aria-label={props.label}
|
|
41
|
-
aria-labelledby={props.titleId}
|
|
42
|
-
data-dashboard-modal=""
|
|
43
|
-
className={cn(
|
|
44
|
-
"data-[swipe-axis=x]:[--drawer-content-width:100%] data-[swipe-axis=x]:sm:[--drawer-content-width:100%]",
|
|
45
|
-
props.width === "wide"
|
|
46
|
-
? "data-[swipe-axis=x]:md:[--drawer-content-width:min(760px,94vw)]"
|
|
47
|
-
: props.width === "narrow"
|
|
48
|
-
? "data-[swipe-axis=x]:md:[--drawer-content-width:min(440px,94vw)]"
|
|
49
|
-
: "data-[swipe-axis=x]:md:[--drawer-content-width:min(560px,94vw)]",
|
|
50
|
-
)}
|
|
51
|
-
>
|
|
52
|
-
<header className="flex shrink-0 items-start gap-3 border-b border-dashboard-border bg-dashboard-bg-elevated px-4 py-3 pt-[max(0.75rem,env(safe-area-inset-top))] md:px-5">
|
|
53
|
-
<div className="min-w-0 flex-1">{props.header}</div>
|
|
54
|
-
<div className="flex shrink-0 items-start gap-1.5">
|
|
55
|
-
{props.actions}
|
|
56
|
-
<DrawerClose
|
|
57
|
-
render={
|
|
58
|
-
<Button
|
|
59
|
-
ref={closeRef}
|
|
60
|
-
aria-label={props.closeLabel}
|
|
61
|
-
size="icon"
|
|
62
|
-
/>
|
|
63
|
-
}
|
|
64
|
-
>
|
|
65
|
-
<X aria-hidden="true" size={15} strokeWidth={2.25} />
|
|
66
|
-
</DrawerClose>
|
|
67
|
-
</div>
|
|
68
|
-
</header>
|
|
69
|
-
<div
|
|
70
|
-
className={cn(
|
|
71
|
-
"min-h-0 flex-1 overflow-auto",
|
|
72
|
-
props.flush
|
|
73
|
-
? "pb-[env(safe-area-inset-bottom)]"
|
|
74
|
-
: "px-4 py-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-5",
|
|
75
|
-
)}
|
|
76
|
-
>
|
|
77
|
-
{props.children}
|
|
78
|
-
</div>
|
|
79
|
-
</DrawerPopup>
|
|
80
|
-
</DrawerRoot>
|
|
19
|
+
<Suspense fallback={null}>
|
|
20
|
+
<DrawerContent key={props.openKey} {...props} />
|
|
21
|
+
</Suspense>
|
|
81
22
|
);
|
|
82
23
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { X } from "lucide-react";
|
|
2
|
+
import { useRef, type ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../styles";
|
|
5
|
+
import { Button } from "./ui/button";
|
|
6
|
+
import {
|
|
7
|
+
Drawer as DrawerRoot,
|
|
8
|
+
DrawerClose,
|
|
9
|
+
DrawerContent as DrawerPopup,
|
|
10
|
+
} from "./ui/drawer";
|
|
11
|
+
import { useSheet } from "./ui/useSheet";
|
|
12
|
+
|
|
13
|
+
/** Props of a side drawer. */
|
|
14
|
+
export type DrawerProps = {
|
|
15
|
+
actions?: ReactNode;
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
closeLabel: string;
|
|
18
|
+
/** Remove the body inset, so content such as the event log meets the edges. */
|
|
19
|
+
flush?: boolean;
|
|
20
|
+
header: ReactNode;
|
|
21
|
+
onClose(): void;
|
|
22
|
+
openKey: string;
|
|
23
|
+
width?: "default" | "wide" | "narrow";
|
|
24
|
+
} & ({ titleId: string; label?: never } | { label: string; titleId?: never });
|
|
25
|
+
|
|
26
|
+
/** Render the open side drawer. `Drawer` loads this module on demand. */
|
|
27
|
+
export function DrawerContent(props: DrawerProps) {
|
|
28
|
+
const closeRef = useRef<HTMLButtonElement>(null);
|
|
29
|
+
const sheet = useSheet(props.onClose, closeRef);
|
|
30
|
+
return (
|
|
31
|
+
<DrawerRoot {...sheet.rootProps} swipeDirection="right">
|
|
32
|
+
<DrawerPopup
|
|
33
|
+
{...sheet.contentProps}
|
|
34
|
+
aria-label={props.label}
|
|
35
|
+
aria-labelledby={props.titleId}
|
|
36
|
+
data-dashboard-modal=""
|
|
37
|
+
className={cn(
|
|
38
|
+
"data-[swipe-axis=x]:[--drawer-content-width:100%] data-[swipe-axis=x]:sm:[--drawer-content-width:100%]",
|
|
39
|
+
props.width === "wide"
|
|
40
|
+
? "data-[swipe-axis=x]:md:[--drawer-content-width:min(760px,94vw)]"
|
|
41
|
+
: props.width === "narrow"
|
|
42
|
+
? "data-[swipe-axis=x]:md:[--drawer-content-width:min(440px,94vw)]"
|
|
43
|
+
: "data-[swipe-axis=x]:md:[--drawer-content-width:min(560px,94vw)]",
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
<header className="flex shrink-0 items-start gap-3 border-b border-dashboard-border bg-dashboard-bg-elevated px-4 py-3 pt-[max(0.75rem,env(safe-area-inset-top))] md:px-5">
|
|
47
|
+
<div className="min-w-0 flex-1">{props.header}</div>
|
|
48
|
+
<div className="flex shrink-0 items-start gap-1.5">
|
|
49
|
+
{props.actions}
|
|
50
|
+
<DrawerClose
|
|
51
|
+
render={
|
|
52
|
+
<Button
|
|
53
|
+
ref={closeRef}
|
|
54
|
+
aria-label={props.closeLabel}
|
|
55
|
+
size="icon"
|
|
56
|
+
/>
|
|
57
|
+
}
|
|
58
|
+
>
|
|
59
|
+
<X aria-hidden="true" size={15} strokeWidth={2.25} />
|
|
60
|
+
</DrawerClose>
|
|
61
|
+
</div>
|
|
62
|
+
</header>
|
|
63
|
+
<div
|
|
64
|
+
className={cn(
|
|
65
|
+
"min-h-0 flex-1 overflow-auto",
|
|
66
|
+
props.flush
|
|
67
|
+
? "pb-[env(safe-area-inset-bottom)]"
|
|
68
|
+
: "px-4 py-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-5",
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{props.children}
|
|
72
|
+
</div>
|
|
73
|
+
</DrawerPopup>
|
|
74
|
+
</DrawerRoot>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -12,7 +12,7 @@ import type { PersonalSpendReport } from "@sentry/junior/api/schema";
|
|
|
12
12
|
import type { PluginUserPageLink } from "@sentry/junior-plugin-api";
|
|
13
13
|
|
|
14
14
|
import { formatCostSummary, peoplePath } from "../format";
|
|
15
|
-
import { pluginUserPagePath } from "../pages/user/
|
|
15
|
+
import { pluginUserPagePath } from "../pages/user/pluginUserPageRoutes";
|
|
16
16
|
import { cn, dashboardInteractiveTextClass } from "../styles";
|
|
17
17
|
import type { Identity } from "../types";
|
|
18
18
|
import { ThemeSwitcher } from "./ThemeSwitcher";
|
|
@@ -170,7 +170,11 @@ export function ProfileMenu({
|
|
|
170
170
|
) : null}
|
|
171
171
|
</div>
|
|
172
172
|
</div>
|
|
173
|
-
<div
|
|
173
|
+
<div
|
|
174
|
+
aria-label="Personal model spend"
|
|
175
|
+
className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 font-sans text-xs tabular-nums text-dashboard-text-muted"
|
|
176
|
+
role="group"
|
|
177
|
+
>
|
|
174
178
|
<span>
|
|
175
179
|
7d{" "}
|
|
176
180
|
<span className="font-semibold text-dashboard-text">
|
|
@@ -320,6 +324,7 @@ export function ProfileMenu({
|
|
|
320
324
|
}
|
|
321
325
|
|
|
322
326
|
const topBar = variant === "top-bar";
|
|
327
|
+
const menuLabel = `${open ? "Close" : "Open"} profile menu for ${name}`;
|
|
323
328
|
return (
|
|
324
329
|
<Popover
|
|
325
330
|
onOpenChange={(nextOpen, details) => {
|
|
@@ -335,7 +340,11 @@ export function ProfileMenu({
|
|
|
335
340
|
open={open}
|
|
336
341
|
>
|
|
337
342
|
<PopoverTrigger
|
|
338
|
-
aria-label={
|
|
343
|
+
aria-label={
|
|
344
|
+
topBar
|
|
345
|
+
? `${menuLabel}. Personal model spend: 7 days ${sevenDaySpend}, 30 days ${thirtyDaySpend}.`
|
|
346
|
+
: menuLabel
|
|
347
|
+
}
|
|
339
348
|
className={cn(
|
|
340
349
|
"cursor-pointer rounded-lg border-0 bg-transparent text-dashboard-text transition-colors hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus/70",
|
|
341
350
|
topBar
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useState,
|
|
7
7
|
type FormEvent,
|
|
8
8
|
type ReactNode,
|
|
9
|
+
type Ref,
|
|
9
10
|
} from "react";
|
|
10
11
|
import {
|
|
11
12
|
buttonUserInputField,
|
|
@@ -161,6 +162,7 @@ function TextField(props: {
|
|
|
161
162
|
disabled: boolean;
|
|
162
163
|
label: string;
|
|
163
164
|
onChange(value: string): void;
|
|
165
|
+
ref?: Ref<HTMLTextAreaElement>;
|
|
164
166
|
value: string;
|
|
165
167
|
}) {
|
|
166
168
|
const id = useId();
|
|
@@ -178,6 +180,7 @@ function TextField(props: {
|
|
|
178
180
|
disabled={props.disabled}
|
|
179
181
|
id={id}
|
|
180
182
|
onChange={(event) => props.onChange(event.target.value)}
|
|
183
|
+
ref={props.ref}
|
|
181
184
|
value={props.value}
|
|
182
185
|
/>
|
|
183
186
|
</Field>
|
|
@@ -188,7 +191,9 @@ function TextField(props: {
|
|
|
188
191
|
* Render the answer controls of an action item request. A request with
|
|
189
192
|
* one short choice field shows one button per choice, and a click is the
|
|
190
193
|
* answer. Choices with descriptions stack as rows, so each description stays
|
|
191
|
-
* with its choice. Other
|
|
194
|
+
* with its choice. Other… turns the buttons into the form, so the reader can
|
|
195
|
+
* add a note or answer in their own words. Other requests show a form that
|
|
196
|
+
* the reader submits once.
|
|
192
197
|
* A failed `onSubmit` lets the reader try again. `secondaryActions`, such as
|
|
193
198
|
* Dismiss, sit after the answer controls. `onUnsentChange` reports whether
|
|
194
199
|
* the form has picks or text that the reader has not sent.
|
|
@@ -206,7 +211,15 @@ export function UserInputForm(props: {
|
|
|
206
211
|
const choicesId = useId();
|
|
207
212
|
const [values, setValues] = useState<Record<string, string[]>>({});
|
|
208
213
|
const [other, setOther] = useState("");
|
|
209
|
-
const
|
|
214
|
+
const [writeOther, setWriteOther] = useState(false);
|
|
215
|
+
const buttonField = writeOther
|
|
216
|
+
? undefined
|
|
217
|
+
: buttonUserInputField(props.request);
|
|
218
|
+
// Other… unmounts the button that had focus, so move focus to the note.
|
|
219
|
+
const noteRef = useRef<HTMLTextAreaElement>(null);
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
if (writeOther) noteRef.current?.focus();
|
|
222
|
+
}, [writeOther]);
|
|
210
223
|
|
|
211
224
|
// A ref blocks a second click before React renders the disabled controls.
|
|
212
225
|
// The lock stays after a sent answer; only a failed send can retry.
|
|
@@ -260,6 +273,13 @@ export function UserInputForm(props: {
|
|
|
260
273
|
{props.secondaryActions}
|
|
261
274
|
</div>
|
|
262
275
|
) : null;
|
|
276
|
+
// Slack cards show the same button, so both surfaces take a free-text
|
|
277
|
+
// answer when no choice fits.
|
|
278
|
+
const otherButton = (
|
|
279
|
+
<Button disabled={sending} onClick={() => setWriteOther(true)}>
|
|
280
|
+
Other…
|
|
281
|
+
</Button>
|
|
282
|
+
);
|
|
263
283
|
|
|
264
284
|
return (
|
|
265
285
|
<>
|
|
@@ -308,9 +328,10 @@ export function UserInputForm(props: {
|
|
|
308
328
|
</button>
|
|
309
329
|
))}
|
|
310
330
|
</div>
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
331
|
+
<div className="mt-3 flex flex-wrap items-center gap-2">
|
|
332
|
+
{otherButton}
|
|
333
|
+
{secondary}
|
|
334
|
+
</div>
|
|
314
335
|
</>
|
|
315
336
|
) : buttonField ? (
|
|
316
337
|
<div className="mt-3 flex flex-wrap items-center gap-2">
|
|
@@ -325,6 +346,7 @@ export function UserInputForm(props: {
|
|
|
325
346
|
{option.recommended ? <SuggestedLabel className="ml-0" /> : null}
|
|
326
347
|
</Button>
|
|
327
348
|
))}
|
|
349
|
+
{otherButton}
|
|
328
350
|
{secondary}
|
|
329
351
|
</div>
|
|
330
352
|
) : (
|
|
@@ -359,6 +381,7 @@ export function UserInputForm(props: {
|
|
|
359
381
|
disabled={sending}
|
|
360
382
|
label={USER_INPUT_NOTE.label}
|
|
361
383
|
onChange={setOther}
|
|
384
|
+
ref={noteRef}
|
|
362
385
|
value={other}
|
|
363
386
|
/>
|
|
364
387
|
<div className="flex flex-wrap items-center gap-2">
|