feat(desktop): theme xterm with active light/dark mode
The right-sidebar terminal hardcoded a light palette, which read poorly on the dark glass surface. Subscribe to `useTheme().resolvedMode` and hot-swap `term.options.theme` so Shift+X (and any other mode change) updates the terminal in place without tearing down the PTY session. Dark mode uses xterm's built-in defaults (white fg/cursor + vivid ANSI 16) with just a transparent background so the glass shows through; light mode keeps the existing hand-tuned overrides for legibility on a bright surface.
This commit is contained in:
@@ -1,14 +1,21 @@
|
|||||||
import type { Terminal } from '@xterm/xterm'
|
import type { ITheme, Terminal } from '@xterm/xterm'
|
||||||
import type { CSSProperties } from 'react'
|
import type { CSSProperties } from 'react'
|
||||||
|
|
||||||
export const TERMINAL_THEME = {
|
// xterm's defaults (white fg/cursor + vivid ANSI 16) already match a dark
|
||||||
background: '#00000000',
|
// terminal — we just punch the background transparent so the glass shows.
|
||||||
|
// Light mode has no built-in, so hand-tune fg/cursor for a bright surface.
|
||||||
|
const TRANSPARENT_BG: ITheme = { background: '#00000000' }
|
||||||
|
|
||||||
|
const LIGHT_OVERRIDES: ITheme = {
|
||||||
cursor: '#6f6f6f',
|
cursor: '#6f6f6f',
|
||||||
cursorAccent: '#f7f7f7',
|
cursorAccent: '#f7f7f7',
|
||||||
foreground: '#4d4d4d',
|
foreground: '#4d4d4d',
|
||||||
selectionBackground: '#8c8c8c33'
|
selectionBackground: '#8c8c8c33'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const terminalTheme = (mode: 'light' | 'dark'): ITheme =>
|
||||||
|
mode === 'dark' ? TRANSPARENT_BG : { ...TRANSPARENT_BG, ...LIGHT_OVERRIDES }
|
||||||
|
|
||||||
export const isMacPlatform = () => navigator.platform.toLowerCase().includes('mac')
|
export const isMacPlatform = () => navigator.platform.toLowerCase().includes('mac')
|
||||||
|
|
||||||
export const addSelectionShortcutLabel = () => (isMacPlatform() ? '⌘L' : 'Ctrl+L')
|
export const addSelectionShortcutLabel = () => (isMacPlatform() ? '⌘L' : 'Ctrl+L')
|
||||||
|
|||||||
@@ -6,13 +6,9 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
|||||||
import type { CSSProperties } from 'react'
|
import type { CSSProperties } from 'react'
|
||||||
|
|
||||||
import { triggerHaptic } from '@/lib/haptics'
|
import { triggerHaptic } from '@/lib/haptics'
|
||||||
|
import { useTheme } from '@/themes/context'
|
||||||
|
|
||||||
import {
|
import { isAddSelectionShortcut, terminalSelectionAnchor, terminalSelectionLabel, terminalTheme } from './selection'
|
||||||
isAddSelectionShortcut,
|
|
||||||
TERMINAL_THEME,
|
|
||||||
terminalSelectionAnchor,
|
|
||||||
terminalSelectionLabel
|
|
||||||
} from './selection'
|
|
||||||
|
|
||||||
type TerminalStatus = 'closed' | 'open' | 'starting'
|
type TerminalStatus = 'closed' | 'open' | 'starting'
|
||||||
|
|
||||||
@@ -29,6 +25,8 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
|||||||
const selectionLabelRef = useRef('')
|
const selectionLabelRef = useRef('')
|
||||||
const selectionRef = useRef('')
|
const selectionRef = useRef('')
|
||||||
const onAddSelectionToChatRef = useRef(onAddSelectionToChat)
|
const onAddSelectionToChatRef = useRef(onAddSelectionToChat)
|
||||||
|
const { resolvedMode } = useTheme()
|
||||||
|
const resolvedModeRef = useRef(resolvedMode)
|
||||||
const [status, setStatus] = useState<TerminalStatus>('starting')
|
const [status, setStatus] = useState<TerminalStatus>('starting')
|
||||||
const [selection, setSelection] = useState('')
|
const [selection, setSelection] = useState('')
|
||||||
const [selectionStyle, setSelectionStyle] = useState<CSSProperties | null>(null)
|
const [selectionStyle, setSelectionStyle] = useState<CSSProperties | null>(null)
|
||||||
@@ -38,11 +36,20 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
|||||||
onAddSelectionToChatRef.current = onAddSelectionToChat
|
onAddSelectionToChatRef.current = onAddSelectionToChat
|
||||||
}, [onAddSelectionToChat])
|
}, [onAddSelectionToChat])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resolvedModeRef.current = resolvedMode
|
||||||
|
|
||||||
|
if (termRef.current) {
|
||||||
|
termRef.current.options.theme = terminalTheme(resolvedMode)
|
||||||
|
}
|
||||||
|
}, [resolvedMode])
|
||||||
|
|
||||||
const addSelectionToChat = useCallback(() => {
|
const addSelectionToChat = useCallback(() => {
|
||||||
const selectedText = selectionRef.current || termRef.current?.getSelection() || ''
|
const selectedText = selectionRef.current || termRef.current?.getSelection() || ''
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
selectionLabelRef.current || (termRef.current ? terminalSelectionLabel(termRef.current, shellNameRef.current, selectedText) : 'selection')
|
selectionLabelRef.current ||
|
||||||
|
(termRef.current ? terminalSelectionLabel(termRef.current, shellNameRef.current, selectedText) : 'selection')
|
||||||
|
|
||||||
const trimmed = selectedText.trim()
|
const trimmed = selectedText.trim()
|
||||||
|
|
||||||
@@ -102,7 +109,7 @@ export function useTerminalSession({ cwd, onAddSelectionToChat }: UseTerminalSes
|
|||||||
lineHeight: 1.12,
|
lineHeight: 1.12,
|
||||||
macOptionIsMeta: true,
|
macOptionIsMeta: true,
|
||||||
scrollback: 1000,
|
scrollback: 1000,
|
||||||
theme: TERMINAL_THEME
|
theme: terminalTheme(resolvedModeRef.current)
|
||||||
})
|
})
|
||||||
|
|
||||||
const fit = new FitAddon()
|
const fit = new FitAddon()
|
||||||
|
|||||||
Reference in New Issue
Block a user