mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
feat(themes): let app themes set the code editor selection color (#42349)
* feat(themes): let app themes set the code editor selection color * docs(themes): keep the editor selection translucent; rewrap
This commit is contained in:
@@ -64,6 +64,7 @@ Every key an app theme may set in `tokens` (see [plugin themes](plugin-themes.md
|
||||
| `--chat-selection` | Selected text in chat; may be translucent, it paints over the chat | no | `--text-link` (else Orca's link blue) mixed into the chat background, up to 30% light and 40% dark, as deep as keeps selected text 4.5:1 (never under 15%) |
|
||||
| `--chat-selection-foreground` | Text inside a chat selection | no | same as `--foreground` |
|
||||
| `--text-link` | Links in Markdown previews, editors and agent chat | no | Orca's built-in color |
|
||||
| `--editor-selection` | Selected text in the code editor; keep it translucent so find matches show through. An unfocused editor shows it at half its opacity | no | `--ring` at 30% opacity |
|
||||
| `--diff-added` | Added-line hue: Monaco overview ruler and chat diff text | no | same as `--git-decoration-added` |
|
||||
| `--diff-added-background` | Added lines in diffs | no | light: `--diff-added` at 13% opacity; dark: `--diff-added` at 16% opacity |
|
||||
| `--diff-added-emphasis` | Added characters within a changed line | no | light: `--diff-added` at 15% opacity; dark: `--diff-added` at 20% opacity |
|
||||
|
||||
@@ -33,13 +33,14 @@ file of at most 16 KiB, or 64 KiB for an icon theme.
|
||||
`--primary`/`--primary-foreground`) must be opaque with at least 4.5:1 contrast; a pair that
|
||||
fails is dropped and keeps Orca's built-in colors. A pair you set only half of is checked
|
||||
against what the other half falls back to. Some checks drop only your token: `--text-link`
|
||||
needs 4.5:1 on `--editor-surface`, each `--git-decoration-*` 3:1 on `--sidebar`,
|
||||
`--chat-bold-foreground` 4.5:1 on the chat background and on `--chat-user-message`,
|
||||
`--chat-muted-foreground` 4.5:1 on the chat background and on code blocks, and
|
||||
needs 4.5:1 on `--editor-surface`, `--editor-selection` 4.5:1 under `--foreground`, each
|
||||
`--git-decoration-*` 3:1 on `--sidebar`, `--chat-bold-foreground` 4.5:1 on the chat
|
||||
background and on `--chat-user-message`, `--chat-muted-foreground` 4.5:1 on the chat
|
||||
background and on code blocks, and
|
||||
`--worktree-sidebar-active`, `--chat-user-message`, `--chat-inline-code` (against the chat link
|
||||
color), `--chat-code-block` and the diff line and character tints 4.5:1 with the text drawn on
|
||||
them. Those surfaces, `--chat-inline-code`, `--chat-selection` and `--host-badge` may be
|
||||
translucent; they are checked as painted over the surface beneath.
|
||||
them. Those surfaces, `--chat-inline-code`, `--chat-selection`, `--editor-selection` and
|
||||
`--host-badge` may be translucent; they are checked as painted over the surface beneath.
|
||||
- An omitted surface token paints exactly what Orca paints without a theme. The code editor's
|
||||
diff view and links keep Monaco's own colors unless the theme sets a diff token or
|
||||
`--text-link`. Setting only `--diff-added` and `--diff-removed` is enough; the line, gutter and
|
||||
@@ -57,8 +58,9 @@ An app theme also colors the code editor: its background, text, line numbers, cu
|
||||
selection follow the theme's tokens. Users can also pick any app theme, of either base, as just
|
||||
their editor theme (Settings → Appearance → Light/Dark editor theme); the editor then takes its
|
||||
colors from `--editor-surface` (else `--background`), `--foreground`, `--muted-foreground`,
|
||||
`--accent`, `--ring`, `--popover`, `--border` and `tokenColors`, while the rest of Orca keeps the
|
||||
app theme. If those tokens can't make a legible editor, it uses Monaco's default colors. To
|
||||
`--accent`, `--ring`, `--editor-selection`, `--popover`, `--border` and `tokenColors`, while the
|
||||
rest of Orca keeps the app theme. Keep `--editor-selection` translucent so find matches and
|
||||
highlighted words show through it; without it the selection is `--ring` at 30%. If those tokens can't make a legible editor, it uses Monaco's default colors. To
|
||||
color code itself, add `tokenColors` (Orca 1.4.245 and later) in the same shape as a VS Code theme's, so most VS Code theme lists paste in unchanged (`validate:plugin` flags entries without a `scope` and any setting other than these two):
|
||||
|
||||
```json
|
||||
|
||||
@@ -119,6 +119,11 @@ const CONTRAST_RULE_DEFINITIONS: readonly ContrastRule[] = [
|
||||
['--chat-inline-code', '--chat-user-message']
|
||||
].map((under) => chatPair('--chat-selection', '--chat-selection-foreground', undefined, under)),
|
||||
{ ...textPair('--editor-surface', '--text-link'), drop: ['--text-link'] },
|
||||
{
|
||||
...textPair('--editor-selection', '--foreground'),
|
||||
drop: ['--editor-selection'],
|
||||
over: '--editor-surface'
|
||||
},
|
||||
chatPair('--chat-user-message', '--chat-link', ['--chat-user-message']),
|
||||
...GIT_DECORATIONS.map((status): ContrastRule => ({
|
||||
surface: '--sidebar',
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import type { ColorProbe } from '../../../app-shell/appearance/plugin-theme-tokens'
|
||||
import { derivePluginMonacoTheme } from './plugin-monaco-theme'
|
||||
|
||||
const probe: ColorProbe = (value) => {
|
||||
const hex = /^#([0-9a-f]{6})([0-9a-f]{2})?$/i.exec(value.trim())
|
||||
if (!hex) {
|
||||
return null
|
||||
}
|
||||
const channel = (offset: number): number => Number.parseInt(hex[1].slice(offset, offset + 2), 16)
|
||||
return [channel(0), channel(2), channel(4), hex[2] ? Number.parseInt(hex[2], 16) / 255 : 1]
|
||||
}
|
||||
|
||||
it('paints a theme-chosen editor selection instead of the ring', () => {
|
||||
const tokens: Record<string, string> = {
|
||||
'--background': '#261d45',
|
||||
'--foreground': '#ffffff',
|
||||
'--ring': '#00ff6a',
|
||||
'--editor-selection': '#ff2a6d66'
|
||||
}
|
||||
const theme = derivePluginMonacoTheme({
|
||||
base: 'dark',
|
||||
read: (token) => tokens[token] ?? '',
|
||||
painted: tokens,
|
||||
probe
|
||||
})
|
||||
expect(theme?.data.colors['editor.selectionBackground']).toBe('#ff2a6d66')
|
||||
expect(theme?.data.colors['editor.inactiveSelectionBackground']).toBe('#ff2a6d33')
|
||||
})
|
||||
@@ -84,6 +84,9 @@ export function derivePluginMonacoTheme(input: {
|
||||
const muted = color('--muted-foreground') ?? foreground
|
||||
const accent = color('--accent')
|
||||
const ring = color('--ring') ?? foreground
|
||||
const themed = input.painted['--editor-selection'] ? color('--editor-selection') : null
|
||||
// Why the ring by default: a theme's accent is a faint hover tint, too weak to read as a selection.
|
||||
const selection: Rgba = themed ?? [ring[0], ring[1], ring[2], 0.3]
|
||||
const popover = color('--popover')
|
||||
const border = color('--border')
|
||||
const colors: Record<string, string> = {
|
||||
@@ -94,9 +97,13 @@ export function derivePluginMonacoTheme(input: {
|
||||
'editorLineNumber.foreground': hex(muted),
|
||||
'editorLineNumber.activeForeground': hex(foreground),
|
||||
'editorCursor.foreground': hex(ring),
|
||||
// Why from the ring: a theme's accent is a faint hover tint, too weak to read as a selection.
|
||||
'editor.selectionBackground': hex([ring[0], ring[1], ring[2], 0.3]),
|
||||
'editor.inactiveSelectionBackground': hex([ring[0], ring[1], ring[2], 0.15])
|
||||
'editor.selectionBackground': hex(selection),
|
||||
'editor.inactiveSelectionBackground': hex([
|
||||
selection[0],
|
||||
selection[1],
|
||||
selection[2],
|
||||
selection[3] / 2
|
||||
])
|
||||
}
|
||||
if (accent) {
|
||||
colors['editor.lineHighlightBackground'] = hex(accent)
|
||||
|
||||
@@ -68,6 +68,7 @@ export const PLUGIN_APP_THEME_TOKENS = [
|
||||
'--chat-selection',
|
||||
'--chat-selection-foreground',
|
||||
'--text-link',
|
||||
'--editor-selection',
|
||||
'--diff-added',
|
||||
'--diff-added-background',
|
||||
'--diff-added-emphasis',
|
||||
|
||||
@@ -74,7 +74,9 @@ const PLUGIN_APP_THEME_CSS_FALLBACKS: Record<
|
||||
light: { tint: '--text-link', percent: 30, into: '--chat-canvas' },
|
||||
dark: { tint: '--text-link', percent: 40, into: '--chat-canvas' }
|
||||
},
|
||||
'--chat-selection-foreground': ['--foreground']
|
||||
'--chat-selection-foreground': ['--foreground'],
|
||||
// Monaco paints this one, not main.css (plugin-monaco-theme.ts).
|
||||
'--editor-selection': { tint: '--ring', percent: 30 }
|
||||
}
|
||||
|
||||
export function pluginAppThemeCssFallback(
|
||||
|
||||
@@ -73,6 +73,8 @@ const TOKEN_ROLES: Record<PluginAppThemeToken, string> = {
|
||||
'--chat-selection': 'Selected text in chat; may be translucent, it paints over the chat',
|
||||
'--chat-selection-foreground': 'Text inside a chat selection',
|
||||
'--text-link': 'Links in Markdown previews, editors and agent chat',
|
||||
'--editor-selection':
|
||||
'Selected text in the code editor; keep it translucent so find matches show through. An unfocused editor shows it at half its opacity',
|
||||
'--diff-added': 'Added-line hue: Monaco overview ruler and chat diff text',
|
||||
'--diff-added-background': 'Added lines in diffs',
|
||||
'--diff-added-emphasis': 'Added characters within a changed line',
|
||||
|
||||
Reference in New Issue
Block a user