@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.
- package/dist/app.js +2 -2
- package/dist/client.js +36 -35
- package/dist/index.js +2 -2
- package/dist/tailwind.css +1 -1
- package/package.json +3 -3
- package/src/client/App.tsx +1 -5
- package/src/client/components/ActorAvatar.tsx +7 -4
- package/src/client/components/AutomationCard.tsx +2 -2
- package/src/client/components/Button.tsx +12 -11
- package/src/client/components/ConversationList.tsx +3 -3
- package/src/client/components/ConversationSummary.tsx +2 -1
- package/src/client/components/DetailList.tsx +3 -3
- package/src/client/components/Drawer.tsx +2 -2
- package/src/client/components/EmptyTelemetry.tsx +1 -1
- package/src/client/components/Field.tsx +1 -2
- package/src/client/components/FilterBar.tsx +6 -6
- package/src/client/components/Metric.tsx +4 -4
- package/src/client/components/Notice.tsx +7 -7
- package/src/client/components/PageContentSkeleton.tsx +2 -2
- package/src/client/components/Pagination.tsx +2 -2
- package/src/client/components/ParticipantAvatarStack.tsx +4 -4
- package/src/client/components/ProfileMenu.tsx +5 -7
- package/src/client/components/README.md +122 -58
- package/src/client/components/SearchInput.tsx +4 -3
- package/src/client/components/SegmentedNav.tsx +10 -17
- package/src/client/components/SegmentedTabs.tsx +3 -3
- package/src/client/components/Select.tsx +1 -1
- package/src/client/components/SelectableRow.tsx +2 -2
- package/src/client/components/Skeleton.tsx +1 -1
- package/src/client/components/StatusChip.tsx +1 -1
- package/src/client/components/StatusDot.tsx +1 -1
- package/src/client/components/TextInput.tsx +1 -1
- package/src/client/components/Tooltip.tsx +2 -2
- package/src/client/components/VersionDriftBanner.tsx +2 -2
- package/src/client/components/charts/ActivityChart.tsx +5 -6
- package/src/client/components/charts/CacheWrites.tsx +3 -3
- package/src/client/components/charts/ChartHeader.tsx +4 -4
- package/src/client/components/charts/ChartLegend.tsx +1 -1
- package/src/client/components/charts/InputCacheChart.tsx +5 -5
- package/src/client/components/charts/WorkspaceUsageChart.tsx +4 -11
- package/src/client/components/controls/DirectorySortSelect.tsx +2 -2
- package/src/client/components/directory/DirectoryMetric.tsx +2 -2
- package/src/client/components/directory/DirectoryRowsSkeleton.tsx +5 -5
- package/src/client/components/directory/DirectoryTable.tsx +3 -3
- package/src/client/components/layout/CardHeader.tsx +4 -4
- package/src/client/components/layout/DashboardChrome.tsx +9 -3
- package/src/client/components/layout/DashboardHeader.tsx +7 -7
- package/src/client/components/layout/PageHeader.tsx +2 -2
- package/src/client/components/layout/SecondaryNavigation.tsx +7 -14
- package/src/client/components/layout/SectionIntro.tsx +1 -1
- package/src/client/components/layout/SectionTitle.tsx +1 -1
- package/src/client/components/metrics/StatCard.tsx +2 -2
- package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
- package/src/client/conversations/ConversationBrief.tsx +4 -4
- package/src/client/conversations/ConversationChildAgents.tsx +7 -4
- package/src/client/conversations/ConversationComposer.tsx +8 -7
- package/src/client/conversations/ConversationHeader.tsx +5 -5
- package/src/client/conversations/ConversationHeaderActions.tsx +9 -9
- package/src/client/conversations/ConversationHomeList.tsx +3 -3
- package/src/client/conversations/ConversationListStatusIcon.tsx +1 -1
- package/src/client/conversations/ConversationMemories.tsx +4 -4
- package/src/client/conversations/ConversationMeta.tsx +10 -10
- package/src/client/conversations/ConversationPage.tsx +2 -1
- package/src/client/conversations/ConversationSidebar.tsx +6 -6
- package/src/client/conversations/ConversationUsageBreakdown.tsx +4 -4
- package/src/client/conversations/ConversationWorkspace.tsx +1 -1
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +4 -4
- package/src/client/conversations/CopyMarkdownButton.tsx +1 -1
- package/src/client/conversations/EventUsage.tsx +2 -2
- package/src/client/conversations/PendingMailboxStack.tsx +5 -5
- package/src/client/conversations/README.md +9 -0
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +39 -23
- package/src/client/conversations/ToolFrame.tsx +36 -14
- package/src/client/conversations/TranscriptActivityGroup.tsx +10 -3
- package/src/client/conversations/TranscriptAttachmentsDeliveredView.tsx +4 -6
- package/src/client/conversations/TranscriptContextEventView.tsx +2 -2
- package/src/client/conversations/TranscriptEventView.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +3 -3
- package/src/client/conversations/TranscriptMessageContextView.tsx +7 -7
- package/src/client/conversations/TranscriptMessageView.tsx +2 -6
- package/src/client/conversations/TranscriptRailEvent.tsx +20 -8
- package/src/client/conversations/TranscriptReasoningView.tsx +12 -12
- package/src/client/conversations/TranscriptStructuredEventView.tsx +16 -11
- package/src/client/conversations/TranscriptSubagentView.tsx +64 -39
- package/src/client/conversations/TranscriptText.tsx +5 -4
- package/src/client/conversations/TranscriptToolView.tsx +9 -11
- package/src/client/conversations/TranscriptTurnContextView.tsx +3 -3
- package/src/client/conversations/TranscriptView.tsx +3 -3
- package/src/client/conversations/UsageCard.tsx +1 -1
- package/src/client/conversations/eventTranscript.ts +7 -0
- package/src/client/conversations/transcriptMarkdownLinks.ts +2 -4
- package/src/client/conversations/transcriptStyles.ts +1 -1
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/admin/AdminPage.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +2 -2
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +5 -10
- package/src/client/pages/automations/AutomationEditor.tsx +1 -1
- package/src/client/pages/automations/AutomationExecutionChart.tsx +2 -2
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +2 -2
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +4 -4
- package/src/client/pages/automations/AutomationRow.tsx +4 -4
- package/src/client/pages/automations/AutomationRunsPage.tsx +3 -3
- package/src/client/pages/automations/AutomationScheduleFields.tsx +1 -1
- package/src/client/pages/automations/AutomationVersionsPage.tsx +1 -1
- package/src/client/pages/code/CodeActivityChart.tsx +7 -6
- package/src/client/pages/code/CodeChangeRow.tsx +1 -1
- package/src/client/pages/code/CodePage.tsx +5 -5
- package/src/client/pages/code/RepositoryPage.tsx +2 -2
- package/src/client/pages/dev/ComponentsPage.tsx +40 -146
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +206 -0
- package/src/client/pages/locations/LocationDetailPage.tsx +4 -4
- package/src/client/pages/locations/LocationDirectory.tsx +1 -1
- package/src/client/pages/locations/PrivateActivityCard.tsx +3 -3
- package/src/client/pages/memory/MemoryCostChart.tsx +3 -5
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +5 -5
- package/src/client/pages/memory/MemoryPage.tsx +15 -15
- package/src/client/pages/memory/MemoryTimeline.tsx +3 -5
- package/src/client/pages/people/ContributionGrid.tsx +8 -5
- package/src/client/pages/people/PersonProfilePage.tsx +3 -6
- package/src/client/pages/people/ProfilePluginReports.tsx +2 -2
- package/src/client/pages/system/GuardianActivity.tsx +9 -9
- package/src/client/pages/system/PluginBarChart.tsx +4 -6
- package/src/client/pages/system/PluginDetails.tsx +11 -7
- package/src/client/pages/system/PluginPanels.tsx +8 -8
- package/src/client/pages/system/PluginReportRecordSet.tsx +14 -14
- package/src/client/pages/system/PluginReports.tsx +7 -11
- package/src/client/pages/system/SkillInventory.tsx +9 -9
- package/src/client/pages/system/SystemActivity.tsx +1 -1
- package/src/client/pages/system/SystemPage.tsx +3 -6
- package/src/client/pages/system/WorkspaceDetails.tsx +2 -5
- package/src/client/pages/system/WorkspaceEditor.tsx +2 -2
- package/src/client/pages/system/WorkspaceList.tsx +2 -2
- package/src/client/pages/user/PluginUserPage.tsx +3 -3
- package/src/client/styles.ts +5 -4
- package/src/client/types.ts +2 -0
- package/src/tailwind.css +36 -38
|
@@ -2,11 +2,6 @@ import { useLayoutEffect, useRef, useState, type CSSProperties } from "react";
|
|
|
2
2
|
import { Link, NavLink, useLocation } from "react-router";
|
|
3
3
|
|
|
4
4
|
import { pathWithSearch } from "../searchParams";
|
|
5
|
-
import {
|
|
6
|
-
segmentedTabClass,
|
|
7
|
-
segmentedTabsFrameClass,
|
|
8
|
-
segmentedTabsInsetClass,
|
|
9
|
-
} from "./SegmentedTabs";
|
|
10
5
|
import { cn } from "../styles";
|
|
11
6
|
|
|
12
7
|
export type SegmentedNavItem = {
|
|
@@ -84,7 +79,10 @@ function edgeFade(edges: HiddenEdges): CSSProperties | undefined {
|
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
/**
|
|
87
|
-
* Show links between sibling pages
|
|
82
|
+
* Show links between sibling pages as underline tabs. The current page has
|
|
83
|
+
* a solid underline that sits on the border below the row. App navigation
|
|
84
|
+
* uses pills and in-place panel switches use `SegmentedTabs`, so the three
|
|
85
|
+
* levels stay distinct.
|
|
88
86
|
*
|
|
89
87
|
* With `fill`, links share the row width equally while their labels fit.
|
|
90
88
|
* Otherwise each link keeps its label width. When the links do not fit, the
|
|
@@ -139,26 +137,21 @@ export function SegmentedNav(props: {
|
|
|
139
137
|
|
|
140
138
|
const linkClass = ({ isActive }: { isActive: boolean }) =>
|
|
141
139
|
cn(
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
"relative flex h-10 items-center justify-center whitespace-nowrap font-sans text-sm font-medium no-underline transition-colors after:absolute after:inset-x-0 after:bottom-0 after:h-0.5 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus",
|
|
141
|
+
isActive
|
|
142
|
+
? "text-dashboard-text after:bg-dashboard-text-solid"
|
|
143
|
+
: "text-dashboard-text-muted hover:text-dashboard-text",
|
|
144
144
|
props.fill ? "min-w-max flex-[1_0_0]" : "shrink-0",
|
|
145
145
|
);
|
|
146
146
|
|
|
147
147
|
return (
|
|
148
148
|
<nav
|
|
149
149
|
aria-label={props.ariaLabel}
|
|
150
|
-
className={cn(
|
|
151
|
-
segmentedTabsFrameClass,
|
|
152
|
-
"flex min-w-0 max-w-full",
|
|
153
|
-
!props.fill && "w-fit",
|
|
154
|
-
)}
|
|
150
|
+
className={cn("flex min-w-0 max-w-full", !props.fill && "w-fit")}
|
|
155
151
|
>
|
|
156
|
-
{/* Pad the scrolling row, not the frame, so the last link keeps its
|
|
157
|
-
padding when the row is scrolled to the end. */}
|
|
158
152
|
<div
|
|
159
153
|
className={cn(
|
|
160
|
-
|
|
161
|
-
"relative flex min-w-0 flex-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
154
|
+
"relative -mx-1 flex gap-5 px-1 min-w-0 flex-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
162
155
|
)}
|
|
163
156
|
ref={rowRef}
|
|
164
157
|
style={edgeFade(edges)}
|
|
@@ -3,11 +3,11 @@ import { useId, type ReactNode } from "react";
|
|
|
3
3
|
import { cn } from "../styles";
|
|
4
4
|
|
|
5
5
|
/** Style the border and fill of the segmented pill track. */
|
|
6
|
-
|
|
6
|
+
const segmentedTabsFrameClass =
|
|
7
7
|
"rounded-lg border border-dashboard-border bg-dashboard-surface-panel";
|
|
8
8
|
|
|
9
9
|
/** Style the spacing inside the segmented pill track. */
|
|
10
|
-
|
|
10
|
+
const segmentedTabsInsetClass = "gap-1 p-1";
|
|
11
11
|
|
|
12
12
|
/** Style one tab or link in the segmented pill track. */
|
|
13
13
|
export function segmentedTabClass(selected: boolean): string {
|
|
@@ -88,7 +88,7 @@ export function SegmentedTabs<const Value extends string>(props: {
|
|
|
88
88
|
</div>
|
|
89
89
|
<div
|
|
90
90
|
aria-labelledby={`${id}-${props.value}`}
|
|
91
|
-
className="min-w-0 rounded focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
91
|
+
className="min-w-0 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus"
|
|
92
92
|
id={`${id}-panel`}
|
|
93
93
|
role="tabpanel"
|
|
94
94
|
tabIndex={0}
|
|
@@ -10,7 +10,7 @@ export function Select({
|
|
|
10
10
|
<select
|
|
11
11
|
{...props}
|
|
12
12
|
className={cn(
|
|
13
|
-
"block min-h-11 w-full min-w-0 rounded border border-dashboard-border-emphasis bg-dashboard-ink px-3 py-2 text-base text-dashboard-text focus:border-dashboard-focus focus:outline-none aria-invalid:border-rose-300 disabled:cursor-not-allowed disabled:opacity-60 sm:min-h-10 sm:text-sm",
|
|
13
|
+
"block min-h-11 w-full min-w-0 rounded-md border border-dashboard-border-emphasis bg-dashboard-ink px-3 py-2 text-base text-dashboard-text focus:border-dashboard-focus focus:outline-none aria-invalid:border-rose-300 disabled:cursor-not-allowed disabled:opacity-60 sm:min-h-10 sm:text-sm",
|
|
14
14
|
className,
|
|
15
15
|
)}
|
|
16
16
|
/>
|
|
@@ -27,8 +27,8 @@ export function SelectableRow(props: {
|
|
|
27
27
|
className={cn(
|
|
28
28
|
"group cursor-pointer transition-colors",
|
|
29
29
|
props.selected
|
|
30
|
-
? "bg-
|
|
31
|
-
: "hover:bg-
|
|
30
|
+
? "bg-dashboard-accent/[0.045]"
|
|
31
|
+
: "hover:bg-dashboard-fill-soft focus-within:bg-dashboard-fill-soft",
|
|
32
32
|
props.className,
|
|
33
33
|
)}
|
|
34
34
|
onClick={handleClick}
|
|
@@ -9,7 +9,7 @@ export function Skeleton(props: { as?: ElementType; className?: string }) {
|
|
|
9
9
|
<Component
|
|
10
10
|
aria-hidden="true"
|
|
11
11
|
className={cn(
|
|
12
|
-
"animate-pulse rounded bg-dashboard-fill-soft motion-reduce:animate-none",
|
|
12
|
+
"animate-pulse rounded-md bg-dashboard-fill-soft motion-reduce:animate-none",
|
|
13
13
|
props.className,
|
|
14
14
|
)}
|
|
15
15
|
/>
|
|
@@ -13,7 +13,7 @@ export type StatusChipTone =
|
|
|
13
13
|
export type StatusChipSize = "default" | "compact";
|
|
14
14
|
|
|
15
15
|
const toneClass: Record<StatusChipTone, string> = {
|
|
16
|
-
accent: "text-
|
|
16
|
+
accent: "text-dashboard-accent",
|
|
17
17
|
danger: "text-rose-300",
|
|
18
18
|
info: "text-cyan-300",
|
|
19
19
|
neutral: "text-dashboard-text-muted",
|
|
@@ -3,7 +3,7 @@ import type { InputHTMLAttributes, TextareaHTMLAttributes } from "react";
|
|
|
3
3
|
import { cn } from "../styles";
|
|
4
4
|
|
|
5
5
|
const textControlClassName =
|
|
6
|
-
"block w-full rounded border border-dashboard-border-emphasis bg-dashboard-ink px-3 py-2 text-sm text-dashboard-text focus:border-dashboard-focus focus:outline-none aria-invalid:border-rose-300 disabled:cursor-not-allowed disabled:opacity-50";
|
|
6
|
+
"block w-full rounded-md border border-dashboard-border-emphasis bg-dashboard-ink px-3 py-2 font-sans text-sm text-dashboard-text placeholder:text-dashboard-text-muted/70 focus:border-dashboard-focus focus:outline-none focus:ring-1 focus:ring-dashboard-focus/25 aria-invalid:border-rose-300 disabled:cursor-not-allowed disabled:opacity-50";
|
|
7
7
|
|
|
8
8
|
export type TextInputProps = Omit<
|
|
9
9
|
InputHTMLAttributes<HTMLInputElement>,
|
|
@@ -166,7 +166,7 @@ export function Tooltip({
|
|
|
166
166
|
className={cn(
|
|
167
167
|
"z-50 select-text outline-none",
|
|
168
168
|
className ??
|
|
169
|
-
"min-w-36 max-w-lg rounded-md border border-dashboard-border-emphasis bg-dashboard-surface-raised px-3 py-2 font-
|
|
169
|
+
"min-w-36 max-w-lg rounded-md border border-dashboard-border-emphasis bg-dashboard-surface-raised px-3 py-2 font-sans text-xs leading-relaxed text-dashboard-text-muted shadow-2xl shadow-black/70",
|
|
170
170
|
)}
|
|
171
171
|
collisionPadding={VIEWPORT_GAP}
|
|
172
172
|
hideWhenDetached
|
|
@@ -176,7 +176,7 @@ export function Tooltip({
|
|
|
176
176
|
sideOffset={ANCHOR_GAP}
|
|
177
177
|
>
|
|
178
178
|
{label ? (
|
|
179
|
-
<div className="mb-1 font-semibold
|
|
179
|
+
<div className="mb-1 font-semibold text-dashboard-text-muted">
|
|
180
180
|
{label}
|
|
181
181
|
</div>
|
|
182
182
|
) : null}
|
|
@@ -7,12 +7,12 @@ export function VersionDriftBanner(props: { serverVersion?: string }) {
|
|
|
7
7
|
return (
|
|
8
8
|
<div
|
|
9
9
|
aria-live="polite"
|
|
10
|
-
className="flex flex-wrap items-center justify-center gap-x-3 gap-y-1 border-t border-
|
|
10
|
+
className="flex flex-wrap items-center justify-center gap-x-3 gap-y-1 border-t border-dashboard-accent/15 bg-dashboard-accent/[0.07] px-3 py-1.5 text-center font-sans text-xs text-dashboard-accent-text/80"
|
|
11
11
|
role="status"
|
|
12
12
|
>
|
|
13
13
|
<span>The dashboard changed. Refresh to load the latest version.</span>
|
|
14
14
|
<button
|
|
15
|
-
className="cursor-pointer border-0 bg-transparent p-0 font-
|
|
15
|
+
className="cursor-pointer border-0 bg-transparent p-0 font-sans font-semibold text-dashboard-accent-text underline decoration-dashboard-accent/40 underline-offset-2 hover:text-dashboard-text-solid"
|
|
16
16
|
onClick={() => window.location.reload()}
|
|
17
17
|
type="button"
|
|
18
18
|
>
|
|
@@ -65,9 +65,7 @@ export function isActivityHourBucket(date: string): boolean {
|
|
|
65
65
|
/** UTC start of a day (`YYYY-MM-DD`) or hour (`YYYY-MM-DDTHH`) bucket key. */
|
|
66
66
|
export function activityBucketStartMs(date: string): number {
|
|
67
67
|
return Date.parse(
|
|
68
|
-
isActivityHourBucket(date)
|
|
69
|
-
? `${date}:00:00.000Z`
|
|
70
|
-
: `${date}T00:00:00.000Z`,
|
|
68
|
+
isActivityHourBucket(date) ? `${date}:00:00.000Z` : `${date}T00:00:00.000Z`,
|
|
71
69
|
);
|
|
72
70
|
}
|
|
73
71
|
|
|
@@ -134,7 +132,7 @@ const ChartSvgScaleContext = createContext(1);
|
|
|
134
132
|
|
|
135
133
|
/** Shared visual style for SVG chart axis labels. */
|
|
136
134
|
export const chartAxisLabelClassName =
|
|
137
|
-
"fill-
|
|
135
|
+
"fill-dashboard-text-muted font-sans tabular-nums leading-none text-dashboard-text-muted";
|
|
138
136
|
|
|
139
137
|
type ChartAxisLabelProps = Omit<
|
|
140
138
|
SVGProps<SVGTextElement>,
|
|
@@ -167,7 +165,7 @@ export function ChartAxisHtmlLabel(props: {
|
|
|
167
165
|
return (
|
|
168
166
|
<span
|
|
169
167
|
className={cn(
|
|
170
|
-
"font-
|
|
168
|
+
"font-sans tabular-nums text-2xs leading-none text-dashboard-text-muted",
|
|
171
169
|
props.className,
|
|
172
170
|
)}
|
|
173
171
|
style={props.style}
|
|
@@ -331,7 +329,8 @@ export function activityChartAverage(values: readonly number[]): number {
|
|
|
331
329
|
}
|
|
332
330
|
|
|
333
331
|
/** Shared visual style for average-line labels. */
|
|
334
|
-
export const chartAverageLabelClassName =
|
|
332
|
+
export const chartAverageLabelClassName =
|
|
333
|
+
"fill-dashboard-text-solid font-sans tabular-nums leading-none";
|
|
335
334
|
|
|
336
335
|
/** Solid chip behind average labels so series colors never wash out the text. */
|
|
337
336
|
const chartAverageLabelChip = "rgba(9, 12, 14, 0.92)";
|
|
@@ -21,17 +21,17 @@ export function CacheWrites(props: {
|
|
|
21
21
|
const unit = props.bucketUnit === "6hour" ? "6 hours" : props.bucketUnit;
|
|
22
22
|
return (
|
|
23
23
|
<details className="border-t border-dashboard-border-subtle px-4 py-3 sm:px-5">
|
|
24
|
-
<summary className="cursor-pointer font-
|
|
24
|
+
<summary className="cursor-pointer font-sans tabular-nums text-xs text-dashboard-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus">
|
|
25
25
|
Largest cache writes
|
|
26
26
|
</summary>
|
|
27
|
-
<p className="mb-2 mt-3 font-
|
|
27
|
+
<p className="mb-2 mt-3 font-sans tabular-nums text-xs leading-relaxed text-dashboard-text-muted">
|
|
28
28
|
Top three by tokens written per {unit}, within the selected time window.
|
|
29
29
|
Large writes alone do not mean a cache problem.
|
|
30
30
|
</p>
|
|
31
31
|
<div className="overflow-x-auto">
|
|
32
32
|
<table
|
|
33
33
|
aria-label="Largest cache writes"
|
|
34
|
-
className="w-full min-w-80 table-fixed border-collapse font-
|
|
34
|
+
className="w-full min-w-80 table-fixed border-collapse font-sans text-xs tabular-nums"
|
|
35
35
|
>
|
|
36
36
|
<thead className="text-dashboard-text-muted">
|
|
37
37
|
<tr>
|
|
@@ -4,11 +4,11 @@ import { cn } from "../../styles";
|
|
|
4
4
|
|
|
5
5
|
/** Shared title style for chart headers and period-total values. */
|
|
6
6
|
export const chartHeaderPrimaryClassName =
|
|
7
|
-
"m-0 font-
|
|
7
|
+
"m-0 font-sans text-sm font-medium text-dashboard-text";
|
|
8
8
|
|
|
9
9
|
/** Shared supporting copy style for chart descriptions and total captions. */
|
|
10
10
|
export const chartHeaderSecondaryClassName =
|
|
11
|
-
"mt-1 mb-0 font-
|
|
11
|
+
"mt-1 mb-0 font-sans text-xs leading-relaxed text-dashboard-text-muted";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Card header used by metric charts.
|
|
@@ -26,7 +26,7 @@ export function ChartHeader(props: {
|
|
|
26
26
|
return (
|
|
27
27
|
<div
|
|
28
28
|
className={cn(
|
|
29
|
-
"flex flex-wrap items-start justify-between gap-3 border-b border-
|
|
29
|
+
"flex flex-wrap items-start justify-between gap-3 border-b border-dashboard-border-subtle px-4 py-4 sm:px-5",
|
|
30
30
|
props.className,
|
|
31
31
|
)}
|
|
32
32
|
>
|
|
@@ -42,7 +42,7 @@ export function ChartHeader(props: {
|
|
|
42
42
|
<div className={chartHeaderSecondaryClassName}>{totalLabel}</div>
|
|
43
43
|
</div>
|
|
44
44
|
) : props.trailing ? (
|
|
45
|
-
<div className="font-
|
|
45
|
+
<div className="font-sans text-xs text-dashboard-text-muted">
|
|
46
46
|
{props.trailing}
|
|
47
47
|
</div>
|
|
48
48
|
) : null}
|
|
@@ -19,7 +19,7 @@ export function ChartLegend(props: {
|
|
|
19
19
|
<ul
|
|
20
20
|
aria-label={props.ariaLabel}
|
|
21
21
|
className={cn(
|
|
22
|
-
"m-0 flex list-none flex-wrap gap-x-4 gap-y-1 p-0 font-
|
|
22
|
+
"m-0 flex list-none flex-wrap gap-x-4 gap-y-1 p-0 font-sans text-2xs text-dashboard-text-muted",
|
|
23
23
|
!props.inline && "mt-4",
|
|
24
24
|
)}
|
|
25
25
|
>
|
|
@@ -69,7 +69,7 @@ export function InputCacheChart(props: {
|
|
|
69
69
|
</div>
|
|
70
70
|
}
|
|
71
71
|
/>
|
|
72
|
-
<dl className="m-0 flex flex-wrap gap-x-5 gap-y-2 px-4 py-3 font-
|
|
72
|
+
<dl className="m-0 flex flex-wrap gap-x-5 gap-y-2 px-4 py-3 font-sans tabular-nums text-xs sm:px-5">
|
|
73
73
|
{CACHE_INPUT_SERIES.map(({ key, label, color }) => (
|
|
74
74
|
<div key={key} className="flex items-center gap-2">
|
|
75
75
|
<dt className="flex items-center gap-1.5 text-dashboard-text-muted">
|
|
@@ -87,11 +87,11 @@ export function InputCacheChart(props: {
|
|
|
87
87
|
))}
|
|
88
88
|
</dl>
|
|
89
89
|
{summary.activePeriods === 0 ? (
|
|
90
|
-
<p className="m-0 px-4 pb-2 font-
|
|
90
|
+
<p className="m-0 px-4 pb-2 font-sans tabular-nums text-xs text-dashboard-text-muted sm:px-5">
|
|
91
91
|
No input data in this time window.
|
|
92
92
|
</p>
|
|
93
93
|
) : summary.incompletePeriods > 0 ? (
|
|
94
|
-
<p className="m-0 px-4 pb-2 font-
|
|
94
|
+
<p className="m-0 px-4 pb-2 font-sans tabular-nums text-xs text-dashboard-text-muted sm:px-5">
|
|
95
95
|
Missing data in {summary.incompletePeriods} of {summary.activePeriods}{" "}
|
|
96
96
|
active time intervals. Percentages are unavailable; Tokens shows the
|
|
97
97
|
reported values.
|
|
@@ -183,8 +183,8 @@ export function InputCacheChart(props: {
|
|
|
183
183
|
</ChartSvg>
|
|
184
184
|
</div>
|
|
185
185
|
<CacheWrites days={props.days} bucketUnit={props.bucketUnit} />
|
|
186
|
-
<details className="border-t border-dashboard-border-subtle px-4 py-3 font-
|
|
187
|
-
<summary className="cursor-pointer text-dashboard-text focus-visible:outline focus-visible:outline-
|
|
186
|
+
<details className="border-t border-dashboard-border-subtle px-4 py-3 font-sans tabular-nums text-xs text-dashboard-text-muted sm:px-5">
|
|
187
|
+
<summary className="cursor-pointer text-dashboard-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus">
|
|
188
188
|
How to read this
|
|
189
189
|
</summary>
|
|
190
190
|
<div className="mt-3 grid gap-2 leading-relaxed">
|
|
@@ -11,10 +11,7 @@ import {
|
|
|
11
11
|
import { ChartHeader } from "./ChartHeader";
|
|
12
12
|
import type { TimeRangeDays } from "../controls/TimeRangeSelector";
|
|
13
13
|
import { Card } from "../layout/Card";
|
|
14
|
-
import {
|
|
15
|
-
formatActivityChartAverage,
|
|
16
|
-
formatCompactNumber,
|
|
17
|
-
} from "../../format";
|
|
14
|
+
import { formatActivityChartAverage, formatCompactNumber } from "../../format";
|
|
18
15
|
import type { WorkspaceUsageDay } from "./workspaceUsage";
|
|
19
16
|
|
|
20
17
|
const USAGE_COLOR = "#a78bfa";
|
|
@@ -59,15 +56,11 @@ export function WorkspaceUsageChart(props: {
|
|
|
59
56
|
return (
|
|
60
57
|
<ActivityChartTooltip
|
|
61
58
|
key={day.date}
|
|
62
|
-
content={
|
|
63
|
-
<ActivityTooltipRows rows={[["usage", day.count]]} />
|
|
64
|
-
}
|
|
59
|
+
content={<ActivityTooltipRows rows={[["usage", day.count]]} />}
|
|
65
60
|
date={day.date}
|
|
66
61
|
summary={`${day.count} switches`}
|
|
67
62
|
>
|
|
68
|
-
<g
|
|
69
|
-
tabIndex={0}
|
|
70
|
-
>
|
|
63
|
+
<g tabIndex={0}>
|
|
71
64
|
<rect
|
|
72
65
|
fill={USAGE_COLOR}
|
|
73
66
|
height={renderedHeight}
|
|
@@ -102,7 +95,7 @@ export function WorkspaceUsageChart(props: {
|
|
|
102
95
|
/>
|
|
103
96
|
</ChartSvg>
|
|
104
97
|
{!hasUsage ? (
|
|
105
|
-
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-
|
|
98
|
+
<div className="pointer-events-none absolute inset-0 grid place-items-center pt-12 font-sans text-xs text-dashboard-text-muted">
|
|
106
99
|
No usage in this period.
|
|
107
100
|
</div>
|
|
108
101
|
) : null}
|
|
@@ -12,12 +12,12 @@ export function DirectorySortSelect(props: {
|
|
|
12
12
|
}) {
|
|
13
13
|
return (
|
|
14
14
|
<label className="grid h-9 min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center overflow-hidden rounded-lg border border-dashboard-border-strong bg-dashboard-fill-faint transition-colors hover:border-dashboard-border-emphasis focus-within:border-amber-500/35 focus-within:ring-1 focus-within:ring-amber-500/15">
|
|
15
|
-
<span className="flex h-full items-center border-r border-dashboard-border-subtle px-2 font-
|
|
15
|
+
<span className="flex h-full items-center border-r border-dashboard-border-subtle px-2 font-sans font-medium text-xs text-dashboard-text-muted">
|
|
16
16
|
Sort
|
|
17
17
|
</span>
|
|
18
18
|
<select
|
|
19
19
|
aria-label={props.ariaLabel}
|
|
20
|
-
className="h-full min-w-0 bg-dashboard-control px-2 font-
|
|
20
|
+
className="h-full min-w-0 bg-dashboard-control px-2 font-sans text-xs text-dashboard-text-muted outline-none [color-scheme:var(--dashboard-color-scheme,dark)]"
|
|
21
21
|
value={props.value}
|
|
22
22
|
onChange={(event) => props.onChange(event.currentTarget.value)}
|
|
23
23
|
>
|
|
@@ -6,11 +6,11 @@ export function DirectoryMetric(props: { label: string; value: ReactNode }) {
|
|
|
6
6
|
<div className="justify-self-end text-right max-md:justify-self-stretch max-md:text-left">
|
|
7
7
|
<div
|
|
8
8
|
aria-hidden="true"
|
|
9
|
-
className="mb-1 hidden font-
|
|
9
|
+
className="mb-1 hidden font-sans font-medium text-xs text-dashboard-text-muted max-md:block"
|
|
10
10
|
>
|
|
11
11
|
{props.label}
|
|
12
12
|
</div>
|
|
13
|
-
<div className="font-display text-xl font-light leading-none text-dashboard-text md:font-
|
|
13
|
+
<div className="font-display text-xl font-light leading-none text-dashboard-text md:font-sans md:tabular-nums md:text-xs md:text-dashboard-text-muted">
|
|
14
14
|
<span className="sr-only">{props.label}: </span>
|
|
15
15
|
{props.value}
|
|
16
16
|
</div>
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
23
|
-
<span className="h-2.5 w-3/5 rounded bg-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
/**
|
|
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
|
-
|
|
117
|
-
|
|
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-
|
|
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-
|
|
82
|
+
"rounded-lg px-3 py-3 font-sans text-sm font-medium no-underline transition-colors",
|
|
83
83
|
isActive
|
|
84
|
-
? "bg-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
30
|
+
"rounded-lg px-3 py-3 pl-6 font-sans text-sm font-medium no-underline transition-colors",
|
|
31
31
|
isActive
|
|
32
|
-
? "bg-
|
|
33
|
-
: cn("hover:bg-
|
|
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="
|
|
41
|
-
<
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
);
|