@sentry/junior-dashboard 0.248.0 → 0.249.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 (136) hide show
  1. package/dist/app.js +2 -2
  2. package/dist/client.js +36 -35
  3. package/dist/index.js +2 -2
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +3 -3
  6. package/src/client/App.tsx +1 -5
  7. package/src/client/components/ActorAvatar.tsx +7 -4
  8. package/src/client/components/AutomationCard.tsx +2 -2
  9. package/src/client/components/Button.tsx +12 -11
  10. package/src/client/components/ConversationList.tsx +3 -3
  11. package/src/client/components/ConversationSummary.tsx +2 -1
  12. package/src/client/components/DetailList.tsx +3 -3
  13. package/src/client/components/Drawer.tsx +2 -2
  14. package/src/client/components/EmptyTelemetry.tsx +1 -1
  15. package/src/client/components/Field.tsx +1 -2
  16. package/src/client/components/FilterBar.tsx +6 -6
  17. package/src/client/components/Metric.tsx +4 -4
  18. package/src/client/components/Notice.tsx +7 -7
  19. package/src/client/components/PageContentSkeleton.tsx +2 -2
  20. package/src/client/components/Pagination.tsx +2 -2
  21. package/src/client/components/ParticipantAvatarStack.tsx +4 -4
  22. package/src/client/components/ProfileMenu.tsx +5 -7
  23. package/src/client/components/README.md +122 -58
  24. package/src/client/components/SearchInput.tsx +4 -3
  25. package/src/client/components/SegmentedNav.tsx +10 -17
  26. package/src/client/components/SegmentedTabs.tsx +3 -3
  27. package/src/client/components/Select.tsx +1 -1
  28. package/src/client/components/SelectableRow.tsx +2 -2
  29. package/src/client/components/Skeleton.tsx +1 -1
  30. package/src/client/components/StatusChip.tsx +1 -1
  31. package/src/client/components/StatusDot.tsx +1 -1
  32. package/src/client/components/TextInput.tsx +1 -1
  33. package/src/client/components/Tooltip.tsx +2 -2
  34. package/src/client/components/VersionDriftBanner.tsx +2 -2
  35. package/src/client/components/charts/ActivityChart.tsx +5 -6
  36. package/src/client/components/charts/CacheWrites.tsx +3 -3
  37. package/src/client/components/charts/ChartHeader.tsx +4 -4
  38. package/src/client/components/charts/ChartLegend.tsx +1 -1
  39. package/src/client/components/charts/InputCacheChart.tsx +5 -5
  40. package/src/client/components/charts/WorkspaceUsageChart.tsx +4 -11
  41. package/src/client/components/controls/DirectorySortSelect.tsx +2 -2
  42. package/src/client/components/directory/DirectoryMetric.tsx +2 -2
  43. package/src/client/components/directory/DirectoryRowsSkeleton.tsx +5 -5
  44. package/src/client/components/directory/DirectoryTable.tsx +3 -3
  45. package/src/client/components/layout/CardHeader.tsx +4 -4
  46. package/src/client/components/layout/DashboardChrome.tsx +9 -3
  47. package/src/client/components/layout/DashboardHeader.tsx +7 -7
  48. package/src/client/components/layout/PageHeader.tsx +2 -2
  49. package/src/client/components/layout/SecondaryNavigation.tsx +7 -14
  50. package/src/client/components/layout/SectionIntro.tsx +1 -1
  51. package/src/client/components/layout/SectionTitle.tsx +1 -1
  52. package/src/client/components/metrics/StatCard.tsx +2 -2
  53. package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
  54. package/src/client/conversations/ConversationBrief.tsx +4 -4
  55. package/src/client/conversations/ConversationChildAgents.tsx +7 -4
  56. package/src/client/conversations/ConversationComposer.tsx +8 -7
  57. package/src/client/conversations/ConversationHeader.tsx +5 -5
  58. package/src/client/conversations/ConversationHeaderActions.tsx +9 -9
  59. package/src/client/conversations/ConversationHomeList.tsx +3 -3
  60. package/src/client/conversations/ConversationListStatusIcon.tsx +1 -1
  61. package/src/client/conversations/ConversationMemories.tsx +4 -4
  62. package/src/client/conversations/ConversationMeta.tsx +10 -10
  63. package/src/client/conversations/ConversationPage.tsx +2 -1
  64. package/src/client/conversations/ConversationSidebar.tsx +6 -6
  65. package/src/client/conversations/ConversationUsageBreakdown.tsx +4 -4
  66. package/src/client/conversations/ConversationWorkspace.tsx +1 -1
  67. package/src/client/conversations/ConversationWorkspaceLoading.tsx +4 -4
  68. package/src/client/conversations/CopyMarkdownButton.tsx +1 -1
  69. package/src/client/conversations/EventUsage.tsx +2 -2
  70. package/src/client/conversations/PendingMailboxStack.tsx +5 -5
  71. package/src/client/conversations/README.md +9 -0
  72. package/src/client/conversations/SubagentTranscriptDrawer.tsx +39 -23
  73. package/src/client/conversations/ToolFrame.tsx +36 -14
  74. package/src/client/conversations/TranscriptActivityGroup.tsx +10 -3
  75. package/src/client/conversations/TranscriptAttachmentsDeliveredView.tsx +4 -6
  76. package/src/client/conversations/TranscriptContextEventView.tsx +2 -2
  77. package/src/client/conversations/TranscriptEventView.tsx +1 -1
  78. package/src/client/conversations/TranscriptMarkdown.tsx +3 -3
  79. package/src/client/conversations/TranscriptMessageContextView.tsx +7 -7
  80. package/src/client/conversations/TranscriptMessageView.tsx +2 -6
  81. package/src/client/conversations/TranscriptRailEvent.tsx +20 -8
  82. package/src/client/conversations/TranscriptReasoningView.tsx +12 -12
  83. package/src/client/conversations/TranscriptStructuredEventView.tsx +16 -11
  84. package/src/client/conversations/TranscriptSubagentView.tsx +64 -39
  85. package/src/client/conversations/TranscriptText.tsx +5 -4
  86. package/src/client/conversations/TranscriptToolView.tsx +9 -11
  87. package/src/client/conversations/TranscriptTurnContextView.tsx +3 -3
  88. package/src/client/conversations/TranscriptView.tsx +3 -3
  89. package/src/client/conversations/UsageCard.tsx +1 -1
  90. package/src/client/conversations/eventTranscript.ts +7 -0
  91. package/src/client/conversations/transcriptMarkdownLinks.ts +2 -4
  92. package/src/client/conversations/transcriptStyles.ts +1 -1
  93. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  94. package/src/client/pages/admin/AdminPage.tsx +2 -2
  95. package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
  96. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +5 -10
  97. package/src/client/pages/automations/AutomationEditor.tsx +1 -1
  98. package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
  99. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +2 -2
  100. package/src/client/pages/automations/AutomationExecutionsPage.tsx +4 -4
  101. package/src/client/pages/automations/AutomationRow.tsx +4 -4
  102. package/src/client/pages/automations/AutomationRunsPage.tsx +3 -3
  103. package/src/client/pages/automations/AutomationScheduleFields.tsx +1 -1
  104. package/src/client/pages/automations/AutomationVersionsPage.tsx +1 -1
  105. package/src/client/pages/code/CodeActivityChart.tsx +7 -6
  106. package/src/client/pages/code/CodeChangeRow.tsx +1 -1
  107. package/src/client/pages/code/CodePage.tsx +5 -5
  108. package/src/client/pages/code/RepositoryPage.tsx +2 -2
  109. package/src/client/pages/dev/ComponentsPage.tsx +40 -146
  110. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +206 -0
  111. package/src/client/pages/locations/LocationDetailPage.tsx +4 -4
  112. package/src/client/pages/locations/LocationDirectory.tsx +1 -1
  113. package/src/client/pages/locations/PrivateActivityCard.tsx +3 -3
  114. package/src/client/pages/memory/MemoryCostChart.tsx +3 -5
  115. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +5 -5
  116. package/src/client/pages/memory/MemoryPage.tsx +15 -15
  117. package/src/client/pages/memory/MemoryTimeline.tsx +3 -5
  118. package/src/client/pages/people/ContributionGrid.tsx +8 -5
  119. package/src/client/pages/people/PersonProfilePage.tsx +3 -6
  120. package/src/client/pages/people/ProfilePluginReports.tsx +2 -2
  121. package/src/client/pages/system/GuardianActivity.tsx +9 -9
  122. package/src/client/pages/system/PluginBarChart.tsx +4 -6
  123. package/src/client/pages/system/PluginDetails.tsx +11 -7
  124. package/src/client/pages/system/PluginPanels.tsx +8 -8
  125. package/src/client/pages/system/PluginReportRecordSet.tsx +14 -14
  126. package/src/client/pages/system/PluginReports.tsx +7 -11
  127. package/src/client/pages/system/SkillInventory.tsx +9 -9
  128. package/src/client/pages/system/SystemActivity.tsx +1 -1
  129. package/src/client/pages/system/SystemPage.tsx +3 -6
  130. package/src/client/pages/system/WorkspaceDetails.tsx +2 -5
  131. package/src/client/pages/system/WorkspaceEditor.tsx +2 -2
  132. package/src/client/pages/system/WorkspaceList.tsx +2 -2
  133. package/src/client/pages/user/PluginUserPage.tsx +3 -3
  134. package/src/client/styles.ts +5 -4
  135. package/src/client/types.ts +2 -0
  136. package/src/tailwind.css +36 -38
@@ -1,65 +1,90 @@
1
- import { formatMessageTimestamp } from "../format";
1
+ import { ChevronRight } from "lucide-react";
2
+
3
+ import { StatusChip } from "../components/StatusChip";
4
+ import { formatElapsedDuration, formatMessageTimestamp } from "../format";
5
+ import { cn } from "../styles";
2
6
  import type { TranscriptViewSubagentPart } from "../types";
3
- import { ToolFrame } from "./ToolFrame";
7
+ import { subagentStatusTone } from "./ConversationChildAgents";
4
8
  import { HighlightText } from "./transcriptSearch";
5
9
 
6
- /** Render a child-agent lifecycle event inside the transcript stream. */
10
+ /**
11
+ * Render a child agent as a compact card in the transcript stream. The card
12
+ * matches the child agent rows in the details drawer: name, status, and run
13
+ * time first, then the task title. It opens the child transcript.
14
+ */
7
15
  export function TranscriptSubagentView(props: {
8
16
  onOpenTranscript?: (part: TranscriptViewSubagentPart) => void;
9
17
  part: TranscriptViewSubagentPart;
10
18
  timestamp?: number;
11
19
  }) {
12
20
  const label = props.part.agentName ?? props.part.subagentKind;
13
- const summary = props.part.title ?? props.part.task;
21
+ const headline = props.part.title ?? props.part.task;
14
22
  const status = props.part.status;
15
- const meta = [
16
- status,
23
+ const duration = formatElapsedDuration(
24
+ props.timestamp,
25
+ props.part.finishedTimestamp,
26
+ );
27
+ const started =
17
28
  typeof props.timestamp === "number"
18
- ? formatMessageTimestamp(props.timestamp)
19
- : undefined,
20
- ].filter(isString);
29
+ ? `Started ${formatMessageTimestamp(props.timestamp)}`
30
+ : undefined;
31
+ const interactive = Boolean(props.onOpenTranscript);
21
32
 
22
- const frame = (
23
- <ToolFrame
24
- meta={meta}
25
- mobileSummaryMeta={status}
26
- signature={
27
- <>
28
- <strong className="min-w-0 break-words font-bold text-cyan-100">
29
- <HighlightText text={label} />
30
- </strong>
31
- {summary ? (
32
- <span className="min-w-0 truncate text-dashboard-text-muted">
33
- <HighlightText text={summary} />
34
- </span>
35
- ) : null}
36
- {props.part.status === "running" ? (
37
- <span
38
- aria-hidden="true"
39
- className="mt-px size-1.5 shrink-0 animate-pulse rounded-full bg-cyan-300"
40
- />
41
- ) : null}
42
- </>
43
- }
44
- />
33
+ const content = (
34
+ <>
35
+ <span className="flex min-w-0 items-center gap-2 text-xs leading-5">
36
+ <span className="min-w-0 truncate font-sans text-sm font-semibold text-dashboard-text">
37
+ <HighlightText text={label} />
38
+ </span>
39
+ <StatusChip
40
+ className={cn("shrink-0", status === "running" && "animate-pulse")}
41
+ size="compact"
42
+ tone={subagentStatusTone(status)}
43
+ >
44
+ {status}
45
+ </StatusChip>
46
+ {duration ? (
47
+ <span className="shrink-0 tabular-nums text-dashboard-text-muted">
48
+ {duration}
49
+ </span>
50
+ ) : null}
51
+ {interactive ? (
52
+ <ChevronRight
53
+ aria-hidden="true"
54
+ className="ml-auto size-4 shrink-0 text-dashboard-text-faint transition-colors group-hover/subagent:text-dashboard-text"
55
+ />
56
+ ) : null}
57
+ </span>
58
+ {headline ? (
59
+ <span className="block min-w-0 truncate text-sm leading-snug text-dashboard-text-subtle">
60
+ <HighlightText text={headline} />
61
+ </span>
62
+ ) : null}
63
+ </>
45
64
  );
65
+ const frameClass =
66
+ "group/subagent grid w-full min-w-0 max-w-[37.5rem] gap-0.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2 text-left text-inherit";
46
67
 
47
68
  if (!props.onOpenTranscript) {
48
- return frame;
69
+ return (
70
+ <div className={frameClass} title={started}>
71
+ {content}
72
+ </div>
73
+ );
49
74
  }
50
75
 
51
76
  return (
52
77
  <button
53
78
  aria-label={`Open ${label} transcript`}
54
- className="block w-full min-w-0 cursor-pointer text-left transition-colors hover:bg-white/[0.035] focus-visible:outline focus-visible:outline-1 focus-visible:outline-cyan-300/55"
79
+ className={cn(
80
+ frameClass,
81
+ "cursor-pointer transition-colors hover:border-dashboard-border-emphasis hover:bg-dashboard-surface-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus",
82
+ )}
55
83
  onClick={() => props.onOpenTranscript?.(props.part)}
84
+ title={started}
56
85
  type="button"
57
86
  >
58
- {frame}
87
+ {content}
59
88
  </button>
60
89
  );
61
90
  }
62
-
63
- function isString(value: string | undefined): value is string {
64
- return typeof value === "string" && value.length > 0;
65
- }
@@ -26,11 +26,12 @@ export const TranscriptText = memo(function TranscriptText(props: {
26
26
  }
27
27
 
28
28
  return (
29
- <HighlightedCode
30
- code={block.code}
29
+ <div
30
+ className="min-w-0 overflow-hidden rounded-md border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5"
31
31
  key={index}
32
- language={block.language}
33
- />
32
+ >
33
+ <HighlightedCode code={block.code} language={block.language} />
34
+ </div>
34
35
  );
35
36
  })}
36
37
  </div>
@@ -1,5 +1,5 @@
1
1
  import { memo, useMemo } from "react";
2
- import { TriangleAlert } from "lucide-react";
2
+ import { TriangleAlert, Wrench } from "lucide-react";
3
3
 
4
4
  import { HighlightedCode } from "../code";
5
5
  import {
@@ -45,6 +45,12 @@ export const TranscriptToolView = memo(function TranscriptToolView(props: {
45
45
  const frame = (
46
46
  <ToolFrame
47
47
  expandable={hasDetails}
48
+ icon={props.part.status === "error" ? TriangleAlert : Wrench}
49
+ iconClassName={
50
+ props.part.status === "error"
51
+ ? "text-rose-300"
52
+ : "text-dashboard-text-faint"
53
+ }
48
54
  meta={meta}
49
55
  mobileSummaryMeta={mobileSummary}
50
56
  signature={signature}
@@ -78,14 +84,6 @@ function ToolSignature(props: {
78
84
 
79
85
  return (
80
86
  <>
81
- {failed ? (
82
- <TriangleAlert
83
- aria-hidden="true"
84
- className="shrink-0 !text-rose-300"
85
- size={12}
86
- strokeWidth={2.2}
87
- />
88
- ) : null}
89
87
  <ShimmerText
90
88
  active={shimmering}
91
89
  aria-label={statusLabel}
@@ -109,8 +107,8 @@ function ToolSignature(props: {
109
107
  function ToolPayload(props: { label: string; value: unknown }) {
110
108
  const code = useMemo(() => stringifyPartValue(props.value), [props.value]);
111
109
  return (
112
- <div className="min-w-0 max-w-full overflow-hidden bg-black/20 px-2.5 py-2">
113
- <div className="pb-1.5 font-mono text-2xs font-bold uppercase leading-none tracking-[0.08em] text-dashboard-text-muted">
110
+ <div className="min-w-0 max-w-full overflow-hidden border-dashboard-border-subtle px-3 py-2.5 [&+&]:border-t">
111
+ <div className="pb-1.5 font-sans text-xs font-medium leading-none text-dashboard-text-muted">
114
112
  {props.label}
115
113
  </div>
116
114
  <HighlightedCode code={code} language="json" />
@@ -24,7 +24,7 @@ export function TranscriptTurnContextView(props: {
24
24
  aria-expanded={open}
25
25
  aria-label="View turn context"
26
26
  className={cn(
27
- "inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus",
27
+ "inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus",
28
28
  open && "bg-dashboard-fill-hover text-dashboard-text",
29
29
  )}
30
30
  onClick={() => setOpen(true)}
@@ -80,13 +80,13 @@ function TurnContext(props: { context: TranscriptViewTurnContext }) {
80
80
  {memory ? (
81
81
  <Brain
82
82
  aria-hidden="true"
83
- className="shrink-0 text-cyan-100/60"
83
+ className="shrink-0 text-dashboard-accent-text/60"
84
84
  size={15}
85
85
  />
86
86
  ) : (
87
87
  <Braces
88
88
  aria-hidden="true"
89
- className="shrink-0 text-cyan-100/60"
89
+ className="shrink-0 text-dashboard-accent-text/60"
90
90
  size={15}
91
91
  />
92
92
  )}
@@ -72,10 +72,10 @@ export function Transcript(props: {
72
72
  ) : null}
73
73
  {props.hasPreviousPage || props.loadingPreviousPage ? (
74
74
  <div className="mb-3 flex items-center gap-3">
75
- <span className="h-px min-w-4 flex-1 bg-white/[0.08]" />
75
+ <span className="h-px min-w-4 flex-1 bg-dashboard-fill-strong" />
76
76
  <button
77
77
  aria-label="Load earlier events"
78
- className="shrink-0 cursor-pointer border-0 bg-transparent px-1 py-0.5 font-sans text-xs font-medium text-dashboard-text-muted transition-colors hover:text-dashboard-text focus-visible:outline focus-visible:outline-1 focus-visible:outline-cyan-300/55 disabled:cursor-not-allowed disabled:opacity-50"
78
+ className="shrink-0 cursor-pointer border-0 bg-transparent px-1 py-0.5 font-sans text-xs font-medium text-dashboard-text-muted transition-colors hover:text-dashboard-text focus-visible:outline focus-visible:outline-1 focus-visible:outline-dashboard-focus/55 disabled:cursor-not-allowed disabled:opacity-50"
79
79
  disabled={props.loadingPreviousPage}
80
80
  onClick={() => {
81
81
  bottomPinning.preserveViewportForPrepend();
@@ -88,7 +88,7 @@ export function Transcript(props: {
88
88
  ? `Loading earlier ${historyLabel}…`
89
89
  : `Show earlier ${historyLabel}`}
90
90
  </button>
91
- <span className="h-px min-w-4 flex-1 bg-white/[0.08]" />
91
+ <span className="h-px min-w-4 flex-1 bg-dashboard-fill-strong" />
92
92
  </div>
93
93
  ) : null}
94
94
  {props.historyError ? (
@@ -118,7 +118,7 @@ export function UsageCard(props: UsageCardProps) {
118
118
  className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-2"
119
119
  key={section.title}
120
120
  >
121
- <span className="col-span-2 font-mono uppercase tracking-[0.08em] text-dashboard-text-faint">
121
+ <span className="col-span-2 font-sans font-medium text-dashboard-text-faint">
122
122
  {section.title}
123
123
  {section.total ? ` · ${section.total}` : null}
124
124
  </span>
@@ -352,8 +352,12 @@ export function transcriptMessagesFromEvents(
352
352
  if (data.type === "subagent") {
353
353
  const subagentId = `${data.childConversationId}:${data.startedSeq}`;
354
354
  const existing = subagents.get(subagentId);
355
+ const finishedTimestamp =
356
+ data.status === "running" ? undefined : eventTimestamp(event);
355
357
  if (existing) {
356
358
  existing.status = data.status;
359
+ if (finishedTimestamp === undefined) delete existing.finishedTimestamp;
360
+ else existing.finishedTimestamp = finishedTimestamp;
357
361
  continue;
358
362
  }
359
363
  const part = {
@@ -362,6 +366,9 @@ export function transcriptMessagesFromEvents(
362
366
  childConversationId: data.childConversationId,
363
367
  subagentKind: data.subagentKind,
364
368
  status: data.status,
369
+ ...(finishedTimestamp === undefined
370
+ ? undefined
371
+ : { finishedTimestamp }),
365
372
  ...(data.agentName ? { agentName: data.agentName } : undefined),
366
373
  ...(data.task ? { task: data.task } : undefined),
367
374
  ...(data.title ? { title: data.title } : undefined),
@@ -1,5 +1,5 @@
1
1
  export const TRANSCRIPT_ANCHOR_CLASS =
2
- "font-medium text-cyan-100 underline decoration-cyan-300/35 underline-offset-2 transition-colors hover:text-dashboard-text hover:decoration-white";
2
+ "font-medium text-dashboard-accent-text underline decoration-dashboard-accent/35 underline-offset-2 transition-colors hover:text-dashboard-text hover:decoration-dashboard-text-solid";
3
3
 
4
4
  export type TranscriptMarkdownLink = {
5
5
  end: number;
@@ -36,9 +36,7 @@ export function findTranscriptMarkdownLinks(
36
36
  ...markdownLinks,
37
37
  ...autolinks,
38
38
  ...findBareLinks(text, bareRanges),
39
- ].sort(
40
- (left, right) => left.start - right.start || left.end - right.end,
41
- );
39
+ ].sort((left, right) => left.start - right.start || left.end - right.end);
42
40
  }
43
41
 
44
42
  function findMarkdownLinks(
@@ -5,6 +5,6 @@ export function transcriptEmptyClass(
5
5
  const colors =
6
6
  tone === "error"
7
7
  ? "border-rose-300/25 bg-rose-300/[0.07] text-rose-100"
8
- : "border-white/[0.07] bg-white/[0.025] text-dashboard-text-muted";
8
+ : "border-dashboard-border bg-dashboard-fill-soft text-dashboard-text-muted";
9
9
  return `rounded-lg border p-5 text-sm leading-relaxed ${colors}`;
10
10
  }
@@ -110,11 +110,11 @@ export function PersonalTokensPage() {
110
110
 
111
111
  <Card className="mb-0 mt-6" padding="md" variant="raised">
112
112
  {createdToken ? (
113
- <div className="mt-5 rounded border border-emerald-400/40 bg-emerald-400/5 p-3">
113
+ <div className="mt-5 rounded-md border border-emerald-400/40 bg-emerald-400/5 p-3">
114
114
  <p className="mt-0 mb-2 text-sm font-semibold">
115
115
  Copy this token now. It won't be shown again.
116
116
  </p>
117
- <code className="block overflow-x-auto rounded bg-black p-2 text-xs text-emerald-300">
117
+ <code className="block overflow-x-auto rounded-md bg-dashboard-ink p-2 text-xs text-emerald-300">
118
118
  {createdToken.token}
119
119
  </code>
120
120
  <Button
@@ -166,10 +166,10 @@ export function PersonalTokensPage() {
166
166
  ) : (
167
167
  tokens.map((token) => (
168
168
  <div
169
- className="flex items-center gap-3 rounded border border-white/10 p-3"
169
+ className="flex items-center gap-3 rounded-md border border-dashboard-border-strong p-3"
170
170
  key={token.id}
171
171
  >
172
- <KeyRound className="text-[#beaaff]" size={16} />
172
+ <KeyRound className="text-dashboard-accent" size={16} />
173
173
  <div className="min-w-0 flex-1">
174
174
  <p className="m-0 truncate text-sm font-semibold">
175
175
  {token.name}
@@ -31,7 +31,7 @@ export function AdminPage(props: { pages: PluginUserPageLink[] }) {
31
31
  {adminPages.length === 0 ? (
32
32
  <Card padding="md">
33
33
  <div className="flex items-center gap-4">
34
- <div className="grid size-10 shrink-0 place-items-center rounded border border-white/[0.07] bg-white/[0.025] text-dashboard-text-muted">
34
+ <div className="grid size-10 shrink-0 place-items-center rounded-md border border-dashboard-border bg-dashboard-fill-soft text-dashboard-text-muted">
35
35
  <Boxes aria-hidden="true" size={17} />
36
36
  </div>
37
37
  <p className="m-0 text-sm text-dashboard-text-muted">
@@ -50,7 +50,7 @@ export function AdminPage(props: { pages: PluginUserPageLink[] }) {
50
50
  <Card padding="md">
51
51
  <div className="flex items-center gap-3">
52
52
  <div className="min-w-0 flex-1">
53
- <div className="font-mono text-xs uppercase tracking-[0.12em] text-dashboard-text-muted">
53
+ <div className="font-sans font-medium text-xs text-dashboard-text-muted">
54
54
  {page.pluginDisplayName}
55
55
  </div>
56
56
  <h2 className="mt-1 mb-0 font-display text-base font-medium text-dashboard-text">
@@ -45,7 +45,7 @@ export function AutomationCostChart(props: {
45
45
  <h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
46
46
  {formatCostSummary({ total }) || "$0.00"}
47
47
  </h2>
48
- <p className="mt-1 mb-0 font-mono text-xs leading-relaxed text-dashboard-text-muted">
48
+ <p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
49
49
  Spend from conversations linked to completed automation executions.
50
50
  </p>
51
51
  </div>
@@ -112,7 +112,7 @@ export function AutomationCostChart(props: {
112
112
  />
113
113
  </ChartSvg>
114
114
  {!hasCost ? (
115
- <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-mono text-xs text-dashboard-text-muted">
115
+ <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
116
116
  No linked spend in this period.
117
117
  </div>
118
118
  ) : null}
@@ -17,6 +17,7 @@ import {
17
17
  import { conversationPath } from "../../conversations/conversationRoutes";
18
18
  import { formatTime, peoplePath } from "../../format";
19
19
  import { TranscriptText } from "../../conversations/TranscriptText";
20
+ import { cn, dashboardInlineLinkClass } from "../../styles";
20
21
 
21
22
  /** Show one automation's instruction and metadata in a right-side slide-out. */
22
23
  export function AutomationDetailsDrawer(props: {
@@ -30,7 +31,7 @@ export function AutomationDetailsDrawer(props: {
30
31
  const { automation } = props;
31
32
  const createdBy = automation.createdByEmail ? (
32
33
  <Link
33
- className="font-semibold text-dashboard-text underline decoration-white/20 underline-offset-2 transition-colors hover:decoration-white/60"
34
+ className={cn("font-semibold", dashboardInlineLinkClass)}
34
35
  to={peoplePath(automation.createdByEmail)}
35
36
  >
36
37
  {automation.ownedByViewer ? "you" : automation.createdBy}
@@ -175,10 +176,7 @@ function AutomationExecutionSummary(props: {
175
176
  return (
176
177
  <div className="text-sm text-dashboard-text-muted">
177
178
  {automation.totalRuns > 0 ? (
178
- <Link
179
- className="text-dashboard-text underline decoration-white/20 underline-offset-2 hover:decoration-white/60"
180
- to={executionsPath}
181
- >
179
+ <Link className={dashboardInlineLinkClass} to={executionsPath}>
182
180
  {runCount} runs / {rangeLabel}
183
181
  </Link>
184
182
  ) : (
@@ -189,10 +187,7 @@ function AutomationExecutionSummary(props: {
189
187
  <span className="mx-2 opacity-45">·</span>
190
188
  <span>
191
189
  {automation.totalRuns > 0 ? (
192
- <Link
193
- className="text-dashboard-text underline decoration-white/20 underline-offset-2 hover:decoration-white/60"
194
- to={executionsPath}
195
- >
190
+ <Link className={dashboardInlineLinkClass} to={executionsPath}>
196
191
  {automation.totalRuns} total
197
192
  </Link>
198
193
  ) : (
@@ -201,7 +196,7 @@ function AutomationExecutionSummary(props: {
201
196
  {automation.lastRunAt ? " · Last run " : " · Never run"}
202
197
  {automation.lastRunAt && automation.lastConversationId ? (
203
198
  <Link
204
- className="text-dashboard-text underline decoration-white/20 underline-offset-2 hover:decoration-white/60"
199
+ className={dashboardInlineLinkClass}
205
200
  to={conversationPath(automation.lastConversationId)}
206
201
  >
207
202
  {formatAutomationRunTime(automation.lastRunAt)}
@@ -425,7 +425,7 @@ export function AutomationEditor(props: {
425
425
  <div className="flex shrink-0 gap-2">
426
426
  <Link
427
427
  to={props.returnPath}
428
- className="inline-flex min-h-11 items-center rounded border border-dashboard-border-emphasis px-3 text-sm text-dashboard-text no-underline sm:min-h-9"
428
+ className="inline-flex min-h-11 items-center rounded-md border border-dashboard-border-emphasis px-3 text-sm text-dashboard-text no-underline sm:min-h-9"
429
429
  >
430
430
  Cancel
431
431
  </Link>
@@ -45,7 +45,7 @@ export function AutomationExecutionChart(props: {
45
45
  <h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
46
46
  {totals.reduce((sum, value) => sum + value, 0).toLocaleString()} runs
47
47
  </h2>
48
- <p className="mt-1 mb-0 font-mono text-xs leading-relaxed text-dashboard-text-muted">
48
+ <p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
49
49
  Completed automation executions over time.
50
50
  </p>
51
51
  </div>
@@ -104,7 +104,7 @@ export function AutomationExecutionChart(props: {
104
104
  />
105
105
  </ChartSvg>
106
106
  {!hasExecutions ? (
107
- <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-mono text-xs text-dashboard-text-muted">
107
+ <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
108
108
  No automation executions in this period.
109
109
  </div>
110
110
  ) : null}
@@ -44,7 +44,7 @@ export function AutomationExecutionStatusChart(props: {
44
44
  <h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
45
45
  Executions over time
46
46
  </h2>
47
- <p className="mt-1 mb-0 font-mono text-xs leading-relaxed text-dashboard-text-muted">
47
+ <p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
48
48
  {bucketUnit === "hour"
49
49
  ? "Terminal runs for this automation each hour."
50
50
  : bucketUnit === "6hour"
@@ -121,7 +121,7 @@ export function AutomationExecutionStatusChart(props: {
121
121
  />
122
122
  </ChartSvg>
123
123
  {!hasExecutions ? (
124
- <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-mono text-xs text-dashboard-text-muted">
124
+ <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
125
125
  No automation executions in this period.
126
126
  </div>
127
127
  ) : null}
@@ -87,7 +87,7 @@ export function AutomationExecutionsPage(props: { enabled: boolean }) {
87
87
  : "Automation executions could not be loaded. Try again."}
88
88
  </InlineError>
89
89
  <Link
90
- className="mt-3 inline-flex items-center gap-2 font-mono text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
90
+ className="mt-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
91
91
  to={
92
92
  automationId ? automationPath(automationId) : "/automations/list"
93
93
  }
@@ -122,7 +122,7 @@ function AutomationExecutionsView(props: {
122
122
  <>
123
123
  <div>
124
124
  <Link
125
- className="mb-3 inline-flex items-center gap-2 font-mono text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
125
+ className="mb-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
126
126
  to={props.backTo}
127
127
  >
128
128
  <ArrowLeft aria-hidden="true" size={14} />
@@ -177,7 +177,7 @@ function AutomationExecutionsView(props: {
177
177
  <div className="min-w-[36rem]">
178
178
  <div
179
179
  className={cn(
180
- "sticky top-0 z-[1] hidden 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 md:grid",
180
+ "sticky top-0 z-[1] hidden 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 md:grid",
181
181
  EXECUTION_GRID,
182
182
  )}
183
183
  role="row"
@@ -290,7 +290,7 @@ function MetricCell(props: { label: string; value: string }) {
290
290
  <div className="min-w-0 max-md:flex max-md:items-center max-md:gap-2">
291
291
  <div
292
292
  aria-hidden="true"
293
- className="mb-1 hidden font-mono text-2xs uppercase tracking-[0.1em] text-dashboard-text-muted max-md:mb-0 max-md:block"
293
+ className="mb-1 hidden font-sans font-medium text-2xs text-dashboard-text-muted max-md:mb-0 max-md:block"
294
294
  >
295
295
  {props.label}
296
296
  </div>
@@ -58,7 +58,7 @@ export function AutomationRow(props: {
58
58
  <button
59
59
  aria-expanded={props.selected}
60
60
  aria-label={`View automation details: ${automation.title}`}
61
- className="block w-full min-w-0 cursor-pointer rounded border-0 bg-transparent p-0 text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
61
+ className="block w-full min-w-0 cursor-pointer rounded-md border-0 bg-transparent p-0 text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
62
62
  onClick={props.onSelect}
63
63
  type="button"
64
64
  >
@@ -88,7 +88,7 @@ export function AutomationRow(props: {
88
88
  <div className="mt-2.5 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-dashboard-text-muted">
89
89
  {automation.createdByEmail ? (
90
90
  <Link
91
- className="inline-flex min-w-0 max-w-full items-center gap-2 rounded text-dashboard-text no-underline hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
91
+ className="inline-flex min-w-0 max-w-full items-center gap-2 rounded-md text-dashboard-text no-underline hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
92
92
  title={automation.createdBy}
93
93
  to={peoplePath(automation.createdByEmail)}
94
94
  >
@@ -320,12 +320,12 @@ function AutomationActions(props: {
320
320
  <Link
321
321
  role="menuitem"
322
322
  to={props.editPath}
323
- className="flex min-h-11 items-center rounded px-3 text-sm text-dashboard-text no-underline hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-dashboard-focus"
323
+ className="flex min-h-11 items-center rounded-md px-3 text-sm text-dashboard-text no-underline hover:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-dashboard-focus"
324
324
  >
325
325
  Edit automation
326
326
  </Link>
327
327
  <button
328
- className="flex min-h-11 w-full cursor-pointer items-center gap-2 rounded border-0 bg-transparent px-3 text-sm text-rose-300 hover:bg-dashboard-fill-hover focus-visible:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
328
+ className="flex min-h-11 w-full cursor-pointer items-center gap-2 rounded-md border-0 bg-transparent px-3 text-sm text-rose-300 hover:bg-dashboard-fill-hover focus-visible:bg-dashboard-fill-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
329
329
  onClick={() => {
330
330
  close();
331
331
  props.onDelete();
@@ -156,7 +156,7 @@ export function AutomationRunsPage(props: { enabled: boolean }) {
156
156
  <div className="min-w-[44rem]">
157
157
  <div
158
158
  className={cn(
159
- "sticky top-0 z-[1] hidden 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 md:grid",
159
+ "sticky top-0 z-[1] hidden 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 md:grid",
160
160
  RUN_GRID,
161
161
  )}
162
162
  role="row"
@@ -278,7 +278,7 @@ function MetricCell(props: { label: string; value: string }) {
278
278
  <div className="min-w-0 max-md:flex max-md:items-center max-md:gap-2">
279
279
  <div
280
280
  aria-hidden="true"
281
- className="mb-1 hidden font-mono text-2xs uppercase tracking-[0.1em] text-dashboard-text-muted max-md:mb-0 max-md:block"
281
+ className="mb-1 hidden font-sans font-medium text-2xs text-dashboard-text-muted max-md:mb-0 max-md:block"
282
282
  >
283
283
  {props.label}
284
284
  </div>
@@ -297,7 +297,7 @@ function AutomationKindIcon(props: { kind: AutomationRun["kind"] }) {
297
297
  return (
298
298
  <span
299
299
  aria-label={label}
300
- className="inline-flex size-3.5 shrink-0 items-center justify-center text-cyan-300/80"
300
+ className="inline-flex size-3.5 shrink-0 items-center justify-center text-dashboard-accent/80"
301
301
  role="img"
302
302
  title={label}
303
303
  >
@@ -152,7 +152,7 @@ export function AutomationScheduleFields(props: {
152
152
  })
153
153
  }
154
154
  className={cn(
155
- "min-h-11 cursor-pointer rounded border px-1 text-xs font-medium capitalize focus-visible:outline-2 focus-visible:outline-dashboard-focus sm:min-h-10 sm:text-sm",
155
+ "min-h-11 cursor-pointer rounded-md border px-1 text-xs font-medium capitalize focus-visible:outline-2 focus-visible:outline-dashboard-focus sm:min-h-10 sm:text-sm",
156
156
  value.weekdays?.includes(day)
157
157
  ? "border-dashboard-focus/50 bg-dashboard-focus/10 text-dashboard-focus"
158
158
  : "border-dashboard-border-emphasis text-dashboard-text-muted hover:bg-dashboard-fill-hover",
@@ -77,7 +77,7 @@ export function AutomationVersionsPage(props: { enabled: boolean }) {
77
77
  function BackLink(props: { to: string }) {
78
78
  return (
79
79
  <Link
80
- className="mb-3 inline-flex items-center gap-2 font-mono text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
80
+ className="mb-3 inline-flex items-center gap-2 font-sans text-xs text-dashboard-text-muted no-underline hover:text-dashboard-text"
81
81
  to={props.to}
82
82
  >
83
83
  <ArrowLeft aria-hidden="true" size={14} />
@@ -40,7 +40,10 @@ export function CodeActivityChart(props: {
40
40
  const gap = Math.max(1, barWidth * 0.2);
41
41
  const groupWidth = barWidth * 3 + gap * 2;
42
42
  const totals = days.map((day) => day.created + day.merged + day.closed);
43
- const maximum = Math.max(1, ...days.flatMap((day) => [day.created, day.merged, day.closed]));
43
+ const maximum = Math.max(
44
+ 1,
45
+ ...days.flatMap((day) => [day.created, day.merged, day.closed]),
46
+ );
44
47
  const average = activityChartAverage(days.map((day) => day.created));
45
48
  const hasActivity = totals.some((total) => total > 0);
46
49
 
@@ -51,7 +54,7 @@ export function CodeActivityChart(props: {
51
54
  <h2 className="m-0 font-display text-xl font-medium text-dashboard-text">
52
55
  Changes over time
53
56
  </h2>
54
- <p className="mt-1 mb-0 font-mono text-xs leading-relaxed text-dashboard-text-muted">
57
+ <p className="mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted">
55
58
  Code changes created, merged, and closed each day.
56
59
  </p>
57
60
  </div>
@@ -106,9 +109,7 @@ export function CodeActivityChart(props: {
106
109
  date={day.date}
107
110
  summary={`${day.created} created, ${day.merged} merged, ${day.closed} closed`}
108
111
  >
109
- <g
110
- tabIndex={0}
111
- >
112
+ <g tabIndex={0}>
112
113
  {series.map((entry, seriesIndex) => {
113
114
  const height = (entry.value / maximum) * layout.plotHeight;
114
115
  const x = groupX + seriesIndex * (barWidth + gap);
@@ -151,7 +152,7 @@ export function CodeActivityChart(props: {
151
152
  />
152
153
  </ChartSvg>
153
154
  {!hasActivity ? (
154
- <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-mono text-xs text-dashboard-text-muted">
155
+ <div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
155
156
  No code changes in this period.
156
157
  </div>
157
158
  ) : null}
@@ -18,7 +18,7 @@ export function CodeChangeRow(props: { change: CodeChangeSummaryReport }) {
18
18
  <div className="truncate font-display text-sm text-dashboard-text">
19
19
  {change.url ? (
20
20
  <a
21
- className="text-inherit no-underline hover:text-cyan-100"
21
+ className="text-inherit no-underline hover:text-dashboard-accent-text"
22
22
  href={change.url}
23
23
  rel="noreferrer"
24
24
  target="_blank"