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:
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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -288,6 +288,8 @@ const settings: Record<string, string> = {
|
||||
'settings.theme.chooseImage': 'Choose image',
|
||||
'settings.theme.clearImage': 'Remove',
|
||||
'settings.theme.imageOpacity': 'Image opacity',
|
||||
'settings.theme.backgroundImageDim': 'Background dim',
|
||||
'settings.theme.backgroundImageDimDesc': 'Overlays a black dim layer on top of the image; text is brightened automatically. 30–50% works well for bright images',
|
||||
'settings.theme.customBadge': 'Custom',
|
||||
'settings.theme.deleteTitle': 'Delete custom theme',
|
||||
'settings.theme.create': 'New theme',
|
||||
|
||||
@@ -283,6 +283,8 @@ const settings: Record<string, string> = {
|
||||
'settings.theme.chooseImage': '画像を選択',
|
||||
'settings.theme.clearImage': '削除',
|
||||
'settings.theme.imageOpacity': '画像の不透明度',
|
||||
'settings.theme.backgroundImageDim': '背景の減光',
|
||||
'settings.theme.backgroundImageDimDesc': '画像の上に黒い減光レイヤーを重ね、文字は自動で明るくなります。明るい画像は 30–50% が目安です',
|
||||
'settings.theme.customBadge': 'カスタム',
|
||||
'settings.theme.deleteTitle': 'カスタムテーマを削除',
|
||||
'settings.theme.create': '新しいテーマ',
|
||||
|
||||
@@ -272,6 +272,8 @@ const settings: Record<string, string> = {
|
||||
'settings.theme.chooseImage': '选择图片',
|
||||
'settings.theme.clearImage': '移除图片',
|
||||
'settings.theme.imageOpacity': '图片不透明度',
|
||||
'settings.theme.backgroundImageDim': '背景压暗',
|
||||
'settings.theme.backgroundImageDimDesc': '在图片之上叠加黑色压暗层,文字随之自动提亮;亮图建议 30–50%',
|
||||
'settings.theme.customBadge': '自定义',
|
||||
'settings.theme.deleteTitle': '删除自定义主题',
|
||||
'settings.theme.create': '新建主题',
|
||||
|
||||
@@ -272,6 +272,8 @@ const settings: Record<string, string> = {
|
||||
'settings.theme.chooseImage': '選擇圖片',
|
||||
'settings.theme.clearImage': '移除圖片',
|
||||
'settings.theme.imageOpacity': '圖片不透明度',
|
||||
'settings.theme.backgroundImageDim': '背景壓暗',
|
||||
'settings.theme.backgroundImageDimDesc': '在圖片之上疊加黑色壓暗層,文字隨之自動提亮;亮圖建議 30–50%',
|
||||
'settings.theme.customBadge': '自訂',
|
||||
'settings.theme.deleteTitle': '刪除自訂主題',
|
||||
'settings.theme.create': '新增主題',
|
||||
|
||||
@@ -112,6 +112,8 @@ export interface TerminalSettings {
|
||||
backgroundImage: string
|
||||
/** background image layer opacity, 10..100 (%) */
|
||||
backgroundImageOpacity: number
|
||||
/** black scrim over the background image, under the text, 0..90 (%); 0 = off */
|
||||
backgroundImageDim: number
|
||||
}
|
||||
|
||||
export type HighlightMode = 'all' | 'basic' | 'off'
|
||||
@@ -459,7 +461,8 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||
highlightThemeColors: false,
|
||||
highlightPerHost: false,
|
||||
backgroundImage: '',
|
||||
backgroundImageOpacity: 30
|
||||
backgroundImageOpacity: 30,
|
||||
backgroundImageDim: 0
|
||||
},
|
||||
customThemes: [],
|
||||
highlightRules: DEFAULT_HIGHLIGHT_RULES,
|
||||
|
||||
Reference in new issue
Block a user