@sentry/junior-dashboard 0.269.0 → 0.271.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/app.js +2 -2
  2. package/dist/client.js +78 -75
  3. package/dist/index.js +2 -2
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +5 -4
  6. package/src/client/App.tsx +103 -65
  7. package/src/client/components/AGENTS.md +30 -59
  8. package/src/client/components/AutomationCard.tsx +2 -1
  9. package/src/client/components/ProfileMenu.tsx +0 -24
  10. package/src/client/components/UserInputForm.tsx +143 -39
  11. package/src/client/components/layout/PageHeader.tsx +25 -10
  12. package/src/client/conversations/AGENTS.md +12 -37
  13. package/src/client/conversations/ConversationBrief.tsx +55 -36
  14. package/src/client/conversations/ConversationComposer.tsx +27 -8
  15. package/src/client/conversations/ConversationEventLog.tsx +1 -0
  16. package/src/client/conversations/ConversationHomeList.tsx +43 -26
  17. package/src/client/conversations/ConversationPage.tsx +137 -40
  18. package/src/client/conversations/ConversationSectionHeading.tsx +44 -0
  19. package/src/client/conversations/ConversationSidebar.tsx +55 -25
  20. package/src/client/conversations/ConversationTranscript.tsx +3 -2
  21. package/src/client/conversations/ConversationWatches.tsx +4 -1
  22. package/src/client/conversations/ConversationWorkspace.tsx +43 -25
  23. package/src/client/conversations/EventDetails.tsx +6 -9
  24. package/src/client/conversations/PendingMailboxStack.tsx +5 -2
  25. package/src/client/conversations/TranscriptContextEventView.tsx +3 -4
  26. package/src/client/conversations/TranscriptEventView.tsx +9 -3
  27. package/src/client/conversations/TranscriptMessageView.tsx +6 -5
  28. package/src/client/conversations/TranscriptReasoningView.tsx +4 -5
  29. package/src/client/conversations/TranscriptRows.tsx +6 -1
  30. package/src/client/conversations/TranscriptStructuredEventView.tsx +5 -4
  31. package/src/client/conversations/TranscriptToolView.tsx +2 -1
  32. package/src/client/conversations/TranscriptView.tsx +6 -6
  33. package/src/client/conversations/conversationOutbox.ts +13 -3
  34. package/src/client/conversations/conversationRoutes.ts +30 -3
  35. package/src/client/conversations/conversationSections.ts +36 -8
  36. package/src/client/conversations/eventTranscript.ts +1 -0
  37. package/src/client/conversations/transcriptBottomPinning.ts +171 -81
  38. package/src/client/conversations/transcriptScrollAnchor.ts +101 -0
  39. package/src/client/conversations/useCollapsedConversationSections.ts +48 -0
  40. package/src/client/display-text/DisplayText.tsx +68 -0
  41. package/src/client/display-text/emoji.ts +54 -0
  42. package/src/client/{conversations/transcriptMarkdownLinks.ts → display-text/links.ts} +16 -3
  43. package/src/client/{conversations/TranscriptMarkdown.tsx → display-text/markdown.tsx} +83 -19
  44. package/src/client/display-text/mentions.tsx +148 -0
  45. package/src/client/display-text/plainText.ts +29 -0
  46. package/src/client/pages/automations/AGENTS.md +6 -25
  47. package/src/client/pages/automations/AutomationFilters.tsx +6 -6
  48. package/src/client/pages/automations/AutomationOverview.tsx +2 -2
  49. package/src/client/pages/automations/AutomationVersionsTab.tsx +12 -8
  50. package/src/client/pages/code/RepositoryTabs.tsx +6 -1
  51. package/src/client/pages/memory/MemoryDetails.tsx +5 -13
  52. package/src/client/pages/settings/ConnectionsPage.tsx +198 -0
  53. package/src/client/pages/settings/McpSettingsPage.tsx +77 -0
  54. package/src/client/pages/settings/PersonalTokensPage.tsx +198 -0
  55. package/src/client/pages/{SettingsPage.tsx → settings/SettingsPage.tsx} +54 -62
  56. package/src/client/pages/settings/SettingsPageLayout.tsx +45 -0
  57. package/src/client/primaryNav.ts +1 -1
  58. package/src/client/review-desk/ConversationReviewBar.tsx +0 -1
  59. package/src/client/review-desk/ReviewDeskPage.tsx +202 -241
  60. package/src/client/review-desk/ReviewDeskSidebar.tsx +253 -0
  61. package/src/client/review-desk/ReviewItemActions.tsx +6 -2
  62. package/src/client/review-desk/ReviewItemCard.tsx +58 -17
  63. package/src/client/review-desk/ReviewItemContext.tsx +108 -0
  64. package/src/client/review-desk/ReviewItemDetails.tsx +153 -0
  65. package/src/client/review-desk/ReviewItemView.tsx +159 -0
  66. package/src/client/review-desk/UnsentAnswers.tsx +148 -0
  67. package/src/client/review-desk/queries.ts +4 -2
  68. package/src/client/review-desk/reviewItemFormat.ts +83 -1
  69. package/src/client/components/charts/AGENTS.md +0 -15
  70. package/src/client/conversations/TranscriptText.tsx +0 -47
  71. package/src/client/conversations/conversationActivityPreview.ts +0 -16
  72. package/src/client/conversations/transcriptMentions.tsx +0 -134
  73. package/src/client/pages/ConnectionsPage.tsx +0 -207
  74. package/src/client/pages/PersonalTokensPage.tsx +0 -205
  75. package/src/client/pages/system/AGENTS.md +0 -12
  76. package/src/client/review-desk/ReviewItemDetail.tsx +0 -123
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-dashboard",
3
- "version": "0.269.0",
3
+ "version": "0.271.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -39,10 +39,10 @@
39
39
  "remark-gfm": "^4.0.1",
40
40
  "shiki": "4.1.0",
41
41
  "zod": "^4.5.4",
42
- "@sentry/junior-plugin-api": "0.269.0"
42
+ "@sentry/junior-plugin-api": "0.271.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@sentry/junior": "0.269.0"
45
+ "@sentry/junior": "0.271.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@oxlint/plugins": "1.79.0",
@@ -52,6 +52,7 @@
52
52
  "@types/react": "^19.2.15",
53
53
  "@types/react-dom": "^19.2.3",
54
54
  "@vitest/coverage-v8": "4.1.12",
55
+ "emojibase-data": "^18.0.0",
55
56
  "eslint": "^10.8.0",
56
57
  "nitro": "3.0.260522-beta",
57
58
  "oxlint": "^1.79.0",
@@ -62,7 +63,7 @@
62
63
  "tw-animate-css": "^1.4.0",
63
64
  "typescript": "^7.0.2",
64
65
  "vitest": "^4.1.12",
65
- "@sentry/junior": "0.269.0"
66
+ "@sentry/junior": "0.271.0"
66
67
  },
67
68
  "scripts": {
68
69
  "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",
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useState } from "react";
1
+ import { useEffect, useMemo, useState, type ReactNode } from "react";
2
2
  import { Navigate, Route, Routes, useLocation, useParams } from "react-router";
3
3
 
4
4
  import {
@@ -11,7 +11,10 @@ import {
11
11
  import { ConnectionBanner } from "./components/ConnectionBanner";
12
12
  import { LoadingView } from "./components/LoadingView";
13
13
  import { VersionDriftBanner } from "./components/VersionDriftBanner";
14
- import { PageRouteLoading } from "./components/PageRouteLoading";
14
+ import {
15
+ PageContentSkeleton,
16
+ type PageContentSkeletonVariant,
17
+ } from "./components/PageContentSkeleton";
15
18
  import { ProfileMenu } from "./components/ProfileMenu";
16
19
  import {
17
20
  DashboardChrome,
@@ -36,10 +39,12 @@ import { CodePage } from "./pages/code/CodePage";
36
39
  import { LocationDetailPage } from "./pages/locations/LocationDetailPage";
37
40
  import { LocationsPage } from "./pages/locations/LocationsPage";
38
41
  import { PeoplePage } from "./pages/people/PeoplePage";
39
- import { ConnectionsPage } from "./pages/ConnectionsPage";
40
- import { PersonalTokensPage } from "./pages/PersonalTokensPage";
41
42
  import { PersonProfilePage } from "./pages/people/PersonProfilePage";
42
- import { SettingsPage } from "./pages/SettingsPage";
43
+ import { ConnectionsPage } from "./pages/settings/ConnectionsPage";
44
+ import { McpSettingsPage } from "./pages/settings/McpSettingsPage";
45
+ import { PersonalTokensPage } from "./pages/settings/PersonalTokensPage";
46
+ import { SettingsPage } from "./pages/settings/SettingsPage";
47
+ import { SettingsPageLayout } from "./pages/settings/SettingsPageLayout";
43
48
  import { SystemPage } from "./pages/system/SystemPage";
44
49
  import {
45
50
  SystemPageLayout,
@@ -65,7 +70,10 @@ import {
65
70
  } from "./pages/user/PluginUserPage";
66
71
  import { AdminPage } from "./pages/admin/AdminPage";
67
72
  import { buildPrimaryNavItems } from "./primaryNav";
68
- import { ReviewDeskPage } from "./review-desk/ReviewDeskPage";
73
+ import {
74
+ ReviewDeskLoading,
75
+ ReviewDeskPage,
76
+ } from "./review-desk/ReviewDeskPage";
69
77
  import { useReviewItemCountData } from "./review-desk/queries";
70
78
  import type { DashboardCoreData } from "./types";
71
79
 
@@ -74,6 +82,56 @@ const dashboardNoise = {
74
82
  "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\")",
75
83
  };
76
84
 
85
+ const settingsRoutes: {
86
+ loading: {
87
+ description: string;
88
+ label: string;
89
+ title: string;
90
+ variant?: PageContentSkeletonVariant;
91
+ };
92
+ path: string;
93
+ render(data: DashboardCoreData): ReactNode;
94
+ }[] = [
95
+ {
96
+ loading: {
97
+ description: "Manage your profile and preferences.",
98
+ label: "Loading settings",
99
+ title: "General",
100
+ },
101
+ path: "/settings",
102
+ render: (data) => <SettingsPage identity={data.me} />,
103
+ },
104
+ {
105
+ loading: {
106
+ description: "Create and revoke personal API tokens.",
107
+ label: "Loading API tokens",
108
+ title: "API Tokens",
109
+ variant: "list",
110
+ },
111
+ path: "/settings/api-tokens",
112
+ render: () => <PersonalTokensPage />,
113
+ },
114
+ {
115
+ loading: {
116
+ description: "Connect and disconnect your accounts.",
117
+ label: "Loading identities",
118
+ title: "Identities",
119
+ variant: "list",
120
+ },
121
+ path: "/settings/connections",
122
+ render: () => <ConnectionsPage />,
123
+ },
124
+ {
125
+ loading: {
126
+ description: "Connect an MCP client to your conversations.",
127
+ label: "Loading MCP",
128
+ title: "MCP",
129
+ },
130
+ path: "/settings/mcp",
131
+ render: () => <McpSettingsPage />,
132
+ },
133
+ ];
134
+
77
135
  /** Render the dashboard SPA shell and route-level loading states. */
78
136
  export function DashboardShell() {
79
137
  const location = useLocation();
@@ -124,10 +182,20 @@ export function DashboardShell() {
124
182
  }, [location.pathname]);
125
183
 
126
184
  async function signOut() {
127
- await fetch(`${data?.config.authPath ?? "/api/auth"}/sign-out`, {
128
- credentials: "same-origin",
129
- method: "POST",
130
- });
185
+ // Better Auth rejects a request body without a JSON content type. On
186
+ // Vercel, a bodiless POST still arrives with an empty body.
187
+ const response = await fetch(
188
+ `${data?.config.authPath ?? "/api/auth"}/sign-out`,
189
+ {
190
+ body: "{}",
191
+ credentials: "same-origin",
192
+ headers: { "content-type": "application/json" },
193
+ method: "POST",
194
+ },
195
+ );
196
+ if (!response.ok) {
197
+ throw new Error(`Sign-out returned ${response.status}`);
198
+ }
131
199
  window.location.assign(data?.config.basePath ?? "/");
132
200
  }
133
201
 
@@ -137,6 +205,7 @@ export function DashboardShell() {
137
205
  className="bg-dashboard-bg"
138
206
  enabled={
139
207
  workspace ||
208
+ location.pathname === "/review" ||
140
209
  hasAutomationDetailsFrame(location.pathname) ||
141
210
  isMemoryLibraryPath(location.pathname)
142
211
  }
@@ -365,11 +434,7 @@ export function DashboardShell() {
365
434
  <Route
366
435
  element={
367
436
  loading ? (
368
- <PageRouteLoading
369
- description="Items that need a person, and who can act on them."
370
- label="Loading review desk"
371
- title="Review desk"
372
- />
437
+ <ReviewDeskLoading />
373
438
  ) : loggedIn ? (
374
439
  <ReviewDeskPage />
375
440
  ) : (
@@ -513,56 +578,29 @@ export function DashboardShell() {
513
578
  }
514
579
  path="/system/*"
515
580
  />
516
- <Route
517
- element={
518
- loading ? (
519
- <PageRouteLoading
520
- description="Manage your Junior profile and preferences."
521
- label="Loading settings"
522
- title="Settings"
523
- />
524
- ) : loggedIn ? (
525
- <SettingsPage identity={data!.me} />
526
- ) : (
527
- <Navigate replace to="/" />
528
- )
529
- }
530
- path="/settings"
531
- />
532
- <Route
533
- element={
534
- loading ? (
535
- <PageRouteLoading
536
- description="Create and revoke personal API tokens."
537
- label="Loading API tokens"
538
- title="API tokens"
539
- variant="list"
540
- />
541
- ) : loggedIn ? (
542
- <PersonalTokensPage />
543
- ) : (
544
- <Navigate replace to="/" />
545
- )
546
- }
547
- path="/settings/api-tokens"
548
- />
549
- <Route
550
- element={
551
- loading ? (
552
- <PageRouteLoading
553
- description="Connect and disconnect your accounts."
554
- label="Loading connected accounts"
555
- title="Connected accounts"
556
- variant="list"
557
- />
558
- ) : loggedIn ? (
559
- <ConnectionsPage />
560
- ) : (
561
- <Navigate replace to="/" />
562
- )
563
- }
564
- path="/settings/connections"
565
- />
581
+ {settingsRoutes.map((route) => (
582
+ <Route
583
+ element={
584
+ loading ? (
585
+ <SettingsPageLayout
586
+ description={route.loading.description}
587
+ title={route.loading.title}
588
+ >
589
+ <PageContentSkeleton
590
+ label={route.loading.label}
591
+ variant={route.loading.variant}
592
+ />
593
+ </SettingsPageLayout>
594
+ ) : loggedIn ? (
595
+ route.render(data!)
596
+ ) : (
597
+ <Navigate replace to="/" />
598
+ )
599
+ }
600
+ key={route.path}
601
+ path={route.path}
602
+ />
603
+ ))}
566
604
  <Route
567
605
  element={
568
606
  loading || userPagesQuery.isPending ? (
@@ -1,65 +1,36 @@
1
- # Dashboard UI kit
2
-
3
- Shared dashboard components live here. Feature pages compose them instead of
4
- copying Tailwind blocks. Follow `policies/frontend-components.md`.
5
- `pnpm dashboard-style:check` enforces the token rules.
6
-
7
- ## shadcn/ui
8
-
9
- - Add components with
10
- `pnpm --filter @sentry/junior-dashboard exec shadcn add <component>`. They go
11
- to `ui/`.
12
- - `ui/` files are shadcn source on dashboard tokens. Remove unused exports.
13
- Change a `ui/` file when every caller needs the same style.
14
- - Use `ui/` components directly. Add an app component only when it adds
15
- structure or behavior, not to rename a `ui/` component or its props.
16
- - Do not apply the CLI's global border or body resets. Keep the Tailwind
17
- radius scale.
18
-
19
- ## Component rules
20
-
21
- - Use `ui/alert-dialog` for every confirmation. Never use `window.confirm`.
22
- - Use `ui/dialog` for a short task form. Never put a form in a `Drawer` or
23
- `DetailsPanel`. Use `useSheet` for each dialog and mount it only while open.
24
- - Use `ui/trace` for transcript activity rows. Put only inline elements in
25
- the row button.
26
- - Give an icon-only control a label with `TooltipContent size="compact"`.
27
- Never use the native `title` attribute.
28
- - Set `focusableWhenDisabled` on a `ui/button` tooltip trigger that can be
1
+ # Dashboard UI rules
2
+
3
+ These apply to all dashboard UI. `pnpm dashboard-style:check` enforces the
4
+ token rules.
5
+
6
+ - Add shadcn components with
7
+ `pnpm --filter @sentry/junior-dashboard exec shadcn add <component>`.
8
+ - Change a `ui/` file only when every caller needs the same style. Do not
9
+ wrap a `ui/` component just to rename it or its props.
10
+ - Do not apply the shadcn CLI's global border or body resets. Keep the
11
+ Tailwind radius scale.
12
+ - Confirmations use `ui/alert-dialog`, not `window.confirm`.
13
+ - Short task forms use `ui/dialog`, never a `Drawer` or `DetailsPanel`. Mount
14
+ each dialog through `useSheet`, only while open.
15
+ - Put only inline elements in a `ui/trace` row button.
16
+ - Label icon-only controls with `TooltipContent size="compact"`, not the
17
+ native `title` attribute.
18
+ - Set `focusableWhenDisabled` on a tooltip trigger `ui/button` that can be
29
19
  disabled.
30
- - Build form fields from `ui/field`. Use the ids `<control id>-error` and
31
- `<control id>-help`.
20
+ - Field ids are `<control id>-error` and `<control id>-help`.
32
21
  - Put a checkbox or radio inside its `<label>`. Give each `RadioGroup` an
33
22
  `aria-label`.
34
- - `ui/alert` sets `role="alert"`. Pass `role="status"` for a message that is
35
- not an error.
36
23
  - A form page has one `Button variant="primary"`.
37
- - `ObjectIcon` owns native object icons. Do not replace it with a provider
38
- logo.
39
-
40
- ## Tokens
41
-
42
- - Keep every color in a token in `src/tailwind.css`, so each theme can
43
- redefine it. Use `light:` only for values that no token covers.
44
- - Use `var(--color-*)` for chart series. Never use hex values.
45
- - Do not use `white/*` or `black/*` fills, borders, or rings, or hex surface
46
- colors. Shadows may use black.
24
+ - Do not replace `ObjectIcon` with a provider logo.
25
+ - Use `light:` only for values that no token covers. Chart series use
26
+ `var(--color-*)`, not hex.
47
27
  - Use `outline-dashboard-focus` for keyboard focus only.
48
- - Use the named type scale. Keep `text-2xs` for compact badges and chart
49
- annotations.
50
- - Labels use sentence case. Do not use uppercase or letter spacing.
51
-
52
- ## Navigation
53
-
28
+ - Labels use sentence case, with no uppercase or letter spacing.
54
29
  - App navigation uses pills, page links use `SegmentedNav`, and panel
55
- switches use `ui/tabs`. Keep the three levels distinct.
56
-
57
- ## Ownership
58
-
59
- - Conversation mobile geometry has hard owners: `layout/VisualViewportShell`,
60
- `conversations/ChatLayout`, and `conversations/ComposerDock`. Pages pass
61
- children only. Do not add viewport, offset, or safe-area math in pages.
62
- - Home and create must not use `ComposerDock`.
63
- - Feature surfaces stay under `conversations/` or `pages/` until two real
64
- callers share them. Add a named component on the second real use.
65
- - Keep Tailwind classes on the owning component.
30
+ switches use `ui/tabs`.
31
+ - Do not add viewport, offset, or safe-area math in pages.
32
+ `layout/VisualViewportShell`, `conversations/ChatLayout`, and
33
+ `conversations/ComposerDock` own it. Home and create do not use
34
+ `ComposerDock`.
35
+ - Keep a feature component under `conversations/` or `pages/` until a second
36
+ real caller needs it.
@@ -4,6 +4,7 @@ import { StatusChip } from "./StatusChip";
4
4
  import { ArrowUpRight, CircleAlert, Clock3 } from "lucide-react";
5
5
  import { Link } from "react-router";
6
6
  import type { AutomationCard as AutomationCardValue } from "@sentry/junior/api/schema";
7
+ import { DisplayText } from "../display-text/DisplayText";
7
8
  import { automationPath } from "../format";
8
9
 
9
10
  /** Show a saved automation inside a reply, with a preview and a detail link. */
@@ -43,7 +44,7 @@ export function AutomationCard({
43
44
  </StatusChip>
44
45
  )}
45
46
  <p className="m-0 line-clamp-3 break-words text-sm leading-relaxed text-dashboard-text-muted">
46
- {card.instruction}
47
+ <DisplayText inline text={card.instruction} />
47
48
  </p>
48
49
  <div className="flex items-start gap-2 text-xs leading-relaxed text-dashboard-text">
49
50
  <Clock3
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  Boxes,
3
3
  ChevronDown,
4
- KeyRound,
5
4
  LogOut,
6
- Plug,
7
5
  Settings,
8
6
  ShieldCheck,
9
7
  UserRound,
@@ -197,22 +195,6 @@ export function ProfileMenu({
197
195
  <Settings aria-hidden="true" size={16} strokeWidth={2} />
198
196
  Settings
199
197
  </Link>
200
- <Link
201
- className={profileLinkClass}
202
- onClick={close}
203
- to="/settings/api-tokens"
204
- >
205
- <KeyRound aria-hidden="true" size={16} strokeWidth={2} />
206
- API tokens
207
- </Link>
208
- <Link
209
- className={profileLinkClass}
210
- onClick={close}
211
- to="/settings/connections"
212
- >
213
- <Plug aria-hidden="true" size={16} strokeWidth={2} />
214
- Connected accounts
215
- </Link>
216
198
  {identity.user.isAdmin ? (
217
199
  <Link className={profileLinkClass} onClick={close} to="/admin">
218
200
  <ShieldCheck aria-hidden="true" size={16} strokeWidth={2} />
@@ -282,12 +264,6 @@ export function ProfileMenu({
282
264
  <Link className={sheetItemClass} to="/settings">
283
265
  Settings
284
266
  </Link>
285
- <Link className={sheetItemClass} to="/settings/api-tokens">
286
- API tokens
287
- </Link>
288
- <Link className={sheetItemClass} to="/settings/connections">
289
- Connected accounts
290
- </Link>
291
267
  {identity.user.isAdmin ? (
292
268
  <Link className={sheetItemClass} to="/admin">
293
269
  Admin