diff --git a/core/http/react-ui/index.html b/core/http/react-ui/index.html index 727105148..ee1a144cf 100644 --- a/core/http/react-ui/index.html +++ b/core/http/react-ui/index.html @@ -7,7 +7,7 @@ - +
diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index debb7bca7..116574ecb 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -143,15 +143,15 @@ width: var(--sidebar-width); height: 100vh; height: 100dvh; - background: var(--color-bg-primary); + 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); - transition: width var(--duration-normal) var(--ease-default), - transform var(--duration-normal) var(--ease-default); + transition: width var(--duration-normal) var(--ease-spring), + transform var(--duration-normal) var(--ease-spring); will-change: transform; } @@ -250,30 +250,32 @@ } .nav-item { + position: relative; display: flex; align-items: center; gap: var(--spacing-sm); - padding: 8px var(--spacing-md) 8px var(--spacing-sm); + 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); - transition: color var(--duration-fast), background var(--duration-fast), border-color var(--duration-fast); - border-left: 3px solid transparent; + 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); - background: var(--color-surface-hover); + background: var(--color-surface-elevated); } .nav-item.active { color: var(--color-primary); background: var(--color-primary-light); - border-left-color: var(--color-primary); - font-weight: var(--font-weight-semibold); + box-shadow: inset 2px 0 0 var(--color-primary); + font-weight: var(--font-weight-medium); } .nav-icon { @@ -492,6 +494,13 @@ padding: var(--spacing-xs) var(--spacing-md); } +.operation-text { + font-family: var(--font-mono); +} +.operation-progress { + font-variant-numeric: tabular-nums; +} + .operation-item { display: flex; align-items: center; @@ -540,7 +549,7 @@ .operation-bar-container { flex: 0 1 160px; min-width: 80px; - height: 4px; + height: 3px; background: var(--color-surface-sunken); border-radius: var(--radius-full); overflow: hidden; @@ -550,7 +559,8 @@ height: 100%; background: var(--color-primary); border-radius: var(--radius-full); - transition: width 300ms var(--ease-default); + 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) */ @@ -602,43 +612,45 @@ align-items: center; gap: var(--spacing-sm); padding: var(--spacing-sm) var(--spacing-md); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-subtle); border-radius: var(--radius-lg); - box-shadow: var(--shadow-lg); - font-size: 0.875rem; - animation: slideIn 200ms ease-out; + 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; - transform: translateX(20px); + transform: translateX(12px); } .toast-exit { opacity: 0; - transform: translateX(20px); - transition: opacity 150ms ease, transform 150ms ease; + transform: translateX(12px); + transition: opacity var(--duration-fast) var(--ease-spring), + transform var(--duration-fast) var(--ease-spring); } .toast-success { - background: var(--color-success-light); - border: 1px solid var(--color-success-border); - color: var(--color-success); + box-shadow: inset 3px 0 0 var(--color-success), var(--shadow-sm); + border-color: var(--color-border-subtle); } .toast-error { - background: var(--color-error-light); - border: 1px solid var(--color-error-border); - color: var(--color-error); + box-shadow: inset 3px 0 0 var(--color-error), var(--shadow-sm); + border-color: var(--color-border-subtle); } .toast-warning { - background: var(--color-warning-light); - border: 1px solid var(--color-warning-border); - color: var(--color-warning); + box-shadow: inset 3px 0 0 var(--color-warning), var(--shadow-sm); + border-color: var(--color-border-subtle); } .toast-info { - background: var(--color-info-light); - border: 1px solid var(--color-info-border); - color: var(--color-info); + box-shadow: inset 3px 0 0 var(--color-info), var(--shadow-sm); + border-color: var(--color-border-subtle); } .toast-close { @@ -845,20 +857,30 @@ /* Cards */ .card { - background: var(--color-surface-raised); + 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); - transition: border-color var(--duration-fast), box-shadow var(--duration-fast), transform var(--duration-fast); + 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 { - border-color: var(--color-border-default); - box-shadow: var(--shadow-md), var(--shadow-inset-top); + border-color: var(--color-border-strong); + box-shadow: var(--shadow-sm), var(--shadow-inset-top); transform: translateY(-1px); } +/* 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)); @@ -1008,7 +1030,7 @@ border: 1px solid var(--color-border-subtle); border-radius: var(--radius-md); padding: var(--spacing-sm) var(--spacing-md); - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-size: var(--text-xs); line-height: var(--leading-snug); color: var(--color-text-secondary); @@ -1283,50 +1305,59 @@ justify-content: center; gap: var(--spacing-xs); padding: 0.5rem var(--spacing-md); - min-height: 36px; + min-height: 34px; border-radius: var(--radius-md); font-size: var(--text-sm); font-family: inherit; font-weight: var(--font-weight-medium); - letter-spacing: 0.005em; + letter-spacing: -0.005em; cursor: pointer; border: 1px solid transparent; - transition: background var(--duration-fast) var(--ease-default), color var(--duration-fast) var(--ease-default), border-color var(--duration-fast) var(--ease-default), box-shadow var(--duration-fast) var(--ease-default), transform var(--duration-fast) var(--ease-default); + 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; - box-shadow: 0 0 0 3px var(--color-border-focus); + 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 { - outline: 2px solid var(--color-border-focus); - outline-offset: 2px; + 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); - box-shadow: var(--shadow-subtle); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25), var(--shadow-inset-hi); } .btn-primary:hover:not(:disabled) { - background: var(--color-primary-hover); - box-shadow: var(--shadow-sm); + 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 { - background: var(--color-surface-raised); + 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); - background: var(--color-primary-light); - color: var(--color-primary); + background: var(--color-surface-hover); + transform: translateY(-1px); } .btn-ghost { @@ -1335,7 +1366,7 @@ border-color: transparent; } .btn-ghost:hover:not(:disabled) { - background: var(--color-surface-hover); + background: var(--color-surface-elevated); color: var(--color-text-primary); } @@ -1348,21 +1379,27 @@ background: var(--color-error); color: var(--color-text-inverse); border-color: var(--color-error); + filter: brightness(1.04); + transform: translateY(-1px); } .btn-sm { padding: 0.35rem var(--spacing-sm); - min-height: 30px; + min-height: 28px; font-size: var(--text-xs); + letter-spacing: 0; } .btn:active:not(:disabled) { - transform: translateY(1px); + filter: brightness(0.95); + transform: translateY(0); } .btn:disabled { - opacity: 0.5; + opacity: 0.45; cursor: not-allowed; + filter: none; + transform: none; } /* Toggle switch */ @@ -1415,68 +1452,84 @@ opacity: 0.5; } -/* Inputs */ +/* Inputs — sunken well, quiet border, sage focus ring */ .input { - background: var(--color-surface-raised); + 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; + letter-spacing: -0.005em; outline: none; width: 100%; - transition: border-color var(--duration-fast), box-shadow var(--duration-fast), background var(--duration-fast); + 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); + opacity: 0.8; } .input:hover:not(:disabled):not(:focus) { border-color: var(--color-border-strong); } .input:focus { border-color: var(--color-primary); - box-shadow: 0 0 0 3px var(--color-primary-light); + 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; + opacity: 0.7; } select.input { cursor: pointer; padding-right: var(--spacing-xl); } +.input-mono { + font-family: var(--font-mono); + font-size: var(--text-sm); + letter-spacing: -0.01em; +} + .textarea { - background: var(--color-surface-raised); + 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; + letter-spacing: -0.005em; outline: none; width: 100%; resize: vertical; min-height: 80px; line-height: var(--leading-normal); - transition: border-color var(--duration-fast), box-shadow var(--duration-fast); + transition: border-color var(--duration-normal) var(--ease-spring), + box-shadow var(--duration-normal) var(--ease-spring); } .textarea::placeholder { color: var(--color-text-muted); + opacity: 0.8; } .textarea:hover:not(:disabled):not(:focus) { border-color: var(--color-border-strong); } .textarea:focus { border-color: var(--color-primary); - box-shadow: 0 0 0 3px var(--color-primary-light); + 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; + opacity: 0.7; } /* CodeMirror editor wrapper */ @@ -1502,15 +1555,20 @@ select.input { font-weight: 500; } -/* Badges */ +/* Badges — sharp editorial rectangles, mono caps */ .badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; - border-radius: var(--radius-full); - font-size: 0.6875rem; + border-radius: var(--radius-sm); + font-family: var(--font-mono); + font-size: 0.625rem; font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + line-height: 1.4; + font-variant-numeric: tabular-nums; } .badge-success { @@ -1553,7 +1611,7 @@ select.input { } .cell-mono { - font-family: 'JetBrains Mono', ui-monospace, monospace; + font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text-primary); } @@ -1679,7 +1737,7 @@ select.input { .stat-card__value { font-size: var(--text-2xl); font-weight: 600; - font-family: 'JetBrains Mono', ui-monospace, monospace; + font-family: var(--font-mono); line-height: 1; color: var(--color-text-primary); word-break: break-word; @@ -2123,7 +2181,7 @@ select.input { } .model-item-name { - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-size: 0.8rem; color: var(--color-text-primary); overflow: hidden; @@ -2366,28 +2424,66 @@ select.input { } /* Empty state */ +/* 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 { - text-align: center; - padding: var(--spacing-3xl, 4rem) var(--spacing-xl); - animation: fadeIn var(--duration-normal) var(--ease-default); + 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); } +/* Center the column within its parent without shrinking type. */ +.empty-state > * { max-width: 100%; } + .empty-state-icon { - font-size: 3rem; - color: var(--color-text-muted); - margin-bottom: var(--spacing-md); + font-size: 1.5rem; + color: var(--color-primary); + opacity: 0.7; + margin: 0; } .empty-state-title { - font-size: 1.25rem; - font-weight: 600; - margin-bottom: var(--spacing-sm); + 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); - font-size: 0.875rem; - margin-bottom: var(--spacing-lg); + 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 */ @@ -2396,18 +2492,43 @@ select.input { } @keyframes slideIn { - from { opacity: 0; transform: translateX(20px); } - to { opacity: 1; transform: translateX(0); } + 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; } - to { opacity: 1; } + 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; } - 50% { opacity: 0.5; } + 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 { @@ -2635,29 +2756,34 @@ select.input { } .chat-message-model { + font-family: var(--font-mono); font-size: 0.625rem; - font-weight: 600; + font-weight: 500; color: var(--color-text-muted); text-transform: uppercase; - letter-spacing: 0.03em; + letter-spacing: 0.18em; padding-left: 2px; } .chat-message-content { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border-subtle); - border-radius: 4px 16px 16px 16px; - padding: var(--spacing-md) var(--spacing-lg); - font-size: 0.875rem; - line-height: 1.6; + 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; + color: var(--color-text-primary); } .chat-message-user .chat-message-content { - background: var(--color-primary); - color: var(--color-primary-text); - border-color: var(--color-primary); + background: var(--color-primary-light); + color: var(--color-text-primary); + border: 1px solid var(--color-primary-border); border-radius: 16px 4px 16px 16px; + padding: var(--spacing-md) var(--spacing-lg); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02); } .chat-message-content pre { @@ -2671,21 +2797,21 @@ select.input { } .chat-message-user .chat-message-content pre { - background: color-mix(in oklab, var(--color-primary-text) 12%, transparent); - border-color: color-mix(in oklab, var(--color-primary-text) 18%, transparent); - color: var(--color-primary-text); + background: var(--color-surface-sunken); + border-color: var(--color-border-subtle); + color: var(--color-text-primary); } .chat-message-user .chat-message-content code { - color: var(--color-primary-text); + color: var(--color-text-primary); } .chat-message-user .chat-message-content a { - color: var(--color-primary-text); + color: var(--color-primary); text-decoration: underline; text-underline-offset: 2px; } .chat-message-content code { - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-size: 0.8125rem; } @@ -2878,7 +3004,7 @@ select.input { font-size: 0.6875rem; color: var(--color-text-muted); padding-top: var(--spacing-xs); - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); display: flex; align-items: center; gap: 4px; @@ -3196,7 +3322,7 @@ select.input { overflow-y: auto; } .chat-activity-item-code code { - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-size: 0.65rem; } .chat-activity-params { @@ -3456,7 +3582,7 @@ select.input { } .chat-model-info-row > span:last-child { color: var(--color-text-primary); - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-size: 0.75rem; max-width: 60%; overflow: hidden; @@ -4387,17 +4513,17 @@ select.input { justify-content: center; background: var(--color-modal-backdrop); backdrop-filter: blur(4px); - animation: fadeIn 150ms ease; + animation: fadeIn var(--duration-normal) var(--ease-spring); } .confirm-dialog { background: var(--color-bg-secondary); - border: 1px solid var(--color-border-subtle); + border: 1px solid var(--color-border-strong); border-radius: var(--radius-lg); max-width: 420px; width: 90%; padding: var(--spacing-lg); - box-shadow: var(--shadow-lg); - animation: slideUp 150ms ease; + box-shadow: var(--shadow-md); + animation: popIn var(--duration-slow) var(--ease-spring); will-change: transform, opacity; } @keyframes slideUp { @@ -4430,13 +4556,15 @@ select.input { justify-content: flex-end; gap: var(--spacing-sm); } -.btn-danger { +.confirm-dialog-actions .btn-danger { background: var(--color-error); - color: white; - border: none; + 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); } -.btn-danger:hover { - background: var(--color-error-hover, #dc2626); +.confirm-dialog-actions .btn-danger:hover:not(:disabled) { + filter: brightness(1.06); + transform: translateY(-1px); } /* Home page */ @@ -4487,7 +4615,7 @@ select.input { } .home-resource-pct { margin-left: auto; - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); font-weight: var(--font-weight-medium); font-size: var(--text-xs); } @@ -4721,7 +4849,7 @@ select.input { border: 1px solid var(--color-border-divider); border-radius: var(--radius-full); font-size: var(--text-xs); - font-family: 'JetBrains Mono', monospace; + font-family: var(--font-mono); } .home-loaded-item button { background: none; diff --git a/core/http/react-ui/src/components/ClientMCPDropdown.jsx b/core/http/react-ui/src/components/ClientMCPDropdown.jsx index 493656997..7be97ba9d 100644 --- a/core/http/react-ui/src/components/ClientMCPDropdown.jsx +++ b/core/http/react-ui/src/components/ClientMCPDropdown.jsx @@ -80,7 +80,7 @@ export default function ClientMCPDropdown({ placeholder="Server URL (e.g. https://mcp.example.com/sse)" value={url} onChange={e => setUrl(e.target.value)} - style={{ width: '100%', marginBottom: '4px' }} + style={{ width: '100%', marginBottom: 'var(--spacing-xs)' }} /> setName(e.target.value)} - style={{ width: '100%', marginBottom: '4px' }} + style={{ width: '100%', marginBottom: 'var(--spacing-xs)' }} /> setAuthToken(e.target.value)} - style={{ width: '100%', marginBottom: '4px' }} + style={{ width: '100%', marginBottom: 'var(--spacing-xs)' }} /> -{t.description}
-
{trace.content}
@@ -71,7 +71,7 @@ function TraceCard({ trace, index }) {
Arguments:
{typeof trace.arguments === 'string' ? trace.arguments : JSON.stringify(trace.arguments, null, 2)}
@@ -207,7 +207,7 @@ export default function AgentJobDetails() {
Job ID
- {job.id}
+ {job.id}
Task
@@ -264,7 +264,7 @@ export default function AgentJobDetails() {
{Object.entries(job.cron_parameters).map(([k, v]) => (
-
+
{k}={v}
))}
@@ -281,7 +281,7 @@ export default function AgentJobDetails() {
{Object.entries(job.parameters).map(([k, v]) => (
-
+
{k}={v}
))}
diff --git a/core/http/react-ui/src/pages/AgentJobs.jsx b/core/http/react-ui/src/pages/AgentJobs.jsx
index 927bcf9ab..807627c5c 100644
--- a/core/http/react-ui/src/pages/AgentJobs.jsx
+++ b/core/http/react-ui/src/pages/AgentJobs.jsx
@@ -253,7 +253,7 @@ export default function AgentJobs() {
Example MCP configuration (YAML):
- {`mcp:
+ {`mcp:
stdio:
- name: my-tool
command: /path/to/tool
@@ -345,7 +345,7 @@ export default function AgentJobs() {
{task.cron ? (
-
+
{task.cron}
) : '-'}
@@ -426,7 +426,7 @@ export default function AgentJobs() {
{filteredJobs.map(job => (
- navigate(`/app/agent-jobs/jobs/${job.id}`)} style={{ cursor: 'pointer', color: 'var(--color-primary)', fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}>
+ navigate(`/app/agent-jobs/jobs/${job.id}`)} style={{ cursor: 'pointer', color: 'var(--color-primary)', fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}>
{job.id?.slice(0, 12)}...
@@ -510,7 +510,7 @@ export default function AgentJobs() {
{(items || []).map(job => (
- {job.id?.slice(0, 12)}...
+ {job.id?.slice(0, 12)}...
{job.task_id || '-'}
{statusBadge(job.status)}
{formatDate(job.created_at)}
@@ -566,7 +566,7 @@ export default function AgentJobs() {
onChange={(e) => setExecuteParams(e.target.value)}
rows={5}
placeholder={`topic=AI trends\nformat=markdown`}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
These will be available as {'{{.parameter_name}}'} in the prompt template.
@@ -590,7 +590,7 @@ export default function AgentJobs() {
{executeMultimedia[type].map((item, i) => (
{item.name || item.url?.slice(0, 40)}
@@ -229,13 +229,13 @@ export default function AgentTaskDetails() {
Execute by name
-
+
{`curl -X POST ${window.location.origin}${basePath}/api/agent/tasks/${encodeURIComponent(task.name)}/execute`}
Execute with multimedia
-
+
{`curl -X POST ${window.location.origin}${basePath}/api/agent/tasks/${encodeURIComponent(task.name)}/execute \\
-H "Content-Type: application/json" \\
-d '{"multimedia": {"images": [{"url": "https://example.com/image.jpg"}]}}'`}
@@ -243,7 +243,7 @@ export default function AgentTaskDetails() {
Check job status
-
+
{`curl ${window.location.origin}${basePath}/api/agent/jobs/`}
@@ -261,7 +261,7 @@ export default function AgentTaskDetails() {
{wh.method || 'POST'}
- {wh.url}
+ {wh.url}
))}
@@ -283,7 +283,7 @@ export default function AgentTaskDetails() {
{jobHistory.map(job => (
-
+
{job.id?.slice(0, 12)}...
{statusBadge(job.status)}
@@ -351,7 +351,7 @@ export default function AgentTaskDetails() {
onChange={(e) => updateField('prompt', e.target.value)}
rows={8}
placeholder={`Write a summary about {{.topic}} in {{.format}} format.`}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
Use {'{{.parameter_name}}'} for dynamic parameters. Parameters are provided when executing the task.
@@ -376,7 +376,7 @@ export default function AgentTaskDetails() {
value={task.cron}
onChange={(e) => { updateField('cron', e.target.value); validateCron(e.target.value) }}
placeholder="0 */6 * * *"
- style={{ fontFamily: "'JetBrains Mono', monospace" }}
+ style={{ fontFamily: 'var(--font-mono)' }}
/>
{cronError &&
{cronError}
}
@@ -392,7 +392,7 @@ export default function AgentTaskDetails() {
onChange={(e) => updateField('cron_parameters', e.target.value)}
rows={3}
placeholder={`topic=daily news\nformat=bullet points`}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
Default parameters used when the cron triggers the task.
@@ -437,7 +437,7 @@ export default function AgentTaskDetails() {
- updateMultimediaSource(i, 'headers', e.target.value)} placeholder='{"Authorization": "Bearer ..."}' style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }} />
+ updateMultimediaSource(i, 'headers', e.target.value)} placeholder='{"Authorization": "Bearer ..."}' style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }} />
))
@@ -479,7 +479,7 @@ export default function AgentTaskDetails() {
- updateWebhook(i, 'headers', e.target.value)} placeholder='{"Content-Type": "application/json"}' style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }} />
+ updateWebhook(i, 'headers', e.target.value)} placeholder='{"Content-Type": "application/json"}' style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }} />
@@ -489,7 +489,7 @@ export default function AgentTaskDetails() {
onChange={(e) => updateWebhook(i, 'payload_template', e.target.value)}
rows={3}
placeholder={`{"text": "Job {{.Status}}: {{if .Error}}Error: {{.Error}}{{else}}{{.Result}}{{end}}"}`}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
Available: {'{{.Job}}'} {'{{.Task}}'} {'{{.Result}}'} {'{{.Error}}'} {'{{.Status}}'}
diff --git a/core/http/react-ui/src/pages/BackendLogs.jsx b/core/http/react-ui/src/pages/BackendLogs.jsx
index d8798d921..614fd6cef 100644
--- a/core/http/react-ui/src/pages/BackendLogs.jsx
+++ b/core/http/react-ui/src/pages/BackendLogs.jsx
@@ -229,7 +229,7 @@ function BackendLogsDetail({ modelId }) {
borderRadius: 'var(--radius-md)',
overflow: 'auto',
maxHeight: 'calc(100vh - 280px)',
- fontFamily: 'JetBrains Mono, Consolas, monospace',
+ fontFamily: 'var(--font-mono)',
fontSize: '0.75rem',
lineHeight: '1.5',
}}
diff --git a/core/http/react-ui/src/pages/Backends.jsx b/core/http/react-ui/src/pages/Backends.jsx
index 26d479ea4..ea34ffdbb 100644
--- a/core/http/react-ui/src/pages/Backends.jsx
+++ b/core/http/react-ui/src/pages/Backends.jsx
@@ -591,7 +591,7 @@ function BackendDetail({ backend }) {
{backend.tags?.length > 0 && (
-
+
{backend.tags.map(tag => (
{tag}
))}
diff --git a/core/http/react-ui/src/pages/CollectionDetails.jsx b/core/http/react-ui/src/pages/CollectionDetails.jsx
index afac4c9b3..200da2af0 100644
--- a/core/http/react-ui/src/pages/CollectionDetails.jsx
+++ b/core/http/react-ui/src/pages/CollectionDetails.jsx
@@ -282,7 +282,7 @@ export default function CollectionDetails() {
.collection-detail-modal-content {
white-space: pre-wrap;
word-break: break-word;
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
font-size: 0.8125rem;
background: var(--color-bg-secondary);
border-radius: var(--radius-md);
diff --git a/core/http/react-ui/src/pages/FineTune.jsx b/core/http/react-ui/src/pages/FineTune.jsx
index fa153efb1..9a58a78e7 100644
--- a/core/http/react-ui/src/pages/FineTune.jsx
+++ b/core/http/react-ui/src/pages/FineTune.jsx
@@ -241,7 +241,7 @@ function SingleMetricChart({ data, valueKey, label, color, formatValue, events }
return (
-
+
{label}
)
}
@@ -483,26 +483,26 @@ function CheckpointsPanel({ job, onResume, onExportCheckpoint }) {
- Step
- Epoch
- Loss
- Created
- Path
- Actions
+ Step
+ Epoch
+ Loss
+ Created
+ Path
+ Actions
{checkpoints.map(cp => (
- {cp.step}
- {cp.epoch?.toFixed(2)}
- {cp.loss?.toFixed(4)}
- {cp.created_at}
-
+ {cp.step}
+ {cp.epoch?.toFixed(2)}
+ {cp.loss?.toFixed(4)}
+ {cp.created_at}
+
{cp.path}
-
-
{/* Use Cases */}
-
diff --git a/core/http/react-ui/src/pages/ModelEditor.jsx b/core/http/react-ui/src/pages/ModelEditor.jsx
index 0d0d1ccff..575bef971 100644
--- a/core/http/react-ui/src/pages/ModelEditor.jsx
+++ b/core/http/react-ui/src/pages/ModelEditor.jsx
@@ -443,7 +443,7 @@ export default function ModelEditor() {
setTab(t)
}}
style={{
- padding: '8px 16px', border: 'none',
+ padding: 'var(--spacing-sm) var(--spacing-md)', border: 'none',
cursor: blocked ? 'not-allowed' : 'pointer',
background: 'transparent', fontSize: '0.875rem',
fontWeight: active ? 600 : 400,
@@ -525,7 +525,7 @@ export default function ModelEditor() {
placeholder="my-model-name"
style={{ maxWidth: 400 }}
/>
-
+
Use letters, numbers, hyphens, underscores, and dots only.
diff --git a/core/http/react-ui/src/pages/Models.jsx b/core/http/react-ui/src/pages/Models.jsx
index 98c8e13d3..3ba7f260c 100644
--- a/core/http/react-ui/src/pages/Models.jsx
+++ b/core/http/react-ui/src/pages/Models.jsx
@@ -41,7 +41,7 @@ function GalleryLoader() {
minHeight: '280px', gap: 'var(--spacing-lg)',
}}>
{/* Animated dots */}
-
+
{[0, 1, 2, 3, 4].map(i => (
{fit !== null && (
-
+
{fit ? 'Fits' : 'May not fit'}
@@ -595,7 +595,7 @@ function ModelDetail({ model, fit, expandedFiles, setExpandedFiles }) {
{model.estimated_vram_display && model.estimated_vram_display !== '0 B' ? (
-
+
{model.estimated_vram_display}
{fit !== null && (
@@ -610,7 +610,7 @@ function ModelDetail({ model, fit, expandedFiles, setExpandedFiles }) {
{model.tags?.length > 0 && (
-
+
{model.tags.map(tag => (
{tag}
))}
@@ -651,17 +651,17 @@ function ModelDetail({ model, fit, expandedFiles, setExpandedFiles }) {
- Filename
- URI
- SHA256
+ Filename
+ URI
+ SHA256
{files.map((f, i) => (
- {f.filename || '—'}
- {f.uri || '—'}
-
+ {f.filename || '—'}
+ {f.uri || '—'}
+
{f.sha256 ? f.sha256.substring(0, 16) + '...' : '—'}
diff --git a/core/http/react-ui/src/pages/NodeBackendLogs.jsx b/core/http/react-ui/src/pages/NodeBackendLogs.jsx
index e6ff4b9b0..7f4841721 100644
--- a/core/http/react-ui/src/pages/NodeBackendLogs.jsx
+++ b/core/http/react-ui/src/pages/NodeBackendLogs.jsx
@@ -222,7 +222,7 @@ export default function NodeBackendLogs() {
borderRadius: 'var(--radius-md)',
overflow: 'auto',
maxHeight: 'calc(100vh - 280px)',
- fontFamily: 'JetBrains Mono, Consolas, monospace',
+ fontFamily: 'var(--font-mono)',
fontSize: '0.75rem',
lineHeight: '1.5',
}}
diff --git a/core/http/react-ui/src/pages/Nodes.jsx b/core/http/react-ui/src/pages/Nodes.jsx
index 0e0698241..5ffecda79 100644
--- a/core/http/react-ui/src/pages/Nodes.jsx
+++ b/core/http/react-ui/src/pages/Nodes.jsx
@@ -92,7 +92,7 @@ function CommandBlock({ command, addToast }) {
{Object.entries(node.labels).slice(0, 5).map(([k, v]) => (
{k}={v}
@@ -709,7 +709,7 @@ export default function Nodes() {
{hasGPU && totalVRAMStr ? (
-
+
{vendorLabel && (
{vendorLabel}
)}
@@ -718,7 +718,7 @@ export default function Nodes() {
) : totalRAMStr ? (
-
+
CPU
{usedRAMStr || '0'} / {totalRAMStr} RAM
@@ -729,7 +729,7 @@ export default function Nodes() {
)}
-
+
{timeAgo(node.last_heartbeat)}
@@ -802,7 +802,7 @@ export default function Nodes() {
const stCfg = modelStateConfig[m.state] || modelStateConfig.idle
return (
-
+
{m.model_name}
@@ -814,7 +814,7 @@ export default function Nodes() {
{m.state}
-
+
{m.in_flight ?? 0}
@@ -878,7 +878,7 @@ export default function Nodes() {
{backends.map(b => (
-
+
{b.name}
@@ -922,9 +922,9 @@ export default function Nodes() {
{node.labels && Object.entries(node.labels).map(([k, v]) => (
{k}={v}
{cfg.model_name}
{modeLabel}
@@ -1032,18 +1032,18 @@ export default function Nodes() {
const sel = typeof cfg.node_selector === 'string' ? JSON.parse(cfg.node_selector) : cfg.node_selector
return Object.entries(sel).map(([k,v]) => (
{k}={v}
))
} catch { return {cfg.node_selector} }
})() : Any node}
-
+
{isAutoScaling ? cfg.min_replicas : '-'}
-
+
{isAutoScaling ? (cfg.max_replicas || 'no limit') : '-'}
diff --git a/core/http/react-ui/src/pages/P2P.jsx b/core/http/react-ui/src/pages/P2P.jsx
index f514b6898..d680e468d 100644
--- a/core/http/react-ui/src/pages/P2P.jsx
+++ b/core/http/react-ui/src/pages/P2P.jsx
@@ -24,7 +24,7 @@ function NodeCard({ node, label, iconColor, iconBg }) {
{label}
-
+
{node.id}
@@ -68,7 +68,7 @@ function CommandBlock({ command, addToast }) {
{token || 'Loading...'}
@@ -459,7 +459,7 @@ export default function P2P() {
-
+
{[1, 2, 3].map(n => (
RPC
-
+
{['Layer 1-10', 'Layer 11-20', 'Layer 21-30'].map((label, i) => (
Ring / JACCL
-
+
{['Layers 1-16', 'Layers 17-32'].map((label, i) => (
GPU {i}
-
-
+
+
{usedPct}%
{formatBytes(gpu.used)} / {formatBytes(gpu.total)}
@@ -231,8 +231,8 @@ export default function Settings() {
const usedPct = resources.ram.total > 0 ? Math.round((resources.ram.used / resources.ram.total) * 100) : 0
return (
<>
-
-
+
+
{usedPct}%
{formatBytes(resources.ram.used)} / {formatBytes(resources.ram.total)}
@@ -380,7 +380,7 @@ export default function Settings() {
onChange={(e) => update('galleries_json', e.target.value)}
rows={4}
placeholder={'[\n { "url": "https://...", "name": "my-gallery" }\n]'}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
@@ -391,7 +391,7 @@ export default function Settings() {
onChange={(e) => update('backend_galleries_json', e.target.value)}
rows={4}
placeholder={'[\n { "url": "https://...", "name": "my-backends" }\n]'}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
@@ -413,7 +413,7 @@ export default function Settings() {
onChange={(e) => update('api_keys_text', e.target.value)}
rows={4}
placeholder="sk-key-1
sk-key-2"
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }}
/>
diff --git a/core/http/react-ui/src/pages/SkillEdit.jsx b/core/http/react-ui/src/pages/SkillEdit.jsx
index c371ba8f0..9d93593c0 100644
--- a/core/http/react-ui/src/pages/SkillEdit.jsx
+++ b/core/http/react-ui/src/pages/SkillEdit.jsx
@@ -191,7 +191,7 @@ function ResourcesSection({ skillName, addToast }) {
value={editor.content}
onChange={(e) => setEditor((x) => ({ ...x, content: e.target.value }))}
rows={14}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.875rem', marginBottom: 'var(--spacing-md)', width: '100%' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.875rem', marginBottom: 'var(--spacing-md)', width: '100%' }}
/>
setEditor((e) => ({ ...e, open: false }))}>Cancel
@@ -594,7 +594,7 @@ export default function SkillEdit() {
value={form.content}
onChange={(e) => setForm((f) => ({ ...f, content: e.target.value }))}
rows={14}
- style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.875rem' }}
+ style={{ fontFamily: 'var(--font-mono)', fontSize: '0.875rem' }}
/>
diff --git a/core/http/react-ui/src/pages/Talk.jsx b/core/http/react-ui/src/pages/Talk.jsx
index 2af98c91e..0bc9b5b66 100644
--- a/core/http/react-ui/src/pages/Talk.jsx
+++ b/core/http/react-ui/src/pages/Talk.jsx
@@ -525,7 +525,7 @@ export default function Talk() {
padding: 'var(--spacing-xs)', border: '1px solid var(--color-border)',
}}>
{item.label}
- {item.value}
+ {item.value}
))}
@@ -623,7 +623,7 @@ export default function Talk() {
) : (
<>
+ style={{ background: 'var(--color-accent)', color: 'var(--color-primary-text)', border: 'none' }}>
Test Tone
@@ -634,7 +634,7 @@ export default function Talk() {
{isConnected && (
+ style={{ background: 'var(--color-error)', color: 'var(--color-text-inverse)', border: 'none' }}>
Disconnect
)}
@@ -660,12 +660,12 @@ export default function Talk() {
Waveform
+ style={{ width: '100%', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', background: 'var(--color-surface-sunken)' }} />
Spectrum (FFT)
+ style={{ width: '100%', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', background: 'var(--color-surface-sunken)' }} />
@@ -684,7 +684,7 @@ export default function Talk() {
background: 'var(--color-bg-secondary)', borderRadius: 'var(--radius-sm)', padding: 'var(--spacing-xs)',
}}>
{item.label}
- {item.value}
+ {item.value}
))}
@@ -693,7 +693,7 @@ export default function Talk() {
fontSize: '0.6875rem', color: 'var(--color-text-secondary)',
background: 'var(--color-bg-secondary)', borderRadius: 'var(--radius-sm)',
padding: 'var(--spacing-xs)', maxHeight: '8rem', overflowY: 'auto',
- fontFamily: 'monospace', whiteSpace: 'pre-wrap', margin: 0,
+ fontFamily: 'var(--font-mono)', whiteSpace: 'pre-wrap', margin: 0,
}}>
{diagStats.raw || 'Waiting for stats...'}
diff --git a/core/http/react-ui/src/pages/Traces.jsx b/core/http/react-ui/src/pages/Traces.jsx
index 23942cde4..c03c3d83b 100644
--- a/core/http/react-ui/src/pages/Traces.jsx
+++ b/core/http/react-ui/src/pages/Traces.jsx
@@ -102,7 +102,7 @@ function AudioSnippet({ data }) {
{metrics.map(m => (
{m.label}
- {m.value}
+ {m.value}
))}
@@ -155,9 +155,9 @@ function DataFields({ data, nested }) {
) : (
)}
- {key}
+ {key}
{objValue && !expanded && {fieldSummary(value)}}
- {!objValue && !large && {formatValue(value)}}
+ {!objValue && !large && {formatValue(value)}}
{!objValue && large && !expanded && {truncateValue(value, 120)}}
{expanded && objValue && (
@@ -170,7 +170,7 @@ function DataFields({ data, nested }) {
{formatLargeValue(value)}
@@ -250,7 +250,7 @@ function ApiTraceDetail({ trace }) {
display: 'flex', alignItems: 'center', gap: 'var(--spacing-xs)',
}}>
- {trace.error}
+ {trace.error}
)}
@@ -259,7 +259,7 @@ function ApiTraceDetail({ trace }) {
{decodeTraceBody(trace.request?.body)}
@@ -270,7 +270,7 @@ function ApiTraceDetail({ trace }) {
{decodeTraceBody(trace.response?.body)}
@@ -511,7 +511,7 @@ export default function Traces() {
setExpandedRow(expandedRow === i ? null : i)} style={{ cursor: 'pointer' }}>
{trace.request?.method || '-'}
- {trace.request?.path || '-'}
+ {trace.request?.path || '-'}
{trace.response?.status || '-'}
{trace.error
@@ -552,7 +552,7 @@ export default function Traces() {
{trace.type || '-'}
{formatTimestamp(trace.timestamp)}
- {trace.model_name || '-'}
+ {trace.model_name || '-'}
{trace.summary || '-'}
diff --git a/core/http/react-ui/src/pages/Usage.jsx b/core/http/react-ui/src/pages/Usage.jsx
index ea0f042c4..63c8a7b59 100644
--- a/core/http/react-ui/src/pages/Usage.jsx
+++ b/core/http/react-ui/src/pages/Usage.jsx
@@ -28,7 +28,7 @@ function StatCard({ icon, label, value, muted }) {
{label}
-
+
{muted ? '~' : ''}{formatNumber(value)}
@@ -39,12 +39,12 @@ function UsageBar({ value, max }) {
const pct = max > 0 ? Math.min((value / max) * 100, 100) : 0
return (
@@ -342,13 +342,13 @@ function PredictionCards({ predictions, quotaExhaustion, period }) {
{q.items.map((item, ii) => (
-
+
{item.label}
-
+
{formatNumber(item.current)}/{formatNumber(item.max)}
{item.withinLimits ? (
@@ -409,12 +409,12 @@ function UsageTimeChart({ data, predictedData, period }) {
Tokens over time
- Prompt
- Completion
+ Prompt
+ Completion
{predictedData && predictedData.length > 0 && (
@@ -432,7 +432,7 @@ function UsageTimeChart({ data, predictedData, period }) {
return (
-
+
{formatYLabel(t)}
@@ -537,7 +537,7 @@ function UsageTimeChart({ data, predictedData, period }) {
return (
{formatBucket(d.bucket, period)}
@@ -556,7 +556,7 @@ function UsageTimeChart({ data, predictedData, period }) {
borderRadius: 'var(--radius-md)',
padding: 'var(--spacing-xs) var(--spacing-sm)',
fontSize: '0.75rem',
- fontFamily: 'JetBrains Mono, monospace',
+ fontFamily: 'var(--font-mono)',
color: 'var(--color-text-primary)',
pointerEvents: 'none',
zIndex: 10,
@@ -592,8 +592,8 @@ function ModelDistChart({ rows }) {
Token distribution by model
- Prompt
- Completion
+ Prompt
+ Completion
@@ -603,17 +603,17 @@ function ModelDistChart({ rows }) {
return (
{row.model}
-
+
{formatNumber(row.total_tokens)}
@@ -702,7 +702,7 @@ export default function Usage() {
const quotaExhaustion = computeQuotaExhaustion(quotas, timeSeries, period)
const userPredictions = isAdmin && userRows.length > 0 ? generateUserPredictions(adminUsage, userRows, period) : {}
- const monoCell = { fontFamily: 'JetBrains Mono, monospace', fontSize: '0.8125rem' }
+ const monoCell = { fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' }
return (
diff --git a/core/http/react-ui/src/pages/auth.css b/core/http/react-ui/src/pages/auth.css
index cbfbd1b6c..0860decbc 100644
--- a/core/http/react-ui/src/pages/auth.css
+++ b/core/http/react-ui/src/pages/auth.css
@@ -172,7 +172,7 @@
.user-email {
font-size: 0.8125rem;
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
}
/* ─── Table cells ─── */
@@ -201,7 +201,7 @@
/* ─── Monospace / code text ─── */
.mono-text {
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
color: var(--color-text-secondary);
}
@@ -350,7 +350,7 @@
}
.invite-link-text {
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -493,7 +493,7 @@
.apikey-details {
font-size: 0.6875rem;
color: var(--color-text-muted);
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
}
.apikey-revoke-btn {
@@ -535,7 +535,7 @@
padding: var(--spacing-xs) var(--spacing-sm);
background: var(--color-bg-primary);
border-radius: var(--radius-sm);
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
font-size: 0.75rem;
word-break: break-all;
color: var(--color-text-primary);
@@ -558,19 +558,20 @@
justify-content: center;
background: var(--color-modal-backdrop);
backdrop-filter: blur(4px);
- animation: fadeIn 150ms ease;
+ animation: fadeIn var(--duration-normal) var(--ease-spring);
}
.modal-panel {
background: var(--color-bg-secondary);
- border: 1px solid var(--color-border-subtle);
+ border: 1px solid var(--color-border-strong);
border-radius: var(--radius-lg);
width: 90%;
max-height: 80vh;
display: flex;
flex-direction: column;
overflow: auto;
- animation: slideUp 150ms ease;
+ box-shadow: var(--shadow-md);
+ animation: popIn var(--duration-slow) var(--ease-spring);
}
/* ─── Model list in permissions modal ─── */
@@ -613,7 +614,7 @@
}
.model-item-name {
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
font-size: 0.75rem;
}
@@ -748,6 +749,6 @@
font-size: 0.6875rem;
color: var(--color-text-secondary);
white-space: nowrap;
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--font-mono);
flex-shrink: 0;
}
diff --git a/core/http/react-ui/src/theme.css b/core/http/react-ui/src/theme.css
index 46eb74d19..53c97ebdb 100644
--- a/core/http/react-ui/src/theme.css
+++ b/core/http/react-ui/src/theme.css
@@ -1,206 +1,235 @@
-/* LocalAI Theme - CSS Variables System */
+/* LocalAI Theme — Nord palette (polar night + frost + aurora).
+ Adapted from claudemaster's Nord preset. Variable names preserved. */
:root,
[data-theme="dark"] {
- --color-bg-primary: #0b0d14;
- --color-bg-secondary: #14172099;
- --color-bg-secondary: #161923;
- --color-bg-tertiary: #232838;
- --color-bg-overlay: rgba(11, 13, 20, 0.95);
+ /* Surfaces — deep blue-black, beyond polar night */
+ --color-bg-primary: #13171f; /* page — very dark, cool */
+ --color-bg-secondary: #1a1f2a; /* sidebar, headers, cards */
+ --color-bg-tertiary: #242a36; /* wells / sunken rows */
+ --color-bg-overlay: rgba(19, 23, 31, 0.92);
+ --color-bg-hover: #242a36;
- --color-surface-raised: #1a1e2c;
- --color-surface-sunken: #090b12;
- --color-surface-hover: #242938;
+ --color-surface-raised: #1a1f2a;
+ --color-surface-sunken: #0e1117;
+ --color-surface-hover: #242a36;
+ --color-surface-elevated: #2f3644;
- --color-primary: #f1f3f8;
- --color-primary-hover: #ffffff;
- --color-primary-active: #d8dce6;
- --color-primary-text: #0a0c12;
- --color-primary-light: rgba(241, 243, 248, 0.08);
- --color-primary-border: rgba(241, 243, 248, 0.22);
+ /* Primary — frost cyan (nord8) */
+ --color-primary: #88c0d0;
+ --color-primary-hover: #9ccbd9;
+ --color-primary-active: #7ab4c4;
+ --color-primary-text: #2e3440;
+ --color-primary-light: rgba(136, 192, 208, 0.14);
+ --color-primary-border: rgba(136, 192, 208, 0.34);
- --color-secondary: #6b7487;
- --color-secondary-hover: #525b6e;
- --color-secondary-light: rgba(107, 116, 135, 0.12);
+ --color-secondary: #81a1c1; /* nord9 */
+ --color-secondary-hover: #8faed0;
+ --color-secondary-light: rgba(129, 161, 193, 0.12);
- --color-accent: #e8a87c;
- --color-accent-hover: #d89668;
- --color-accent-light: rgba(232, 168, 124, 0.14);
+ /* Accent alias — frost cyan remains the brand accent */
+ --color-accent: #88c0d0;
+ --color-accent-hover: #9ccbd9;
+ --color-accent-light: rgba(136, 192, 208, 0.14);
+ --color-accent-border: rgba(136, 192, 208, 0.34);
- --color-text-primary: #f1f3f8;
- --color-text-secondary: #a8adbd;
- --color-text-muted: #6c7084;
- --color-text-disabled: #3e4253;
- --color-text-inverse: #FFFFFF;
+ /* Text — snow storm scale */
+ --color-text-primary: #eceff4; /* nord6 */
+ --color-text-secondary: #d8dee9; /* nord4 */
+ --color-text-muted: #a1acb9;
+ --color-text-disabled: #6e7a8c;
+ --color-text-inverse: #2e3440;
- --color-border-subtle: rgba(255, 255, 255, 0.06);
- --color-border-default: rgba(255, 255, 255, 0.12);
- --color-border-strong: rgba(255, 255, 255, 0.22);
- --color-border-divider: rgba(255, 255, 255, 0.05);
- --color-border-primary: rgba(255, 255, 255, 0.22);
- --color-border-focus: rgba(241, 243, 248, 0.38);
+ /* Borders — cool blue-gray */
+ --color-border-subtle: rgba(216, 222, 233, 0.06);
+ --color-border-default: rgba(216, 222, 233, 0.12);
+ --color-border-strong: rgba(216, 222, 233, 0.24);
+ --color-border-divider: rgba(216, 222, 233, 0.05);
+ --color-border-primary: rgba(136, 192, 208, 0.45);
+ --color-border-focus: rgba(136, 192, 208, 0.45);
- --color-success: #22C55E;
- --color-success-light: rgba(34, 197, 94, 0.14);
- --color-success-border: rgba(34, 197, 94, 0.32);
- --color-warning: #F59E0B;
- --color-warning-light: rgba(245, 158, 11, 0.14);
- --color-warning-border: rgba(245, 158, 11, 0.32);
- --color-error: #EF4444;
- --color-error-light: rgba(239, 68, 68, 0.14);
- --color-error-border: rgba(239, 68, 68, 0.32);
- --color-info: #7dc7d1;
- --color-info-light: rgba(125, 199, 209, 0.14);
- --color-info-border: rgba(125, 199, 209, 0.32);
- --color-accent-border: rgba(232, 168, 124, 0.32);
- --color-modal-backdrop: rgba(0, 0, 0, 0.7);
+ /* Status — aurora */
+ --color-success: #a3be8c; /* nord14 */
+ --color-success-light: rgba(163, 190, 140, 0.14);
+ --color-success-border: rgba(163, 190, 140, 0.32);
+ --color-warning: #ebcb8b; /* nord13 */
+ --color-warning-light: rgba(235, 203, 139, 0.14);
+ --color-warning-border: rgba(235, 203, 139, 0.32);
+ --color-error: #bf616a; /* nord11 */
+ --color-error-light: rgba(191, 97, 106, 0.14);
+ --color-error-border: rgba(191, 97, 106, 0.32);
+ --color-info: #81a1c1; /* nord9 */
+ --color-info-light: rgba(129, 161, 193, 0.14);
+ --color-info-border: rgba(129, 161, 193, 0.32);
- /* Data viz palette — distinct hues, readable on dark */
- --color-data-1: #7dc7d1;
- --color-data-2: #f87171;
- --color-data-3: #c084fc;
- --color-data-4: #fbbf24;
- --color-data-5: #34d399;
- --color-data-6: #fb923c;
- --color-data-7: #f472b6;
- --color-data-8: #22d3ee;
+ --color-modal-backdrop: rgba(8, 11, 17, 0.68);
- /* Log streams */
- --color-log-stdout: #d1d5db;
- --color-log-stderr: #fca5a5;
- --color-log-info: #93c5fd;
- --color-log-warn: #fcd34d;
+ --color-focus-ring: rgba(136, 192, 208, 0.34);
- --shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.4);
- --shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.45);
- --shadow-md: 0 6px 18px rgba(0, 0, 0, 0.5);
- --shadow-lg: 0 16px 32px rgba(0, 0, 0, 0.55);
+ /* Data viz — full aurora + frost palette */
+ --color-data-1: #88c0d0; /* frost cyan */
+ --color-data-2: #bf616a; /* red */
+ --color-data-3: #b48ead; /* purple */
+ --color-data-4: #ebcb8b; /* yellow */
+ --color-data-5: #a3be8c; /* green */
+ --color-data-6: #d08770; /* orange */
+ --color-data-7: #81a1c1; /* blue */
+ --color-data-8: #8fbcbb; /* teal */
+
+ /* Log streams — tuned to Nord aurora */
+ --color-log-stdout: #d8dee9;
+ --color-log-stderr: #bf616a;
+ --color-log-info: #88c0d0;
+ --color-log-warn: #ebcb8b;
+
+ /* Shadows — cool, deeper */
+ --shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.5);
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5), 0 4px 14px rgba(0, 0, 0, 0.45);
+ --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.55), 0 20px 48px rgba(0, 0, 0, 0.65);
+ --shadow-lg: 0 2px 8px rgba(0, 0, 0, 0.6), 0 28px 64px rgba(0, 0, 0, 0.7);
--shadow-glow: var(--shadow-md);
- --shadow-sidebar: 1px 0 0 rgba(255, 255, 255, 0.06);
+ --shadow-sidebar: 1px 0 0 rgba(216, 222, 233, 0.06);
- /* Inset top-edge highlight — fakes elevation in dark mode */
- --shadow-inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.06);
+ --shadow-inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.05);
+ --shadow-inset-hi: inset 0 1px 0 rgba(255, 255, 255, 0.18);
- --duration-fast: 150ms;
- --duration-normal: 200ms;
- --duration-slow: 300ms;
- --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
+ /* Motion */
+ --duration-fast: 120ms;
+ --duration-normal: 180ms;
+ --duration-slow: 260ms;
+ --ease-default: cubic-bezier(0.22, 1, 0.36, 1);
+ --ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
+ /* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
--spacing-2xl: 3rem;
+ --spacing-3xl: 4rem;
- --radius-sm: 4px;
- --radius-md: 6px;
- --radius-lg: 10px;
- --radius-xl: 14px;
+ /* Radii — sharp, editorial */
+ --radius-sm: 3px;
+ --radius-md: 5px;
+ --radius-lg: 8px;
+ --radius-xl: 10px;
--radius-full: 9999px;
- /* Typography scale */
+ /* Typography — Geist Variable + Geist Mono Variable */
+ --font-sans: "Geist", "Geist Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
+ --font-mono: "Geist Mono", "Geist Mono Variable", ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
+
--text-xs: 0.6875rem;
--text-sm: 0.8125rem;
- --text-base: 0.9375rem;
- --text-lg: 1.0625rem;
+ --text-base: 0.875rem;
+ --text-lg: 1rem;
--text-xl: 1.25rem;
- --text-2xl: 1.5rem;
- --text-3xl: 1.875rem;
+ --text-2xl: 1.625rem;
+ --text-3xl: 2rem;
+ --text-4xl: 2.5rem;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
- --leading-tight: 1.25;
+ --leading-tight: 1.2;
--leading-snug: 1.4;
--leading-normal: 1.55;
--leading-relaxed: 1.7;
--sidebar-width: 200px;
--sidebar-width-collapsed: 52px;
- --color-toggle-off: #3a4152;
+ --color-toggle-off: #2f3644;
--color-toggle-on: var(--color-primary);
}
[data-theme="light"] {
- /* Pearl palette — soft cool off-white, clean layered surfaces */
- --color-bg-primary: #f5f6f8; /* page: soft pearl */
- --color-bg-secondary: #ffffff; /* raised surface */
- --color-bg-tertiary: #e7e9ef; /* sunken wells */
- --color-bg-overlay: rgba(245, 246, 248, 0.92);
+ /* Snow storm */
+ --color-bg-primary: #eceff4; /* nord6 */
+ --color-bg-secondary: #ffffff;
+ --color-bg-tertiary: #e5e9f0; /* nord5 */
+ --color-bg-overlay: rgba(236, 239, 244, 0.92);
+ --color-bg-hover: #e5e9f0;
--color-surface-raised: #ffffff;
- --color-surface-sunken: #eef0f4;
- --color-surface-hover: #edeff4;
+ --color-surface-sunken: #e5e9f0;
+ --color-surface-hover: #d8dee9;
+ --color-surface-elevated: #d8dee9; /* nord4 */
- --color-primary: #14161f; /* near-black (cool-leaning) */
- --color-primary-hover: #000000;
- --color-primary-active: #2a2e3a;
- --color-primary-text: #FFFFFF;
- --color-primary-light: rgba(20, 22, 31, 0.06);
- --color-primary-border: rgba(20, 22, 31, 0.2);
+ /* Primary — deeper frost for WCAG on snow storm */
+ --color-primary: #5e81ac; /* nord10 */
+ --color-primary-hover: #4c6d92;
+ --color-primary-active: #3e5b7c;
+ --color-primary-text: #eceff4;
+ --color-primary-light: rgba(94, 129, 172, 0.12);
+ --color-primary-border: rgba(94, 129, 172, 0.34);
- --color-secondary: #525966;
- --color-secondary-hover: #3b414c;
- --color-secondary-light: rgba(82, 89, 102, 0.1);
+ --color-secondary: #4c566a; /* nord3 */
+ --color-secondary-hover: #3b4252;
+ --color-secondary-light: rgba(76, 86, 106, 0.1);
- --color-accent: #c97b5a; /* muted terracotta accent */
- --color-accent-hover: #b56a4a;
- --color-accent-light: rgba(201, 123, 90, 0.12);
+ --color-accent: #5e81ac;
+ --color-accent-hover: #4c6d92;
+ --color-accent-light: rgba(94, 129, 172, 0.12);
+ --color-accent-border: rgba(94, 129, 172, 0.32);
- --color-text-primary: #14161f; /* crisp near-black, cool-leaning */
- --color-text-secondary: #555a68;
- --color-text-muted: #8a8e9b;
- --color-text-disabled: #c2c5cf;
- --color-text-inverse: #FFFFFF;
+ --color-text-primary: #2e3440; /* nord0 */
+ --color-text-secondary: #3b4252; /* nord1 */
+ --color-text-muted: #6e7a8c;
+ --color-text-disabled: #a1acb9;
+ --color-text-inverse: #ffffff;
- --color-border-subtle: rgba(20, 22, 31, 0.07);
- --color-border-default: rgba(20, 22, 31, 0.14);
- --color-border-strong: rgba(20, 22, 31, 0.32);
- --color-border-divider: rgba(20, 22, 31, 0.05);
- --color-border-primary: rgba(20, 22, 31, 0.22);
- --color-border-focus: rgba(20, 22, 31, 0.45);
+ --color-border-subtle: rgba(46, 52, 64, 0.08);
+ --color-border-default: rgba(46, 52, 64, 0.14);
+ --color-border-strong: rgba(46, 52, 64, 0.28);
+ --color-border-divider: rgba(46, 52, 64, 0.06);
+ --color-border-primary: rgba(94, 129, 172, 0.45);
+ --color-border-focus: rgba(94, 129, 172, 0.45);
- --color-success: #16A34A;
- --color-success-light: rgba(22, 163, 74, 0.1);
- --color-success-border: rgba(22, 163, 74, 0.3);
- --color-warning: #D97706;
- --color-warning-light: rgba(217, 119, 6, 0.1);
- --color-warning-border: rgba(217, 119, 6, 0.3);
- --color-error: #DC2626;
- --color-error-light: rgba(220, 38, 38, 0.09);
- --color-error-border: rgba(220, 38, 38, 0.3);
- --color-info: #0f7583;
- --color-info-light: rgba(15, 117, 131, 0.10);
- --color-info-border: rgba(15, 117, 131, 0.3);
- --color-accent-border: rgba(201, 123, 90, 0.3);
- --color-modal-backdrop: rgba(20, 22, 31, 0.5);
+ /* Status — darker aurora for light mode contrast */
+ --color-success: #6b8a5a;
+ --color-success-light: rgba(107, 138, 90, 0.12);
+ --color-success-border: rgba(107, 138, 90, 0.3);
+ --color-warning: #b08334;
+ --color-warning-light: rgba(176, 131, 52, 0.12);
+ --color-warning-border: rgba(176, 131, 52, 0.3);
+ --color-error: #a13e47;
+ --color-error-light: rgba(161, 62, 71, 0.1);
+ --color-error-border: rgba(161, 62, 71, 0.3);
+ --color-info: #4c6d92;
+ --color-info-light: rgba(76, 109, 146, 0.12);
+ --color-info-border: rgba(76, 109, 146, 0.3);
- /* Data viz palette */
- --color-data-1: #0f7583;
- --color-data-2: #c97b5a;
- --color-data-3: #8b5cf6;
- --color-data-4: #d97706;
- --color-data-5: #059669;
- --color-data-6: #dc2626;
- --color-data-7: #db2777;
- --color-data-8: #0891b2;
+ --color-modal-backdrop: rgba(46, 52, 64, 0.38);
- --color-log-stdout: #14161f;
- --color-log-stderr: #b91c1c;
- --color-log-info: #0f7583;
- --color-log-warn: #b45309;
+ --color-focus-ring: rgba(94, 129, 172, 0.34);
- --shadow-subtle: 0 1px 2px rgba(20, 22, 31, 0.05);
- --shadow-sm: 0 2px 6px rgba(20, 22, 31, 0.07);
- --shadow-md: 0 8px 20px rgba(20, 22, 31, 0.08);
- --shadow-lg: 0 20px 38px rgba(20, 22, 31, 0.1);
- --shadow-glow: var(--shadow-md);
- --shadow-sidebar: 4px 0 24px -12px rgba(20, 22, 31, 0.1), 1px 0 0 rgba(20, 22, 31, 0.06);
+ /* Data viz — muted aurora for light mode */
+ --color-data-1: #5e81ac;
+ --color-data-2: #a13e47;
+ --color-data-3: #8b5a92;
+ --color-data-4: #b08334;
+ --color-data-5: #6b8a5a;
+ --color-data-6: #b8684f;
+ --color-data-7: #4c6d92;
+ --color-data-8: #5a9090;
- --shadow-inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.6);
+ --color-log-stdout: #2e3440;
+ --color-log-stderr: #a13e47;
+ --color-log-info: #4c6d92;
+ --color-log-warn: #b08334;
- --color-toggle-off: #c5c8d2;
+ /* Soft cool shadows */
+ --shadow-subtle: 0 1px 2px rgba(46, 52, 64, 0.05);
+ --shadow-sm: 0 1px 2px rgba(46, 52, 64, 0.07), 0 4px 14px rgba(46, 52, 64, 0.07);
+ --shadow-md: 0 2px 8px rgba(46, 52, 64, 0.1), 0 20px 48px rgba(46, 52, 64, 0.12);
+ --shadow-lg: 0 4px 12px rgba(46, 52, 64, 0.14), 0 28px 64px rgba(46, 52, 64, 0.16);
+ --shadow-sidebar: 1px 0 0 rgba(46, 52, 64, 0.06);
+
+ --shadow-inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.7);
+ --shadow-inset-hi: inset 0 1px 0 rgba(255, 255, 255, 0.8);
+
+ --color-toggle-off: #c3cad6;
--color-toggle-on: var(--color-primary);
}
diff --git a/core/http/react-ui/src/utils/cmTheme.js b/core/http/react-ui/src/utils/cmTheme.js
index d7c91a112..1ef8caa42 100644
--- a/core/http/react-ui/src/utils/cmTheme.js
+++ b/core/http/react-ui/src/utils/cmTheme.js
@@ -2,126 +2,126 @@ import { EditorView } from '@codemirror/view'
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
import { tags } from '@lezer/highlight'
-// Dark theme — vibrant palette on deep indigo background
+// Dark theme — Nord polar-night surfaces with aurora syntax highlighting
const darkEditorTheme = EditorView.theme({
'&': {
- backgroundColor: '#1a1a2e',
- color: '#e2e8f0',
- fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
+ backgroundColor: '#13171f',
+ color: '#eceff4',
+ fontFamily: 'var(--font-mono)',
fontSize: '0.8125rem',
lineHeight: '1.5',
},
'.cm-content': {
- caretColor: '#a78bfa',
+ caretColor: '#88c0d0',
padding: '0',
},
- '.cm-cursor, .cm-dropCursor': { borderLeftColor: '#a78bfa', borderLeftWidth: '2px' },
+ '.cm-cursor, .cm-dropCursor': { borderLeftColor: '#88c0d0', borderLeftWidth: '2px' },
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
- backgroundColor: 'rgba(139, 92, 246, 0.3)',
+ backgroundColor: 'rgba(136, 192, 208, 0.25)',
},
'.cm-gutters': {
- backgroundColor: '#16162a',
- color: '#4c5772',
- borderRight: '1px solid #2d2b55',
+ backgroundColor: '#1a1f2a',
+ color: '#6e7a8c',
+ borderRight: '1px solid #2f3644',
},
- '.cm-activeLineGutter': { backgroundColor: 'rgba(139, 92, 246, 0.12)', color: '#8b8db5' },
- '.cm-activeLine': { backgroundColor: 'rgba(139, 92, 246, 0.06)' },
- '.cm-foldPlaceholder': { backgroundColor: '#2d2b55', border: 'none', color: '#8b8db5' },
- '.cm-matchingBracket': { backgroundColor: 'rgba(139, 92, 246, 0.25)', outline: '1px solid rgba(139, 92, 246, 0.5)' },
+ '.cm-activeLineGutter': { backgroundColor: 'rgba(136, 192, 208, 0.1)', color: '#a1acb9' },
+ '.cm-activeLine': { backgroundColor: 'rgba(136, 192, 208, 0.06)' },
+ '.cm-foldPlaceholder': { backgroundColor: '#2f3644', border: 'none', color: '#a1acb9' },
+ '.cm-matchingBracket': { backgroundColor: 'rgba(136, 192, 208, 0.22)', outline: '1px solid rgba(136, 192, 208, 0.5)' },
'.cm-tooltip': {
- backgroundColor: '#1e1e3a',
- border: '1px solid #2d2b55',
- borderRadius: '6px',
+ backgroundColor: '#1a1f2a',
+ border: '1px solid #2f3644',
+ borderRadius: 'var(--radius-md)',
boxShadow: '0 4px 16px rgba(0,0,0,0.5)',
},
'.cm-tooltip-autocomplete': {
- '& > ul': { fontFamily: "'JetBrains Mono', 'Fira Code', monospace", fontSize: '0.8125rem' },
- '& > ul > li': { padding: '4px 8px' },
- '& > ul > li[aria-selected]': { backgroundColor: 'rgba(139, 92, 246, 0.3)', color: '#f1f5f9' },
+ '& > ul': { fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' },
+ '& > ul > li': { padding: 'var(--spacing-xs) var(--spacing-sm)' },
+ '& > ul > li[aria-selected]': { backgroundColor: 'rgba(136, 192, 208, 0.22)', color: '#eceff4' },
},
- '.cm-tooltip.cm-completionInfo': { padding: '8px 10px', maxWidth: '300px' },
- '.cm-completionDetail': { color: '#8b8db5', fontStyle: 'italic', marginLeft: '0.5em' },
- '.cm-panels': { backgroundColor: '#16162a', color: '#e2e8f0' },
- '.cm-panels.cm-panels-top': { borderBottom: '1px solid #2d2b55' },
- '.cm-panels.cm-panels-bottom': { borderTop: '1px solid #2d2b55' },
- '.cm-searchMatch': { backgroundColor: 'rgba(250, 204, 21, 0.2)', outline: '1px solid rgba(250, 204, 21, 0.4)' },
- '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: 'rgba(250, 204, 21, 0.4)' },
- '.cm-selectionMatch': { backgroundColor: 'rgba(139, 92, 246, 0.15)' },
+ '.cm-tooltip.cm-completionInfo': { padding: 'var(--spacing-sm)', maxWidth: '300px' },
+ '.cm-completionDetail': { color: '#a1acb9', fontStyle: 'italic', marginLeft: '0.5em' },
+ '.cm-panels': { backgroundColor: '#1a1f2a', color: '#eceff4' },
+ '.cm-panels.cm-panels-top': { borderBottom: '1px solid #2f3644' },
+ '.cm-panels.cm-panels-bottom': { borderTop: '1px solid #2f3644' },
+ '.cm-searchMatch': { backgroundColor: 'rgba(235, 203, 139, 0.2)', outline: '1px solid rgba(235, 203, 139, 0.45)' },
+ '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: 'rgba(235, 203, 139, 0.42)' },
+ '.cm-selectionMatch': { backgroundColor: 'rgba(136, 192, 208, 0.12)' },
}, { dark: true })
const darkHighlightStyle = HighlightStyle.define([
- { tag: tags.propertyName, color: '#79c0ff', fontWeight: '500' }, // YAML keys — bright blue
- { tag: tags.string, color: '#7ee787' }, // strings — vivid green
- { tag: tags.number, color: '#ffa657' }, // numbers — warm orange
- { tag: tags.bool, color: '#ff7eb6' }, // booleans — hot pink
- { tag: tags.null, color: '#ff7eb6' }, // null — hot pink
- { tag: tags.keyword, color: '#d2a8ff' }, // keywords — bright purple
- { tag: tags.comment, color: '#5c6a82', fontStyle: 'italic' }, // comments — subtle
- { tag: tags.meta, color: '#a5b4cf' }, // directives
- { tag: tags.punctuation, color: '#8b949e' }, // colons, dashes
- { tag: tags.atom, color: '#ff7eb6' }, // special values
- { tag: tags.labelName, color: '#79c0ff', fontWeight: '500' }, // anchors/aliases
+ { tag: tags.propertyName, color: '#88c0d0', fontWeight: '500' }, // YAML keys — frost cyan
+ { tag: tags.string, color: '#a3be8c' }, // strings — aurora green
+ { tag: tags.number, color: '#d08770' }, // numbers — aurora orange
+ { tag: tags.bool, color: '#b48ead' }, // booleans — aurora purple
+ { tag: tags.null, color: '#b48ead' }, // null — aurora purple
+ { tag: tags.keyword, color: '#81a1c1' }, // keywords — frost blue
+ { tag: tags.comment, color: '#6e7a8c', fontStyle: 'italic' }, // comments — muted
+ { tag: tags.meta, color: '#d8dee9' }, // directives — snow storm
+ { tag: tags.punctuation, color: '#8fbcbb' }, // colons, dashes — frost teal
+ { tag: tags.atom, color: '#bf616a' }, // special values — aurora red
+ { tag: tags.labelName, color: '#88c0d0', fontWeight: '500' }, // anchors/aliases
])
-// Light theme — rich saturated colors on warm paper background
+// Light theme — Nord snow-storm surfaces with darkened aurora highlighting
const lightEditorTheme = EditorView.theme({
'&': {
- backgroundColor: '#fafaf9',
- color: '#1c1917',
- fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
+ backgroundColor: '#ffffff',
+ color: '#2e3440',
+ fontFamily: 'var(--font-mono)',
fontSize: '0.8125rem',
lineHeight: '1.5',
},
'.cm-content': {
- caretColor: '#7c3aed',
+ caretColor: '#5e81ac',
padding: '0',
},
- '.cm-cursor, .cm-dropCursor': { borderLeftColor: '#7c3aed', borderLeftWidth: '2px' },
+ '.cm-cursor, .cm-dropCursor': { borderLeftColor: '#5e81ac', borderLeftWidth: '2px' },
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
- backgroundColor: 'rgba(124, 58, 237, 0.15)',
+ backgroundColor: 'rgba(94, 129, 172, 0.18)',
},
'.cm-gutters': {
- backgroundColor: '#f5f5f4',
- color: '#a8a29e',
- borderRight: '1px solid #e7e5e4',
+ backgroundColor: '#e5e9f0',
+ color: '#6e7a8c',
+ borderRight: '1px solid #d8dee9',
},
- '.cm-activeLineGutter': { backgroundColor: 'rgba(124, 58, 237, 0.06)', color: '#78716c' },
- '.cm-activeLine': { backgroundColor: 'rgba(124, 58, 237, 0.03)' },
- '.cm-foldPlaceholder': { backgroundColor: '#e7e5e4', border: 'none', color: '#78716c' },
- '.cm-matchingBracket': { backgroundColor: 'rgba(124, 58, 237, 0.15)', outline: '1px solid rgba(124, 58, 237, 0.3)' },
+ '.cm-activeLineGutter': { backgroundColor: 'rgba(94, 129, 172, 0.1)', color: '#3b4252' },
+ '.cm-activeLine': { backgroundColor: 'rgba(94, 129, 172, 0.05)' },
+ '.cm-foldPlaceholder': { backgroundColor: '#d8dee9', border: 'none', color: '#4c566a' },
+ '.cm-matchingBracket': { backgroundColor: 'rgba(94, 129, 172, 0.18)', outline: '1px solid rgba(94, 129, 172, 0.35)' },
'.cm-tooltip': {
backgroundColor: '#ffffff',
- border: '1px solid #e7e5e4',
- borderRadius: '6px',
- boxShadow: '0 4px 16px rgba(0,0,0,0.08)',
+ border: '1px solid #d8dee9',
+ borderRadius: 'var(--radius-md)',
+ boxShadow: '0 4px 16px rgba(46, 52, 64, 0.12)',
},
'.cm-tooltip-autocomplete': {
- '& > ul': { fontFamily: "'JetBrains Mono', 'Fira Code', monospace", fontSize: '0.8125rem' },
- '& > ul > li': { padding: '4px 8px' },
- '& > ul > li[aria-selected]': { backgroundColor: 'rgba(124, 58, 237, 0.1)', color: '#1c1917' },
+ '& > ul': { fontFamily: 'var(--font-mono)', fontSize: '0.8125rem' },
+ '& > ul > li': { padding: 'var(--spacing-xs) var(--spacing-sm)' },
+ '& > ul > li[aria-selected]': { backgroundColor: 'rgba(94, 129, 172, 0.14)', color: '#2e3440' },
},
- '.cm-tooltip.cm-completionInfo': { padding: '8px 10px', maxWidth: '300px' },
- '.cm-completionDetail': { color: '#78716c', fontStyle: 'italic', marginLeft: '0.5em' },
- '.cm-panels': { backgroundColor: '#f5f5f4', color: '#1c1917' },
- '.cm-panels.cm-panels-top': { borderBottom: '1px solid #e7e5e4' },
- '.cm-panels.cm-panels-bottom': { borderTop: '1px solid #e7e5e4' },
- '.cm-searchMatch': { backgroundColor: 'rgba(234, 179, 8, 0.25)', outline: '1px solid rgba(234, 179, 8, 0.5)' },
- '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: 'rgba(234, 179, 8, 0.45)' },
- '.cm-selectionMatch': { backgroundColor: 'rgba(124, 58, 237, 0.08)' },
+ '.cm-tooltip.cm-completionInfo': { padding: 'var(--spacing-sm)', maxWidth: '300px' },
+ '.cm-completionDetail': { color: '#6e7a8c', fontStyle: 'italic', marginLeft: '0.5em' },
+ '.cm-panels': { backgroundColor: '#e5e9f0', color: '#2e3440' },
+ '.cm-panels.cm-panels-top': { borderBottom: '1px solid #d8dee9' },
+ '.cm-panels.cm-panels-bottom': { borderTop: '1px solid #d8dee9' },
+ '.cm-searchMatch': { backgroundColor: 'rgba(176, 131, 52, 0.22)', outline: '1px solid rgba(176, 131, 52, 0.45)' },
+ '.cm-searchMatch.cm-searchMatch-selected': { backgroundColor: 'rgba(176, 131, 52, 0.4)' },
+ '.cm-selectionMatch': { backgroundColor: 'rgba(94, 129, 172, 0.1)' },
})
const lightHighlightStyle = HighlightStyle.define([
- { tag: tags.propertyName, color: '#0550ae', fontWeight: '500' }, // YAML keys — deep blue
- { tag: tags.string, color: '#116329' }, // strings — forest green
- { tag: tags.number, color: '#cf5500' }, // numbers — burnt orange
- { tag: tags.bool, color: '#cf222e' }, // booleans — crimson
- { tag: tags.null, color: '#cf222e' }, // null — crimson
- { tag: tags.keyword, color: '#8250df' }, // keywords — vivid purple
- { tag: tags.comment, color: '#a3a3a3', fontStyle: 'italic' }, // comments — soft gray
- { tag: tags.meta, color: '#57606a' }, // directives
- { tag: tags.punctuation, color: '#6e7781' }, // colons, dashes
- { tag: tags.atom, color: '#cf222e' }, // special values
- { tag: tags.labelName, color: '#0550ae', fontWeight: '500' }, // anchors/aliases
+ { tag: tags.propertyName, color: '#5e81ac', fontWeight: '500' }, // YAML keys — frost blue
+ { tag: tags.string, color: '#4c6b3a' }, // strings — deep aurora green
+ { tag: tags.number, color: '#b8684f' }, // numbers — warm orange
+ { tag: tags.bool, color: '#8b5a92' }, // booleans — muted purple
+ { tag: tags.null, color: '#8b5a92' }, // null — muted purple
+ { tag: tags.keyword, color: '#4c6d92' }, // keywords — deeper frost
+ { tag: tags.comment, color: '#7a8598', fontStyle: 'italic' }, // comments — cool gray
+ { tag: tags.meta, color: '#3b4252' }, // directives
+ { tag: tags.punctuation, color: '#5a8080' }, // colons, dashes — muted teal
+ { tag: tags.atom, color: '#a13e47' }, // special values — deep aurora red
+ { tag: tags.labelName, color: '#5e81ac', fontWeight: '500' }, // anchors/aliases
])
export const darkTheme = [darkEditorTheme, syntaxHighlighting(darkHighlightStyle)]