diff options
| author | Adam <[email protected]> | 2025-12-28 05:12:32 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-28 05:12:36 -0600 |
| commit | 4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7 (patch) | |
| tree | ad035366cdebc87d4eea75475201af3d11bd64ed /packages/ui/src | |
| parent | d6db6ff198c4513ca5511ae49b03d4277bc21718 (diff) | |
| download | opencode-4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7.tar.gz opencode-4a9ff9412e8daedc36319bd2ee8ca62d5aa52be7.zip | |
feat(desktop): themes
Diffstat (limited to 'packages/ui/src')
22 files changed, 3287 insertions, 979 deletions
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index a8a9e6a31..92323876c 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -137,6 +137,9 @@ [data-slot="message-part-tool-error-message"] { color: var(--text-on-critical-weak); + max-height: 240px; + overflow-y: auto; + word-break: break-word; } } diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 1748feab9..599116d28 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -343,6 +343,8 @@ .error-card { color: var(--text-on-critical-base); + max-height: 240px; + overflow-y: auto; } [data-slot="session-turn-collapsible-content-inner"] { diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 6584a5e64..e8de4a06c 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -68,987 +68,8 @@ color-scheme: light; --text-mix-blend-mode: multiply; - /* OC-1-light */ - --background-base: #f8f7f7; - --background-weak: var(--smoke-light-3); - --background-strong: var(--smoke-light-1); - --background-stronger: #fcfcfc; - --surface-base: var(--smoke-light-alpha-2); - --base: var(--smoke-light-alpha-2); - --surface-base-hover: #0500000f; - --surface-base-active: var(--smoke-light-alpha-3); - --surface-base-interactive-active: var(--cobalt-light-alpha-3); - --base2: var(--smoke-light-alpha-2); - --base3: var(--smoke-light-alpha-2); - --surface-inset-base: var(--smoke-light-alpha-2); - --surface-inset-base-hover: var(--smoke-light-alpha-3); - --surface-inset-strong: #1f000017; - --surface-inset-strong-hover: #1f000017; - --surface-raised-base: var(--smoke-light-alpha-1); - --surface-float-base: var(--smoke-dark-1); - --surface-float-base-hover: var(--smoke-dark-2); - --surface-raised-base-hover: var(--smoke-light-alpha-2); - --surface-raised-base-active: var(--smoke-light-alpha-3); - --surface-raised-strong: var(--smoke-light-1); - --surface-raised-strong-hover: var(--white); - --surface-raised-stronger: var(--white); - --surface-raised-stronger-hover: var(--white); - --surface-weak: var(--smoke-light-alpha-3); - --surface-weaker: var(--smoke-light-alpha-4); - --surface-strong: #ffffff; - --surface-raised-stronger-non-alpha: var(--white); - --surface-brand-base: var(--yuzu-light-9); - --surface-brand-hover: var(--yuzu-light-10); - --surface-interactive-base: var(--cobalt-light-3); - --surface-interactive-hover: var(--cobalt-light-4); - --surface-interactive-weak: var(--cobalt-light-2); - --surface-interactive-weak-hover: var(--cobalt-light-3); - --surface-success-base: var(--apple-light-3); - --surface-success-weak: var(--apple-light-2); - --surface-success-strong: var(--apple-light-9); - --surface-warning-base: var(--solaris-light-3); - --surface-warning-weak: var(--solaris-light-2); - --surface-warning-strong: var(--solaris-light-9); - --surface-critical-base: var(--ember-light-3); - --surface-critical-weak: var(--ember-light-2); - --surface-critical-strong: var(--ember-light-9); - --surface-info-base: var(--lilac-light-3); - --surface-info-weak: var(--lilac-light-2); - --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: #ffffff00; - --surface-diff-skip-base: var(--smoke-light-2); - --surface-diff-hidden-base: var(--blue-light-3); - --surface-diff-hidden-weak: var(--blue-light-2); - --surface-diff-hidden-weaker: var(--blue-light-1); - --surface-diff-hidden-strong: var(--blue-light-5); - --surface-diff-hidden-stronger: var(--blue-light-9); - --surface-diff-add-base: #dafbe0; - --surface-diff-add-weak: var(--mint-light-2); - --surface-diff-add-weaker: var(--mint-light-1); - --surface-diff-add-strong: var(--mint-light-5); - --surface-diff-add-stronger: var(--mint-light-9); - --surface-diff-delete-base: var(--ember-light-3); - --surface-diff-delete-weak: var(--ember-light-2); - --surface-diff-delete-weaker: var(--ember-light-1); - --surface-diff-delete-strong: var(--ember-light-6); - --surface-diff-delete-stronger: var(--ember-light-9); - --input-base: var(--smoke-light-1); - --input-hover: var(--smoke-light-2); - --input-active: var(--cobalt-light-1); - --input-selected: var(--cobalt-light-4); - --input-focus: var(--cobalt-light-1); - --input-disabled: var(--smoke-light-4); - --text-base: var(--smoke-light-11); - --text-weak: var(--smoke-light-9); - --text-weaker: var(--smoke-light-8); - --text-strong: var(--smoke-light-12); - --text-invert-base: var(--smoke-dark-alpha-11); - --text-invert-weak: var(--smoke-dark-alpha-9); - --text-invert-weaker: var(--smoke-dark-alpha-8); - --text-invert-strong: var(--smoke-dark-alpha-12); - --text-interactive-base: var(--cobalt-light-9); - --text-on-brand-base: var(--smoke-light-alpha-11); - --text-on-interactive-base: var(--smoke-light-1); - --text-on-interactive-weak: var(--smoke-dark-alpha-11); - --text-on-success-base: var(--apple-light-10); - --text-on-critical-base: var(--ember-light-10); - --text-on-critical-weak: var(--ember-light-8); - --text-on-critical-strong: var(--ember-light-12); - --text-on-warning-base: var(--smoke-dark-alpha-11); - --text-on-info-base: var(--smoke-dark-alpha-11); - --text-diff-add-base: var(--mint-light-11); - --text-diff-delete-base: var(--ember-light-10); - --text-diff-delete-strong: var(--ember-light-12); - --text-diff-add-strong: var(--mint-light-12); - --text-on-info-weak: var(--smoke-dark-alpha-9); - --text-on-info-strong: var(--smoke-dark-alpha-12); - --text-on-warning-weak: var(--smoke-dark-alpha-9); - --text-on-warning-strong: var(--smoke-dark-alpha-12); - --text-on-success-weak: var(--apple-light-6); - --text-on-success-strong: var(--apple-light-12); - --text-on-brand-weak: var(--smoke-light-alpha-9); - --text-on-brand-weaker: var(--smoke-light-alpha-8); - --text-on-brand-strong: var(--smoke-light-alpha-12); - --button-secondary-base: #fdfcfc; - --button-secondary-hover: #faf9f9; - --border-base: var(--smoke-light-alpha-7); - --border-hover: var(--smoke-light-alpha-8); - --border-active: var(--smoke-light-alpha-9); - --border-selected: var(--cobalt-light-alpha-9); - --border-disabled: var(--smoke-light-alpha-8); - --border-focus: var(--smoke-light-alpha-9); - --border-weak-base: var(--smoke-light-alpha-5); - --border-strong-base: var(--smoke-light-alpha-7); - --border-strong-hover: var(--smoke-light-alpha-8); - --border-strong-active: var(--smoke-light-alpha-7); - --border-strong-selected: var(--cobalt-light-alpha-6); - --border-strong-disabled: var(--smoke-light-alpha-6); - --border-strong-focus: var(--smoke-light-alpha-7); - --border-weak-hover: var(--smoke-light-alpha-6); - --border-weak-active: var(--smoke-light-alpha-7); - --border-weak-selected: var(--cobalt-light-alpha-5); - --border-weak-disabled: var(--smoke-light-alpha-6); - --border-weak-focus: var(--smoke-light-alpha-7); - --border-interactive-base: var(--cobalt-light-7); - --border-interactive-hover: var(--cobalt-light-8); - --border-interactive-active: var(--cobalt-light-9); - --border-interactive-selected: var(--cobalt-light-9); - --border-interactive-disabled: var(--smoke-light-8); - --border-interactive-focus: var(--cobalt-light-9); - --border-success-base: var(--apple-light-6); - --border-success-hover: var(--apple-light-7); - --border-success-selected: var(--apple-light-9); - --border-warning-base: var(--solaris-light-6); - --border-warning-hover: var(--solaris-light-7); - --border-warning-selected: var(--solaris-light-9); - --border-critical-base: var(--ember-light-6); - --border-critical-hover: var(--ember-light-7); - --border-critical-selected: var(--ember-light-9); - --border-info-base: var(--lilac-light-6); - --border-info-hover: var(--lilac-light-7); - --border-info-selected: var(--lilac-light-9); - --icon-base: var(--smoke-light-9); - --icon-hover: var(--smoke-light-11); - --icon-active: var(--smoke-light-12); - --icon-selected: var(--smoke-light-12); - --icon-disabled: var(--smoke-light-8); - --icon-focus: var(--smoke-light-12); - --icon-invert-base: #ffffff; - --icon-weak-base: var(--smoke-light-7); - --icon-weak-hover: var(--smoke-light-8); - --icon-weak-active: var(--smoke-light-9); - --icon-weak-selected: var(--smoke-light-10); - --icon-weak-disabled: var(--smoke-light-6); - --icon-weak-focus: var(--smoke-light-9); - --icon-strong-base: var(--smoke-light-12); - --icon-strong-hover: #151313; - --icon-strong-active: #020202; - --icon-strong-selected: #020202; - --icon-strong-disabled: var(--smoke-light-8); - --icon-strong-focus: #020202; - --icon-brand-base: var(--smoke-light-12); - --icon-interactive-base: var(--cobalt-light-9); - --icon-success-base: var(--apple-light-7); - --icon-success-hover: var(--apple-light-8); - --icon-success-active: var(--apple-light-11); - --icon-warning-base: var(--amber-light-7); - --icon-warning-hover: var(--amber-light-8); - --icon-warning-active: var(--amber-light-11); - --icon-critical-base: var(--ember-light-10); - --icon-critical-hover: var(--ember-light-11); - --icon-critical-active: var(--ember-light-12); - --icon-info-base: var(--lilac-light-7); - --icon-info-hover: var(--lilac-light-8); - --icon-info-active: var(--lilac-light-11); - --icon-on-brand-base: var(--smoke-light-alpha-11); - --icon-on-brand-hover: var(--smoke-light-alpha-12); - --icon-on-brand-selected: var(--smoke-light-alpha-12); - --icon-on-interactive-base: var(--smoke-light-1); - --icon-agent-plan-base: var(--purple-light-9); - --icon-agent-docs-base: var(--amber-light-9); - --icon-agent-ask-base: var(--cyan-light-9); - --icon-agent-build-base: var(--cobalt-light-9); - --icon-on-success-base: var(--apple-light-alpha-9); - --icon-on-success-hover: var(--apple-light-alpha-10); - --icon-on-success-selected: var(--apple-light-alpha-11); - --icon-on-warning-base: var(--amber-lightalpha-9); - --icon-on-warning-hover: var(--amber-lightalpha-10); - --icon-on-warning-selected: var(--amber-lightalpha-11); - --icon-on-critical-base: var(--ember-light-alpha-9); - --icon-on-critical-hover: var(--ember-light-alpha-10); - --icon-on-critical-selected: var(--ember-light-alpha-11); - --icon-on-info-base: var(--lilac-light-9); - --icon-on-info-hover: var(--lilac-light-alpha-10); - --icon-on-info-selected: var(--lilac-light-alpha-11); - --icon-diff-add-base: var(--mint-light-11); - --icon-diff-add-hover: var(--mint-light-12); - --icon-diff-add-active: var(--mint-light-12); - --icon-diff-delete-base: var(--ember-light-10); - --icon-diff-delete-hover: var(--ember-light-11); - --syntax-comment: var(--text-weak); - --syntax-regexp: var(--text-base); - --syntax-string: #006656; - --syntax-keyword: var(--text-weak); - --syntax-primitive: #fb4804; - --syntax-operator: var(--text-base); - --syntax-variable: var(--text-strong); - --syntax-property: #ed6dc8; - --syntax-type: #596600; - --syntax-constant: #007b80; - --syntax-punctuation: var(--text-base); - --syntax-object: var(--text-strong); - --syntax-success: var(--apple-light-10); - --syntax-warning: var(--amber-light-10); - --syntax-critical: var(--ember-light-10); - --syntax-info: #0092a8; - --syntax-diff-add: var(--mint-light-11); - --syntax-diff-delete: var(--ember-light-11); - --syntax-diff-unknown: #ff0000; - --markdown-heading: #d68c27; - --markdown-text: #1a1a1a; - --markdown-link: #3b7dd8; - --markdown-link-text: #318795; - --markdown-code: #3d9a57; - --markdown-block-quote: #b0851f; - --markdown-emph: #b0851f; - --markdown-strong: #d68c27; - --markdown-horizontal-rule: #8a8a8a; - --markdown-list-item: #3b7dd8; - --markdown-list-enumeration: #318795; - --markdown-image: #3b7dd8; - --markdown-image-text: #318795; - --markdown-code-block: #1a1a1a; - --border-color: #ffffff; - --border-weaker-base: var(--smoke-light-alpha-3); - --border-weaker-hover: var(--smoke-light-alpha-4); - --border-weaker-active: var(--smoke-light-alpha-6); - --border-weaker-selected: var(--cobalt-light-alpha-4); - --border-weaker-disabled: var(--smoke-light-alpha-2); - --border-weaker-focus: var(--smoke-light-alpha-6); - --button-ghost-hover: var(--smoke-light-alpha-2); - --button-ghost-hover2: var(--smoke-light-alpha-3); - --avatar-background-pink: #feeef8; - --avatar-background-mint: #e1fbf4; - --avatar-background-orange: #fff1e7; - --avatar-background-purple: #f9f1fe; - --avatar-background-cyan: #e7f9fb; - --avatar-background-lime: #eefadc; - --avatar-text-pink: #cd1d8d; - --avatar-text-mint: #147d6f; - --avatar-text-orange: #ed5f00; - --avatar-text-purple: #8445bc; - --avatar-text-cyan: #0894b3; - --avatar-text-lime: #5d770d; - @media (prefers-color-scheme: dark) { color-scheme: dark; --text-mix-blend-mode: plus-lighter; - - /* OC-1-dark */ - --background-base: var(--smoke-dark-1); - --background-weak: #1c1717; - --background-strong: #151313; - --background-stronger: #191515; - --surface-base: var(--smoke-dark-alpha-2); - --base: var(--smoke-dark-alpha-2); - --surface-base-hover: #e0b7b716; - --surface-base-active: var(--smoke-dark-alpha-3); - --surface-base-interactive-active: var(--cobalt-dark-alpha-2); - --base2: var(--smoke-dark-alpha-2); - --base3: var(--smoke-dark-alpha-2); - --surface-inset-base: #0e0b0b7f; - --surface-inset-base-hover: #0e0b0b7f; - --surface-inset-strong: #060505cc; - --surface-inset-strong-hover: #060505cc; - --surface-raised-base: var(--smoke-dark-alpha-3); - --surface-float-base: var(--smoke-dark-1); - --surface-float-base-hover: var(--smoke-dark-2); - --surface-raised-base-hover: var(--smoke-dark-alpha-4); - --surface-raised-base-active: var(--smoke-dark-alpha-5); - --surface-raised-strong: var(--smoke-dark-alpha-4); - --surface-raised-strong-hover: var(--smoke-dark-alpha-6); - --surface-raised-stronger: var(--smoke-dark-alpha-6); - --surface-raised-stronger-hover: var(--smoke-dark-alpha-7); - --surface-weak: var(--smoke-dark-alpha-4); - --surface-weaker: var(--smoke-dark-alpha-5); - --surface-strong: var(--smoke-dark-alpha-7); - --surface-raised-stronger-non-alpha: var(--smoke-dark-3); - --surface-brand-base: var(--yuzu-light-9); - --surface-brand-hover: var(--yuzu-light-10); - --surface-interactive-base: var(--cobalt-light-3); - --surface-interactive-hover: var(--cobalt-light-4); - --surface-interactive-weak: var(--cobalt-light-2); - --surface-interactive-weak-hover: var(--cobalt-light-3); - --surface-success-base: var(--apple-light-3); - --surface-success-weak: var(--apple-light-2); - --surface-success-strong: var(--apple-light-9); - --surface-warning-base: var(--solaris-light-3); - --surface-warning-weak: var(--solaris-light-2); - --surface-warning-strong: var(--solaris-light-9); - --surface-critical-base: var(--ember-dark-3); - --surface-critical-weak: var(--ember-dark-2); - --surface-critical-strong: var(--ember-dark-9); - --surface-info-base: var(--lilac-light-3); - --surface-info-weak: var(--lilac-light-2); - --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: var(--smoke-dark-1); - --surface-diff-skip-base: var(--smoke-dark-alpha-1); - --surface-diff-hidden-base: var(--blue-dark-2); - --surface-diff-hidden-weak: var(--blue-dark-1); - --surface-diff-hidden-weaker: var(--blue-dark-3); - --surface-diff-hidden-strong: var(--blue-dark-5); - --surface-diff-hidden-stronger: var(--blue-dark-11); - --surface-diff-add-base: var(--mint-dark-3); - --surface-diff-add-weak: var(--mint-dark-4); - --surface-diff-add-weaker: var(--mint-dark-3); - --surface-diff-add-strong: var(--mint-dark-5); - --surface-diff-add-stronger: var(--mint-dark-11); - --surface-diff-delete-base: var(--ember-dark-3); - --surface-diff-delete-weak: var(--ember-dark-4); - --surface-diff-delete-weaker: var(--ember-dark-3); - --surface-diff-delete-strong: var(--ember-dark-5); - --surface-diff-delete-stronger: var(--ember-dark-11); - --input-base: var(--smoke-dark-2); - --input-hover: var(--smoke-dark-2); - --input-active: var(--cobalt-dark-1); - --input-selected: var(--cobalt-dark-2); - --input-focus: var(--cobalt-dark-1); - --input-disabled: var(--smoke-dark-4); - --text-base: var(--smoke-dark-alpha-11); - --text-weak: var(--smoke-dark-alpha-9); - --text-weaker: var(--smoke-dark-alpha-8); - --text-strong: var(--smoke-dark-alpha-12); - --text-invert-base: var(--smoke-dark-alpha-11); - --text-invert-weak: var(--smoke-dark-alpha-9); - --text-invert-weaker: var(--smoke-dark-alpha-8); - --text-invert-strong: var(--smoke-dark-alpha-12); - --text-interactive-base: var(--cobalt-dark-11); - --text-on-brand-base: var(--smoke-dark-alpha-11); - --text-on-interactive-base: var(--smoke-dark-12); - --text-on-interactive-weak: var(--smoke-dark-alpha-11); - --text-on-success-base: var(--apple-dark-9); - --text-on-critical-base: var(--ember-dark-9); - --text-on-critical-weak: var(--ember-dark-8); - --text-on-critical-strong: var(--ember-dark-12); - --text-on-warning-base: var(--smoke-dark-alpha-11); - --text-on-info-base: var(--smoke-dark-alpha-11); - --text-diff-add-base: var(--mint-dark-11); - --text-diff-delete-base: var(--ember-dark-9); - --text-diff-delete-strong: var(--ember-dark-12); - --text-diff-add-strong: var(--mint-dark-8); - --text-on-info-weak: var(--smoke-dark-alpha-9); - --text-on-info-strong: var(--smoke-dark-alpha-12); - --text-on-warning-weak: var(--smoke-dark-alpha-9); - --text-on-warning-strong: var(--smoke-dark-alpha-12); - --text-on-success-weak: var(--apple-dark-8); - --text-on-success-strong: var(--apple-dark-12); - --text-on-brand-weak: var(--smoke-dark-alpha-9); - --text-on-brand-weaker: var(--smoke-dark-alpha-8); - --text-on-brand-strong: var(--smoke-dark-alpha-12); - --button-secondary-base: #231f1f; - --button-secondary-hover: #2a2727; - --border-base: var(--smoke-dark-alpha-7); - --border-hover: var(--smoke-dark-alpha-8); - --border-active: var(--smoke-dark-alpha-9); - --border-selected: var(--cobalt-dark-alpha-11); - --border-disabled: var(--smoke-dark-alpha-8); - --border-focus: var(--smoke-dark-alpha-9); - --border-weak-base: var(--smoke-dark-alpha-6); - --border-strong-base: var(--smoke-dark-alpha-8); - --border-strong-hover: var(--smoke-dark-alpha-7); - --border-strong-active: var(--smoke-dark-alpha-8); - --border-strong-selected: var(--cobalt-dark-alpha-6); - --border-strong-disabled: var(--smoke-dark-alpha-6); - --border-strong-focus: var(--smoke-dark-alpha-8); - --border-weak-hover: var(--smoke-dark-alpha-7); - --border-weak-active: var(--smoke-dark-alpha-8); - --border-weak-selected: var(--cobalt-dark-alpha-6); - --border-weak-disabled: var(--smoke-dark-alpha-6); - --border-weak-focus: var(--smoke-dark-alpha-8); - --border-interactive-base: var(--cobalt-light-7); - --border-interactive-hover: var(--cobalt-light-8); - --border-interactive-active: var(--cobalt-light-9); - --border-interactive-selected: var(--cobalt-light-9); - --border-interactive-disabled: var(--smoke-light-8); - --border-interactive-focus: var(--cobalt-light-9); - --border-success-base: var(--apple-light-6); - --border-success-hover: var(--apple-light-7); - --border-success-selected: var(--apple-light-9); - --border-warning-base: var(--solaris-light-6); - --border-warning-hover: var(--solaris-light-7); - --border-warning-selected: var(--solaris-light-9); - --border-critical-base: var(--ember-dark-5); - --border-critical-hover: var(--ember-dark-7); - --border-critical-selected: var(--ember-dark-9); - --border-info-base: var(--lilac-light-6); - --border-info-hover: var(--lilac-light-7); - --border-info-selected: var(--lilac-light-9); - --icon-base: var(--smoke-dark-9); - --icon-hover: var(--smoke-dark-10); - --icon-active: var(--smoke-dark-11); - --icon-selected: var(--smoke-dark-12); - --icon-disabled: var(--smoke-dark-7); - --icon-focus: var(--smoke-dark-12); - --icon-invert-base: var(--smoke-dark-1); - --icon-weak-base: var(--smoke-dark-6); - --icon-weak-hover: var(--smoke-light-7); - --icon-weak-active: var(--smoke-light-8); - --icon-weak-selected: var(--smoke-light-9); - --icon-weak-disabled: var(--smoke-light-4); - --icon-weak-focus: var(--smoke-light-9); - --icon-strong-base: var(--smoke-dark-12); - --icon-strong-hover: #f6f3f3; - --icon-strong-active: #fcfcfc; - --icon-strong-selected: #fdfcfc; - --icon-strong-disabled: var(--smoke-dark-8); - --icon-strong-focus: #fdfcfc; - --icon-brand-base: var(--white); - --icon-interactive-base: var(--cobalt-dark-9); - --icon-success-base: var(--apple-dark-7); - --icon-success-hover: var(--apple-dark-8); - --icon-success-active: var(--apple-dark-11); - --icon-warning-base: var(--amber-dark-7); - --icon-warning-hover: var(--amber-dark-8); - --icon-warning-active: var(--amber-dark-11); - --icon-critical-base: var(--ember-dark-9); - --icon-critical-hover: var(--ember-dark-11); - --icon-critical-active: var(--ember-dark-12); - --icon-info-base: var(--lilac-dark-7); - --icon-info-hover: var(--lilac-dark-8); - --icon-info-active: var(--lilac-dark-11); - --icon-on-brand-base: var(--smoke-light-alpha-11); - --icon-on-brand-hover: var(--smoke-light-alpha-12); - --icon-on-brand-selected: var(--smoke-light-alpha-12); - --icon-on-interactive-base: var(--smoke-dark-12); - --icon-agent-plan-base: var(--purple-dark-9); - --icon-agent-docs-base: var(--amber-dark-9); - --icon-agent-ask-base: var(--cyan-dark-9); - --icon-agent-build-base: var(--cobalt-dark-11); - --icon-on-success-base: var(--apple-dark-alpha-9); - --icon-on-success-hover: var(--apple-dark-alpha-10); - --icon-on-success-selected: var(--apple-dark-alpha-11); - --icon-on-warning-base: var(--amber-darkalpha-9); - --icon-on-warning-hover: var(--amber-darkalpha-10); - --icon-on-warning-selected: var(--amber-darkalpha-11); - --icon-on-critical-base: var(--ember-dark-alpha-9); - --icon-on-critical-hover: var(--ember-dark-alpha-10); - --icon-on-critical-selected: var(--ember-dark-alpha-11); - --icon-on-info-base: var(--lilac-dark-9); - --icon-on-info-hover: var(--lilac-dark-alpha-10); - --icon-on-info-selected: var(--lilac-dark-alpha-11); - --icon-diff-add-base: var(--mint-dark-11); - --icon-diff-add-hover: var(--mint-dark-10); - --icon-diff-add-active: var(--mint-dark-11); - --icon-diff-delete-base: var(--ember-dark-9); - --icon-diff-delete-hover: var(--ember-dark-10); - --syntax-comment: var(--text-weak); - --syntax-regexp: var(--text-base); - --syntax-string: #00ceb9; - --syntax-keyword: var(--text-weak); - --syntax-primitive: #ffba92; - --syntax-operator: var(--text-weak); - --syntax-variable: var(--text-strong); - --syntax-property: #ff9ae2; - --syntax-type: #ecf58c; - --syntax-constant: #93e9f6; - --syntax-punctuation: var(--text-weak); - --syntax-object: var(--text-strong); - --syntax-success: var(--apple-dark-10); - --syntax-warning: var(--amber-dark-10); - --syntax-critical: var(--ember-dark-10); - --syntax-info: #93e9f6; - --syntax-diff-add: var(--mint-dark-11); - --syntax-diff-delete: var(--ember-dark-11); - --syntax-diff-unknown: #ff0000; - --markdown-heading: #9d7cd8; - --markdown-text: #eeeeee; - --markdown-link: #fab283; - --markdown-link-text: #56b6c2; - --markdown-code: #7fd88f; - --markdown-block-quote: #e5c07b; - --markdown-emph: #e5c07b; - --markdown-strong: #f5a742; - --markdown-horizontal-rule: #808080; - --markdown-list-item: #fab283; - --markdown-list-enumeration: #56b6c2; - --markdown-image: #fab283; - --markdown-image-text: #56b6c2; - --markdown-code-block: #eeeeee; - --border-color: #ffffff; - --border-weaker-base: var(--smoke-dark-alpha-3); - --border-weaker-hover: var(--smoke-dark-alpha-4); - --border-weaker-active: var(--smoke-dark-alpha-6); - --border-weaker-selected: var(--cobalt-dark-alpha-3); - --border-weaker-disabled: var(--smoke-dark-alpha-2); - --border-weaker-focus: var(--smoke-dark-alpha-6); - --button-ghost-hover: var(--smoke-dark-alpha-2); - --button-ghost-hover2: var(--smoke-dark-alpha-3); - --avatar-background-pink: #501b3f; - --avatar-background-mint: #033a34; - --avatar-background-orange: #5f2a06; - --avatar-background-purple: #432155; - --avatar-background-cyan: #0f3058; - --avatar-background-lime: #2b3711; - --avatar-text-pink: #e34ba9; - --avatar-text-mint: #95f3d9; - --avatar-text-orange: #ff802b; - --avatar-text-purple: #9d5bd2; - --avatar-text-cyan: #369eff; - --avatar-text-lime: #c4f042; - } -} - -html[data-theme="oc-2-paper"] { - /* OC-2-paper */ - --background-base: #f7f8f8; - --background-weak: var(--ink-light-3); - --background-strong: var(--ink-light-1); - --background-stronger: #fcfcfc; - --surface-base: var(--ink-light-alpha-2); - --base: var(--ink-light-alpha-2); - --surface-base-hover: #0005050f; - --surface-base-active: var(--ink-light-alpha-3); - --surface-base-interactive-active: var(--cobalt-light-alpha-3); - --base2: var(--ink-light-alpha-2); - --base3: var(--ink-light-alpha-2); - --surface-inset-base: var(--ink-light-alpha-2); - --surface-inset-base-hover: var(--ink-light-alpha-3); - --surface-inset-strong: #001f1f17; - --surface-inset-strong-hover: #001f1f17; - --surface-raised-base: var(--ink-light-alpha-1); - --surface-float-base: var(--ink-dark-1); - --surface-float-base-hover: var(--ink-dark-2); - --surface-raised-base-hover: var(--ink-light-alpha-2); - --surface-raised-base-active: var(--ink-light-alpha-3); - --surface-raised-strong: var(--ink-light-1); - --surface-raised-strong-hover: var(--white); - --surface-raised-stronger: var(--white); - --surface-raised-stronger-hover: var(--white); - --surface-weak: var(--ink-light-alpha-3); - --surface-weaker: var(--ink-light-alpha-4); - --surface-strong: #ffffff; - --surface-raised-stronger-non-alpha: var(--white); - --surface-brand-base: var(--yuzu-light-9); - --surface-brand-hover: var(--yuzu-light-10); - --surface-interactive-base: var(--cobalt-light-3); - --surface-interactive-hover: var(--cobalt-light-4); - --surface-interactive-weak: var(--cobalt-light-2); - --surface-interactive-weak-hover: var(--cobalt-light-3); - --surface-success-base: var(--apple-light-3); - --surface-success-weak: var(--apple-light-2); - --surface-success-strong: var(--apple-light-9); - --surface-warning-base: var(--solaris-light-3); - --surface-warning-weak: var(--solaris-light-2); - --surface-warning-strong: var(--solaris-light-9); - --surface-critical-base: var(--ember-light-3); - --surface-critical-weak: var(--ember-light-2); - --surface-critical-strong: var(--ember-light-9); - --surface-info-base: var(--lilac-light-3); - --surface-info-weak: var(--lilac-light-2); - --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: #ffffff00; - --surface-diff-skip-base: var(--ink-light-2); - --surface-diff-hidden-base: var(--blue-light-3); - --surface-diff-hidden-weak: var(--blue-light-2); - --surface-diff-hidden-weaker: var(--blue-light-1); - --surface-diff-hidden-strong: var(--blue-light-5); - --surface-diff-hidden-stronger: var(--blue-light-9); - --surface-diff-add-base: var(--mint-light-3); - --surface-diff-add-weak: var(--mint-light-2); - --surface-diff-add-weaker: var(--mint-light-1); - --surface-diff-add-strong: var(--mint-light-5); - --surface-diff-add-stronger: var(--mint-light-9); - --surface-diff-delete-base: var(--ember-light-3); - --surface-diff-delete-weak: var(--ember-light-2); - --surface-diff-delete-weaker: var(--ember-light-1); - --surface-diff-delete-strong: var(--ember-light-6); - --surface-diff-delete-stronger: var(--ember-light-9); - --text-base: var(--ink-light-11); - --input-base: var(--ink-light-1); - --input-hover: var(--ink-light-2); - --input-active: var(--cobalt-light-1); - --input-selected: var(--cobalt-light-4); - --input-focus: var(--cobalt-light-1); - --input-disabled: var(--ink-light-4); - --text-weak: var(--ink-light-9); - --text-weaker: var(--ink-light-8); - --text-strong: var(--ink-light-12); - --text-interactive-base: var(--cobalt-light-9); - --text-on-brand-base: var(--ink-light-alpha-11); - --text-on-interactive-base: var(--ink-light-1); - --text-on-interactive-weak: var(--ink-light-alpha-11); - --text-on-success-base: var(--apple-light-10); - --text-on-critical-base: var(--ember-light-10); - --text-on-critical-weak: var(--ember-light-8); - --text-on-critical-strong: var(--ember-light-12); - --text-on-warning-base: var(--ink-dark-alpha-11); - --text-on-info-base: var(--ink-dark-alpha-11); - --text-diff-add-base: var(--mint-light-11); - --text-diff-delete-base: var(--ember-light-10); - --text-diff-delete-strong: var(--ember-light-12); - --text-diff-add-strong: var(--mint-light-12); - --text-on-info-weak: var(--ink-dark-alpha-9); - --text-on-info-strong: var(--ink-dark-alpha-12); - --text-on-warning-weak: var(--ink-dark-alpha-9); - --text-on-warning-strong: var(--ink-dark-alpha-12); - --text-on-success-weak: var(--apple-light-6); - --text-on-success-strong: var(--apple-light-12); - --text-on-brand-weak: var(--ink-light-alpha-9); - --text-on-brand-weaker: var(--ink-light-alpha-8); - --text-on-brand-strong: var(--ink-light-alpha-12); - --button-secondary-base: #fcfdfd; - --button-secondary-hover: #f9fafa; - --border-base: var(--ink-light-alpha-7); - --border-hover: var(--ink-light-alpha-8); - --border-active: var(--ink-light-alpha-9); - --border-selected: var(--cobalt-light-alpha-9); - --border-disabled: var(--ink-light-alpha-8); - --border-focus: var(--ink-light-alpha-9); - --border-weak-base: var(--ink-light-alpha-5); - --border-strong-base: var(--ink-light-alpha-7); - --border-strong-hover: var(--ink-light-alpha-8); - --border-strong-active: var(--ink-light-alpha-7); - --border-strong-selected: var(--cobalt-light-alpha-6); - --border-strong-disabled: var(--ink-light-alpha-6); - --border-strong-focus: var(--ink-light-alpha-7); - --border-weak-hover: var(--ink-light-alpha-6); - --border-weak-active: var(--ink-light-alpha-7); - --border-weak-selected: var(--cobalt-light-alpha-5); - --border-weak-disabled: var(--ink-light-alpha-6); - --border-weak-focus: var(--ink-light-alpha-7); - --border-interactive-base: var(--cobalt-light-7); - --border-interactive-hover: var(--cobalt-light-8); - --border-interactive-active: var(--cobalt-light-9); - --border-interactive-selected: var(--cobalt-light-9); - --border-interactive-disabled: var(--ink-light-8); - --border-interactive-focus: var(--cobalt-light-9); - --border-success-base: var(--apple-light-6); - --border-success-hover: var(--apple-light-7); - --border-success-selected: var(--apple-light-9); - --border-warning-base: var(--solaris-light-6); - --border-warning-hover: var(--solaris-light-7); - --border-warning-selected: var(--solaris-light-9); - --border-critical-base: var(--ember-light-6); - --border-critical-hover: var(--ember-light-7); - --border-critical-selected: var(--ember-light-9); - --border-info-base: var(--lilac-light-6); - --border-info-hover: var(--lilac-light-7); - --border-info-selected: var(--lilac-light-9); - --icon-base: var(--ink-light-9); - --icon-hover: var(--ink-light-11); - --icon-active: var(--ink-light-12); - --icon-selected: var(--ink-light-12); - --icon-disabled: var(--ink-light-8); - --icon-focus: var(--ink-light-12); - --icon-invert-base: #ffffff; - --icon-weak-base: var(--ink-light-7); - --icon-weak-hover: var(--ink-light-8); - --icon-weak-active: var(--ink-light-9); - --icon-weak-selected: var(--ink-light-10); - --icon-weak-disabled: var(--ink-light-6); - --icon-weak-focus: var(--ink-light-9); - --icon-strong-base: var(--ink-light-12); - --icon-strong-hover: #131515; - --icon-strong-active: #020202; - --icon-strong-selected: #020202; - --icon-strong-disabled: var(--ink-light-8); - --icon-strong-focus: #020202; - --icon-brand-base: var(--ink-light-12); - --icon-interactive-base: var(--cobalt-light-9); - --icon-success-base: var(--apple-light-7); - --icon-success-hover: var(--apple-light-8); - --icon-success-active: var(--apple-light-11); - --icon-warning-base: var(--amber-light-7); - --icon-warning-hover: var(--amber-light-8); - --icon-warning-active: var(--amber-light-11); - --icon-critical-base: var(--ember-light-10); - --icon-critical-hover: var(--ember-light-11); - --icon-critical-active: var(--ember-light-12); - --icon-info-base: var(--lilac-light-7); - --icon-info-hover: var(--lilac-light-8); - --icon-info-active: var(--lilac-light-11); - --icon-on-brand-base: var(--ink-light-alpha-11); - --icon-on-brand-hover: var(--ink-light-alpha-12); - --icon-on-brand-selected: var(--ink-light-alpha-12); - --icon-on-interactive-base: var(--ink-light-1); - --icon-agent-plan-base: var(--purple-light-9); - --icon-agent-docs-base: var(--amber-light-9); - --icon-agent-ask-base: var(--cyan-light-9); - --icon-agent-build-base: var(--cobalt-light-9); - --icon-on-success-base: var(--apple-light-alpha-9); - --icon-on-success-hover: var(--apple-light-alpha-10); - --icon-on-success-selected: var(--apple-light-alpha-11); - --icon-on-warning-base: var(--amber-lightalpha-9); - --icon-on-warning-hover: var(--amber-lightalpha-10); - --icon-on-warning-selected: var(--amber-lightalpha-11); - --icon-on-critical-base: var(--ember-light-alpha-9); - --icon-on-critical-hover: var(--ember-light-alpha-10); - --icon-on-critical-selected: var(--ember-light-alpha-11); - --icon-on-info-base: var(--lilac-light-9); - --icon-on-info-hover: var(--lilac-light-alpha-10); - --icon-on-info-selected: var(--lilac-light-alpha-11); - --icon-diff-add-base: var(--mint-light-11); - --icon-diff-add-hover: var(--mint-light-12); - --icon-diff-add-active: var(--mint-light-12); - --icon-diff-delete-base: var(--ember-light-10); - --icon-diff-delete-hover: var(--ember-light-11); - --syntax-comment: var(--text-weak); - --syntax-regexp: var(--text-base); - --syntax-string: #007663; - --syntax-keyword: var(--text-weak); - --syntax-primitive: #fb7f51; - --syntax-operator: var(--text-weak); - --syntax-variable: var(--text-strong); - --syntax-property: #ec6cc8; - --syntax-type: #738400; - --syntax-constant: #00b2b9; - --syntax-punctuation: var(--text-weak); - --syntax-object: var(--text-strong); - --syntax-success: var(--apple-light-10); - --syntax-warning: var(--amber-light-10); - --syntax-critical: var(--ember-light-9); - --syntax-info: #0091a7; - --syntax-diff-add: var(--mint-light-11); - --syntax-diff-delete: var(--ember-light-11); - --syntax-diff-unknown: #ff0000; - --markdown-heading: #d68c27; - --markdown-text: #1a1a1a; - --markdown-link: #3b7dd8; - --markdown-link-text: #318795; - --markdown-code: #3d9a57; - --markdown-block-quote: #b0851f; - --markdown-emph: #b0851f; - --markdown-strong: #d68c27; - --markdown-horizontal-rule: #8a8a8a; - --markdown-list-item: #3b7dd8; - --markdown-list-enumeration: #318795; - --markdown-image: #3b7dd8; - --markdown-image-text: #318795; - --markdown-code-block: #1a1a1a; - --border-color: #ffffff; - --border-weaker-base: var(--ink-light-alpha-3); - --border-weaker-hover: var(--ink-light-alpha-4); - --border-weaker-active: var(--ink-light-alpha-6); - --border-weaker-selected: var(--cobalt-light-alpha-4); - --border-weaker-disabled: var(--ink-light-alpha-2); - --border-weaker-focus: var(--ink-light-alpha-6); - --button-ghost-hover: var(--ink-light-alpha-2); - --button-ghost-hover2: var(--ink-light-alpha-3); - - @media (prefers-color-scheme: dark) { - --background-base: var(--ink-dark-1); - --background-weak: #171c1c; - --background-strong: #131515; - --background-stronger: #151919; - --surface-base: var(--ink-dark-alpha-2); - --base: var(--ink-dark-alpha-2); - --surface-base-hover: #b8e0e00f; - --surface-base-active: var(--ink-dark-alpha-3); - --surface-base-interactive-active: var(--cobalt-light-alpha-3); - --base2: var(--ink-dark-alpha-2); - --base3: var(--ink-dark-alpha-2); - --surface-inset-base: #0b0e0e7f; - --surface-inset-base-hover: #0b0e0e7f; - --surface-inset-strong: #050606cc; - --surface-inset-strong-hover: #050606cc; - --surface-raised-base: var(--ink-light-alpha-1); - --surface-float-base: var(--ink-dark-1); - --surface-float-base-hover: var(--ink-dark-2); - --surface-raised-base-hover: var(--ink-light-alpha-2); - --surface-raised-base-active: var(--ink-light-alpha-3); - --surface-raised-strong: var(--ink-light-1); - --surface-raised-strong-hover: var(--white); - --surface-raised-stronger: var(--white); - --surface-raised-stronger-hover: var(--white); - --surface-weak: var(--ink-dark-alpha-4); - --surface-weaker: var(--ink-dark-alpha-5); - --surface-strong: var(--ink-dark-alpha-7); - --surface-raised-stronger-non-alpha: var(--white); - --surface-brand-base: var(--yuzu-light-9); - --surface-brand-hover: var(--yuzu-light-10); - --surface-interactive-base: var(--cobalt-light-3); - --surface-interactive-hover: var(--cobalt-light-4); - --surface-interactive-weak: var(--cobalt-light-2); - --surface-interactive-weak-hover: var(--cobalt-light-3); - --surface-success-base: var(--apple-light-3); - --surface-success-weak: var(--apple-light-2); - --surface-success-strong: var(--apple-light-9); - --surface-warning-base: var(--solaris-light-3); - --surface-warning-weak: var(--solaris-light-2); - --surface-warning-strong: var(--solaris-light-9); - --surface-critical-base: var(--ember-light-3); - --surface-critical-weak: var(--ember-light-2); - --surface-critical-strong: var(--ember-light-9); - --surface-info-base: var(--lilac-light-3); - --surface-info-weak: var(--lilac-light-2); - --surface-info-strong: var(--lilac-light-9); - --surface-diff-unchanged-base: #ffffff00; - --surface-diff-skip-base: var(--ink-light-2); - --surface-diff-hidden-base: var(--blue-light-3); - --surface-diff-hidden-weak: var(--blue-light-2); - --surface-diff-hidden-weaker: var(--blue-light-1); - --surface-diff-hidden-strong: var(--blue-light-5); - --surface-diff-hidden-stronger: var(--blue-light-9); - --surface-diff-add-base: var(--mint-light-3); - --surface-diff-add-weak: var(--mint-light-2); - --surface-diff-add-weaker: var(--mint-light-1); - --surface-diff-add-strong: var(--mint-light-5); - --surface-diff-add-stronger: var(--mint-light-9); - --surface-diff-delete-base: var(--ember-light-3); - --surface-diff-delete-weak: var(--ember-light-2); - --surface-diff-delete-weaker: var(--ember-light-1); - --surface-diff-delete-strong: var(--ember-light-6); - --surface-diff-delete-stronger: var(--ember-light-9); - --text-base: var(--ink-light-11); - --input-base: var(--ink-light-1); - --input-hover: var(--ink-light-2); - --input-active: var(--cobalt-light-1); - --input-selected: var(--cobalt-light-4); - --input-focus: var(--cobalt-light-1); - --input-disabled: var(--ink-light-4); - --text-weak: var(--ink-light-9); - --text-weaker: var(--ink-light-8); - --text-strong: var(--ink-light-12); - --text-interactive-base: var(--cobalt-light-9); - --text-on-brand-base: var(--ink-light-alpha-11); - --text-on-interactive-base: var(--ink-light-1); - --text-on-interactive-weak: var(--ink-light-alpha-11); - --text-on-success-base: var(--apple-light-10); - --text-on-critical-base: var(--ember-light-10); - --text-on-critical-weak: var(--ember-light-8); - --text-on-critical-strong: var(--ember-light-12); - --text-on-warning-base: var(--ink-dark-alpha-11); - --text-on-info-base: var(--ink-dark-alpha-11); - --text-diff-add-base: var(--mint-light-11); - --text-diff-delete-base: var(--ember-light-10); - --text-diff-delete-strong: var(--ember-light-12); - --text-diff-add-strong: var(--mint-light-12); - --text-on-info-weak: var(--ink-dark-alpha-9); - --text-on-info-strong: var(--ink-dark-alpha-12); - --text-on-warning-weak: var(--ink-dark-alpha-9); - --text-on-warning-strong: var(--ink-dark-alpha-12); - --text-on-success-weak: var(--apple-light-6); - --text-on-success-strong: var(--apple-light-12); - --text-on-brand-weak: var(--ink-light-alpha-9); - --text-on-brand-weaker: var(--ink-light-alpha-8); - --text-on-brand-strong: var(--ink-light-alpha-12); - --button-secondary-base: #fcfdfd; - --button-secondary-hover: #f9fafa; - --border-base: var(--ink-light-alpha-7); - --border-hover: var(--ink-light-alpha-8); - --border-active: var(--ink-light-alpha-9); - --border-selected: var(--cobalt-light-alpha-9); - --border-disabled: var(--ink-light-alpha-8); - --border-focus: var(--ink-light-alpha-9); - --border-weak-base: var(--ink-light-alpha-5); - --border-strong-base: var(--ink-light-alpha-7); - --border-strong-hover: var(--ink-light-alpha-8); - --border-strong-active: var(--ink-light-alpha-7); - --border-strong-selected: var(--cobalt-light-alpha-6); - --border-strong-disabled: var(--ink-light-alpha-6); - --border-strong-focus: var(--ink-light-alpha-7); - --border-weak-hover: var(--ink-light-alpha-6); - --border-weak-active: var(--ink-light-alpha-7); - --border-weak-selected: var(--cobalt-light-alpha-5); - --border-weak-disabled: var(--ink-light-alpha-6); - --border-weak-focus: var(--ink-light-alpha-7); - --border-interactive-base: var(--cobalt-light-7); - --border-interactive-hover: var(--cobalt-light-8); - --border-interactive-active: var(--cobalt-light-9); - --border-interactive-selected: var(--cobalt-light-9); - --border-interactive-disabled: var(--ink-light-8); - --border-interactive-focus: var(--cobalt-light-9); - --border-success-base: var(--apple-light-6); - --border-success-hover: var(--apple-light-7); - --border-success-selected: var(--apple-light-9); - --border-warning-base: var(--solaris-light-6); - --border-warning-hover: var(--solaris-light-7); - --border-warning-selected: var(--solaris-light-9); - --border-critical-base: var(--ember-light-6); - --border-critical-hover: var(--ember-light-7); - --border-critical-selected: var(--ember-light-9); - --border-info-base: var(--lilac-light-6); - --border-info-hover: var(--lilac-light-7); - --border-info-selected: var(--lilac-light-9); - --icon-base: var(--ink-light-9); - --icon-hover: var(--ink-light-11); - --icon-active: var(--ink-light-12); - --icon-selected: var(--ink-light-12); - --icon-disabled: var(--ink-light-8); - --icon-focus: var(--ink-light-12); - --icon-invert-base: #ffffff; - --icon-weak-base: var(--ink-light-7); - --icon-weak-hover: var(--ink-light-8); - --icon-weak-active: var(--ink-light-9); - --icon-weak-selected: var(--ink-light-10); - --icon-weak-disabled: var(--ink-light-6); - --icon-weak-focus: var(--ink-light-9); - --icon-strong-base: var(--ink-light-12); - --icon-strong-hover: #131515; - --icon-strong-active: #020202; - --icon-strong-selected: #020202; - --icon-strong-disabled: var(--ink-light-8); - --icon-strong-focus: #020202; - --icon-brand-base: var(--ink-light-12); - --icon-interactive-base: var(--cobalt-light-9); - --icon-success-base: var(--apple-light-7); - --icon-success-hover: var(--apple-light-8); - --icon-success-active: var(--apple-light-11); - --icon-warning-base: var(--amber-light-7); - --icon-warning-hover: var(--amber-light-8); - --icon-warning-active: var(--amber-light-11); - --icon-critical-base: var(--ember-light-10); - --icon-critical-hover: var(--ember-light-11); - --icon-critical-active: var(--ember-light-12); - --icon-info-base: var(--lilac-light-7); - --icon-info-hover: var(--lilac-light-8); - --icon-info-active: var(--lilac-light-11); - --icon-on-brand-base: var(--ink-light-alpha-11); - --icon-on-brand-hover: var(--ink-light-alpha-12); - --icon-on-brand-selected: var(--ink-light-alpha-12); - --icon-on-interactive-base: var(--ink-light-1); - --icon-agent-plan-base: var(--purple-light-9); - --icon-agent-docs-base: var(--amber-light-9); - --icon-agent-ask-base: var(--cyan-light-9); - --icon-agent-build-base: var(--cobalt-light-9); - --icon-on-success-base: var(--apple-light-alpha-9); - --icon-on-success-hover: var(--apple-light-alpha-10); - --icon-on-success-selected: var(--apple-light-alpha-11); - --icon-on-warning-base: var(--amber-lightalpha-9); - --icon-on-warning-hover: var(--amber-lightalpha-10); - --icon-on-warning-selected: var(--amber-lightalpha-11); - --icon-on-critical-base: var(--ember-light-alpha-9); - --icon-on-critical-hover: var(--ember-light-alpha-10); - --icon-on-critical-selected: var(--ember-light-alpha-11); - --icon-on-info-base: var(--lilac-light-9); - --icon-on-info-hover: var(--lilac-light-alpha-10); - --icon-on-info-selected: var(--lilac-light-alpha-11); - --icon-diff-add-base: var(--mint-light-11); - --icon-diff-add-hover: var(--mint-light-12); - --icon-diff-add-active: var(--mint-light-12); - --icon-diff-delete-base: var(--ember-light-10); - --icon-diff-delete-hover: var(--ember-light-11); - --syntax-comment: var(--text-weak); - --syntax-regexp: var(--text-base); - --syntax-string: #007663; - --syntax-keyword: var(--text-weak); - --syntax-primitive: #fb7f51; - --syntax-operator: var(--text-weak); - --syntax-variable: var(--text-strong); - --syntax-property: #ec6cc8; - --syntax-type: #738400; - --syntax-constant: #00b2b9; - --syntax-punctuation: var(--text-weak); - --syntax-object: var(--text-strong); - --syntax-success: var(--apple-light-10); - --syntax-warning: var(--amber-light-10); - --syntax-critical: var(--ember-light-9); - --syntax-info: #0091a7; - --syntax-diff-add: var(--mint-light-11); - --syntax-diff-delete: var(--ember-light-11); - --syntax-diff-unknown: #ff0000; - --markdown-heading: #d68c27; - --markdown-text: #1a1a1a; - --markdown-link: #3b7dd8; - --markdown-link-text: #318795; - --markdown-code: #3d9a57; - --markdown-block-quote: #b0851f; - --markdown-emph: #b0851f; - --markdown-strong: #d68c27; - --markdown-horizontal-rule: #8a8a8a; - --markdown-list-item: #3b7dd8; - --markdown-list-enumeration: #318795; - --markdown-image: #3b7dd8; - --markdown-image-text: #318795; - --markdown-code-block: #1a1a1a; - --border-color: #ffffff; - --border-weaker-base: var(--ink-light-alpha-3); - --border-weaker-hover: var(--ink-light-alpha-4); - --border-weaker-active: var(--ink-light-alpha-6); - --border-weaker-selected: var(--cobalt-light-alpha-4); - --border-weaker-disabled: var(--ink-light-alpha-2); - --border-weaker-focus: var(--ink-light-alpha-6); - --button-ghost-hover: var(--ink-light-alpha-2); - --button-ghost-hover2: var(--ink-light-alpha-3); } } diff --git a/packages/ui/src/theme/color.ts b/packages/ui/src/theme/color.ts new file mode 100644 index 000000000..3a0526ca6 --- /dev/null +++ b/packages/ui/src/theme/color.ts @@ -0,0 +1,267 @@ +/** + * Color utilities for theme generation using OKLCH color space. + * OKLCH provides perceptually uniform color manipulation. + */ + +import type { HexColor, OklchColor } from "./types" + +/** + * Convert hex color to RGB values (0-1 range) + */ +export function hexToRgb(hex: HexColor): { r: number; g: number; b: number } { + const h = hex.replace("#", "") + const full = + h.length === 3 + ? h + .split("") + .map((c) => c + c) + .join("") + : h + + const num = parseInt(full, 16) + return { + r: ((num >> 16) & 255) / 255, + g: ((num >> 8) & 255) / 255, + b: (num & 255) / 255, + } +} + +/** + * Convert RGB (0-1 range) to hex color + */ +export function rgbToHex(r: number, g: number, b: number): HexColor { + const toHex = (v: number) => { + const clamped = Math.max(0, Math.min(1, v)) + const int = Math.round(clamped * 255) + return int.toString(16).padStart(2, "0") + } + return `#${toHex(r)}${toHex(g)}${toHex(b)}` +} + +/** + * Convert linear RGB to sRGB + */ +function linearToSrgb(c: number): number { + if (c <= 0.0031308) return c * 12.92 + return 1.055 * Math.pow(c, 1 / 2.4) - 0.055 +} + +/** + * Convert sRGB to linear RGB + */ +function srgbToLinear(c: number): number { + if (c <= 0.04045) return c / 12.92 + return Math.pow((c + 0.055) / 1.055, 2.4) +} + +/** + * Convert RGB to OKLCH + */ +export function rgbToOklch(r: number, g: number, b: number): OklchColor { + // Convert to linear RGB + const lr = srgbToLinear(r) + const lg = srgbToLinear(g) + const lb = srgbToLinear(b) + + // RGB to OKLab matrix multiplication + const l_ = 0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb + const m_ = 0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb + const s_ = 0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb + + const l = Math.cbrt(l_) + const m = Math.cbrt(m_) + const s = Math.cbrt(s_) + + const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s + const a = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s + const bOk = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s + + const C = Math.sqrt(a * a + bOk * bOk) + let H = Math.atan2(bOk, a) * (180 / Math.PI) + if (H < 0) H += 360 + + return { l: L, c: C, h: H } +} + +/** + * Convert OKLCH to RGB + */ +export function oklchToRgb(oklch: OklchColor): { r: number; g: number; b: number } { + const { l: L, c: C, h: H } = oklch + + const a = C * Math.cos((H * Math.PI) / 180) + const b = C * Math.sin((H * Math.PI) / 180) + + const l = L + 0.3963377774 * a + 0.2158037573 * b + const m = L - 0.1055613458 * a - 0.0638541728 * b + const s = L - 0.0894841775 * a - 1.291485548 * b + + const l3 = l * l * l + const m3 = m * m * m + const s3 = s * s * s + + const lr = 4.0767416621 * l3 - 3.3077115913 * m3 + 0.2309699292 * s3 + const lg = -1.2684380046 * l3 + 2.6097574011 * m3 - 0.3413193965 * s3 + const lb = -0.0041960863 * l3 - 0.7034186147 * m3 + 1.707614701 * s3 + + return { + r: linearToSrgb(lr), + g: linearToSrgb(lg), + b: linearToSrgb(lb), + } +} + +/** + * Convert hex to OKLCH + */ +export function hexToOklch(hex: HexColor): OklchColor { + const { r, g, b } = hexToRgb(hex) + return rgbToOklch(r, g, b) +} + +/** + * Convert OKLCH to hex + */ +export function oklchToHex(oklch: OklchColor): HexColor { + const { r, g, b } = oklchToRgb(oklch) + return rgbToHex(r, g, b) +} + +/** + * Generate a 12-step color scale from a seed color. + * Steps 1-4: Very light (backgrounds) + * Steps 5-7: Mid tones (borders, subtle UI) + * Steps 8-9: Saturated (primary buttons, text) + * Steps 10-12: Dark (text, strong accents) + * + * @param seed - The seed color (typically step 9 - the main accent) + * @param isDark - Whether generating for dark mode + */ +export function generateScale(seed: HexColor, isDark: boolean): HexColor[] { + const base = hexToOklch(seed) + const scale: HexColor[] = [] + + // Lightness values for each step (0-1) + // These are tuned to match Radix-style color scales + const lightSteps = isDark + ? [0.15, 0.18, 0.22, 0.26, 0.32, 0.38, 0.46, 0.56, base.l, base.l - 0.05, 0.75, 0.93] + : [0.99, 0.97, 0.94, 0.9, 0.85, 0.79, 0.72, 0.64, base.l, base.l + 0.05, 0.45, 0.25] + + // Chroma multipliers - less saturation at extremes + const chromaMultipliers = isDark + ? [0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.85, 1, 1, 0.9, 0.6] + : [0.1, 0.15, 0.25, 0.35, 0.45, 0.55, 0.7, 0.85, 1, 1, 0.95, 0.85] + + for (let i = 0; i < 12; i++) { + scale.push( + oklchToHex({ + l: lightSteps[i], + c: base.c * chromaMultipliers[i], + h: base.h, + }), + ) + } + + return scale +} + +/** + * Generate a neutral gray scale from a seed color. + * The seed color's hue is used to tint the grays slightly. + * + * @param seed - A neutral-ish color to derive the gray scale from + * @param isDark - Whether generating for dark mode + */ +export function generateNeutralScale(seed: HexColor, isDark: boolean): HexColor[] { + const base = hexToOklch(seed) + const scale: HexColor[] = [] + + // Very low chroma for neutrals - just a hint of the hue + const neutralChroma = Math.min(base.c, 0.02) + + const lightSteps = isDark + ? [0.13, 0.16, 0.2, 0.24, 0.28, 0.33, 0.4, 0.52, 0.58, 0.66, 0.82, 0.96] + : [0.995, 0.98, 0.96, 0.94, 0.91, 0.88, 0.84, 0.78, 0.62, 0.56, 0.46, 0.2] + + for (let i = 0; i < 12; i++) { + scale.push( + oklchToHex({ + l: lightSteps[i], + c: neutralChroma, + h: base.h, + }), + ) + } + + return scale +} + +/** + * Generate alpha variants of a color scale. + * Returns hex colors with alpha pre-multiplied against white (light) or black (dark). + */ +export function generateAlphaScale(scale: HexColor[], isDark: boolean): HexColor[] { + // Alpha values for each step + const alphas = isDark + ? [0.02, 0.04, 0.08, 0.12, 0.16, 0.2, 0.26, 0.36, 0.44, 0.52, 0.76, 0.96] + : [0.01, 0.03, 0.06, 0.09, 0.12, 0.15, 0.2, 0.28, 0.48, 0.56, 0.64, 0.88] + + return scale.map((hex, i) => { + const { r, g, b } = hexToRgb(hex) + const a = alphas[i] + + // Pre-multiply against white (light) or black (dark) + const bg = isDark ? 0 : 1 + const blendedR = r * a + bg * (1 - a) + const blendedG = g * a + bg * (1 - a) + const blendedB = b * a + bg * (1 - a) + + // Return as hex with alpha encoded in the color itself + // For true alpha, we'd need rgba(), but this approximates it + return rgbToHex(blendedR, blendedG, blendedB) + }) +} + +/** + * Mix two colors together + */ +export function mixColors(color1: HexColor, color2: HexColor, amount: number): HexColor { + const c1 = hexToOklch(color1) + const c2 = hexToOklch(color2) + + return oklchToHex({ + l: c1.l + (c2.l - c1.l) * amount, + c: c1.c + (c2.c - c1.c) * amount, + h: c1.h + (c2.h - c1.h) * amount, + }) +} + +/** + * Lighten a color by a given amount (0-1) + */ +export function lighten(color: HexColor, amount: number): HexColor { + const oklch = hexToOklch(color) + return oklchToHex({ + ...oklch, + l: Math.min(1, oklch.l + amount), + }) +} + +/** + * Darken a color by a given amount (0-1) + */ +export function darken(color: HexColor, amount: number): HexColor { + const oklch = hexToOklch(color) + return oklchToHex({ + ...oklch, + l: Math.max(0, oklch.l - amount), + }) +} + +/** + * Adjust the alpha/opacity of a hex color (returns rgba string) + */ +export function withAlpha(color: HexColor, alpha: number): string { + const { r, g, b } = hexToRgb(color) + return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${alpha})` +} diff --git a/packages/ui/src/theme/context.tsx b/packages/ui/src/theme/context.tsx new file mode 100644 index 000000000..d8ca6d507 --- /dev/null +++ b/packages/ui/src/theme/context.tsx @@ -0,0 +1,280 @@ +/** + * Theme context for SolidJS applications. + * Provides reactive theme management with localStorage persistence and caching. + * + * Works in conjunction with the preload script to provide zero-FOUC theming: + * 1. Preload script applies cached CSS immediately from localStorage + * 2. ThemeProvider takes over, resolves theme, and updates cache + */ + +import { + createContext, + useContext, + createSignal, + onMount, + onCleanup, + createEffect, + type JSX, + type Accessor, +} from "solid-js" +import type { DesktopTheme } from "./types" +import { resolveThemeVariant, themeToCss } from "./resolve" +import { STORAGE_KEYS, getThemeCacheKey } from "./preload" +import { DEFAULT_THEMES } from "./default-themes" + +export type ColorScheme = "light" | "dark" | "system" + +interface ThemeContextValue { + /** Currently active theme ID */ + themeId: Accessor<string> + /** Current color scheme preference */ + colorScheme: Accessor<ColorScheme> + /** Resolved current mode (light or dark) */ + mode: Accessor<"light" | "dark"> + /** All available themes */ + themes: Accessor<Record<string, DesktopTheme>> + /** Set the active theme by ID */ + setTheme: (id: string) => void + /** Set color scheme preference */ + setColorScheme: (scheme: ColorScheme) => void + /** Register a custom theme */ + registerTheme: (theme: DesktopTheme) => void +} + +const ThemeContext = createContext<ThemeContextValue>() + +/** + * Static tokens that don't change between themes + */ +const STATIC_TOKENS = ` + --font-family-sans: "Inter", "Inter Fallback"; + --font-family-sans--font-feature-settings: "ss03" 1; + --font-family-mono: "IBM Plex Mono", "IBM Plex Mono Fallback"; + --font-family-mono--font-feature-settings: "ss01" 1; + --font-size-small: 13px; + --font-size-base: 14px; + --font-size-large: 16px; + --font-size-x-large: 20px; + --font-weight-regular: 400; + --font-weight-medium: 500; + --line-height-large: 150%; + --line-height-x-large: 180%; + --line-height-2x-large: 200%; + --letter-spacing-normal: 0; + --letter-spacing-tight: -0.16; + --letter-spacing-tightest: -0.32; + --paragraph-spacing-base: 0; + --spacing: 0.25rem; + --breakpoint-sm: 40rem; + --breakpoint-md: 48rem; + --breakpoint-lg: 64rem; + --breakpoint-xl: 80rem; + --breakpoint-2xl: 96rem; + --container-3xs: 16rem; + --container-2xs: 18rem; + --container-xs: 20rem; + --container-sm: 24rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-xl: 36rem; + --container-2xl: 42rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --container-7xl: 80rem; + --radius-xs: 0.125rem; + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.625rem; + --shadow-xs: 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-md: 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12); + --shadow-xs-border: 0 0 0 1px var(--border-base), 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-xs-border-base: 0 0 0 1px var(--border-weak-base), 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-xs-border-select: 0 0 0 3px var(--border-weak-selected), 0 0 0 1px var(--border-selected), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12); + --shadow-xs-border-focus: 0 0 0 1px var(--border-base), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), 0 0 0 2px var(--background-weak), 0 0 0 3px var(--border-selected); +` + +const THEME_STYLE_ID = "oc-theme" + +function ensureThemeStyleElement(): HTMLStyleElement { + const existing = document.getElementById(THEME_STYLE_ID) as HTMLStyleElement | null + if (existing) { + return existing + } + const element = document.createElement("style") + element.id = THEME_STYLE_ID + document.head.appendChild(element) + return element +} + +/** + * Resolve a mode from system preference + */ +function getSystemMode(): "light" | "dark" { + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" +} + +/** + * Apply theme CSS to the document + */ +function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "dark"): void { + const isDark = mode === "dark" + const variant = isDark ? theme.dark : theme.light + const tokens = resolveThemeVariant(variant, isDark) + const css = themeToCss(tokens) + + // Cache to localStorage for preload script + const cacheKey = getThemeCacheKey(themeId, mode) + try { + localStorage.setItem(cacheKey, css) + } catch { + // localStorage might be full or disabled + } + + // Build full CSS + const fullCss = `:root { + ${STATIC_TOKENS} + color-scheme: ${mode}; + --text-mix-blend-mode: ${isDark ? "plus-lighter" : "multiply"}; + ${css} +}` + + // Remove preload style if it exists + const preloadStyle = document.getElementById("oc-theme-preload") + if (preloadStyle) { + preloadStyle.remove() + } + + const themeStyleElement = ensureThemeStyleElement() + themeStyleElement.textContent = fullCss + + // Update data attributes + document.documentElement.dataset.theme = themeId + document.documentElement.dataset.colorScheme = mode +} + +/** + * Cache both light and dark variants of a theme + */ +function cacheThemeVariants(theme: DesktopTheme, themeId: string): void { + for (const mode of ["light", "dark"] as const) { + const isDark = mode === "dark" + const variant = isDark ? theme.dark : theme.light + const tokens = resolveThemeVariant(variant, isDark) + const css = themeToCss(tokens) + const cacheKey = getThemeCacheKey(themeId, mode) + try { + localStorage.setItem(cacheKey, css) + } catch { + // localStorage might be full or disabled + } + } +} + +export function ThemeProvider(props: { children: JSX.Element; defaultTheme?: string }) { + const [themes, setThemes] = createSignal<Record<string, DesktopTheme>>(DEFAULT_THEMES) + const [themeId, setThemeIdSignal] = createSignal(props.defaultTheme ?? "oc-1") + const [colorScheme, setColorSchemeSignal] = createSignal<ColorScheme>("system") + const [mode, setMode] = createSignal<"light" | "dark">(getSystemMode()) + + // Listen for system color scheme changes + onMount(() => { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") + const handler = () => { + if (colorScheme() === "system") { + setMode(getSystemMode()) + } + } + mediaQuery.addEventListener("change", handler) + onCleanup(() => mediaQuery.removeEventListener("change", handler)) + + // Load saved preferences + const savedTheme = localStorage.getItem(STORAGE_KEYS.THEME_ID) + const savedScheme = localStorage.getItem(STORAGE_KEYS.COLOR_SCHEME) as ColorScheme | null + + if (savedTheme && themes()[savedTheme]) { + setThemeIdSignal(savedTheme) + } + + if (savedScheme) { + setColorSchemeSignal(savedScheme) + if (savedScheme !== "system") { + setMode(savedScheme) + } + } + + // Cache current theme variants for future preloads + const currentTheme = themes()[themeId()] + if (currentTheme) { + cacheThemeVariants(currentTheme, themeId()) + } + }) + + // Apply theme when themeId or mode changes + createEffect(() => { + const id = themeId() + const m = mode() + const theme = themes()[id] + if (theme) { + applyThemeCss(theme, id, m) + } + }) + + const setTheme = (id: string) => { + const theme = themes()[id] + if (!theme) { + console.warn(`Theme "${id}" not found`) + return + } + + setThemeIdSignal(id) + localStorage.setItem(STORAGE_KEYS.THEME_ID, id) + + // Cache both variants for future preloads + cacheThemeVariants(theme, id) + } + + const setColorSchemePref = (scheme: ColorScheme) => { + setColorSchemeSignal(scheme) + localStorage.setItem(STORAGE_KEYS.COLOR_SCHEME, scheme) + + if (scheme === "system") { + setMode(getSystemMode()) + } else { + setMode(scheme) + } + } + + const registerTheme = (theme: DesktopTheme) => { + setThemes((prev) => ({ + ...prev, + [theme.id]: theme, + })) + } + + return ( + <ThemeContext.Provider + value={{ + themeId, + colorScheme, + mode, + themes, + setTheme, + setColorScheme: setColorSchemePref, + registerTheme, + }} + > + {props.children} + </ThemeContext.Provider> + ) +} + +export function useTheme(): ThemeContextValue { + const ctx = useContext(ThemeContext) + if (!ctx) { + throw new Error("useTheme must be used within a ThemeProvider") + } + return ctx +} diff --git a/packages/ui/src/theme/default-themes.ts b/packages/ui/src/theme/default-themes.ts new file mode 100644 index 000000000..749d5e97c --- /dev/null +++ b/packages/ui/src/theme/default-themes.ts @@ -0,0 +1,35 @@ +import type { DesktopTheme } from "./types" +import oc1ThemeJson from "./themes/oc-1.json" +import tokyoThemeJson from "./themes/tokyonight.json" +import draculaThemeJson from "./themes/dracula.json" +import monokaiThemeJson from "./themes/monokai.json" +import solarizedThemeJson from "./themes/solarized.json" +import nordThemeJson from "./themes/nord.json" +import catppuccinThemeJson from "./themes/catppuccin.json" +import ayuThemeJson from "./themes/ayu.json" +import oneDarkProThemeJson from "./themes/onedarkpro.json" +import shadesOfPurpleThemeJson from "./themes/shadesofpurple.json" + +export const oc1Theme = oc1ThemeJson as DesktopTheme +export const tokyonightTheme = tokyoThemeJson as DesktopTheme +export const draculaTheme = draculaThemeJson as DesktopTheme +export const monokaiTheme = monokaiThemeJson as DesktopTheme +export const solarizedTheme = solarizedThemeJson as DesktopTheme +export const nordTheme = nordThemeJson as DesktopTheme +export const catppuccinTheme = catppuccinThemeJson as DesktopTheme +export const ayuTheme = ayuThemeJson as DesktopTheme +export const oneDarkProTheme = oneDarkProThemeJson as DesktopTheme +export const shadesOfPurpleTheme = shadesOfPurpleThemeJson as DesktopTheme + +export const DEFAULT_THEMES: Record<string, DesktopTheme> = { + "oc-1": oc1Theme, + tokyonight: tokyonightTheme, + dracula: draculaTheme, + monokai: monokaiTheme, + solarized: solarizedTheme, + nord: nordTheme, + catppuccin: catppuccinTheme, + ayu: ayuTheme, + onedarkpro: oneDarkProTheme, + shadesofpurple: shadesOfPurpleTheme, +} diff --git a/packages/ui/src/theme/desktop-theme.schema.json b/packages/ui/src/theme/desktop-theme.schema.json new file mode 100644 index 000000000..b60a8f37c --- /dev/null +++ b/packages/ui/src/theme/desktop-theme.schema.json @@ -0,0 +1,104 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://opencode.ai/desktop-theme.json", + "title": "OpenCode Desktop Theme", + "description": "A theme definition for the OpenCode desktop application", + "type": "object", + "required": ["name", "id", "light", "dark"], + "properties": { + "$schema": { + "type": "string", + "description": "JSON Schema reference" + }, + "name": { + "type": "string", + "description": "Human-readable theme name" + }, + "id": { + "type": "string", + "description": "Unique theme identifier (slug)", + "pattern": "^[a-z0-9-]+$" + }, + "light": { + "$ref": "#/definitions/ThemeVariant", + "description": "Light mode color variant" + }, + "dark": { + "$ref": "#/definitions/ThemeVariant", + "description": "Dark mode color variant" + } + }, + "definitions": { + "HexColor": { + "type": "string", + "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$", + "description": "A hex color value like #fff, #ffff, #ffffff, or #ffffffff" + }, + "ColorValue": { + "type": "string", + "pattern": "^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|var\(--[a-z0-9-]+\))$", + "description": "Either a hex color value (#rgb/#rgba/#rrggbb/#rrggbbaa) or a CSS variable reference" + }, + "ThemeSeedColors": { + "type": "object", + "description": "The minimum set of colors needed to generate a theme", + "required": ["neutral", "primary", "success", "warning", "error", "info", "interactive", "diffAdd", "diffDelete"], + "properties": { + "neutral": { + "$ref": "#/definitions/HexColor", + "description": "Base neutral color for generating the gray scale" + }, + "primary": { + "$ref": "#/definitions/HexColor", + "description": "Primary brand/accent color" + }, + "success": { + "$ref": "#/definitions/HexColor", + "description": "Success state color (typically green)" + }, + "warning": { + "$ref": "#/definitions/HexColor", + "description": "Warning state color (typically yellow/orange)" + }, + "error": { + "$ref": "#/definitions/HexColor", + "description": "Error/critical state color (typically red)" + }, + "info": { + "$ref": "#/definitions/HexColor", + "description": "Informational state color (typically purple/blue)" + }, + "interactive": { + "$ref": "#/definitions/HexColor", + "description": "Interactive element color (links, buttons)" + }, + "diffAdd": { + "$ref": "#/definitions/HexColor", + "description": "Color for diff additions" + }, + "diffDelete": { + "$ref": "#/definitions/HexColor", + "description": "Color for diff deletions" + } + } + }, + "ThemeVariant": { + "type": "object", + "description": "A theme variant (light or dark) with seed colors and optional overrides", + "required": ["seeds"], + "properties": { + "seeds": { + "$ref": "#/definitions/ThemeSeedColors", + "description": "Seed colors used to generate the full palette" + }, + "overrides": { + "type": "object", + "description": "Optional direct overrides for any CSS variable (without -- prefix)", + "additionalProperties": { + "$ref": "#/definitions/ColorValue" + } + } + } + } + } +} diff --git a/packages/ui/src/theme/index.ts b/packages/ui/src/theme/index.ts new file mode 100644 index 000000000..8f3da4ca1 --- /dev/null +++ b/packages/ui/src/theme/index.ts @@ -0,0 +1,71 @@ +/** + * Desktop Theme System + * + * Provides JSON-based theming for the desktop app. Unlike TUI themes, + * desktop themes use more design tokens and generate full color scales + * from seed colors. + * + * Usage: + * ```ts + * import { applyTheme } from "@opencode/ui/theme" + * import myTheme from "./themes/my-theme.json" + * + * applyTheme(myTheme) + * ``` + */ + +// Types +export type { + DesktopTheme, + ThemeSeedColors, + ThemeVariant, + HexColor, + OklchColor, + ResolvedTheme, + ColorValue, + CssVarRef, +} from "./types" + +// Color utilities +export { + hexToRgb, + rgbToHex, + hexToOklch, + oklchToHex, + rgbToOklch, + oklchToRgb, + generateScale, + generateNeutralScale, + generateAlphaScale, + mixColors, + lighten, + darken, + withAlpha, +} from "./color" + +// Theme resolution +export { resolveThemeVariant, resolveTheme, themeToCss } from "./resolve" + +// Theme loader +export { applyTheme, loadThemeFromUrl, getActiveTheme, removeTheme, setColorScheme } from "./loader" + +// Theme context (SolidJS) +export { ThemeProvider, useTheme, type ColorScheme } from "./context" + +// Preload script utilities +export { generatePreloadScript, generatePreloadScriptFormatted, STORAGE_KEYS, getThemeCacheKey } from "./preload" + +// Default themes +export { + DEFAULT_THEMES, + oc1Theme, + tokyonightTheme, + draculaTheme, + monokaiTheme, + solarizedTheme, + nordTheme, + catppuccinTheme, + ayuTheme, + oneDarkProTheme, + shadesOfPurpleTheme, +} from "./default-themes" diff --git a/packages/ui/src/theme/loader.ts b/packages/ui/src/theme/loader.ts new file mode 100644 index 000000000..b25c833dd --- /dev/null +++ b/packages/ui/src/theme/loader.ts @@ -0,0 +1,213 @@ +/** + * Theme loader - loads theme JSON files and applies them to the DOM. + */ + +import type { DesktopTheme, ResolvedTheme } from "./types" +import { resolveThemeVariant, themeToCss } from "./resolve" + +/** Currently active theme */ +let activeTheme: DesktopTheme | null = null + +const THEME_STYLE_ID = "opencode-theme" + +function ensureLoaderStyleElement(): HTMLStyleElement { + const existing = document.getElementById(THEME_STYLE_ID) as HTMLStyleElement | null + if (existing) { + return existing + } + const element = document.createElement("style") + element.id = THEME_STYLE_ID + document.head.appendChild(element) + return element +} + +/** + * Load and apply a theme to the document. + * Creates or updates a <style> element with the theme's CSS custom properties. + * + * @param theme - The desktop theme to apply + * @param themeId - Optional theme ID for the data-theme attribute + */ +export function applyTheme(theme: DesktopTheme, themeId?: string): void { + activeTheme = theme + + // Resolve both variants + const lightTokens = resolveThemeVariant(theme.light, false) + const darkTokens = resolveThemeVariant(theme.dark, true) + + const targetThemeId = themeId ?? theme.id + + // Build the CSS + const css = buildThemeCss(lightTokens, darkTokens, targetThemeId) + + const themeStyleElement = ensureLoaderStyleElement() + themeStyleElement.textContent = css + + document.documentElement.setAttribute("data-theme", targetThemeId) +} + +/** + * Build CSS string from resolved theme tokens + */ +function buildThemeCss(light: ResolvedTheme, dark: ResolvedTheme, themeId: string): string { + const isDefaultTheme = themeId === "oc-1" + + // Static tokens that don't change between themes + const staticTokens = ` + --font-family-sans: "Inter", "Inter Fallback"; + --font-family-sans--font-feature-settings: "ss03" 1; + --font-family-mono: "IBM Plex Mono", "IBM Plex Mono Fallback"; + --font-family-mono--font-feature-settings: "ss01" 1; + + --font-size-small: 13px; + --font-size-base: 14px; + --font-size-large: 16px; + --font-size-x-large: 20px; + --font-weight-regular: 400; + --font-weight-medium: 500; + --line-height-large: 150%; + --line-height-x-large: 180%; + --line-height-2x-large: 200%; + --letter-spacing-normal: 0; + --letter-spacing-tight: -0.1599999964237213; + --letter-spacing-tightest: -0.3199999928474426; + --paragraph-spacing-base: 0; + + --spacing: 0.25rem; + + --breakpoint-sm: 40rem; + --breakpoint-md: 48rem; + --breakpoint-lg: 64rem; + --breakpoint-xl: 80rem; + --breakpoint-2xl: 96rem; + + --container-3xs: 16rem; + --container-2xs: 18rem; + --container-xs: 20rem; + --container-sm: 24rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-xl: 36rem; + --container-2xl: 42rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --container-7xl: 80rem; + + --radius-xs: 0.125rem; + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.625rem; + + --shadow-xs: + 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-md: + 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12); + --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), + 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-xs-border-base: + 0 0 0 1px var(--border-weak-base, rgba(17, 0, 0, 0.12)), 0 1px 2px -1px rgba(19, 16, 16, 0.04), + 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); + --shadow-xs-border-select: + 0 0 0 3px var(--border-weak-selected, rgba(1, 103, 255, 0.29)), + 0 0 0 1px var(--border-selected, rgba(0, 74, 255, 0.99)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12); + --shadow-xs-border-focus: + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), 0 0 0 2px var(--background-weak, #f1f0f0), + 0 0 0 3px var(--border-selected, rgba(0, 74, 255, 0.99));` + + const lightCss = themeToCss(light) + const darkCss = themeToCss(dark) + + // For the default theme, we use :root directly + // For named themes, we use the [data-theme] selector + if (isDefaultTheme) { + return ` +:root { + ${staticTokens} + + color-scheme: light; + --text-mix-blend-mode: multiply; + + ${lightCss} + + @media (prefers-color-scheme: dark) { + color-scheme: dark; + --text-mix-blend-mode: plus-lighter; + + ${darkCss} + } +} +` + } + + return ` +html[data-theme="${themeId}"] { + ${staticTokens} + + color-scheme: light; + --text-mix-blend-mode: multiply; + + ${lightCss} + + @media (prefers-color-scheme: dark) { + color-scheme: dark; + --text-mix-blend-mode: plus-lighter; + + ${darkCss} + } +} +` +} + +/** + * Load a theme from a JSON file URL + */ +export async function loadThemeFromUrl(url: string): Promise<DesktopTheme> { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`Failed to load theme from ${url}: ${response.statusText}`) + } + return response.json() +} + +/** + * Get the currently active theme + */ +export function getActiveTheme(): DesktopTheme | null { + const activeId = document.documentElement.getAttribute("data-theme") + if (!activeId) { + return null + } + if (activeTheme?.id === activeId) { + return activeTheme + } + return null +} + +/** + * Remove the current theme and clean up + */ +export function removeTheme(): void { + activeTheme = null + const existingElement = document.getElementById(THEME_STYLE_ID) + if (existingElement) { + existingElement.remove() + } + document.documentElement.removeAttribute("data-theme") +} + +/** + * Force a specific color scheme (light/dark) regardless of system preference + */ +export function setColorScheme(scheme: "light" | "dark" | "auto"): void { + if (scheme === "auto") { + document.documentElement.style.removeProperty("color-scheme") + } else { + document.documentElement.style.setProperty("color-scheme", scheme) + } +} diff --git a/packages/ui/src/theme/preload.ts b/packages/ui/src/theme/preload.ts new file mode 100644 index 000000000..a5eadb323 --- /dev/null +++ b/packages/ui/src/theme/preload.ts @@ -0,0 +1,115 @@ +/** + * Theme preload script generator. + * + * Generates a minimal inline script that: + * 1. Reads theme preferences from localStorage + * 2. Applies cached theme CSS immediately (avoiding FOUC) + * 3. Falls back to embedded default theme CSS on first visit + * + * The script should be placed in the document <head> before any stylesheets. + */ + +import { resolveThemeVariant, themeToCss } from "./resolve" +import type { DesktopTheme } from "./types" +import oc1Theme from "./themes/oc-1.json" + +// Storage keys used by both the preload script and the ThemeProvider +export const STORAGE_KEYS = { + THEME_ID: "opencode-theme-id", + COLOR_SCHEME: "opencode-color-scheme", + THEME_CSS_PREFIX: "opencode-theme-css", +} as const + +/** + * Get the localStorage key for cached theme CSS + */ +export function getThemeCacheKey(themeId: string, mode: "light" | "dark"): string { + return `${STORAGE_KEYS.THEME_CSS_PREFIX}-${themeId}-${mode}` +} + +/** + * Generate the embedded default theme CSS for the preload script. + * This is used as a fallback when no cached theme exists. + */ +function generateEmbeddedDefaults(): { light: string; dark: string } { + const theme = oc1Theme as DesktopTheme + const lightTokens = resolveThemeVariant(theme.light, false) + const darkTokens = resolveThemeVariant(theme.dark, true) + + return { + light: themeToCss(lightTokens), + dark: themeToCss(darkTokens), + } +} + +/** + * Static tokens that don't change between themes. + * These are embedded in the preload CSS. + */ +const STATIC_TOKENS = `--font-family-sans:"Inter","Inter Fallback";--font-family-sans--font-feature-settings:"ss03" 1;--font-family-mono:"IBM Plex Mono","IBM Plex Mono Fallback";--font-family-mono--font-feature-settings:"ss01" 1;--font-size-small:13px;--font-size-base:14px;--font-size-large:16px;--font-size-x-large:20px;--font-weight-regular:400;--font-weight-medium:500;--line-height-large:150%;--line-height-x-large:180%;--line-height-2x-large:200%;--letter-spacing-normal:0;--letter-spacing-tight:-0.16;--letter-spacing-tightest:-0.32;--paragraph-spacing-base:0;--spacing:0.25rem;--breakpoint-sm:40rem;--breakpoint-md:48rem;--breakpoint-lg:64rem;--breakpoint-xl:80rem;--breakpoint-2xl:96rem;--container-3xs:16rem;--container-2xs:18rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--container-7xl:80rem;--radius-xs:0.125rem;--radius-sm:0.25rem;--radius-md:0.375rem;--radius-lg:0.5rem;--radius-xl:0.625rem;--shadow-xs:0 1px 2px -1px rgba(19,16,16,0.04),0 1px 2px 0 rgba(19,16,16,0.06),0 1px 3px 0 rgba(19,16,16,0.08);--shadow-md:0 6px 8px -4px rgba(19,16,16,0.12),0 4px 3px -2px rgba(19,16,16,0.12),0 1px 2px -1px rgba(19,16,16,0.12)` + +/** + * Generate the inline preload script. + * + * This script should be placed in the document <head> to avoid FOUC. + * It reads theme preferences from localStorage and applies the theme CSS + * immediately, falling back to an embedded default theme. + */ +export function generatePreloadScript(): string { + const defaults = generateEmbeddedDefaults() + + // Minified version of the preload logic + // Variables: T=themeId, S=scheme, D=isDark, M=mode, C=css, K=cacheKey + return `(function(){var T=localStorage.getItem("${STORAGE_KEYS.THEME_ID}")||"oc-1";var S=localStorage.getItem("${STORAGE_KEYS.COLOR_SCHEME}")||"system";var D=S==="dark"||(S==="system"&&matchMedia("(prefers-color-scheme:dark)").matches);var M=D?"dark":"light";var K="${STORAGE_KEYS.THEME_CSS_PREFIX}-"+T+"-"+M;var C=localStorage.getItem(K);if(!C&&T==="oc-1"){C=D?${JSON.stringify(defaults.dark)}:${JSON.stringify(defaults.light)}}if(C){var s=document.createElement("style");s.id="oc-theme-preload";s.textContent=":root{${STATIC_TOKENS};color-scheme:"+M+";--text-mix-blend-mode:"+(D?"plus-lighter":"multiply")+";"+C+"}";document.head.appendChild(s)}document.documentElement.dataset.theme=T;document.documentElement.dataset.colorScheme=M})();` +} + +/** + * Generate a formatted (readable) version of the preload script. + * Useful for debugging. + */ +export function generatePreloadScriptFormatted(): string { + const defaults = generateEmbeddedDefaults() + + return `(function() { + var THEME_KEY = "${STORAGE_KEYS.THEME_ID}"; + var SCHEME_KEY = "${STORAGE_KEYS.COLOR_SCHEME}"; + var CSS_PREFIX = "${STORAGE_KEYS.THEME_CSS_PREFIX}"; + + // Read preferences from localStorage + var themeId = localStorage.getItem(THEME_KEY) || "oc-1"; + var scheme = localStorage.getItem(SCHEME_KEY) || "system"; + + // Determine if dark mode + var isDark = scheme === "dark" || + (scheme === "system" && matchMedia("(prefers-color-scheme: dark)").matches); + var mode = isDark ? "dark" : "light"; + + // Try to get cached CSS for this theme + mode + var cacheKey = CSS_PREFIX + "-" + themeId + "-" + mode; + var css = localStorage.getItem(cacheKey); + + // Fallback to embedded default for oc-1 theme + if (!css && themeId === "oc-1") { + css = isDark + ? ${JSON.stringify(defaults.dark)} + : ${JSON.stringify(defaults.light)}; + } + + // Apply CSS if we have it + if (css) { + var style = document.createElement("style"); + style.id = "oc-theme-preload"; + style.textContent = ":root{" + + "${STATIC_TOKENS};" + + "color-scheme:" + mode + ";" + + "--text-mix-blend-mode:" + (isDark ? "plus-lighter" : "multiply") + ";" + + css + + "}"; + document.head.appendChild(style); + } + + // Set data attributes for CSS/JS reference + document.documentElement.dataset.theme = themeId; + document.documentElement.dataset.colorScheme = mode; +})();` +} diff --git a/packages/ui/src/theme/resolve.ts b/packages/ui/src/theme/resolve.ts new file mode 100644 index 000000000..e8b6d9c3e --- /dev/null +++ b/packages/ui/src/theme/resolve.ts @@ -0,0 +1,365 @@ +/** + * Theme resolver - generates all CSS custom properties from theme seed colors. + */ + +import type { ColorValue, DesktopTheme, HexColor, ResolvedTheme, ThemeVariant } from "./types" +import { generateNeutralScale, generateScale, hexToOklch, oklchToHex, withAlpha } from "./color" + +/** + * Resolve a theme variant to all CSS custom properties + */ +export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): ResolvedTheme { + const { seeds, overrides = {} } = variant + + // Generate color scales + const neutral = generateNeutralScale(seeds.neutral, isDark) + const primary = generateScale(seeds.primary, isDark) + const success = generateScale(seeds.success, isDark) + const warning = generateScale(seeds.warning, isDark) + const error = generateScale(seeds.error, isDark) + const info = generateScale(seeds.info, isDark) + const interactive = generateScale(seeds.interactive, isDark) + const diffAdd = generateScale(seeds.diffAdd, isDark) + const diffDelete = generateScale(seeds.diffDelete, isDark) + + // Generate alpha variants for neutral + const neutralAlpha = generateNeutralAlphaScale(neutral, isDark) + + // Build the full token map + const tokens: ResolvedTheme = {} + + // === Background tokens === + tokens["background-base"] = neutral[0] + tokens["background-weak"] = neutral[2] + tokens["background-strong"] = neutral[0] + tokens["background-stronger"] = isDark ? neutral[1] : "#fcfcfc" + + // === Surface tokens === + tokens["surface-base"] = neutralAlpha[1] + tokens["base"] = neutralAlpha[1] + tokens["surface-base-hover"] = neutralAlpha[2] + tokens["surface-base-active"] = neutralAlpha[2] + tokens["surface-base-interactive-active"] = withAlpha(interactive[2], 0.3) as ColorValue + tokens["base2"] = neutralAlpha[1] + tokens["base3"] = neutralAlpha[1] + tokens["surface-inset-base"] = neutralAlpha[1] + tokens["surface-inset-base-hover"] = neutralAlpha[2] + tokens["surface-inset-strong"] = isDark + ? (withAlpha(neutral[0], 0.5) as ColorValue) + : (withAlpha(neutral[3], 0.09) as ColorValue) + tokens["surface-inset-strong-hover"] = tokens["surface-inset-strong"] + tokens["surface-raised-base"] = neutralAlpha[0] + tokens["surface-float-base"] = isDark ? neutral[0] : neutral[11] + tokens["surface-float-base-hover"] = isDark ? neutral[1] : neutral[10] + tokens["surface-raised-base-hover"] = neutralAlpha[1] + tokens["surface-raised-base-active"] = neutralAlpha[2] + tokens["surface-raised-strong"] = isDark ? neutralAlpha[3] : neutral[0] + tokens["surface-raised-strong-hover"] = isDark ? neutralAlpha[5] : "#ffffff" + tokens["surface-raised-stronger"] = isDark ? neutralAlpha[5] : "#ffffff" + tokens["surface-raised-stronger-hover"] = isDark ? neutralAlpha[6] : "#ffffff" + tokens["surface-weak"] = neutralAlpha[2] + tokens["surface-weaker"] = neutralAlpha[3] + tokens["surface-strong"] = isDark ? neutralAlpha[6] : "#ffffff" + tokens["surface-raised-stronger-non-alpha"] = isDark ? neutral[2] : "#ffffff" + + // Brand surface + tokens["surface-brand-base"] = primary[8] + tokens["surface-brand-hover"] = primary[9] + + // Interactive surfaces + tokens["surface-interactive-base"] = interactive[2] + tokens["surface-interactive-hover"] = interactive[3] + tokens["surface-interactive-weak"] = interactive[1] + tokens["surface-interactive-weak-hover"] = interactive[2] + + // Status surfaces + tokens["surface-success-base"] = success[2] + tokens["surface-success-weak"] = success[1] + tokens["surface-success-strong"] = success[8] + tokens["surface-warning-base"] = warning[2] + tokens["surface-warning-weak"] = warning[1] + tokens["surface-warning-strong"] = warning[8] + tokens["surface-critical-base"] = error[2] + tokens["surface-critical-weak"] = error[1] + tokens["surface-critical-strong"] = error[8] + tokens["surface-info-base"] = info[2] + tokens["surface-info-weak"] = info[1] + tokens["surface-info-strong"] = info[8] + + // Diff surfaces + tokens["surface-diff-unchanged-base"] = isDark ? neutral[0] : "#ffffff00" + tokens["surface-diff-skip-base"] = isDark ? neutralAlpha[0] : neutral[1] + tokens["surface-diff-hidden-base"] = interactive[isDark ? 1 : 2] + tokens["surface-diff-hidden-weak"] = interactive[isDark ? 0 : 1] + tokens["surface-diff-hidden-weaker"] = interactive[isDark ? 2 : 0] + tokens["surface-diff-hidden-strong"] = interactive[4] + tokens["surface-diff-hidden-stronger"] = interactive[isDark ? 10 : 8] + tokens["surface-diff-add-base"] = diffAdd[2] + tokens["surface-diff-add-weak"] = diffAdd[isDark ? 3 : 1] + tokens["surface-diff-add-weaker"] = diffAdd[isDark ? 2 : 0] + tokens["surface-diff-add-strong"] = diffAdd[4] + tokens["surface-diff-add-stronger"] = diffAdd[isDark ? 10 : 8] + tokens["surface-diff-delete-base"] = diffDelete[2] + tokens["surface-diff-delete-weak"] = diffDelete[isDark ? 3 : 1] + tokens["surface-diff-delete-weaker"] = diffDelete[isDark ? 2 : 0] + tokens["surface-diff-delete-strong"] = diffDelete[isDark ? 4 : 5] + tokens["surface-diff-delete-stronger"] = diffDelete[isDark ? 10 : 8] + + // === Input tokens === + tokens["input-base"] = isDark ? neutral[1] : neutral[0] + tokens["input-hover"] = neutral[1] + tokens["input-active"] = interactive[0] + tokens["input-selected"] = interactive[3] + tokens["input-focus"] = interactive[0] + tokens["input-disabled"] = neutral[3] + + // === Text tokens === + tokens["text-base"] = neutral[10] + tokens["text-weak"] = neutral[8] + tokens["text-weaker"] = neutral[7] + tokens["text-strong"] = neutral[11] + tokens["text-invert-base"] = isDark ? neutral[10] : neutralAlpha[10] + tokens["text-invert-weak"] = isDark ? neutral[8] : neutralAlpha[8] + tokens["text-invert-weaker"] = isDark ? neutral[7] : neutralAlpha[7] + tokens["text-invert-strong"] = isDark ? neutral[11] : neutralAlpha[11] + tokens["text-interactive-base"] = interactive[isDark ? 10 : 8] + tokens["text-on-brand-base"] = neutralAlpha[10] + tokens["text-on-interactive-base"] = isDark ? neutral[11] : neutral[0] + tokens["text-on-interactive-weak"] = neutralAlpha[10] + tokens["text-on-success-base"] = success[isDark ? 8 : 9] + tokens["text-on-critical-base"] = error[isDark ? 8 : 9] + tokens["text-on-critical-weak"] = error[7] + tokens["text-on-critical-strong"] = error[11] + tokens["text-on-warning-base"] = neutralAlpha[10] + tokens["text-on-info-base"] = neutralAlpha[10] + tokens["text-diff-add-base"] = diffAdd[10] + tokens["text-diff-delete-base"] = diffDelete[isDark ? 8 : 9] + tokens["text-diff-delete-strong"] = diffDelete[11] + tokens["text-diff-add-strong"] = diffAdd[isDark ? 7 : 11] + tokens["text-on-info-weak"] = neutralAlpha[8] + tokens["text-on-info-strong"] = neutralAlpha[11] + tokens["text-on-warning-weak"] = neutralAlpha[8] + tokens["text-on-warning-strong"] = neutralAlpha[11] + tokens["text-on-success-weak"] = success[isDark ? 7 : 5] + tokens["text-on-success-strong"] = success[11] + tokens["text-on-brand-weak"] = neutralAlpha[8] + tokens["text-on-brand-weaker"] = neutralAlpha[7] + tokens["text-on-brand-strong"] = neutralAlpha[11] + + // === Button tokens === + tokens["button-secondary-base"] = isDark ? neutral[2] : neutral[0] + tokens["button-secondary-hover"] = isDark ? neutral[3] : neutral[1] + tokens["button-ghost-hover"] = neutralAlpha[1] + tokens["button-ghost-hover2"] = neutralAlpha[2] + + // === Border tokens === + tokens["border-base"] = neutralAlpha[6] + tokens["border-hover"] = neutralAlpha[7] + tokens["border-active"] = neutralAlpha[8] + tokens["border-selected"] = withAlpha(interactive[8], isDark ? 0.9 : 0.99) as ColorValue + tokens["border-disabled"] = neutralAlpha[7] + tokens["border-focus"] = neutralAlpha[8] + tokens["border-weak-base"] = neutralAlpha[isDark ? 5 : 4] + tokens["border-strong-base"] = neutralAlpha[isDark ? 7 : 6] + tokens["border-strong-hover"] = neutralAlpha[7] + tokens["border-strong-active"] = neutralAlpha[isDark ? 7 : 6] + tokens["border-strong-selected"] = withAlpha(interactive[5], 0.6) as ColorValue + tokens["border-strong-disabled"] = neutralAlpha[5] + tokens["border-strong-focus"] = neutralAlpha[isDark ? 7 : 6] + tokens["border-weak-hover"] = neutralAlpha[isDark ? 6 : 5] + tokens["border-weak-active"] = neutralAlpha[isDark ? 7 : 6] + tokens["border-weak-selected"] = withAlpha(interactive[4], isDark ? 0.6 : 0.5) as ColorValue + tokens["border-weak-disabled"] = neutralAlpha[5] + tokens["border-weak-focus"] = neutralAlpha[isDark ? 7 : 6] + tokens["border-weaker-base"] = neutralAlpha[2] + tokens["border-weaker-hover"] = neutralAlpha[3] + tokens["border-weaker-active"] = neutralAlpha[5] + tokens["border-weaker-selected"] = withAlpha(interactive[3], isDark ? 0.3 : 0.4) as ColorValue + tokens["border-weaker-disabled"] = neutralAlpha[1] + tokens["border-weaker-focus"] = neutralAlpha[5] + + // Interactive borders + tokens["border-interactive-base"] = interactive[6] + tokens["border-interactive-hover"] = interactive[7] + tokens["border-interactive-active"] = interactive[8] + tokens["border-interactive-selected"] = interactive[8] + tokens["border-interactive-disabled"] = neutral[7] + tokens["border-interactive-focus"] = interactive[8] + + // Status borders + tokens["border-success-base"] = success[5] + tokens["border-success-hover"] = success[6] + tokens["border-success-selected"] = success[8] + tokens["border-warning-base"] = warning[5] + tokens["border-warning-hover"] = warning[6] + tokens["border-warning-selected"] = warning[8] + tokens["border-critical-base"] = error[isDark ? 4 : 5] + tokens["border-critical-hover"] = error[6] + tokens["border-critical-selected"] = error[8] + tokens["border-info-base"] = info[5] + tokens["border-info-hover"] = info[6] + tokens["border-info-selected"] = info[8] + tokens["border-color"] = "#ffffff" + + // === Icon tokens === + tokens["icon-base"] = neutral[8] + tokens["icon-hover"] = neutral[isDark ? 9 : 10] + tokens["icon-active"] = neutral[isDark ? 10 : 11] + tokens["icon-selected"] = neutral[11] + tokens["icon-disabled"] = neutral[isDark ? 6 : 7] + tokens["icon-focus"] = neutral[11] + tokens["icon-invert-base"] = isDark ? neutral[0] : "#ffffff" + tokens["icon-weak-base"] = neutral[isDark ? 5 : 6] + tokens["icon-weak-hover"] = neutral[6] + tokens["icon-weak-active"] = neutral[7] + tokens["icon-weak-selected"] = neutral[8] + tokens["icon-weak-disabled"] = neutral[isDark ? 3 : 5] + tokens["icon-weak-focus"] = neutral[8] + tokens["icon-strong-base"] = neutral[11] + tokens["icon-strong-hover"] = isDark ? "#f6f3f3" : "#151313" + tokens["icon-strong-active"] = isDark ? "#fcfcfc" : "#020202" + tokens["icon-strong-selected"] = isDark ? "#fdfcfc" : "#020202" + tokens["icon-strong-disabled"] = neutral[7] + tokens["icon-strong-focus"] = isDark ? "#fdfcfc" : "#020202" + tokens["icon-brand-base"] = isDark ? "#ffffff" : neutral[11] + tokens["icon-interactive-base"] = interactive[8] + tokens["icon-success-base"] = success[isDark ? 6 : 6] + tokens["icon-success-hover"] = success[7] + tokens["icon-success-active"] = success[10] + tokens["icon-warning-base"] = warning[6] + tokens["icon-warning-hover"] = warning[7] + tokens["icon-warning-active"] = warning[10] + tokens["icon-critical-base"] = error[isDark ? 8 : 9] + tokens["icon-critical-hover"] = error[10] + tokens["icon-critical-active"] = error[11] + tokens["icon-info-base"] = info[isDark ? 6 : 6] + tokens["icon-info-hover"] = info[7] + tokens["icon-info-active"] = info[10] + tokens["icon-on-brand-base"] = neutralAlpha[10] + tokens["icon-on-brand-hover"] = neutralAlpha[11] + tokens["icon-on-brand-selected"] = neutralAlpha[11] + tokens["icon-on-interactive-base"] = isDark ? neutral[11] : neutral[0] + + // Agent icons (using semantic colors) + tokens["icon-agent-plan-base"] = info[8] + tokens["icon-agent-docs-base"] = warning[8] + tokens["icon-agent-ask-base"] = interactive[8] + tokens["icon-agent-build-base"] = interactive[isDark ? 10 : 8] + + // Status icons + tokens["icon-on-success-base"] = withAlpha(success[8], 0.9) as ColorValue + tokens["icon-on-success-hover"] = withAlpha(success[9], 0.9) as ColorValue + tokens["icon-on-success-selected"] = withAlpha(success[10], 0.9) as ColorValue + tokens["icon-on-warning-base"] = withAlpha(warning[8], 0.9) as ColorValue + tokens["icon-on-warning-hover"] = withAlpha(warning[9], 0.9) as ColorValue + tokens["icon-on-warning-selected"] = withAlpha(warning[10], 0.9) as ColorValue + tokens["icon-on-critical-base"] = withAlpha(error[8], 0.9) as ColorValue + tokens["icon-on-critical-hover"] = withAlpha(error[9], 0.9) as ColorValue + tokens["icon-on-critical-selected"] = withAlpha(error[10], 0.9) as ColorValue + tokens["icon-on-info-base"] = info[8] + tokens["icon-on-info-hover"] = withAlpha(info[9], 0.9) as ColorValue + tokens["icon-on-info-selected"] = withAlpha(info[10], 0.9) as ColorValue + + // Diff icons + tokens["icon-diff-add-base"] = diffAdd[10] + tokens["icon-diff-add-hover"] = diffAdd[isDark ? 9 : 11] + tokens["icon-diff-add-active"] = diffAdd[isDark ? 10 : 11] + tokens["icon-diff-delete-base"] = diffDelete[isDark ? 8 : 9] + tokens["icon-diff-delete-hover"] = diffDelete[isDark ? 9 : 10] + + // === Syntax tokens === + tokens["syntax-comment"] = "var(--text-weak)" + tokens["syntax-regexp"] = "var(--text-base)" + tokens["syntax-string"] = isDark ? "#00ceb9" : "#006656" + tokens["syntax-keyword"] = "var(--text-weak)" + tokens["syntax-primitive"] = isDark ? "#ffba92" : "#fb4804" + tokens["syntax-operator"] = isDark ? "var(--text-weak)" : "var(--text-base)" + tokens["syntax-variable"] = "var(--text-strong)" + tokens["syntax-property"] = isDark ? "#ff9ae2" : "#ed6dc8" + tokens["syntax-type"] = isDark ? "#ecf58c" : "#596600" + tokens["syntax-constant"] = isDark ? "#93e9f6" : "#007b80" + tokens["syntax-punctuation"] = isDark ? "var(--text-weak)" : "var(--text-base)" + tokens["syntax-object"] = "var(--text-strong)" + tokens["syntax-success"] = success[9] + tokens["syntax-warning"] = warning[9] + tokens["syntax-critical"] = error[isDark ? 9 : 9] + tokens["syntax-info"] = isDark ? "#93e9f6" : "#0092a8" + tokens["syntax-diff-add"] = diffAdd[10] + tokens["syntax-diff-delete"] = diffDelete[10] + tokens["syntax-diff-unknown"] = "#ff0000" + + // === Markdown tokens === + tokens["markdown-heading"] = isDark ? "#9d7cd8" : "#d68c27" + tokens["markdown-text"] = isDark ? "#eeeeee" : "#1a1a1a" + tokens["markdown-link"] = isDark ? "#fab283" : "#3b7dd8" + tokens["markdown-link-text"] = isDark ? "#56b6c2" : "#318795" + tokens["markdown-code"] = isDark ? "#7fd88f" : "#3d9a57" + tokens["markdown-block-quote"] = isDark ? "#e5c07b" : "#b0851f" + tokens["markdown-emph"] = isDark ? "#e5c07b" : "#b0851f" + tokens["markdown-strong"] = isDark ? "#f5a742" : "#d68c27" + tokens["markdown-horizontal-rule"] = isDark ? "#808080" : "#8a8a8a" + tokens["markdown-list-item"] = isDark ? "#fab283" : "#3b7dd8" + tokens["markdown-list-enumeration"] = isDark ? "#56b6c2" : "#318795" + tokens["markdown-image"] = isDark ? "#fab283" : "#3b7dd8" + tokens["markdown-image-text"] = isDark ? "#56b6c2" : "#318795" + tokens["markdown-code-block"] = isDark ? "#eeeeee" : "#1a1a1a" + + // === Avatar tokens === + tokens["avatar-background-pink"] = isDark ? "#501b3f" : "#feeef8" + tokens["avatar-background-mint"] = isDark ? "#033a34" : "#e1fbf4" + tokens["avatar-background-orange"] = isDark ? "#5f2a06" : "#fff1e7" + tokens["avatar-background-purple"] = isDark ? "#432155" : "#f9f1fe" + tokens["avatar-background-cyan"] = isDark ? "#0f3058" : "#e7f9fb" + tokens["avatar-background-lime"] = isDark ? "#2b3711" : "#eefadc" + tokens["avatar-text-pink"] = isDark ? "#e34ba9" : "#cd1d8d" + tokens["avatar-text-mint"] = isDark ? "#95f3d9" : "#147d6f" + tokens["avatar-text-orange"] = isDark ? "#ff802b" : "#ed5f00" + tokens["avatar-text-purple"] = isDark ? "#9d5bd2" : "#8445bc" + tokens["avatar-text-cyan"] = isDark ? "#369eff" : "#0894b3" + tokens["avatar-text-lime"] = isDark ? "#c4f042" : "#5d770d" + + // Apply any overrides + for (const [key, value] of Object.entries(overrides)) { + tokens[key] = value + } + + return tokens +} + +/** + * Generate neutral alpha scale (approximated as solid colors) + */ +function generateNeutralAlphaScale(neutralScale: HexColor[], isDark: boolean): HexColor[] { + const alphas = isDark + ? [0.02, 0.04, 0.08, 0.12, 0.16, 0.2, 0.26, 0.36, 0.44, 0.52, 0.72, 0.94] + : [0.01, 0.03, 0.06, 0.09, 0.12, 0.15, 0.2, 0.27, 0.46, 0.61, 0.5, 0.87] + + return neutralScale.map((hex, i) => { + const baseOklch = hexToOklch(hex) + // Adjust lightness based on alpha - closer to background color + const targetL = isDark ? 0.1 + alphas[i] * 0.8 : 1 - alphas[i] * 0.8 + return oklchToHex({ + ...baseOklch, + l: baseOklch.l * alphas[i] + targetL * (1 - alphas[i]), + }) + }) +} + +/** + * Resolve a complete theme to CSS for both light and dark modes + */ +export function resolveTheme(theme: DesktopTheme): { light: ResolvedTheme; dark: ResolvedTheme } { + return { + light: resolveThemeVariant(theme.light, false), + dark: resolveThemeVariant(theme.dark, true), + } +} + +/** + * Convert resolved theme tokens to CSS custom properties string + */ +export function themeToCss(tokens: ResolvedTheme): string { + return Object.entries(tokens) + .map(([key, value]) => `--${key}: ${value};`) + .join("\n ") +} diff --git a/packages/ui/src/theme/themes/ayu.json b/packages/ui/src/theme/themes/ayu.json new file mode 100644 index 000000000..944241450 --- /dev/null +++ b/packages/ui/src/theme/themes/ayu.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Ayu", + "id": "ayu", + "light": { + "seeds": { + "neutral": "#fdfaf4", + "primary": "#55b4d4", + "success": "#6ac782", + "warning": "#f2ae49", + "error": "#f05f65", + "info": "#36a3d9", + "interactive": "#55b4d4", + "diffAdd": "#b8df8a", + "diffDelete": "#f05f65" + }, + "overrides": { + "background-base": "#fdfaf4", + "background-weak": "#f6f0e7", + "background-strong": "#f1ebe2", + "background-stronger": "#ece4da", + "border-weak-base": "#e6ddcf", + "border-weak-hover": "#dcd3c5", + "border-weak-active": "#d1c9ba", + "border-weak-selected": "#c6bfaf", + "border-weak-disabled": "#f7f0e6", + "border-weak-focus": "#cbc4b6", + "border-base": "#bfb3a3", + "border-hover": "#b4a898", + "border-active": "#a99e8e", + "border-selected": "#9e9383", + "border-disabled": "#efe5d8", + "border-focus": "#b09f8f", + "border-strong-base": "#8f806f", + "border-strong-hover": "#837465", + "border-strong-active": "#77685a", + "border-strong-selected": "#6b5d51", + "border-strong-disabled": "#d8cabc", + "border-strong-focus": "#7c6d5e", + "surface-diff-add-base": "#eef5e4", + "surface-diff-delete-base": "#fde5e5", + "surface-diff-hidden-base": "#e3edf3", + "text-base": "#5c6773", + "text-weak": "#8a939f", + "text-strong": "#2a3038", + "syntax-string": "#86b300", + "syntax-primitive": "#f28779", + "syntax-property": "#55b4d4", + "syntax-type": "#f29e32", + "syntax-constant": "#36a3d9", + "syntax-info": "#36a3d9", + "markdown-heading": "#55b4d4", + "markdown-text": "#5c6773", + "markdown-link": "#55b4d4", + "markdown-link-text": "#36a3d9", + "markdown-code": "#86b300", + "markdown-block-quote": "#f29e32", + "markdown-emph": "#f29e32", + "markdown-strong": "#f28779", + "markdown-horizontal-rule": "#d7cec0", + "markdown-list-item": "#55b4d4", + "markdown-list-enumeration": "#36a3d9", + "markdown-image": "#55b4d4", + "markdown-image-text": "#36a3d9", + "markdown-code-block": "#55b4d4" + } + }, + "dark": { + "seeds": { + "neutral": "#0f1419", + "primary": "#39bae6", + "success": "#7fd962", + "warning": "#ebb062", + "error": "#ff8f77", + "info": "#73d0ff", + "interactive": "#39bae6", + "diffAdd": "#5cc885", + "diffDelete": "#ff8f77" + }, + "overrides": { + "background-base": "#0f1419", + "background-weak": "#121920", + "background-strong": "#0d1116", + "background-stronger": "#0a0e13", + "border-weak-base": "#262c34", + "border-weak-hover": "#2b323d", + "border-weak-active": "#303746", + "border-weak-selected": "#363d50", + "border-weak-disabled": "#080b0f", + "border-weak-focus": "#323a48", + "border-base": "#3d4555", + "border-hover": "#454d61", + "border-active": "#4c556d", + "border-selected": "#545d79", + "border-disabled": "#0e1218", + "border-focus": "#495368", + "border-strong-base": "#626c81", + "border-strong-hover": "#6c7690", + "border-strong-active": "#76819f", + "border-strong-selected": "#808bae", + "border-strong-disabled": "#151b23", + "border-strong-focus": "#6f7a96", + "surface-diff-add-base": "#102922", + "surface-diff-delete-base": "#2b1718", + "surface-diff-hidden-base": "#182028", + "text-base": "#ced0d6", + "text-weak": "#8f9aa5", + "text-strong": "#f6f7f9", + "syntax-string": "#b8cc52", + "syntax-primitive": "#f59074", + "syntax-property": "#39bae6", + "syntax-type": "#ebb062", + "syntax-constant": "#73d0ff", + "syntax-info": "#73d0ff", + "markdown-heading": "#39bae6", + "markdown-text": "#ced0d6", + "markdown-link": "#39bae6", + "markdown-link-text": "#73d0ff", + "markdown-code": "#b8cc52", + "markdown-block-quote": "#ebb062", + "markdown-emph": "#ebb062", + "markdown-strong": "#f59074", + "markdown-horizontal-rule": "#1f2630", + "markdown-list-item": "#39bae6", + "markdown-list-enumeration": "#73d0ff", + "markdown-image": "#39bae6", + "markdown-image-text": "#73d0ff", + "markdown-code-block": "#ced0d6" + } + } +} diff --git a/packages/ui/src/theme/themes/catppuccin.json b/packages/ui/src/theme/themes/catppuccin.json new file mode 100644 index 000000000..2a32df098 --- /dev/null +++ b/packages/ui/src/theme/themes/catppuccin.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Catppuccin", + "id": "catppuccin", + "light": { + "seeds": { + "neutral": "#f5e0dc", + "primary": "#7287fd", + "success": "#40a02b", + "warning": "#df8e1d", + "error": "#d20f39", + "info": "#04a5e5", + "interactive": "#7287fd", + "diffAdd": "#a6d189", + "diffDelete": "#e78284" + }, + "overrides": { + "background-base": "#f5e0dc", + "background-weak": "#f2d8d4", + "background-strong": "#f9e8e4", + "background-stronger": "#fdeeee", + "border-weak-base": "#e0cfd3", + "border-weak-hover": "#d6c4c8", + "border-weak-active": "#cdb9be", + "border-weak-selected": "#c2aeb4", + "border-weak-disabled": "#fbeff2", + "border-weak-focus": "#c7b4ba", + "border-base": "#bca6b2", + "border-hover": "#b19ca8", + "border-active": "#a6929e", + "border-selected": "#9a8894", + "border-disabled": "#f3e4e7", + "border-focus": "#ab97a1", + "border-strong-base": "#83677f", + "border-strong-hover": "#775b73", + "border-strong-active": "#6b5068", + "border-strong-selected": "#5f465d", + "border-strong-disabled": "#d9c5cf", + "border-strong-focus": "#714f66", + "surface-diff-add-base": "#edf5e6", + "surface-diff-delete-base": "#fde1e3", + "surface-diff-hidden-base": "#e4e2f6", + "text-base": "#4c4f69", + "text-weak": "#6c6f85", + "text-strong": "#1f1f2a", + "syntax-string": "#40a02b", + "syntax-primitive": "#d20f39", + "syntax-property": "#7287fd", + "syntax-type": "#df8e1d", + "syntax-constant": "#04a5e5", + "syntax-info": "#04a5e5", + "markdown-heading": "#7287fd", + "markdown-text": "#4c4f69", + "markdown-link": "#7287fd", + "markdown-link-text": "#04a5e5", + "markdown-code": "#40a02b", + "markdown-block-quote": "#df8e1d", + "markdown-emph": "#df8e1d", + "markdown-strong": "#d20f39", + "markdown-horizontal-rule": "#d4c5cf", + "markdown-list-item": "#7287fd", + "markdown-list-enumeration": "#04a5e5", + "markdown-image": "#7287fd", + "markdown-image-text": "#04a5e5", + "markdown-code-block": "#7287fd" + } + }, + "dark": { + "seeds": { + "neutral": "#1e1e2e", + "primary": "#b4befe", + "success": "#a6d189", + "warning": "#f4b8e4", + "error": "#f38ba8", + "info": "#89dceb", + "interactive": "#b4befe", + "diffAdd": "#94e2d5", + "diffDelete": "#f38ba8" + }, + "overrides": { + "background-base": "#1e1e2e", + "background-weak": "#211f31", + "background-strong": "#1c1c29", + "background-stronger": "#191926", + "border-weak-base": "#35324a", + "border-weak-hover": "#393655", + "border-weak-active": "#403c61", + "border-weak-selected": "#47436d", + "border-weak-disabled": "#141426", + "border-weak-focus": "#3d3a63", + "border-base": "#4a4763", + "border-hover": "#524f70", + "border-active": "#5a577d", + "border-selected": "#625f8a", + "border-disabled": "#1b1a2c", + "border-focus": "#575379", + "border-strong-base": "#6e6a8c", + "border-strong-hover": "#787497", + "border-strong-active": "#8380a2", + "border-strong-selected": "#8d8bad", + "border-strong-disabled": "#232237", + "border-strong-focus": "#7b779b", + "surface-diff-add-base": "#1d2c30", + "surface-diff-delete-base": "#2c1f2a", + "surface-diff-hidden-base": "#232538", + "text-base": "#cdd6f4", + "text-weak": "#a6adc8", + "text-strong": "#f4f2ff", + "syntax-string": "#a6e3a1", + "syntax-primitive": "#f38ba8", + "syntax-property": "#b4befe", + "syntax-type": "#f9e2af", + "syntax-constant": "#89dceb", + "syntax-info": "#89dceb", + "markdown-heading": "#b4befe", + "markdown-text": "#cdd6f4", + "markdown-link": "#b4befe", + "markdown-link-text": "#89dceb", + "markdown-code": "#a6e3a1", + "markdown-block-quote": "#f9e2af", + "markdown-emph": "#f9e2af", + "markdown-strong": "#f38ba8", + "markdown-horizontal-rule": "#2e2d45", + "markdown-list-item": "#b4befe", + "markdown-list-enumeration": "#89dceb", + "markdown-image": "#b4befe", + "markdown-image-text": "#89dceb", + "markdown-code-block": "#cdd6f4" + } + } +} diff --git a/packages/ui/src/theme/themes/dracula.json b/packages/ui/src/theme/themes/dracula.json new file mode 100644 index 000000000..696f1060c --- /dev/null +++ b/packages/ui/src/theme/themes/dracula.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Dracula", + "id": "dracula", + "light": { + "seeds": { + "neutral": "#f8f8f2", + "primary": "#7c6bf5", + "success": "#2fbf71", + "warning": "#f7a14d", + "error": "#d9536f", + "info": "#1d7fc5", + "interactive": "#7c6bf5", + "diffAdd": "#9fe3b3", + "diffDelete": "#f8a1b8" + }, + "overrides": { + "background-base": "#f8f8f2", + "background-weak": "#f1f2ed", + "background-strong": "#f6f6f1", + "background-stronger": "#f2f2ec", + "border-weak-base": "#e2e3da", + "border-weak-hover": "#d8d9d0", + "border-weak-active": "#cfd0c7", + "border-weak-selected": "#c4c6bc", + "border-weak-disabled": "#eceee3", + "border-weak-focus": "#c9cabf", + "border-base": "#c4c6ba", + "border-hover": "#b8baae", + "border-active": "#abada3", + "border-selected": "#979a90", + "border-disabled": "#e5e7dd", + "border-focus": "#b0b2a7", + "border-strong-base": "#9fa293", + "border-strong-hover": "#8e9185", + "border-strong-active": "#7e8176", + "border-strong-selected": "#6f7268", + "border-strong-disabled": "#c7c9be", + "border-strong-focus": "#878b7f", + "surface-diff-add-base": "#e4f5e6", + "surface-diff-delete-base": "#fae4eb", + "surface-diff-hidden-base": "#dedfe9", + "text-base": "#1f1f2f", + "text-weak": "#52526b", + "text-strong": "#05040c", + "syntax-string": "#2fbf71", + "syntax-primitive": "#d16090", + "syntax-property": "#7c6bf5", + "syntax-type": "#f7a14d", + "syntax-constant": "#1d7fc5", + "syntax-info": "#1d7fc5", + "markdown-heading": "#7c6bf5", + "markdown-text": "#1f1f2f", + "markdown-link": "#7c6bf5", + "markdown-link-text": "#1d7fc5", + "markdown-code": "#2fbf71", + "markdown-block-quote": "#f7a14d", + "markdown-emph": "#f7a14d", + "markdown-strong": "#d16090", + "markdown-horizontal-rule": "#c3c5d4", + "markdown-list-item": "#7c6bf5", + "markdown-list-enumeration": "#1d7fc5", + "markdown-image": "#7c6bf5", + "markdown-image-text": "#1d7fc5", + "markdown-code-block": "#1d7fc5" + } + }, + "dark": { + "seeds": { + "neutral": "#1d1e28", + "primary": "#bd93f9", + "success": "#50fa7b", + "warning": "#ffb86c", + "error": "#ff5555", + "info": "#8be9fd", + "interactive": "#bd93f9", + "diffAdd": "#2fb27d", + "diffDelete": "#ff6b81" + }, + "overrides": { + "background-base": "#14151f", + "background-weak": "#181926", + "background-strong": "#161722", + "background-stronger": "#191a26", + "border-weak-base": "#2d2f3c", + "border-weak-hover": "#303244", + "border-weak-active": "#35364c", + "border-weak-selected": "#3b3d55", + "border-weak-disabled": "#1e1f2b", + "border-weak-focus": "#383a50", + "border-base": "#3f415a", + "border-hover": "#464967", + "border-active": "#4d5073", + "border-selected": "#55587f", + "border-disabled": "#272834", + "border-focus": "#4a4d6d", + "border-strong-base": "#606488", + "border-strong-hover": "#6a6e96", + "border-strong-active": "#7378a3", + "border-strong-selected": "#7d82b1", + "border-strong-disabled": "#343649", + "border-strong-focus": "#6f739c", + "surface-diff-add-base": "#1f2a2f", + "surface-diff-delete-base": "#2d1f27", + "surface-diff-hidden-base": "#24253a", + "text-base": "#f8f8f2", + "text-weak": "#b6b9e4", + "text-strong": "#ffffff", + "syntax-string": "#50fa7b", + "syntax-primitive": "#ff79c6", + "syntax-property": "#bd93f9", + "syntax-type": "#ffb86c", + "syntax-constant": "#8be9fd", + "syntax-info": "#8be9fd", + "markdown-heading": "#bd93f9", + "markdown-text": "#f8f8f2", + "markdown-link": "#bd93f9", + "markdown-link-text": "#8be9fd", + "markdown-code": "#50fa7b", + "markdown-block-quote": "#ffb86c", + "markdown-emph": "#ffb86c", + "markdown-strong": "#ff79c6", + "markdown-horizontal-rule": "#44475a", + "markdown-list-item": "#bd93f9", + "markdown-list-enumeration": "#8be9fd", + "markdown-image": "#bd93f9", + "markdown-image-text": "#8be9fd", + "markdown-code-block": "#f8f8f2" + } + } +} diff --git a/packages/ui/src/theme/themes/monokai.json b/packages/ui/src/theme/themes/monokai.json new file mode 100644 index 000000000..d49846ddb --- /dev/null +++ b/packages/ui/src/theme/themes/monokai.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Monokai", + "id": "monokai", + "light": { + "seeds": { + "neutral": "#fdf8ec", + "primary": "#bf7bff", + "success": "#4fb54b", + "warning": "#f1a948", + "error": "#e54b4b", + "info": "#2d9ad7", + "interactive": "#bf7bff", + "diffAdd": "#bfe7a3", + "diffDelete": "#f6a3ae" + }, + "overrides": { + "background-base": "#fdf8ec", + "background-weak": "#f8f2e6", + "background-strong": "#fbf5e8", + "background-stronger": "#f7efdd", + "border-weak-base": "#e9e0cf", + "border-weak-hover": "#dfd5c3", + "border-weak-active": "#d5cab7", + "border-weak-selected": "#cabfad", + "border-weak-disabled": "#f3ebdd", + "border-weak-focus": "#d0c2b1", + "border-base": "#c7b9a5", + "border-hover": "#bcae98", + "border-active": "#b0a28c", + "border-selected": "#a49781", + "border-disabled": "#efe5d6", + "border-focus": "#b6a893", + "border-strong-base": "#998b76", + "border-strong-hover": "#8a7c67", + "border-strong-active": "#7a6d58", + "border-strong-selected": "#6c604c", + "border-strong-disabled": "#d7cabc", + "border-strong-focus": "#82745f", + "surface-diff-add-base": "#e8f7e1", + "surface-diff-delete-base": "#fde5e4", + "surface-diff-hidden-base": "#e9e0d0", + "text-base": "#292318", + "text-weak": "#6d5c40", + "text-strong": "#1c150c", + "syntax-string": "#4fb54b", + "syntax-primitive": "#d9487c", + "syntax-property": "#bf7bff", + "syntax-type": "#f1a948", + "syntax-constant": "#2d9ad7", + "syntax-info": "#2d9ad7", + "markdown-heading": "#bf7bff", + "markdown-text": "#292318", + "markdown-link": "#bf7bff", + "markdown-link-text": "#2d9ad7", + "markdown-code": "#4fb54b", + "markdown-block-quote": "#f1a948", + "markdown-emph": "#f1a948", + "markdown-strong": "#d9487c", + "markdown-horizontal-rule": "#cdbdab", + "markdown-list-item": "#bf7bff", + "markdown-list-enumeration": "#2d9ad7", + "markdown-image": "#bf7bff", + "markdown-image-text": "#2d9ad7", + "markdown-code-block": "#2d9ad7" + } + }, + "dark": { + "seeds": { + "neutral": "#272822", + "primary": "#ae81ff", + "success": "#a6e22e", + "warning": "#fd971f", + "error": "#f92672", + "info": "#66d9ef", + "interactive": "#ae81ff", + "diffAdd": "#4d7f2a", + "diffDelete": "#f4477c" + }, + "overrides": { + "background-base": "#23241e", + "background-weak": "#27281f", + "background-strong": "#25261f", + "background-stronger": "#292a23", + "border-weak-base": "#343528", + "border-weak-hover": "#393a2d", + "border-weak-active": "#3f4033", + "border-weak-selected": "#454639", + "border-weak-disabled": "#1d1e16", + "border-weak-focus": "#414235", + "border-base": "#494a3a", + "border-hover": "#50523f", + "border-active": "#585a45", + "border-selected": "#60624b", + "border-disabled": "#23241b", + "border-focus": "#555741", + "border-strong-base": "#6a6c55", + "border-strong-hover": "#73755d", + "border-strong-active": "#7d7f66", + "border-strong-selected": "#878970", + "border-strong-disabled": "#2c2d23", + "border-strong-focus": "#7a7c63", + "surface-diff-add-base": "#1e2a1d", + "surface-diff-delete-base": "#301c24", + "surface-diff-hidden-base": "#2f2f24", + "text-base": "#f8f8f2", + "text-weak": "#c5c5c0", + "text-strong": "#ffffff", + "syntax-string": "#a6e22e", + "syntax-primitive": "#f92672", + "syntax-property": "#ae81ff", + "syntax-type": "#fd971f", + "syntax-constant": "#66d9ef", + "syntax-info": "#66d9ef", + "markdown-heading": "#ae81ff", + "markdown-text": "#f8f8f2", + "markdown-link": "#ae81ff", + "markdown-link-text": "#66d9ef", + "markdown-code": "#a6e22e", + "markdown-block-quote": "#fd971f", + "markdown-emph": "#fd971f", + "markdown-strong": "#f92672", + "markdown-horizontal-rule": "#3b3c34", + "markdown-list-item": "#ae81ff", + "markdown-list-enumeration": "#66d9ef", + "markdown-image": "#ae81ff", + "markdown-image-text": "#66d9ef", + "markdown-code-block": "#f8f8f2" + } + } +} diff --git a/packages/ui/src/theme/themes/nord.json b/packages/ui/src/theme/themes/nord.json new file mode 100644 index 000000000..44378de06 --- /dev/null +++ b/packages/ui/src/theme/themes/nord.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Nord", + "id": "nord", + "light": { + "seeds": { + "neutral": "#eceff4", + "primary": "#5e81ac", + "success": "#8fbcbb", + "warning": "#d08770", + "error": "#bf616a", + "info": "#81a1c1", + "interactive": "#5e81ac", + "diffAdd": "#a3be8c", + "diffDelete": "#bf616a" + }, + "overrides": { + "background-base": "#eceff4", + "background-weak": "#e4e8f0", + "background-strong": "#f1f3f8", + "background-stronger": "#f6f8fc", + "border-weak-base": "#d5dbe7", + "border-weak-hover": "#c9d0de", + "border-weak-active": "#bec5d4", + "border-weak-selected": "#b2bacc", + "border-weak-disabled": "#f0f3fa", + "border-weak-focus": "#b9bfd0", + "border-base": "#afb7cb", + "border-hover": "#a3abc1", + "border-active": "#979fb7", + "border-selected": "#8b94ad", + "border-disabled": "#e5e9f2", + "border-focus": "#9ca4ba", + "border-strong-base": "#757f97", + "border-strong-hover": "#69718a", + "border-strong-active": "#5d647d", + "border-strong-selected": "#525970", + "border-strong-disabled": "#c9cedc", + "border-strong-focus": "#636c84", + "surface-diff-add-base": "#e4f0e4", + "surface-diff-delete-base": "#f4e1e4", + "surface-diff-hidden-base": "#dfe6f2", + "text-base": "#2e3440", + "text-weak": "#4c566a", + "text-strong": "#1f2530", + "syntax-string": "#a3be8c", + "syntax-primitive": "#bf616a", + "syntax-property": "#5e81ac", + "syntax-type": "#d08770", + "syntax-constant": "#81a1c1", + "syntax-info": "#81a1c1", + "markdown-heading": "#5e81ac", + "markdown-text": "#2e3440", + "markdown-link": "#5e81ac", + "markdown-link-text": "#81a1c1", + "markdown-code": "#a3be8c", + "markdown-block-quote": "#d08770", + "markdown-emph": "#d08770", + "markdown-strong": "#bf616a", + "markdown-horizontal-rule": "#cbd3e1", + "markdown-list-item": "#5e81ac", + "markdown-list-enumeration": "#81a1c1", + "markdown-image": "#5e81ac", + "markdown-image-text": "#81a1c1", + "markdown-code-block": "#5e81ac" + } + }, + "dark": { + "seeds": { + "neutral": "#2e3440", + "primary": "#88c0d0", + "success": "#a3be8c", + "warning": "#d08770", + "error": "#bf616a", + "info": "#81a1c1", + "interactive": "#88c0d0", + "diffAdd": "#81a1c1", + "diffDelete": "#bf616a" + }, + "overrides": { + "background-base": "#1f2430", + "background-weak": "#222938", + "background-strong": "#1c202a", + "background-stronger": "#181c24", + "border-weak-base": "#343a47", + "border-weak-hover": "#383f50", + "border-weak-active": "#3d4458", + "border-weak-selected": "#434a62", + "border-weak-disabled": "#151923", + "border-weak-focus": "#3f4359", + "border-base": "#4a5163", + "border-hover": "#515870", + "border-active": "#585f7c", + "border-selected": "#606889", + "border-disabled": "#1b202a", + "border-focus": "#545b78", + "border-strong-base": "#6a7492", + "border-strong-hover": "#747e9f", + "border-strong-active": "#7e88ac", + "border-strong-selected": "#8993b9", + "border-strong-disabled": "#232836", + "border-strong-focus": "#76819f", + "surface-diff-add-base": "#1f2e33", + "surface-diff-delete-base": "#2e212a", + "surface-diff-hidden-base": "#222b3a", + "text-base": "#e5e9f0", + "text-weak": "#a4adbf", + "text-strong": "#f8fafc", + "syntax-string": "#a3be8c", + "syntax-primitive": "#d57780", + "syntax-property": "#88c0d0", + "syntax-type": "#eac196", + "syntax-constant": "#81a1c1", + "syntax-info": "#81a1c1", + "markdown-heading": "#88c0d0", + "markdown-text": "#e5e9f0", + "markdown-link": "#88c0d0", + "markdown-link-text": "#81a1c1", + "markdown-code": "#a3be8c", + "markdown-block-quote": "#d08770", + "markdown-emph": "#d08770", + "markdown-strong": "#bf616a", + "markdown-horizontal-rule": "#2f384a", + "markdown-list-item": "#88c0d0", + "markdown-list-enumeration": "#81a1c1", + "markdown-image": "#88c0d0", + "markdown-image-text": "#81a1c1", + "markdown-code-block": "#cbd3e1" + } + } +} diff --git a/packages/ui/src/theme/themes/oc-1.json b/packages/ui/src/theme/themes/oc-1.json new file mode 100644 index 000000000..9f54bd6b4 --- /dev/null +++ b/packages/ui/src/theme/themes/oc-1.json @@ -0,0 +1,535 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "OC-1", + "id": "oc-1", + "light": { + "seeds": { + "neutral": "#8e8b8b", + "primary": "#dcde8d", + "success": "#12c905", + "warning": "#ffdc17", + "error": "#fc533a", + "info": "#a753ae", + "interactive": "#034cff", + "diffAdd": "#9ff29a", + "diffDelete": "#fc533a" + }, + "overrides": { + "background-base": "#f8f7f7", + "background-weak": "var(--smoke-light-3)", + "background-strong": "var(--smoke-light-1)", + "background-stronger": "#fcfcfc", + "surface-base": "var(--smoke-light-alpha-2)", + "base": "var(--smoke-light-alpha-2)", + "surface-base-hover": "#0500000f", + "surface-base-active": "var(--smoke-light-alpha-3)", + "surface-base-interactive-active": "var(--cobalt-light-alpha-3)", + "base2": "var(--smoke-light-alpha-2)", + "base3": "var(--smoke-light-alpha-2)", + "surface-inset-base": "var(--smoke-light-alpha-2)", + "surface-inset-base-hover": "var(--smoke-light-alpha-3)", + "surface-inset-strong": "#1f000017", + "surface-inset-strong-hover": "#1f000017", + "surface-raised-base": "var(--smoke-light-alpha-1)", + "surface-float-base": "var(--smoke-dark-1)", + "surface-float-base-hover": "var(--smoke-dark-2)", + "surface-raised-base-hover": "var(--smoke-light-alpha-2)", + "surface-raised-base-active": "var(--smoke-light-alpha-3)", + "surface-raised-strong": "var(--smoke-light-1)", + "surface-raised-strong-hover": "var(--white)", + "surface-raised-stronger": "var(--white)", + "surface-raised-stronger-hover": "var(--white)", + "surface-weak": "var(--smoke-light-alpha-3)", + "surface-weaker": "var(--smoke-light-alpha-4)", + "surface-strong": "#ffffff", + "surface-raised-stronger-non-alpha": "var(--white)", + "surface-brand-base": "var(--yuzu-light-9)", + "surface-brand-hover": "var(--yuzu-light-10)", + "surface-interactive-base": "var(--cobalt-light-3)", + "surface-interactive-hover": "var(--cobalt-light-4)", + "surface-interactive-weak": "var(--cobalt-light-2)", + "surface-interactive-weak-hover": "var(--cobalt-light-3)", + "surface-success-base": "var(--apple-light-3)", + "surface-success-weak": "var(--apple-light-2)", + "surface-success-strong": "var(--apple-light-9)", + "surface-warning-base": "var(--solaris-light-3)", + "surface-warning-weak": "var(--solaris-light-2)", + "surface-warning-strong": "var(--solaris-light-9)", + "surface-critical-base": "var(--ember-light-3)", + "surface-critical-weak": "var(--ember-light-2)", + "surface-critical-strong": "var(--ember-light-9)", + "surface-info-base": "var(--lilac-light-3)", + "surface-info-weak": "var(--lilac-light-2)", + "surface-info-strong": "var(--lilac-light-9)", + "surface-diff-unchanged-base": "#ffffff00", + "surface-diff-skip-base": "var(--smoke-light-2)", + "surface-diff-hidden-base": "var(--blue-light-3)", + "surface-diff-hidden-weak": "var(--blue-light-2)", + "surface-diff-hidden-weaker": "var(--blue-light-1)", + "surface-diff-hidden-strong": "var(--blue-light-5)", + "surface-diff-hidden-stronger": "var(--blue-light-9)", + "surface-diff-add-base": "#dafbe0", + "surface-diff-add-weak": "var(--mint-light-2)", + "surface-diff-add-weaker": "var(--mint-light-1)", + "surface-diff-add-strong": "var(--mint-light-5)", + "surface-diff-add-stronger": "var(--mint-light-9)", + "surface-diff-delete-base": "var(--ember-light-3)", + "surface-diff-delete-weak": "var(--ember-light-2)", + "surface-diff-delete-weaker": "var(--ember-light-1)", + "surface-diff-delete-strong": "var(--ember-light-6)", + "surface-diff-delete-stronger": "var(--ember-light-9)", + "input-base": "var(--smoke-light-1)", + "input-hover": "var(--smoke-light-2)", + "input-active": "var(--cobalt-light-1)", + "input-selected": "var(--cobalt-light-4)", + "input-focus": "var(--cobalt-light-1)", + "input-disabled": "var(--smoke-light-4)", + "text-base": "var(--smoke-light-11)", + "text-weak": "var(--smoke-light-9)", + "text-weaker": "var(--smoke-light-8)", + "text-strong": "var(--smoke-light-12)", + "text-invert-base": "var(--smoke-dark-alpha-11)", + "text-invert-weak": "var(--smoke-dark-alpha-9)", + "text-invert-weaker": "var(--smoke-dark-alpha-8)", + "text-invert-strong": "var(--smoke-dark-alpha-12)", + "text-interactive-base": "var(--cobalt-light-9)", + "text-on-brand-base": "var(--smoke-light-alpha-11)", + "text-on-interactive-base": "var(--smoke-light-1)", + "text-on-interactive-weak": "var(--smoke-dark-alpha-11)", + "text-on-success-base": "var(--apple-light-10)", + "text-on-critical-base": "var(--ember-light-10)", + "text-on-critical-weak": "var(--ember-light-8)", + "text-on-critical-strong": "var(--ember-light-12)", + "text-on-warning-base": "var(--smoke-dark-alpha-11)", + "text-on-info-base": "var(--smoke-dark-alpha-11)", + "text-diff-add-base": "var(--mint-light-11)", + "text-diff-delete-base": "var(--ember-light-10)", + "text-diff-delete-strong": "var(--ember-light-12)", + "text-diff-add-strong": "var(--mint-light-12)", + "text-on-info-weak": "var(--smoke-dark-alpha-9)", + "text-on-info-strong": "var(--smoke-dark-alpha-12)", + "text-on-warning-weak": "var(--smoke-dark-alpha-9)", + "text-on-warning-strong": "var(--smoke-dark-alpha-12)", + "text-on-success-weak": "var(--apple-light-6)", + "text-on-success-strong": "var(--apple-light-12)", + "text-on-brand-weak": "var(--smoke-light-alpha-9)", + "text-on-brand-weaker": "var(--smoke-light-alpha-8)", + "text-on-brand-strong": "var(--smoke-light-alpha-12)", + "button-secondary-base": "#fdfcfc", + "button-secondary-hover": "#faf9f9", + "border-base": "var(--smoke-light-alpha-7)", + "border-hover": "var(--smoke-light-alpha-8)", + "border-active": "var(--smoke-light-alpha-9)", + "border-selected": "var(--cobalt-light-alpha-9)", + "border-disabled": "var(--smoke-light-alpha-8)", + "border-focus": "var(--smoke-light-alpha-9)", + "border-weak-base": "var(--smoke-light-alpha-5)", + "border-strong-base": "var(--smoke-light-alpha-7)", + "border-strong-hover": "var(--smoke-light-alpha-8)", + "border-strong-active": "var(--smoke-light-alpha-7)", + "border-strong-selected": "var(--cobalt-light-alpha-6)", + "border-strong-disabled": "var(--smoke-light-alpha-6)", + "border-strong-focus": "var(--smoke-light-alpha-7)", + "border-weak-hover": "var(--smoke-light-alpha-6)", + "border-weak-active": "var(--smoke-light-alpha-7)", + "border-weak-selected": "var(--cobalt-light-alpha-5)", + "border-weak-disabled": "var(--smoke-light-alpha-6)", + "border-weak-focus": "var(--smoke-light-alpha-7)", + "border-interactive-base": "var(--cobalt-light-7)", + "border-interactive-hover": "var(--cobalt-light-8)", + "border-interactive-active": "var(--cobalt-light-9)", + "border-interactive-selected": "var(--cobalt-light-9)", + "border-interactive-disabled": "var(--smoke-light-8)", + "border-interactive-focus": "var(--cobalt-light-9)", + "border-success-base": "var(--apple-light-6)", + "border-success-hover": "var(--apple-light-7)", + "border-success-selected": "var(--apple-light-9)", + "border-warning-base": "var(--solaris-light-6)", + "border-warning-hover": "var(--solaris-light-7)", + "border-warning-selected": "var(--solaris-light-9)", + "border-critical-base": "var(--ember-light-6)", + "border-critical-hover": "var(--ember-light-7)", + "border-critical-selected": "var(--ember-light-9)", + "border-info-base": "var(--lilac-light-6)", + "border-info-hover": "var(--lilac-light-7)", + "border-info-selected": "var(--lilac-light-9)", + "icon-base": "var(--smoke-light-9)", + "icon-hover": "var(--smoke-light-11)", + "icon-active": "var(--smoke-light-12)", + "icon-selected": "var(--smoke-light-12)", + "icon-disabled": "var(--smoke-light-8)", + "icon-focus": "var(--smoke-light-12)", + "icon-invert-base": "#ffffff", + "icon-weak-base": "var(--smoke-light-7)", + "icon-weak-hover": "var(--smoke-light-8)", + "icon-weak-active": "var(--smoke-light-9)", + "icon-weak-selected": "var(--smoke-light-10)", + "icon-weak-disabled": "var(--smoke-light-6)", + "icon-weak-focus": "var(--smoke-light-9)", + "icon-strong-base": "var(--smoke-light-12)", + "icon-strong-hover": "#151313", + "icon-strong-active": "#020202", + "icon-strong-selected": "#020202", + "icon-strong-disabled": "var(--smoke-light-8)", + "icon-strong-focus": "#020202", + "icon-brand-base": "var(--smoke-light-12)", + "icon-interactive-base": "var(--cobalt-light-9)", + "icon-success-base": "var(--apple-light-7)", + "icon-success-hover": "var(--apple-light-8)", + "icon-success-active": "var(--apple-light-11)", + "icon-warning-base": "var(--amber-light-7)", + "icon-warning-hover": "var(--amber-light-8)", + "icon-warning-active": "var(--amber-light-11)", + "icon-critical-base": "var(--ember-light-10)", + "icon-critical-hover": "var(--ember-light-11)", + "icon-critical-active": "var(--ember-light-12)", + "icon-info-base": "var(--lilac-light-7)", + "icon-info-hover": "var(--lilac-light-8)", + "icon-info-active": "var(--lilac-light-11)", + "icon-on-brand-base": "var(--smoke-light-alpha-11)", + "icon-on-brand-hover": "var(--smoke-light-alpha-12)", + "icon-on-brand-selected": "var(--smoke-light-alpha-12)", + "icon-on-interactive-base": "var(--smoke-light-1)", + "icon-agent-plan-base": "var(--purple-light-9)", + "icon-agent-docs-base": "var(--amber-light-9)", + "icon-agent-ask-base": "var(--cyan-light-9)", + "icon-agent-build-base": "var(--cobalt-light-9)", + "icon-on-success-base": "var(--apple-light-alpha-9)", + "icon-on-success-hover": "var(--apple-light-alpha-10)", + "icon-on-success-selected": "var(--apple-light-alpha-11)", + "icon-on-warning-base": "var(--amber-lightalpha-9)", + "icon-on-warning-hover": "var(--amber-lightalpha-10)", + "icon-on-warning-selected": "var(--amber-lightalpha-11)", + "icon-on-critical-base": "var(--ember-light-alpha-9)", + "icon-on-critical-hover": "var(--ember-light-alpha-10)", + "icon-on-critical-selected": "var(--ember-light-alpha-11)", + "icon-on-info-base": "var(--lilac-light-9)", + "icon-on-info-hover": "var(--lilac-light-alpha-10)", + "icon-on-info-selected": "var(--lilac-light-alpha-11)", + "icon-diff-add-base": "var(--mint-light-11)", + "icon-diff-add-hover": "var(--mint-light-12)", + "icon-diff-add-active": "var(--mint-light-12)", + "icon-diff-delete-base": "var(--ember-light-10)", + "icon-diff-delete-hover": "var(--ember-light-11)", + "syntax-comment": "var(--text-weak)", + "syntax-regexp": "var(--text-base)", + "syntax-string": "#006656", + "syntax-keyword": "var(--text-weak)", + "syntax-primitive": "#fb4804", + "syntax-operator": "var(--text-base)", + "syntax-variable": "var(--text-strong)", + "syntax-property": "#ed6dc8", + "syntax-type": "#596600", + "syntax-constant": "#007b80", + "syntax-punctuation": "var(--text-base)", + "syntax-object": "var(--text-strong)", + "syntax-success": "var(--apple-light-10)", + "syntax-warning": "var(--amber-light-10)", + "syntax-critical": "var(--ember-light-10)", + "syntax-info": "#0092a8", + "syntax-diff-add": "var(--mint-light-11)", + "syntax-diff-delete": "var(--ember-light-11)", + "syntax-diff-unknown": "#ff0000", + "markdown-heading": "#d68c27", + "markdown-text": "#1a1a1a", + "markdown-link": "#3b7dd8", + "markdown-link-text": "#318795", + "markdown-code": "#3d9a57", + "markdown-block-quote": "#b0851f", + "markdown-emph": "#b0851f", + "markdown-strong": "#d68c27", + "markdown-horizontal-rule": "#8a8a8a", + "markdown-list-item": "#3b7dd8", + "markdown-list-enumeration": "#318795", + "markdown-image": "#3b7dd8", + "markdown-image-text": "#318795", + "markdown-code-block": "#1a1a1a", + "border-color": "#ffffff", + "border-weaker-base": "var(--smoke-light-alpha-3)", + "border-weaker-hover": "var(--smoke-light-alpha-4)", + "border-weaker-active": "var(--smoke-light-alpha-6)", + "border-weaker-selected": "var(--cobalt-light-alpha-4)", + "border-weaker-disabled": "var(--smoke-light-alpha-2)", + "border-weaker-focus": "var(--smoke-light-alpha-6)", + "button-ghost-hover": "var(--smoke-light-alpha-2)", + "button-ghost-hover2": "var(--smoke-light-alpha-3)", + "avatar-background-pink": "#feeef8", + "avatar-background-mint": "#e1fbf4", + "avatar-background-orange": "#fff1e7", + "avatar-background-purple": "#f9f1fe", + "avatar-background-cyan": "#e7f9fb", + "avatar-background-lime": "#eefadc", + "avatar-text-pink": "#cd1d8d", + "avatar-text-mint": "#147d6f", + "avatar-text-orange": "#ed5f00", + "avatar-text-purple": "#8445bc", + "avatar-text-cyan": "#0894b3", + "avatar-text-lime": "#5d770d" + } + }, + "dark": { + "seeds": { + "neutral": "#716c6b", + "primary": "#fab283", + "success": "#12c905", + "warning": "#fcd53a", + "error": "#fc533a", + "info": "#edb2f1", + "interactive": "#034cff", + "diffAdd": "#c8ffc4", + "diffDelete": "#fc533a" + }, + "overrides": { + "background-base": "var(--smoke-dark-1)", + "background-weak": "#1c1717", + "background-strong": "#151313", + "background-stronger": "#191515", + "surface-base": "var(--smoke-dark-alpha-2)", + "base": "var(--smoke-dark-alpha-2)", + "surface-base-hover": "#e0b7b716", + "surface-base-active": "var(--smoke-dark-alpha-3)", + "surface-base-interactive-active": "var(--cobalt-dark-alpha-2)", + "base2": "var(--smoke-dark-alpha-2)", + "base3": "var(--smoke-dark-alpha-2)", + "surface-inset-base": "#0e0b0b7f", + "surface-inset-base-hover": "#0e0b0b7f", + "surface-inset-strong": "#060505cc", + "surface-inset-strong-hover": "#060505cc", + "surface-raised-base": "var(--smoke-dark-alpha-3)", + "surface-float-base": "var(--smoke-dark-1)", + "surface-float-base-hover": "var(--smoke-dark-2)", + "surface-raised-base-hover": "var(--smoke-dark-alpha-4)", + "surface-raised-base-active": "var(--smoke-dark-alpha-5)", + "surface-raised-strong": "var(--smoke-dark-alpha-4)", + "surface-raised-strong-hover": "var(--smoke-dark-alpha-6)", + "surface-raised-stronger": "var(--smoke-dark-alpha-6)", + "surface-raised-stronger-hover": "var(--smoke-dark-alpha-7)", + "surface-weak": "var(--smoke-dark-alpha-4)", + "surface-weaker": "var(--smoke-dark-alpha-5)", + "surface-strong": "var(--smoke-dark-alpha-7)", + "surface-raised-stronger-non-alpha": "var(--smoke-dark-3)", + "surface-brand-base": "var(--yuzu-light-9)", + "surface-brand-hover": "var(--yuzu-light-10)", + "surface-interactive-base": "var(--cobalt-light-3)", + "surface-interactive-hover": "var(--cobalt-light-4)", + "surface-interactive-weak": "var(--cobalt-light-2)", + "surface-interactive-weak-hover": "var(--cobalt-light-3)", + "surface-success-base": "var(--apple-light-3)", + "surface-success-weak": "var(--apple-light-2)", + "surface-success-strong": "var(--apple-light-9)", + "surface-warning-base": "var(--solaris-light-3)", + "surface-warning-weak": "var(--solaris-light-2)", + "surface-warning-strong": "var(--solaris-light-9)", + "surface-critical-base": "var(--ember-dark-3)", + "surface-critical-weak": "var(--ember-dark-2)", + "surface-critical-strong": "var(--ember-dark-9)", + "surface-info-base": "var(--lilac-light-3)", + "surface-info-weak": "var(--lilac-light-2)", + "surface-info-strong": "var(--lilac-light-9)", + "surface-diff-unchanged-base": "var(--smoke-dark-1)", + "surface-diff-skip-base": "var(--smoke-dark-alpha-1)", + "surface-diff-hidden-base": "var(--blue-dark-2)", + "surface-diff-hidden-weak": "var(--blue-dark-1)", + "surface-diff-hidden-weaker": "var(--blue-dark-3)", + "surface-diff-hidden-strong": "var(--blue-dark-5)", + "surface-diff-hidden-stronger": "var(--blue-dark-11)", + "surface-diff-add-base": "var(--mint-dark-3)", + "surface-diff-add-weak": "var(--mint-dark-4)", + "surface-diff-add-weaker": "var(--mint-dark-3)", + "surface-diff-add-strong": "var(--mint-dark-5)", + "surface-diff-add-stronger": "var(--mint-dark-11)", + "surface-diff-delete-base": "var(--ember-dark-3)", + "surface-diff-delete-weak": "var(--ember-dark-4)", + "surface-diff-delete-weaker": "var(--ember-dark-3)", + "surface-diff-delete-strong": "var(--ember-dark-5)", + "surface-diff-delete-stronger": "var(--ember-dark-11)", + "input-base": "var(--smoke-dark-2)", + "input-hover": "var(--smoke-dark-2)", + "input-active": "var(--cobalt-dark-1)", + "input-selected": "var(--cobalt-dark-2)", + "input-focus": "var(--cobalt-dark-1)", + "input-disabled": "var(--smoke-dark-4)", + "text-base": "var(--smoke-dark-alpha-11)", + "text-weak": "var(--smoke-dark-alpha-9)", + "text-weaker": "var(--smoke-dark-alpha-8)", + "text-strong": "var(--smoke-dark-alpha-12)", + "text-invert-base": "var(--smoke-dark-alpha-11)", + "text-invert-weak": "var(--smoke-dark-alpha-9)", + "text-invert-weaker": "var(--smoke-dark-alpha-8)", + "text-invert-strong": "var(--smoke-dark-alpha-12)", + "text-interactive-base": "var(--cobalt-dark-11)", + "text-on-brand-base": "var(--smoke-dark-alpha-11)", + "text-on-interactive-base": "var(--smoke-dark-12)", + "text-on-interactive-weak": "var(--smoke-dark-alpha-11)", + "text-on-success-base": "var(--apple-dark-9)", + "text-on-critical-base": "var(--ember-dark-9)", + "text-on-critical-weak": "var(--ember-dark-8)", + "text-on-critical-strong": "var(--ember-dark-12)", + "text-on-warning-base": "var(--smoke-dark-alpha-11)", + "text-on-info-base": "var(--smoke-dark-alpha-11)", + "text-diff-add-base": "var(--mint-dark-11)", + "text-diff-delete-base": "var(--ember-dark-9)", + "text-diff-delete-strong": "var(--ember-dark-12)", + "text-diff-add-strong": "var(--mint-dark-8)", + "text-on-info-weak": "var(--smoke-dark-alpha-9)", + "text-on-info-strong": "var(--smoke-dark-alpha-12)", + "text-on-warning-weak": "var(--smoke-dark-alpha-9)", + "text-on-warning-strong": "var(--smoke-dark-alpha-12)", + "text-on-success-weak": "var(--apple-dark-8)", + "text-on-success-strong": "var(--apple-dark-12)", + "text-on-brand-weak": "var(--smoke-dark-alpha-9)", + "text-on-brand-weaker": "var(--smoke-dark-alpha-8)", + "text-on-brand-strong": "var(--smoke-dark-alpha-12)", + "button-secondary-base": "#231f1f", + "button-secondary-hover": "#2a2727", + "border-base": "var(--smoke-dark-alpha-7)", + "border-hover": "var(--smoke-dark-alpha-8)", + "border-active": "var(--smoke-dark-alpha-9)", + "border-selected": "var(--cobalt-dark-alpha-11)", + "border-disabled": "var(--smoke-dark-alpha-8)", + "border-focus": "var(--smoke-dark-alpha-9)", + "border-weak-base": "var(--smoke-dark-alpha-6)", + "border-strong-base": "var(--smoke-dark-alpha-8)", + "border-strong-hover": "var(--smoke-dark-alpha-7)", + "border-strong-active": "var(--smoke-dark-alpha-8)", + "border-strong-selected": "var(--cobalt-dark-alpha-6)", + "border-strong-disabled": "var(--smoke-dark-alpha-6)", + "border-strong-focus": "var(--smoke-dark-alpha-8)", + "border-weak-hover": "var(--smoke-dark-alpha-7)", + "border-weak-active": "var(--smoke-dark-alpha-8)", + "border-weak-selected": "var(--cobalt-dark-alpha-6)", + "border-weak-disabled": "var(--smoke-dark-alpha-6)", + "border-weak-focus": "var(--smoke-dark-alpha-8)", + "border-interactive-base": "var(--cobalt-light-7)", + "border-interactive-hover": "var(--cobalt-light-8)", + "border-interactive-active": "var(--cobalt-light-9)", + "border-interactive-selected": "var(--cobalt-light-9)", + "border-interactive-disabled": "var(--smoke-light-8)", + "border-interactive-focus": "var(--cobalt-light-9)", + "border-success-base": "var(--apple-light-6)", + "border-success-hover": "var(--apple-light-7)", + "border-success-selected": "var(--apple-light-9)", + "border-warning-base": "var(--solaris-light-6)", + "border-warning-hover": "var(--solaris-light-7)", + "border-warning-selected": "var(--solaris-light-9)", + "border-critical-base": "var(--ember-dark-5)", + "border-critical-hover": "var(--ember-dark-7)", + "border-critical-selected": "var(--ember-dark-9)", + "border-info-base": "var(--lilac-light-6)", + "border-info-hover": "var(--lilac-light-7)", + "border-info-selected": "var(--lilac-light-9)", + "icon-base": "var(--smoke-dark-9)", + "icon-hover": "var(--smoke-dark-10)", + "icon-active": "var(--smoke-dark-11)", + "icon-selected": "var(--smoke-dark-12)", + "icon-disabled": "var(--smoke-dark-7)", + "icon-focus": "var(--smoke-dark-12)", + "icon-invert-base": "var(--smoke-dark-1)", + "icon-weak-base": "var(--smoke-dark-6)", + "icon-weak-hover": "var(--smoke-light-7)", + "icon-weak-active": "var(--smoke-light-8)", + "icon-weak-selected": "var(--smoke-light-9)", + "icon-weak-disabled": "var(--smoke-light-4)", + "icon-weak-focus": "var(--smoke-light-9)", + "icon-strong-base": "var(--smoke-dark-12)", + "icon-strong-hover": "#f6f3f3", + "icon-strong-active": "#fcfcfc", + "icon-strong-selected": "#fdfcfc", + "icon-strong-disabled": "var(--smoke-dark-8)", + "icon-strong-focus": "#fdfcfc", + "icon-brand-base": "var(--white)", + "icon-interactive-base": "var(--cobalt-dark-9)", + "icon-success-base": "var(--apple-dark-7)", + "icon-success-hover": "var(--apple-dark-8)", + "icon-success-active": "var(--apple-dark-11)", + "icon-warning-base": "var(--amber-dark-7)", + "icon-warning-hover": "var(--amber-dark-8)", + "icon-warning-active": "var(--amber-dark-11)", + "icon-critical-base": "var(--ember-dark-9)", + "icon-critical-hover": "var(--ember-dark-11)", + "icon-critical-active": "var(--ember-dark-12)", + "icon-info-base": "var(--lilac-dark-7)", + "icon-info-hover": "var(--lilac-dark-8)", + "icon-info-active": "var(--lilac-dark-11)", + "icon-on-brand-base": "var(--smoke-light-alpha-11)", + "icon-on-brand-hover": "var(--smoke-light-alpha-12)", + "icon-on-brand-selected": "var(--smoke-light-alpha-12)", + "icon-on-interactive-base": "var(--smoke-dark-12)", + "icon-agent-plan-base": "var(--purple-dark-9)", + "icon-agent-docs-base": "var(--amber-dark-9)", + "icon-agent-ask-base": "var(--cyan-dark-9)", + "icon-agent-build-base": "var(--cobalt-dark-11)", + "icon-on-success-base": "var(--apple-dark-alpha-9)", + "icon-on-success-hover": "var(--apple-dark-alpha-10)", + "icon-on-success-selected": "var(--apple-dark-alpha-11)", + "icon-on-warning-base": "var(--amber-darkalpha-9)", + "icon-on-warning-hover": "var(--amber-darkalpha-10)", + "icon-on-warning-selected": "var(--amber-darkalpha-11)", + "icon-on-critical-base": "var(--ember-dark-alpha-9)", + "icon-on-critical-hover": "var(--ember-dark-alpha-10)", + "icon-on-critical-selected": "var(--ember-dark-alpha-11)", + "icon-on-info-base": "var(--lilac-dark-9)", + "icon-on-info-hover": "var(--lilac-dark-alpha-10)", + "icon-on-info-selected": "var(--lilac-dark-alpha-11)", + "icon-diff-add-base": "var(--mint-dark-11)", + "icon-diff-add-hover": "var(--mint-dark-10)", + "icon-diff-add-active": "var(--mint-dark-11)", + "icon-diff-delete-base": "var(--ember-dark-9)", + "icon-diff-delete-hover": "var(--ember-dark-10)", + "syntax-comment": "var(--text-weak)", + "syntax-regexp": "var(--text-base)", + "syntax-string": "#00ceb9", + "syntax-keyword": "var(--text-weak)", + "syntax-primitive": "#ffba92", + "syntax-operator": "var(--text-weak)", + "syntax-variable": "var(--text-strong)", + "syntax-property": "#ff9ae2", + "syntax-type": "#ecf58c", + "syntax-constant": "#93e9f6", + "syntax-punctuation": "var(--text-weak)", + "syntax-object": "var(--text-strong)", + "syntax-success": "var(--apple-dark-10)", + "syntax-warning": "var(--amber-dark-10)", + "syntax-critical": "var(--ember-dark-10)", + "syntax-info": "#93e9f6", + "syntax-diff-add": "var(--mint-dark-11)", + "syntax-diff-delete": "var(--ember-dark-11)", + "syntax-diff-unknown": "#ff0000", + "markdown-heading": "#9d7cd8", + "markdown-text": "#eeeeee", + "markdown-link": "#fab283", + "markdown-link-text": "#56b6c2", + "markdown-code": "#7fd88f", + "markdown-block-quote": "#e5c07b", + "markdown-emph": "#e5c07b", + "markdown-strong": "#f5a742", + "markdown-horizontal-rule": "#808080", + "markdown-list-item": "#fab283", + "markdown-list-enumeration": "#56b6c2", + "markdown-image": "#fab283", + "markdown-image-text": "#56b6c2", + "markdown-code-block": "#eeeeee", + "border-color": "#ffffff", + "border-weaker-base": "var(--smoke-dark-alpha-3)", + "border-weaker-hover": "var(--smoke-dark-alpha-4)", + "border-weaker-active": "var(--smoke-dark-alpha-6)", + "border-weaker-selected": "var(--cobalt-dark-alpha-3)", + "border-weaker-disabled": "var(--smoke-dark-alpha-2)", + "border-weaker-focus": "var(--smoke-dark-alpha-6)", + "button-ghost-hover": "var(--smoke-dark-alpha-2)", + "button-ghost-hover2": "var(--smoke-dark-alpha-3)", + "avatar-background-pink": "#501b3f", + "avatar-background-mint": "#033a34", + "avatar-background-orange": "#5f2a06", + "avatar-background-purple": "#432155", + "avatar-background-cyan": "#0f3058", + "avatar-background-lime": "#2b3711", + "avatar-text-pink": "#e34ba9", + "avatar-text-mint": "#95f3d9", + "avatar-text-orange": "#ff802b", + "avatar-text-purple": "#9d5bd2", + "avatar-text-cyan": "#369eff", + "avatar-text-lime": "#c4f042" + } + } +} diff --git a/packages/ui/src/theme/themes/onedarkpro.json b/packages/ui/src/theme/themes/onedarkpro.json new file mode 100644 index 000000000..ce01511e8 --- /dev/null +++ b/packages/ui/src/theme/themes/onedarkpro.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "One Dark Pro", + "id": "onedarkpro", + "light": { + "seeds": { + "neutral": "#f5f6f8", + "primary": "#528bff", + "success": "#4fa66d", + "warning": "#d19a66", + "error": "#e06c75", + "info": "#61afef", + "interactive": "#528bff", + "diffAdd": "#c2ebcf", + "diffDelete": "#f7c1c5" + }, + "overrides": { + "background-base": "#f5f6f8", + "background-weak": "#eef0f4", + "background-strong": "#fafbfc", + "background-stronger": "#ffffff", + "border-weak-base": "#dee2eb", + "border-weak-hover": "#d4d9e3", + "border-weak-active": "#caced6", + "border-weak-selected": "#bec4d0", + "border-weak-disabled": "#f4f6fb", + "border-weak-focus": "#c4cada", + "border-base": "#b5bccd", + "border-hover": "#aab1c2", + "border-active": "#a0a7b8", + "border-selected": "#959cae", + "border-disabled": "#eceef4", + "border-focus": "#a6adbf", + "border-strong-base": "#747c92", + "border-strong-hover": "#6a7287", + "border-strong-active": "#60687c", + "border-strong-selected": "#565e71", + "border-strong-disabled": "#cbd0dd", + "border-strong-focus": "#666d82", + "surface-diff-add-base": "#e5f4ea", + "surface-diff-delete-base": "#fde7ea", + "surface-diff-hidden-base": "#e4e8f4", + "text-base": "#2b303b", + "text-weak": "#6b717f", + "text-strong": "#0e1118", + "syntax-string": "#4fa66d", + "syntax-primitive": "#d85462", + "syntax-property": "#528bff", + "syntax-type": "#d19a66", + "syntax-constant": "#61afef", + "syntax-info": "#61afef", + "markdown-heading": "#528bff", + "markdown-text": "#2b303b", + "markdown-link": "#528bff", + "markdown-link-text": "#61afef", + "markdown-code": "#4fa66d", + "markdown-block-quote": "#d19a66", + "markdown-emph": "#d19a66", + "markdown-strong": "#d85462", + "markdown-horizontal-rule": "#d3d7e4", + "markdown-list-item": "#528bff", + "markdown-list-enumeration": "#61afef", + "markdown-image": "#528bff", + "markdown-image-text": "#61afef", + "markdown-code-block": "#528bff" + } + }, + "dark": { + "seeds": { + "neutral": "#1e222a", + "primary": "#61afef", + "success": "#98c379", + "warning": "#e5c07b", + "error": "#e06c75", + "info": "#56b6c2", + "interactive": "#61afef", + "diffAdd": "#4b815a", + "diffDelete": "#b2555f" + }, + "overrides": { + "background-base": "#1e222a", + "background-weak": "#212631", + "background-strong": "#1b1f27", + "background-stronger": "#171b23", + "border-weak-base": "#323848", + "border-weak-hover": "#363d52", + "border-weak-active": "#3c435c", + "border-weak-selected": "#424967", + "border-weak-disabled": "#141720", + "border-weak-focus": "#3f4560", + "border-base": "#4a5164", + "border-hover": "#515871", + "border-active": "#585f7e", + "border-selected": "#60688a", + "border-disabled": "#1a1e27", + "border-focus": "#555c79", + "border-strong-base": "#6a7390", + "border-strong-hover": "#737c9d", + "border-strong-active": "#7d87ab", + "border-strong-selected": "#8791b8", + "border-strong-disabled": "#212533", + "border-strong-focus": "#7680a2", + "surface-diff-add-base": "#1c2a26", + "surface-diff-delete-base": "#2a1c22", + "surface-diff-hidden-base": "#232836", + "text-base": "#abb2bf", + "text-weak": "#818899", + "text-strong": "#f6f7fb", + "syntax-string": "#98c379", + "syntax-primitive": "#e06c75", + "syntax-property": "#61afef", + "syntax-type": "#e5c07b", + "syntax-constant": "#56b6c2", + "syntax-info": "#56b6c2", + "markdown-heading": "#61afef", + "markdown-text": "#abb2bf", + "markdown-link": "#61afef", + "markdown-link-text": "#56b6c2", + "markdown-code": "#98c379", + "markdown-block-quote": "#e5c07b", + "markdown-emph": "#e5c07b", + "markdown-strong": "#e06c75", + "markdown-horizontal-rule": "#2d3444", + "markdown-list-item": "#61afef", + "markdown-list-enumeration": "#56b6c2", + "markdown-image": "#61afef", + "markdown-image-text": "#56b6c2", + "markdown-code-block": "#abb2bf" + } + } +} diff --git a/packages/ui/src/theme/themes/shadesofpurple.json b/packages/ui/src/theme/themes/shadesofpurple.json new file mode 100644 index 000000000..bc625770f --- /dev/null +++ b/packages/ui/src/theme/themes/shadesofpurple.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Shades of Purple", + "id": "shadesofpurple", + "light": { + "seeds": { + "neutral": "#f7ebff", + "primary": "#7a5af8", + "success": "#3dd598", + "warning": "#f7c948", + "error": "#ff6bd5", + "info": "#62d4ff", + "interactive": "#7a5af8", + "diffAdd": "#c8f8da", + "diffDelete": "#ffc3ef" + }, + "overrides": { + "background-base": "#f7ebff", + "background-weak": "#f2e2ff", + "background-strong": "#fbf2ff", + "background-stronger": "#fff7ff", + "border-weak-base": "#e5d3ff", + "border-weak-hover": "#dac8f5", + "border-weak-active": "#d1bdeb", + "border-weak-selected": "#c6b3e1", + "border-weak-disabled": "#fcf6ff", + "border-weak-focus": "#ccb9e7", + "border-base": "#baa4d5", + "border-hover": "#b098cb", + "border-active": "#a68dc2", + "border-selected": "#9b82b8", + "border-disabled": "#f1e7ff", + "border-focus": "#a692c6", + "border-strong-base": "#8769a9", + "border-strong-hover": "#7b5c9d", + "border-strong-active": "#704f91", + "border-strong-selected": "#664587", + "border-strong-disabled": "#d8c4f0", + "border-strong-focus": "#755495", + "surface-diff-add-base": "#edf8f1", + "surface-diff-delete-base": "#ffe4f4", + "surface-diff-hidden-base": "#e9e4ff", + "text-base": "#3b2c59", + "text-weak": "#6c568f", + "text-strong": "#1c1033", + "syntax-string": "#3dd598", + "syntax-primitive": "#ff6bd5", + "syntax-property": "#7a5af8", + "syntax-type": "#f7c948", + "syntax-constant": "#62d4ff", + "syntax-info": "#62d4ff", + "markdown-heading": "#7a5af8", + "markdown-text": "#3b2c59", + "markdown-link": "#7a5af8", + "markdown-link-text": "#62d4ff", + "markdown-code": "#3dd598", + "markdown-block-quote": "#f7c948", + "markdown-emph": "#f7c948", + "markdown-strong": "#ff6bd5", + "markdown-horizontal-rule": "#decbed", + "markdown-list-item": "#7a5af8", + "markdown-list-enumeration": "#62d4ff", + "markdown-image": "#7a5af8", + "markdown-image-text": "#62d4ff", + "markdown-code-block": "#7a5af8" + } + }, + "dark": { + "seeds": { + "neutral": "#1a102b", + "primary": "#c792ff", + "success": "#7be0b0", + "warning": "#ffd580", + "error": "#ff7ac6", + "info": "#7dd4ff", + "interactive": "#c792ff", + "diffAdd": "#53c39f", + "diffDelete": "#d85aa0" + }, + "overrides": { + "background-base": "#1a102b", + "background-weak": "#1f1434", + "background-strong": "#1c122f", + "background-stronger": "#170e26", + "border-weak-base": "#352552", + "border-weak-hover": "#3a2a5d", + "border-weak-active": "#402f68", + "border-weak-selected": "#463674", + "border-weak-disabled": "#10091b", + "border-weak-focus": "#3d2d65", + "border-base": "#4d3a73", + "border-hover": "#553f7f", + "border-active": "#5d468c", + "border-selected": "#654c99", + "border-disabled": "#150d21", + "border-focus": "#594283", + "border-strong-base": "#7659b0", + "border-strong-hover": "#8262be", + "border-strong-active": "#8e6ccc", + "border-strong-selected": "#9a77da", + "border-strong-disabled": "#1c122c", + "border-strong-focus": "#8666c4", + "surface-diff-add-base": "#142c27", + "surface-diff-delete-base": "#2d1424", + "surface-diff-hidden-base": "#231737", + "text-base": "#f5f0ff", + "text-weak": "#c9b6ff", + "text-strong": "#ffffff", + "syntax-string": "#7be0b0", + "syntax-primitive": "#ff7ac6", + "syntax-property": "#c792ff", + "syntax-type": "#ffd580", + "syntax-constant": "#7dd4ff", + "syntax-info": "#7dd4ff", + "markdown-heading": "#c792ff", + "markdown-text": "#f5f0ff", + "markdown-link": "#c792ff", + "markdown-link-text": "#7dd4ff", + "markdown-code": "#7be0b0", + "markdown-block-quote": "#ffd580", + "markdown-emph": "#ffd580", + "markdown-strong": "#ff7ac6", + "markdown-horizontal-rule": "#2d1d41", + "markdown-list-item": "#c792ff", + "markdown-list-enumeration": "#7dd4ff", + "markdown-image": "#c792ff", + "markdown-image-text": "#7dd4ff", + "markdown-code-block": "#f5f0ff" + } + } +} diff --git a/packages/ui/src/theme/themes/solarized.json b/packages/ui/src/theme/themes/solarized.json new file mode 100644 index 000000000..7cb44775a --- /dev/null +++ b/packages/ui/src/theme/themes/solarized.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Solarized", + "id": "solarized", + "light": { + "seeds": { + "neutral": "#fdf6e3", + "primary": "#268bd2", + "success": "#859900", + "warning": "#b58900", + "error": "#dc322f", + "info": "#2aa198", + "interactive": "#268bd2", + "diffAdd": "#c6dc7a", + "diffDelete": "#f2a1a1" + }, + "overrides": { + "background-base": "#fdf6e3", + "background-weak": "#f6efda", + "background-strong": "#faf3dc", + "background-stronger": "#f6edd4", + "border-weak-base": "#e3e0cd", + "border-weak-hover": "#d9d4c2", + "border-weak-active": "#cfcab7", + "border-weak-selected": "#c5c0ad", + "border-weak-disabled": "#f2edda", + "border-weak-focus": "#cbc6b2", + "border-base": "#bcb5a0", + "border-hover": "#b1aa96", + "border-active": "#a59f8c", + "border-selected": "#999382", + "border-disabled": "#ede7d4", + "border-focus": "#aca58f", + "border-strong-base": "#8c8572", + "border-strong-hover": "#7f7866", + "border-strong-active": "#716b5b", + "border-strong-selected": "#645f50", + "border-strong-disabled": "#d5cdb8", + "border-strong-focus": "#78715f", + "surface-diff-add-base": "#eef5d6", + "surface-diff-delete-base": "#fde4dd", + "surface-diff-hidden-base": "#e3ecf3", + "text-base": "#586e75", + "text-weak": "#7a8c8e", + "text-strong": "#073642", + "syntax-string": "#859900", + "syntax-primitive": "#d33682", + "syntax-property": "#268bd2", + "syntax-type": "#b58900", + "syntax-constant": "#2aa198", + "syntax-info": "#2aa198", + "markdown-heading": "#268bd2", + "markdown-text": "#586e75", + "markdown-link": "#268bd2", + "markdown-link-text": "#2aa198", + "markdown-code": "#859900", + "markdown-block-quote": "#b58900", + "markdown-emph": "#b58900", + "markdown-strong": "#d33682", + "markdown-horizontal-rule": "#cfd1bf", + "markdown-list-item": "#268bd2", + "markdown-list-enumeration": "#2aa198", + "markdown-image": "#268bd2", + "markdown-image-text": "#2aa198", + "markdown-code-block": "#2aa198" + } + }, + "dark": { + "seeds": { + "neutral": "#002b36", + "primary": "#6c71c4", + "success": "#859900", + "warning": "#b58900", + "error": "#dc322f", + "info": "#2aa198", + "interactive": "#6c71c4", + "diffAdd": "#4c7654", + "diffDelete": "#c34b4b" + }, + "overrides": { + "background-base": "#001f27", + "background-weak": "#022733", + "background-strong": "#01222b", + "background-stronger": "#032830", + "border-weak-base": "#20373f", + "border-weak-hover": "#243e47", + "border-weak-active": "#28434f", + "border-weak-selected": "#2d4958", + "border-weak-disabled": "#0f2026", + "border-weak-focus": "#2a4552", + "border-base": "#31505b", + "border-hover": "#365765", + "border-active": "#3c5e70", + "border-selected": "#42657a", + "border-disabled": "#13272e", + "border-focus": "#3a5a6b", + "border-strong-base": "#4a7887", + "border-strong-hover": "#528294", + "border-strong-active": "#5a8ca1", + "border-strong-selected": "#6396ae", + "border-strong-disabled": "#1b323b", + "border-strong-focus": "#56879a", + "surface-diff-add-base": "#0f2f29", + "surface-diff-delete-base": "#321c1c", + "surface-diff-hidden-base": "#0f3844", + "text-base": "#93a1a1", + "text-weak": "#6c7f80", + "text-strong": "#fdf6e3", + "syntax-string": "#859900", + "syntax-primitive": "#d33682", + "syntax-property": "#6c71c4", + "syntax-type": "#b58900", + "syntax-constant": "#2aa198", + "syntax-info": "#2aa198", + "markdown-heading": "#6c71c4", + "markdown-text": "#93a1a1", + "markdown-link": "#6c71c4", + "markdown-link-text": "#2aa198", + "markdown-code": "#859900", + "markdown-block-quote": "#b58900", + "markdown-emph": "#b58900", + "markdown-strong": "#d33682", + "markdown-horizontal-rule": "#0e3b46", + "markdown-list-item": "#6c71c4", + "markdown-list-enumeration": "#2aa198", + "markdown-image": "#6c71c4", + "markdown-image-text": "#2aa198", + "markdown-code-block": "#93a1a1" + } + } +} diff --git a/packages/ui/src/theme/themes/tokyonight.json b/packages/ui/src/theme/themes/tokyonight.json new file mode 100644 index 000000000..31d0e8a47 --- /dev/null +++ b/packages/ui/src/theme/themes/tokyonight.json @@ -0,0 +1,155 @@ +{ + "$schema": "https://opencode.ai/desktop-theme.json", + "name": "Tokyonight", + "id": "tokyonight", + "light": { + "seeds": { + "neutral": "#e1e2e7", + "primary": "#2e7de9", + "success": "#587539", + "warning": "#8c6c3e", + "error": "#c94060", + "info": "#007197", + "interactive": "#2e7de9", + "diffAdd": "#4f8f7b", + "diffDelete": "#d05f7c" + }, + "overrides": { + "background-base": "#e1e2e7", + "background-weak": "#dee0ea", + "background-strong": "#e5e6ee", + "background-stronger": "#e9eaf1", + "border-weak-base": "#cdd0dc", + "border-weak-hover": "#c3c6d2", + "border-weak-active": "#b9bcc8", + "border-weak-selected": "#aeb2bf", + "border-weak-disabled": "#e6e7ef", + "border-weak-focus": "#b3b6c3", + "border-base": "#a7abbb", + "border-hover": "#9ba0b1", + "border-active": "#9095a8", + "border-selected": "#83889e", + "border-disabled": "#dedfe6", + "border-focus": "#9599a8", + "border-strong-base": "#757b90", + "border-strong-hover": "#6a7084", + "border-strong-active": "#5f6578", + "border-strong-selected": "#545a6d", + "border-strong-disabled": "#c4c6d0", + "border-strong-focus": "#666b7f", + "surface-diff-add-base": "#dfe7da", + "surface-diff-delete-base": "#f4dadd", + "surface-diff-hidden-base": "#cfd1dd", + "text-base": "#273153", + "text-weak": "#5c6390", + "text-strong": "#1c2544", + "syntax-string": "#587539", + "syntax-primitive": "#b15c00", + "syntax-property": "#9854f1", + "syntax-type": "#3760bf", + "syntax-constant": "#007197", + "syntax-info": "#007197", + "markdown-heading": "#9854f1", + "markdown-text": "#273153", + "markdown-link": "#2e7de9", + "markdown-link-text": "#007197", + "markdown-code": "#587539", + "markdown-block-quote": "#8c6c3e", + "markdown-emph": "#8c6c3e", + "markdown-strong": "#b15c00", + "markdown-horizontal-rule": "#a1a6c5", + "markdown-list-item": "#2e7de9", + "markdown-list-enumeration": "#007197", + "markdown-image": "#2e7de9", + "markdown-image-text": "#007197", + "markdown-code-block": "#3760bf" + } + }, + "dark": { + "seeds": { + "neutral": "#1a1b26", + "primary": "#7aa2f7", + "success": "#9ece6a", + "warning": "#e0af68", + "error": "#f7768e", + "info": "#7dcfff", + "interactive": "#7aa2f7", + "diffAdd": "#41a6b5", + "diffDelete": "#c34043" + }, + "overrides": { + "background-base": "#0f111a", + "background-weak": "#111428", + "background-strong": "#101324", + "background-stronger": "#13172a", + "border-weak-base": "#25283b", + "border-weak-hover": "#292c43", + "border-weak-active": "#2e314b", + "border-weak-selected": "#343755", + "border-weak-disabled": "#151727", + "border-weak-focus": "#30324f", + "border-base": "#3a3e57", + "border-hover": "#414264", + "border-active": "#474972", + "border-selected": "#4f507f", + "border-disabled": "#1c1d2d", + "border-focus": "#45496f", + "border-strong-base": "#5a5f82", + "border-strong-hover": "#646994", + "border-strong-active": "#6f74a6", + "border-strong-selected": "#7a7fb8", + "border-strong-disabled": "#23243a", + "border-strong-focus": "#6a6f9f", + "surface-base": "#1f2335", + "base": "#1f2335", + "surface-base-hover": "#232840", + "surface-base-active": "#262c46", + "surface-base-interactive-active": "#2b3357", + "base2": "#1f2335", + "base3": "#1f2335", + "surface-inset-base": "#161a2ab3", + "surface-inset-base-hover": "#161a2acc", + "surface-inset-strong": "#0d111fcc", + "surface-inset-strong-hover": "#0d111fcc", + "surface-raised-base": "#242a42", + "surface-float-base": "#242b45", + "surface-float-base-hover": "#2a3154", + "surface-raised-base-hover": "#272e49", + "surface-raised-base-active": "#2c3353", + "surface-raised-strong": "#31385a", + "surface-raised-strong-hover": "#373f6b", + "surface-raised-stronger": "#3b4261", + "surface-raised-stronger-hover": "#444c82", + "surface-weak": "#1b2033", + "surface-weaker": "#181d2d", + "surface-strong": "#323858", + "surface-raised-stronger-non-alpha": "#2b3150", + "surface-diff-add-base": "#1c2a38", + "surface-diff-delete-base": "#2a1f32", + "surface-diff-hidden-base": "#24283b", + "text-base": "#c0caf5", + "text-weak": "#7a88cf", + "text-strong": "#eaeaff", + "syntax-string": "#9ece6a", + "syntax-primitive": "#ff9e64", + "syntax-property": "#bb9af7", + "syntax-type": "#e0af68", + "syntax-constant": "#7dcfff", + "syntax-info": "#7dcfff", + "markdown-heading": "#bb9af7", + "markdown-text": "#c0caf5", + "markdown-link": "#7aa2f7", + "markdown-link-text": "#7dcfff", + "markdown-code": "#9ece6a", + "markdown-block-quote": "#e0af68", + "markdown-emph": "#e0af68", + "markdown-strong": "#ff9e64", + "markdown-horizontal-rule": "#3b4261", + "markdown-list-item": "#7aa2f7", + "markdown-list-enumeration": "#7dcfff", + "markdown-image": "#7aa2f7", + "markdown-image-text": "#7dcfff", + "markdown-code-block": "#c0caf5" + } + } +} diff --git a/packages/ui/src/theme/types.ts b/packages/ui/src/theme/types.ts new file mode 100644 index 000000000..7a584ef0e --- /dev/null +++ b/packages/ui/src/theme/types.ts @@ -0,0 +1,94 @@ +/** + * Desktop Theme System + * + * Unlike the TUI themes, desktop themes require more design tokens and use + * OKLCH color space for generating color scales from seed colors. + */ + +/** A hex color string like "#ffffff" or "#fff" */ +export type HexColor = `#${string}` + +/** OKLCH color representation for calculations */ +export interface OklchColor { + l: number // Lightness 0-1 + c: number // Chroma 0-0.4+ + h: number // Hue 0-360 +} + +/** The minimum colors needed to define a theme variant */ +export interface ThemeSeedColors { + /** Base neutral color - used to generate gray scale (smoke/ink) */ + neutral: HexColor + /** Primary brand/accent color */ + primary: HexColor + /** Success color (green) */ + success: HexColor + /** Warning color (yellow/orange) */ + warning: HexColor + /** Error/critical color (red) */ + error: HexColor + /** Info color (purple/blue) */ + info: HexColor + /** Interactive/link color (blue) */ + interactive: HexColor + /** Diff add color */ + diffAdd: HexColor + /** Diff delete color */ + diffDelete: HexColor +} + +/** A theme variant (light or dark) with seed colors and optional overrides */ +export interface ThemeVariant { + /** Seed colors used to generate the full palette */ + seeds: ThemeSeedColors + /** Optional direct overrides for any CSS variable (without -- prefix) */ + overrides?: Record<string, ColorValue> +} + +/** A complete desktop theme definition */ +export interface DesktopTheme { + /** Schema version for future compatibility */ + $schema?: string + /** Theme display name */ + name: string + /** Theme identifier (slug) */ + id: string + /** Light mode variant */ + light: ThemeVariant + /** Dark mode variant */ + dark: ThemeVariant +} + +/** + * Categories of CSS variables that get generated from seed colors. + * Each category maps to specific CSS custom properties. + */ +export type TokenCategory = + | "background" + | "surface" + | "text" + | "border" + | "icon" + | "input" + | "button" + | "syntax" + | "markdown" + | "diff" + | "avatar" + +/** + * All CSS variable names (without -- prefix) that the theme system generates. + * These match the variables defined in theme.css + */ +export type ThemeToken = string + +/** A CSS variable reference like "var(--text-weak)" */ +export type CssVarRef = `var(--${string})` + +/** A color value - either a hex color or a CSS variable reference */ +export type ColorValue = HexColor | CssVarRef + +/** + * Resolved theme - all tokens mapped to their final colors + */ +export type ResolvedTheme = Record<ThemeToken, ColorValue> |
