@sentry/junior-dashboard 0.248.0 → 0.250.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 (139) hide show
  1. package/README.md +3 -0
  2. package/dist/app.js +2 -2
  3. package/dist/client.js +40 -38
  4. package/dist/index.js +2 -2
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +3 -3
  7. package/src/client/App.tsx +19 -5
  8. package/src/client/components/ActorAvatar.tsx +7 -4
  9. package/src/client/components/AutomationCard.tsx +2 -2
  10. package/src/client/components/Button.tsx +12 -11
  11. package/src/client/components/ConversationList.tsx +3 -3
  12. package/src/client/components/ConversationSummary.tsx +2 -1
  13. package/src/client/components/DetailList.tsx +3 -3
  14. package/src/client/components/Drawer.tsx +2 -2
  15. package/src/client/components/EmptyTelemetry.tsx +1 -1
  16. package/src/client/components/Field.tsx +1 -2
  17. package/src/client/components/FilterBar.tsx +6 -6
  18. package/src/client/components/Metric.tsx +4 -4
  19. package/src/client/components/Notice.tsx +7 -7
  20. package/src/client/components/PageContentSkeleton.tsx +2 -2
  21. package/src/client/components/Pagination.tsx +2 -2
  22. package/src/client/components/ParticipantAvatarStack.tsx +4 -4
  23. package/src/client/components/ProfileMenu.tsx +17 -7
  24. package/src/client/components/README.md +122 -58
  25. package/src/client/components/SearchInput.tsx +4 -3
  26. package/src/client/components/SegmentedNav.tsx +10 -17
  27. package/src/client/components/SegmentedTabs.tsx +3 -3
  28. package/src/client/components/Select.tsx +1 -1
  29. package/src/client/components/SelectableRow.tsx +2 -2
  30. package/src/client/components/Skeleton.tsx +1 -1
  31. package/src/client/components/StatusChip.tsx +1 -1
  32. package/src/client/components/StatusDot.tsx +1 -1
  33. package/src/client/components/TextInput.tsx +1 -1
  34. package/src/client/components/Tooltip.tsx +2 -2
  35. package/src/client/components/VersionDriftBanner.tsx +2 -2
  36. package/src/client/components/charts/ActivityChart.tsx +5 -6
  37. package/src/client/components/charts/CacheWrites.tsx +3 -3
  38. package/src/client/components/charts/ChartHeader.tsx +4 -4
  39. package/src/client/components/charts/ChartLegend.tsx +1 -1
  40. package/src/client/components/charts/InputCacheChart.tsx +5 -5
  41. package/src/client/components/charts/WorkspaceUsageChart.tsx +4 -11
  42. package/src/client/components/controls/DirectorySortSelect.tsx +2 -2
  43. package/src/client/components/directory/DirectoryMetric.tsx +2 -2
  44. package/src/client/components/directory/DirectoryRowsSkeleton.tsx +5 -5
  45. package/src/client/components/directory/DirectoryTable.tsx +3 -3
  46. package/src/client/components/layout/CardHeader.tsx +4 -4
  47. package/src/client/components/layout/DashboardChrome.tsx +9 -3
  48. package/src/client/components/layout/DashboardHeader.tsx +7 -7
  49. package/src/client/components/layout/PageHeader.tsx +2 -2
  50. package/src/client/components/layout/SecondaryNavigation.tsx +7 -14
  51. package/src/client/components/layout/SectionIntro.tsx +1 -1
  52. package/src/client/components/layout/SectionTitle.tsx +1 -1
  53. package/src/client/components/metrics/StatCard.tsx +2 -2
  54. package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
  55. package/src/client/conversations/ConversationBrief.tsx +4 -4
  56. package/src/client/conversations/ConversationChildAgents.tsx +7 -4
  57. package/src/client/conversations/ConversationComposer.tsx +8 -7
  58. package/src/client/conversations/ConversationHeader.tsx +5 -5
  59. package/src/client/conversations/ConversationHeaderActions.tsx +9 -9
  60. package/src/client/conversations/ConversationHomeList.tsx +3 -3
  61. package/src/client/conversations/ConversationListStatusIcon.tsx +1 -1
  62. package/src/client/conversations/ConversationMemories.tsx +4 -4
  63. package/src/client/conversations/ConversationMeta.tsx +10 -10
  64. package/src/client/conversations/ConversationPage.tsx +2 -1
  65. package/src/client/conversations/ConversationSidebar.tsx +6 -6
  66. package/src/client/conversations/ConversationUsageBreakdown.tsx +4 -4
  67. package/src/client/conversations/ConversationWorkspace.tsx +1 -1
  68. package/src/client/conversations/ConversationWorkspaceLoading.tsx +4 -4
  69. package/src/client/conversations/CopyMarkdownButton.tsx +1 -1
  70. package/src/client/conversations/EventUsage.tsx +2 -2
  71. package/src/client/conversations/PendingMailboxStack.tsx +5 -5
  72. package/src/client/conversations/README.md +9 -0
  73. package/src/client/conversations/SubagentTranscriptDrawer.tsx +39 -23
  74. package/src/client/conversations/ToolFrame.tsx +36 -14
  75. package/src/client/conversations/TranscriptActivityGroup.tsx +10 -3
  76. package/src/client/conversations/TranscriptAttachmentsDeliveredView.tsx +4 -6
  77. package/src/client/conversations/TranscriptContextEventView.tsx +2 -2
  78. package/src/client/conversations/TranscriptEventView.tsx +1 -1
  79. package/src/client/conversations/TranscriptMarkdown.tsx +3 -3
  80. package/src/client/conversations/TranscriptMessageContextView.tsx +7 -7
  81. package/src/client/conversations/TranscriptMessageView.tsx +2 -6
  82. package/src/client/conversations/TranscriptRailEvent.tsx +20 -8
  83. package/src/client/conversations/TranscriptReasoningView.tsx +12 -12
  84. package/src/client/conversations/TranscriptStructuredEventView.tsx +16 -11
  85. package/src/client/conversations/TranscriptSubagentView.tsx +64 -39
  86. package/src/client/conversations/TranscriptText.tsx +5 -4
  87. package/src/client/conversations/TranscriptToolView.tsx +9 -11
  88. package/src/client/conversations/TranscriptTurnContextView.tsx +3 -3
  89. package/src/client/conversations/TranscriptView.tsx +3 -3
  90. package/src/client/conversations/UsageCard.tsx +1 -1
  91. package/src/client/conversations/eventTranscript.ts +7 -0
  92. package/src/client/conversations/transcriptMarkdownLinks.ts +2 -4
  93. package/src/client/conversations/transcriptStyles.ts +1 -1
  94. package/src/client/pages/ConnectionsPage.tsx +207 -0
  95. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  96. package/src/client/pages/admin/AdminPage.tsx +2 -2
  97. package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
  98. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +5 -10
  99. package/src/client/pages/automations/AutomationEditor.tsx +1 -1
  100. package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
  101. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +2 -2
  102. package/src/client/pages/automations/AutomationExecutionsPage.tsx +4 -4
  103. package/src/client/pages/automations/AutomationRow.tsx +4 -4
  104. package/src/client/pages/automations/AutomationRunsPage.tsx +3 -3
  105. package/src/client/pages/automations/AutomationScheduleFields.tsx +1 -1
  106. package/src/client/pages/automations/AutomationVersionsPage.tsx +1 -1
  107. package/src/client/pages/code/CodeActivityChart.tsx +7 -6
  108. package/src/client/pages/code/CodeChangeRow.tsx +1 -1
  109. package/src/client/pages/code/CodePage.tsx +5 -5
  110. package/src/client/pages/code/RepositoryPage.tsx +2 -2
  111. package/src/client/pages/dev/ChartFixtures.tsx +265 -21
  112. package/src/client/pages/dev/ComponentsPage.tsx +40 -146
  113. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +206 -0
  114. package/src/client/pages/locations/LocationDetailPage.tsx +4 -4
  115. package/src/client/pages/locations/LocationDirectory.tsx +1 -1
  116. package/src/client/pages/locations/PrivateActivityCard.tsx +3 -3
  117. package/src/client/pages/memory/MemoryCostChart.tsx +3 -5
  118. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +5 -5
  119. package/src/client/pages/memory/MemoryPage.tsx +15 -15
  120. package/src/client/pages/memory/MemoryTimeline.tsx +3 -5
  121. package/src/client/pages/people/ContributionGrid.tsx +8 -5
  122. package/src/client/pages/people/PersonProfilePage.tsx +3 -6
  123. package/src/client/pages/people/ProfilePluginReports.tsx +2 -2
  124. package/src/client/pages/system/GuardianActivity.tsx +9 -9
  125. package/src/client/pages/system/PluginBarChart.tsx +4 -6
  126. package/src/client/pages/system/PluginDetails.tsx +11 -7
  127. package/src/client/pages/system/PluginPanels.tsx +8 -8
  128. package/src/client/pages/system/PluginReportRecordSet.tsx +14 -14
  129. package/src/client/pages/system/PluginReports.tsx +7 -11
  130. package/src/client/pages/system/SkillInventory.tsx +9 -9
  131. package/src/client/pages/system/SystemActivity.tsx +1 -1
  132. package/src/client/pages/system/SystemPage.tsx +3 -6
  133. package/src/client/pages/system/WorkspaceDetails.tsx +2 -5
  134. package/src/client/pages/system/WorkspaceEditor.tsx +2 -2
  135. package/src/client/pages/system/WorkspaceList.tsx +2 -2
  136. package/src/client/pages/user/PluginUserPage.tsx +3 -3
  137. package/src/client/styles.ts +5 -4
  138. package/src/client/types.ts +2 -0
  139. package/src/tailwind.css +36 -38
@@ -13,19 +13,19 @@ export function DirectoryRowsSkeleton(props: { wideRuntime?: boolean }) {
13
13
  {Array.from({ length: 5 }, (_, index) => (
14
14
  <div
15
15
  aria-hidden="true"
16
- className={`grid min-w-0 ${gridColumns} items-center gap-4 border-b border-white/[0.055] px-4 py-3.5 last:border-b-0 max-md:gap-x-3 max-md:gap-y-4`}
16
+ className={`grid min-w-0 ${gridColumns} items-center gap-4 border-b border-dashboard-border-subtle px-4 py-3.5 last:border-b-0 max-md:gap-x-3 max-md:gap-y-4`}
17
17
  key={index}
18
18
  >
19
19
  <div className="flex min-w-0 items-center gap-3 max-md:col-span-3">
20
- <span className="size-9 shrink-0 rounded bg-white/[0.07]" />
20
+ <span className="size-9 shrink-0 rounded-md bg-dashboard-fill-hover" />
21
21
  <div className="grid min-w-0 flex-1 gap-2">
22
- <span className="h-3.5 w-2/5 rounded bg-white/[0.08]" />
23
- <span className="h-2.5 w-3/5 rounded bg-white/[0.045]" />
22
+ <span className="h-3.5 w-2/5 rounded-md bg-dashboard-fill-strong" />
23
+ <span className="h-2.5 w-3/5 rounded-md bg-dashboard-fill-soft" />
24
24
  </div>
25
25
  </div>
26
26
  {[0, 1, 2].map((metric) => (
27
27
  <span
28
- className="h-4 w-10 justify-self-end rounded bg-white/[0.06]"
28
+ className="h-4 w-10 justify-self-end rounded-md bg-dashboard-fill-hover"
29
29
  key={metric}
30
30
  />
31
31
  ))}
@@ -43,7 +43,7 @@ export function DirectoryTable(props: {
43
43
  <div
44
44
  aria-hidden="true"
45
45
  className={cn(
46
- "grid items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-mono text-xs uppercase tracking-[0.1em] text-dashboard-text-muted max-md:hidden",
46
+ "grid items-center gap-4 border-b border-dashboard-border-subtle bg-dashboard-overlay-soft px-4 py-2.5 font-sans font-medium text-xs text-dashboard-text-muted max-md:hidden",
47
47
  directoryGridClass,
48
48
  )}
49
49
  >
@@ -84,7 +84,7 @@ export function DirectoryIdentity(props: {
84
84
  <div className="flex min-w-0 items-center gap-3 max-md:col-span-3">
85
85
  <span
86
86
  className={cn(
87
- "grid size-9 shrink-0 place-items-center rounded border border-white/10 transition-colors",
87
+ "grid size-9 shrink-0 place-items-center rounded-md border border-dashboard-border-strong transition-colors",
88
88
  props.iconClassName,
89
89
  )}
90
90
  >
@@ -95,7 +95,7 @@ export function DirectoryIdentity(props: {
95
95
  {props.title}
96
96
  </div>
97
97
  {props.description ? (
98
- <div className="mt-1 truncate font-mono text-xs leading-tight text-dashboard-text-muted">
98
+ <div className="mt-1 truncate font-sans text-xs leading-tight text-dashboard-text-muted">
99
99
  {props.description}
100
100
  </div>
101
101
  ) : null}
@@ -12,22 +12,22 @@ export function CardHeader(props: {
12
12
  return (
13
13
  <div
14
14
  className={cn(
15
- "flex flex-wrap items-start justify-between gap-4 border-b border-white/[0.06] px-4 py-4 sm:px-5",
15
+ "flex flex-wrap items-start justify-between gap-4 border-b border-dashboard-border-subtle px-4 py-4 sm:px-5",
16
16
  props.className,
17
17
  )}
18
18
  >
19
19
  <div>
20
- <h3 className="m-0 font-mono text-xs font-medium uppercase tracking-[0.14em] text-dashboard-text-muted">
20
+ <h3 className="m-0 font-sans text-xs font-medium text-dashboard-text-muted">
21
21
  {props.title}
22
22
  </h3>
23
23
  {props.description ? (
24
- <p className="mt-1 mb-0 font-mono text-xs leading-relaxed text-dashboard-text-muted">
24
+ <p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
25
25
  {props.description}
26
26
  </p>
27
27
  ) : null}
28
28
  </div>
29
29
  {props.trailing ? (
30
- <div className="font-mono text-xs text-dashboard-text-muted">
30
+ <div className="font-sans text-xs text-dashboard-text-muted">
31
31
  {props.trailing}
32
32
  </div>
33
33
  ) : null}
@@ -101,7 +101,11 @@ export function DashboardChromeProvider(props: { children: ReactNode }) {
101
101
  );
102
102
  }
103
103
 
104
- /** Sticky shell chrome: primary header, secondary navigation slot, and banner. */
104
+ /**
105
+ * Sticky shell chrome: primary header, secondary navigation slot, and banner.
106
+ * One bottom border closes the header and page links, so page link
107
+ * underlines sit on it. Banners render below that border.
108
+ */
105
109
  export function DashboardChrome(props: {
106
110
  banner?: ReactNode;
107
111
  header: ReactNode;
@@ -113,8 +117,10 @@ export function DashboardChrome(props: {
113
117
 
114
118
  return (
115
119
  <div className="sticky top-0 z-30 bg-dashboard-bg/95 pt-[env(safe-area-inset-top)]">
116
- {props.header}
117
- <div className="hidden md:block" ref={chrome.secondarySlotRef} />
120
+ <div className="border-b border-dashboard-border-strong">
121
+ {props.header}
122
+ <div className="hidden md:block" ref={chrome.secondarySlotRef} />
123
+ </div>
118
124
  {props.banner}
119
125
  </div>
120
126
  );
@@ -74,14 +74,14 @@ export function DashboardHeader(props: {
74
74
  cn(
75
75
  "shrink-0 whitespace-nowrap rounded-lg px-3 py-2 font-sans text-sm font-medium tracking-normal no-underline transition-colors",
76
76
  isActive
77
- ? "bg-cyan-300/[0.1] text-cyan-50"
77
+ ? "bg-dashboard-accent/[0.1] text-dashboard-accent-text"
78
78
  : cn("hover:bg-dashboard-fill-soft", dashboardInteractiveTextClass),
79
79
  );
80
80
  const sheetLinkClass = ({ isActive }: { isActive: boolean }) =>
81
81
  cn(
82
- "rounded-lg px-3 py-3 font-mono text-sm font-medium tracking-normal no-underline transition-colors",
82
+ "rounded-lg px-3 py-3 font-sans text-sm font-medium no-underline transition-colors",
83
83
  isActive
84
- ? "bg-cyan-300/[0.1] text-cyan-50"
84
+ ? "bg-dashboard-accent/[0.1] text-dashboard-accent-text"
85
85
  : cn("hover:bg-dashboard-fill-soft", dashboardInteractiveTextClass),
86
86
  );
87
87
 
@@ -148,7 +148,7 @@ export function DashboardHeader(props: {
148
148
  }, [props.mobileNavigationOpen]);
149
149
 
150
150
  return (
151
- <header className="relative border-b border-dashboard-border-emphasis bg-dashboard-bg">
151
+ <header className="relative bg-dashboard-bg">
152
152
  <div
153
153
  className={cn(
154
154
  props.compact ? "w-full min-w-0" : dashboardContainerClass,
@@ -200,7 +200,7 @@ export function DashboardHeader(props: {
200
200
  )}
201
201
  to="/"
202
202
  >
203
- <span className="truncate font-mono text-sm font-semibold md:hidden">
203
+ <span className="truncate font-sans text-sm font-semibold md:hidden">
204
204
  {getDashboardAgentName()}
205
205
  </span>
206
206
  <span className="hidden md:block">
@@ -252,7 +252,7 @@ export function DashboardHeader(props: {
252
252
  <div className="flex items-center justify-between gap-3 border-b border-dashboard-border-subtle bg-dashboard-surface-raised px-3 py-2 pt-[max(0.5rem,env(safe-area-inset-top))]">
253
253
  <Link
254
254
  aria-label={`${getDashboardAgentName()} home`}
255
- className="min-w-0 truncate font-mono text-sm font-semibold text-inherit no-underline"
255
+ className="min-w-0 truncate font-sans text-sm font-semibold text-inherit no-underline"
256
256
  to="/"
257
257
  >
258
258
  {getDashboardAgentName()}
@@ -307,7 +307,7 @@ export function DashboardHeader(props: {
307
307
  </div>
308
308
  ) : null}
309
309
  {props.version ? (
310
- <p className="m-0 font-mono text-xs text-dashboard-text-muted">
310
+ <p className="m-0 font-sans text-xs text-dashboard-text-muted">
311
311
  junior version {props.version}
312
312
  </p>
313
313
  ) : null}
@@ -36,12 +36,12 @@ export function PageHeader(props: {
36
36
  const actions = props.actions ?? rangeActions;
37
37
 
38
38
  return (
39
- <header className="flex min-w-0 items-center justify-between gap-6 border-b border-white/[0.05] pb-4 sm:pb-6 max-md:flex-col max-md:items-stretch">
39
+ <header className="flex min-w-0 items-center justify-between gap-6 border-b border-dashboard-border-subtle pb-4 sm:pb-6 max-md:flex-col max-md:items-stretch">
40
40
  <div className="min-w-0">
41
41
  <h2 className="m-0 font-display text-3xl font-light leading-none tracking-[-0.03em] text-dashboard-text sm:text-4xl">
42
42
  {props.title}
43
43
  </h2>
44
- <div className="mt-2 max-w-2xl font-mono text-xs leading-relaxed text-dashboard-text-muted sm:text-sm">
44
+ <div className="mt-2 max-w-2xl font-sans text-xs leading-relaxed text-dashboard-text-muted sm:text-sm">
45
45
  {props.description}
46
46
  </div>
47
47
  </div>
@@ -27,31 +27,24 @@ export function SecondaryNavigation(props: {
27
27
  }) {
28
28
  const menuLinkClass = ({ isActive }: { isActive: boolean }) =>
29
29
  cn(
30
- "rounded-lg px-3 py-3 pl-6 font-mono text-sm font-medium no-underline transition-colors",
30
+ "rounded-lg px-3 py-3 pl-6 font-sans text-sm font-medium no-underline transition-colors",
31
31
  isActive
32
- ? "bg-cyan-300/[0.1] text-cyan-50"
33
- : cn("hover:bg-white/[0.035]", dashboardInteractiveTextClass),
32
+ ? "bg-dashboard-accent/[0.1] text-dashboard-accent-text"
33
+ : cn("hover:bg-dashboard-fill-faint", dashboardInteractiveTextClass),
34
34
  );
35
35
 
36
36
  return (
37
37
  <div className="min-w-0">
38
38
  <SecondaryNavigationPortal
39
39
  desktop={
40
- <div className="border-b border-white/[0.06] bg-white/[0.018]">
41
- <div
42
- className={cn(
43
- dashboardContainerClass,
44
- "min-w-0 px-4 py-2 md:px-8",
45
- )}
46
- >
47
- <SegmentedNav ariaLabel={props.ariaLabel} items={props.items} />
48
- </div>
40
+ <div className={cn(dashboardContainerClass, "min-w-0 px-4 md:px-8")}>
41
+ <SegmentedNav ariaLabel={props.ariaLabel} items={props.items} />
49
42
  </div>
50
43
  }
51
44
  mobile={
52
45
  <nav
53
46
  aria-label={props.ariaLabel}
54
- className="mt-3 grid gap-1 border-t border-white/[0.07] pt-3"
47
+ className="mt-3 grid gap-1 border-t border-dashboard-border pt-3"
55
48
  >
56
49
  {props.items.map((item) => (
57
50
  <SiblingPageLink
@@ -63,7 +56,7 @@ export function SecondaryNavigation(props: {
63
56
  </nav>
64
57
  }
65
58
  />
66
- <div className="px-4 pt-4 sm:px-8 md:hidden">
59
+ <div className="border-b border-dashboard-border px-4 pt-2 sm:px-8 md:hidden">
67
60
  <SegmentedNav ariaLabel={props.ariaLabel} fill items={props.items} />
68
61
  </div>
69
62
  {props.children}
@@ -16,7 +16,7 @@ export function SectionIntro(props: {
16
16
  {props.title}
17
17
  </h2>
18
18
  {props.description ? (
19
- <p className="mt-1.5 mb-0 max-w-2xl font-mono text-xs leading-relaxed text-dashboard-text-muted">
19
+ <p className="mt-1.5 mb-0 max-w-2xl font-sans text-xs leading-relaxed text-dashboard-text-muted">
20
20
  {props.description}
21
21
  </p>
22
22
  ) : null}
@@ -3,7 +3,7 @@ import type { ReactNode } from "react";
3
3
  /** Render compact section titles that fit inside operational panels. */
4
4
  export function SectionTitle(props: { children: ReactNode }) {
5
5
  return (
6
- <div className="min-w-0 break-words font-mono text-xs font-medium uppercase leading-tight tracking-[0.14em] text-dashboard-text-muted">
6
+ <div className="min-w-0 break-words font-sans text-xs font-medium leading-tight text-dashboard-text-muted">
7
7
  {props.children}
8
8
  </div>
9
9
  );
@@ -14,7 +14,7 @@ export function StatCard(props: {
14
14
  return (
15
15
  <Card className="relative p-4 sm:p-5">
16
16
  <div className="flex items-start justify-between gap-4">
17
- <div className="font-mono text-xs font-medium uppercase tracking-[0.14em] text-dashboard-text-muted">
17
+ <div className="font-sans text-xs font-medium text-dashboard-text-muted">
18
18
  {props.label}
19
19
  </div>
20
20
  <Icon aria-hidden="true" className="text-amber-400/55" size={15} />
@@ -23,7 +23,7 @@ export function StatCard(props: {
23
23
  {props.value}
24
24
  </div>
25
25
  {props.detail ? (
26
- <div className="mt-2 font-mono text-xs leading-relaxed text-dashboard-text-muted">
26
+ <div className="mt-2 font-sans text-xs leading-relaxed text-dashboard-text-muted">
27
27
  {props.detail}
28
28
  </div>
29
29
  ) : null}
@@ -102,7 +102,7 @@ function ArchivedNotice(props: {
102
102
  >
103
103
  {restore.error ? (
104
104
  <div
105
- className="border-t border-rose-300/25 bg-rose-400/[0.12] px-3 py-2 font-mono text-xs text-rose-50/85"
105
+ className="border-t border-rose-300/25 bg-rose-400/[0.12] px-3 py-2 font-sans text-xs text-rose-50/85"
106
106
  role="alert"
107
107
  >
108
108
  Could not restore the conversation.
@@ -49,7 +49,7 @@ export function ConversationBrief(props: {
49
49
  <StatusChip tone={outcomeTones[content.outcome.status]}>
50
50
  {displayLabel(content.outcome.status)}
51
51
  </StatusChip>
52
- <span className="font-mono text-xs text-dashboard-text-muted">
52
+ <span className="text-xs text-dashboard-text-muted">
53
53
  Brief v{props.brief.version} · updated{" "}
54
54
  {formatTime(props.brief.updatedAt, {
55
55
  dateStyle: "medium",
@@ -101,14 +101,14 @@ export function ConversationBrief(props: {
101
101
  key={`${link.kind}:${link.url}`}
102
102
  >
103
103
  <a
104
- className="min-w-0 break-all font-medium text-cyan-200 underline decoration-cyan-300/30 underline-offset-2 hover:text-cyan-100"
104
+ className="min-w-0 break-all font-medium text-dashboard-accent-text underline decoration-dashboard-accent/30 underline-offset-2 hover:text-dashboard-accent-text"
105
105
  href={link.url}
106
106
  rel="noopener noreferrer"
107
107
  target="_blank"
108
108
  >
109
109
  {link.label}
110
110
  </a>
111
- <span className="font-mono text-xs text-dashboard-text-muted">
111
+ <span className="text-xs text-dashboard-text-muted">
112
112
  {displayLabel(link.kind)}
113
113
  </span>
114
114
  {link.status ? (
@@ -172,7 +172,7 @@ function BriefDecisions(props: { decisions: BriefContent["decisions"] }) {
172
172
  <span className="min-w-0">{decision.text}</span>
173
173
  </div>
174
174
  {decision.by ? (
175
- <div className="mt-0.5 font-mono text-xs text-dashboard-text-muted">
175
+ <div className="mt-0.5 text-xs text-dashboard-text-muted">
176
176
  by · {decision.by}
177
177
  </div>
178
178
  ) : null}
@@ -124,13 +124,16 @@ function ChildAgentRow(props: {
124
124
 
125
125
  return (
126
126
  <button
127
- className="grid w-full min-w-0 cursor-pointer gap-1.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface px-3 py-2.5 text-left text-inherit transition-colors duration-200 hover:border-dashboard-border-emphasis hover:bg-dashboard-surface-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus"
127
+ className="grid w-full min-w-0 cursor-pointer gap-1.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5 text-left text-inherit transition-colors duration-200 hover:border-dashboard-border-emphasis hover:bg-dashboard-surface-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus"
128
128
  onClick={() =>
129
129
  props.onOpen({
130
130
  conversationId: agent.conversationId,
131
131
  ...(agent.redacted
132
132
  ? undefined
133
- : { label: agent.title ?? agent.name }),
133
+ : {
134
+ ...(agent.name ? { agentName: agent.name } : undefined),
135
+ label: agent.title ?? agent.name,
136
+ }),
134
137
  status: agent.status,
135
138
  })
136
139
  }
@@ -143,7 +146,7 @@ function ChildAgentRow(props: {
143
146
  Private subagent
144
147
  </span>
145
148
  ) : (
146
- <span className="min-w-0 truncate font-mono text-dashboard-text-muted">
149
+ <span className="min-w-0 truncate font-medium text-dashboard-text-muted">
147
150
  {agent.name ?? "subagent"}
148
151
  </span>
149
152
  )}
@@ -185,7 +188,7 @@ function ChildAgentRow(props: {
185
188
  </span>
186
189
  ) : null}
187
190
  {stats.length > 0 ? (
188
- <span className="block font-mono text-xs tabular-nums text-dashboard-text-muted">
191
+ <span className="block text-xs tabular-nums text-dashboard-text-muted">
189
192
  {stats.join(" · ")}
190
193
  </span>
191
194
  ) : null}
@@ -351,7 +351,7 @@ export const ConversationComposer = memo(function ConversationComposer(
351
351
  {props.error || imageError ? (
352
352
  <div
353
353
  aria-live="polite"
354
- className="min-w-0 font-mono text-xs leading-relaxed text-red-300/80"
354
+ className="min-w-0 font-sans text-xs leading-relaxed text-rose-300"
355
355
  >
356
356
  {imageError ?? props.error}
357
357
  </div>
@@ -361,10 +361,10 @@ export const ConversationComposer = memo(function ConversationComposer(
361
361
  // Accessory chrome needs a full footer row. Side-by-side mobile is
362
362
  // only for the bare reply dock.
363
363
  props.footerStart
364
- ? "block overflow-hidden focus-within:border-cyan-300/35 focus-within:ring-1 focus-within:ring-cyan-300/35"
365
- : "grid grid-cols-[minmax(0,1fr)_auto] items-end overflow-hidden focus-within:border-cyan-300/35 focus-within:ring-1 focus-within:ring-cyan-300/35 md:block",
364
+ ? "block overflow-hidden focus-within:border-dashboard-focus/45 focus-within:ring-1 focus-within:ring-dashboard-focus/25"
365
+ : "grid grid-cols-[minmax(0,1fr)_auto] items-end overflow-hidden focus-within:border-dashboard-accent/35 focus-within:ring-1 focus-within:ring-dashboard-focus/35 md:block",
366
366
  dashboardComposerSurfaceClass,
367
- dragging && "ring-2 ring-cyan-300",
367
+ dragging && "ring-2 ring-dashboard-focus",
368
368
  )}
369
369
  onDragOver={(event) => {
370
370
  if (!event.dataTransfer.types.includes("Files")) return;
@@ -431,7 +431,7 @@ export const ConversationComposer = memo(function ConversationComposer(
431
431
  autoCapitalize="off"
432
432
  autoComplete="off"
433
433
  autoCorrect="off"
434
- className="min-h-12 max-h-28 w-full resize-none overflow-y-auto border-0 bg-transparent px-3 py-3 font-sans text-base leading-relaxed text-dashboard-text outline-none placeholder:text-dashboard-text-muted/65 md:min-h-14 md:max-h-none md:resize-y md:overflow-visible md:px-4 md:py-3"
434
+ className="min-h-12 max-h-28 w-full resize-none overflow-y-auto border-0 bg-transparent px-3 py-3 font-sans text-base leading-relaxed text-dashboard-text outline-none placeholder:text-dashboard-text-muted/65 md:min-h-14 md:max-h-none md:overflow-visible md:px-4 md:py-3"
435
435
  disabled={Boolean(props.restoreDraftOnError && createPending)}
436
436
  enterKeyHint="send"
437
437
  id={id}
@@ -462,6 +462,7 @@ export const ConversationComposer = memo(function ConversationComposer(
462
462
  aria-label="Attach images"
463
463
  disabled={sendLocked || readingImages}
464
464
  onClick={() => fileInputRef.current?.click()}
465
+ size="icon"
465
466
  title="Attach images (up to 3 MB total)"
466
467
  type="button"
467
468
  >
@@ -478,7 +479,6 @@ export const ConversationComposer = memo(function ConversationComposer(
478
479
  {props.onStop ? (
479
480
  <Button
480
481
  aria-label={props.stopPending ? "Stopping" : "Stop"}
481
- className="rounded-lg font-sans"
482
482
  disabled={props.stopPending || !online}
483
483
  onClick={() => void stopTurn()}
484
484
  title="Stop Junior"
@@ -491,7 +491,7 @@ export const ConversationComposer = memo(function ConversationComposer(
491
491
  ) : null}
492
492
  <Button
493
493
  aria-label={sendLocked ? "Sending message" : props.submitLabel}
494
- className="rounded-lg !border-0 !bg-cyan-100 font-sans !text-dashboard-text-inverse hover:!bg-cyan-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-cyan-300/55 disabled:!bg-dashboard-fill-hover disabled:!text-dashboard-text-muted"
494
+ className="disabled:!border-transparent disabled:!bg-dashboard-fill-hover disabled:!text-dashboard-text-muted disabled:!opacity-100"
495
495
  disabled={
496
496
  (!canSend && !images.length) ||
497
497
  !online ||
@@ -499,6 +499,7 @@ export const ConversationComposer = memo(function ConversationComposer(
499
499
  readingImages
500
500
  }
501
501
  title={sendLocked ? "Sending message" : props.submitLabel}
502
+ tone="primary"
502
503
  type="submit"
503
504
  >
504
505
  <Send aria-hidden="true" size={14} />
@@ -114,7 +114,7 @@ export function ConversationHeader(props: {
114
114
  <Ellipsis aria-hidden="true" size={18} strokeWidth={2} />
115
115
  </HeaderIconButton>
116
116
  {mobileMenuOpen ? (
117
- <div className="absolute right-0 top-[calc(100%+0.35rem)] z-40 w-56 rounded-xl border border-white/[0.08] bg-dashboard-surface-raised/95 p-1.5 shadow-2xl shadow-black/75 backdrop-blur-xl">
117
+ <div className="absolute right-0 top-[calc(100%+0.35rem)] z-40 w-56 rounded-xl border border-dashboard-border-strong bg-dashboard-surface-raised/95 p-1.5 shadow-2xl shadow-black/75 backdrop-blur-xl">
118
118
  <ConversationHeaderActions
119
119
  archive={props.archive}
120
120
  conversationId={props.conversationId}
@@ -138,7 +138,7 @@ export function ConversationHeader(props: {
138
138
  />
139
139
  {openMobileNavigation ? (
140
140
  <button
141
- className="mt-0.5 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-white/10 focus-visible:bg-white/10 focus-visible:outline-none"
141
+ className="mt-0.5 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-hover focus-visible:bg-dashboard-fill-hover focus-visible:outline-none"
142
142
  onClick={() => {
143
143
  setMobileMenuOpen(false);
144
144
  openMobileNavigation();
@@ -175,7 +175,7 @@ export function ConversationHeader(props: {
175
175
  <MobileHeaderLivePortal>{liveIndicator}</MobileHeaderLivePortal>
176
176
  <header
177
177
  className={cn(
178
- "sticky top-0 z-10 -mx-4 border-b border-dashboard-border-emphasis bg-dashboard-bg md:-mx-7",
178
+ "sticky top-0 z-10 -mx-4 border-b border-dashboard-border bg-dashboard-bg md:-mx-7",
179
179
  !showMobileHeader && "hidden md:block",
180
180
  )}
181
181
  >
@@ -221,12 +221,12 @@ export function ConversationHeader(props: {
221
221
  {props.linkedWork ? (
222
222
  <div
223
223
  aria-label="Linked work"
224
- className="border-t border-dashboard-border bg-dashboard-surface-panel px-4 py-3 md:px-7"
224
+ className="border-t border-dashboard-border-subtle px-4 py-2.5 md:px-7"
225
225
  >
226
226
  {props.linkedWork}
227
227
  </div>
228
228
  ) : null}
229
- <div className="hidden border-t border-dashboard-border px-7 md:flex">
229
+ <div className="hidden border-t border-dashboard-border-subtle px-7 md:flex">
230
230
  <TranscriptViewToggle
231
231
  labelled
232
232
  onChange={props.onViewChange}
@@ -96,7 +96,7 @@ export function ConversationHeaderActions(props: {
96
96
  <Button
97
97
  aria-label="Conversation details"
98
98
  aria-pressed={props.detailsOpen}
99
- className="ml-2 rounded-lg font-sans text-xs"
99
+ className="ml-2"
100
100
  onClick={props.onDetailsClick}
101
101
  >
102
102
  <PanelRight aria-hidden="true" size={15} strokeWidth={2} />
@@ -153,13 +153,13 @@ function ViewModeButton(props: {
153
153
  <ToggleButton
154
154
  aria-label={props.label}
155
155
  className={cn(
156
- "!normal-case !no-underline font-sans",
156
+ "!no-underline",
157
157
  props.labelled
158
- ? "relative flex h-11 items-center gap-2 px-0 py-0 text-xs after:absolute after:inset-x-0 after:bottom-0 after:h-0.5"
159
- : "grid size-7 place-items-center rounded px-0 py-0",
158
+ ? "relative flex h-11 items-center gap-2 rounded-none px-0 py-0 text-sm after:absolute after:inset-x-0 after:bottom-0 after:h-0.5"
159
+ : "grid size-7 place-items-center rounded-md px-0 py-0",
160
160
  props.active &&
161
161
  (props.labelled
162
- ? "text-cyan-100 after:bg-cyan-100"
162
+ ? "text-dashboard-text after:bg-dashboard-text-solid"
163
163
  : "bg-dashboard-fill-hover text-dashboard-text"),
164
164
  )}
165
165
  onClick={props.onClick}
@@ -191,7 +191,7 @@ export function HeaderIconButton(props: {
191
191
  aria-pressed={props.pressed}
192
192
  className={cn(
193
193
  "text-dashboard-text-muted",
194
- props.pressed && "bg-white/10 text-dashboard-text",
194
+ props.pressed && "bg-dashboard-fill-strong text-dashboard-text",
195
195
  )}
196
196
  onClick={props.onClick}
197
197
  size="icon"
@@ -233,8 +233,8 @@ function MenuActionButton(props: {
233
233
  aria-label={props.label}
234
234
  aria-pressed={props.pressed}
235
235
  className={cn(
236
- "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-white/10 focus-visible:bg-white/10 focus-visible:outline-none",
237
- props.pressed && "bg-white/[0.08]",
236
+ "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",
237
+ props.pressed && "bg-dashboard-fill-strong",
238
238
  )}
239
239
  onClick={props.onClick}
240
240
  type="button"
@@ -258,7 +258,7 @@ function ArchiveConversationButton(
258
258
  return (
259
259
  <button
260
260
  aria-label={label}
261
- 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-white/10 focus-visible:bg-white/10 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
261
+ 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"
262
262
  disabled={props.disabled}
263
263
  onClick={props.onClick}
264
264
  type="button"
@@ -172,7 +172,7 @@ function ConversationCardSection(props: {
172
172
  return (
173
173
  <section aria-labelledby={`conversation-section-${props.section.key}`}>
174
174
  <h3
175
- className="mb-2 mt-0 px-1 font-display text-xs font-semibold uppercase tracking-[0.08em] text-dashboard-text-muted"
175
+ className="mb-2 mt-0 px-1 font-sans text-xs font-medium text-dashboard-text-muted"
176
176
  id={`conversation-section-${props.section.key}`}
177
177
  >
178
178
  {props.section.label}
@@ -238,7 +238,7 @@ function ConversationCard(props: {
238
238
  </div>
239
239
  <div className="flex items-start gap-3">
240
240
  <Link
241
- className="pointer-events-auto whitespace-nowrap font-mono text-xs text-dashboard-text-muted hover:text-dashboard-text focus:outline-none focus:ring-2 focus:ring-dashboard-focus"
241
+ className="pointer-events-auto whitespace-nowrap font-sans text-xs tabular-nums text-dashboard-text-muted hover:text-dashboard-text focus:outline-none focus:ring-2 focus:ring-dashboard-focus"
242
242
  to={conversationPath(conversation.id)}
243
243
  >
244
244
  {formatRelativeTime(conversation.lastSeenAt)}
@@ -272,7 +272,7 @@ function ConversationCard(props: {
272
272
  {status === "active" ? "Working…" : "No recent message"}
273
273
  </p>
274
274
  )}
275
- <div className="relative z-[1] flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 font-mono text-xs text-dashboard-text-muted pointer-events-none">
275
+ <div className="relative z-[1] flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 font-sans text-xs text-dashboard-text-muted pointer-events-none">
276
276
  {location ? <span className="truncate">{location}</span> : null}
277
277
  {location && participants.length > 0 ? (
278
278
  <span aria-hidden="true">·</span>
@@ -45,7 +45,7 @@ export function ConversationListStatusIcon(props: {
45
45
  className={cn(
46
46
  "size-1.5 shrink-0 rounded-full",
47
47
  props.status === "failed" && "bg-rose-300",
48
- props.status === "idle" && "bg-white/25",
48
+ props.status === "idle" && "bg-dashboard-text-faint",
49
49
  )}
50
50
  />
51
51
  );
@@ -66,17 +66,17 @@ export function ConversationMemories(props: { conversationId: string }) {
66
66
  <ol className="m-0 grid list-none gap-3 p-0">
67
67
  {query.data.memories.map((memory) => (
68
68
  <li
69
- className="grid gap-2 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface px-3 py-3"
69
+ className="grid gap-2 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-3"
70
70
  key={memory.id}
71
71
  >
72
72
  <p className="m-0 whitespace-pre-wrap break-words text-sm leading-relaxed text-dashboard-text">
73
73
  {memory.content}
74
74
  </p>
75
- <div className="flex flex-wrap items-center gap-1.5 font-mono text-xs text-dashboard-text-muted">
76
- <span className="rounded border border-dashboard-border-subtle bg-dashboard-surface-raised px-1.5 py-0.5">
75
+ <div className="flex flex-wrap items-center gap-1.5 text-xs tabular-nums text-dashboard-text-muted">
76
+ <span className="rounded-md border border-dashboard-border-subtle bg-dashboard-surface-raised px-1.5 py-0.5">
77
77
  {memory.kind}
78
78
  </span>
79
- <span className="rounded border border-dashboard-border-subtle bg-dashboard-surface-raised px-1.5 py-0.5">
79
+ <span className="rounded-md border border-dashboard-border-subtle bg-dashboard-surface-raised px-1.5 py-0.5">
80
80
  {memory.visibility}
81
81
  </span>
82
82
  <time className="ml-auto" dateTime={memory.capturedAt}>