@sentry/junior-dashboard 0.248.0 → 0.250.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/app.js +2 -2
- package/dist/client.js +40 -38
- package/dist/index.js +2 -2
- package/dist/tailwind.css +1 -1
- package/package.json +3 -3
- package/src/client/App.tsx +19 -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 +17 -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/ConnectionsPage.tsx +207 -0
- 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/ChartFixtures.tsx +265 -21
- 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
|
@@ -1,55 +1,128 @@
|
|
|
1
1
|
# Dashboard UI kit
|
|
2
2
|
|
|
3
3
|
Shared dashboard components live here. Feature pages compose these surfaces
|
|
4
|
-
instead of copying Tailwind blocks.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
4
|
+
instead of copying Tailwind blocks. See `policies/frontend-components.md` for
|
|
5
|
+
the policy. `pnpm dashboard-style:check` enforces the token rules below.
|
|
6
|
+
|
|
7
|
+
## Foundations
|
|
8
|
+
|
|
9
|
+
Tokens live in the `@theme` block of `src/tailwind.css`. Light mode does not
|
|
10
|
+
exist yet. Keep every color in a token so a second theme can redefine them.
|
|
11
|
+
|
|
12
|
+
- **Surfaces.** Use the surface ladder, darkest to lightest: `bg`,
|
|
13
|
+
`bg-elevated` (drawers), `surface-panel` (sidebar, cards, bubbles),
|
|
14
|
+
`surface-raised` (buttons, tooltips), `control`, `surface-hover`,
|
|
15
|
+
`surface-active` (selected rows). Use `fill-*` for translucent fills and
|
|
16
|
+
`overlay-*` for scrims. Do not use `white/*` or `black/*` fills, borders, or
|
|
17
|
+
rings, or hex surface colors. Shadows may use black.
|
|
18
|
+
- **Borders.** Use `border-subtle` for hairlines inside a surface,
|
|
19
|
+
`border` for card edges, `border-strong` for shell edges, and
|
|
20
|
+
`border-emphasis` or `border-interactive` for controls and hover.
|
|
21
|
+
- **Text.** Brightest to dimmest: `text-solid`, `text`, `text-subtle`,
|
|
22
|
+
`text-muted`, `text-faint`.
|
|
23
|
+
- **Accent.** Lavender is the one brand accent. Use `accent` for icons,
|
|
24
|
+
active markers, and tinted fills, such as `bg-dashboard-accent/10`. Use
|
|
25
|
+
`accent-text` for links and accent text. Keep Tailwind palette colors for
|
|
26
|
+
status (info, success, warning, error) and for categorical chart, event, and
|
|
27
|
+
subagent colors.
|
|
28
|
+
- **Focus.** Use `outline-dashboard-focus` for keyboard focus. Do not use other
|
|
29
|
+
colors for focus rings.
|
|
30
|
+
- **Type faces.** `font-sans` is the UI face for prose, controls, navigation,
|
|
31
|
+
labels, and names. `font-display` is for headings, record titles, and large
|
|
32
|
+
metric values. `font-mono` is for machine values: code, ids, paths, file
|
|
33
|
+
names, tool names, and raw payloads. Show counts, times, and prices in
|
|
34
|
+
`font-sans tabular-nums`.
|
|
35
|
+
- **Type scale.** Use the named steps `text-2xs` through `text-4xl`. Keep
|
|
36
|
+
`text-2xs` for compact badges and chart annotations.
|
|
37
|
+
- **Labels.** Section, card, field, and column labels use
|
|
38
|
+
`font-sans text-xs font-medium text-dashboard-text-muted` in sentence case.
|
|
39
|
+
Do not use uppercase or letter spacing for labels.
|
|
40
|
+
- **Radius.** Controls and chips use `rounded-md`. Cards, list rows, and panels
|
|
41
|
+
use `rounded-lg`. The composer, menus, and transcript object cards use
|
|
42
|
+
`rounded-xl`. Avatars and count badges use `rounded-full`.
|
|
43
|
+
|
|
44
|
+
## Shared class helpers
|
|
45
|
+
|
|
46
|
+
`src/client/styles.ts` holds the few class strings that many components share.
|
|
47
|
+
|
|
48
|
+
- `cn` joins class names.
|
|
49
|
+
- `dashboardContainerClass` sets the page width.
|
|
50
|
+
- `dashboardInlineLinkClass` styles a neutral underlined link inside prose.
|
|
51
|
+
- `dashboardInteractiveTextClass` styles muted text that brightens on hover.
|
|
52
|
+
- `dashboardComposerSurfaceClass` styles the composer surface.
|
|
53
|
+
|
|
54
|
+
## Components
|
|
55
|
+
|
|
56
|
+
### Primitives
|
|
57
|
+
|
|
58
|
+
- **Actions.** `Button` (`default`, `primary`, `danger`, and `size="icon"`) and
|
|
59
|
+
`ButtonLink`. Form pages use `Button tone="primary"` for their one main save
|
|
60
|
+
action. Use `ToggleButton` variants (`pill`, `segment`, `text`) for pressed
|
|
61
|
+
states, not one-off styles.
|
|
62
|
+
- **Forms.** `Field` (`default`, `compact`), `TextInput`, `TextArea`, `Select`,
|
|
63
|
+
and `SearchInput`. Use `TextInput size="comfortable"` for larger mobile
|
|
64
|
+
controls. Use `TextArea prose` for instructions, not code. `Field error`
|
|
65
|
+
renders an error at `<htmlFor>-error`. The owning control must set
|
|
66
|
+
`aria-invalid` and `aria-describedby`. `Select` matches native text controls.
|
|
67
|
+
- **Status.** Use `StatusDot` for dense table or list markers. Use `StatusChip`
|
|
68
|
+
when the label must stay visible. `StatusChip` is a small dot and
|
|
69
|
+
sentence-case text, with no border or fill. It is not a control.
|
|
70
|
+
- **Messages.** Use `Notice` for compact transient messages and `FormNotice`
|
|
71
|
+
for wrapping form recovery text.
|
|
72
|
+
- **Overlays.** `Drawer` owns dismissal, focus trapping, focus return, scroll
|
|
73
|
+
locking, and panel layout. Context panels use it, not a separate modal.
|
|
74
|
+
`Tooltip` and `IconButtonTooltip` own hover text.
|
|
75
|
+
- **Identity.** `ActorAvatar`, `ParticipantAvatarStack`, and `ObjectIcon`.
|
|
76
|
+
`ObjectIcon` owns native Ticket, Code change, Automation, Deployment, and
|
|
77
|
+
Item icons. It uses the shared type and status mapping and the original
|
|
78
|
+
Octicons paths. Do not replace it with a provider logo or a status-only icon.
|
|
79
|
+
- **Loading.** Use `Skeleton` for placeholder leaves and `PageContentSkeleton`
|
|
80
|
+
variants for page bodies. Use `PageRouteLoading` when a page keeps its real
|
|
81
|
+
header above a body skeleton. Use a feature-owned loading component for
|
|
82
|
+
distinct geometry, such as conversations.
|
|
83
|
+
|
|
84
|
+
### Navigation
|
|
85
|
+
|
|
86
|
+
The dashboard has three navigation levels. Each has its own look, so the
|
|
87
|
+
levels stay distinct.
|
|
88
|
+
|
|
89
|
+
- **App navigation** in `layout/DashboardHeader` uses pills.
|
|
90
|
+
- **Page links** use `SegmentedNav`, through `layout/SecondaryNavigation`. They
|
|
91
|
+
are underline tabs that sit on the header border. With `fill`, links share
|
|
92
|
+
the row width while their labels fit. Rows that do not fit scroll sideways.
|
|
93
|
+
- **Panel switches** use `SegmentedTabs`, which draws a pill track. Pass the
|
|
94
|
+
selected value, items, change handler, and panel content. It owns tab and
|
|
95
|
+
panel labels and keyboard focus.
|
|
96
|
+
|
|
97
|
+
Keep collection filters in `FilterTabList`.
|
|
98
|
+
|
|
99
|
+
### Patterns
|
|
100
|
+
|
|
101
|
+
- **Layout.** `layout/` holds the shell chrome, page headers, cards, and
|
|
102
|
+
section titles. `DashboardChrome` owns the sticky header block and its one
|
|
103
|
+
bottom border.
|
|
104
|
+
- **Directory tables and metrics.** `directory/` and `metrics/`.
|
|
105
|
+
- **Charts.** Activity time-series charts own bucket labels in
|
|
106
|
+
`charts/ActivityChart` (`formatActivityDate` for axes,
|
|
107
|
+
`ActivityChartTooltip` and `formatActivityTooltipDate` for hover labels). New
|
|
108
|
+
day or hour charts compose those helpers. They do not format UTC bucket keys
|
|
109
|
+
inline.
|
|
110
|
+
- **Conversation mobile geometry** is a hard ownership edge:
|
|
111
|
+
- `layout/VisualViewportShell` owns the fixed shell, visualViewport CSS
|
|
112
|
+
variables, and body lock.
|
|
113
|
+
- `conversations/ChatLayout` owns the scroll-above-dock frame for reply
|
|
114
|
+
threads.
|
|
115
|
+
- `conversations/ComposerDock` owns the reply bottom pad and dock chrome.
|
|
116
|
+
|
|
117
|
+
Pages pass children only. Home and create are one landing surface, not a
|
|
118
|
+
dock, and must not use `ComposerDock`. Do not add viewport height, offset, or
|
|
119
|
+
bottom safe-area math in page modules.
|
|
120
|
+
|
|
121
|
+
### Features
|
|
122
|
+
|
|
123
|
+
Feature surfaces stay under `conversations/` or `pages/` until two real callers
|
|
124
|
+
share them. Transcript layout stays in `conversations/`. See
|
|
125
|
+
[Transcript components](../conversations/README.md) for ownership.
|
|
53
126
|
|
|
54
127
|
## Rules
|
|
55
128
|
|
|
@@ -58,12 +131,3 @@ instead of copying Tailwind blocks.
|
|
|
58
131
|
- Add gallery fixtures under `/dev/<section>` for new or changed shared
|
|
59
132
|
components. Keep the `/dev` index as the catalog only.
|
|
60
133
|
- One-off page glue may stay inline when extraction only adds indirection.
|
|
61
|
-
|
|
62
|
-
See `policies/frontend-components.md`.
|
|
63
|
-
|
|
64
|
-
Use `TextInput size="comfortable"` for larger mobile form controls.
|
|
65
|
-
Form pages use `Button tone="primary"` for their one main save action. Use
|
|
66
|
-
`TextArea prose` for instructions rather than code. `Field error` renders an
|
|
67
|
-
error at `<htmlFor>-error`; the owning control must set `aria-invalid` and
|
|
68
|
-
`aria-describedby`. `Select` matches native text controls. Use `FormNotice` for
|
|
69
|
-
wrapping form recovery text; use `Notice` for compact transient messages.
|
|
@@ -30,7 +30,7 @@ export function SearchInput(props: {
|
|
|
30
30
|
<input
|
|
31
31
|
aria-label={props.label}
|
|
32
32
|
className={cn(
|
|
33
|
-
"w-full rounded-
|
|
33
|
+
"w-full rounded-md border border-dashboard-border font-sans text-sm text-dashboard-text outline-none transition-colors placeholder:text-dashboard-text-muted/70 hover:border-dashboard-border-emphasis focus:border-dashboard-focus",
|
|
34
34
|
searchInputClass[size],
|
|
35
35
|
)}
|
|
36
36
|
onChange={(event) => props.onChange(event.currentTarget.value)}
|
|
@@ -49,7 +49,8 @@ const searchIconClass: Record<SearchInputSize, string> = {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
const searchInputClass: Record<SearchInputSize, string> = {
|
|
52
|
-
compact:
|
|
52
|
+
compact:
|
|
53
|
+
"h-9 bg-dashboard-overlay-soft pr-3 pl-8 focus:ring-1 focus:ring-dashboard-focus/25",
|
|
53
54
|
default:
|
|
54
|
-
"h-10 bg-dashboard-overlay-soft pr-3 pl-9 focus:ring-
|
|
55
|
+
"h-10 bg-dashboard-overlay-soft pr-3 pl-9 focus:ring-1 focus:ring-dashboard-focus/25",
|
|
55
56
|
};
|
|
@@ -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>
|