diff --git a/packages/ui/src/styles/animations.css b/packages/ui/src/styles/animations.css index f9a09df379e1..09146cb79972 100644 --- a/packages/ui/src/styles/animations.css +++ b/packages/ui/src/styles/animations.css @@ -1,8 +1,30 @@ +/* ============================================ + OpenCode Animation System + Refined micro-interactions & motion design + ============================================ */ + :root { + /* Animation tokens */ + --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); + --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1); + --ease-in-out-quart: cubic-bezier(0.76, 0, 0.24, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); + --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); + + /* Duration tokens */ + --duration-instant: 75ms; + --duration-fast: 120ms; + --duration-normal: 200ms; + --duration-slow: 300ms; + --duration-slower: 450ms; + + /* Existing pulse animations */ --animate-pulse: pulse-opacity 2s ease-in-out infinite; --animate-pulse-scale: pulse-scale 1.2s ease-in-out infinite; } +/* ---- Pulse animations ---- */ + @keyframes pulse-opacity { 0%, 100% { @@ -33,6 +55,8 @@ } } +/* ---- Entrance animations ---- */ + @keyframes fadeUp { from { opacity: 0; @@ -44,98 +68,145 @@ } } -.fade-up-text { - animation: fadeUp 0.4s ease-out forwards; - opacity: 0; - - &:nth-child(1) { - animation-delay: 0.1s; - } - &:nth-child(2) { - animation-delay: 0.2s; - } - &:nth-child(3) { - animation-delay: 0.3s; - } - &:nth-child(4) { - animation-delay: 0.4s; - } - &:nth-child(5) { - animation-delay: 0.5s; - } - &:nth-child(6) { - animation-delay: 0.6s; - } - &:nth-child(7) { - animation-delay: 0.7s; - } - &:nth-child(8) { - animation-delay: 0.8s; - } - &:nth-child(9) { - animation-delay: 0.9s; - } - &:nth-child(10) { - animation-delay: 1s; - } - &:nth-child(11) { - animation-delay: 1.1s; - } - &:nth-child(12) { - animation-delay: 1.2s; - } - &:nth-child(13) { - animation-delay: 1.3s; +@keyframes fadeIn { + from { + opacity: 0; } - &:nth-child(14) { - animation-delay: 1.4s; + to { + opacity: 1; } - &:nth-child(15) { - animation-delay: 1.5s; +} + +@keyframes fadeInScale { + from { + opacity: 0; + transform: scale(0.95); } - &:nth-child(16) { - animation-delay: 1.6s; + to { + opacity: 1; + transform: scale(1); } - &:nth-child(17) { - animation-delay: 1.7s; +} + +@keyframes slideInFromRight { + from { + opacity: 0; + transform: translateX(8px); } - &:nth-child(18) { - animation-delay: 1.8s; + to { + opacity: 1; + transform: translateX(0); } - &:nth-child(19) { - animation-delay: 1.9s; +} + +@keyframes slideInFromLeft { + from { + opacity: 0; + transform: translateX(-8px); } - &:nth-child(20) { - animation-delay: 2s; + to { + opacity: 1; + transform: translateX(0); } - &:nth-child(21) { - animation-delay: 2.1s; +} + +@keyframes slideInFromBottom { + from { + opacity: 0; + transform: translateY(12px); } - &:nth-child(22) { - animation-delay: 2.2s; + to { + opacity: 1; + transform: translateY(0); } - &:nth-child(23) { - animation-delay: 2.3s; +} + +/* ---- Subtle glow for active/focus states ---- */ + +@keyframes subtleGlow { + 0%, + 100% { + box-shadow: 0 0 0 0 transparent; } - &:nth-child(24) { - animation-delay: 2.4s; + 50% { + box-shadow: 0 0 8px 2px color-mix(in srgb, var(--border-selected) 25%, transparent); } - &:nth-child(25) { - animation-delay: 2.5s; +} + +/* ---- Shimmer for loading states ---- */ + +@keyframes shimmer { + 0% { + background-position: -200% 0; } - &:nth-child(26) { - animation-delay: 2.6s; + 100% { + background-position: 200% 0; } - &:nth-child(27) { - animation-delay: 2.7s; +} + +/* ---- Spin ---- */ + +@keyframes spin { + from { + transform: rotate(0deg); } - &:nth-child(28) { - animation-delay: 2.8s; + to { + transform: rotate(360deg); } - &:nth-child(29) { - animation-delay: 2.9s; +} + +/* ---- Staggered fade-up text ---- */ + +.fade-up-text { + animation: fadeUp 0.4s var(--ease-out-expo) forwards; + opacity: 0; + + &:nth-child(1) { animation-delay: 0.05s; } + &:nth-child(2) { animation-delay: 0.1s; } + &:nth-child(3) { animation-delay: 0.15s; } + &:nth-child(4) { animation-delay: 0.2s; } + &:nth-child(5) { animation-delay: 0.25s; } + &:nth-child(6) { animation-delay: 0.3s; } + &:nth-child(7) { animation-delay: 0.35s; } + &:nth-child(8) { animation-delay: 0.4s; } + &:nth-child(9) { animation-delay: 0.45s; } + &:nth-child(10) { animation-delay: 0.5s; } + &:nth-child(11) { animation-delay: 0.55s; } + &:nth-child(12) { animation-delay: 0.6s; } + &:nth-child(13) { animation-delay: 0.65s; } + &:nth-child(14) { animation-delay: 0.7s; } + &:nth-child(15) { animation-delay: 0.75s; } + &:nth-child(16) { animation-delay: 0.8s; } + &:nth-child(17) { animation-delay: 0.85s; } + &:nth-child(18) { animation-delay: 0.9s; } + &:nth-child(19) { animation-delay: 0.95s; } + &:nth-child(20) { animation-delay: 1s; } + &:nth-child(21) { animation-delay: 1.05s; } + &:nth-child(22) { animation-delay: 1.1s; } + &:nth-child(23) { animation-delay: 1.15s; } + &:nth-child(24) { animation-delay: 1.2s; } + &:nth-child(25) { animation-delay: 1.25s; } + &:nth-child(26) { animation-delay: 1.3s; } + &:nth-child(27) { animation-delay: 1.35s; } + &:nth-child(28) { animation-delay: 1.4s; } + &:nth-child(29) { animation-delay: 1.45s; } + &:nth-child(30) { animation-delay: 1.5s; } +} + +/* ---- Reduced motion preference ---- */ + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; } - &:nth-child(30) { - animation-delay: 3s; + + .fade-up-text { + opacity: 1; + animation: none; } } diff --git a/packages/ui/src/styles/aurora.css b/packages/ui/src/styles/aurora.css new file mode 100644 index 000000000000..829a2dd5d852 --- /dev/null +++ b/packages/ui/src/styles/aurora.css @@ -0,0 +1,36 @@ +/* ================================================================ + AURORA DESIGN SYSTEM — "Code illuminated from within" + ================================================================ + + This file previously contained 1174 lines of !important overrides + for the Aurora theme. Those have been absorbed into the native + architecture: + + Token pipeline (resolve.ts): + --accent-*, --glow-*, --glass-*, --border-accent-*, --card-*, + --message-*, --prompt-*, --dialog-*, --popover-*, --code-*, + --scroll-*, --selection-*, --thinking-*, --progress-*, + --empty-bg, --permission-glow, --radius-*, --ease-*, --duration-* + + Component CSS files (23 files): + button, icon-button, dialog, card, dock-surface, session-turn, + popover, dropdown-menu, context-menu, text-field, checkbox, + switch, progress, spinner, tooltip, toast, collapsible, + accordion, tag, hover-card, logo, keybind, resize-handle + + Global styles (base.css): + ::selection, :focus-visible, cursor:pointer, prefers-reduced-motion + + Typography + spacing (theme.css): + --font-family-display, --text-xs through --text-4xl, + --space-*, --gap-*, --padding-*, --max-prose-width + + ================================================================ */ + +/* Aurora-specific font override: prefer JetBrains Mono Nerd Font for full + Unicode coverage (arrows ←→↑↓, box-drawing ╭╮╰╯, math ∑∫≤≥±√∞, icons) */ +html[data-theme="aurora"] { + --font-family-mono: + "JetBrains Mono Nerd Font Mono", "JetBrains Mono", "GeistMono Nerd Font Mono", "SF Mono", "Fira Code", + "CaskaydiaCove Nerd Font Mono", "IBM Plex Mono", ui-monospace, monospace; +} diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index b5604ad61914..889a487942e5 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -34,6 +34,10 @@ html, font-feature-settings: var(--font-family-sans--font-feature-settings, normal); /* 5 */ font-variation-settings: var(--font-family-sans--font-variation-settings, normal); /* 6 */ -webkit-tap-highlight-color: transparent; /* 7 */ + -webkit-font-smoothing: antialiased; /* 8 - Crisp font rendering */ + -moz-osx-font-smoothing: grayscale; /* 8 */ + text-rendering: optimizeLegibility; /* 9 - Better kerning */ + scroll-behavior: smooth; /* 10 - Smooth scrolling */ } /* @@ -402,3 +406,39 @@ input:where([type="button"], [type="reset"], [type="submit"]), font-size: 16px !important; } } + +/* ─── Aurora: Global selection accent ─── */ +::selection { + background: var(--selection-accent); + color: inherit; +} + +/* ─── Aurora: Focus-visible accent ─── */ +:focus-visible { + outline: 2px solid var(--accent-base); + outline-offset: 2px; +} + +:focus:not(:focus-visible) { + outline: none; +} + +/* ─── Aurora: Cursor mandate for interactive elements ─── */ +button, +[role="button"], +a, +summary { + cursor: pointer; +} + +/* ─── Aurora: Reduced motion ─── */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 1b17f6c2b782..b985a152ed93 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -63,3 +63,4 @@ @import "./utilities.css" layer(utilities); @import "./animations.css" layer(utilities); +@import "./aurora.css" layer(utilities); diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 751036598d4e..3dc2743a66da 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -1,26 +1,109 @@ :root { - --font-family-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; - --font-family-sans--font-feature-settings: normal; - --font-family-mono: - ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --font-family-mono--font-feature-settings: normal; + /* ─── FONT FAMILIES ─── */ + --font-family-sans: "Inter", "Inter Fallback", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; + --font-family-sans--font-feature-settings: "ss01" 1, "ss03" 1, "cv01" 1, "cv02" 1; + --font-family-mono: "IBM Plex Mono", "BlexMono Nerd Font Mono", "IBM Plex Mono Fallback", ui-monospace, monospace; + --font-family-mono--font-feature-settings: "ss01" 1; + --font-family-display: + "Geist", "Inter", "SF Pro Display", system-ui, "Apple Color Emoji", "Segoe UI Emoji", sans-serif; + /* Math-capable fallback (KaTeX/MathJax will use their own fonts; this covers inline math Unicode) */ + --font-family-math: "Cambria Math", "STIX Two Math", "Latin Modern Math", math, serif; + /* ─── LEGACY FONT SIZES (kept for backward compat) ─── */ --font-size-small: 13px; --font-size-base: 14px; --font-size-large: 16px; --font-size-x-large: 20px; + + /* ─── AURORA TYPE SCALE — Major Third (1.250) ─── */ + --text-xs: 0.64rem; /* ~10px — Labels, captions */ + --text-sm: 0.8rem; /* ~13px — Small UI text */ + --text-base: 1rem; /* 16px — Body text */ + --text-md: 1.25rem; /* 20px — Large body */ + --text-lg: 1.563rem; /* 25px — Section headers */ + --text-xl: 1.953rem; /* ~31px — Page headers */ + --text-2xl: 2.441rem; /* ~39px — Hero subheads */ + --text-3xl: 3.052rem; /* ~49px — Hero headlines */ + --text-4xl: 3.815rem; /* ~61px — Display text */ + + /* ─── FONT WEIGHTS ─── */ --font-weight-regular: 400; --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* ─── LINE HEIGHTS ─── */ + --line-height-none: 1; + --line-height-tight: 1.25; + --line-height-snug: 1.375; --line-height-normal: 130%; + --line-height-relaxed: 1.625; + --line-height-loose: 1.75; --line-height-large: 150%; --line-height-x-large: 180%; --line-height-2x-large: 200%; + + /* ─── LETTER SPACING ─── */ + --letter-spacing-tighter: -0.05em; + --letter-spacing-tight: -0.025em; --letter-spacing-normal: 0; - --letter-spacing-tight: -0.1599999964237213; + --letter-spacing-wide: 0.025em; + --letter-spacing-wider: 0.05em; --letter-spacing-tightest: -0.3199999928474426; --paragraph-spacing-base: 0; + /* ─── PROSE WIDTH ─── */ + --max-prose-width: 72ch; + + /* ─── SPACING SYSTEM — 4px Base Grid ─── */ --spacing: 0.25rem; + --space-px: 1px; + --space-0: 0; + --space-0-5: 0.125rem; /* 2px */ + --space-1: 0.25rem; /* 4px */ + --space-1-5: 0.375rem; /* 6px */ + --space-2: 0.5rem; /* 8px */ + --space-2-5: 0.625rem; /* 10px */ + --space-3: 0.75rem; /* 12px */ + --space-3-5: 0.875rem; /* 14px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-8: 2rem; /* 32px */ + --space-10: 2.5rem; /* 40px */ + --space-12: 3rem; /* 48px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + --space-24: 6rem; /* 96px */ + + /* ─── SEMANTIC SPACING ─── */ + --gap-xs: var(--space-1); /* 4px — Inline elements */ + --gap-sm: var(--space-2); /* 8px — Tight groups */ + --gap-md: var(--space-4); /* 16px — Default gap */ + --gap-lg: var(--space-6); /* 24px — Section spacing */ + --gap-xl: var(--space-8); /* 32px — Major sections */ + --gap-2xl: var(--space-12); /* 48px — Page sections */ + + /* ─── COMPONENT PADDING ─── */ + --padding-button: var(--space-2) var(--space-4); + --padding-button-sm: var(--space-1-5) var(--space-3); + --padding-button-lg: var(--space-3) var(--space-6); + --padding-card: var(--space-5); + --padding-card-sm: var(--space-3); + --padding-card-lg: var(--space-6); + --padding-input: var(--space-2-5) var(--space-3); + + /* ─── Blur tokens (default: none — no GPU cost for non-Aurora themes) ─── */ + --blur-card: none; + --blur-dialog: none; + --blur-overlay: blur(4px); + --blur-popover: none; + --blur-tooltip: none; + --blur-toast: none; + --blur-dock: none; + --blur-secondary-btn: none; + --blur-sidebar: none; + --blur-titlebar: none; --breakpoint-sm: 40rem; --breakpoint-md: 48rem; @@ -43,23 +126,81 @@ --container-7xl: 80rem; --radius-xs: 0.125rem; - --radius-sm: 0.25rem; - --radius-md: 0.375rem; - --radius-lg: 0.5rem; - --radius-xl: 0.625rem; + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 14px; + --radius-xl: 18px; + --radius-2xl: 24px; + --radius-full: 9999px; + + /* ─── Motion / Easing defaults ─── */ + --ease-smooth: cubic-bezier(0.22, 1, 0.36, 1); + --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); + --ease-snappy: cubic-bezier(0.16, 1, 0.3, 1); + --duration-fast: 150ms; + --duration-normal: 250ms; + --duration-slow: 350ms; + + /* ─── Accent / Glow / Glass defaults (OC-1 fallbacks) ─── */ + --accent-base: var(--icon-interactive-base, #0070ff); + --accent-hover: var(--border-interactive-hover, #005cd1); + --glow-accent: none; + --glow-accent-hover: none; + --glow-accent-focus: none; + --glow-secondary: none; + --glow-secondary-hover: none; + --glow-tertiary: none; + --glow-error: none; + --glow-success: none; + --glow-none: none; + --glass-base: var(--surface-raised-stronger-non-alpha); + --glass-strong: var(--surface-raised-stronger-non-alpha); + --glass-subtle: var(--surface-raised-base); + --border-accent-base: var(--border-weak-base); + --border-accent-hover: var(--border-hover); + --border-accent-focus: var(--border-interactive-base); + --border-accent-secondary: var(--border-weak-base); + --message-user-tint: transparent; + --message-user-border: var(--border-interactive-base); + --message-assistant-border: var(--border-info-base); + --card-bg: var(--surface-inset-base); + --card-bg-hover: var(--surface-base-hover); + --card-glow: var(--shadow-xs); + --code-bg: var(--surface-inset-strong); + --code-border: var(--border-weak-base); + --scroll-accent: var(--border-weak-base); + --scroll-accent-hover: var(--border-hover); + --selection-accent: var(--surface-interactive-base); + --prompt-glow: var(--shadow-xs-border-select); + --prompt-btn-glow: var(--shadow-sm); + --prompt-btn-glow-hover: var(--shadow-md); + --dialog-shadow: var(--shadow-lg-border-base); + --popover-shadow: var(--shadow-lg); + --permission-glow: var(--shadow-xs-border-critical-base); + --empty-bg: transparent; + --thinking-color: var(--icon-interactive-base); + --thinking-glow: none; + --progress-gradient: var(--border-active); --shadow-xs: - 0 1px 2px -0.5px light-dark(hsl(0 0% 0% / 0.04), hsl(0 0% 0% / 0.06)), - 0 0.5px 1.5px 0 light-dark(hsl(0 0% 0% / 0.025), hsl(0 0% 0% / 0.08)), - 0 1px 3px 0 light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.1)); + 0 1px 2px -0.5px light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.08)), + 0 0.5px 1.5px 0 light-dark(hsl(0 0% 0% / 0.03), hsl(0 0% 0% / 0.1)), + 0 1px 3px 0 light-dark(hsl(0 0% 0% / 0.06), hsl(0 0% 0% / 0.12)); + --shadow-sm: + 0 2px 4px -1px light-dark(hsl(0 0% 0% / 0.06), hsl(0 0% 0% / 0.1)), + 0 1px 2px 0 light-dark(hsl(0 0% 0% / 0.04), hsl(0 0% 0% / 0.08)); --shadow-md: - 0 6px 12px -2px light-dark(hsl(0 0% 0% / 0.075), hsl(0 0% 0% / 0.1)), - 0 4px 8px -2px light-dark(hsl(0 0% 0% / 0.075), hsl(0 0% 0% / 0.15)), - 0 1px 2px light-dark(hsl(0 0% 0% / 0.1), hsl(0 0% 0% / 0.15)); + 0 8px 16px -3px light-dark(hsl(0 0% 0% / 0.08), hsl(0 0% 0% / 0.12)), + 0 4px 8px -2px light-dark(hsl(0 0% 0% / 0.06), hsl(0 0% 0% / 0.1)), + 0 1px 3px 0 light-dark(hsl(0 0% 0% / 0.08), hsl(0 0% 0% / 0.12)); --shadow-lg: - 0 16px 48px -6px light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.15)), - 0 6px 12px -2px light-dark(hsl(0 0% 0% / 0.025), hsl(0 0% 0% / 0.1)), - 0 1px 2.5px light-dark(hsl(0 0% 0% / 0.025), hsl(0 0% 0% / 0.1)); + 0 20px 56px -8px light-dark(hsl(0 0% 0% / 0.08), hsl(0 0% 0% / 0.2)), + 0 8px 16px -4px light-dark(hsl(0 0% 0% / 0.04), hsl(0 0% 0% / 0.12)), + 0 2px 4px 0 light-dark(hsl(0 0% 0% / 0.03), hsl(0 0% 0% / 0.08)); + --shadow-xl: + 0 28px 72px -12px light-dark(hsl(0 0% 0% / 0.1), hsl(0 0% 0% / 0.25)), + 0 12px 24px -4px light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.15)), + 0 4px 8px 0 light-dark(hsl(0 0% 0% / 0.04), hsl(0 0% 0% / 0.1)); --shadow-xxs-border: 0 0 0 0.5px var(--border-weak-base, rgba(0, 0, 0, 0.07)); --shadow-xs-border: 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.04), diff --git a/packages/ui/src/styles/utilities.css b/packages/ui/src/styles/utilities.css index 3a05a9515fa0..7e9786a8dd80 100644 --- a/packages/ui/src/styles/utilities.css +++ b/packages/ui/src/styles/utilities.css @@ -4,25 +4,85 @@ [data-popper-positioner] { pointer-events: none; } +} + +/* ---- Text selection styling ---- */ + +::selection { + background-color: color-mix(in srgb, var(--border-selected, #034cff) 30%, transparent); + color: var(--text-strong); +} + +/* ---- Global transition defaults for interactive elements ---- */ - /* ::selection { */ - /* background-color: color-mix(in srgb, var(--color-primary) 33%, transparent); */ - /* background-color: var(--color-primary); */ - /* color: var(--color-background); */ - /* } */ +button, +a, +[role="button"], +[data-component="button"], +[data-component="icon-button"], +[data-component="card"], +[data-component="list-item"], +[data-component="tab"], +input, +select, +textarea { + transition-property: background-color, border-color, color, box-shadow, opacity, transform; + transition-duration: var(--duration-fast, 120ms); + transition-timing-function: var(--ease-smooth, cubic-bezier(0.4, 0, 0.2, 1)); } +/* ---- Focus ring utility ---- */ + +:focus-visible { + outline: 2px solid var(--border-selected, #034cff); + outline-offset: 1px; + border-radius: var(--radius-sm); +} + +/* Suppress focus ring inside specific components that handle their own */ +[data-component="dialog"] :focus-visible, +[data-component="text-field"] :focus-visible, +[contenteditable]:focus-visible { + outline: none; +} + +/* ---- Scrollbar styling ---- */ + .no-scrollbar { &::-webkit-scrollbar { display: none; } - /* Hide scrollbar for IE, Edge and Firefox */ & { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; + scrollbar-width: none; } } +/* Thin scrollbar for scroll areas */ +[data-component="scroll-view"], +.thin-scrollbar { + scrollbar-width: thin; + scrollbar-color: var(--surface-weak, #ccc) transparent; + + &::-webkit-scrollbar { + width: 6px; + height: 6px; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background-color: var(--surface-weak, #ccc); + border-radius: 3px; + + &:hover { + background-color: var(--surface-weaker, #aaa); + } + } +} + +/* ---- Screen reader only ---- */ + .sr-only { position: absolute; width: 1px; @@ -35,6 +95,8 @@ border-width: 0; } +/* ---- Truncation utilities ---- */ + .truncate-start { text-overflow: ellipsis; overflow: hidden; @@ -43,12 +105,14 @@ text-align: left; } +/* ---- Typography scale ---- */ + .text-12-regular { font-family: var(--font-family-sans); font-size: var(--font-size-small); font-style: normal; font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 166.667% */ + line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); } @@ -57,7 +121,7 @@ font-size: var(--font-size-small); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 166.667% */ + line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); } @@ -67,7 +131,7 @@ font-size: var(--font-size-small); font-style: normal; font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 166.667% */ + line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); } @@ -76,7 +140,7 @@ font-size: var(--font-size-base); font-style: normal; font-weight: var(--font-weight-regular); - line-height: var(--line-height-x-large); /* 171.429% */ + line-height: var(--line-height-x-large); letter-spacing: var(--letter-spacing-normal); } @@ -85,7 +149,7 @@ font-size: var(--font-size-base); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 171.429% */ + line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); } @@ -95,7 +159,7 @@ font-size: var(--font-size-base); font-style: normal; font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 171.429% */ + line-height: var(--line-height-large); letter-spacing: var(--letter-spacing-normal); } @@ -104,7 +168,7 @@ font-size: var(--font-size-large); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-x-large); /* 150% */ + line-height: var(--line-height-x-large); letter-spacing: var(--letter-spacing-tight); } @@ -113,6 +177,6 @@ font-size: var(--font-size-x-large); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-x-large); /* 120% */ + line-height: var(--line-height-x-large); letter-spacing: var(--letter-spacing-tightest); } diff --git a/packages/ui/src/theme/context.tsx b/packages/ui/src/theme/context.tsx index 7d25ac39722a..e7c0a1052b57 100644 --- a/packages/ui/src/theme/context.tsx +++ b/packages/ui/src/theme/context.tsx @@ -1,7 +1,7 @@ import { createEffect, onCleanup, onMount } from "solid-js" import { createStore } from "solid-js/store" import { createSimpleContext } from "../context/helper" -import oc2ThemeJson from "./themes/oc-2.json" +import { DEFAULT_THEMES } from "./default-themes" import { resolveThemeVariant, themeToCss } from "./resolve" import type { DesktopTheme } from "./types" @@ -15,101 +15,14 @@ const STORAGE_KEYS = { } as const const THEME_STYLE_ID = "oc-theme" -let files: Record Promise<{ default: DesktopTheme }>> | undefined -let ids: string[] | undefined -let known: Set | undefined - -function getFiles() { - if (files) return files - files = import.meta.glob<{ default: DesktopTheme }>("./themes/*.json") - return files -} - -function themeIDs() { - if (ids) return ids - ids = Object.keys(getFiles()) - .map((path) => path.slice("./themes/".length, -".json".length)) - .sort() - return ids -} - -function knownThemes() { - if (known) return known - known = new Set(themeIDs()) - return known -} - -const names: Record = { - "oc-2": "OC-2", - amoled: "AMOLED", - aura: "Aura", - ayu: "Ayu", - carbonfox: "Carbonfox", - catppuccin: "Catppuccin", - "catppuccin-frappe": "Catppuccin Frappe", - "catppuccin-macchiato": "Catppuccin Macchiato", - cobalt2: "Cobalt2", - cursor: "Cursor", - dracula: "Dracula", - everforest: "Everforest", - flexoki: "Flexoki", - github: "GitHub", - gruvbox: "Gruvbox", - kanagawa: "Kanagawa", - "lucent-orng": "Lucent Orng", - material: "Material", - matrix: "Matrix", - mercury: "Mercury", - monokai: "Monokai", - nightowl: "Night Owl", - nord: "Nord", - "one-dark": "One Dark", - onedarkpro: "One Dark Pro", - opencode: "OpenCode", - orng: "Orng", - "osaka-jade": "Osaka Jade", - palenight: "Palenight", - rosepine: "Rose Pine", - shadesofpurple: "Shades of Purple", - solarized: "Solarized", - synthwave84: "Synthwave '84", - tokyonight: "Tokyonight", - vercel: "Vercel", - vesper: "Vesper", - zenburn: "Zenburn", -} -const oc2Theme = oc2ThemeJson as DesktopTheme function normalize(id: string | null | undefined) { return id === "oc-1" ? "oc-2" : id } -function read(key: string) { - if (typeof localStorage !== "object") return null - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function write(key: string, value: string) { - if (typeof localStorage !== "object") return - try { - localStorage.setItem(key, value) - } catch {} -} - -function drop(key: string) { - if (typeof localStorage !== "object") return - try { - localStorage.removeItem(key) - } catch {} -} - function clear() { - drop(STORAGE_KEYS.THEME_CSS_LIGHT) - drop(STORAGE_KEYS.THEME_CSS_DARK) + localStorage.removeItem(STORAGE_KEYS.THEME_CSS_LIGHT) + localStorage.removeItem(STORAGE_KEYS.THEME_CSS_DARK) } function ensureThemeStyleElement(): HTMLStyleElement { @@ -122,7 +35,6 @@ function ensureThemeStyleElement(): HTMLStyleElement { } function getSystemMode(): "light" | "dark" { - if (typeof window !== "object") return "light" return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" } @@ -133,7 +45,9 @@ function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "da const css = themeToCss(tokens) if (themeId !== "oc-2") { - write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css) + try { + localStorage.setItem(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css) + } catch {} } const fullCss = `:root { @@ -155,122 +69,74 @@ function cacheThemeVariants(theme: DesktopTheme, themeId: string) { const variant = isDark ? theme.dark : theme.light const tokens = resolveThemeVariant(variant, isDark) const css = themeToCss(tokens) - write(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css) + try { + localStorage.setItem(isDark ? STORAGE_KEYS.THEME_CSS_DARK : STORAGE_KEYS.THEME_CSS_LIGHT, css) + } catch {} } } export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ name: "Theme", init: (props: { defaultTheme?: string; onThemeApplied?: (theme: DesktopTheme, mode: "light" | "dark") => void }) => { - const themeId = normalize(read(STORAGE_KEYS.THEME_ID) ?? props.defaultTheme) ?? "oc-2" - const colorScheme = (read(STORAGE_KEYS.COLOR_SCHEME) as ColorScheme | null) ?? "system" - const mode = colorScheme === "system" ? getSystemMode() : colorScheme const [store, setStore] = createStore({ - themes: { - "oc-2": oc2Theme, - } as Record, - themeId, - colorScheme, - mode, + themes: DEFAULT_THEMES as Record, + themeId: normalize(props.defaultTheme) ?? "aurora", + colorScheme: "system" as ColorScheme, + mode: getSystemMode(), previewThemeId: null as string | null, previewScheme: null as ColorScheme | null, }) - const loads = new Map>() - - const load = (id: string) => { - const next = normalize(id) - if (!next) return Promise.resolve(undefined) - const hit = store.themes[next] - if (hit) return Promise.resolve(hit) - const pending = loads.get(next) - if (pending) return pending - const file = getFiles()[`./themes/${next}.json`] - if (!file) return Promise.resolve(undefined) - const task = file() - .then((mod) => { - const theme = mod.default - setStore("themes", next, theme) - return theme - }) - .finally(() => { - loads.delete(next) - }) - loads.set(next, task) - return task - } - - const applyTheme = (theme: DesktopTheme, themeId: string, mode: "light" | "dark") => { - applyThemeCss(theme, themeId, mode) - props.onThemeApplied?.(theme, mode) - } - - const ids = () => { - const extra = Object.keys(store.themes) - .filter((id) => !knownThemes().has(id)) - .sort() - const all = themeIDs() - if (extra.length === 0) return all - return [...all, ...extra] - } - - const loadThemes = () => Promise.all(themeIDs().map(load)).then(() => store.themes) - - const onStorage = (e: StorageEvent) => { - if (e.key === STORAGE_KEYS.THEME_ID && e.newValue) { - const next = normalize(e.newValue) - if (!next) return - if (next !== "oc-2" && !knownThemes().has(next) && !store.themes[next]) return - setStore("themeId", next) - if (next === "oc-2") { - clear() - return - } - void load(next).then((theme) => { - if (!theme || store.themeId !== next) return - cacheThemeVariants(theme, next) - }) - } + window.addEventListener("storage", (e) => { + if (e.key === STORAGE_KEYS.THEME_ID && e.newValue) setStore("themeId", e.newValue) if (e.key === STORAGE_KEYS.COLOR_SCHEME && e.newValue) { setStore("colorScheme", e.newValue as ColorScheme) - setStore("mode", e.newValue === "system" ? getSystemMode() : (e.newValue as "light" | "dark")) + setStore("mode", e.newValue === "system" ? getSystemMode() : (e.newValue as any)) } - } - - if (typeof window === "object") { - window.addEventListener("storage", onStorage) - onCleanup(() => window.removeEventListener("storage", onStorage)) - } + }) onMount(() => { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") - const onMedia = () => { - if (store.colorScheme !== "system") return - setStore("mode", getSystemMode()) + const handler = () => { + if (store.colorScheme === "system") { + setStore("mode", getSystemMode()) + } } - mediaQuery.addEventListener("change", onMedia) - onCleanup(() => mediaQuery.removeEventListener("change", onMedia)) - - const rawTheme = read(STORAGE_KEYS.THEME_ID) - const savedTheme = normalize(rawTheme ?? props.defaultTheme) ?? "oc-2" - const savedScheme = (read(STORAGE_KEYS.COLOR_SCHEME) as ColorScheme | null) ?? "system" - if (rawTheme && rawTheme !== savedTheme) { - write(STORAGE_KEYS.THEME_ID, savedTheme) + mediaQuery.addEventListener("change", handler) + onCleanup(() => mediaQuery.removeEventListener("change", handler)) + + const savedTheme = localStorage.getItem(STORAGE_KEYS.THEME_ID) + const themeId = normalize(savedTheme) + const savedScheme = localStorage.getItem(STORAGE_KEYS.COLOR_SCHEME) as ColorScheme | null + if (themeId && store.themes[themeId]) { + setStore("themeId", themeId) + } + if (savedTheme && themeId && savedTheme !== themeId) { + localStorage.setItem(STORAGE_KEYS.THEME_ID, themeId) clear() } - if (savedTheme !== store.themeId) setStore("themeId", savedTheme) - if (savedScheme !== store.colorScheme) setStore("colorScheme", savedScheme) - setStore("mode", savedScheme === "system" ? getSystemMode() : savedScheme) - void load(savedTheme).then((theme) => { - if (!theme || store.themeId !== savedTheme) return - cacheThemeVariants(theme, savedTheme) - }) + if (savedScheme) { + setStore("colorScheme", savedScheme) + if (savedScheme !== "system") { + setStore("mode", savedScheme) + } + } + const currentTheme = store.themes[store.themeId] + if (currentTheme) { + cacheThemeVariants(currentTheme, store.themeId) + } }) + const applyTheme = (theme: DesktopTheme, themeId: string, mode: "light" | "dark") => { + applyThemeCss(theme, themeId, mode) + props.onThemeApplied?.(theme, mode) + } + createEffect(() => { const theme = store.themes[store.themeId] - if (!theme) return - applyTheme(theme, store.themeId, store.mode) + if (theme) { + applyTheme(theme, store.themeId, store.mode) + } }) const setTheme = (id: string) => { @@ -279,26 +145,23 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ console.warn(`Theme "${id}" not found`) return } - if (next !== "oc-2" && !knownThemes().has(next) && !store.themes[next]) { + const theme = store.themes[next] + if (!theme) { console.warn(`Theme "${id}" not found`) return } setStore("themeId", next) + localStorage.setItem(STORAGE_KEYS.THEME_ID, next) if (next === "oc-2") { - write(STORAGE_KEYS.THEME_ID, next) clear() return } - void load(next).then((theme) => { - if (!theme || store.themeId !== next) return - cacheThemeVariants(theme, next) - write(STORAGE_KEYS.THEME_ID, next) - }) + cacheThemeVariants(theme, next) } const setColorScheme = (scheme: ColorScheme) => { setStore("colorScheme", scheme) - write(STORAGE_KEYS.COLOR_SCHEME, scheme) + localStorage.setItem(STORAGE_KEYS.COLOR_SCHEME, scheme) setStore("mode", scheme === "system" ? getSystemMode() : scheme) } @@ -306,9 +169,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ themeId: () => store.themeId, colorScheme: () => store.colorScheme, mode: () => store.mode, - ids, - name: (id: string) => store.themes[id]?.name ?? names[id] ?? id, - loadThemes, themes: () => store.themes, setTheme, setColorScheme, @@ -316,28 +176,24 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ previewTheme: (id: string) => { const next = normalize(id) if (!next) return - if (next !== "oc-2" && !knownThemes().has(next) && !store.themes[next]) return + const theme = store.themes[next] + if (!theme) return setStore("previewThemeId", next) - void load(next).then((theme) => { - if (!theme || store.previewThemeId !== next) return - const mode = store.previewScheme - ? store.previewScheme === "system" - ? getSystemMode() - : store.previewScheme - : store.mode - applyTheme(theme, next, mode) - }) + const previewMode = store.previewScheme + ? store.previewScheme === "system" + ? getSystemMode() + : store.previewScheme + : store.mode + applyTheme(theme, next, previewMode) }, previewColorScheme: (scheme: ColorScheme) => { setStore("previewScheme", scheme) - const mode = scheme === "system" ? getSystemMode() : scheme + const previewMode = scheme === "system" ? getSystemMode() : scheme const id = store.previewThemeId ?? store.themeId - void load(id).then((theme) => { - if (!theme) return - if ((store.previewThemeId ?? store.themeId) !== id) return - if (store.previewScheme !== scheme) return - applyTheme(theme, id, mode) - }) + const theme = store.themes[id] + if (theme) { + applyTheme(theme, id, previewMode) + } }, commitPreview: () => { if (store.previewThemeId) { @@ -352,10 +208,10 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ cancelPreview: () => { setStore("previewThemeId", null) setStore("previewScheme", null) - void load(store.themeId).then((theme) => { - if (!theme) return + const theme = store.themes[store.themeId] + if (theme) { applyTheme(theme, store.themeId, store.mode) - }) + } }, } }, diff --git a/packages/ui/src/theme/default-themes.ts b/packages/ui/src/theme/default-themes.ts index c14198955812..801206f35271 100644 --- a/packages/ui/src/theme/default-themes.ts +++ b/packages/ui/src/theme/default-themes.ts @@ -1,6 +1,7 @@ import type { DesktopTheme } from "./types" import oc2ThemeJson from "./themes/oc-2.json" import amoledThemeJson from "./themes/amoled.json" +import auroraThemeJson from "./themes/aurora.json" import auraThemeJson from "./themes/aura.json" import ayuThemeJson from "./themes/ayu.json" import carbonfoxThemeJson from "./themes/carbonfox.json" @@ -35,10 +36,12 @@ import synthwave84ThemeJson from "./themes/synthwave84.json" import tokyonightThemeJson from "./themes/tokyonight.json" import vercelThemeJson from "./themes/vercel.json" import vesperThemeJson from "./themes/vesper.json" +import midnightThemeJson from "./themes/midnight.json" import zenburnThemeJson from "./themes/zenburn.json" export const oc2Theme = oc2ThemeJson as DesktopTheme export const amoledTheme = amoledThemeJson as DesktopTheme +export const auroraTheme = auroraThemeJson as DesktopTheme export const auraTheme = auraThemeJson as DesktopTheme export const ayuTheme = ayuThemeJson as DesktopTheme export const carbonfoxTheme = carbonfoxThemeJson as DesktopTheme @@ -73,11 +76,13 @@ export const synthwave84Theme = synthwave84ThemeJson as DesktopTheme export const tokyonightTheme = tokyonightThemeJson as DesktopTheme export const vercelTheme = vercelThemeJson as DesktopTheme export const vesperTheme = vesperThemeJson as DesktopTheme +export const midnightTheme = midnightThemeJson as DesktopTheme export const zenburnTheme = zenburnThemeJson as DesktopTheme export const DEFAULT_THEMES: Record = { "oc-2": oc2Theme, amoled: amoledTheme, + aurora: auroraTheme, aura: auraTheme, ayu: ayuTheme, carbonfox: carbonfoxTheme, @@ -112,5 +117,6 @@ export const DEFAULT_THEMES: Record = { tokyonight: tokyonightTheme, vercel: vercelTheme, vesper: vesperTheme, + midnight: midnightTheme, zenburn: zenburnTheme, } diff --git a/packages/ui/src/theme/desktop-theme.schema.json b/packages/ui/src/theme/desktop-theme.schema.json index 36f4c2c99108..80653618ed5e 100644 --- a/packages/ui/src/theme/desktop-theme.schema.json +++ b/packages/ui/src/theme/desktop-theme.schema.json @@ -80,6 +80,18 @@ "diffDelete": { "$ref": "#/definitions/HexColor", "description": "Color for diff deletions" + }, + "accent": { + "$ref": "#/definitions/HexColor", + "description": "Accent color for glows, highlights, and interactive emphasis. Falls back to interactive." + }, + "accentSecondary": { + "$ref": "#/definitions/HexColor", + "description": "Secondary accent for assistant borders and secondary highlights. Falls back to info." + }, + "accentTertiary": { + "$ref": "#/definitions/HexColor", + "description": "Tertiary accent for warm highlights and attention states. Falls back to warning." } } }, diff --git a/packages/ui/src/theme/resolve.ts b/packages/ui/src/theme/resolve.ts index 2cf6711e69ab..648cef13aca0 100644 --- a/packages/ui/src/theme/resolve.ts +++ b/packages/ui/src/theme/resolve.ts @@ -34,7 +34,7 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res c: isDark ? 1.04 : 1.02, }) : undefined - const backgroundOverride = overrides["background-base"] + const backgroundOverride = overrides["background-base"] as ColorValue | undefined const backgroundHex = getHex(backgroundOverride) const overlay = Boolean(backgroundOverride) && !backgroundHex const content = (seed: HexColor, scale: HexColor[]) => { diff --git a/packages/ui/src/theme/themes/aurora.json b/packages/ui/src/theme/themes/aurora.json new file mode 100644 index 000000000000..74c5a55e4203 --- /dev/null +++ b/packages/ui/src/theme/themes/aurora.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Aurora", + "id": "aurora", + "light": { + "seeds": { + "neutral": "#F5F5FA", + "primary": "#0891B2", + "success": "#16A34A", + "warning": "#D97706", + "error": "#DC2626", + "info": "#7C3AED", + "interactive": "#0891B2", + "diffAdd": "#16A34A", + "diffDelete": "#DC2626", + "accent": "#0891B2", + "accentSecondary": "#7C3AED", + "accentTertiary": "#D97706" + }, + "overrides": { + "background-base": "#F5F5FA", + "background-weak": "#EEEEF5", + "background-strong": "#FAFAFF", + "background-stronger": "#FFFFFF", + "surface-base": "#E8E8F0", + "surface-raised-base": "#F0F0F8", + "surface-raised-stronger-non-alpha": "#FFFFFF", + "text-base": "#1A1A2E", + "text-weak": "#4A4A6A", + "text-strong": "#0A0A1F", + "border-weak-base": "#D8D8E8", + "border-base": "#C0C0D5", + "syntax-string": "#059669", + "syntax-primitive": "#DC2626", + "syntax-property": "#0891B2", + "syntax-type": "#D97706", + "syntax-constant": "#7C3AED", + "syntax-info": "#7C3AED", + "markdown-heading": "#0891B2", + "markdown-text": "#1A1A2E", + "markdown-link": "#7C3AED", + "markdown-link-text": "#0891B2", + "markdown-code": "#059669", + "markdown-block-quote": "#6B7280", + "markdown-emph": "#D97706", + "markdown-strong": "#0891B2", + "markdown-horizontal-rule": "#D8D8E8", + "markdown-list-item": "#0891B2", + "markdown-list-enumeration": "#0891B2", + "markdown-image": "#7C3AED", + "markdown-image-text": "#0891B2", + "markdown-code-block": "#1A1A2E" + } + }, + "dark": { + "seeds": { + "neutral": "#0A0A0F", + "primary": "#00D4FF", + "success": "#4ADE80", + "warning": "#FFBB33", + "error": "#F87171", + "info": "#A78BFA", + "interactive": "#00D4FF", + "diffAdd": "#4ADE80", + "diffDelete": "#F87171", + "accent": "#00D4FF", + "accentSecondary": "#A78BFA", + "accentTertiary": "#FFBB33" + }, + "overrides": { + "background-base": "#0A0A0F", + "background-weak": "#0F0F14", + "background-strong": "#070710", + "background-stronger": "#0D0D12", + "surface-base": "#14141A", + "surface-raised-base": "#1A1A22", + "surface-raised-strong": "#20202A", + "surface-raised-stronger": "#24242E", + "surface-raised-stronger-non-alpha": "#1E1E28", + "surface-inset-base": "#0E0E14", + "surface-inset-strong": "#08080E", + "border-weak-base": "#1E1E2A", + "border-weak-hover": "#262636", + "border-base": "#2A2A3A", + "border-hover": "#343448", + "text-base": "#E0E0EE", + "text-weak": "#8888A0", + "text-weaker": "#5A5A72", + "text-strong": "#F5F5FF", + "syntax-string": "#4ADE80", + "syntax-primitive": "#F87171", + "syntax-property": "#00D4FF", + "syntax-type": "#FFBB33", + "syntax-constant": "#A78BFA", + "syntax-info": "#A78BFA", + "markdown-heading": "#00D4FF", + "markdown-text": "#E0E0EE", + "markdown-link": "#A78BFA", + "markdown-link-text": "#00D4FF", + "markdown-code": "#4ADE80", + "markdown-block-quote": "#8888A0", + "markdown-emph": "#FFBB33", + "markdown-strong": "#00D4FF", + "markdown-horizontal-rule": "#2A2A3A", + "markdown-list-item": "#00D4FF", + "markdown-list-enumeration": "#00D4FF", + "markdown-image": "#A78BFA", + "markdown-image-text": "#00D4FF", + "markdown-code-block": "#E0E0EE" + } + } +} diff --git a/packages/ui/src/theme/themes/everforest.json b/packages/ui/src/theme/themes/everforest.json index 21c04c8ab38c..20d858e52e5a 100644 --- a/packages/ui/src/theme/themes/everforest.json +++ b/packages/ui/src/theme/themes/everforest.json @@ -3,86 +3,128 @@ "name": "Everforest", "id": "everforest", "light": { - "palette": { + "seeds": { "neutral": "#fdf6e3", - "ink": "#5c6a72", "primary": "#8da101", - "accent": "#df69ba", - "success": "#8da101", - "warning": "#f57d26", + "success": "#93b259", + "warning": "#dfa000", "error": "#f85552", - "info": "#35a77c", - "diffAdd": "#4db380", - "diffDelete": "#f52a65" + "info": "#3a94c5", + "interactive": "#35a77c", + "diffAdd": "#93b259", + "diffDelete": "#f85552" }, "overrides": { - "text-weak": "#a6b0a0", - "syntax-comment": "#a6b0a0", - "syntax-keyword": "#df69ba", + "background-base": "#fdf6e3", + "background-weak": "#f4efda", + "background-strong": "#fff9ec", + "background-stronger": "#fffcf0", + "border-weak-base": "#e0dcc3", + "border-weak-hover": "#d5d1b5", + "border-weak-active": "#cbc7a8", + "border-weak-selected": "#bdb898", + "border-weak-disabled": "#f0ebd8", + "border-weak-focus": "#c6c1a4", + "border-base": "#b0ab8e", + "border-hover": "#a5a082", + "border-active": "#9b9676", + "border-selected": "#918b6a", + "border-disabled": "#eae5d4", + "border-focus": "#9e9978", + "border-strong-base": "#7c7862", + "border-strong-hover": "#716d57", + "border-strong-active": "#66624c", + "border-strong-selected": "#5b5741", + "border-strong-disabled": "#d6d1bc", + "border-strong-focus": "#6c684e", + "surface-diff-add-base": "#eef4e2", + "surface-diff-delete-base": "#fce4e2", + "surface-diff-hidden-base": "#e8eef4", + "text-base": "#5c6a72", + "text-weak": "#829181", + "text-strong": "#272e33", "syntax-string": "#8da101", - "syntax-primitive": "#8da101", - "syntax-variable": "#f85552", + "syntax-primitive": "#f85552", "syntax-property": "#35a77c", "syntax-type": "#dfa000", - "syntax-constant": "#f57d26", - "syntax-operator": "#35a77c", - "syntax-punctuation": "#5c6a72", - "syntax-object": "#f85552", - "markdown-heading": "#df69ba", + "syntax-constant": "#3a94c5", + "syntax-info": "#3a94c5", + "markdown-heading": "#35a77c", "markdown-text": "#5c6a72", - "markdown-link": "#8da101", - "markdown-link-text": "#35a77c", + "markdown-link": "#35a77c", + "markdown-link-text": "#3a94c5", "markdown-code": "#8da101", "markdown-block-quote": "#dfa000", "markdown-emph": "#dfa000", - "markdown-strong": "#f57d26", - "markdown-horizontal-rule": "#a6b0a0", - "markdown-list-item": "#8da101", - "markdown-list-enumeration": "#35a77c", - "markdown-image": "#8da101", - "markdown-image-text": "#35a77c", + "markdown-strong": "#f85552", + "markdown-horizontal-rule": "#d5d1b5", + "markdown-list-item": "#35a77c", + "markdown-list-enumeration": "#3a94c5", + "markdown-image": "#35a77c", + "markdown-image-text": "#3a94c5", "markdown-code-block": "#5c6a72" } }, "dark": { - "palette": { + "seeds": { "neutral": "#2d353b", - "ink": "#d3c6aa", "primary": "#a7c080", - "accent": "#d699b6", - "success": "#a7c080", - "warning": "#e69875", + "success": "#83c092", + "warning": "#dbbc7f", "error": "#e67e80", - "info": "#83c092", - "diffAdd": "#b8db87", - "diffDelete": "#e26a75" + "info": "#7fbbb3", + "interactive": "#a7c080", + "diffAdd": "#83c092", + "diffDelete": "#e67e80" }, "overrides": { - "text-weak": "#7a8478", - "syntax-comment": "#7a8478", - "syntax-keyword": "#d699b6", + "background-base": "#2d353b", + "background-weak": "#323c41", + "background-strong": "#272e33", + "background-stronger": "#232a2e", + "border-weak-base": "#3d4a50", + "border-weak-hover": "#445258", + "border-weak-active": "#4b5a60", + "border-weak-selected": "#526268", + "border-weak-disabled": "#1e262a", + "border-weak-focus": "#485660", + "border-base": "#5c6c72", + "border-hover": "#657579", + "border-active": "#6e7e82", + "border-selected": "#77878b", + "border-disabled": "#263034", + "border-focus": "#697a7e", + "border-strong-base": "#859ea2", + "border-strong-hover": "#8ea8ac", + "border-strong-active": "#98b2b6", + "border-strong-selected": "#a2bcbe", + "border-strong-disabled": "#2f3e44", + "border-strong-focus": "#92aeb2", + "surface-diff-add-base": "#283a32", + "surface-diff-delete-base": "#3a2c30", + "surface-diff-hidden-base": "#2d3848", + "text-base": "#d3c6aa", + "text-weak": "#859289", + "text-strong": "#e8dfc8", "syntax-string": "#a7c080", - "syntax-primitive": "#a7c080", - "syntax-variable": "#e67e80", + "syntax-primitive": "#e67e80", "syntax-property": "#83c092", "syntax-type": "#dbbc7f", - "syntax-constant": "#e69875", - "syntax-operator": "#83c092", - "syntax-punctuation": "#d3c6aa", - "syntax-object": "#e67e80", - "markdown-heading": "#d699b6", + "syntax-constant": "#7fbbb3", + "syntax-info": "#7fbbb3", + "markdown-heading": "#a7c080", "markdown-text": "#d3c6aa", "markdown-link": "#a7c080", - "markdown-link-text": "#83c092", - "markdown-code": "#a7c080", + "markdown-link-text": "#7fbbb3", + "markdown-code": "#83c092", "markdown-block-quote": "#dbbc7f", "markdown-emph": "#dbbc7f", - "markdown-strong": "#e69875", - "markdown-horizontal-rule": "#7a8478", + "markdown-strong": "#e67e80", + "markdown-horizontal-rule": "#3d4a50", "markdown-list-item": "#a7c080", - "markdown-list-enumeration": "#83c092", + "markdown-list-enumeration": "#7fbbb3", "markdown-image": "#a7c080", - "markdown-image-text": "#83c092", + "markdown-image-text": "#7fbbb3", "markdown-code-block": "#d3c6aa" } } diff --git a/packages/ui/src/theme/themes/kanagawa.json b/packages/ui/src/theme/themes/kanagawa.json index e1b308c15e38..0e704feab49c 100644 --- a/packages/ui/src/theme/themes/kanagawa.json +++ b/packages/ui/src/theme/themes/kanagawa.json @@ -3,87 +3,129 @@ "name": "Kanagawa", "id": "kanagawa", "light": { - "palette": { - "neutral": "#F2E9DE", - "ink": "#54433A", - "primary": "#2D4F67", - "accent": "#D27E99", - "success": "#98BB6C", - "warning": "#D7A657", - "error": "#E82424", - "info": "#76946A", - "diffAdd": "#89AF5B", - "diffDelete": "#D61F1F" + "seeds": { + "neutral": "#f2ecbc", + "primary": "#624c83", + "success": "#6f894e", + "warning": "#cc6d00", + "error": "#c84053", + "info": "#4e8ca2", + "interactive": "#624c83", + "diffAdd": "#6f894e", + "diffDelete": "#c84053" }, "overrides": { - "text-weak": "#9E9389", - "syntax-comment": "#9E9389", - "syntax-keyword": "#957FB8", - "syntax-string": "#98BB6C", - "syntax-primitive": "#2D4F67", - "syntax-variable": "#54433A", - "syntax-property": "#76946A", - "syntax-type": "#C38D9D", - "syntax-constant": "#D7A657", - "syntax-operator": "#D27E99", - "syntax-punctuation": "#54433A", - "syntax-object": "#54433A", - "markdown-heading": "#957FB8", - "markdown-text": "#54433A", - "markdown-link": "#2D4F67", - "markdown-link-text": "#76946A", - "markdown-code": "#98BB6C", - "markdown-block-quote": "#9E9389", - "markdown-emph": "#C38D9D", - "markdown-strong": "#D7A657", - "markdown-horizontal-rule": "#9E9389", - "markdown-list-item": "#2D4F67", - "markdown-list-enumeration": "#76946A", - "markdown-image": "#2D4F67", - "markdown-image-text": "#76946A", - "markdown-code-block": "#54433A" + "background-base": "#f2ecbc", + "background-weak": "#e7dba0", + "background-strong": "#f7f2cc", + "background-stronger": "#faf7d6", + "border-weak-base": "#d8ccaa", + "border-weak-hover": "#cec09a", + "border-weak-active": "#c4b48a", + "border-weak-selected": "#b6a67c", + "border-weak-disabled": "#ebe4c2", + "border-weak-focus": "#c0ae86", + "border-base": "#aa9a6e", + "border-hover": "#a09064", + "border-active": "#96865a", + "border-selected": "#8c7c50", + "border-disabled": "#e4dcb8", + "border-focus": "#9a8a5e", + "border-strong-base": "#766a42", + "border-strong-hover": "#6c6038", + "border-strong-active": "#62562e", + "border-strong-selected": "#584c24", + "border-strong-disabled": "#cfc4a0", + "border-strong-focus": "#665a34", + "surface-diff-add-base": "#e8f0dd", + "surface-diff-delete-base": "#f4e0e2", + "surface-diff-hidden-base": "#e8e0f0", + "text-base": "#545464", + "text-weak": "#8a8980", + "text-strong": "#1f1f28", + "syntax-string": "#6f894e", + "syntax-primitive": "#c84053", + "syntax-property": "#624c83", + "syntax-type": "#cc6d00", + "syntax-constant": "#4e8ca2", + "syntax-info": "#4e8ca2", + "markdown-heading": "#624c83", + "markdown-text": "#545464", + "markdown-link": "#624c83", + "markdown-link-text": "#4e8ca2", + "markdown-code": "#6f894e", + "markdown-block-quote": "#cc6d00", + "markdown-emph": "#cc6d00", + "markdown-strong": "#c84053", + "markdown-horizontal-rule": "#d0c498", + "markdown-list-item": "#624c83", + "markdown-list-enumeration": "#4e8ca2", + "markdown-image": "#624c83", + "markdown-image-text": "#4e8ca2", + "markdown-code-block": "#545464" } }, "dark": { - "palette": { - "neutral": "#1F1F28", - "ink": "#DCD7BA", - "primary": "#7E9CD8", - "accent": "#D27E99", - "success": "#98BB6C", - "warning": "#D7A657", - "error": "#E82424", - "info": "#76946A", - "diffAdd": "#A9D977", - "diffDelete": "#F24A4A" + "seeds": { + "neutral": "#1f1f28", + "primary": "#957fb8", + "success": "#76946a", + "warning": "#ff9e3b", + "error": "#e82424", + "info": "#7fb4ca", + "interactive": "#957fb8", + "diffAdd": "#76946a", + "diffDelete": "#c34043" }, "overrides": { + "background-base": "#1f1f28", + "background-weak": "#232330", + "background-strong": "#1a1a22", + "background-stronger": "#16161d", + "border-weak-base": "#2a2a37", + "border-weak-hover": "#313140", + "border-weak-active": "#383849", + "border-weak-selected": "#3f3f52", + "border-weak-disabled": "#141420", + "border-weak-focus": "#353548", + "border-base": "#454559", + "border-hover": "#4e4e64", + "border-active": "#57576f", + "border-selected": "#60607a", + "border-disabled": "#1c1c28", + "border-focus": "#52526a", + "border-strong-base": "#727289", + "border-strong-hover": "#7c7c94", + "border-strong-active": "#86869f", + "border-strong-selected": "#9090aa", + "border-strong-disabled": "#232336", + "border-strong-focus": "#7f7f98", + "surface-diff-add-base": "#1d2a1f", + "surface-diff-delete-base": "#2d1a1c", + "surface-diff-hidden-base": "#222035", + "text-base": "#dcd7ba", "text-weak": "#727169", - "syntax-comment": "#727169", - "syntax-keyword": "#957FB8", - "syntax-string": "#98BB6C", - "syntax-primitive": "#7E9CD8", - "syntax-variable": "#DCD7BA", - "syntax-property": "#76946A", - "syntax-type": "#C38D9D", - "syntax-constant": "#D7A657", - "syntax-operator": "#D27E99", - "syntax-punctuation": "#DCD7BA", - "syntax-object": "#DCD7BA", - "markdown-heading": "#957FB8", - "markdown-text": "#DCD7BA", - "markdown-link": "#7E9CD8", - "markdown-link-text": "#76946A", - "markdown-code": "#98BB6C", - "markdown-block-quote": "#727169", - "markdown-emph": "#C38D9D", - "markdown-strong": "#D7A657", - "markdown-horizontal-rule": "#727169", - "markdown-list-item": "#7E9CD8", - "markdown-list-enumeration": "#76946A", - "markdown-image": "#7E9CD8", - "markdown-image-text": "#76946A", - "markdown-code-block": "#DCD7BA" + "text-strong": "#f2ecbc", + "syntax-string": "#98bb6c", + "syntax-primitive": "#e46876", + "syntax-property": "#957fb8", + "syntax-type": "#ff9e3b", + "syntax-constant": "#7fb4ca", + "syntax-info": "#7fb4ca", + "markdown-heading": "#957fb8", + "markdown-text": "#dcd7ba", + "markdown-link": "#957fb8", + "markdown-link-text": "#7fb4ca", + "markdown-code": "#98bb6c", + "markdown-block-quote": "#ff9e3b", + "markdown-emph": "#ff9e3b", + "markdown-strong": "#e46876", + "markdown-horizontal-rule": "#2a2a37", + "markdown-list-item": "#957fb8", + "markdown-list-enumeration": "#7fb4ca", + "markdown-image": "#957fb8", + "markdown-image-text": "#7fb4ca", + "markdown-code-block": "#dcd7ba" } } } diff --git a/packages/ui/src/theme/themes/midnight.json b/packages/ui/src/theme/themes/midnight.json new file mode 100644 index 000000000000..95059620e973 --- /dev/null +++ b/packages/ui/src/theme/themes/midnight.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Midnight", + "id": "midnight", + "light": { + "seeds": { + "neutral": "#F8FAFC", + "primary": "#1E293B", + "success": "#22C55E", + "warning": "#F59E0B", + "error": "#EF4444", + "info": "#3B82F6", + "interactive": "#3B82F6", + "diffAdd": "#22C55E", + "diffDelete": "#EF4444" + }, + "overrides": { + "background-base": "#F8FAFC", + "background-weak": "#F1F5F9", + "background-strong": "#FFFFFF", + "background-stronger": "#FFFFFF", + "border-weak-base": "#E2E8F0", + "border-weak-hover": "#CBD5E1", + "border-weak-active": "#94A3B8", + "border-weak-selected": "#93C5FD", + "border-weak-disabled": "#F1F5F9", + "border-weak-focus": "#93C5FD", + "border-base": "#CBD5E1", + "border-hover": "#94A3B8", + "border-active": "#64748B", + "border-selected": "#3B82F6", + "border-disabled": "#E2E8F0", + "border-focus": "#3B82F6", + "border-strong-base": "#94A3B8", + "border-strong-hover": "#64748B", + "border-strong-active": "#475569", + "border-strong-selected": "#3B82F6", + "border-strong-disabled": "#CBD5E1", + "border-strong-focus": "#3B82F6", + "surface-diff-add-base": "#DCFCE7", + "surface-diff-delete-base": "#FEE2E2", + "surface-diff-hidden-base": "#DBEAFE", + "text-base": "#334155", + "text-weak": "#64748B", + "text-strong": "#0F172A", + "syntax-string": "#059669", + "syntax-primitive": "#DC2626", + "syntax-property": "#7C3AED", + "syntax-type": "#D97706", + "syntax-constant": "#0284C7", + "syntax-info": "#0284C7", + "markdown-heading": "#1E293B", + "markdown-text": "#334155", + "markdown-link": "#3B82F6", + "markdown-link-text": "#0284C7", + "markdown-code": "#059669", + "markdown-block-quote": "#D97706", + "markdown-emph": "#D97706", + "markdown-strong": "#1E293B", + "markdown-horizontal-rule": "#E2E8F0", + "markdown-list-item": "#3B82F6", + "markdown-list-enumeration": "#0284C7", + "markdown-image": "#3B82F6", + "markdown-image-text": "#0284C7", + "markdown-code-block": "#334155" + } + }, + "dark": { + "seeds": { + "neutral": "#0F172A", + "primary": "#22C55E", + "success": "#22C55E", + "warning": "#F59E0B", + "error": "#EF4444", + "info": "#3B82F6", + "interactive": "#3B82F6", + "diffAdd": "#22C55E", + "diffDelete": "#EF4444" + }, + "overrides": { + "background-base": "#0F172A", + "background-weak": "#131C31", + "background-strong": "#0B1120", + "background-stronger": "#0D1424", + "border-weak-base": "#1E293B", + "border-weak-hover": "#253347", + "border-weak-active": "#334155", + "border-weak-selected": "#1D4ED8", + "border-weak-disabled": "#0F172A", + "border-weak-focus": "#2563EB", + "border-base": "#334155", + "border-hover": "#475569", + "border-active": "#64748B", + "border-selected": "#3B82F6", + "border-disabled": "#1E293B", + "border-focus": "#3B82F6", + "border-strong-base": "#475569", + "border-strong-hover": "#64748B", + "border-strong-active": "#94A3B8", + "border-strong-selected": "#60A5FA", + "border-strong-disabled": "#1E293B", + "border-strong-focus": "#60A5FA", + "surface-diff-add-base": "#052E16", + "surface-diff-delete-base": "#450A0A", + "surface-diff-hidden-base": "#172554", + "text-base": "#CBD5E1", + "text-weak": "#64748B", + "text-strong": "#F8FAFC", + "syntax-string": "#4ADE80", + "syntax-primitive": "#FB7185", + "syntax-property": "#A78BFA", + "syntax-type": "#FBBF24", + "syntax-constant": "#38BDF8", + "syntax-info": "#38BDF8", + "markdown-heading": "#22C55E", + "markdown-text": "#E2E8F0", + "markdown-link": "#60A5FA", + "markdown-link-text": "#38BDF8", + "markdown-code": "#4ADE80", + "markdown-block-quote": "#FBBF24", + "markdown-emph": "#FBBF24", + "markdown-strong": "#F8FAFC", + "markdown-horizontal-rule": "#1E293B", + "markdown-list-item": "#60A5FA", + "markdown-list-enumeration": "#38BDF8", + "markdown-image": "#60A5FA", + "markdown-image-text": "#38BDF8", + "markdown-code-block": "#E2E8F0" + } + } +} diff --git a/packages/ui/src/theme/themes/rosepine.json b/packages/ui/src/theme/themes/rosepine.json index a71ad18ce000..5e641f67184f 100644 --- a/packages/ui/src/theme/themes/rosepine.json +++ b/packages/ui/src/theme/themes/rosepine.json @@ -1,84 +1,130 @@ { "$schema": "https://opencode.ai/desktop-theme.json", - "name": "Rose Pine", + "name": "Rosé Pine", "id": "rosepine", "light": { - "palette": { + "seeds": { "neutral": "#faf4ed", - "ink": "#575279", - "primary": "#31748f", - "accent": "#d7827e", - "success": "#286983", + "primary": "#907aa9", + "success": "#56949f", "warning": "#ea9d34", "error": "#b4637a", - "info": "#56949f" + "info": "#286983", + "interactive": "#907aa9", + "diffAdd": "#56949f", + "diffDelete": "#b4637a" }, "overrides": { - "text-weak": "#9893a5", - "syntax-comment": "#9893a5", - "syntax-keyword": "#286983", - "syntax-string": "#ea9d34", - "syntax-primitive": "#d7827e", - "syntax-variable": "#575279", - "syntax-property": "#d7827e", - "syntax-type": "#56949f", - "syntax-constant": "#907aa9", - "syntax-operator": "#797593", - "syntax-punctuation": "#797593", - "syntax-object": "#575279", + "background-base": "#faf4ed", + "background-weak": "#f4ede8", + "background-strong": "#fffaf3", + "background-stronger": "#fef9f2", + "border-weak-base": "#dfdad9", + "border-weak-hover": "#d4cecb", + "border-weak-active": "#c9c3bf", + "border-weak-selected": "#c4b8bb", + "border-weak-disabled": "#f0ebe7", + "border-weak-focus": "#cfbfc3", + "border-base": "#bfb3b7", + "border-hover": "#b4a8ad", + "border-active": "#a99da3", + "border-selected": "#9a8c96", + "border-disabled": "#ebe6e2", + "border-focus": "#af9fa6", + "border-strong-base": "#857e89", + "border-strong-hover": "#7a717f", + "border-strong-active": "#706577", + "border-strong-selected": "#66596e", + "border-strong-disabled": "#d5cfce", + "border-strong-focus": "#746971", + "surface-diff-add-base": "#ecf4f0", + "surface-diff-delete-base": "#fae8ed", + "surface-diff-hidden-base": "#f0edf5", + "text-base": "#575279", + "text-weak": "#797593", + "text-strong": "#26233a", + "syntax-string": "#56949f", + "syntax-primitive": "#b4637a", + "syntax-property": "#907aa9", + "syntax-type": "#ea9d34", + "syntax-constant": "#286983", + "syntax-info": "#286983", "markdown-heading": "#907aa9", "markdown-text": "#575279", - "markdown-link": "#31748f", - "markdown-link-text": "#d7827e", - "markdown-code": "#286983", - "markdown-block-quote": "#9893a5", + "markdown-link": "#907aa9", + "markdown-link-text": "#286983", + "markdown-code": "#56949f", + "markdown-block-quote": "#ea9d34", "markdown-emph": "#ea9d34", "markdown-strong": "#b4637a", "markdown-horizontal-rule": "#dfdad9", - "markdown-list-item": "#31748f", - "markdown-list-enumeration": "#d7827e", - "markdown-image": "#31748f", - "markdown-image-text": "#d7827e", + "markdown-list-item": "#907aa9", + "markdown-list-enumeration": "#286983", + "markdown-image": "#907aa9", + "markdown-image-text": "#286983", "markdown-code-block": "#575279" } }, "dark": { - "palette": { + "seeds": { "neutral": "#191724", - "ink": "#e0def4", - "primary": "#9ccfd8", - "accent": "#ebbcba", - "success": "#31748f", + "primary": "#c4a7e7", + "success": "#9ccfd8", "warning": "#f6c177", "error": "#eb6f92", - "info": "#9ccfd8" + "info": "#31748f", + "interactive": "#c4a7e7", + "diffAdd": "#9ccfd8", + "diffDelete": "#eb6f92" }, "overrides": { - "text-weak": "#6e6a86", - "syntax-comment": "#6e6a86", - "syntax-keyword": "#31748f", - "syntax-string": "#f6c177", - "syntax-primitive": "#ebbcba", - "syntax-variable": "#e0def4", - "syntax-property": "#ebbcba", - "syntax-type": "#9ccfd8", - "syntax-constant": "#c4a7e7", - "syntax-operator": "#908caa", - "syntax-punctuation": "#908caa", - "syntax-object": "#e0def4", + "background-base": "#191724", + "background-weak": "#1f1d2e", + "background-strong": "#161320", + "background-stronger": "#13111e", + "border-weak-base": "#2a2740", + "border-weak-hover": "#312e4c", + "border-weak-active": "#393558", + "border-weak-selected": "#403b64", + "border-weak-disabled": "#141222", + "border-weak-focus": "#353157", + "border-base": "#44415a", + "border-hover": "#4e4b66", + "border-active": "#585573", + "border-selected": "#625f80", + "border-disabled": "#1c1a2e", + "border-focus": "#514e6c", + "border-strong-base": "#6e6a86", + "border-strong-hover": "#787492", + "border-strong-active": "#82809e", + "border-strong-selected": "#8c8aaa", + "border-strong-disabled": "#232138", + "border-strong-focus": "#7a779a", + "surface-diff-add-base": "#1a2a2e", + "surface-diff-delete-base": "#2b1a28", + "surface-diff-hidden-base": "#21203a", + "text-base": "#e0def4", + "text-weak": "#908caa", + "text-strong": "#f4f0ff", + "syntax-string": "#9ccfd8", + "syntax-primitive": "#eb6f92", + "syntax-property": "#c4a7e7", + "syntax-type": "#f6c177", + "syntax-constant": "#31748f", + "syntax-info": "#31748f", "markdown-heading": "#c4a7e7", "markdown-text": "#e0def4", - "markdown-link": "#9ccfd8", - "markdown-link-text": "#ebbcba", - "markdown-code": "#31748f", - "markdown-block-quote": "#6e6a86", + "markdown-link": "#c4a7e7", + "markdown-link-text": "#9ccfd8", + "markdown-code": "#9ccfd8", + "markdown-block-quote": "#f6c177", "markdown-emph": "#f6c177", "markdown-strong": "#eb6f92", - "markdown-horizontal-rule": "#403d52", - "markdown-list-item": "#9ccfd8", - "markdown-list-enumeration": "#ebbcba", - "markdown-image": "#9ccfd8", - "markdown-image-text": "#ebbcba", + "markdown-horizontal-rule": "#2a2740", + "markdown-list-item": "#c4a7e7", + "markdown-list-enumeration": "#9ccfd8", + "markdown-image": "#c4a7e7", + "markdown-image-text": "#9ccfd8", "markdown-code-block": "#e0def4" } } diff --git a/packages/ui/src/theme/types.ts b/packages/ui/src/theme/types.ts index bec67abc013c..58e60c6ed98b 100644 --- a/packages/ui/src/theme/types.ts +++ b/packages/ui/src/theme/types.ts @@ -16,6 +16,12 @@ export interface ThemeSeedColors { interactive: HexColor diffAdd: HexColor diffDelete: HexColor + /** Accent color for glows, highlights, and interactive emphasis. Falls back to `interactive`. */ + accent?: HexColor + /** Secondary accent (e.g. assistant borders, secondary highlights). Falls back to `info`. */ + accentSecondary?: HexColor + /** Tertiary accent (e.g. attention, warm highlights). Falls back to `warning`. */ + accentTertiary?: HexColor } export interface ThemePaletteColors { @@ -33,7 +39,7 @@ export interface ThemePaletteColors { } type ThemeVariantBase = { - overrides?: Record + overrides?: Record } export type ThemeVariant = @@ -60,6 +66,12 @@ export type TokenCategory = | "markdown" | "diff" | "avatar" + | "accent" + | "glow" + | "glass" + | "motion" + | "radius" + | "message" export type ThemeToken = string @@ -67,4 +79,7 @@ export type CssVarRef = `var(--${string})` export type ColorValue = HexColor | CssVarRef -export type ResolvedTheme = Record +/** Any CSS-valid token value — hex colors, rgba(), box-shadow strings, easing functions, etc. */ +export type TokenValue = string + +export type ResolvedTheme = Record