@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.265.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@base-ui/react": "^1.9.0",
|
|
26
|
-
"@radix-ui/react-hover-card": "^1.1.23",
|
|
27
26
|
"@tanstack/react-query": "^5.100.14",
|
|
27
|
+
"@uidotdev/usehooks": "2.4.1",
|
|
28
|
+
"class-variance-authority": "^0.7.1",
|
|
29
|
+
"cn": "^0.5.0",
|
|
28
30
|
"hono": "^4.13.11",
|
|
29
31
|
"lucide-react": "^1.17.0",
|
|
30
32
|
"nitro": "3.0.260522-beta",
|
|
@@ -32,10 +34,11 @@
|
|
|
32
34
|
"react-dom": "^19.2.6",
|
|
33
35
|
"react-is": "^19.2.6",
|
|
34
36
|
"react-router": "^7.18.2",
|
|
37
|
+
"recharts": "3.8.0",
|
|
35
38
|
"shiki": "4.1.0",
|
|
36
39
|
"zod": "^4.5.4",
|
|
37
|
-
"@sentry/junior": "0.
|
|
38
|
-
"@sentry/junior-plugin-api": "0.
|
|
40
|
+
"@sentry/junior": "0.265.0",
|
|
41
|
+
"@sentry/junior-plugin-api": "0.265.0"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -47,9 +50,11 @@
|
|
|
47
50
|
"@vitest/coverage-v8": "4.1.12",
|
|
48
51
|
"eslint": "^10.8.0",
|
|
49
52
|
"oxlint": "^1.79.0",
|
|
53
|
+
"shadcn": "4.23.0",
|
|
50
54
|
"tailwindcss": "^4.3.0",
|
|
51
55
|
"tsdown": "0.23.1",
|
|
52
56
|
"tsup": "^8.5.1",
|
|
57
|
+
"tw-animate-css": "^1.4.0",
|
|
53
58
|
"typescript": "^7.0.2",
|
|
54
59
|
"vitest": "^4.1.12"
|
|
55
60
|
},
|
package/src/api/schema.ts
CHANGED
package/src/app.ts
CHANGED
|
@@ -79,7 +79,6 @@ export interface JuniorDashboardOptions {
|
|
|
79
79
|
authPath?: string;
|
|
80
80
|
authRequired?: boolean;
|
|
81
81
|
auth?: DashboardAuth;
|
|
82
|
-
componentGallery?: boolean;
|
|
83
82
|
mockConversations?: boolean;
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -129,9 +128,6 @@ function resolveDashboardOptions(
|
|
|
129
128
|
...options,
|
|
130
129
|
authRequired:
|
|
131
130
|
options.authRequired ?? readEnvFlag("JUNIOR_DASHBOARD_AUTH_REQUIRED"),
|
|
132
|
-
componentGallery:
|
|
133
|
-
options.componentGallery ??
|
|
134
|
-
readEnvFlag("JUNIOR_DASHBOARD_COMPONENT_GALLERY"),
|
|
135
131
|
mockConversations:
|
|
136
132
|
options.mockConversations ??
|
|
137
133
|
readEnvFlag("JUNIOR_DASHBOARD_MOCK_CONVERSATIONS"),
|
|
@@ -142,12 +138,8 @@ function isJsonRoute(pathname: string): boolean {
|
|
|
142
138
|
return pathname.startsWith("/api/");
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
function isDashboardPagePath(
|
|
146
|
-
|
|
147
|
-
basePath: string,
|
|
148
|
-
options: { componentGallery?: boolean } = {},
|
|
149
|
-
): boolean {
|
|
150
|
-
for (const { nested, path } of dashboardPagePaths(basePath, options)) {
|
|
141
|
+
function isDashboardPagePath(pathname: string, basePath: string): boolean {
|
|
142
|
+
for (const { nested, path } of dashboardPagePaths(basePath)) {
|
|
151
143
|
if (pathname === path || (nested && pathname.startsWith(`${path}/`))) {
|
|
152
144
|
return true;
|
|
153
145
|
}
|
|
@@ -158,7 +150,6 @@ function isDashboardPagePath(
|
|
|
158
150
|
|
|
159
151
|
interface DashboardReturnPathOptions {
|
|
160
152
|
authenticatedRoutes?: readonly AuthenticatedRoute[];
|
|
161
|
-
componentGallery?: boolean;
|
|
162
153
|
}
|
|
163
154
|
|
|
164
155
|
function dashboardReturnPath(
|
|
@@ -167,7 +158,7 @@ function dashboardReturnPath(
|
|
|
167
158
|
options: DashboardReturnPathOptions = {},
|
|
168
159
|
): string | undefined {
|
|
169
160
|
if (
|
|
170
|
-
!isDashboardPagePath(url.pathname, basePath
|
|
161
|
+
!isDashboardPagePath(url.pathname, basePath) &&
|
|
171
162
|
!isAuthPath(url.pathname, options.authenticatedRoutes ?? [])
|
|
172
163
|
) {
|
|
173
164
|
return undefined;
|
|
@@ -190,7 +181,7 @@ function requestedReturnPath(
|
|
|
190
181
|
const returnUrl = new URL(next, url.origin);
|
|
191
182
|
if (
|
|
192
183
|
returnUrl.origin !== url.origin ||
|
|
193
|
-
(!isDashboardPagePath(returnUrl.pathname, basePath
|
|
184
|
+
(!isDashboardPagePath(returnUrl.pathname, basePath) &&
|
|
194
185
|
!isAuthPath(returnUrl.pathname, options.authenticatedRoutes ?? []))
|
|
195
186
|
) {
|
|
196
187
|
return undefined;
|
|
@@ -399,7 +390,6 @@ export function createDashboardApp(
|
|
|
399
390
|
if (canonicalUrl) return Response.redirect(canonicalUrl, 302);
|
|
400
391
|
const returnUrl = callbackUrl(c.req.raw, basePath, {
|
|
401
392
|
authenticatedRoutes,
|
|
402
|
-
componentGallery: options.componentGallery,
|
|
403
393
|
});
|
|
404
394
|
if (!authRequired || !auth) {
|
|
405
395
|
return Response.redirect(returnUrl, 302);
|
|
@@ -471,7 +461,6 @@ export function createDashboardApp(
|
|
|
471
461
|
if (!auth) {
|
|
472
462
|
return unauthorized(c.req.raw, basePath, canonicalBaseURL, {
|
|
473
463
|
authenticatedRoutes,
|
|
474
|
-
componentGallery: options.componentGallery,
|
|
475
464
|
});
|
|
476
465
|
}
|
|
477
466
|
const browserSession = await auth.getSession(c.req.raw);
|
|
@@ -491,7 +480,6 @@ export function createDashboardApp(
|
|
|
491
480
|
if (!session) {
|
|
492
481
|
return unauthorized(c.req.raw, basePath, canonicalBaseURL, {
|
|
493
482
|
authenticatedRoutes,
|
|
494
|
-
componentGallery: options.componentGallery,
|
|
495
483
|
});
|
|
496
484
|
}
|
|
497
485
|
if (!dashboardSessionIsAuthorized(session, allowedDomains)) {
|
|
@@ -543,9 +531,7 @@ export function createDashboardApp(
|
|
|
543
531
|
}
|
|
544
532
|
}
|
|
545
533
|
|
|
546
|
-
for (const { nested, path } of dashboardPagePaths(basePath
|
|
547
|
-
componentGallery: options.componentGallery,
|
|
548
|
-
})) {
|
|
534
|
+
for (const { nested, path } of dashboardPagePaths(basePath)) {
|
|
549
535
|
app.get(path, () => renderDashboard(basePath, agentName));
|
|
550
536
|
if (nested) {
|
|
551
537
|
app.get(`${path}/*`, () => renderDashboard(basePath, agentName));
|
|
@@ -580,7 +566,6 @@ export function createDashboardApp(
|
|
|
580
566
|
authRequired,
|
|
581
567
|
authPath,
|
|
582
568
|
basePath,
|
|
583
|
-
componentGallery: options.componentGallery === true,
|
|
584
569
|
sentryConversationLinks: hasSentryConversationLinks(),
|
|
585
570
|
timeZone: dashboardTimeZone(),
|
|
586
571
|
version: JUNIOR_VERSION,
|
package/src/client/App.tsx
CHANGED
|
@@ -31,7 +31,6 @@ import { useDashboardServerVersion } from "./dashboard-version";
|
|
|
31
31
|
import { ConversationWorkspace } from "./conversations/ConversationWorkspace";
|
|
32
32
|
import { ConversationWorkspaceLoading } from "./conversations/ConversationWorkspaceLoading";
|
|
33
33
|
import { useConversationData } from "./conversations/queries";
|
|
34
|
-
import { ComponentsPage } from "./pages/dev/ComponentsPage";
|
|
35
34
|
import { codeRepositoryTabs } from "./pages/code/codeRepositoryRoutes";
|
|
36
35
|
import { RepositoryPage } from "./pages/code/RepositoryPage";
|
|
37
36
|
import { CodePage } from "./pages/code/CodePage";
|
|
@@ -49,13 +48,17 @@ import {
|
|
|
49
48
|
} from "./pages/system/SystemPageLayout";
|
|
50
49
|
import { WorkspaceFormPage } from "./pages/system/WorkspaceFormPage";
|
|
51
50
|
import { WorkspacesPage } from "./pages/system/WorkspacesPage";
|
|
52
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
isMemoryLibraryPath,
|
|
53
|
+
MemoryRouteLoading,
|
|
54
|
+
} from "./pages/memory/MemoryPageLayout";
|
|
53
55
|
import { AutomationExecutionsPage } from "./pages/automations/AutomationExecutionsPage";
|
|
54
56
|
import { AutomationRunsPage } from "./pages/automations/AutomationRunsPage";
|
|
55
57
|
import { AutomationVersionsPage } from "./pages/automations/AutomationVersionsPage";
|
|
56
58
|
import { AutomationsPage } from "./pages/automations/AutomationsPage";
|
|
57
59
|
import {
|
|
58
60
|
AutomationsPageLayout,
|
|
61
|
+
hasAutomationDetailsFrame,
|
|
59
62
|
AutomationsRouteLoading,
|
|
60
63
|
} from "./pages/automations/AutomationsPageLayout";
|
|
61
64
|
import {
|
|
@@ -121,17 +124,6 @@ export function DashboardShell() {
|
|
|
121
124
|
setMobileNavigationOpen(false);
|
|
122
125
|
}, [location.pathname]);
|
|
123
126
|
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (!mobileNavigationOpen) return;
|
|
126
|
-
|
|
127
|
-
function closeOnEscape(event: KeyboardEvent) {
|
|
128
|
-
if (event.key === "Escape") setMobileNavigationOpen(false);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
document.addEventListener("keydown", closeOnEscape);
|
|
132
|
-
return () => document.removeEventListener("keydown", closeOnEscape);
|
|
133
|
-
}, [mobileNavigationOpen]);
|
|
134
|
-
|
|
135
127
|
async function signOut() {
|
|
136
128
|
await fetch(`${data?.config.authPath ?? "/api/auth"}/sign-out`, {
|
|
137
129
|
credentials: "same-origin",
|
|
@@ -142,7 +134,14 @@ export function DashboardShell() {
|
|
|
142
134
|
|
|
143
135
|
return (
|
|
144
136
|
<DashboardChromeProvider>
|
|
145
|
-
<VisualViewportShell
|
|
137
|
+
<VisualViewportShell
|
|
138
|
+
className="bg-dashboard-bg"
|
|
139
|
+
enabled={
|
|
140
|
+
workspace ||
|
|
141
|
+
hasAutomationDetailsFrame(location.pathname) ||
|
|
142
|
+
isMemoryLibraryPath(location.pathname)
|
|
143
|
+
}
|
|
144
|
+
>
|
|
146
145
|
<DashboardChrome
|
|
147
146
|
banner={
|
|
148
147
|
<>
|
|
@@ -424,22 +423,6 @@ export function DashboardShell() {
|
|
|
424
423
|
path="/conversations/:conversationId"
|
|
425
424
|
/>
|
|
426
425
|
<Route element={<Navigate replace to="/" />} path="/conversations" />
|
|
427
|
-
<Route
|
|
428
|
-
element={
|
|
429
|
-
loading ? (
|
|
430
|
-
<LoadingView label="Loading components" />
|
|
431
|
-
) : !data ? (
|
|
432
|
-
<LoadingView
|
|
433
|
-
label={query.error?.message ?? "Dashboard unavailable"}
|
|
434
|
-
/>
|
|
435
|
-
) : data.config.componentGallery ? (
|
|
436
|
-
<ComponentsPage />
|
|
437
|
-
) : (
|
|
438
|
-
<Navigate replace to="/" />
|
|
439
|
-
)
|
|
440
|
-
}
|
|
441
|
-
path="/dev/*"
|
|
442
|
-
/>
|
|
443
426
|
<Route
|
|
444
427
|
element={<LegacySystemRedirect section="people" />}
|
|
445
428
|
path="/people"
|
|
@@ -496,6 +479,21 @@ export function DashboardShell() {
|
|
|
496
479
|
}
|
|
497
480
|
path="/system/workspaces/new"
|
|
498
481
|
/>
|
|
482
|
+
<Route
|
|
483
|
+
element={
|
|
484
|
+
loading ? (
|
|
485
|
+
<SystemRouteLoading
|
|
486
|
+
description="Repository recipes Junior can switch into."
|
|
487
|
+
label="Loading Workspaces"
|
|
488
|
+
title="Workspaces"
|
|
489
|
+
variant="list"
|
|
490
|
+
/>
|
|
491
|
+
) : (
|
|
492
|
+
<WorkspacesPage />
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
path="/system/workspaces/:workspaceId"
|
|
496
|
+
/>
|
|
499
497
|
<Route
|
|
500
498
|
element={
|
|
501
499
|
loading ? (
|
|
@@ -506,7 +504,7 @@ export function DashboardShell() {
|
|
|
506
504
|
<WorkspaceFormPage />
|
|
507
505
|
)
|
|
508
506
|
}
|
|
509
|
-
path="/system/workspaces/:workspaceId"
|
|
507
|
+
path="/system/workspaces/:workspaceId/edit"
|
|
510
508
|
/>
|
|
511
509
|
<Route
|
|
512
510
|
element={
|
package/src/client/api.ts
CHANGED
|
@@ -103,6 +103,13 @@ export function useConversationsData(search = "") {
|
|
|
103
103
|
signal,
|
|
104
104
|
),
|
|
105
105
|
placeholderData: keepPreviousData,
|
|
106
|
+
// Active rows change while no Conversation is open, such as a new title.
|
|
107
|
+
refetchInterval: (query) =>
|
|
108
|
+
query.state.data?.conversations.some(
|
|
109
|
+
(conversation) => conversation.status === "active",
|
|
110
|
+
)
|
|
111
|
+
? 10_000
|
|
112
|
+
: false,
|
|
106
113
|
retry: false,
|
|
107
114
|
});
|
|
108
115
|
}
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
+
import { cn } from "../styles";
|
|
1
2
|
import type { ReactNode } from "react";
|
|
2
3
|
|
|
3
|
-
import { Sheet,
|
|
4
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "./ui/sheet";
|
|
5
|
+
import { useSheet } from "./ui/useSheet";
|
|
4
6
|
|
|
5
|
-
/** Show details in
|
|
7
|
+
/** Show app details in the shadcn sheet with a visible title and close control. */
|
|
6
8
|
export function BottomSheet(props: {
|
|
7
9
|
children: ReactNode;
|
|
8
10
|
label: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
size?: "full" | "content";
|
|
9
13
|
onClose(): void;
|
|
10
14
|
}) {
|
|
15
|
+
const sheet = useSheet(props.onClose);
|
|
11
16
|
return (
|
|
12
|
-
<Sheet
|
|
13
|
-
<SheetContent
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
className="
|
|
26
|
-
|
|
17
|
+
<Sheet {...sheet.rootProps}>
|
|
18
|
+
<SheetContent
|
|
19
|
+
{...sheet.contentProps}
|
|
20
|
+
aria-label={props.label}
|
|
21
|
+
side="bottom"
|
|
22
|
+
className={cn(
|
|
23
|
+
"mx-auto w-full max-w-2xl gap-0 rounded-t-2xl outline-none",
|
|
24
|
+
props.size === "content"
|
|
25
|
+
? "max-h-[85dvh]"
|
|
26
|
+
: "max-h-full data-[side=bottom]:h-[85dvh]",
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
<SheetHeader className="border-b border-dashboard-border">
|
|
30
|
+
<SheetTitle className="break-words">
|
|
31
|
+
{props.title ?? props.label}
|
|
32
|
+
</SheetTitle>
|
|
33
|
+
</SheetHeader>
|
|
34
|
+
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
|
27
35
|
{props.children}
|
|
28
|
-
</
|
|
36
|
+
</div>
|
|
29
37
|
</SheetContent>
|
|
30
38
|
</Sheet>
|
|
31
39
|
);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Check, CircleAlert, Copy } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "./ui/button";
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
|
6
|
+
|
|
7
|
+
/** Copy available text and show whether the clipboard write succeeded. */
|
|
8
|
+
export function CopyButton(props: {
|
|
9
|
+
getText?: () => Promise<string> | string;
|
|
10
|
+
label: string;
|
|
11
|
+
copyingLabel?: string;
|
|
12
|
+
/** `menu` shows icon + label for mobile overflow lists. */
|
|
13
|
+
layout?: "icon" | "menu";
|
|
14
|
+
}) {
|
|
15
|
+
const [status, setStatus] = useState<
|
|
16
|
+
"copied" | "copying" | "failed" | "idle"
|
|
17
|
+
>("idle");
|
|
18
|
+
const label =
|
|
19
|
+
status === "copied"
|
|
20
|
+
? "Copied"
|
|
21
|
+
: status === "copying"
|
|
22
|
+
? (props.copyingLabel ?? "Copying")
|
|
23
|
+
: status === "failed"
|
|
24
|
+
? "Copy failed"
|
|
25
|
+
: props.label;
|
|
26
|
+
const Icon = {
|
|
27
|
+
copied: Check,
|
|
28
|
+
copying: Copy,
|
|
29
|
+
failed: CircleAlert,
|
|
30
|
+
idle: Copy,
|
|
31
|
+
}[status];
|
|
32
|
+
|
|
33
|
+
async function copyText() {
|
|
34
|
+
if (!props.getText) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
setStatus("copying");
|
|
38
|
+
await navigator.clipboard.writeText(await props.getText());
|
|
39
|
+
setStatus("copied");
|
|
40
|
+
} catch {
|
|
41
|
+
setStatus("failed");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (props.layout === "menu") {
|
|
46
|
+
return (
|
|
47
|
+
<button
|
|
48
|
+
aria-label={label}
|
|
49
|
+
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"
|
|
50
|
+
disabled={!props.getText || status === "copying"}
|
|
51
|
+
onClick={() => void copyText()}
|
|
52
|
+
type="button"
|
|
53
|
+
>
|
|
54
|
+
<Icon aria-hidden="true" size={16} strokeWidth={2} />
|
|
55
|
+
<span>{label}</span>
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Tooltip>
|
|
62
|
+
<TooltipTrigger
|
|
63
|
+
render={
|
|
64
|
+
<Button
|
|
65
|
+
focusableWhenDisabled
|
|
66
|
+
aria-label={label}
|
|
67
|
+
disabled={!props.getText || status === "copying"}
|
|
68
|
+
onClick={() => void copyText()}
|
|
69
|
+
size="icon"
|
|
70
|
+
>
|
|
71
|
+
<Icon
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
className={status === "failed" ? "text-rose-300" : undefined}
|
|
74
|
+
size={15}
|
|
75
|
+
strokeWidth={2}
|
|
76
|
+
/>
|
|
77
|
+
</Button>
|
|
78
|
+
}
|
|
79
|
+
/>
|
|
80
|
+
<TooltipContent size="compact">{label}</TooltipContent>
|
|
81
|
+
</Tooltip>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { X } from "lucide-react";
|
|
2
|
+
import { useEffect, useRef, useSyncExternalStore, type ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "./ui/button";
|
|
5
|
+
import { BottomSheet } from "./BottomSheet";
|
|
6
|
+
import { Drawer } from "./Drawer";
|
|
7
|
+
|
|
8
|
+
const panelMediaQuery = "(min-width: 1280px)";
|
|
9
|
+
|
|
10
|
+
function subscribeToPanelLayout(onChange: () => void) {
|
|
11
|
+
const media = window.matchMedia(panelMediaQuery);
|
|
12
|
+
media.addEventListener("change", onChange);
|
|
13
|
+
return () => media.removeEventListener("change", onChange);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hasPanelSpace() {
|
|
17
|
+
return window.matchMedia(panelMediaQuery).matches;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Report whether details fit beside their source instead of in a sheet. */
|
|
21
|
+
export function useDetailsPanel(): boolean {
|
|
22
|
+
return useSyncExternalStore(
|
|
23
|
+
subscribeToPanelLayout,
|
|
24
|
+
hasPanelSpace,
|
|
25
|
+
() => false,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Show details beside their source, or in a bottom sheet when space is limited. */
|
|
30
|
+
export function DetailsPanel(props: {
|
|
31
|
+
actions?: ReactNode;
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
onClose(): void;
|
|
34
|
+
label: string;
|
|
35
|
+
title: ReactNode;
|
|
36
|
+
sheetTitle?: string;
|
|
37
|
+
/** Nested details stay modal inside an existing drawer. */
|
|
38
|
+
overlay?: boolean;
|
|
39
|
+
}) {
|
|
40
|
+
const { onClose, overlay } = props;
|
|
41
|
+
const closeLabel = `Close ${props.label.toLowerCase()}`;
|
|
42
|
+
const panel = useDetailsPanel();
|
|
43
|
+
const closeRef = useRef<HTMLButtonElement>(null);
|
|
44
|
+
const wasPanel = useRef(panel);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (
|
|
47
|
+
panel &&
|
|
48
|
+
!wasPanel.current &&
|
|
49
|
+
!document.querySelector("[data-dashboard-modal]")
|
|
50
|
+
) {
|
|
51
|
+
closeRef.current?.focus({ preventScroll: true });
|
|
52
|
+
}
|
|
53
|
+
wasPanel.current = panel;
|
|
54
|
+
}, [panel]);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!panel || overlay) return;
|
|
57
|
+
function onKeyDown(event: KeyboardEvent) {
|
|
58
|
+
if (
|
|
59
|
+
event.key === "Escape" &&
|
|
60
|
+
!event.defaultPrevented &&
|
|
61
|
+
!document.querySelector("[data-dashboard-modal]")
|
|
62
|
+
) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
onClose();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
document.addEventListener("keydown", onKeyDown);
|
|
68
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
69
|
+
}, [panel, overlay, onClose]);
|
|
70
|
+
if (!panel) {
|
|
71
|
+
return (
|
|
72
|
+
<BottomSheet
|
|
73
|
+
label={props.label}
|
|
74
|
+
title={props.sheetTitle}
|
|
75
|
+
onClose={onClose}
|
|
76
|
+
>
|
|
77
|
+
{props.actions ? (
|
|
78
|
+
<div className="grid h-full min-h-0 grid-rows-[minmax(0,1fr)_auto]">
|
|
79
|
+
<div className="min-h-0">{props.children}</div>
|
|
80
|
+
<div className="flex justify-end gap-1 border-t border-dashboard-border px-4 py-2">
|
|
81
|
+
{props.actions}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
) : (
|
|
85
|
+
props.children
|
|
86
|
+
)}
|
|
87
|
+
</BottomSheet>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (overlay) {
|
|
92
|
+
return (
|
|
93
|
+
<Drawer
|
|
94
|
+
actions={props.actions}
|
|
95
|
+
closeLabel={closeLabel}
|
|
96
|
+
header={props.title}
|
|
97
|
+
label={props.label}
|
|
98
|
+
onClose={onClose}
|
|
99
|
+
openKey={props.label}
|
|
100
|
+
width="wide"
|
|
101
|
+
>
|
|
102
|
+
{props.children}
|
|
103
|
+
</Drawer>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<aside
|
|
109
|
+
aria-label={props.label}
|
|
110
|
+
className="grid min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)] border-l border-dashboard-border bg-dashboard-bg-elevated"
|
|
111
|
+
>
|
|
112
|
+
<header className="flex items-center justify-between gap-3 border-b border-dashboard-border py-2 pl-5 pr-3">
|
|
113
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
114
|
+
<div className="min-w-0">{props.title}</div>
|
|
115
|
+
{props.actions ? (
|
|
116
|
+
<div className="flex items-center gap-0.5">{props.actions}</div>
|
|
117
|
+
) : null}
|
|
118
|
+
</div>
|
|
119
|
+
<Button
|
|
120
|
+
ref={closeRef}
|
|
121
|
+
aria-label={closeLabel}
|
|
122
|
+
onClick={onClose}
|
|
123
|
+
size="icon"
|
|
124
|
+
>
|
|
125
|
+
<X aria-hidden="true" size={15} />
|
|
126
|
+
</Button>
|
|
127
|
+
</header>
|
|
128
|
+
<div className="min-h-0 overflow-y-auto overscroll-contain">
|
|
129
|
+
{props.children}
|
|
130
|
+
</div>
|
|
131
|
+
</aside>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -1,57 +1,58 @@
|
|
|
1
|
-
import { X } from "lucide-react";
|
|
2
1
|
import { useRef, type ReactNode } from "react";
|
|
3
2
|
|
|
4
3
|
import { cn } from "../styles";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { Sheet, SheetContent, SheetHeader } from "./ui/sheet";
|
|
5
|
+
import { useSheet } from "./ui/useSheet";
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
export function Drawer(props: {
|
|
7
|
+
type DrawerProps = {
|
|
10
8
|
actions?: ReactNode;
|
|
11
9
|
children: ReactNode;
|
|
12
10
|
closeLabel: string;
|
|
13
11
|
header: ReactNode;
|
|
14
12
|
onClose(): void;
|
|
15
13
|
openKey: string;
|
|
16
|
-
titleId: string;
|
|
17
14
|
width?: "default" | "wide" | "narrow";
|
|
18
|
-
}
|
|
15
|
+
} & ({ titleId: string; label?: never } | { label: string; titleId?: never });
|
|
16
|
+
|
|
17
|
+
/** Render a side drawer with shared modal focus and dismissal behavior. */
|
|
18
|
+
export function Drawer(props: DrawerProps) {
|
|
19
|
+
return <DrawerContent key={props.openKey} {...props} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function DrawerContent(props: DrawerProps) {
|
|
19
23
|
const closeRef = useRef<HTMLButtonElement>(null);
|
|
24
|
+
const sheet = useSheet(props.onClose, closeRef);
|
|
20
25
|
return (
|
|
21
|
-
<Sheet
|
|
26
|
+
<Sheet {...sheet.rootProps}>
|
|
22
27
|
<SheetContent
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
{...sheet.contentProps}
|
|
29
|
+
aria-label={props.label}
|
|
30
|
+
aria-labelledby={props.titleId}
|
|
25
31
|
side="right"
|
|
32
|
+
closeButtonProps={{
|
|
33
|
+
ref: closeRef,
|
|
34
|
+
"aria-label": props.closeLabel,
|
|
35
|
+
className:
|
|
36
|
+
"right-4 top-[max(0.75rem,env(safe-area-inset-top))] md:right-5",
|
|
37
|
+
}}
|
|
26
38
|
className={cn(
|
|
39
|
+
"grid min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)] gap-0 outline-none data-[side=right]:w-full data-[side=right]:sm:max-w-none",
|
|
27
40
|
props.width === "wide"
|
|
28
|
-
? "md:w-[min(760px,94vw)]"
|
|
41
|
+
? "data-[side=right]:md:w-[min(760px,94vw)]"
|
|
29
42
|
: props.width === "narrow"
|
|
30
|
-
? "md:w-[min(440px,94vw)]"
|
|
31
|
-
: "md:w-[min(560px,94vw)]",
|
|
43
|
+
? "data-[side=right]:md:w-[min(440px,94vw)]"
|
|
44
|
+
: "data-[side=right]:md:w-[min(560px,94vw)]",
|
|
32
45
|
)}
|
|
33
46
|
>
|
|
34
|
-
<
|
|
35
|
-
<div className="min-w-0">{props.header}</div>
|
|
36
|
-
|
|
37
|
-
{props.actions}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
ref={closeRef}
|
|
42
|
-
aria-label={props.closeLabel}
|
|
43
|
-
size="icon"
|
|
44
|
-
title="Close"
|
|
45
|
-
/>
|
|
46
|
-
}
|
|
47
|
-
>
|
|
48
|
-
<X aria-hidden="true" size={15} strokeWidth={2.25} />
|
|
49
|
-
</SheetClose>
|
|
50
|
-
</div>
|
|
51
|
-
</header>
|
|
52
|
-
<SheetBody className="min-h-0 overflow-auto px-4 py-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-5">
|
|
47
|
+
<SheetHeader className="flex-row items-start gap-3 border-b border-dashboard-border bg-dashboard-bg-elevated px-4 py-3 pr-14 pt-[max(0.75rem,env(safe-area-inset-top))] md:pl-5 md:pr-14">
|
|
48
|
+
<div className="min-w-0 flex-1">{props.header}</div>
|
|
49
|
+
{props.actions ? (
|
|
50
|
+
<div className="flex items-start gap-1.5">{props.actions}</div>
|
|
51
|
+
) : null}
|
|
52
|
+
</SheetHeader>
|
|
53
|
+
<div className="min-h-0 overflow-auto px-4 py-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:px-5">
|
|
53
54
|
{props.children}
|
|
54
|
-
</
|
|
55
|
+
</div>
|
|
55
56
|
</SheetContent>
|
|
56
57
|
</Sheet>
|
|
57
58
|
);
|