LocalAI/core/http/react-ui/src/App.css

6205 lines
141 KiB
CSS
Raw Normal View History

/* 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);
}
.sidebar-is-collapsed .main-content {
margin-left: var(--sidebar-width-collapsed);
}
.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;
min-width: 0;
overflow: hidden;
}
.app-layout-chat .main-content-inner {
overflow: hidden;
min-width: 0;
}
/* Footer */
.app-footer {
background: transparent;
border-top: 1px solid var(--color-border-divider);
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 {
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 {
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;
font-size: var(--text-xs);
color: var(--color-text-muted);
text-decoration: none;
transition: color 150ms;
}
.app-footer-links a:hover {
color: var(--color-text-secondary);
}
.app-footer-copyright {
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.app-footer-copyright a {
color: var(--color-text-muted);
text-decoration: none;
transition: color 150ms;
}
.app-footer-copyright a:hover {
color: var(--color-text-secondary);
}
/* 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);
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);
will-change: transform;
}
.sidebar-overlay {
display: none;
}
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-sm) var(--spacing-sm);
border-bottom: 1px solid var(--color-border-subtle);
min-height: 44px;
}
.sidebar-logo-link {
display: block;
}
.sidebar-logo-img {
width: 100%;
max-width: 120px;
height: auto;
padding: 0 var(--spacing-xs);
}
.sidebar-logo-icon {
display: none;
}
.sidebar-logo-icon-img {
width: 28px;
height: 28px;
}
.sidebar-close-btn {
display: none;
background: none;
border: none;
color: var(--color-text-secondary);
font-size: 1.25rem;
cursor: pointer;
}
.sidebar-nav {
flex: 1;
padding: 2px 0;
overflow-y: auto;
}
.sidebar-section {
padding: 2px 0;
}
.sidebar-section-title {
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-xs);
font-size: var(--text-xs);
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-secondary);
white-space: nowrap;
overflow: hidden;
}
.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 {
color: var(--color-text-primary);
}
.sidebar-section-chevron {
font-size: 0.5rem;
opacity: 0.6;
transition: transform var(--duration-fast), opacity var(--duration-fast);
flex-shrink: 0;
}
.sidebar-section-toggle:hover .sidebar-section-chevron {
opacity: 1;
}
.sidebar-section-toggle.open .sidebar-section-chevron {
transform: rotate(90deg);
}
.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;
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);
color: var(--color-text-secondary);
text-decoration: none;
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);
white-space: nowrap;
overflow: hidden;
}
.nav-item:hover:not(.active) {
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);
}
.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);
}
.nav-icon {
width: 18px;
text-align: center;
flex-shrink: 0;
font-size: 0.85rem;
}
.nav-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 150ms ease;
}
.nav-external {
font-size: 0.55rem;
margin-left: auto;
opacity: 0.5;
flex-shrink: 0;
}
.sidebar-footer {
padding: var(--spacing-xs) var(--spacing-sm);
border-top: 1px solid var(--color-border-subtle);
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-xs);
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;
}
.sidebar-collapse-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
border-radius: var(--radius-md);
font-size: var(--text-sm);
transition: color var(--duration-fast), background var(--duration-fast);
flex-shrink: 0;
}
.sidebar-collapse-btn:hover {
color: var(--color-text-primary);
background: var(--color-surface-hover);
}
/* 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;
}
.sidebar.collapsed .sidebar-section-chevron {
display: none;
}
.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;
}
/* 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;
}
.operation-item {
display: flex;
align-items: center;
gap: var(--spacing-md);
padding: var(--spacing-xs) 0;
}
.operation-info {
display: flex;
align-items: center;
gap: var(--spacing-sm);
flex: 2 1 0;
min-width: 0;
}
.operation-info > .operation-text {
flex: 1 1 auto;
min-width: 0;
}
.operation-spinner {
width: 16px;
height: 16px;
flex-shrink: 0;
box-sizing: border-box;
border: 2px solid var(--color-border-default);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
display: inline-block;
}
.operation-text {
font-size: 0.8125rem;
color: var(--color-text-secondary);
overflow: hidden;
text-overflow: ellipsis;
}
.operation-progress {
font-size: 0.75rem;
color: var(--color-primary);
font-weight: 500;
}
.operation-bar-container {
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;
background: var(--color-surface-sunken);
border-radius: var(--radius-full);
overflow: hidden;
}
.operation-bar {
height: 100%;
background: var(--color-primary);
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;
}
/* 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;
}
.operation-cancel {
flex-shrink: 0;
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
padding: 4px 6px;
font-size: 0.875rem;
}
.operation-cancel:hover {
color: var(--color-error);
}
/* Toast */
.toast-container {
position: fixed;
top: var(--spacing-lg);
right: var(--spacing-lg);
z-index: 1100;
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);
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);
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);
}
.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);
}
.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);
}
.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);
}
.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);
}
.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);
}
.toast-close {
margin-left: auto;
background: none;
border: none;
color: inherit;
opacity: 0.6;
cursor: pointer;
padding: 2px;
}
.toast-close:hover { opacity: 1; }
.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;
}
/* 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 {
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-2xl);
max-width: 1200px;
margin: 0 auto;
width: 100%;
animation: fadeIn var(--duration-normal) var(--ease-default);
}
.page-header {
margin-bottom: var(--spacing-xl);
}
.page-title {
font-size: var(--text-2xl);
font-weight: var(--font-weight-semibold);
letter-spacing: -0.015em;
line-height: var(--leading-tight);
margin-bottom: var(--spacing-xs);
color: var(--color-text-primary);
}
.page-subtitle {
font-size: var(--text-sm);
color: var(--color-text-secondary);
line-height: var(--leading-normal);
}
/* 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);
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);
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);
}
.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);
transform: translateY(-1px);
}
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);
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
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);
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;
gap: var(--spacing-md);
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;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--spacing-xs);
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;
border-radius: var(--radius-md);
font-size: var(--text-sm);
font-family: inherit;
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;
cursor: pointer;
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);
text-decoration: none;
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);
}
/* 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);
border-radius: var(--radius-sm);
}
.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);
}
.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);
}
.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);
color: var(--color-text-primary);
border-color: var(--color-border-default);
}
.btn-secondary:hover:not(:disabled) {
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);
}
.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);
color: var(--color-text-primary);
}
.btn-danger {
background: var(--color-error-light);
color: var(--color-error);
border-color: var(--color-error-border);
}
.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);
}
.btn-sm {
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;
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;
}
.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);
}
.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;
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;
}
/* 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 */
.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);
color: var(--color-text-primary);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-md);
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--text-sm);
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;
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);
}
.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;
}
.input:hover:not(:disabled):not(:focus) {
border-color: var(--color-border-strong);
}
.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);
}
.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;
}
select.input {
cursor: pointer;
padding-right: var(--spacing-xl);
}
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;
}
.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);
color: var(--color-text-primary);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-md);
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--text-sm);
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;
outline: none;
width: 100%;
resize: vertical;
min-height: 80px;
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);
}
.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;
}
.textarea:hover:not(:disabled):not(:focus) {
border-color: var(--color-border-strong);
}
.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);
}
.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;
}
/* CodeMirror editor wrapper */
.code-editor-cm .cm-editor {
border: 1px solid var(--color-border-default);
border-radius: var(--radius-md);
}
.code-editor-cm .cm-editor.cm-focused {
border-color: var(--color-border-strong);
outline: none;
}
/* 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 */
.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;
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;
}
.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;
}
/* 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);
transition: background var(--duration-fast) var(--ease-default);
}
.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;
}
/* 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);
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);
}
/* 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 {
padding: 6px var(--spacing-md);
background: var(--color-surface-raised);
color: var(--color-text-secondary);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-full);
cursor: pointer;
font-size: var(--text-xs);
font-weight: var(--font-weight-medium);
font-family: inherit;
box-shadow: var(--shadow-subtle);
transition: all var(--duration-fast);
}
.filter-btn:hover {
border-color: var(--color-primary-border);
color: var(--color-primary);
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.filter-btn.active {
background: var(--color-primary-light);
color: var(--color-primary);
border-color: var(--color-primary-border);
}
/* 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);
}
/* 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. */
.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);
}
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%; }
.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;
}
.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;
}
.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);
}
/* 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); }
}
@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); }
}
@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); }
}
@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;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
min-width: 0;
}
/* Chat-specific styles */
.chat-layout {
display: flex;
flex: 1;
min-height: 0;
min-width: 0;
overflow: hidden;
position: relative;
}
.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;
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
cursor: pointer;
font-size: var(--text-sm);
color: var(--color-text-secondary);
border-left: 3px solid transparent;
transition: background var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast);
margin-bottom: 2px;
}
.chat-list-item:hover:not(.active) {
background: var(--color-surface-hover);
color: var(--color-text-primary);
}
.chat-list-item.active {
background: var(--color-primary-light);
color: var(--color-primary);
border-left-color: var(--color-primary);
font-weight: var(--font-weight-medium);
}
.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;
overflow: hidden;
}
.chat-messages {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
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%;
min-width: 0;
animation: messageSlideIn 250ms ease-out;
}
.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);
color: var(--color-primary-text);
}
.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);
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;
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;
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);
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);
}
.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);
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);
}
.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);
}
.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);
}
.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);
text-decoration: underline;
text-underline-offset: 2px;
}
.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);
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);
}
.chat-message-system {
align-self: center;
max-width: 90%;
}
.chat-message-system .chat-message-bubble {
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);
}
.chat-message-timestamp {
font-size: 0.6875rem;
color: var(--color-text-muted);
margin-top: 2px;
}
.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;
background: var(--color-surface-raised);
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 {
border-color: var(--color-primary);
}
.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);
color: var(--color-primary-text);
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), transform var(--duration-fast);
}
.chat-send-btn:hover:not(:disabled) {
background: var(--color-primary-hover);
}
.chat-send-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.chat-send-btn:active:not(:disabled) {
transform: scale(0.92);
}
.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;
}
/* 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);
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; }
}
/* 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;
}
/* 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; }
}
/* 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;
}
.chat-empty-suggestions {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-sm);
justify-content: center;
margin-bottom: var(--spacing-lg);
max-width: 520px;
}
.chat-empty-suggestion {
padding: 8px var(--spacing-md);
background: var(--color-surface-raised);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-full);
font-size: var(--text-sm);
font-weight: var(--font-weight-medium);
font-family: inherit;
color: var(--color-text-secondary);
cursor: pointer;
box-shadow: var(--shadow-subtle), var(--shadow-inset-top);
transition: all var(--duration-fast);
}
.chat-empty-suggestion:hover {
border-color: var(--color-primary-border);
color: var(--color-primary);
background: var(--color-surface-raised);
transform: translateY(-1px);
box-shadow: var(--shadow-sm), var(--shadow-inset-top);
}
.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;
}
/* Activity group (thinking + tools collapsed into one line) */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.chat-activity-group {
display: flex;
flex-direction: column;
min-width: 0;
overflow: hidden;
border-left: 2px solid var(--color-border-subtle);
}
.chat-activity-streaming {
border-left-color: var(--color-primary);
}
.chat-activity-toggle {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-sm);
padding: 6px 12px;
background: none;
border: none;
cursor: pointer;
font-family: inherit;
color: var(--color-text-muted);
transition: color 150ms;
width: 100%;
text-align: left;
}
.chat-activity-toggle:hover {
color: var(--color-text-secondary);
}
.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;
font-weight: 600;
color: var(--color-text-muted);
opacity: 0.6;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.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;
overflow-y: auto;
overflow-x: hidden;
line-height: 1.5;
word-break: break-word;
overflow-wrap: anywhere;
min-width: 0;
}
.chat-activity-item-content.chat-activity-live {
max-height: 300px;
}
.chat-activity-item-content p { margin: 0 0 4px; }
.chat-activity-item-content p:last-child { margin-bottom: 0; }
.chat-activity-item-content pre {
background: var(--color-bg-tertiary);
padding: var(--spacing-xs);
border-radius: var(--radius-sm);
overflow-x: auto;
font-size: 0.75rem;
white-space: pre-wrap;
word-break: break-word;
}
.chat-activity-item-content code {
word-break: break-word;
overflow-wrap: anywhere;
}
.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;
}
.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);
font-size: 0.65rem;
}
.chat-activity-params {
display: flex;
flex-direction: column;
gap: 3px;
margin-top: 2px;
}
.chat-activity-param {
display: flex;
gap: 6px;
font-size: 0.675rem;
line-height: 1.4;
word-break: break-word;
}
.chat-activity-param-key {
color: var(--color-text-muted);
flex-shrink: 0;
opacity: 0.7;
}
.chat-activity-param-val {
color: var(--color-text-secondary);
white-space: pre-wrap;
word-break: break-word;
min-width: 0;
}
.chat-activity-param-val-long {
max-height: 80px;
overflow-y: auto;
}
.chat-activity-thinking {
border-left-color: var(--color-info-border);
}
.chat-activity-tool-call {
border-left-color: var(--color-warning-border);
}
.chat-activity-tool-result {
border-left-color: var(--color-success-border);
}
/* 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);
}
/* Chat MCP dropdown */
.chat-mcp-dropdown {
position: relative;
display: inline-block;
}
.chat-mcp-dropdown .btn {
display: flex;
align-items: center;
gap: 5px;
}
.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);
left: 0;
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);
animation: dropdownIn 120ms ease-out;
}
.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);
font-size: 0.75rem;
font-weight: 600;
color: var(--color-text-secondary);
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 {
background: var(--color-success);
}
.chat-client-mcp-status-connecting {
background: var(--color-warning);
animation: pulse 1s infinite;
}
.chat-client-mcp-status-error {
background: var(--color-error);
}
.chat-client-mcp-status-disconnected {
background: var(--color-text-tertiary);
}
/* 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);
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);
will-change: transform;
}
.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 {
background: rgba(59, 130, 246, 0.15);
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;
background: var(--color-border-default);
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;
background: color-mix(in oklab, currentColor 12%, transparent);
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;
}
/* Studio tabs */
.studio-tabs {
display: flex;
gap: var(--spacing-xs);
border-bottom: 1px solid var(--color-border-subtle);
padding: var(--spacing-sm) var(--spacing-xl) 0;
background: var(--color-bg-primary);
position: sticky;
top: 0;
z-index: 10;
}
.studio-tab {
display: flex;
align-items: center;
gap: var(--spacing-xs);
background: none;
border: none;
padding: 10px var(--spacing-md);
font-size: var(--text-sm);
font-family: inherit;
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
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);
font-weight: var(--font-weight-semibold);
}
/* Two-column layout for media generation pages */
.media-layout {
display: grid;
grid-template-columns: minmax(320px, 420px) 1fr;
gap: var(--spacing-lg);
padding: var(--spacing-xl);
max-width: 1280px;
margin: 0 auto;
width: 100%;
align-items: start;
}
@media (max-width: 900px) {
.media-layout {
grid-template-columns: 1fr;
}
}
.media-controls {
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);
position: sticky;
top: var(--spacing-lg);
}
.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;
}
.media-controls .page-header {
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-border-divider);
}
.media-controls .page-title {
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);
}
.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%;
}
/* 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);
}
/* Responsive */
@media (max-width: 1023px) {
.main-content,
.sidebar-is-collapsed .main-content {
margin-left: 0;
}
.mobile-header {
display: flex;
}
.sidebar {
transform: translateX(-100%);
width: var(--sidebar-width);
}
.sidebar.collapsed {
width: var(--sidebar-width);
}
.sidebar.open {
transform: translateX(0);
}
.sidebar-close-btn {
display: block;
}
.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;
}
.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);
}
}
/* 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;
}
}
@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);
}
.chat-empty-suggestions {
flex-direction: column;
align-items: stretch;
}
}
/* 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);
}
/* 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);
}
.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);
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);
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 {
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);
}
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);
}
/* Home page */
.home-page {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 52rem;
margin: 0 auto;
padding: var(--spacing-2xl) var(--spacing-xl);
width: 100%;
gap: var(--spacing-md);
}
.home-hero {
text-align: center;
padding: var(--spacing-sm) 0 var(--spacing-md);
}
.home-logo {
width: 72px;
height: auto;
margin: 0 auto;
display: block;
}
/* Home resource bar - prominent */
.home-resource-bar {
width: 100%;
max-width: 420px;
padding: var(--spacing-md) var(--spacing-lg);
background: var(--color-surface-raised);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-subtle);
}
.home-resource-bar-header {
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-size: var(--text-sm);
color: var(--color-text-secondary);
margin-bottom: var(--spacing-sm);
}
.home-resource-label {
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
}
.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);
font-weight: var(--font-weight-medium);
font-size: var(--text-xs);
}
.home-resource-track {
width: 100%;
height: 6px;
background: var(--color-surface-sunken);
border-radius: var(--radius-full);
overflow: hidden;
}
.home-resource-fill {
height: 100%;
border-radius: var(--radius-full);
transition: width 500ms ease;
}
.home-cluster-status {
display: flex;
align-items: center;
gap: var(--spacing-xs);
font-size: var(--text-xs);
color: var(--color-text-muted);
margin-top: var(--spacing-sm);
}
.home-cluster-dot {
width: 6px;
height: 6px;
border-radius: var(--radius-full);
background: var(--color-success);
display: inline-block;
}
/* Home chat card */
.home-chat-card {
width: 100%;
background: var(--color-surface-raised);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-xl);
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
}
.home-model-row {
display: flex;
align-items: center;
gap: var(--spacing-sm);
margin-bottom: var(--spacing-md);
}
.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;
gap: 6px;
padding: 4px 10px;
background: var(--color-surface-sunken);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-full);
font-size: var(--text-xs);
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;
}
.home-file-tag button:hover {
color: var(--color-error);
}
/* Home input container */
.home-input-container {
background: var(--color-surface-sunken);
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 {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-primary-light);
}
.home-textarea {
width: 100%;
background: transparent;
color: var(--color-text-primary);
border: none;
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
padding: var(--spacing-md);
font-size: var(--text-base);
font-family: inherit;
outline: none;
resize: none;
min-height: 84px;
line-height: var(--leading-normal);
}
.home-textarea::placeholder {
color: var(--color-text-muted);
}
.home-input-footer {
display: flex;
align-items: center;
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);
}
.home-attach-buttons {
display: flex;
gap: 2px;
}
.home-attach-btn {
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
padding: 6px 8px;
font-size: var(--text-sm);
border-radius: var(--radius-md);
transition: color var(--duration-fast), background var(--duration-fast);
}
.home-attach-btn:hover {
color: var(--color-primary);
background: var(--color-primary-light);
}
.home-input-hint {
flex: 1;
text-align: center;
font-size: var(--text-xs);
color: var(--color-text-muted);
letter-spacing: 0.02em;
}
.home-send-btn {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
background: var(--color-primary);
color: var(--color-primary-text);
border: none;
border-radius: var(--radius-full);
font-size: var(--text-sm);
cursor: pointer;
box-shadow: var(--shadow-sm);
transition: background var(--duration-fast), transform 100ms, box-shadow var(--duration-fast);
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);
padding: 8px var(--spacing-md);
background: var(--color-surface-raised);
color: var(--color-text-secondary);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-full);
font-size: var(--text-xs);
font-weight: var(--font-weight-medium);
font-family: inherit;
cursor: pointer;
text-decoration: none;
box-shadow: var(--shadow-subtle);
transition: all var(--duration-fast);
}
.home-link-btn:hover {
border-color: var(--color-primary-border);
color: var(--color-primary);
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
/* Home loaded models */
.home-loaded-models {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--color-surface-raised);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-lg);
font-size: var(--text-xs);
color: var(--color-text-secondary);
width: 100%;
box-shadow: var(--shadow-subtle);
}
.home-loaded-dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: var(--color-success);
}
.home-loaded-text {
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
}
.home-loaded-list {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-xs);
}
.home-loaded-item {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
background: var(--color-surface-sunken);
border: 1px solid var(--color-border-divider);
border-radius: var(--radius-full);
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);
}
.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 {
font-size: var(--text-2xl);
font-weight: var(--font-weight-semibold);
letter-spacing: -0.015em;
margin-bottom: var(--spacing-sm);
color: var(--color-text-primary);
}
.home-wizard-hero p {
color: var(--color-text-secondary);
font-size: var(--text-base);
line-height: var(--leading-normal);
}
.home-wizard-steps {
margin-bottom: var(--spacing-xl);
}
.home-wizard-steps h2 {
font-size: var(--text-lg);
font-weight: var(--font-weight-semibold);
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;
}
/* 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;
}
}