@sentry/junior-dashboard 0.259.0 → 0.260.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 (63) hide show
  1. package/README.md +5 -3
  2. package/dist/app.js +88 -37
  3. package/dist/chunks/github-light-QHW3AEMR.js +1 -0
  4. package/dist/client.js +86 -50
  5. package/dist/index.js +88 -37
  6. package/dist/tailwind.css +1 -1
  7. package/package.json +3 -3
  8. package/src/client/App.tsx +0 -1
  9. package/src/client/code.tsx +10 -4
  10. package/src/client/components/ProfileMenu.tsx +3 -0
  11. package/src/client/components/README.md +12 -2
  12. package/src/client/components/ThemeSwitcher.tsx +48 -0
  13. package/src/client/components/charts/ActivityChart.tsx +6 -7
  14. package/src/client/components/charts/InputCacheChart.tsx +1 -1
  15. package/src/client/components/charts/SystemMetricCharts.tsx +3 -3
  16. package/src/client/components/charts/WorkspaceUsageChart.tsx +1 -1
  17. package/src/client/components/charts/cache-input.ts +11 -3
  18. package/src/client/components/layout/DashboardHeader.tsx +2 -9
  19. package/src/client/conversations/ConversationBrief.tsx +35 -9
  20. package/src/client/conversations/ConversationHomeList.tsx +7 -1
  21. package/src/client/conversations/ConversationMeta.tsx +19 -7
  22. package/src/client/conversations/ConversationTranscript.tsx +8 -11
  23. package/src/client/conversations/EventDetails.tsx +9 -5
  24. package/src/client/conversations/README.md +6 -0
  25. package/src/client/conversations/TranscriptContextEventView.tsx +5 -1
  26. package/src/client/conversations/TranscriptEventView.tsx +48 -9
  27. package/src/client/conversations/TranscriptMessageView.tsx +6 -1
  28. package/src/client/conversations/TranscriptStructuredEventView.tsx +4 -2
  29. package/src/client/conversations/TranscriptText.tsx +28 -20
  30. package/src/client/conversations/conversationActivityPreview.ts +6 -0
  31. package/src/client/conversations/eventTranscript.ts +4 -1
  32. package/src/client/conversations/transcriptMentions.tsx +27 -10
  33. package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
  34. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +4 -1
  35. package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
  36. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +3 -3
  37. package/src/client/pages/code/CodeActivityChart.tsx +4 -4
  38. package/src/client/pages/dev/ComponentsPage.tsx +7 -3
  39. package/src/client/pages/dev/ConversationFixture.tsx +29 -0
  40. package/src/client/pages/locations/LocationActivityChart.tsx +14 -4
  41. package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +14 -9
  42. package/src/client/pages/memory/MemoryCostChart.tsx +5 -5
  43. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +9 -2
  44. package/src/client/pages/memory/MemoryTimeline.tsx +2 -2
  45. package/src/client/pages/people/ContributionGrid.tsx +8 -8
  46. package/src/client/pages/people/PeopleActivityChart.tsx +23 -10
  47. package/src/client/pages/system/BaseSandboxCard.tsx +76 -0
  48. package/src/client/pages/system/ConversationActivityChart.tsx +2 -2
  49. package/src/client/pages/system/PluginBarChart.tsx +12 -6
  50. package/src/client/pages/system/SandboxSizeSummary.tsx +47 -0
  51. package/src/client/pages/system/WorkspaceDetails.tsx +11 -1
  52. package/src/client/pages/system/WorkspaceEditor.tsx +19 -0
  53. package/src/client/pages/system/WorkspaceFormPage.tsx +28 -19
  54. package/src/client/pages/system/WorkspaceList.tsx +6 -1
  55. package/src/client/pages/system/WorkspacesPage.tsx +18 -12
  56. package/src/client/pages/system/workspaceDraft.ts +8 -0
  57. package/src/client/theme.ts +52 -0
  58. package/src/client/types.ts +8 -1
  59. package/src/mock-reporting/fixtures.ts +48 -35
  60. package/src/shell.ts +4 -1
  61. package/src/tailwind.css +109 -0
  62. package/src/theme.ts +49 -0
  63. package/src/client/pages/system/BaselineSnapshotCard.tsx +0 -18
@@ -1,11 +1,17 @@
1
1
  import { memo } from "react";
2
+ import type { ActorIdentity } from "@sentry/junior/api/schema";
2
3
 
3
4
  import { HighlightedCode } from "../code";
4
5
  import { parseMarkdownBlocks, transcriptRoleKind } from "../format";
5
6
  import { TranscriptMarkdown } from "./TranscriptMarkdown";
7
+ import { MentionPeopleProvider } from "./transcriptMentions";
6
8
 
7
- /** Render transcript prose as Markdown with explicit fenced code blocks. */
9
+ /**
10
+ * Render transcript prose as Markdown with explicit fenced code blocks.
11
+ * `mentions` are the people that the API resolved for this text.
12
+ */
8
13
  export const TranscriptText = memo(function TranscriptText(props: {
14
+ mentions: readonly ActorIdentity[];
9
15
  role?: string;
10
16
  text: string;
11
17
  }) {
@@ -13,27 +19,29 @@ export const TranscriptText = memo(function TranscriptText(props: {
13
19
  const blocks = parseMarkdownBlocks(props.text);
14
20
 
15
21
  return (
16
- <div className="grid min-w-0 gap-2">
17
- {blocks.map((block, index) => {
18
- if (block.language === "markdown" && !block.fenced) {
22
+ <MentionPeopleProvider mentions={props.mentions}>
23
+ <div className="grid min-w-0 gap-2">
24
+ {blocks.map((block, index) => {
25
+ if (block.language === "markdown" && !block.fenced) {
26
+ return (
27
+ <TranscriptMarkdown
28
+ compact={roleKind === "assistant" || roleKind === "user"}
29
+ key={index}
30
+ text={block.code}
31
+ />
32
+ );
33
+ }
34
+
19
35
  return (
20
- <TranscriptMarkdown
21
- compact={roleKind === "assistant" || roleKind === "user"}
36
+ <div
37
+ className="min-w-0 overflow-hidden rounded-md border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5"
22
38
  key={index}
23
- text={block.code}
24
- />
39
+ >
40
+ <HighlightedCode code={block.code} language={block.language} />
41
+ </div>
25
42
  );
26
- }
27
-
28
- return (
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
- key={index}
32
- >
33
- <HighlightedCode code={block.code} language={block.language} />
34
- </div>
35
- );
36
- })}
37
- </div>
43
+ })}
44
+ </div>
45
+ </MentionPeopleProvider>
38
46
  );
39
47
  });
@@ -1,6 +1,12 @@
1
+ // Slack writes links as `<https://example.com>` or `<https://example.com|label>`.
2
+ const SLACK_LINK_RE = /<((?:https?|mailto):[^\s|<>]+)(?:\|([^<>]*))?>/g;
3
+
1
4
  /** Reduce message Markdown to compact plain text for conversation previews. */
2
5
  export function formatConversationActivityPreview(text: string): string {
3
6
  return text
7
+ .replace(SLACK_LINK_RE, (_, url: string, label?: string) =>
8
+ label?.trim() ? label.trim() : url,
9
+ )
4
10
  .replace(/\[([^\]]+)\]\([^\s)]+(?:\s+"[^"]*")?\)/g, "$1")
5
11
  .replace(/(^|\s)([*_~`]{1,3})(?=\S)/g, "$1")
6
12
  .replace(/([*_~`]{1,3})(?=\s|$|[.,!?;:])/g, "")
@@ -37,6 +37,7 @@ function eventMessage(
37
37
  role,
38
38
  sourceSeq: event.seq,
39
39
  timestamp: eventTimestamp(event),
40
+ ...(event.mentions ? { mentions: event.mentions } : undefined),
40
41
  };
41
42
  }
42
43
 
@@ -213,12 +214,12 @@ export function transcriptMessagesFromEvents(
213
214
  ...(data.actorIdentity
214
215
  ? { actorIdentity: data.actorIdentity }
215
216
  : undefined),
216
- ...(data.mentions ? { mentions: data.mentions } : undefined),
217
217
  ...(data.eventType ? { eventType: data.eventType } : undefined),
218
218
  eventObjectType: data.eventObjectType,
219
219
  ...(data.trustedSummary
220
220
  ? { trustedSummary: data.trustedSummary }
221
221
  : undefined),
222
+ ...(data.events ? { events: data.events } : undefined),
222
223
  ...(data.explicitMention !== undefined
223
224
  ? { explicitMention: data.explicitMention }
224
225
  : undefined),
@@ -388,6 +389,7 @@ export function transcriptMessagesFromEvents(
388
389
  eventMessage(event, "system", [
389
390
  {
390
391
  type: "structured_event",
392
+ ...(event.mentions ? { mentions: event.mentions } : undefined),
391
393
  namespace: data.namespace,
392
394
  name: data.name,
393
395
  version: data.version,
@@ -415,6 +417,7 @@ export function transcriptMessagesFromEvents(
415
417
  eventMessage(event, "system", [
416
418
  {
417
419
  type: "context_event",
420
+ ...(event.mentions ? { mentions: event.mentions } : undefined),
418
421
  event:
419
422
  data.type === "handoff"
420
423
  ? {
@@ -3,7 +3,7 @@ import { Link } from "react-router";
3
3
  import type { ActorIdentity } from "@sentry/junior/api/schema";
4
4
 
5
5
  import { peoplePath } from "../format";
6
- import type { TranscriptViewMessage } from "../types";
6
+ import { cn } from "../styles";
7
7
  import { TRANSCRIPT_ANCHOR_CLASS } from "./transcriptMarkdownLinks";
8
8
  import { HighlightText } from "./transcriptSearch";
9
9
 
@@ -22,16 +22,17 @@ const TranscriptMentionPeople = createContext<
22
22
  ReadonlyMap<string, ActorIdentity>
23
23
  >(new Map());
24
24
 
25
- /** Share the people that the API resolved for mentions in one transcript. */
26
- export function TranscriptMentionPeopleProvider(props: {
25
+ /**
26
+ * Share the people that the API resolved for one block of display text.
27
+ * Use `TranscriptText` or `MentionText`, which require the API mentions.
28
+ */
29
+ export function MentionPeopleProvider(props: {
27
30
  children: ReactNode;
28
- messages: readonly TranscriptViewMessage[];
31
+ mentions: readonly ActorIdentity[];
29
32
  }) {
30
33
  const people = new Map<string, ActorIdentity>();
31
- for (const message of props.messages) {
32
- for (const person of message.mentions ?? []) {
33
- if (person.slackUserId) people.set(person.slackUserId, person);
34
- }
34
+ for (const person of props.mentions) {
35
+ if (person.slackUserId) people.set(person.slackUserId, person);
35
36
  }
36
37
  return (
37
38
  <TranscriptMentionPeople.Provider value={people}>
@@ -40,7 +41,22 @@ export function TranscriptMentionPeopleProvider(props: {
40
41
  );
41
42
  }
42
43
 
43
- /** Render text with Slack `<@U123>` and `<#C123|name>` tokens as readable mentions. */
44
+ /** Render one line of display text with the people the API resolved for it. */
45
+ export function MentionText(props: {
46
+ mentions: readonly ActorIdentity[];
47
+ text: string;
48
+ }) {
49
+ return (
50
+ <MentionPeopleProvider mentions={props.mentions}>
51
+ {renderMentionText(props.text, "mention-text")}
52
+ </MentionPeopleProvider>
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Render text with Slack `<@U123>` and `<#C123|name>` tokens as readable
58
+ * mentions. It reads people from the nearest `MentionPeopleProvider`.
59
+ */
44
60
  export function renderMentionText(text: string, keyBase: string): ReactNode[] {
45
61
  const nodes: ReactNode[] = [];
46
62
  let cursor = 0;
@@ -88,7 +104,8 @@ function MentionView(props: { mention: TranscriptMention }) {
88
104
  if (person?.email) {
89
105
  return (
90
106
  <Link
91
- className={TRANSCRIPT_ANCHOR_CLASS}
107
+ // Previews sit in cards that disable pointer events on their text.
108
+ className={cn("pointer-events-auto", TRANSCRIPT_ANCHOR_CLASS)}
92
109
  title={id}
93
110
  to={peoplePath(person.email)}
94
111
  >
@@ -18,7 +18,7 @@ import {
18
18
  import { Card } from "../../components/layout/Card";
19
19
  import { formatCostSummary } from "../../format";
20
20
 
21
- const COST_COLOR = "#67e8f9";
21
+ const COST_COLOR = "var(--color-cyan-300)";
22
22
 
23
23
  /** Render linked conversation spend for completed automation executions. */
24
24
  export function AutomationCostChart(props: {
@@ -103,7 +103,7 @@ export function AutomationCostChart(props: {
103
103
  format={(value) => formatCostSummary({ total: value })}
104
104
  layout={layout}
105
105
  maximum={maximum}
106
- stroke="#e2e8f0"
106
+ stroke="var(--color-slate-200)"
107
107
  />
108
108
  <ActivityChartDateLabels
109
109
  dates={days.map((day) => day.date)}
@@ -88,7 +88,10 @@ export function AutomationDetailsDrawer(props: {
88
88
  <h3 className="mt-0 mb-2 text-sm font-medium text-dashboard-text-muted">
89
89
  What to do
90
90
  </h3>
91
- <TranscriptText text={automation.instruction} />
91
+ <TranscriptText
92
+ mentions={automation.mentions ?? []}
93
+ text={automation.instruction}
94
+ />
92
95
  </div>
93
96
  <AutomationLifecycle
94
97
  key={`${automation.kind}:${automation.id}`}
@@ -18,7 +18,7 @@ import {
18
18
  import { Card } from "../../components/layout/Card";
19
19
  import { formatActivityChartAverage } from "../../format";
20
20
 
21
- const EXECUTION_COLOR = "#fbbf24";
21
+ const EXECUTION_COLOR = "var(--color-amber-400)";
22
22
 
23
23
  /** Render completed automation executions over a trailing window. */
24
24
  export function AutomationExecutionChart(props: {
@@ -95,7 +95,7 @@ export function AutomationExecutionChart(props: {
95
95
  format={formatActivityChartAverage}
96
96
  layout={layout}
97
97
  maximum={maximum}
98
- stroke="#e2e8f0"
98
+ stroke="var(--color-slate-200)"
99
99
  />
100
100
  <ActivityChartDateLabels
101
101
  dates={days.map((day) => day.date)}
@@ -15,9 +15,9 @@ import {
15
15
  import { Card } from "../../components/layout/Card";
16
16
 
17
17
  const series = [
18
- { color: "#6ee7b7", key: "completed", label: "Completed" },
19
- { color: "#fda4af", key: "failed", label: "Failed" },
20
- { color: "#fcd34d", key: "blocked", label: "Blocked" },
18
+ { color: "var(--color-emerald-300)", key: "completed", label: "Completed" },
19
+ { color: "var(--color-rose-300)", key: "failed", label: "Failed" },
20
+ { color: "var(--color-amber-300)", key: "blocked", label: "Blocked" },
21
21
  ] as const;
22
22
 
23
23
  /** Render one automation's terminal executions by status over a trailing window. */
@@ -19,9 +19,9 @@ import { ChartLegend } from "../../components/charts/ChartLegend";
19
19
  import { Card } from "../../components/layout/Card";
20
20
  import { formatActivityChartAverage } from "../../format";
21
21
 
22
- const CREATED_COLOR = "#67e8f9";
23
- const MERGED_COLOR = "#6ee7b7";
24
- const CLOSED_COLOR = "#fb7185";
22
+ const CREATED_COLOR = "var(--color-cyan-300)";
23
+ const MERGED_COLOR = "var(--color-emerald-300)";
24
+ const CLOSED_COLOR = "var(--color-rose-400)";
25
25
 
26
26
  /** Render created, merged, and closed code changes over a trailing window. */
27
27
  export function CodeActivityChart(props: {
@@ -143,7 +143,7 @@ export function CodeActivityChart(props: {
143
143
  format={formatActivityChartAverage}
144
144
  layout={layout}
145
145
  maximum={maximum}
146
- stroke="#e2e8f0"
146
+ stroke="var(--color-slate-200)"
147
147
  />
148
148
  <ActivityChartDateLabels
149
149
  dates={days.map((day) => day.date)}
@@ -50,6 +50,7 @@ import { StatCard } from "../../components/metrics/StatCard";
50
50
  import { StatusChip } from "../../components/StatusChip";
51
51
  import { StatusDot } from "../../components/StatusDot";
52
52
  import { TextArea, TextInput } from "../../components/TextInput";
53
+ import { ThemeSwitcher } from "../../components/ThemeSwitcher";
53
54
  import { TranscriptMarkdown } from "../../conversations/TranscriptMarkdown";
54
55
  import { TranscriptText } from "../../conversations/TranscriptText";
55
56
  import { TranscriptMessageShell } from "../../conversations/TranscriptMessageShell";
@@ -231,7 +232,6 @@ function FoundationsGalleryPage() {
231
232
  <Fixture title="Conversation navigation">
232
233
  <DashboardChromeProvider>
233
234
  <DashboardHeader
234
- compact
235
235
  mobileNavigationOpen={navigationOpen}
236
236
  navItems={[
237
237
  { key: "code", label: "Code", to: "/code" },
@@ -301,6 +301,9 @@ function FoundationsGalleryPage() {
301
301
  </Drawer>
302
302
  ) : null}
303
303
  </Fixture>
304
+ <Fixture title="Theme">
305
+ <ThemeSwitcher className="max-w-64" />
306
+ </Fixture>
304
307
  <Fixture title="Color tokens">
305
308
  <div className="grid gap-4">
306
309
  <TokenSwatchRow
@@ -733,6 +736,7 @@ function TranscriptsGalleryPage() {
733
736
  Junior
734
737
  </span>
735
738
  <TranscriptText
739
+ mentions={[]}
736
740
  role="assistant"
737
741
  text={
738
742
  "Your reminder is set.\n\nI'll remind you to drink water every 30 minutes until you ask me to stop."
@@ -793,10 +797,10 @@ function TranscriptsGalleryPage() {
793
797
  <TranscriptMarkdown text={GFM_SAMPLE} />
794
798
  </Fixture>
795
799
  <Fixture title="TranscriptText assistant">
796
- <TranscriptText role="assistant" text={MIXED_ASSISTANT} />
800
+ <TranscriptText mentions={[]} role="assistant" text={MIXED_ASSISTANT} />
797
801
  </Fixture>
798
802
  <Fixture title="TranscriptText user">
799
- <TranscriptText role="user" text={EVENT_NOTIFICATION} />
803
+ <TranscriptText mentions={[]} role="user" text={EVENT_NOTIFICATION} />
800
804
  </Fixture>
801
805
  <Fixture title="Tool calls">
802
806
  <ToolCallGallery />
@@ -107,6 +107,35 @@ const EVENTS: ConversationReportEventData[] = [
107
107
  trustedSummary: "GitHub PR getsentry/junior#1200 received a review.",
108
108
  text: "The preview is ready for visual review.",
109
109
  },
110
+ {
111
+ type: "message",
112
+ messageId: "gallery-event-batch",
113
+ role: "user",
114
+ eventType: "pull_request.review.approved",
115
+ eventObjectType: "code_change",
116
+ trustedSummary: "GitHub PR getsentry/junior#1200 was approved by dcramer.",
117
+ events: [
118
+ {
119
+ eventType: "pull_request.review.approved",
120
+ label: "GitHub PR getsentry/junior#1200",
121
+ trustedSummary:
122
+ "GitHub PR getsentry/junior#1200 was approved by dcramer.",
123
+ },
124
+ {
125
+ eventType: "pull_request.review.commented",
126
+ label: "GitHub PR getsentry/junior#1200",
127
+ trustedSummary:
128
+ "GitHub PR getsentry/junior#1200 received a review comment from cursor[bot].",
129
+ },
130
+ {
131
+ eventType: "pull_request.review_comment.created",
132
+ label: "GitHub PR getsentry/junior#1200",
133
+ trustedSummary:
134
+ "GitHub PR getsentry/junior#1200 received an inline review comment from cursor[bot].",
135
+ },
136
+ ],
137
+ text: "[task]\n\nThis is a task, not a message from a person.\n\nYou received 3 events.",
138
+ },
110
139
  ];
111
140
 
112
141
  const CONVERSATION: ConversationDetailReport = {
@@ -43,7 +43,13 @@ export function LocationActivityChart(props: {
43
43
  <ChartLegend
44
44
  ariaLabel="Conversation activity period"
45
45
  inline
46
- items={[{ color: "#22d3ee", key: "period", label: "90 days" }]}
46
+ items={[
47
+ {
48
+ color: "var(--color-cyan-400)",
49
+ key: "period",
50
+ label: "90 days",
51
+ },
52
+ ]}
47
53
  />
48
54
  }
49
55
  />
@@ -55,8 +61,12 @@ export function LocationActivityChart(props: {
55
61
  >
56
62
  <defs>
57
63
  <linearGradient id="location-bars" x1="0" x2="0" y1="0" y2="1">
58
- <stop offset="0%" stopColor="#22d3ee" />
59
- <stop offset="100%" stopColor="#f59e0b" stopOpacity="0.65" />
64
+ <stop offset="0%" stopColor="var(--color-cyan-400)" />
65
+ <stop
66
+ offset="100%"
67
+ stopColor="var(--color-amber-500)"
68
+ stopOpacity="0.65"
69
+ />
60
70
  </linearGradient>
61
71
  </defs>
62
72
  <ActivityChartGrid layout={layout} maximum={maximum} />
@@ -98,7 +108,7 @@ export function LocationActivityChart(props: {
98
108
  format={formatActivityChartAverage}
99
109
  layout={layout}
100
110
  maximum={maximum}
101
- stroke="#22d3ee"
111
+ stroke="var(--color-cyan-400)"
102
112
  />
103
113
  <ActivityChartDateLabels
104
114
  dates={props.days.map((day) => day.date)}
@@ -24,8 +24,7 @@ export function LocationDirectoryActivityChart(props: {
24
24
  days: LocationActivityDayReport[];
25
25
  }) {
26
26
  const bucketUnit = props.bucketUnit ?? "day";
27
- const perBucket =
28
- `Conversations per ${timeRangeBucketPerLabel(bucketUnit)}`;
27
+ const perBucket = `Conversations per ${timeRangeBucketPerLabel(bucketUnit)}`;
29
28
 
30
29
  const layout = createActivityChartLayout(260);
31
30
  const maximum = Math.max(
@@ -42,17 +41,23 @@ export function LocationDirectoryActivityChart(props: {
42
41
  return (
43
42
  <Card>
44
43
  <CardHeader
45
- description={
46
- `${timeRangeBucketAdjective(bucketUnit)} public volume compared with private activity in aggregate.`
47
- }
44
+ description={`${timeRangeBucketAdjective(bucketUnit)} public volume compared with private activity in aggregate.`}
48
45
  title={perBucket}
49
46
  trailing={
50
47
  <ChartLegend
51
48
  ariaLabel="Conversation visibility legend"
52
49
  inline
53
50
  items={[
54
- { color: "#22d3ee", key: "public", label: "public" },
55
- { color: "#fbbf24", key: "private", label: "private" },
51
+ {
52
+ color: "var(--color-cyan-400)",
53
+ key: "public",
54
+ label: "public",
55
+ },
56
+ {
57
+ color: "var(--color-amber-400)",
58
+ key: "private",
59
+ label: "private",
60
+ },
56
61
  ]}
57
62
  />
58
63
  }
@@ -70,13 +75,13 @@ export function LocationDirectoryActivityChart(props: {
70
75
  [
71
76
  {
72
77
  count: day.publicConversations,
73
- fill: "#22d3ee",
78
+ fill: "var(--color-cyan-400)",
74
79
  key: "public",
75
80
  x: groupX,
76
81
  },
77
82
  {
78
83
  count: day.privateConversations,
79
- fill: "#fbbf24",
84
+ fill: "var(--color-amber-400)",
80
85
  key: "private",
81
86
  x: groupX + barWidth + gap,
82
87
  },
@@ -74,13 +74,13 @@ export function MemoryCostChart(props: {
74
74
  ariaLabel="Memory cost legend"
75
75
  items={[
76
76
  {
77
- color: "#67e8f9",
77
+ color: "var(--color-cyan-300)",
78
78
  key: "extraction",
79
79
  label: "Extraction",
80
80
  value: formatCostSummary({ total: extractionTotal }),
81
81
  },
82
82
  {
83
- color: "#e879f9",
83
+ color: "var(--color-fuchsia-400)",
84
84
  key: "recall",
85
85
  label: "Recall",
86
86
  value: formatCostSummary({ total: recallTotal }),
@@ -130,7 +130,7 @@ export function MemoryCostChart(props: {
130
130
  >
131
131
  <g tabIndex={0}>
132
132
  <rect
133
- fill="#67e8f9"
133
+ fill="var(--color-cyan-300)"
134
134
  height={extractionHeight}
135
135
  opacity={day.extraction.costUsd > 0 ? 0.82 : 0.1}
136
136
  rx="1"
@@ -139,7 +139,7 @@ export function MemoryCostChart(props: {
139
139
  y={layout.top + layout.plotHeight - extractionHeight}
140
140
  />
141
141
  <rect
142
- fill="#e879f9"
142
+ fill="var(--color-fuchsia-400)"
143
143
  height={recallHeight}
144
144
  opacity={day.recall.costUsd > 0 ? 0.82 : 0.1}
145
145
  rx="1"
@@ -169,7 +169,7 @@ export function MemoryCostChart(props: {
169
169
  format={(value) => formatCostSummary({ total: value })}
170
170
  layout={layout}
171
171
  maximum={maximum}
172
- stroke="#e2e8f0"
172
+ stroke="var(--color-slate-200)"
173
173
  />
174
174
  <ActivityChartDateLabels
175
175
  dates={days.map((day) => day.date)}
@@ -10,6 +10,10 @@ import type {
10
10
  PluginUserPageRecordAction,
11
11
  } from "../user/pluginUserPageData";
12
12
 
13
+ // The memory plugin API does not resolve mentions yet, so memory text shows
14
+ // Slack ids instead of names.
15
+ const NO_MEMORY_MENTIONS: readonly [] = [];
16
+
13
17
  type MemoryActionMutation = UseMutationResult<
14
18
  void,
15
19
  Error,
@@ -86,10 +90,13 @@ export function MemoryDetailsDrawer(props: {
86
90
  <div className="mb-2 font-sans font-medium text-xs text-dashboard-text-muted">
87
91
  Memory
88
92
  </div>
89
- <TranscriptText text={record.title} />
93
+ <TranscriptText mentions={NO_MEMORY_MENTIONS} text={record.title} />
90
94
  {record.description ? (
91
95
  <div className="mt-3">
92
- <TranscriptText text={record.description} />
96
+ <TranscriptText
97
+ mentions={NO_MEMORY_MENTIONS}
98
+ text={record.description}
99
+ />
93
100
  </div>
94
101
  ) : null}
95
102
  </div>
@@ -15,8 +15,8 @@ import { Card } from "../../components/layout/Card";
15
15
  import type { MemoryDay } from "./memoryDashboard";
16
16
 
17
17
  const series = [
18
- { color: "#67e8f9", key: "personal", label: "Personal" },
19
- { color: "#6ee7b7", key: "public", label: "Public" },
18
+ { color: "var(--color-cyan-300)", key: "personal", label: "Personal" },
19
+ { color: "var(--color-emerald-300)", key: "public", label: "Public" },
20
20
  ] as const;
21
21
 
22
22
  /** Render viewer memory creation as a stacked personal/public timeline. */
@@ -124,10 +124,10 @@ export function ContributionGrid(props: { days: ContributionDay[] }) {
124
124
  className={cn(
125
125
  "size-3 border border-dashboard-overlay",
126
126
  level === 0 && "bg-dashboard-surface-hover",
127
- level === 1 && "bg-[#133225]",
128
- level === 2 && "bg-[#176a4a]",
129
- level === 3 && "bg-[#22a06b]",
130
- level === 4 && "bg-[#8bdc97]",
127
+ level === 1 && "bg-[#133225] light:bg-[#9be9a8]",
128
+ level === 2 && "bg-[#176a4a] light:bg-[#40c463]",
129
+ level === 3 && "bg-[#22a06b] light:bg-[#30a14e]",
130
+ level === 4 && "bg-[#8bdc97] light:bg-[#216e39]",
131
131
  )}
132
132
  key={level}
133
133
  />
@@ -156,8 +156,8 @@ function activityRuntime(day: ContributionDay): string {
156
156
  function activityClass(count: number, max: number): string {
157
157
  if (count <= 0) return "bg-dashboard-surface-hover";
158
158
  const ratio = count / max;
159
- if (ratio >= 0.75) return "bg-[#8bdc97]";
160
- if (ratio >= 0.45) return "bg-[#22a06b]";
161
- if (ratio >= 0.2) return "bg-[#176a4a]";
162
- return "bg-[#133225]";
159
+ if (ratio >= 0.75) return "bg-[#8bdc97] light:bg-[#216e39]";
160
+ if (ratio >= 0.45) return "bg-[#22a06b] light:bg-[#30a14e]";
161
+ if (ratio >= 0.2) return "bg-[#176a4a] light:bg-[#40c463]";
162
+ return "bg-[#133225] light:bg-[#9be9a8]";
163
163
  }
@@ -44,8 +44,7 @@ export function PeopleActivityChart(props: {
44
44
  days: PeopleActivityDayReport[];
45
45
  }) {
46
46
  const bucketUnit = props.bucketUnit ?? "day";
47
- const chartTitle =
48
- `Active people per ${timeRangeBucketPerLabel(bucketUnit)}`;
47
+ const chartTitle = `Active people per ${timeRangeBucketPerLabel(bucketUnit)}`;
49
48
 
50
49
  const layout = createActivityChartLayout(260);
51
50
  const values = props.days.map((day) => day.activePeople);
@@ -70,7 +69,13 @@ export function PeopleActivityChart(props: {
70
69
  <ChartLegend
71
70
  ariaLabel="People activity legend"
72
71
  inline
73
- items={[{ color: "#fbbf24", key: "people", label: "people" }]}
72
+ items={[
73
+ {
74
+ color: "var(--color-amber-400)",
75
+ key: "people",
76
+ label: "people",
77
+ },
78
+ ]}
74
79
  />
75
80
  }
76
81
  />
@@ -82,13 +87,21 @@ export function PeopleActivityChart(props: {
82
87
  >
83
88
  <defs>
84
89
  <linearGradient id="people-area" x1="0" x2="0" y1="0" y2="1">
85
- <stop offset="0%" stopColor="#f59e0b" stopOpacity="0.3" />
86
- <stop offset="100%" stopColor="#f59e0b" stopOpacity="0" />
90
+ <stop
91
+ offset="0%"
92
+ stopColor="var(--color-amber-500)"
93
+ stopOpacity="0.3"
94
+ />
95
+ <stop
96
+ offset="100%"
97
+ stopColor="var(--color-amber-500)"
98
+ stopOpacity="0"
99
+ />
87
100
  </linearGradient>
88
101
  <linearGradient id="people-line" x1="0" x2="1" y1="0" y2="0">
89
- <stop offset="0%" stopColor="#f59e0b" />
90
- <stop offset="55%" stopColor="#fbbf24" />
91
- <stop offset="100%" stopColor="#22d3ee" />
102
+ <stop offset="0%" stopColor="var(--color-amber-500)" />
103
+ <stop offset="55%" stopColor="var(--color-amber-400)" />
104
+ <stop offset="100%" stopColor="var(--color-cyan-400)" />
92
105
  </linearGradient>
93
106
  </defs>
94
107
  <ActivityChartGrid layout={layout} maximum={maximum} />
@@ -122,7 +135,7 @@ export function PeopleActivityChart(props: {
122
135
  <circle
123
136
  cx={point.x}
124
137
  cy={point.y}
125
- fill="#fbbf24"
138
+ fill="var(--color-amber-400)"
126
139
  opacity={props.days.length > 30 ? 0.35 : 0.75}
127
140
  r={props.days.length > 30 ? 3 : 4}
128
141
  tabIndex={0}
@@ -136,7 +149,7 @@ export function PeopleActivityChart(props: {
136
149
  format={formatActivityChartAverage}
137
150
  layout={layout}
138
151
  maximum={maximum}
139
- stroke="#fbbf24"
152
+ stroke="var(--color-amber-400)"
140
153
  />
141
154
  <ActivityChartDateLabels
142
155
  dates={props.days.map((day) => day.date)}