feat(theme): add background image dim scrim slider

New terminal.backgroundImageDim setting (0-90%, default 0 = off). A black
scrim layer sits between the background image and the xterm screen, so
bright wallpapers stay readable at any opacity: unlike the opacity slider
(which blends the image toward the dark dock base), the scrim darkens the
image while preserving its saturation, and the raised minimumContrastRatio
(4.5) keeps lifted text legible on top.

- settings: backgroundImageDim with deepMerge type-fallback sanitize
- TerminalView: render .term-bg-dim only when image set and dim > 0
- ThemeSettingsTab: slider follows the existing draft + onChangeComplete
  pattern (no settings writes while dragging)
- i18n: settings.theme.backgroundImageDim(+Desc) in zh-CN/zh-TW/en/ja
- AGENTS.md: document the third image-mode invariant
This commit is contained in:
Bill committed 2026-10-07 21:24:27 +08:00
1 parent e16c860c7a
commit 6c04f0e8c1
9 files changed
+53 -2

No files matched your search

@@ -32,6 +32,7 @@ export function ThemeSettingsTab({
const setCustomThemes = useSettingsStore((s) => s.setCustomThemes)
const activeTheme = useResolvedTheme()
const [opacityDraft, setOpacityDraft] = useState<number | null>(null)
const [dimDraft, setDimDraft] = useState<number | null>(null)
const themeCards = useMemo(
() => [...BUILTIN_THEMES, ...settings.customThemes],
@@ -90,6 +91,26 @@ export function ThemeSettingsTab({
/>
</div>
)}
{settings.terminal.backgroundImage !== '' && (
<div className="theme-accent-row">
<div>
<div className="settings-block-label">{t('settings.theme.backgroundImageDim')}</div>
<div className="settings-block-hint">{t('settings.theme.backgroundImageDimDesc')}</div>
</div>
<Slider
style={{ width: 200 }}
min={0}
max={90}
step={5}
value={dimDraft ?? settings.terminal.backgroundImageDim}
onChange={(v) => setDimDraft(v)}
onChangeComplete={(v) => {
setDimDraft(null)
void updateTerminal({ backgroundImageDim: v })
}}
/>
</div>
)}
<div className="theme-accent-row">
<div>
<div className="settings-block-label">{t('settings.theme.accent')}</div>
@@ -1400,6 +1400,14 @@ export const TerminalView: ForwardRefExoticComponent<TerminalViewProps & { ref?:
}}
/>
)}
{settings.terminal.backgroundImage !== '' && settings.terminal.backgroundImageDim > 0 && (
<div
className="term-bg-dim"
style={{
opacity: Math.min(90, Math.max(0, settings.terminal.backgroundImageDim)) / 100
}}
/>
)}
{dead && (
<div className="term-dead-mask">
<div>{t('terminal.dead.message', { code: exitCode })}</div>
+11
View File
@@ -380,6 +380,17 @@
pointer-events: none;
}
/* Black scrim between the background image and the xterm screen: dims the
image without washing it out (unlike opacity, which blends toward the dark
dock base), so bright images stay readable at any opacity. */
.term-bg-dim {
position: absolute;
inset: 0;
z-index: 0;
background: #000;
pointer-events: none;
}
.terminal-view-dock > .xterm {
position: relative;
z-index: 1;