2026-03-05 20:47:12 +00:00
|
|
|
/* Layout */
|
|
|
|
|
.app-layout {
|
|
|
|
|
display: flex;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
min-height: 100dvh;
|
|
|
|
|
background-color: var(--color-bg-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.main-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
margin-left: var(--sidebar-width);
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
min-height: 100dvh;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
transition: margin-left var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 06:30:49 +00:00
|
|
|
.sidebar-is-collapsed .main-content {
|
|
|
|
|
margin-left: var(--sidebar-width-collapsed);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.main-content-inner {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-layout-chat {
|
|
|
|
|
height: 100vh;
|
|
|
|
|
height: 100dvh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-layout-chat .main-content {
|
|
|
|
|
height: 100vh;
|
|
|
|
|
height: 100dvh;
|
|
|
|
|
min-height: 0;
|
2026-03-09 22:42:47 +00:00
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-layout-chat .main-content-inner {
|
|
|
|
|
overflow: hidden;
|
2026-03-09 22:42:47 +00:00
|
|
|
min-width: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Footer */
|
|
|
|
|
.app-footer {
|
2026-04-14 21:53:03 +00:00
|
|
|
background: transparent;
|
|
|
|
|
border-top: 1px solid var(--color-border-divider);
|
2026-03-05 20:47:12 +00:00
|
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
|
|
|
margin-top: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-inner {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-version {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-version a {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
transition: color 150ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-version a:hover {
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-links {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-links a {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
2026-03-05 20:47:12 +00:00
|
|
|
text-decoration: none;
|
|
|
|
|
transition: color 150ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-links a:hover {
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-copyright {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-copyright a {
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-muted);
|
2026-03-05 20:47:12 +00:00
|
|
|
text-decoration: none;
|
|
|
|
|
transition: color 150ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-footer-copyright a:hover {
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Mobile header */
|
|
|
|
|
.mobile-header {
|
|
|
|
|
display: none;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hamburger-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mobile-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sidebar */
|
|
|
|
|
.sidebar {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
width: var(--sidebar-width);
|
|
|
|
|
height: 100vh;
|
|
|
|
|
height: 100dvh;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-bg-secondary);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-right: 1px solid var(--color-border-subtle);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
z-index: 50;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
box-shadow: var(--shadow-sidebar);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: width var(--duration-normal) var(--ease-spring),
|
|
|
|
|
transform var(--duration-normal) var(--ease-spring);
|
2026-03-19 20:40:51 +00:00
|
|
|
will-change: transform;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-overlay {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
2026-03-11 06:30:49 +00:00
|
|
|
padding: var(--spacing-sm) var(--spacing-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
2026-03-11 06:30:49 +00:00
|
|
|
min-height: 44px;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-logo-link {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-logo-img {
|
|
|
|
|
width: 100%;
|
2026-03-11 06:30:49 +00:00
|
|
|
max-width: 120px;
|
2026-03-05 20:47:12 +00:00
|
|
|
height: auto;
|
|
|
|
|
padding: 0 var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 06:30:49 +00:00
|
|
|
.sidebar-logo-icon {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-logo-icon-img {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.sidebar-close-btn {
|
|
|
|
|
display: none;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-nav {
|
|
|
|
|
flex: 1;
|
2026-03-11 06:30:49 +00:00
|
|
|
padding: 2px 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-section {
|
2026-03-11 06:30:49 +00:00
|
|
|
padding: 2px 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-section-title {
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-xs);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
2026-03-05 20:47:12 +00:00
|
|
|
text-transform: uppercase;
|
2026-04-14 21:53:03 +00:00
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
color: var(--color-text-secondary);
|
2026-03-11 06:30:49 +00:00
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:08:02 +00:00
|
|
|
.sidebar-section-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
width: 100%;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
transition: color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-section-toggle:hover {
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-03-21 01:08:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-section-chevron {
|
|
|
|
|
font-size: 0.5rem;
|
2026-04-14 21:53:03 +00:00
|
|
|
opacity: 0.6;
|
|
|
|
|
transition: transform var(--duration-fast), opacity var(--duration-fast);
|
2026-03-21 01:08:02 +00:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 21:53:03 +00:00
|
|
|
.sidebar-section-toggle:hover .sidebar-section-chevron {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:08:02 +00:00
|
|
|
.sidebar-section-toggle.open .sidebar-section-chevron {
|
|
|
|
|
transform: rotate(90deg);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.nav-item {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
position: relative;
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
padding: 8px var(--spacing-md) 8px calc(var(--spacing-sm) + 2px);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
text-decoration: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
background var(--duration-normal) var(--ease-spring),
|
|
|
|
|
box-shadow var(--duration-normal) var(--ease-spring);
|
2026-03-11 06:30:49 +00:00
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 21:53:03 +00:00
|
|
|
.nav-item:hover:not(.active) {
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-primary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-elevated);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-item.active {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
background: var(--color-primary-light);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: inset 2px 0 0 var(--color-primary);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-icon {
|
2026-03-11 06:30:49 +00:00
|
|
|
width: 18px;
|
2026-03-05 20:47:12 +00:00
|
|
|
text-align: center;
|
2026-03-11 06:30:49 +00:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
font-size: 0.85rem;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-label {
|
|
|
|
|
flex: 1;
|
2026-03-11 06:30:49 +00:00
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
2026-03-19 20:40:51 +00:00
|
|
|
transition: opacity 150ms ease;
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-external {
|
|
|
|
|
font-size: 0.55rem;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
flex-shrink: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-footer {
|
2026-03-11 06:30:49 +00:00
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-top: 1px solid var(--color-border-subtle);
|
2026-03-11 06:30:49 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-xs);
|
2026-03-19 20:40:51 +00:00
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: var(--spacing-xs) 0;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user-avatar {
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user-avatar-icon {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user-link {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 2px var(--spacing-xs);
|
|
|
|
|
margin: -2px calc(-1 * var(--spacing-xs));
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
color: inherit;
|
|
|
|
|
font: inherit;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background var(--duration-fast), color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user-link:hover {
|
|
|
|
|
background: var(--color-bg-hover);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-user-name {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-logout-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-logout-btn:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-user {
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-user-link {
|
|
|
|
|
flex: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-user-name,
|
|
|
|
|
.sidebar.collapsed .sidebar-logout-btn {
|
|
|
|
|
display: none;
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-collapse-btn {
|
2026-04-14 21:53:03 +00:00
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 32px;
|
|
|
|
|
height: 32px;
|
2026-03-11 06:30:49 +00:00
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
transition: color var(--duration-fast), background var(--duration-fast);
|
2026-03-11 06:30:49 +00:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-collapse-btn:hover {
|
|
|
|
|
color: var(--color-text-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-hover);
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Collapsed sidebar (desktop only) */
|
|
|
|
|
.sidebar.collapsed {
|
|
|
|
|
width: var(--sidebar-width-collapsed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-logo-link {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-logo-icon {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-header {
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .nav-label,
|
|
|
|
|
.sidebar.collapsed .nav-external,
|
|
|
|
|
.sidebar.collapsed .sidebar-section-title {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:08:02 +00:00
|
|
|
.sidebar.collapsed .sidebar-section-chevron {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 06:30:49 +00:00
|
|
|
.sidebar.collapsed .nav-item {
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
border-left-width: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .nav-icon {
|
|
|
|
|
width: auto;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-footer {
|
|
|
|
|
justify-content: center;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .theme-toggle {
|
|
|
|
|
padding: 4px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .theme-toggle .nav-label {
|
|
|
|
|
display: none;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Theme toggle */
|
|
|
|
|
.theme-toggle {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
transition: all var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.theme-toggle:hover {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Operations bar */
|
|
|
|
|
.operations-bar {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
.operation-text {
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
}
|
|
|
|
|
.operation-progress {
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.operation-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-md);
|
2026-03-05 20:47:12 +00:00
|
|
|
padding: var(--spacing-xs) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
2026-04-14 21:53:03 +00:00
|
|
|
flex: 2 1 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-info > .operation-text {
|
|
|
|
|
flex: 1 1 auto;
|
2026-03-12 17:19:06 +00:00
|
|
|
min-width: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-spinner {
|
2026-04-14 21:53:03 +00:00
|
|
|
width: 16px;
|
|
|
|
|
height: 16px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
border: 2px solid var(--color-border-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-top-color: var(--color-primary);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: spin 0.8s linear infinite;
|
2026-04-14 21:53:03 +00:00
|
|
|
display: inline-block;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-text {
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
2026-03-12 17:19:06 +00:00
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-progress {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-bar-container {
|
2026-04-14 21:53:03 +00:00
|
|
|
flex: 0 1 160px;
|
|
|
|
|
min-width: 80px;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
height: 3px;
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border-radius: var(--radius-full);
|
2026-03-05 20:47:12 +00:00
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-bar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-full);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: width var(--duration-slow) var(--ease-spring);
|
|
|
|
|
animation: opsBarBreathe 1.4s ease-in-out infinite;
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline install indicator — used in table rows (Models, Backends) */
|
|
|
|
|
.inline-install {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.inline-install__row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.inline-install__label {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
white-space: nowrap;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.operation-cancel {
|
2026-03-12 17:19:06 +00:00
|
|
|
flex-shrink: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
2026-03-12 17:19:06 +00:00
|
|
|
padding: 4px 6px;
|
2026-03-05 20:47:12 +00:00
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
.operation-cancel:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Toast */
|
|
|
|
|
.toast-container {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: var(--spacing-lg);
|
|
|
|
|
right: var(--spacing-lg);
|
2026-03-19 20:40:51 +00:00
|
|
|
z-index: 1100;
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toast {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: var(--radius-lg);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
letter-spacing: -0.005em;
|
|
|
|
|
animation: toastSlideIn var(--duration-normal) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
min-width: 280px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toast-enter {
|
|
|
|
|
opacity: 0;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transform: translateX(12px);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
.toast-exit {
|
|
|
|
|
opacity: 0;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transform: translateX(12px);
|
|
|
|
|
transition: opacity var(--duration-fast) var(--ease-spring),
|
|
|
|
|
transform var(--duration-fast) var(--ease-spring);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.toast-success {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: inset 3px 0 0 var(--color-success), var(--shadow-sm);
|
|
|
|
|
border-color: var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
.toast-error {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: inset 3px 0 0 var(--color-error), var(--shadow-sm);
|
|
|
|
|
border-color: var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
.toast-warning {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: inset 3px 0 0 var(--color-warning), var(--shadow-sm);
|
|
|
|
|
border-color: var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
.toast-info {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: inset 3px 0 0 var(--color-info), var(--shadow-sm);
|
|
|
|
|
border-color: var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toast-close {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: inherit;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
}
|
|
|
|
|
.toast-close:hover { opacity: 1; }
|
2026-03-20 14:06:07 +00:00
|
|
|
.toast-link {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: inherit;
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
margin-left: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.toast-link:hover { opacity: 1; }
|
|
|
|
|
|
|
|
|
|
/* Chat error trace link */
|
|
|
|
|
.chat-error-trace-link {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
margin-top: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.chat-error-trace-link:hover {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
|
|
|
|
|
/* Spinner */
|
|
|
|
|
.spinner {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.spinner-ring {
|
|
|
|
|
border: 3px solid var(--color-border-subtle);
|
|
|
|
|
border-top-color: var(--color-primary);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: spin 0.8s linear infinite;
|
|
|
|
|
}
|
|
|
|
|
.spinner-sm .spinner-ring { width: 16px; height: 16px; }
|
|
|
|
|
.spinner-md .spinner-ring { width: 24px; height: 24px; }
|
|
|
|
|
.spinner-lg .spinner-ring { width: 40px; height: 40px; }
|
|
|
|
|
|
|
|
|
|
/* Model selector */
|
|
|
|
|
.model-selector {
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
outline: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: border-color var(--duration-fast);
|
|
|
|
|
min-width: 180px;
|
|
|
|
|
}
|
|
|
|
|
.model-selector:focus {
|
|
|
|
|
border-color: var(--color-border-strong);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Resource monitor */
|
|
|
|
|
.resource-monitor {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-monitor-title {
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin-bottom: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-card {
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-name {
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-vendor {
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
background: var(--color-accent-light);
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-gpu-stats {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin-top: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-bar-container {
|
|
|
|
|
height: 4px;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-bar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
transition: width 500ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-bar-ram {
|
|
|
|
|
background: var(--color-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-no-gpu {
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-ram {
|
|
|
|
|
margin-top: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-ram-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-monitor-compact {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.resource-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Common page styles */
|
|
|
|
|
.page {
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-2xl);
|
2026-03-05 20:47:12 +00:00
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
width: 100%;
|
2026-03-19 20:40:51 +00:00
|
|
|
animation: fadeIn var(--duration-normal) var(--ease-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-header {
|
|
|
|
|
margin-bottom: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-title {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-2xl);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
letter-spacing: -0.015em;
|
|
|
|
|
line-height: var(--leading-tight);
|
2026-03-05 20:47:12 +00:00
|
|
|
margin-bottom: var(--spacing-xs);
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-subtitle {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-04-14 21:53:03 +00:00
|
|
|
line-height: var(--leading-normal);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cards */
|
|
|
|
|
.card {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-bg-secondary);
|
2026-03-05 20:47:12 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
box-shadow: var(--shadow-subtle), var(--shadow-inset-top);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: border-color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
box-shadow var(--duration-normal) var(--ease-spring),
|
|
|
|
|
transform var(--duration-normal) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card:hover {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
border-color: var(--color-border-strong);
|
|
|
|
|
box-shadow: var(--shadow-sm), var(--shadow-inset-top);
|
2026-03-19 20:40:51 +00:00
|
|
|
transform: translateY(-1px);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
/* Accent-rail variant — editorial left bar for highlighted cards */
|
|
|
|
|
.card--accent {
|
|
|
|
|
box-shadow: inset 2px 0 0 var(--color-primary), var(--shadow-subtle), var(--shadow-inset-top);
|
|
|
|
|
}
|
|
|
|
|
.card--accent:hover {
|
|
|
|
|
box-shadow: inset 2px 0 0 var(--color-primary), var(--shadow-sm), var(--shadow-inset-top);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.card-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Form rows — consistent label+control rhythm */
|
|
|
|
|
.form-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
padding: var(--spacing-md) 0;
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
}
|
|
|
|
|
.form-row:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
.form-row__label {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
.form-row__label-text {
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.form-row__hint {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
line-height: var(--leading-snug);
|
|
|
|
|
}
|
|
|
|
|
.form-row__control {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-group__title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
padding: var(--spacing-md) 0 var(--spacing-sm);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.form-group__title i {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.form-group__body {
|
|
|
|
|
padding-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.form-group__body:last-child {
|
|
|
|
|
padding-bottom: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.form-group__actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-md) 0 var(--spacing-lg);
|
|
|
|
|
border-top: 1px solid var(--color-border-divider);
|
|
|
|
|
margin-top: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Form layout grids */
|
|
|
|
|
.form-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.form-grid-2col {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.form-grid-3col {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
.form-grid-2col, .form-grid-3col {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-field {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.form-field__label {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
.form-field__hint {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
line-height: var(--leading-snug);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Button modifiers */
|
|
|
|
|
.btn-full {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Progress bar */
|
|
|
|
|
.progress-bar {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 24px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
.progress-bar__fill {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
transition: width 300ms var(--ease-default);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
padding: 0 var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.progress-bar__fill--error {
|
|
|
|
|
background: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Log tail viewport */
|
|
|
|
|
.log-tail {
|
|
|
|
|
max-height: 180px;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
line-height: var(--leading-snug);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.log-tail__line {
|
|
|
|
|
padding: 1px 0;
|
|
|
|
|
}
|
|
|
|
|
.log-tail__line--error {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Result quote (TTS / Sound prompt echo) */
|
|
|
|
|
.result-quote {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
text-align: center;
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Data table — used by Quantize jobs list, Traces, etc. */
|
|
|
|
|
.data-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.data-table th {
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-default);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
}
|
|
|
|
|
.data-table td {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.data-table tbody tr {
|
|
|
|
|
transition: background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.data-table tbody tr:hover {
|
|
|
|
|
background: var(--color-surface-hover);
|
|
|
|
|
}
|
|
|
|
|
.data-table tbody tr.is-selected {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
.data-table tbody tr:last-child td {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
.data-table__actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
.data-table__truncate {
|
|
|
|
|
max-width: 280px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Quantize page */
|
|
|
|
|
.quantize-page__header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.quantize-form {
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.quantize-form__quant-row {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.quantize-form__quant-row > :only-child {
|
|
|
|
|
grid-column: 1 / -1;
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 520px) {
|
|
|
|
|
.quantize-form__quant-row {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card {
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card__header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card__title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card__title i {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card__status {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card__message {
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-progress-card .progress-bar {
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.quantize-import-card {
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.quantize-import-card__title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin: 0 0 var(--spacing-md);
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-import-card__title i {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-import-card__row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.quantize-import-card__name {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 220px;
|
|
|
|
|
max-width: 320px;
|
|
|
|
|
}
|
|
|
|
|
.quantize-jobs {
|
|
|
|
|
padding: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.quantize-jobs__title {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-jobs__title i {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.quantize-jobs__scroll {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Segmented control (Sound mode toggle etc.) */
|
|
|
|
|
.segmented {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
padding: 3px;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
.segmented__item {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: 6px var(--spacing-md);
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background var(--duration-fast), color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.segmented__item:hover {
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.segmented__item.is-active {
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
box-shadow: var(--shadow-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline checkbox row */
|
|
|
|
|
.checkbox-row {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: 10px var(--spacing-md);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
transition: border-color var(--duration-fast);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.checkbox-row:hover {
|
|
|
|
|
border-color: var(--color-border-default);
|
|
|
|
|
}
|
|
|
|
|
.checkbox-row input[type="checkbox"] {
|
|
|
|
|
accent-color: var(--color-primary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Audio result wrapper (TTS/Sound) */
|
|
|
|
|
.audio-result {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: stretch;
|
2026-03-05 20:47:12 +00:00
|
|
|
gap: var(--spacing-md);
|
2026-04-14 21:53:03 +00:00
|
|
|
width: 100%;
|
|
|
|
|
max-width: 480px;
|
|
|
|
|
}
|
|
|
|
|
.audio-result__player {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
.audio-result__actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Media empty state */
|
|
|
|
|
.media-empty {
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
padding: var(--spacing-xl) var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.media-empty__icon {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 2.75rem;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
opacity: 0.35;
|
|
|
|
|
}
|
|
|
|
|
.media-empty p {
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
margin: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Buttons */
|
|
|
|
|
.btn {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
justify-content: center;
|
2026-03-05 20:47:12 +00:00
|
|
|
gap: var(--spacing-xs);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 0.5rem var(--spacing-md);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
min-height: 34px;
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: var(--radius-md);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-family: inherit;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-medium);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: -0.005em;
|
2026-03-05 20:47:12 +00:00
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
border: 1px solid transparent;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: background var(--duration-normal) var(--ease-spring),
|
|
|
|
|
color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
border-color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
box-shadow var(--duration-normal) var(--ease-spring),
|
|
|
|
|
filter var(--duration-normal) var(--ease-spring),
|
|
|
|
|
transform var(--duration-normal) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
text-decoration: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn:focus-visible {
|
|
|
|
|
outline: none;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Global focus ring — any interactive that isn't a .btn */
|
|
|
|
|
:where(a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus-visible {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
outline: none;
|
|
|
|
|
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: var(--color-primary-text);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25), var(--shadow-inset-hi);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.btn-primary:hover:not(:disabled) {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
filter: brightness(1.06);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), var(--shadow-inset-hi);
|
|
|
|
|
}
|
|
|
|
|
.btn-primary:focus-visible:not(:disabled) {
|
|
|
|
|
box-shadow: 0 0 0 3px var(--color-focus-ring), var(--shadow-inset-hi);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-secondary {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-elevated);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-color: var(--color-border-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.btn-secondary:hover:not(:disabled) {
|
2026-03-05 20:47:12 +00:00
|
|
|
border-color: var(--color-border-strong);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-hover);
|
|
|
|
|
transform: translateY(-1px);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-ghost {
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
.btn-ghost:hover:not(:disabled) {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-elevated);
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-danger {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
color: var(--color-error);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-color: var(--color-error-border);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.btn-danger:hover:not(:disabled) {
|
|
|
|
|
background: var(--color-error);
|
|
|
|
|
color: var(--color-text-inverse);
|
|
|
|
|
border-color: var(--color-error);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
filter: brightness(1.04);
|
|
|
|
|
transform: translateY(-1px);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-sm {
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 0.35rem var(--spacing-sm);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
min-height: 28px;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
.btn:active:not(:disabled) {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
filter: brightness(0.95);
|
|
|
|
|
transform: translateY(0);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.btn:disabled {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
opacity: 0.45;
|
2026-03-05 20:47:12 +00:00
|
|
|
cursor: not-allowed;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
filter: none;
|
|
|
|
|
transform: none;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 21:53:03 +00:00
|
|
|
/* Toggle switch */
|
|
|
|
|
.toggle {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 38px;
|
|
|
|
|
height: 22px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.toggle input {
|
|
|
|
|
position: absolute;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
width: 0;
|
|
|
|
|
height: 0;
|
|
|
|
|
}
|
|
|
|
|
.toggle__track {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
background: var(--color-toggle-off);
|
|
|
|
|
transition: background var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.toggle__thumb {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 2px;
|
|
|
|
|
left: 2px;
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
transition: transform var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.toggle--on .toggle__track {
|
|
|
|
|
background: var(--color-toggle-on);
|
|
|
|
|
}
|
|
|
|
|
.toggle--on .toggle__thumb {
|
|
|
|
|
transform: translateX(16px);
|
|
|
|
|
}
|
|
|
|
|
.toggle:hover:not(.toggle--disabled) .toggle__track {
|
|
|
|
|
filter: brightness(1.08);
|
|
|
|
|
}
|
|
|
|
|
.toggle:focus-within .toggle__track {
|
|
|
|
|
box-shadow: 0 0 0 3px var(--color-border-focus);
|
|
|
|
|
}
|
|
|
|
|
.toggle--disabled {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
/* Inputs — sunken well, quiet border, sage focus ring */
|
2026-03-05 20:47:12 +00:00
|
|
|
.input {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-sunken);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
font-size: var(--text-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-family: inherit;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: -0.005em;
|
2026-03-05 20:47:12 +00:00
|
|
|
outline: none;
|
|
|
|
|
width: 100%;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: border-color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
box-shadow var(--duration-normal) var(--ease-spring),
|
|
|
|
|
background var(--duration-normal) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.input::placeholder {
|
|
|
|
|
color: var(--color-text-muted);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
opacity: 0.8;
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.input:hover:not(:disabled):not(:focus) {
|
2026-03-05 20:47:12 +00:00
|
|
|
border-color: var(--color-border-strong);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.input:focus {
|
|
|
|
|
border-color: var(--color-primary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
|
|
|
|
background: var(--color-surface-sunken);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.input:disabled {
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: not-allowed;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
opacity: 0.7;
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
select.input {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding-right: var(--spacing-xl);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
.input-mono {
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
letter-spacing: -0.01em;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.textarea {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-sunken);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
font-size: var(--text-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-family: inherit;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: -0.005em;
|
2026-03-05 20:47:12 +00:00
|
|
|
outline: none;
|
|
|
|
|
width: 100%;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
min-height: 80px;
|
2026-04-14 21:53:03 +00:00
|
|
|
line-height: var(--leading-normal);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
transition: border-color var(--duration-normal) var(--ease-spring),
|
|
|
|
|
box-shadow var(--duration-normal) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.textarea::placeholder {
|
|
|
|
|
color: var(--color-text-muted);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
opacity: 0.8;
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.textarea:hover:not(:disabled):not(:focus) {
|
2026-03-05 20:47:12 +00:00
|
|
|
border-color: var(--color-border-strong);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.textarea:focus {
|
|
|
|
|
border-color: var(--color-primary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
2026-04-14 21:53:03 +00:00
|
|
|
}
|
|
|
|
|
.textarea:disabled {
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: not-allowed;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
opacity: 0.7;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-07 12:42:23 +00:00
|
|
|
/* CodeMirror editor wrapper */
|
|
|
|
|
.code-editor-cm .cm-editor {
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-04-07 12:42:23 +00:00
|
|
|
.code-editor-cm .cm-editor.cm-focused {
|
2026-03-05 20:47:12 +00:00
|
|
|
border-color: var(--color-border-strong);
|
2026-04-07 12:42:23 +00:00
|
|
|
outline: none;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Form groups */
|
|
|
|
|
.form-group {
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-label {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
/* Badges — sharp editorial rectangles, mono caps */
|
2026-03-05 20:47:12 +00:00
|
|
|
.badge {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 2px 8px;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
font-size: 0.625rem;
|
2026-03-05 20:47:12 +00:00
|
|
|
font-weight: 500;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.badge-success {
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
}
|
|
|
|
|
.badge-error {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
.badge-info {
|
|
|
|
|
background: var(--color-info-light);
|
|
|
|
|
color: var(--color-info);
|
|
|
|
|
}
|
|
|
|
|
.badge-warning {
|
|
|
|
|
background: var(--color-warning-light);
|
|
|
|
|
color: var(--color-warning);
|
|
|
|
|
}
|
feat(distributed): sync state with frontends, better backend management reporting (#9426)
* fix(distributed): detect backend upgrades across worker nodes
Before this change `DistributedBackendManager.CheckUpgrades` delegated to the
local manager, which read backends from the frontend filesystem. In
distributed deployments the frontend has no backends installed locally —
they live on workers — so the upgrade-detection loop never ran and the UI
silently never surfaced upgrades even when the gallery advertised newer
versions or digests.
Worker-side: NATS backend.list reply now carries Version, URI and Digest
for each installed backend (read from metadata.json).
Frontend-side: DistributedBackendManager.ListBackends aggregates per-node
refs (name, status, version, digest) instead of deduping, and CheckUpgrades
feeds that aggregation into gallery.CheckUpgradesAgainst — a new entrypoint
factored out of CheckBackendUpgrades so both paths share the same core
logic.
Cluster drift policy: when per-node version/digest tuples disagree, the
backend is flagged upgradeable regardless of whether any single node
matches the gallery, and UpgradeInfo.NodeDrift enumerates the outliers so
operators can see *why* it is out of sync. The next upgrade-all realigns
the cluster.
Tests cover: drift detection, unanimous-match (no upgrade), and the
empty-installed-version path that the old distributed code silently
missed.
* feat(ui): surface backend upgrades in the System page
The System page (Manage.jsx) only showed updates as a tiny inline arrow,
so operators routinely missed them. Port the Backend Gallery's upgrade UX
so System speaks the same visual language:
- Yellow banner at the top of the Backends tab when upgrades are pending,
with an "Upgrade all" button (serial fan-out, matches the gallery) and a
"Updates only" filter toggle.
- Warning pill (↑ N) next to the tab label so the count is glanceable even
when the banner is scrolled out of view.
- Per-row labeled "Upgrade to vX.Y" button (replaces the icon-only button
that silently flipped semantics between Reinstall and Upgrade), plus an
"Update available" badge in the new Version column.
- New columns: Version (with upgrade + drift chips), Nodes (per-node
attribution badges for distributed mode, degrading to a compact
"on N nodes · M offline" chip above three nodes), Installed (relative
time).
- System backends render a "Protected" chip instead of a bare "—" so rows
still align and the reason is obvious.
- Delete uses the softer btn-danger-ghost so rows don't scream red; the
ConfirmDialog still owns the "are you sure".
The upgrade checker also needed the same per-worker fix as the previous
commit: NewUpgradeChecker now takes a BackendManager getter so its
periodic runs call the distributed CheckUpgrades (which asks workers)
instead of the empty frontend filesystem. Without this the /api/backends/
upgrades endpoint stayed empty in distributed mode even with the protocol
change in place.
New CSS primitives — .upgrade-banner, .tab-pill, .badge-row, .cell-stack,
.cell-mono, .cell-muted, .row-actions, .btn-danger-ghost — all live in
App.css so other pages can adopt them without duplicating styles.
* feat(ui): polish the Nodes page so it reads like a product
The Nodes page was the biggest visual liability in distributed mode.
Rework the main dashboard surfaces in place without changing behavior:
StatCards: uniform height (96px min), left accent bar colored by the
metric's semantic (success/warning/error/primary), icon lives in a
36x36 soft-tinted chip top-right, value is left-aligned and large.
Grid auto-fills so the row doesn't collapse on narrow viewports. This
replaces the previous thin-bordered boxes with inconsistent heights.
Table rows: expandable rows now show a chevron cue on the left (rotates
on expand) so users know rows open. Status cell became a dedicated chip
with an LED-style halo dot instead of a bare bullet. Action buttons gained
labels — "Approve", "Resume", "Drain" — so the icons aren't doing all
the semantic work; the destructive remove action uses the softer
btn-danger-ghost variant so rows don't scream red, with the ConfirmDialog
still owning the real "are you sure". Applied cell-mono/cell-muted
utility classes so label chips and addresses share one spacing/font
grammar instead of re-declaring inline styles everywhere.
Expanded drawer: empty states for Loaded Models and Installed Backends
now render as a proper drawer-empty card (dashed border, icon, one-line
hint) instead of a plain muted string that read like broken formatting.
Tabs: three inline-styled buttons became the shared .tab class so they
inherit focus ring, hover state, and the rest of the design system —
matches the System page.
"Add more workers" toggle turned into a .nodes-add-worker dashed-border
button labelled "Register a new worker" (action voice) instead of a
chevron + muted link that operators kept mistaking for broken text.
New shared CSS primitives carry over to other pages:
.stat-grid + .stat-card, .row-chevron, .node-status, .drawer-empty,
.nodes-add-worker.
* feat(distributed): durable backend fan-out + state reconciliation
Two connected problems handled together:
1) Backend delete/install/upgrade used to silently skip non-healthy nodes,
so a delete during an outage left a zombie on the offline node once it
returned. The fan-out now records intent in a new pending_backend_ops
table before attempting the NATS round-trip. Currently-healthy nodes
get an immediate attempt; everyone else is queued. Unique index on
(node_id, backend, op) means reissuing the same operation refreshes
next_retry_at instead of stacking duplicates.
2) Loaded-model state could drift from reality: a worker OOM'd, got
killed, or restarted a backend process would leave a node_models row
claiming the model was still loaded, feeding ghost entries into the
/api/nodes/models listing and the router's scheduling decisions.
The existing ReplicaReconciler gains two new passes that run under a
fresh KeyStateReconciler advisory lock (non-blocking, so one wedged
frontend doesn't freeze the cluster):
- drainPendingBackendOps: retries queued ops whose next_retry_at has
passed on currently-healthy nodes. Success deletes the row; failure
bumps attempts and pushes next_retry_at out with exponential backoff
(30s → 15m cap). ErrNoResponders also marks the node unhealthy.
- probeLoadedModels: gRPC-HealthChecks addresses the DB thinks are
loaded but hasn't seen touched in the last probeStaleAfter (2m).
Unreachable addresses are removed from the registry. A pluggable
ModelProber lets tests substitute a fake without standing up gRPC.
DistributedBackendManager exposes DeleteBackendDetailed so the HTTP
handler can surface per-node outcomes ("2 succeeded, 1 queued") to the
UI in a follow-up commit; the existing DeleteBackend still returns
error-only for callers that don't care about node breakdown.
Multi-frontend safety: the state pass uses advisorylock.TryWithLockCtx
on a new key so N frontends coordinate — the same pattern the health
monitor and replica reconciler already rely on. Single-node mode runs
both passes inline (adapter is nil, state drain is a no-op).
Tests cover the upsert semantics, backoff math, the probe removing an
unreachable model but keeping a reachable one, and filtering by
probeStaleAfter.
* feat(ui): show cluster distribution of models in the System page
When a frontend restarted in distributed mode, models that workers had
already loaded weren't visible until the operator clicked into each node
manually — the /api/models/capabilities endpoint only knew about
configs on the frontend's filesystem, not the registry-backed truth.
/api/models/capabilities now joins in ListAllLoadedModels() when the
registry is active, returning loaded_on[] with node id/name/state/status
for each model. Models that live in the registry but lack a local config
(the actual ghosts, not recovered from the frontend's file cache) still
surface with source="registry-only" so operators can see and persist
them; without that emission they'd be invisible to this frontend.
Manage → Models replaces the old Running/Idle pill with a distribution
cell that lists the first three nodes the model is loaded on as chips
colored by state (green loaded, blue loading, amber anything else). On
wider clusters the remaining count collapses into a +N chip with a
title-attribute breakdown. Disabled / single-node behavior unchanged.
Adopted models get an extra "Adopted" ghost-icon chip with hover copy
explaining what it means and how to make it permanent.
Distributed mode also enables a 10s auto-refresh and a "Last synced Xs
ago" indicator next to the Update button so ghost rows drop off within
one reconcile tick after their owning process dies. Non-distributed
mode is untouched — no polling, no cell-stack, same old Running/Idle.
* feat(ui): NodeDistributionChip — shared per-node attribution component
Large clusters were going to break the Manage → Backends Nodes column:
the old inline logic rendered every node as a badge and would shred the
layout at >10 workers, plus the Manage → Models distribution cell had
copy-pasted its own slightly-different version.
NodeDistributionChip handles any cluster size with two render modes:
- small (≤3 nodes): inline chips of node names, colored by health.
- large: a single "on N nodes · M offline · K drift" summary chip;
clicking opens a Popover with a per-node table (name, status,
version, digest for backends; name, status, state for models).
Drift counting mirrors the backend's summarizeNodeDrift so the UI
number matches UpgradeInfo.NodeDrift. Digests are truncated to the
docker-style 12-char form with the full value preserved in the title.
Popover is a new general-purpose primitive: fixed positioning anchored
to the trigger, flips above when there's no room below, closes on
outside-click or Escape, returns focus to the trigger. Uses .card as
its surface so theming is inherited. Also useful for a future
labels-editor popup and the user menu.
Manage.jsx drops its duplicated inline Nodes-column + loaded_on cell
and uses the shared chip with context="backends" / "models"
respectively. Delete code removes ~40 lines of ad-hoc logic.
* feat(ui): shared FilterBar across the System page tabs
The Backends gallery had a nice search + chip + toggle strip; the System
page had nothing, so the two surfaces felt like different apps. Lift the
pattern into a reusable FilterBar and wire both System tabs through it.
New component core/http/react-ui/src/components/FilterBar.jsx renders a
search input, a role="tablist" chip row (aria-selected for a11y), and
optional toggles / right slot. Chips support an optional `count` which
the System page uses to show "User 3", "Updates 1" etc.
System Models tab: search by id or backend; chips for
All/Running/Idle/Disabled/Pinned plus a conditional Distributed chip in
distributed mode. "Last synced" + Update button live in the right slot.
System Backends tab: search by name/alias/meta-backend-for; chips for
All/User/System/Meta plus conditional Updates / Offline-nodes chips
when relevant. The old ad-hoc "Updates only" toggle from the upgrade
banner folded into the Updates chip — one source of truth for that
filter. Offline chip only appears in distributed mode when at least
one backend has an unhealthy node, so the chip row stays quiet on
healthy clusters.
Filter state persists in URL query params (mq/mf/bq/bf) so deep links
and tab switches keep the operator's filter context instead of
resetting every time.
Also adds an "Adopted" distribution path: when a model in
/api/models/capabilities carries source="registry-only" (discovered on
a worker but not configured locally), the Models tab shows a ghost chip
labelled "Adopted" with hover copy explaining how to persist it — this
is what closes the loop on the ghost-model story end-to-end.
2026-04-19 15:55:53 +00:00
|
|
|
.badge-accent {
|
|
|
|
|
background: var(--color-accent-light);
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Horizontal row of badges used inside table cells — consistent spacing so
|
|
|
|
|
cells line up regardless of how many badges are present. */
|
|
|
|
|
.badge-row {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Vertically stacked cell content (e.g. version + update chip + drift chip).
|
|
|
|
|
Keeps rows readable at scale without inline style={{...}} everywhere. */
|
|
|
|
|
.cell-stack {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cell-mono {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
feat(distributed): sync state with frontends, better backend management reporting (#9426)
* fix(distributed): detect backend upgrades across worker nodes
Before this change `DistributedBackendManager.CheckUpgrades` delegated to the
local manager, which read backends from the frontend filesystem. In
distributed deployments the frontend has no backends installed locally —
they live on workers — so the upgrade-detection loop never ran and the UI
silently never surfaced upgrades even when the gallery advertised newer
versions or digests.
Worker-side: NATS backend.list reply now carries Version, URI and Digest
for each installed backend (read from metadata.json).
Frontend-side: DistributedBackendManager.ListBackends aggregates per-node
refs (name, status, version, digest) instead of deduping, and CheckUpgrades
feeds that aggregation into gallery.CheckUpgradesAgainst — a new entrypoint
factored out of CheckBackendUpgrades so both paths share the same core
logic.
Cluster drift policy: when per-node version/digest tuples disagree, the
backend is flagged upgradeable regardless of whether any single node
matches the gallery, and UpgradeInfo.NodeDrift enumerates the outliers so
operators can see *why* it is out of sync. The next upgrade-all realigns
the cluster.
Tests cover: drift detection, unanimous-match (no upgrade), and the
empty-installed-version path that the old distributed code silently
missed.
* feat(ui): surface backend upgrades in the System page
The System page (Manage.jsx) only showed updates as a tiny inline arrow,
so operators routinely missed them. Port the Backend Gallery's upgrade UX
so System speaks the same visual language:
- Yellow banner at the top of the Backends tab when upgrades are pending,
with an "Upgrade all" button (serial fan-out, matches the gallery) and a
"Updates only" filter toggle.
- Warning pill (↑ N) next to the tab label so the count is glanceable even
when the banner is scrolled out of view.
- Per-row labeled "Upgrade to vX.Y" button (replaces the icon-only button
that silently flipped semantics between Reinstall and Upgrade), plus an
"Update available" badge in the new Version column.
- New columns: Version (with upgrade + drift chips), Nodes (per-node
attribution badges for distributed mode, degrading to a compact
"on N nodes · M offline" chip above three nodes), Installed (relative
time).
- System backends render a "Protected" chip instead of a bare "—" so rows
still align and the reason is obvious.
- Delete uses the softer btn-danger-ghost so rows don't scream red; the
ConfirmDialog still owns the "are you sure".
The upgrade checker also needed the same per-worker fix as the previous
commit: NewUpgradeChecker now takes a BackendManager getter so its
periodic runs call the distributed CheckUpgrades (which asks workers)
instead of the empty frontend filesystem. Without this the /api/backends/
upgrades endpoint stayed empty in distributed mode even with the protocol
change in place.
New CSS primitives — .upgrade-banner, .tab-pill, .badge-row, .cell-stack,
.cell-mono, .cell-muted, .row-actions, .btn-danger-ghost — all live in
App.css so other pages can adopt them without duplicating styles.
* feat(ui): polish the Nodes page so it reads like a product
The Nodes page was the biggest visual liability in distributed mode.
Rework the main dashboard surfaces in place without changing behavior:
StatCards: uniform height (96px min), left accent bar colored by the
metric's semantic (success/warning/error/primary), icon lives in a
36x36 soft-tinted chip top-right, value is left-aligned and large.
Grid auto-fills so the row doesn't collapse on narrow viewports. This
replaces the previous thin-bordered boxes with inconsistent heights.
Table rows: expandable rows now show a chevron cue on the left (rotates
on expand) so users know rows open. Status cell became a dedicated chip
with an LED-style halo dot instead of a bare bullet. Action buttons gained
labels — "Approve", "Resume", "Drain" — so the icons aren't doing all
the semantic work; the destructive remove action uses the softer
btn-danger-ghost variant so rows don't scream red, with the ConfirmDialog
still owning the real "are you sure". Applied cell-mono/cell-muted
utility classes so label chips and addresses share one spacing/font
grammar instead of re-declaring inline styles everywhere.
Expanded drawer: empty states for Loaded Models and Installed Backends
now render as a proper drawer-empty card (dashed border, icon, one-line
hint) instead of a plain muted string that read like broken formatting.
Tabs: three inline-styled buttons became the shared .tab class so they
inherit focus ring, hover state, and the rest of the design system —
matches the System page.
"Add more workers" toggle turned into a .nodes-add-worker dashed-border
button labelled "Register a new worker" (action voice) instead of a
chevron + muted link that operators kept mistaking for broken text.
New shared CSS primitives carry over to other pages:
.stat-grid + .stat-card, .row-chevron, .node-status, .drawer-empty,
.nodes-add-worker.
* feat(distributed): durable backend fan-out + state reconciliation
Two connected problems handled together:
1) Backend delete/install/upgrade used to silently skip non-healthy nodes,
so a delete during an outage left a zombie on the offline node once it
returned. The fan-out now records intent in a new pending_backend_ops
table before attempting the NATS round-trip. Currently-healthy nodes
get an immediate attempt; everyone else is queued. Unique index on
(node_id, backend, op) means reissuing the same operation refreshes
next_retry_at instead of stacking duplicates.
2) Loaded-model state could drift from reality: a worker OOM'd, got
killed, or restarted a backend process would leave a node_models row
claiming the model was still loaded, feeding ghost entries into the
/api/nodes/models listing and the router's scheduling decisions.
The existing ReplicaReconciler gains two new passes that run under a
fresh KeyStateReconciler advisory lock (non-blocking, so one wedged
frontend doesn't freeze the cluster):
- drainPendingBackendOps: retries queued ops whose next_retry_at has
passed on currently-healthy nodes. Success deletes the row; failure
bumps attempts and pushes next_retry_at out with exponential backoff
(30s → 15m cap). ErrNoResponders also marks the node unhealthy.
- probeLoadedModels: gRPC-HealthChecks addresses the DB thinks are
loaded but hasn't seen touched in the last probeStaleAfter (2m).
Unreachable addresses are removed from the registry. A pluggable
ModelProber lets tests substitute a fake without standing up gRPC.
DistributedBackendManager exposes DeleteBackendDetailed so the HTTP
handler can surface per-node outcomes ("2 succeeded, 1 queued") to the
UI in a follow-up commit; the existing DeleteBackend still returns
error-only for callers that don't care about node breakdown.
Multi-frontend safety: the state pass uses advisorylock.TryWithLockCtx
on a new key so N frontends coordinate — the same pattern the health
monitor and replica reconciler already rely on. Single-node mode runs
both passes inline (adapter is nil, state drain is a no-op).
Tests cover the upsert semantics, backoff math, the probe removing an
unreachable model but keeping a reachable one, and filtering by
probeStaleAfter.
* feat(ui): show cluster distribution of models in the System page
When a frontend restarted in distributed mode, models that workers had
already loaded weren't visible until the operator clicked into each node
manually — the /api/models/capabilities endpoint only knew about
configs on the frontend's filesystem, not the registry-backed truth.
/api/models/capabilities now joins in ListAllLoadedModels() when the
registry is active, returning loaded_on[] with node id/name/state/status
for each model. Models that live in the registry but lack a local config
(the actual ghosts, not recovered from the frontend's file cache) still
surface with source="registry-only" so operators can see and persist
them; without that emission they'd be invisible to this frontend.
Manage → Models replaces the old Running/Idle pill with a distribution
cell that lists the first three nodes the model is loaded on as chips
colored by state (green loaded, blue loading, amber anything else). On
wider clusters the remaining count collapses into a +N chip with a
title-attribute breakdown. Disabled / single-node behavior unchanged.
Adopted models get an extra "Adopted" ghost-icon chip with hover copy
explaining what it means and how to make it permanent.
Distributed mode also enables a 10s auto-refresh and a "Last synced Xs
ago" indicator next to the Update button so ghost rows drop off within
one reconcile tick after their owning process dies. Non-distributed
mode is untouched — no polling, no cell-stack, same old Running/Idle.
* feat(ui): NodeDistributionChip — shared per-node attribution component
Large clusters were going to break the Manage → Backends Nodes column:
the old inline logic rendered every node as a badge and would shred the
layout at >10 workers, plus the Manage → Models distribution cell had
copy-pasted its own slightly-different version.
NodeDistributionChip handles any cluster size with two render modes:
- small (≤3 nodes): inline chips of node names, colored by health.
- large: a single "on N nodes · M offline · K drift" summary chip;
clicking opens a Popover with a per-node table (name, status,
version, digest for backends; name, status, state for models).
Drift counting mirrors the backend's summarizeNodeDrift so the UI
number matches UpgradeInfo.NodeDrift. Digests are truncated to the
docker-style 12-char form with the full value preserved in the title.
Popover is a new general-purpose primitive: fixed positioning anchored
to the trigger, flips above when there's no room below, closes on
outside-click or Escape, returns focus to the trigger. Uses .card as
its surface so theming is inherited. Also useful for a future
labels-editor popup and the user menu.
Manage.jsx drops its duplicated inline Nodes-column + loaded_on cell
and uses the shared chip with context="backends" / "models"
respectively. Delete code removes ~40 lines of ad-hoc logic.
* feat(ui): shared FilterBar across the System page tabs
The Backends gallery had a nice search + chip + toggle strip; the System
page had nothing, so the two surfaces felt like different apps. Lift the
pattern into a reusable FilterBar and wire both System tabs through it.
New component core/http/react-ui/src/components/FilterBar.jsx renders a
search input, a role="tablist" chip row (aria-selected for a11y), and
optional toggles / right slot. Chips support an optional `count` which
the System page uses to show "User 3", "Updates 1" etc.
System Models tab: search by id or backend; chips for
All/Running/Idle/Disabled/Pinned plus a conditional Distributed chip in
distributed mode. "Last synced" + Update button live in the right slot.
System Backends tab: search by name/alias/meta-backend-for; chips for
All/User/System/Meta plus conditional Updates / Offline-nodes chips
when relevant. The old ad-hoc "Updates only" toggle from the upgrade
banner folded into the Updates chip — one source of truth for that
filter. Offline chip only appears in distributed mode when at least
one backend has an unhealthy node, so the chip row stays quiet on
healthy clusters.
Filter state persists in URL query params (mq/mf/bq/bf) so deep links
and tab switches keep the operator's filter context instead of
resetting every time.
Also adds an "Adopted" distribution path: when a model in
/api/models/capabilities carries source="registry-only" (discovered on
a worker but not configured locally), the Models tab shows a ghost chip
labelled "Adopted" with hover copy explaining how to persist it — this
is what closes the loop on the ghost-model story end-to-end.
2026-04-19 15:55:53 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cell-muted {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cell-subtle {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cell-name {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
.cell-name > i {
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.row-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Softer delete button for dense tables — the destructive confirm dialog
|
|
|
|
|
already owns the "are you sure" affordance, so the button itself doesn't
|
|
|
|
|
need to scream. Keeps the delete red readable without dominating rows. */
|
|
|
|
|
.btn.btn-danger-ghost {
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
.btn.btn-danger-ghost:hover:not(:disabled) {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
border-color: var(--color-error-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Small count pill used inside tabs ("(3) ↑ 2") so update counts are
|
|
|
|
|
glanceable without extra rows of UI. */
|
|
|
|
|
.tab-pill {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 3px;
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
.tab-pill--warning {
|
|
|
|
|
background: var(--color-warning-light);
|
|
|
|
|
color: var(--color-warning);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Stat cards — uniform-height cluster metrics for the Nodes dashboard.
|
|
|
|
|
Left accent bar ties the color to the metric's semantic (success/warning/
|
|
|
|
|
error/primary), icon chip sits top-right, value is left-aligned and
|
|
|
|
|
prominent so you can scan a row of cards without reading labels. */
|
|
|
|
|
.stat-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
margin-bottom: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
min-height: 96px;
|
|
|
|
|
background: var(--color-bg-raised, var(--color-bg-secondary));
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
transition: transform var(--duration-fast) var(--ease-default),
|
|
|
|
|
box-shadow var(--duration-fast) var(--ease-default),
|
|
|
|
|
border-color var(--duration-fast) var(--ease-default);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.stat-card::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0; top: 0; bottom: 0;
|
|
|
|
|
width: 3px;
|
|
|
|
|
background: var(--stat-accent, var(--color-border-subtle));
|
|
|
|
|
transition: background var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.stat-card:hover {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
border-color: var(--color-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-card__body {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.stat-card__label {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
white-space: normal;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
}
|
|
|
|
|
.stat-card__value {
|
|
|
|
|
font-size: var(--text-2xl);
|
|
|
|
|
font-weight: 600;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
feat(distributed): sync state with frontends, better backend management reporting (#9426)
* fix(distributed): detect backend upgrades across worker nodes
Before this change `DistributedBackendManager.CheckUpgrades` delegated to the
local manager, which read backends from the frontend filesystem. In
distributed deployments the frontend has no backends installed locally —
they live on workers — so the upgrade-detection loop never ran and the UI
silently never surfaced upgrades even when the gallery advertised newer
versions or digests.
Worker-side: NATS backend.list reply now carries Version, URI and Digest
for each installed backend (read from metadata.json).
Frontend-side: DistributedBackendManager.ListBackends aggregates per-node
refs (name, status, version, digest) instead of deduping, and CheckUpgrades
feeds that aggregation into gallery.CheckUpgradesAgainst — a new entrypoint
factored out of CheckBackendUpgrades so both paths share the same core
logic.
Cluster drift policy: when per-node version/digest tuples disagree, the
backend is flagged upgradeable regardless of whether any single node
matches the gallery, and UpgradeInfo.NodeDrift enumerates the outliers so
operators can see *why* it is out of sync. The next upgrade-all realigns
the cluster.
Tests cover: drift detection, unanimous-match (no upgrade), and the
empty-installed-version path that the old distributed code silently
missed.
* feat(ui): surface backend upgrades in the System page
The System page (Manage.jsx) only showed updates as a tiny inline arrow,
so operators routinely missed them. Port the Backend Gallery's upgrade UX
so System speaks the same visual language:
- Yellow banner at the top of the Backends tab when upgrades are pending,
with an "Upgrade all" button (serial fan-out, matches the gallery) and a
"Updates only" filter toggle.
- Warning pill (↑ N) next to the tab label so the count is glanceable even
when the banner is scrolled out of view.
- Per-row labeled "Upgrade to vX.Y" button (replaces the icon-only button
that silently flipped semantics between Reinstall and Upgrade), plus an
"Update available" badge in the new Version column.
- New columns: Version (with upgrade + drift chips), Nodes (per-node
attribution badges for distributed mode, degrading to a compact
"on N nodes · M offline" chip above three nodes), Installed (relative
time).
- System backends render a "Protected" chip instead of a bare "—" so rows
still align and the reason is obvious.
- Delete uses the softer btn-danger-ghost so rows don't scream red; the
ConfirmDialog still owns the "are you sure".
The upgrade checker also needed the same per-worker fix as the previous
commit: NewUpgradeChecker now takes a BackendManager getter so its
periodic runs call the distributed CheckUpgrades (which asks workers)
instead of the empty frontend filesystem. Without this the /api/backends/
upgrades endpoint stayed empty in distributed mode even with the protocol
change in place.
New CSS primitives — .upgrade-banner, .tab-pill, .badge-row, .cell-stack,
.cell-mono, .cell-muted, .row-actions, .btn-danger-ghost — all live in
App.css so other pages can adopt them without duplicating styles.
* feat(ui): polish the Nodes page so it reads like a product
The Nodes page was the biggest visual liability in distributed mode.
Rework the main dashboard surfaces in place without changing behavior:
StatCards: uniform height (96px min), left accent bar colored by the
metric's semantic (success/warning/error/primary), icon lives in a
36x36 soft-tinted chip top-right, value is left-aligned and large.
Grid auto-fills so the row doesn't collapse on narrow viewports. This
replaces the previous thin-bordered boxes with inconsistent heights.
Table rows: expandable rows now show a chevron cue on the left (rotates
on expand) so users know rows open. Status cell became a dedicated chip
with an LED-style halo dot instead of a bare bullet. Action buttons gained
labels — "Approve", "Resume", "Drain" — so the icons aren't doing all
the semantic work; the destructive remove action uses the softer
btn-danger-ghost variant so rows don't scream red, with the ConfirmDialog
still owning the real "are you sure". Applied cell-mono/cell-muted
utility classes so label chips and addresses share one spacing/font
grammar instead of re-declaring inline styles everywhere.
Expanded drawer: empty states for Loaded Models and Installed Backends
now render as a proper drawer-empty card (dashed border, icon, one-line
hint) instead of a plain muted string that read like broken formatting.
Tabs: three inline-styled buttons became the shared .tab class so they
inherit focus ring, hover state, and the rest of the design system —
matches the System page.
"Add more workers" toggle turned into a .nodes-add-worker dashed-border
button labelled "Register a new worker" (action voice) instead of a
chevron + muted link that operators kept mistaking for broken text.
New shared CSS primitives carry over to other pages:
.stat-grid + .stat-card, .row-chevron, .node-status, .drawer-empty,
.nodes-add-worker.
* feat(distributed): durable backend fan-out + state reconciliation
Two connected problems handled together:
1) Backend delete/install/upgrade used to silently skip non-healthy nodes,
so a delete during an outage left a zombie on the offline node once it
returned. The fan-out now records intent in a new pending_backend_ops
table before attempting the NATS round-trip. Currently-healthy nodes
get an immediate attempt; everyone else is queued. Unique index on
(node_id, backend, op) means reissuing the same operation refreshes
next_retry_at instead of stacking duplicates.
2) Loaded-model state could drift from reality: a worker OOM'd, got
killed, or restarted a backend process would leave a node_models row
claiming the model was still loaded, feeding ghost entries into the
/api/nodes/models listing and the router's scheduling decisions.
The existing ReplicaReconciler gains two new passes that run under a
fresh KeyStateReconciler advisory lock (non-blocking, so one wedged
frontend doesn't freeze the cluster):
- drainPendingBackendOps: retries queued ops whose next_retry_at has
passed on currently-healthy nodes. Success deletes the row; failure
bumps attempts and pushes next_retry_at out with exponential backoff
(30s → 15m cap). ErrNoResponders also marks the node unhealthy.
- probeLoadedModels: gRPC-HealthChecks addresses the DB thinks are
loaded but hasn't seen touched in the last probeStaleAfter (2m).
Unreachable addresses are removed from the registry. A pluggable
ModelProber lets tests substitute a fake without standing up gRPC.
DistributedBackendManager exposes DeleteBackendDetailed so the HTTP
handler can surface per-node outcomes ("2 succeeded, 1 queued") to the
UI in a follow-up commit; the existing DeleteBackend still returns
error-only for callers that don't care about node breakdown.
Multi-frontend safety: the state pass uses advisorylock.TryWithLockCtx
on a new key so N frontends coordinate — the same pattern the health
monitor and replica reconciler already rely on. Single-node mode runs
both passes inline (adapter is nil, state drain is a no-op).
Tests cover the upsert semantics, backoff math, the probe removing an
unreachable model but keeping a reachable one, and filtering by
probeStaleAfter.
* feat(ui): show cluster distribution of models in the System page
When a frontend restarted in distributed mode, models that workers had
already loaded weren't visible until the operator clicked into each node
manually — the /api/models/capabilities endpoint only knew about
configs on the frontend's filesystem, not the registry-backed truth.
/api/models/capabilities now joins in ListAllLoadedModels() when the
registry is active, returning loaded_on[] with node id/name/state/status
for each model. Models that live in the registry but lack a local config
(the actual ghosts, not recovered from the frontend's file cache) still
surface with source="registry-only" so operators can see and persist
them; without that emission they'd be invisible to this frontend.
Manage → Models replaces the old Running/Idle pill with a distribution
cell that lists the first three nodes the model is loaded on as chips
colored by state (green loaded, blue loading, amber anything else). On
wider clusters the remaining count collapses into a +N chip with a
title-attribute breakdown. Disabled / single-node behavior unchanged.
Adopted models get an extra "Adopted" ghost-icon chip with hover copy
explaining what it means and how to make it permanent.
Distributed mode also enables a 10s auto-refresh and a "Last synced Xs
ago" indicator next to the Update button so ghost rows drop off within
one reconcile tick after their owning process dies. Non-distributed
mode is untouched — no polling, no cell-stack, same old Running/Idle.
* feat(ui): NodeDistributionChip — shared per-node attribution component
Large clusters were going to break the Manage → Backends Nodes column:
the old inline logic rendered every node as a badge and would shred the
layout at >10 workers, plus the Manage → Models distribution cell had
copy-pasted its own slightly-different version.
NodeDistributionChip handles any cluster size with two render modes:
- small (≤3 nodes): inline chips of node names, colored by health.
- large: a single "on N nodes · M offline · K drift" summary chip;
clicking opens a Popover with a per-node table (name, status,
version, digest for backends; name, status, state for models).
Drift counting mirrors the backend's summarizeNodeDrift so the UI
number matches UpgradeInfo.NodeDrift. Digests are truncated to the
docker-style 12-char form with the full value preserved in the title.
Popover is a new general-purpose primitive: fixed positioning anchored
to the trigger, flips above when there's no room below, closes on
outside-click or Escape, returns focus to the trigger. Uses .card as
its surface so theming is inherited. Also useful for a future
labels-editor popup and the user menu.
Manage.jsx drops its duplicated inline Nodes-column + loaded_on cell
and uses the shared chip with context="backends" / "models"
respectively. Delete code removes ~40 lines of ad-hoc logic.
* feat(ui): shared FilterBar across the System page tabs
The Backends gallery had a nice search + chip + toggle strip; the System
page had nothing, so the two surfaces felt like different apps. Lift the
pattern into a reusable FilterBar and wire both System tabs through it.
New component core/http/react-ui/src/components/FilterBar.jsx renders a
search input, a role="tablist" chip row (aria-selected for a11y), and
optional toggles / right slot. Chips support an optional `count` which
the System page uses to show "User 3", "Updates 1" etc.
System Models tab: search by id or backend; chips for
All/Running/Idle/Disabled/Pinned plus a conditional Distributed chip in
distributed mode. "Last synced" + Update button live in the right slot.
System Backends tab: search by name/alias/meta-backend-for; chips for
All/User/System/Meta plus conditional Updates / Offline-nodes chips
when relevant. The old ad-hoc "Updates only" toggle from the upgrade
banner folded into the Updates chip — one source of truth for that
filter. Offline chip only appears in distributed mode when at least
one backend has an unhealthy node, so the chip row stays quiet on
healthy clusters.
Filter state persists in URL query params (mq/mf/bq/bf) so deep links
and tab switches keep the operator's filter context instead of
resetting every time.
Also adds an "Adopted" distribution path: when a model in
/api/models/capabilities carries source="registry-only" (discovered on
a worker but not configured locally), the Models tab shows a ghost chip
labelled "Adopted" with hover copy explaining how to persist it — this
is what closes the loop on the ghost-model story end-to-end.
2026-04-19 15:55:53 +00:00
|
|
|
line-height: 1;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
.stat-card__icon {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
background: color-mix(in srgb, var(--stat-accent, var(--color-text-muted)) 12%, transparent);
|
|
|
|
|
color: var(--stat-accent, var(--color-text-muted));
|
|
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Subtle "Register a new worker" trigger replacing the broken-text chevron
|
|
|
|
|
link. Still opens the same hint card — just reads like a button now. */
|
|
|
|
|
.nodes-add-worker {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px dashed var(--color-border);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
transition: background var(--duration-fast) var(--ease-default),
|
|
|
|
|
border-color var(--duration-fast) var(--ease-default),
|
|
|
|
|
color var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.nodes-add-worker:hover {
|
|
|
|
|
background: var(--color-bg-raised, var(--color-bg-secondary));
|
|
|
|
|
border-color: var(--color-border-strong);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shared FilterBar layout — search strip + chip row + toggle strip. Lives
|
|
|
|
|
outside the .filter-bar chip row so the padding and wrapping behavior is
|
|
|
|
|
consistent between the Backends gallery and the System tabs. */
|
|
|
|
|
.filter-bar-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.filter-bar-group__search {
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
.filter-bar-group__row {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
.filter-bar-group__right {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
padding-left: var(--spacing-md);
|
|
|
|
|
border-left: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
.filter-bar-group__toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
user-select: none;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.filter-btn__count {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
min-width: 18px;
|
|
|
|
|
padding: 0 5px;
|
|
|
|
|
background: color-mix(in srgb, currentColor 18%, transparent);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Popover — floating surface anchored to a trigger element. Uses the .card
|
|
|
|
|
base so theming is free, adds z-index + fixed-position + scroll cap so it
|
|
|
|
|
behaves on tables with many rows. Kept deliberately unstyled beyond that
|
|
|
|
|
— content is expected to provide its own header/body structure. */
|
|
|
|
|
.popover {
|
|
|
|
|
position: fixed;
|
|
|
|
|
z-index: 200;
|
|
|
|
|
min-width: 260px;
|
|
|
|
|
max-width: min(420px, 95vw);
|
|
|
|
|
max-height: min(420px, 70vh);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
padding: 0; /* sections provide their own padding */
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
|
|
|
|
animation: popoverIn var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes popoverIn {
|
|
|
|
|
from { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
|
|
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.popover__header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.popover__scroll {
|
|
|
|
|
overflow: auto;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.popover__table {
|
|
|
|
|
margin: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
.popover__table th {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
background: var(--color-bg-raised, var(--color-bg-secondary));
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inline-table chip trigger — looks like a badge but is a button (cursor,
|
|
|
|
|
focus ring inherited from global :focus-visible). */
|
|
|
|
|
.chip-trigger {
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
}
|
|
|
|
|
.chip-trigger:hover {
|
|
|
|
|
filter: brightness(1.08);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Truncate + ellipsize a long cell (e.g. OCI digest) without breaking the
|
|
|
|
|
table layout. Tooltip preserves the full value. */
|
|
|
|
|
.cell-truncate {
|
|
|
|
|
max-width: 160px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compact empty-state used inside expanded drawer sections (e.g. "No
|
|
|
|
|
models loaded on this node"). Dimmer than the page-level .empty-state
|
|
|
|
|
because it lives inside another container and shouldn't compete with
|
|
|
|
|
the row's primary content. */
|
|
|
|
|
.drawer-empty {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border: 1px dashed var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.drawer-empty > i {
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Node-status indicator — replaces the tiny bullet with a proper LED-style
|
|
|
|
|
dot next to a bold status label. Colors are applied inline from statusConfig
|
|
|
|
|
so one primitive handles healthy/unhealthy/draining/pending in one shape. */
|
|
|
|
|
.node-status {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
.node-status__dot {
|
|
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 15%, transparent);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Row-chevron cell — small 20px toggle used in table rows that expand.
|
|
|
|
|
The row itself is still clickable; the chevron provides the visible
|
|
|
|
|
affordance users were missing. */
|
|
|
|
|
.row-chevron {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
transition: transform var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.row-chevron.is-expanded {
|
|
|
|
|
transform: rotate(90deg);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Upgrade banner — the yellow strip operators see when updates are available.
|
|
|
|
|
Mirrors the gallery so both pages speak the same visual language. */
|
|
|
|
|
.upgrade-banner {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
background: var(--color-warning-light);
|
|
|
|
|
border: 1px solid var(--color-warning);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-warning);
|
|
|
|
|
}
|
|
|
|
|
.upgrade-banner__text {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.upgrade-banner__actions {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
|
|
|
|
|
/* Tabs */
|
|
|
|
|
.tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0;
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-bottom: 2px solid transparent;
|
|
|
|
|
transition: all var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab:hover {
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-active {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-bottom-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tables */
|
|
|
|
|
.table-container {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table th {
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table td {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
color: var(--color-text-primary);
|
2026-03-19 20:40:51 +00:00
|
|
|
transition: background var(--duration-fast) var(--ease-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table tr:last-child td {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table tr:hover td {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Toggle switch */
|
|
|
|
|
.toggle {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle input {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
width: 0;
|
|
|
|
|
height: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle-slider {
|
|
|
|
|
position: absolute;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: var(--color-toggle-off, #CBD5E1);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
transition: background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle-slider::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
height: 14px;
|
|
|
|
|
width: 14px;
|
|
|
|
|
left: 2px;
|
|
|
|
|
bottom: 2px;
|
|
|
|
|
background: white;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
transition: transform var(--duration-fast);
|
|
|
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle input:checked + .toggle-slider {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toggle input:checked + .toggle-slider::before {
|
|
|
|
|
transform: translateX(16px);
|
|
|
|
|
background: white;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
/* Model checkbox list */
|
|
|
|
|
.model-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-list::-webkit-scrollbar {
|
|
|
|
|
width: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-list::-webkit-scrollbar-track {
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-list::-webkit-scrollbar-thumb {
|
|
|
|
|
background: var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: 6px var(--spacing-sm);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
transition: background var(--duration-fast) var(--ease-default);
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item:hover {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item.model-item-checked {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item input[type="checkbox"] {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item-check {
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
border: 2px solid var(--color-border-default);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: all var(--duration-fast) var(--ease-default);
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item:hover .model-item-check {
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item-checked .model-item-check {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
box-shadow: 0 0 0 1px var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item-checked .model-item-check i {
|
|
|
|
|
color: white;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
animation: checkPop var(--duration-fast) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes checkPop {
|
|
|
|
|
0% { transform: scale(0); }
|
|
|
|
|
60% { transform: scale(1.2); }
|
|
|
|
|
100% { transform: scale(1); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item-name {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-19 20:40:51 +00:00
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.model-item-checked .model-item-name {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
/* Collapsible */
|
|
|
|
|
.collapsible-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.collapsible-header i {
|
|
|
|
|
transition: transform var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.collapsible-header.open i {
|
|
|
|
|
transform: rotate(90deg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search bar */
|
|
|
|
|
.search-bar {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-bar .search-icon {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: var(--spacing-sm);
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-bar .input {
|
|
|
|
|
padding-left: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pagination */
|
|
|
|
|
.pagination {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
margin-top: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination-btn {
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
transition: all var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination-btn:hover:not(:disabled) {
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination-btn.active {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pagination-btn:disabled {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Filter buttons */
|
|
|
|
|
.filter-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-btn {
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 6px var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-raised);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-04-14 21:53:03 +00:00
|
|
|
border: 1px solid var(--color-border-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-family: inherit;
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
transition: all var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-btn:hover {
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
color: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-btn.active {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
/* Login page */
|
|
|
|
|
.login-page {
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
min-height: 100dvh;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-card {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-header {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-bottom: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-logo {
|
|
|
|
|
width: 56px;
|
|
|
|
|
height: 56px;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-title {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-subtitle {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-alert {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-alert-error {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
border: 1px solid var(--color-error-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-alert-success {
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
border: 1px solid var(--color-success-border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-divider {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
margin: var(--spacing-lg) 0;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-divider::before,
|
|
|
|
|
.login-divider::after {
|
|
|
|
|
content: '';
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 1px;
|
|
|
|
|
background: var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-footer {
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin-top: var(--spacing-md);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-link {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-link:hover {
|
|
|
|
|
color: var(--color-primary-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-token-toggle {
|
|
|
|
|
margin-top: var(--spacing-lg);
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-token-toggle > button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-token-toggle > button:hover {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.login-token-form {
|
|
|
|
|
margin-top: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
/* Empty state */
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
/* Empty state — editorial: eyebrow rule + large mono headline + lede.
|
|
|
|
|
Existing pages pass `icon`, `title`, `text` children into .empty-state as
|
|
|
|
|
nested elements; the rules below re-style each without JSX changes. */
|
2026-03-05 20:47:12 +00:00
|
|
|
.empty-state {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: var(--spacing-3xl) var(--spacing-xl);
|
|
|
|
|
max-width: 640px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
animation: fadeIn var(--duration-slow) var(--ease-spring);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
/* Center the column within its parent without shrinking type. */
|
|
|
|
|
.empty-state > * { max-width: 100%; }
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.empty-state-icon {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-size: 1.5rem;
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
margin: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-state-title {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
font-size: clamp(1.5rem, 3vw, var(--text-3xl));
|
|
|
|
|
font-weight: var(--font-weight-regular);
|
|
|
|
|
letter-spacing: -0.03em;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
margin: 0;
|
|
|
|
|
line-height: 1.15;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-state-text {
|
|
|
|
|
color: var(--color-text-secondary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-size: var(--text-base);
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
max-width: 52ch;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Opt-in editorial sub-parts — pages can adopt these class names over time */
|
|
|
|
|
.empty-state__eyebrow {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
font-family: var(--font-mono);
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
letter-spacing: 0.24em;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.empty-state__eyebrow::before {
|
|
|
|
|
content: "";
|
|
|
|
|
display: block;
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 1px;
|
|
|
|
|
background: var(--color-border-strong);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Animations */
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
to { transform: rotate(360deg); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes slideIn {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
from { opacity: 0; transform: translateX(12px); }
|
|
|
|
|
to { opacity: 1; transform: translateX(0); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes toastSlideIn {
|
|
|
|
|
from { opacity: 0; transform: translateX(12px); }
|
|
|
|
|
to { opacity: 1; transform: translateX(0); }
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes fadeIn {
|
|
|
|
|
from { opacity: 0; }
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
to { opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes popIn {
|
|
|
|
|
from { opacity: 0; transform: scale(0.96) translateY(4px); }
|
|
|
|
|
to { opacity: 1; transform: scale(1) translateY(0); }
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
0%, 100% { opacity: 1; }
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
50% { opacity: 0.5; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes attentionPulse {
|
|
|
|
|
0%, 100% { opacity: 0.65; }
|
|
|
|
|
50% { opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes opsBarBreathe {
|
|
|
|
|
0%, 100% { opacity: 0.85; }
|
|
|
|
|
50% { opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes breathingRing {
|
|
|
|
|
0%, 100% { box-shadow: inset 0 0 0 1.5px var(--color-primary-light); }
|
|
|
|
|
50% { box-shadow: inset 0 0 0 2px var(--color-primary); }
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
@keyframes messageSlideIn {
|
|
|
|
|
from { opacity: 0; transform: translateY(8px); }
|
|
|
|
|
to { opacity: 1; transform: translateY(0); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes dropdownIn {
|
|
|
|
|
from { opacity: 0; transform: translateY(-4px); }
|
|
|
|
|
to { opacity: 1; transform: translateY(0); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Page route transitions */
|
|
|
|
|
.page-transition {
|
|
|
|
|
animation: fadeIn 200ms ease;
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
2026-03-19 20:40:51 +00:00
|
|
|
flex-direction: column;
|
2026-03-05 20:47:12 +00:00
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
2026-03-09 22:42:47 +00:00
|
|
|
min-width: 0;
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chat-specific styles */
|
|
|
|
|
.chat-layout {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
position: relative;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-sidebar {
|
|
|
|
|
width: 260px;
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-right: 1px solid var(--color-border-subtle);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: width 200ms ease, opacity 150ms ease;
|
|
|
|
|
}
|
|
|
|
|
.chat-sidebar.hidden {
|
|
|
|
|
width: 0;
|
|
|
|
|
border-right: none;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-sidebar-header {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
scrollbar-width: thin;
|
|
|
|
|
scrollbar-color: var(--color-border-subtle) transparent;
|
|
|
|
|
}
|
|
|
|
|
.chat-list::-webkit-scrollbar { width: 4px; }
|
|
|
|
|
.chat-list::-webkit-scrollbar-track { background: transparent; }
|
|
|
|
|
.chat-list::-webkit-scrollbar-thumb {
|
|
|
|
|
background: var(--color-border-subtle);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-left: 3px solid transparent;
|
|
|
|
|
transition: background var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast);
|
2026-03-05 20:47:12 +00:00
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 21:53:03 +00:00
|
|
|
.chat-list-item:hover:not(.active) {
|
|
|
|
|
background: var(--color-surface-hover);
|
|
|
|
|
color: var(--color-text-primary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item.active {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
color: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-left-color: var(--color-primary);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-top {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-name {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-time {
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-preview {
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item-delete {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-list-item:hover .chat-list-item-delete {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-main {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
min-height: 0;
|
|
|
|
|
position: relative;
|
2026-03-09 22:42:47 +00:00
|
|
|
overflow: hidden;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-messages {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
2026-03-09 22:42:47 +00:00
|
|
|
overflow-x: hidden;
|
2026-03-05 20:47:12 +00:00
|
|
|
padding: var(--spacing-lg) var(--spacing-xl);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
scrollbar-width: thin;
|
|
|
|
|
scrollbar-color: var(--color-border-subtle) transparent;
|
|
|
|
|
}
|
|
|
|
|
.chat-messages::-webkit-scrollbar { width: 6px; }
|
|
|
|
|
.chat-messages::-webkit-scrollbar-track { background: transparent; }
|
|
|
|
|
.chat-messages::-webkit-scrollbar-thumb {
|
|
|
|
|
background: var(--color-border-subtle);
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
}
|
|
|
|
|
.chat-messages::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
background: var(--color-border-default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
max-width: 80%;
|
2026-03-09 22:42:47 +00:00
|
|
|
min-width: 0;
|
2026-03-19 20:40:51 +00:00
|
|
|
animation: messageSlideIn 250ms ease-out;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-user {
|
|
|
|
|
align-self: flex-end;
|
|
|
|
|
flex-direction: row-reverse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-assistant {
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-avatar {
|
|
|
|
|
width: 32px;
|
|
|
|
|
height: 32px;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-user .chat-message-avatar {
|
|
|
|
|
background: var(--color-primary);
|
2026-04-18 23:01:01 +00:00
|
|
|
color: var(--color-primary-text);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-assistant .chat-message-avatar {
|
|
|
|
|
background: var(--color-accent-light);
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-bubble {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-model {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-size: 0.625rem;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-weight: 500;
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
text-transform: uppercase;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
letter-spacing: 0.18em;
|
2026-03-05 20:47:12 +00:00
|
|
|
padding-left: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
border-left: 2px solid var(--color-border-strong);
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
padding: 2px var(--spacing-md);
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
line-height: var(--leading-normal);
|
2026-03-05 20:47:12 +00:00
|
|
|
word-break: break-word;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-user .chat-message-content {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: 1px solid var(--color-primary-border);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: 16px 4px 16px 16px;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
|
|
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content pre {
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
margin: var(--spacing-sm) 0;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-user .chat-message-content pre {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border-color: var(--color-border-subtle);
|
|
|
|
|
color: var(--color-text-primary);
|
2026-04-18 23:01:01 +00:00
|
|
|
}
|
|
|
|
|
.chat-message-user .chat-message-content code {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-04-18 23:01:01 +00:00
|
|
|
}
|
|
|
|
|
.chat-message-user .chat-message-content a {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
color: var(--color-primary);
|
2026-04-18 23:01:01 +00:00
|
|
|
text-decoration: underline;
|
|
|
|
|
text-underline-offset: 2px;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content code {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content p {
|
|
|
|
|
margin: var(--spacing-xs) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content p:first-child {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-message-content p:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Message action buttons */
|
|
|
|
|
.chat-message-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 150ms;
|
|
|
|
|
padding-left: 2px;
|
|
|
|
|
}
|
|
|
|
|
.chat-message:hover .chat-message-actions {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
.chat-message-actions button {
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 3px 6px;
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
transition: all 150ms;
|
|
|
|
|
}
|
|
|
|
|
.chat-message-actions button:hover {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 17:28:44 +00:00
|
|
|
.chat-message-system {
|
|
|
|
|
align-self: center;
|
|
|
|
|
max-width: 90%;
|
|
|
|
|
}
|
|
|
|
|
.chat-message-system .chat-message-bubble {
|
2026-03-09 22:42:47 +00:00
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
letter-spacing: 0.01em;
|
|
|
|
|
padding: 2px 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-message-system .chat-message-content {
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
padding: 2px var(--spacing-sm);
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
color: var(--color-text-muted);
|
2026-03-09 17:28:44 +00:00
|
|
|
}
|
|
|
|
|
.chat-message-timestamp {
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.chat-input-area {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-lg);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-top: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-input-wrapper {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
align-items: flex-end;
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-raised);
|
2026-03-05 20:47:12 +00:00
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.chat-input-wrapper:focus-within {
|
2026-04-14 21:53:03 +00:00
|
|
|
border-color: var(--color-primary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-attach-btn {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border: none !important;
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
color: var(--color-text-muted) !important;
|
|
|
|
|
padding: var(--spacing-xs) !important;
|
|
|
|
|
}
|
|
|
|
|
.chat-attach-btn:hover {
|
|
|
|
|
color: var(--color-primary) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: none;
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
outline: none;
|
|
|
|
|
resize: none;
|
|
|
|
|
min-height: 36px;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-input::placeholder {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-send-btn {
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
background: var(--color-primary);
|
2026-04-18 23:01:01 +00:00
|
|
|
color: var(--color-primary-text);
|
2026-03-05 20:47:12 +00:00
|
|
|
border: none;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
flex-shrink: 0;
|
2026-04-18 23:01:01 +00:00
|
|
|
transition: background var(--duration-fast), transform var(--duration-fast);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-send-btn:hover:not(:disabled) {
|
|
|
|
|
background: var(--color-primary-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-send-btn:disabled {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
.chat-send-btn:active:not(:disabled) {
|
|
|
|
|
transform: scale(0.92);
|
|
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
|
|
|
|
|
.chat-stop-btn {
|
|
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
background: var(--color-error);
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.chat-stop-btn:hover {
|
|
|
|
|
background: var(--color-error-hover, #dc2626);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-token-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-lg);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-streaming-cursor::after {
|
|
|
|
|
content: '';
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
margin-left: 2px;
|
|
|
|
|
animation: pulse 1s infinite;
|
|
|
|
|
vertical-align: text-bottom;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
/* Inline streaming speed indicator */
|
|
|
|
|
.chat-streaming-speed {
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
padding-top: var(--spacing-xs);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-19 20:40:51 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Thinking dots animation */
|
|
|
|
|
.chat-thinking-indicator {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
min-height: 24px;
|
|
|
|
|
}
|
|
|
|
|
.chat-thinking-dots {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.chat-thinking-dots span {
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 6px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--color-text-muted);
|
|
|
|
|
animation: thinkingBounce 1.4s infinite ease-in-out both;
|
|
|
|
|
}
|
|
|
|
|
.chat-thinking-dots span:nth-child(1) { animation-delay: -0.32s; }
|
|
|
|
|
.chat-thinking-dots span:nth-child(2) { animation-delay: -0.16s; }
|
|
|
|
|
.chat-thinking-dots span:nth-child(3) { animation-delay: 0s; }
|
|
|
|
|
@keyframes thinkingBounce {
|
|
|
|
|
0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
|
|
|
|
|
40% { transform: scale(1); opacity: 1; }
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 12:33:58 +00:00
|
|
|
/* Staging progress indicator (replaces thinking dots during model transfer) */
|
|
|
|
|
.chat-staging-progress {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
max-width: 320px;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-label {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-label i {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-detail {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-bar-container {
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 4px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-bar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
transition: width 300ms ease;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-pct {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
min-width: 32px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
.chat-staging-file {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 23:01:01 +00:00
|
|
|
/* Message completion flash — briefly highlights the last assistant bubble when streaming ends */
|
|
|
|
|
@keyframes messageCompletionFlash {
|
|
|
|
|
0% { box-shadow: 0 0 0 0 var(--color-primary-border); }
|
|
|
|
|
40% { box-shadow: 0 0 0 3px var(--color-primary-light); }
|
|
|
|
|
100% { box-shadow: 0 0 0 0 transparent; }
|
|
|
|
|
}
|
|
|
|
|
.chat-message-new .chat-message-content {
|
|
|
|
|
animation: messageCompletionFlash 600ms ease-out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
.chat-message { animation: none; }
|
|
|
|
|
.chat-message-new .chat-message-content { animation: none; }
|
|
|
|
|
.chat-thinking-dots span { animation: none; opacity: 0.7; }
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
/* Chat empty state */
|
|
|
|
|
.chat-empty-state {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
flex: 1;
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
text-align: center;
|
|
|
|
|
min-height: 300px;
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-icon {
|
|
|
|
|
font-size: 3rem;
|
|
|
|
|
color: var(--color-border-default);
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-title {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
margin: 0 0 var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-text {
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin: 0 0 var(--spacing-lg);
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
.chat-empty-suggestions {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-sm);
|
2026-03-19 20:40:51 +00:00
|
|
|
justify-content: center;
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
2026-04-14 21:53:03 +00:00
|
|
|
max-width: 520px;
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.chat-empty-suggestion {
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 8px var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
2026-03-19 20:40:51 +00:00
|
|
|
border-radius: var(--radius-full);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-19 20:40:51 +00:00
|
|
|
font-family: inherit;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle), var(--shadow-inset-top);
|
2026-03-19 20:40:51 +00:00
|
|
|
transition: all var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-suggestion:hover {
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
color: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: var(--shadow-sm), var(--shadow-inset-top);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
.chat-empty-hints {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-hints span {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.chat-empty-hints i {
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 22:42:47 +00:00
|
|
|
/* Activity group (thinking + tools collapsed into one line) */
|
|
|
|
|
@keyframes shimmer {
|
|
|
|
|
0% { background-position: -200% 0; }
|
|
|
|
|
100% { background-position: 200% 0; }
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
|
|
|
|
|
.chat-activity-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border-left: 2px solid var(--color-border-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-streaming {
|
|
|
|
|
border-left-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-toggle {
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
2026-03-09 22:42:47 +00:00
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: 6px 12px;
|
2026-03-05 20:47:12 +00:00
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: inherit;
|
2026-03-09 22:42:47 +00:00
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
transition: color 150ms;
|
|
|
|
|
width: 100%;
|
|
|
|
|
text-align: left;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-toggle:hover {
|
|
|
|
|
color: var(--color-text-secondary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-toggle i {
|
|
|
|
|
font-size: 0.5rem;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-summary {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
letter-spacing: 0.01em;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-count {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
padding: 0 5px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-shimmer {
|
|
|
|
|
background: linear-gradient(
|
|
|
|
|
90deg,
|
|
|
|
|
var(--color-text-muted) 0%,
|
|
|
|
|
var(--color-text-muted) 40%,
|
|
|
|
|
var(--color-primary) 50%,
|
|
|
|
|
var(--color-text-muted) 60%,
|
|
|
|
|
var(--color-text-muted) 100%
|
|
|
|
|
);
|
|
|
|
|
background-size: 200% 100%;
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
animation: shimmer 3s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-details {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
padding: 2px 0 6px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-item {
|
|
|
|
|
padding: 3px 12px;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1px;
|
|
|
|
|
border-left: 2px solid transparent;
|
|
|
|
|
margin-left: -2px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-item-label {
|
|
|
|
|
font-size: 0.575rem;
|
2026-03-05 20:47:12 +00:00
|
|
|
font-weight: 600;
|
2026-03-09 22:42:47 +00:00
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.04em;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-item-text {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-item-content {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
max-height: 200px;
|
2026-03-05 20:47:12 +00:00
|
|
|
overflow-y: auto;
|
|
|
|
|
overflow-x: hidden;
|
2026-03-09 22:42:47 +00:00
|
|
|
line-height: 1.5;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-activity-item-content.chat-activity-live {
|
|
|
|
|
max-height: 300px;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-item-content p { margin: 0 0 4px; }
|
|
|
|
|
.chat-activity-item-content p:last-child { margin-bottom: 0; }
|
|
|
|
|
.chat-activity-item-content pre {
|
2026-03-05 20:47:12 +00:00
|
|
|
background: var(--color-bg-tertiary);
|
2026-03-09 22:42:47 +00:00
|
|
|
padding: var(--spacing-xs);
|
|
|
|
|
border-radius: var(--radius-sm);
|
2026-03-05 20:47:12 +00:00
|
|
|
overflow-x: auto;
|
2026-03-09 22:42:47 +00:00
|
|
|
font-size: 0.75rem;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-item-content code {
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
overflow-wrap: anywhere;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-item-code {
|
|
|
|
|
margin: 2px 0 0;
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
max-height: 120px;
|
|
|
|
|
overflow-y: auto;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-item-code code {
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-09 22:42:47 +00:00
|
|
|
font-size: 0.65rem;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-params {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 3px;
|
|
|
|
|
margin-top: 2px;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-param {
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
2026-03-09 22:42:47 +00:00
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 0.675rem;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
word-break: break-word;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-param-key {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
opacity: 0.7;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-param-val {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
min-width: 0;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-param-val-long {
|
|
|
|
|
max-height: 80px;
|
2026-03-05 20:47:12 +00:00
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-thinking {
|
2026-04-18 23:01:01 +00:00
|
|
|
border-left-color: var(--color-info-border);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-09 22:42:47 +00:00
|
|
|
.chat-activity-tool-call {
|
2026-04-18 23:01:01 +00:00
|
|
|
border-left-color: var(--color-warning-border);
|
2026-03-09 22:42:47 +00:00
|
|
|
}
|
|
|
|
|
.chat-activity-tool-result {
|
2026-04-18 23:01:01 +00:00
|
|
|
border-left-color: var(--color-success-border);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Context window progress bar */
|
|
|
|
|
.chat-context-bar {
|
|
|
|
|
position: relative;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.chat-context-progress {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
height: 100%;
|
|
|
|
|
transition: width 300ms ease;
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
}
|
|
|
|
|
.chat-context-label {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
height: 100%;
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chat header */
|
|
|
|
|
.chat-header {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-header-title {
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
}
|
|
|
|
|
.chat-header-actions {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.chat-header-actions .btn-secondary.active {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
2026-03-11 06:30:49 +00:00
|
|
|
/* Chat MCP dropdown */
|
|
|
|
|
.chat-mcp-dropdown {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-dropdown .btn {
|
2026-03-05 20:47:12 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-03-11 06:30:49 +00:00
|
|
|
gap: 5px;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-11 06:30:49 +00:00
|
|
|
.chat-mcp-badge {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
min-width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
padding: 0 5px;
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
background: rgba(255,255,255,0.25);
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-dropdown-menu {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: calc(100% + 4px);
|
2026-03-18 20:14:41 +00:00
|
|
|
left: 0;
|
2026-03-11 06:30:49 +00:00
|
|
|
z-index: 100;
|
|
|
|
|
min-width: 240px;
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
2026-03-19 20:40:51 +00:00
|
|
|
animation: dropdownIn 120ms ease-out;
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
.chat-mcp-dropdown-loading,
|
|
|
|
|
.chat-mcp-dropdown-empty {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-dropdown-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-size: 0.75rem;
|
2026-03-11 06:30:49 +00:00
|
|
|
font-weight: 600;
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-03-11 06:30:49 +00:00
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.03em;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-select-all {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-transform: none;
|
|
|
|
|
letter-spacing: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-select-all:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background 120ms;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-item:hover {
|
|
|
|
|
background: var(--color-bg-hover);
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-item input[type="checkbox"] {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-name {
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.chat-mcp-server-tools {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-tertiary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Client MCP status indicators */
|
|
|
|
|
.chat-client-mcp-status {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: var(--color-text-tertiary);
|
|
|
|
|
}
|
|
|
|
|
.chat-client-mcp-status-connected {
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-success);
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
.chat-client-mcp-status-connecting {
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-warning);
|
2026-03-11 06:30:49 +00:00
|
|
|
animation: pulse 1s infinite;
|
|
|
|
|
}
|
|
|
|
|
.chat-client-mcp-status-error {
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-error);
|
2026-03-11 06:30:49 +00:00
|
|
|
}
|
|
|
|
|
.chat-client-mcp-status-disconnected {
|
|
|
|
|
background: var(--color-text-tertiary);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chat model info panel */
|
|
|
|
|
.chat-model-info-panel {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
animation: fadeIn 150ms ease;
|
|
|
|
|
}
|
|
|
|
|
.chat-model-info-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
}
|
|
|
|
|
.chat-model-info-body {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
.chat-model-info-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
padding: 2px 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-model-info-row > span:first-child {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
.chat-model-info-row > span:last-child {
|
|
|
|
|
color: var(--color-text-primary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-05 20:47:12 +00:00
|
|
|
font-size: 0.75rem;
|
|
|
|
|
max-width: 60%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Settings drawer */
|
|
|
|
|
.chat-settings-overlay {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(0, 0, 0, 0.3);
|
|
|
|
|
z-index: 10;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition: opacity 200ms;
|
|
|
|
|
}
|
|
|
|
|
.chat-settings-overlay.open {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
.chat-settings-drawer {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 320px;
|
|
|
|
|
max-width: 90%;
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-left: 1px solid var(--color-border-subtle);
|
|
|
|
|
z-index: 11;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
transform: translateX(100%);
|
|
|
|
|
transition: transform 250ms var(--ease-default);
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
2026-03-19 20:40:51 +00:00
|
|
|
will-change: transform;
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
.chat-settings-drawer.open {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
.chat-settings-drawer-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
}
|
|
|
|
|
.chat-settings-drawer-body {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chat search */
|
|
|
|
|
.chat-search-wrapper {
|
|
|
|
|
position: relative;
|
|
|
|
|
margin-bottom: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.chat-search-icon {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 8px;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.chat-search-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 5px 24px 5px 26px;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
outline: none;
|
|
|
|
|
transition: border-color 150ms;
|
|
|
|
|
}
|
|
|
|
|
.chat-search-input:focus {
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
}
|
|
|
|
|
.chat-search-input::placeholder {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.chat-search-clear {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 6px;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Chat list item actions */
|
|
|
|
|
.chat-list-item-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity 150ms;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.chat-list-item:hover .chat-list-item-actions {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
.chat-list-item-actions button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
}
|
|
|
|
|
.chat-list-item-actions button:hover {
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.chat-list-item-actions .chat-list-item-delete:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Max tokens/sec badge */
|
|
|
|
|
.chat-max-tps-badge {
|
2026-03-19 20:40:51 +00:00
|
|
|
background: rgba(59, 130, 246, 0.15);
|
2026-03-05 20:47:12 +00:00
|
|
|
color: var(--color-primary);
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Slider styles */
|
|
|
|
|
.chat-slider {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 4px;
|
|
|
|
|
appearance: none;
|
|
|
|
|
-webkit-appearance: none;
|
2026-03-18 07:31:26 +00:00
|
|
|
background: var(--color-border-default);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: 2px;
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
.chat-slider::-webkit-slider-thumb {
|
|
|
|
|
-webkit-appearance: none;
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.chat-slider::-moz-range-thumb {
|
|
|
|
|
width: 14px;
|
|
|
|
|
height: 14px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
.chat-slider-labels {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Message inline files */
|
|
|
|
|
.chat-message-files {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
margin-top: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.chat-file-inline {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 2px 6px;
|
2026-04-18 23:01:01 +00:00
|
|
|
background: color-mix(in oklab, currentColor 12%, transparent);
|
2026-03-05 20:47:12 +00:00
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.chat-inline-image {
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
margin-top: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-files {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-file-badge {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-file-badge button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:08:02 +00:00
|
|
|
/* Studio tabs */
|
|
|
|
|
.studio-tabs {
|
|
|
|
|
display: flex;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-xs);
|
2026-03-21 01:08:02 +00:00
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-sm) var(--spacing-xl) 0;
|
2026-03-21 01:08:02 +00:00
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.studio-tab {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-xs);
|
2026-03-21 01:08:02 +00:00
|
|
|
background: none;
|
|
|
|
|
border: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 10px var(--spacing-md);
|
|
|
|
|
font-size: var(--text-sm);
|
2026-03-21 01:08:02 +00:00
|
|
|
font-family: inherit;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-21 01:08:02 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-bottom: 2px solid transparent;
|
2026-04-14 21:53:03 +00:00
|
|
|
margin-bottom: -1px;
|
2026-03-21 01:08:02 +00:00
|
|
|
transition: color var(--duration-fast), border-color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.studio-tab:hover {
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.studio-tab-active {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-bottom-color: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-semibold);
|
2026-03-21 01:08:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
/* Two-column layout for media generation pages */
|
|
|
|
|
.media-layout {
|
|
|
|
|
display: grid;
|
2026-04-14 21:53:03 +00:00
|
|
|
grid-template-columns: minmax(320px, 420px) 1fr;
|
2026-03-05 20:47:12 +00:00
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
padding: var(--spacing-xl);
|
2026-04-14 21:53:03 +00:00
|
|
|
max-width: 1280px;
|
2026-03-05 20:47:12 +00:00
|
|
|
margin: 0 auto;
|
|
|
|
|
width: 100%;
|
|
|
|
|
align-items: start;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 21:53:03 +00:00
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
.media-layout {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.media-controls {
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-raised);
|
2026-03-05 20:47:12 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-lg);
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle);
|
2026-03-05 20:47:12 +00:00
|
|
|
position: sticky;
|
|
|
|
|
top: var(--spacing-lg);
|
|
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.media-controls .form-group {
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.media-controls .form-grid-2col,
|
|
|
|
|
.media-controls .form-grid-3col {
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.media-controls .form-grid-2col .form-group,
|
|
|
|
|
.media-controls .form-grid-3col .form-group {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
|
|
|
|
|
.media-controls .page-header {
|
2026-04-14 21:53:03 +00:00
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
padding-bottom: var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-controls .page-title {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-controls .page-title i {
|
|
|
|
|
color: var(--color-accent);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-preview {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-result {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
min-height: 320px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-result img,
|
|
|
|
|
.media-result video {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-result-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 22:49:55 +00:00
|
|
|
/* Media generation history */
|
|
|
|
|
.media-history {
|
|
|
|
|
margin-top: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-clear-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
transition: color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-clear-btn:hover {
|
|
|
|
|
color: var(--color-danger);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-list {
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: var(--spacing-xs) 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-empty {
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
transition: background var(--duration-fast), transform var(--duration-fast);
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item:hover {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
transform: translateX(2px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item.active {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-thumb {
|
|
|
|
|
width: 32px;
|
|
|
|
|
height: 32px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-thumb img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-top {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-prompt {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-time {
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-model {
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-delete {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
transition: opacity var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item:hover .media-history-item-delete {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-history-item-delete:hover {
|
|
|
|
|
color: var(--color-danger);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
/* Responsive */
|
|
|
|
|
@media (max-width: 1023px) {
|
2026-03-11 06:30:49 +00:00
|
|
|
.main-content,
|
|
|
|
|
.sidebar-is-collapsed .main-content {
|
2026-03-05 20:47:12 +00:00
|
|
|
margin-left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mobile-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar {
|
|
|
|
|
transform: translateX(-100%);
|
2026-03-11 06:30:49 +00:00
|
|
|
width: var(--sidebar-width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed {
|
|
|
|
|
width: var(--sidebar-width);
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.open {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-close-btn {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 06:30:49 +00:00
|
|
|
.sidebar-collapse-btn {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .nav-label,
|
|
|
|
|
.sidebar.collapsed .nav-external,
|
|
|
|
|
.sidebar.collapsed .sidebar-section-title {
|
|
|
|
|
display: unset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-logo-link {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-logo-icon {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .nav-item {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
padding: 6px var(--spacing-sm);
|
|
|
|
|
border-left-width: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .nav-icon {
|
|
|
|
|
width: 18px;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.collapsed .sidebar-header {
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
.sidebar-overlay {
|
|
|
|
|
display: block;
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
z-index: 40;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-sidebar {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-settings-drawer {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-layout {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.media-controls {
|
|
|
|
|
position: static;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 22:42:47 +00:00
|
|
|
/* Canvas panel */
|
|
|
|
|
.canvas-panel {
|
|
|
|
|
width: 45%;
|
|
|
|
|
max-width: 720px;
|
|
|
|
|
flex-shrink: 1;
|
|
|
|
|
border-left: 1px solid var(--color-border-subtle);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-tabs {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
scrollbar-width: thin;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-tab {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: all 150ms;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-tab:hover { border-color: var(--color-border-default); }
|
|
|
|
|
.canvas-panel-tab.active {
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-tab span {
|
|
|
|
|
max-width: 100px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.canvas-toggle-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.canvas-toggle-btn {
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
border: none;
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 150ms;
|
|
|
|
|
}
|
|
|
|
|
.canvas-toggle-btn.active {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-body {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: auto;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
min-height: 0;
|
|
|
|
|
}
|
|
|
|
|
.canvas-panel-body pre {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Artifact card (inline in messages) */
|
|
|
|
|
.artifact-card {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
margin: var(--spacing-sm) 0;
|
|
|
|
|
transition: border-color 150ms;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card:hover {
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-icon {
|
|
|
|
|
font-size: 1.1rem;
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-title {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-lang {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-actions button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 4px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
transition: all 150ms;
|
|
|
|
|
}
|
|
|
|
|
.artifact-card-actions button:hover {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
background: var(--color-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Resource cards (below agent messages) */
|
|
|
|
|
.resource-cards {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
margin-top: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.resource-card {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
transition: border-color 150ms;
|
|
|
|
|
}
|
|
|
|
|
.resource-card:hover {
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.resource-card-thumb {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
.resource-card-label {
|
|
|
|
|
max-width: 120px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.resource-cards-more {
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px dashed var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.resource-cards-more:hover {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Canvas preview types */
|
|
|
|
|
.canvas-preview-iframe {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 600px;
|
|
|
|
|
height: calc(100vh - 200px);
|
|
|
|
|
border: none;
|
|
|
|
|
background: white;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
.canvas-preview-image {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
.canvas-preview-svg {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.canvas-preview-svg svg {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
}
|
|
|
|
|
.canvas-preview-markdown {
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.canvas-audio-wrapper {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.canvas-audio-icon {
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
.canvas-url-card {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.canvas-url-card a {
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Canvas mode toggle */
|
|
|
|
|
.canvas-mode-toggle {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.canvas-mode-toggle .canvas-mode-label {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.canvas-mode-toggle .toggle {
|
|
|
|
|
transform: scale(0.8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.canvas-panel {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
width: 100%;
|
|
|
|
|
z-index: 50;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-05 20:47:12 +00:00
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.card-grid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-bar {
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
padding-bottom: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-header {
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-header-title {
|
|
|
|
|
max-width: 120px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-empty-hints {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
|
|
|
|
|
.chat-empty-suggestions {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
}
|
2026-03-05 20:47:12 +00:00
|
|
|
}
|
2026-03-11 06:30:49 +00:00
|
|
|
|
|
|
|
|
/* MCP App Frame */
|
|
|
|
|
.mcp-app-frame-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin: var(--spacing-sm) 0;
|
|
|
|
|
border-radius: var(--border-radius-md);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mcp-app-iframe {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border: none;
|
|
|
|
|
display: block;
|
|
|
|
|
min-height: 100px;
|
|
|
|
|
max-height: 600px;
|
|
|
|
|
transition: height 0.2s ease;
|
|
|
|
|
background: var(--color-bg-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mcp-app-error {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
color: var(--color-text-danger, #e53e3e);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mcp-app-reconnect-overlay {
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border-top: 1px solid var(--color-border-subtle);
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
|
|
|
|
|
/* Confirm Dialog */
|
|
|
|
|
.confirm-dialog-backdrop {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
z-index: 1050;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: var(--color-modal-backdrop);
|
|
|
|
|
backdrop-filter: blur(4px);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
animation: fadeIn var(--duration-normal) var(--ease-spring);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.confirm-dialog {
|
|
|
|
|
background: var(--color-bg-secondary);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
border: 1px solid var(--color-border-strong);
|
2026-03-19 20:40:51 +00:00
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
max-width: 420px;
|
|
|
|
|
width: 90%;
|
|
|
|
|
padding: var(--spacing-lg);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
box-shadow: var(--shadow-md);
|
|
|
|
|
animation: popIn var(--duration-slow) var(--ease-spring);
|
2026-03-19 20:40:51 +00:00
|
|
|
will-change: transform, opacity;
|
|
|
|
|
}
|
|
|
|
|
@keyframes slideUp {
|
|
|
|
|
from { opacity: 0; transform: translateY(8px); }
|
|
|
|
|
to { opacity: 1; transform: translateY(0); }
|
|
|
|
|
}
|
|
|
|
|
.confirm-dialog-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.confirm-dialog-danger-icon {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
font-size: 1.125rem;
|
|
|
|
|
}
|
|
|
|
|
.confirm-dialog-title {
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.confirm-dialog-body {
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
.confirm-dialog-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
.confirm-dialog-actions .btn-danger {
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-error);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
color: var(--color-text-inverse);
|
|
|
|
|
border: 1px solid var(--color-error);
|
|
|
|
|
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), var(--shadow-inset-hi);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
.confirm-dialog-actions .btn-danger:hover:not(:disabled) {
|
|
|
|
|
filter: brightness(1.06);
|
|
|
|
|
transform: translateY(-1px);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Home page */
|
|
|
|
|
.home-page {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
max-width: 52rem;
|
2026-03-19 20:40:51 +00:00
|
|
|
margin: 0 auto;
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-2xl) var(--spacing-xl);
|
2026-03-19 20:40:51 +00:00
|
|
|
width: 100%;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-md);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-hero {
|
|
|
|
|
text-align: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-sm) 0 var(--spacing-md);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-logo {
|
2026-04-14 21:53:03 +00:00
|
|
|
width: 72px;
|
2026-03-19 20:40:51 +00:00
|
|
|
height: auto;
|
2026-04-14 21:53:03 +00:00
|
|
|
margin: 0 auto;
|
2026-03-19 20:40:51 +00:00
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Home resource bar - prominent */
|
|
|
|
|
.home-resource-bar {
|
|
|
|
|
width: 100%;
|
2026-04-14 21:53:03 +00:00
|
|
|
max-width: 420px;
|
|
|
|
|
padding: var(--spacing-md) var(--spacing-lg);
|
|
|
|
|
background: var(--color-surface-raised);
|
2026-03-19 20:40:51 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-resource-bar-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
font-size: var(--text-sm);
|
2026-03-19 20:40:51 +00:00
|
|
|
color: var(--color-text-secondary);
|
2026-04-14 21:53:03 +00:00
|
|
|
margin-bottom: var(--spacing-sm);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-resource-label {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
color: var(--color-text-primary);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-resource-pct {
|
|
|
|
|
margin-left: auto;
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
font-size: var(--text-xs);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-resource-track {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 6px;
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border-radius: var(--radius-full);
|
2026-03-19 20:40:51 +00:00
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.home-resource-fill {
|
|
|
|
|
height: 100%;
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-full);
|
2026-03-19 20:40:51 +00:00
|
|
|
transition: width 500ms ease;
|
|
|
|
|
}
|
2026-03-31 15:37:58 +00:00
|
|
|
.home-cluster-status {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
2026-03-31 15:37:58 +00:00
|
|
|
color: var(--color-text-muted);
|
2026-04-14 21:53:03 +00:00
|
|
|
margin-top: var(--spacing-sm);
|
2026-03-31 15:37:58 +00:00
|
|
|
}
|
|
|
|
|
.home-cluster-dot {
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 6px;
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-full);
|
2026-03-31 15:37:58 +00:00
|
|
|
background: var(--color-success);
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
|
|
|
|
|
/* Home chat card */
|
|
|
|
|
.home-chat-card {
|
|
|
|
|
width: 100%;
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-raised);
|
2026-03-19 20:40:51 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-xl);
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
box-shadow: var(--shadow-md);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-model-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
2026-04-14 21:53:03 +00:00
|
|
|
margin-bottom: var(--spacing-md);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-file-tags {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
margin-bottom: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.home-file-tag {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: 6px;
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
background: var(--color-surface-sunken);
|
2026-03-19 20:40:51 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-full);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
2026-03-19 20:40:51 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.home-file-tag button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
}
|
2026-04-14 21:53:03 +00:00
|
|
|
.home-file-tag button:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
2026-03-19 20:40:51 +00:00
|
|
|
|
|
|
|
|
/* Home input container */
|
|
|
|
|
.home-input-container {
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-surface-sunken);
|
2026-03-19 20:40:51 +00:00
|
|
|
border: 1px solid var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.home-input-container:focus-within {
|
2026-04-14 21:53:03 +00:00
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
box-shadow: 0 0 0 3px var(--color-primary-light);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-textarea {
|
|
|
|
|
width: 100%;
|
|
|
|
|
background: transparent;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
font-size: var(--text-base);
|
2026-03-19 20:40:51 +00:00
|
|
|
font-family: inherit;
|
|
|
|
|
outline: none;
|
|
|
|
|
resize: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
min-height: 84px;
|
|
|
|
|
line-height: var(--leading-normal);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-textarea::placeholder {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.home-input-footer {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-xs) var(--spacing-sm) var(--spacing-xs) var(--spacing-md);
|
|
|
|
|
border-top: 1px solid var(--color-border-divider);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-attach-buttons {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
.home-attach-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 6px 8px;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
transition: color var(--duration-fast), background var(--duration-fast);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-attach-btn:hover {
|
|
|
|
|
color: var(--color-primary);
|
2026-04-14 21:53:03 +00:00
|
|
|
background: var(--color-primary-light);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-input-hint {
|
|
|
|
|
flex: 1;
|
|
|
|
|
text-align: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
2026-03-19 20:40:51 +00:00
|
|
|
color: var(--color-text-muted);
|
2026-04-14 21:53:03 +00:00
|
|
|
letter-spacing: 0.02em;
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-send-btn {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
width: 34px;
|
|
|
|
|
height: 34px;
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: var(--color-primary-text);
|
|
|
|
|
border: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: var(--text-sm);
|
2026-03-19 20:40:51 +00:00
|
|
|
cursor: pointer;
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
transition: background var(--duration-fast), transform 100ms, box-shadow var(--duration-fast);
|
2026-03-19 20:40:51 +00:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.home-send-btn:hover:not(:disabled) {
|
|
|
|
|
background: var(--color-primary-hover);
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
|
|
|
|
.home-send-btn:disabled {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
.home-send-btn:active:not(:disabled) {
|
|
|
|
|
transform: scale(0.92);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Home quick links */
|
|
|
|
|
.home-quick-links {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.home-link-btn {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
2026-04-14 21:53:03 +00:00
|
|
|
padding: 8px var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-raised);
|
2026-03-19 20:40:51 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-full);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
2026-03-19 20:40:51 +00:00
|
|
|
font-family: inherit;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-decoration: none;
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle);
|
2026-03-19 20:40:51 +00:00
|
|
|
transition: all var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.home-link-btn:hover {
|
|
|
|
|
border-color: var(--color-primary-border);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
transform: translateY(-1px);
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-sm);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Home loaded models */
|
|
|
|
|
.home-loaded-models {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-raised);
|
2026-03-19 20:40:51 +00:00
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
2026-03-19 20:40:51 +00:00
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
width: 100%;
|
2026-04-14 21:53:03 +00:00
|
|
|
box-shadow: var(--shadow-subtle);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-loaded-dot {
|
2026-04-14 21:53:03 +00:00
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: var(--radius-full);
|
2026-03-19 20:40:51 +00:00
|
|
|
background: var(--color-success);
|
|
|
|
|
}
|
|
|
|
|
.home-loaded-text {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
color: var(--color-text-primary);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-loaded-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.home-loaded-item {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
2026-04-14 21:53:03 +00:00
|
|
|
gap: 6px;
|
|
|
|
|
padding: 3px 10px;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-divider);
|
2026-03-19 20:40:51 +00:00
|
|
|
border-radius: var(--radius-full);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-xs);
|
feat(react-ui): editorial refresh with Nord palette and polished primitives (#9550)
* feat(react-ui): editorial refresh with Nord palette and polished primitives
Replaces the cool gray-blue theme with a deep Nord-inspired palette:
frost-cyan accent (#88c0d0) on deep blue-black surfaces (#13171f /
#1a1f2a / #242a36), snow-storm text scale, aurora status colours.
- Typography: Geist Variable + Geist Mono Variable (Google Fonts) with
ss01/ss03/cv11 stylistic alternates; strengthened h1-h6 hierarchy;
editorial negative tracking.
- Primitives: buttons gain depth (inset highlight + hover lift +
brightness filter); inputs become sunken wells with sage-swap-to-frost
focus rings; cards hover-lift and gain an .card--accent left-rail
variant; badges become mono caps rectangles with tabular-nums.
- Chrome: sidebar active state is now an inset left rail + tint
(no border-left); modals get popIn animation and proper shadow lift;
toasts carry an inset accent bar + slide-in instead of tinted fills;
operations bar breathes on active installs.
- Empty states: editorial pattern (eyebrow rule, large mono title,
52ch lede) that inherits gracefully even without page JSX edits.
- Chat: assistant bubbles drop the gray-nested-in-gray card for a
transparent pull-quote with a left border; user bubbles soften from
loud accent fill to a subtle frost tint.
- Motion: custom spring easing cubic-bezier(0.22,1,0.36,1), 180ms
standard; breathing/pulse/popIn keyframes; global prefers-reduced-
motion honoring.
- Radii tightened to 3/5/8/10px; warm-shadow tokens redone for cool
depth; ::selection, :focus-visible, kbd globals added.
- Migrated hardcoded 'JetBrains Mono' CSS literals to var(--font-mono)
so the Geist Mono swap lands everywhere.
Scope is intentionally tokens + primitives only. Page JSX and the
~1,800 inline style={{…}} instances are untouched and flagged as
follow-ups.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
* feat(react-ui): complete-coverage pass — migrate inline styles to tokens
Follows up the editorial/Nord token refresh with a mechanical sweep of
page JSX and shared components so nothing bypasses the design system.
- Font family: replaced 80+ 'JetBrains Mono' / 'Space Grotesk' inline
literals (and the string-CSS variants in CollectionDetails and
AgentStatus) with var(--font-mono) / var(--font-sans). SVG <text>
nodes that used the attribute form were switched to style={{ }} so
the CSS variable resolves.
- Radii: every unquoted numeric borderRadius (2/3/4/10) is now a
var(--radius-*) token; 50% and 999px kept as computed shapes.
- Spacing: clean-token gaps and margins (4/8/16px) moved to
var(--spacing-xs/sm/md); padding: '4px 8px' and '8px 16px' lifted
into token pairs. Micro-values (2/6/10/12px) left inline where no
token maps cleanly.
- Colors: Talk.jsx button/canvas-surface hardcodes moved to
var(--color-*); FineTune.jsx chart series colours now use the
--color-data-* Nord palette (cyan/red/purple/orange instead of
tailwind hex); AgentStatus tool-call icon and error tag hex swapped
for var(--color-warning) / var(--color-text-inverse).
- CodeMirror editor (utils/cmTheme.js): both themes rebased on Nord —
polar-night surfaces and aurora syntax highlighting (dark), snow-
storm surfaces with darkened aurora (light). Caret/selection/active
line/search now frost-cyan tinted instead of legacy indigo/purple.
Legitimately dynamic styles (computed widths, per-row colours, canvas
2D context fill/stroke for waveform and spectrogram drawing) remain
inline — they can't be expressed as CSS tokens.
29 files, +237/-237 — identity preserved, semantics re-anchored to
the token system.
Assisted-by: Claude:claude-opus-4-7 [Read] [Edit] [Write]
2026-04-24 21:35:59 +00:00
|
|
|
font-family: var(--font-mono);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-loaded-item button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
}
|
|
|
|
|
.home-stop-all {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
background: none;
|
|
|
|
|
border: 1px solid var(--color-error);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Home wizard (no models) */
|
|
|
|
|
.home-wizard {
|
|
|
|
|
max-width: 48rem;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-hero {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: var(--spacing-xl) 0;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-hero h1 {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-2xl);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
letter-spacing: -0.015em;
|
2026-03-19 20:40:51 +00:00
|
|
|
margin-bottom: var(--spacing-sm);
|
2026-04-14 21:53:03 +00:00
|
|
|
color: var(--color-text-primary);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-wizard-hero p {
|
|
|
|
|
color: var(--color-text-secondary);
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-base);
|
|
|
|
|
line-height: var(--leading-normal);
|
2026-03-19 20:40:51 +00:00
|
|
|
}
|
|
|
|
|
.home-wizard-steps {
|
|
|
|
|
margin-bottom: var(--spacing-xl);
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-steps h2 {
|
2026-04-14 21:53:03 +00:00
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
2026-03-19 20:40:51 +00:00
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-step {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
padding: var(--spacing-sm) 0;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-step-num {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
color: white;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-step strong {
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-step p {
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.home-wizard-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
feat: add biometrics UI (#9524)
* feat(react-ui): add Face & Voice Recognition pages
Expose the face and voice biometrics endpoints
(/v1/face/*, /v1/voice/*) through the React UI. Each page has four
tabs driving the six endpoints per modality: Analyze (demographics
with bounding boxes / waveform segments), Compare (verify with a
match gauge and live threshold slider), Enrollment (register /
identify / forget with a top-K matches view), Embedding (raw
vector inspector with sparkline + copy).
MediaInput supports file upload plus live capture: webcam
snap-to-canvas for face, MediaRecorder -> AudioContext ->
16-bit PCM mono WAV transcode for voice (libsndfile on the
backend only handles WAV/FLAC/OGG natively).
Sidebar gets a new Biometrics section feature-gated on
face_recognition / voice_recognition; routes are wrapped in
<RequireFeature>. No new dependencies -- Font Awesome icons
picked from the Free set.
Assisted-by: Claude:Opus 4.7
* fix(localai): accept data URI prefixes with codec/charset params
Browser MediaRecorder produces data URIs like
data:audio/webm;codecs=opus;base64,...
so the pre-';base64,' section can carry multiple parameter
segments. The `^data:([^;]+);base64,` regex in pkg/utils/base64.go
and core/http/endpoints/localai/audio.go only matched exactly one
segment, so recordings straight from the React UI's live-capture
tab failed the strip and then tripped the base64 decoder on the
leading 'data:' literal, surfacing as
"invalid audio base64: illegal base64 data at input byte 4"
Widened both regexes to `^data:[^,]+?;base64,` so any number of
';param=value' segments between the mime type and ';base64,' are
tolerated. Added a regression test covering the MediaRecorder
shape.
Assisted-by: Claude:Opus 4.7
* fix(insightface): scope pack ONNX loading to known manifests
LocalAI's gallery extracts buffalo_* zips flat into the models
directory, which inevitably mixes with ONNX files from other
backends (opencv face engine, MiniFASNet antispoof, WeSpeaker
voice embedding) and older buffalo pack installs. Feeding those
foreign files into insightface's model_zoo.get_model() blows up
inside the router -- it assumes a 4-D NCHW input and indexes
`input_shape[2]` on tensors that aren't shaped like a face model,
raising IndexError mid-load and leaving the backend unusable.
The router's dispatch isn't amenable to per-file try/except alone
(first-file-wins picks det_10g.onnx from buffalo_l even when the
user asked for buffalo_sc -- alphabetical order happens to favour
the wrong pack). Instead, ship an explicit manifest of the
upstream v0.7 pack contents and scope the glob to that when the
requested pack is known. The manifest is small and stable; future
packs can be added alongside or fall through to the tolerance
loop, which also swallows any remaining IndexError / ValueError
from foreign files with a clear `[insightface] skipped` stderr
line for diagnostics.
Assisted-by: Claude:Opus 4.7
* fix(speaker-recognition): extract FBank features for rank-3 ONNX encoders
Pre-exported speaker-encoder ONNX graphs come in two shapes:
rank-2 [batch, samples] -- some 3D-Speaker exports,
take raw waveform directly.
rank-3 [batch, frames, n_mels] -- WeSpeaker and most Kaldi-
lineage encoders, expect
pre-computed Kaldi FBank.
OnnxDirectEngine unconditionally fed `audio.reshape(1, -1)` --
correct for rank-2, IndexError-on-input_shape[3] on rank-3, which
surfaced to the UI as
"Invalid rank for input: feats Got: 2 Expected: 3"
Detect the input rank at session init and run Kaldi FBank
(80-dim, 25ms/10ms frames, dither=0.0, per-utterance CMN) before
the forward pass when rank>=3. All knobs are configurable via
backend options for encoders that deviate from defaults.
torchaudio.compliance.kaldi is already in the backend's
requirements (SpeechBrain pulls torchaudio in), so no new
dependency.
Assisted-by: Claude:Opus 4.7
* fix(biometrics): isolate face and voice vector stores
Face (ArcFace, 512-D) and voice (ECAPA-TDNN 192-D / WeSpeaker
256-D) biometric embeddings were colliding inside a single
in-memory local-store instance. Enrolling one after the other
failed with
"Try to add key with length N when existing length is M"
because local-store correctly refuses to mix dimensions in one
keyspace.
The registries were constructed with `storeName=""`, which in
StoreBackend() is just a WithModel() call. But ModelLoader's
cache is keyed on `modelID`, not `model` -- so both registries
collapsed to the same `modelID=""` slot and reused the same
backend process despite looking isolated on paper.
Three complementary fixes:
1. application.go -- give each registry a distinct default
namespace ("localai-face-biometrics" /
"localai-voice-biometrics"). The comment claimed
isolation, now it's actually enforced.
2. stores.go -- pass the storeName as both WithModelID and
WithModel so the ModelLoader cache key separates
namespaces and the loader spawns distinct processes.
3. local-store/store.go -- drop the Load() `opts.Model != ""`
guard. It was there to prevent generic model-loading loops
from picking up local-store by accident, but that auto-load
path is being retired; the guard now just blocks legitimate
namespace isolation. opts.Model is treated as a tag; the
per-tuple process isolation upstream handles discrimination.
Assisted-by: Claude:Opus 4.7
* fix(gallery): stale-file cleanup and upgrade-tmp directory safety
Two related robustness fixes for backend install/upgrade:
pkg/downloader/uri.go
OCI downloads passed through
if filepath.Ext(filePath) != "" ...
filePath = filepath.Dir(filePath)
which was intended to redirect file-shaped download targets
into their parent directory for OCI extraction. The heuristic
misfires on directory-shaped paths with a dot-suffix --
gallery.UpgradeBackend uses
tmpPath = "<backendsPath>/<name>.upgrade-tmp"
and Go's filepath.Ext treats ".upgrade-tmp" as an extension.
The rewrite landed the extraction at "<backendsPath>/", which
then **overwrote the real install** (backends/<name>/) with a
flat-layout file and left a stray run.sh at the top level. The
tmp dir itself stayed empty, so the validation step that
checked "<tmpPath>/run.sh" predictably failed with
"upgrade validation failed: run.sh not found in new backend"
Every manual upgrade silently corrupted the backends tree this
way. Guard the rewrite behind "target isn't already an existing
directory" -- InstallBackend / UpgradeBackend both pre-create
the target as a directory, so they get the correct behaviour;
existing file-path callers with a genuine dot-extension still
get the parent redirect.
core/gallery/backends.go
InstallBackend's MkdirAll returned ENOTDIR when something at
the target path was already a file (legacy dev builds dropped
golang backend binaries directly at `<backendsPath>/<name>`
instead of nesting them under their own subdir). That
permanently blocked reinstall and upgrade for anyone carrying
that state, since every retry hit the same error. Detect a
pre-existing non-directory, warn, and remove it before the
MkdirAll so the fresh install can write the correct nested
layout with metadata.json + run.sh.
Assisted-by: Claude:Opus 4.7
* fix(galleryop): refresh upgrade cache after backend ops
UpgradeChecker caches the last upgrade-check result and only
refreshes on the 6-hour tick or after an auto-upgrade cycle.
Manual upgrades (POST /api/backends/upgrade/:name) go through
the async galleryop worker, which completes the upgrade
correctly but never tells UpgradeChecker to re-check -- so
/api/backends/upgrades continued to list a just-upgraded backend
as upgradeable, indistinguishable from a failed upgrade, for up
to six hours.
Add an optional `OnBackendOpCompleted func()` hook on
GalleryService that fires after every successful install /
upgrade / delete on the backend channel (async, so a slow
callback doesn't stall the queue). startup.go wires it to
UpgradeChecker.TriggerCheck after both services exist. Result:
the upgrade banner clears within milliseconds of the worker
finishing.
Assisted-by: Claude:Opus 4.7
* build: prepend GOPATH/bin to PATH for protogen-go
install-go-tools runs `go install` for protoc-gen-go and
protoc-gen-go-grpc, which writes them into `go env GOPATH`/bin.
That directory isn't on every dev's PATH, and protoc resolves
its code-gen plugins via PATH, so the immediately-following
protoc invocation fails with
"protoc-gen-go: program not found"
which in turn blocks `make build` and any
`make backends/%` target that depends on build.
Prepend `go env GOPATH`/bin to PATH for the protoc invocation
so the freshly-installed plugins are found without requiring a
shell-profile change.
Assisted-by: Claude:Opus 4.7
* refactor(ui-api): non-blocking backend upgrade handler with opcache
POST /api/backends/upgrade/:name used to send the ManagementOp
directly onto the unbuffered BackendGalleryChannel, which blocked
the HTTP request whenever the galleryop worker was busy with a
prior operation. The op also didn't show up in /api/operations,
so the Backends UI couldn't reflect upgrade progress on the
affected row.
Register the op in opcache immediately, wrap it in a cancellable
context, store the cancellation function on the GalleryService,
and push onto the channel from a goroutine so the handler
returns right away. Response gains a `jobID` field and a
`message` string so clients have a consistent handle regardless
of whether the op is queued or running.
Pairs with the OnBackendOpCompleted hook added in the galleryop
commit — together the UI sees the upgrade start, watches
progress via /api/operations, and drops the "upgradeable" flag
the moment the worker finishes.
Assisted-by: Claude:Opus 4.7
2026-04-24 06:50:34 +00:00
|
|
|
/* ──────────────────── Biometrics (face + voice recognition) ──────────────────── */
|
|
|
|
|
|
|
|
|
|
.biometrics-page {
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
max-width: 1320px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
width: 100%;
|
|
|
|
|
animation: fadeIn var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-page__header {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr minmax(240px, 320px);
|
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
align-items: end;
|
|
|
|
|
margin-bottom: var(--spacing-lg);
|
|
|
|
|
padding-bottom: var(--spacing-md);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-divider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-page__header .page-title i {
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-page__model {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-page__body {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 720px) {
|
|
|
|
|
.biometrics-page__header {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tabs — flat, underlined, inherit page tone */
|
|
|
|
|
.biometrics-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
border-bottom: 1px solid var(--color-border-subtle);
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
scrollbar-width: none;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-tabs::-webkit-scrollbar { display: none; }
|
|
|
|
|
|
|
|
|
|
.biometrics-tab {
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 0;
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
border-bottom: 2px solid transparent;
|
|
|
|
|
min-height: 44px;
|
|
|
|
|
transition: color var(--duration-fast), border-color var(--duration-fast);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-tab:hover { color: var(--color-text-primary); }
|
|
|
|
|
.biometrics-tab.active {
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
border-bottom-color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-tab i { color: var(--color-accent); font-size: 0.9em; }
|
|
|
|
|
|
|
|
|
|
/* Two-column workflow layout */
|
|
|
|
|
.biometrics-twocol {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: minmax(300px, 380px) 1fr;
|
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
align-items: start;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 980px) {
|
|
|
|
|
.biometrics-twocol { grid-template-columns: 1fr; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-panel {
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-lg);
|
|
|
|
|
box-shadow: var(--shadow-subtle), var(--shadow-inset-top);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-panel__title {
|
|
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-panel__title i { color: var(--color-accent); }
|
|
|
|
|
.biometrics-panel__note {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-results {
|
|
|
|
|
min-width: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-empty {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px dashed var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-2xl) var(--spacing-lg);
|
|
|
|
|
text-align: center;
|
|
|
|
|
min-height: 300px;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-empty > i {
|
|
|
|
|
font-size: 2.5rem;
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-empty h3 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-empty p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
max-width: 48ch;
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Media input — file / webcam / record switcher */
|
|
|
|
|
.biometrics-mediainput {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__tabs {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
padding: 2px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__tab {
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 0;
|
|
|
|
|
font: inherit;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
min-height: 32px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
transition: background var(--duration-fast), color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__tab:hover:not(:disabled) { color: var(--color-text-primary); }
|
|
|
|
|
.biometrics-mediainput__tab.active {
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
box-shadow: var(--shadow-subtle);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__tab:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__body {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__live {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__video {
|
|
|
|
|
width: 100%;
|
|
|
|
|
aspect-ratio: 4 / 3;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__controls .btn { flex: 1; min-height: 40px; }
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__meter {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__meter i { color: var(--color-text-muted); }
|
|
|
|
|
.biometrics-mediainput__meter.recording {
|
|
|
|
|
border-color: var(--color-error-border);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__meter.recording i {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
animation: biometrics-pulse 1.2s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
@keyframes biometrics-pulse {
|
|
|
|
|
0%, 100% { opacity: 1; }
|
|
|
|
|
50% { opacity: 0.35; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__error {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__notice {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
background: var(--color-warning-light);
|
|
|
|
|
border: 1px solid var(--color-warning-border);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__notice > i {
|
|
|
|
|
color: var(--color-warning);
|
|
|
|
|
margin-top: 3px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__notice strong {
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__notice p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__notice code {
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
font-size: 0.95em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__preview {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__preview img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-height: 220px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__preview audio { width: 100%; }
|
|
|
|
|
|
|
|
|
|
.biometrics-mediainput__preview-meta {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__source-pill {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__clear {
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 0;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
min-width: 32px;
|
|
|
|
|
min-height: 32px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
transition: color var(--duration-fast), background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-mediainput__clear:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fieldsets + chip toggles (attribute actions) */
|
|
|
|
|
.biometrics-fieldset {
|
|
|
|
|
border: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-fieldset legend {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-chipset {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-chip {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
text-transform: capitalize;
|
|
|
|
|
transition: border-color var(--duration-fast), color var(--duration-fast), background var(--duration-fast);
|
|
|
|
|
min-height: 32px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-chip input { position: absolute; opacity: 0; pointer-events: none; }
|
|
|
|
|
.biometrics-chip:hover { color: var(--color-text-primary); }
|
|
|
|
|
.biometrics-chip.active {
|
|
|
|
|
border-color: var(--color-accent-border);
|
|
|
|
|
background: var(--color-accent-light);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Toggle switch */
|
|
|
|
|
.biometrics-switch {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 22px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-switch input {
|
|
|
|
|
position: absolute;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-switch > span {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: var(--color-toggle-off);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
transition: background var(--duration-fast);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-switch > span::after {
|
|
|
|
|
content: "";
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 2px;
|
|
|
|
|
top: 2px;
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: #fff;
|
|
|
|
|
transition: transform var(--duration-fast);
|
|
|
|
|
box-shadow: var(--shadow-subtle);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-switch input:checked + span { background: var(--color-accent); }
|
|
|
|
|
.biometrics-switch input:checked + span::after { transform: translateX(18px); }
|
|
|
|
|
.biometrics-switch input:focus-visible + span {
|
|
|
|
|
outline: 2px solid var(--color-border-focus);
|
|
|
|
|
outline-offset: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Split view for analyze (image + summary side) */
|
|
|
|
|
.biometrics-split {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: minmax(0, 1.1fr) minmax(280px, 1fr);
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
align-items: start;
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 980px) {
|
|
|
|
|
.biometrics-split { grid-template-columns: 1fr; }
|
|
|
|
|
}
|
|
|
|
|
.biometrics-split__media {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-split__aside {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Bounding box overlay */
|
|
|
|
|
.biometrics-bbox {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
line-height: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-bbox img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: auto;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-bbox__box {
|
|
|
|
|
position: absolute;
|
|
|
|
|
border: 2px solid var(--color-accent);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25), 0 0 12px rgba(232, 168, 124, 0.35);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition: border-color var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-bbox__box.tone-default { border-color: var(--color-border-strong); box-shadow: none; }
|
|
|
|
|
.biometrics-bbox__box.tone-success { border-color: var(--color-success); }
|
|
|
|
|
.biometrics-bbox__box.tone-error { border-color: var(--color-error); }
|
|
|
|
|
.biometrics-bbox__box.tone-warning { border-color: var(--color-warning); }
|
|
|
|
|
.biometrics-bbox__tag {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: -2px;
|
|
|
|
|
top: -2px;
|
|
|
|
|
transform: translateY(-100%);
|
|
|
|
|
background: var(--color-bg-overlay);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-bottom: 0;
|
|
|
|
|
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
line-height: var(--leading-snug);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-bbox__tag strong { font-weight: var(--font-weight-semibold); }
|
|
|
|
|
.biometrics-bbox__tag span { color: var(--color-text-secondary); }
|
|
|
|
|
|
|
|
|
|
.biometrics-facepicker {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-facepicker__chip {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
min-height: 32px;
|
|
|
|
|
transition: border-color var(--duration-fast), color var(--duration-fast), background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-facepicker__chip:hover { color: var(--color-text-primary); }
|
|
|
|
|
.biometrics-facepicker__chip.active {
|
|
|
|
|
border-color: var(--color-accent-border);
|
|
|
|
|
background: var(--color-accent-light);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-facepicker__chip small { margin-left: 4px; color: var(--color-text-muted); }
|
|
|
|
|
|
|
|
|
|
/* Summary card (dominant attributes) */
|
|
|
|
|
.biometrics-summary {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
margin-bottom: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__head h3 {
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__head h3 i { color: var(--color-accent); }
|
|
|
|
|
.biometrics-summary__head h3 small {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-weight: var(--font-weight-regular);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: max-content 1fr;
|
|
|
|
|
column-gap: var(--spacing-md);
|
|
|
|
|
row-gap: 6px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__grid dt {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
align-self: center;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-summary__grid dd {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Distribution bars */
|
|
|
|
|
.biometrics-dist {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__head h3 {
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
letter-spacing: -0.005em;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__head i { color: var(--color-accent); }
|
|
|
|
|
.biometrics-dist__dominant {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
text-transform: capitalize;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__rows {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__row {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: minmax(80px, 110px) 1fr max-content;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__label {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
text-transform: capitalize;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__bar-wrap {
|
|
|
|
|
height: 6px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__bar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-text-muted);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
transition: width var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__row.dominant .biometrics-dist__label { color: var(--color-text-primary); }
|
|
|
|
|
.biometrics-dist__row.dominant .biometrics-dist__bar { background: var(--color-accent); }
|
|
|
|
|
.biometrics-dist__value {
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-dist__row.dominant .biometrics-dist__value { color: var(--color-text-primary); }
|
|
|
|
|
|
|
|
|
|
/* Pill chips (liveness) */
|
|
|
|
|
.biometrics-pill {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-pill small {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-pill.good {
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
border-color: var(--color-success-border);
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-pill.bad {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
border-color: var(--color-error-border);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-pill.muted { color: var(--color-text-muted); }
|
|
|
|
|
|
|
|
|
|
/* Compare view */
|
|
|
|
|
.biometrics-compare {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr minmax(280px, 360px) 1fr;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
}
|
|
|
|
|
@media (max-width: 1080px) {
|
|
|
|
|
.biometrics-compare { grid-template-columns: 1fr; }
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__panel {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__label {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__center {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__threshold {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__threshold label {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__threshold code {
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__threshold input[type="range"] {
|
|
|
|
|
width: 100%;
|
|
|
|
|
accent-color: var(--color-accent);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__hint {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-compare__hint code { color: var(--color-text-secondary); }
|
|
|
|
|
|
|
|
|
|
/* Match gauge */
|
|
|
|
|
.biometrics-gauge {
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
box-shadow: var(--shadow-subtle), var(--shadow-inset-top);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__verdict {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
font-size: var(--text-lg);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge.tone-success .biometrics-gauge__verdict { color: var(--color-success); }
|
|
|
|
|
.biometrics-gauge.tone-error .biometrics-gauge__verdict { color: var(--color-error); }
|
|
|
|
|
.biometrics-gauge__confidence {
|
|
|
|
|
text-align: right;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
line-height: var(--leading-tight);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__confidence strong {
|
|
|
|
|
display: block;
|
|
|
|
|
font-size: var(--text-xl);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__confidence span {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__track {
|
|
|
|
|
position: relative;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__zone {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
transition: width var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__zone--match {
|
|
|
|
|
left: 0;
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
border-right: 1px dashed var(--color-success-border);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__zone--miss {
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__threshold {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
width: 2px;
|
|
|
|
|
background: var(--color-border-strong);
|
|
|
|
|
transform: translateX(-1px);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__threshold span {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 100%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
padding: 1px 4px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__marker {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: -4px;
|
|
|
|
|
bottom: -4px;
|
|
|
|
|
width: 12px;
|
|
|
|
|
transform: translateX(-6px);
|
|
|
|
|
background: var(--color-text-primary);
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
border: 2px solid var(--color-surface-raised);
|
|
|
|
|
transition: left var(--duration-normal) var(--ease-default);
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__marker span {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 100%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
padding-top: 4px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__footer {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__footer em {
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
font-style: normal;
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-gauge__footer code {
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Waveform */
|
|
|
|
|
.biometrics-waveform {
|
|
|
|
|
--biometrics-wave: var(--color-accent);
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-waveform--error {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-waveform__segment {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
background: rgba(232, 168, 124, 0.16);
|
|
|
|
|
border-left: 1px dashed var(--color-accent-border);
|
|
|
|
|
border-right: 1px dashed var(--color-accent-border);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-waveform__segment.tone-info { background: var(--color-info-light); border-color: var(--color-info-border); }
|
|
|
|
|
.biometrics-waveform__segment.tone-success { background: var(--color-success-light); border-color: var(--color-success-border); }
|
|
|
|
|
.biometrics-waveform__segment.tone-warning { background: var(--color-warning-light); border-color: var(--color-warning-border); }
|
|
|
|
|
.biometrics-waveform__segment.tone-accent { background: var(--color-accent-light); border-color: var(--color-accent-border); }
|
|
|
|
|
.biometrics-waveform__seglabel {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 4px;
|
|
|
|
|
left: 4px;
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
background: var(--color-bg-overlay);
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
max-width: calc(100% - 8px);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-waveform__duration {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 8px;
|
|
|
|
|
bottom: 6px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
background: var(--color-bg-overlay);
|
|
|
|
|
padding: 1px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-waveform__loading {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enrollment layout (register + identify + list) */
|
|
|
|
|
.biometrics-enrollgrid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: minmax(300px, 1fr) minmax(300px, 1fr);
|
|
|
|
|
grid-template-areas:
|
|
|
|
|
"register identify"
|
|
|
|
|
"list list";
|
|
|
|
|
gap: var(--spacing-lg);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enrollgrid__register { grid-area: register; }
|
|
|
|
|
.biometrics-enrollgrid__identify { grid-area: identify; }
|
|
|
|
|
.biometrics-enrollgrid__list { grid-area: list; min-width: 0; }
|
|
|
|
|
@media (max-width: 980px) {
|
|
|
|
|
.biometrics-enrollgrid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
grid-template-areas:
|
|
|
|
|
"register"
|
|
|
|
|
"identify"
|
|
|
|
|
"list";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enrollgrid__register form,
|
|
|
|
|
.biometrics-enrollgrid__identify form {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enrollgrid__err {
|
|
|
|
|
margin-top: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-enroll__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-bottom: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__count {
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
font-weight: var(--font-weight-medium);
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
margin-left: var(--spacing-xs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-enroll__grid {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__card {
|
|
|
|
|
position: relative;
|
|
|
|
|
background: var(--color-surface-raised);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
transition: border-color var(--duration-fast), transform var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__card:hover {
|
|
|
|
|
border-color: var(--color-border-default);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__card.highlight {
|
|
|
|
|
border-color: var(--color-accent-border);
|
|
|
|
|
box-shadow: 0 0 0 1px var(--color-accent-border);
|
|
|
|
|
animation: biometrics-highlight 1.4s ease-out;
|
|
|
|
|
}
|
|
|
|
|
@keyframes biometrics-highlight {
|
|
|
|
|
0% { box-shadow: 0 0 0 4px var(--color-accent-light); }
|
|
|
|
|
100% { box-shadow: 0 0 0 1px var(--color-accent-border); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.biometrics-enroll__media {
|
|
|
|
|
aspect-ratio: 1 / 1;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__media img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__media audio {
|
|
|
|
|
width: 90%;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__initials {
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__body { display: flex; flex-direction: column; gap: 4px; }
|
|
|
|
|
.biometrics-enroll__name {
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__labels {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__labels li {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__labels li span {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__meta {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__delete {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 8px;
|
|
|
|
|
right: 8px;
|
|
|
|
|
background: var(--color-bg-overlay);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: opacity var(--duration-fast), color var(--duration-fast), background var(--duration-fast);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__card:hover .biometrics-enroll__delete,
|
|
|
|
|
.biometrics-enroll__card:focus-within .biometrics-enroll__delete { opacity: 1; }
|
|
|
|
|
.biometrics-enroll__delete:hover {
|
|
|
|
|
color: var(--color-error);
|
|
|
|
|
background: var(--color-error-light);
|
|
|
|
|
border-color: var(--color-error-border);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__empty {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-xl);
|
|
|
|
|
border: 1px dashed var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-lg);
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__empty > i {
|
|
|
|
|
font-size: 2rem;
|
|
|
|
|
color: var(--color-accent);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-enroll__empty p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
max-width: 44ch;
|
|
|
|
|
line-height: var(--leading-normal);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Matches list (identify results) */
|
|
|
|
|
.biometrics-matches {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__empty {
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
border: 1px dashed var(--color-border-default);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__row {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 32px 56px 1fr;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: var(--spacing-sm);
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__row.match { border-color: var(--color-success-border); }
|
|
|
|
|
.biometrics-matches__rank {
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__avatar {
|
|
|
|
|
width: 56px;
|
|
|
|
|
height: 56px;
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
|
|
|
.biometrics-matches__body { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
|
|
|
.biometrics-matches__name {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__name strong {
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__badge {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: var(--radius-sm);
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__badge.match {
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__meter {
|
|
|
|
|
height: 4px;
|
|
|
|
|
background: var(--color-bg-tertiary);
|
|
|
|
|
border-radius: var(--radius-full);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__fill {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: var(--color-accent);
|
|
|
|
|
transition: width var(--duration-normal) var(--ease-default);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__row.match .biometrics-matches__fill { background: var(--color-success); }
|
|
|
|
|
.biometrics-matches__meta {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__meta code {
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-matches__preview { width: 100%; }
|
|
|
|
|
|
|
|
|
|
/* Embedding inspector */
|
|
|
|
|
.biometrics-embed {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-embed__head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: var(--spacing-sm);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-embed__title {
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
font-weight: var(--font-weight-semibold);
|
|
|
|
|
}
|
|
|
|
|
.biometrics-embed__meta {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: var(--spacing-md);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-embed__meta strong { color: var(--color-text-primary); font-variant-numeric: tabular-nums; font-weight: var(--font-weight-semibold); }
|
|
|
|
|
.biometrics-embed__meta code { color: var(--color-text-secondary); }
|
|
|
|
|
|
|
|
|
|
/* Response details pane */
|
|
|
|
|
.biometrics-response {
|
|
|
|
|
background: var(--color-bg-secondary);
|
|
|
|
|
border: 1px solid var(--color-border-subtle);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-response summary {
|
|
|
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--spacing-xs);
|
|
|
|
|
list-style: none;
|
|
|
|
|
user-select: none;
|
|
|
|
|
min-height: 40px;
|
|
|
|
|
}
|
|
|
|
|
.biometrics-response summary::-webkit-details-marker { display: none; }
|
|
|
|
|
.biometrics-response summary i { transition: transform var(--duration-fast); }
|
|
|
|
|
.biometrics-response[open] summary i { transform: rotate(90deg); }
|
|
|
|
|
.biometrics-response pre {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: var(--spacing-md);
|
|
|
|
|
background: var(--color-surface-sunken);
|
|
|
|
|
font-size: var(--text-xs);
|
|
|
|
|
color: var(--color-text-secondary);
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
max-height: 360px;
|
|
|
|
|
line-height: var(--leading-snug);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-label__hint {
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
font-weight: var(--font-weight-regular);
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 20:40:51 +00:00
|
|
|
/* Reduced motion accessibility */
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
|
*, *::before, *::after {
|
|
|
|
|
animation-duration: 0.01ms !important;
|
|
|
|
|
animation-iteration-count: 1 !important;
|
|
|
|
|
transition-duration: 0.01ms !important;
|
|
|
|
|
}
|
|
|
|
|
}
|