diff --git a/v2/src/lib/components/Workspace/SettingsTab.svelte b/v2/src/lib/components/Workspace/SettingsTab.svelte index 1c13cba..7f17857 100644 --- a/v2/src/lib/components/Workspace/SettingsTab.svelte +++ b/v2/src/lib/components/Workspace/SettingsTab.svelte @@ -29,21 +29,40 @@ // Global settings let defaultShell = $state(''); let defaultCwd = $state(''); - let fontFamily = $state(''); - let fontSize = $state(''); + let uiFont = $state(''); + let uiFontSize = $state(''); + let termFont = $state(''); + let termFontSize = $state(''); let selectedTheme = $state(getCurrentTheme()); - let themeDropdownOpen = $state(false); - const FONT_OPTIONS = [ - 'JetBrains Mono', - 'Fira Code', - 'Cascadia Code', - 'Source Code Pro', - 'IBM Plex Mono', - 'Hack', - 'Inconsolata', - 'Ubuntu Mono', - 'monospace', + // Dropdown open states + let themeDropdownOpen = $state(false); + let uiFontDropdownOpen = $state(false); + let termFontDropdownOpen = $state(false); + + const UI_FONTS = [ + { value: '', label: 'System Default' }, + { value: 'Inter', label: 'Inter' }, + { value: 'IBM Plex Sans', label: 'IBM Plex Sans' }, + { value: 'Noto Sans', label: 'Noto Sans' }, + { value: 'Roboto', label: 'Roboto' }, + { value: 'Source Sans 3', label: 'Source Sans 3' }, + { value: 'Ubuntu', label: 'Ubuntu' }, + { value: 'JetBrains Mono', label: 'JetBrains Mono' }, + { value: 'Fira Code', label: 'Fira Code' }, + ]; + + const TERM_FONTS = [ + { value: '', label: 'Default (JetBrains Mono)' }, + { value: 'JetBrains Mono', label: 'JetBrains Mono' }, + { value: 'Fira Code', label: 'Fira Code' }, + { value: 'Cascadia Code', label: 'Cascadia Code' }, + { value: 'Source Code Pro', label: 'Source Code Pro' }, + { value: 'IBM Plex Mono', label: 'IBM Plex Mono' }, + { value: 'Hack', label: 'Hack' }, + { value: 'Inconsolata', label: 'Inconsolata' }, + { value: 'Ubuntu Mono', label: 'Ubuntu Mono' }, + { value: 'monospace', label: 'monospace' }, ]; // Group themes by category @@ -60,25 +79,33 @@ THEME_LIST.find(t => t.id === selectedTheme)?.label ?? selectedTheme, ); + let uiFontLabel = $derived( + UI_FONTS.find(f => f.value === uiFont)?.label ?? 'System Default', + ); + + let termFontLabel = $derived( + TERM_FONTS.find(f => f.value === termFont)?.label ?? 'Default (JetBrains Mono)', + ); + onMount(async () => { - const [shell, cwd, font, size] = await Promise.all([ + const [shell, cwd, font, size, tfont, tsize] = await Promise.all([ getSetting('default_shell'), getSetting('default_cwd'), - getSetting('font_family'), - getSetting('font_size'), + getSetting('ui_font_family'), + getSetting('ui_font_size'), + getSetting('term_font_family'), + getSetting('term_font_size'), ]); defaultShell = shell ?? ''; defaultCwd = cwd ?? ''; - fontFamily = font ?? ''; - fontSize = size ?? ''; + uiFont = font ?? ''; + uiFontSize = size ?? ''; + termFont = tfont ?? ''; + termFontSize = tsize ?? ''; selectedTheme = getCurrentTheme(); - - // Apply saved font settings - if (font) applyFont('--ui-font-family', `'${font}', monospace`); - if (size) applyFont('--ui-font-size', `${size}px`); }); - function applyFont(prop: string, value: string) { + function applyCssProp(prop: string, value: string) { document.documentElement.style.setProperty(prop, value); } @@ -90,18 +117,40 @@ } } - async function handleFontFamilyChange(family: string) { - fontFamily = family; - applyFont('--ui-font-family', family ? `'${family}', monospace` : "'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace"); - await saveGlobalSetting('font_family', family); + async function handleUiFontChange(family: string) { + uiFont = family; + uiFontDropdownOpen = false; + const val = family + ? `'${family}', sans-serif` + : "'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace"; + applyCssProp('--ui-font-family', val); + await saveGlobalSetting('ui_font_family', family); } - async function handleFontSizeChange(size: string) { + async function handleUiFontSizeChange(size: string) { const num = parseInt(size, 10); if (isNaN(num) || num < 8 || num > 24) return; - fontSize = size; - applyFont('--ui-font-size', `${num}px`); - await saveGlobalSetting('font_size', size); + uiFontSize = size; + applyCssProp('--ui-font-size', `${num}px`); + await saveGlobalSetting('ui_font_size', size); + } + + async function handleTermFontChange(family: string) { + termFont = family; + termFontDropdownOpen = false; + const val = family + ? `'${family}', monospace` + : "'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace"; + applyCssProp('--term-font-family', val); + await saveGlobalSetting('term_font_family', family); + } + + async function handleTermFontSizeChange(size: string) { + const num = parseInt(size, 10); + if (isNaN(num) || num < 8 || num > 24) return; + termFontSize = size; + applyCssProp('--term-font-size', `${num}px`); + await saveGlobalSetting('term_font_size', size); } async function handleThemeChange(themeId: ThemeId) { @@ -110,16 +159,20 @@ await setTheme(themeId); } - function handleDropdownKeydown(e: KeyboardEvent) { - if (e.key === 'Escape') { + function handleClickOutside(e: MouseEvent) { + const target = e.target as HTMLElement; + if (!target.closest('.custom-dropdown')) { themeDropdownOpen = false; + uiFontDropdownOpen = false; + termFontDropdownOpen = false; } } - function handleClickOutside(e: MouseEvent) { - const target = e.target as HTMLElement; - if (!target.closest('.theme-dropdown')) { + function handleKeydown(e: KeyboardEvent) { + if (e.key === 'Escape') { themeDropdownOpen = false; + uiFontDropdownOpen = false; + termFontDropdownOpen = false; } } @@ -157,16 +210,16 @@ -
+
-

Global

-
+

Appearance

+
Theme -
+
{#if themeDropdownOpen} -
+
- - -
- -
- -
- - handleFontSizeChange((e.target as HTMLInputElement).value)} - /> - px - + UI Font +
+ +
+ + handleUiFontSizeChange((e.target as HTMLInputElement).value)} + /> + px + +
- + Terminal Font +
+ +
+ + handleTermFontSizeChange((e.target as HTMLInputElement).value)} + /> + px + +
+
+
+
+
+ +
+

Defaults

+
+
+ { defaultShell = (e.target as HTMLInputElement).value; saveGlobalSetting('default_shell', defaultShell); }} />
-
- + input { padding: 6px 10px; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); @@ -398,7 +525,27 @@ font-size: 0.8rem; } - .setting-select { + .setting-row { + display: flex; + gap: 8px; + align-items: stretch; + } + + /* Reusable custom dropdown */ + .custom-dropdown { + position: relative; + } + + .dropdown-grow { + flex: 1; + min-width: 0; + } + + .dropdown-trigger { + display: flex; + align-items: center; + gap: 8px; + width: 100%; padding: 6px 10px; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); @@ -406,12 +553,112 @@ color: var(--ctp-text); font-size: 0.8rem; cursor: pointer; + text-align: left; + height: 100%; } + .dropdown-trigger:hover { + border-color: var(--ctp-surface2); + } + + .dropdown-label { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .dropdown-arrow { + color: var(--ctp-overlay0); + font-size: 0.7rem; + flex-shrink: 0; + } + + .dropdown-menu { + position: absolute; + top: calc(100% + 4px); + left: 0; + min-width: 100%; + width: max-content; + max-height: 360px; + overflow-y: auto; + background: var(--ctp-mantle); + border: 1px solid var(--ctp-surface1); + border-radius: 4px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); + z-index: 100; + padding: 4px 0; + } + + .dropdown-group-label { + padding: 6px 10px 2px; + font-size: 0.65rem; + font-weight: 700; + color: var(--ctp-overlay0); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .dropdown-option { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 5px 10px; + background: transparent; + border: none; + color: var(--ctp-subtext1); + font-size: 0.8rem; + cursor: pointer; + text-align: left; + white-space: nowrap; + } + + .dropdown-option:hover { + background: var(--ctp-surface0); + color: var(--ctp-text); + } + + .dropdown-option.active { + background: var(--ctp-surface0); + color: var(--ctp-text); + font-weight: 600; + } + + .dropdown-option-label { + flex: 1; + } + + /* Theme-specific dropdown extras */ + .theme-swatch { + display: inline-block; + width: 14px; + height: 14px; + border-radius: 3px; + border: 1px solid; + flex-shrink: 0; + } + + .theme-colors { + display: flex; + gap: 3px; + flex-shrink: 0; + } + + .color-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + } + + /* Size control (shared by UI and Terminal font) */ .size-control { display: flex; align-items: center; - gap: 4px; + gap: 2px; + flex-shrink: 0; } .size-btn { @@ -438,8 +685,8 @@ } .size-input { - width: 48px; - padding: 4px 6px; + width: 40px; + padding: 4px 2px; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 4px; @@ -455,122 +702,9 @@ } .size-unit { - font-size: 0.75rem; - color: var(--ctp-overlay0); - } - - /* Custom theme dropdown */ - .theme-dropdown { - position: relative; - } - - .theme-trigger { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 4px 8px; - background: var(--ctp-base); - border: 1px solid var(--ctp-surface1); - border-radius: 3px; - color: var(--ctp-text); - font-size: 0.8rem; - cursor: pointer; - text-align: left; - } - - .theme-trigger:hover { - border-color: var(--ctp-surface2); - } - - .theme-trigger-label { - flex: 1; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .theme-arrow { - color: var(--ctp-overlay0); font-size: 0.7rem; - flex-shrink: 0; - } - - .theme-swatch { - display: inline-block; - width: 14px; - height: 14px; - border-radius: 3px; - border: 1px solid; - flex-shrink: 0; - } - - .theme-menu { - position: absolute; - top: calc(100% + 4px); - left: 0; - min-width: 280px; - max-height: 400px; - overflow-y: auto; - background: var(--ctp-mantle); - border: 1px solid var(--ctp-surface1); - border-radius: 4px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); - z-index: 100; - padding: 4px 0; - } - - .theme-group-label { - padding: 6px 10px 2px; - font-size: 0.65rem; - font-weight: 700; color: var(--ctp-overlay0); - text-transform: uppercase; - letter-spacing: 0.05em; - } - - .theme-option { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - padding: 5px 10px; - background: transparent; - border: none; - color: var(--ctp-subtext1); - font-size: 0.8rem; - cursor: pointer; - text-align: left; - } - - .theme-option:hover { - background: var(--ctp-surface0); - color: var(--ctp-text); - } - - .theme-option.active { - background: var(--ctp-surface0); - color: var(--ctp-text); - font-weight: 600; - } - - .theme-option-label { - flex: 1; - white-space: nowrap; - } - - .theme-colors { - display: flex; - gap: 3px; - flex-shrink: 0; - } - - .color-dot { - display: inline-block; - width: 8px; - height: 8px; - border-radius: 50%; + margin-right: 2px; } /* Groups & Projects */ diff --git a/v2/src/lib/stores/theme.svelte.ts b/v2/src/lib/stores/theme.svelte.ts index c6f603c..8c32966 100644 --- a/v2/src/lib/stores/theme.svelte.ts +++ b/v2/src/lib/stores/theme.svelte.ts @@ -81,16 +81,17 @@ export async function initTheme(): Promise { // Apply saved font settings try { - const [fontFamily, fontSize] = await Promise.all([ - getSetting('font_family'), - getSetting('font_size'), + const [uiFont, uiSize, termFont, termSize] = await Promise.all([ + getSetting('ui_font_family'), + getSetting('ui_font_size'), + getSetting('term_font_family'), + getSetting('term_font_size'), ]); - if (fontFamily) { - document.documentElement.style.setProperty('--ui-font-family', `'${fontFamily}', monospace`); - } - if (fontSize) { - document.documentElement.style.setProperty('--ui-font-size', `${fontSize}px`); - } + const root = document.documentElement.style; + if (uiFont) root.setProperty('--ui-font-family', `'${uiFont}', sans-serif`); + if (uiSize) root.setProperty('--ui-font-size', `${uiSize}px`); + if (termFont) root.setProperty('--term-font-family', `'${termFont}', monospace`); + if (termSize) root.setProperty('--term-font-size', `${termSize}px`); } catch { // Font settings are optional — defaults from catppuccin.css apply } diff --git a/v2/src/lib/styles/catppuccin.css b/v2/src/lib/styles/catppuccin.css index 0ae1903..d353a29 100644 --- a/v2/src/lib/styles/catppuccin.css +++ b/v2/src/lib/styles/catppuccin.css @@ -46,6 +46,8 @@ /* Typography */ --ui-font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; --ui-font-size: 13px; + --term-font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; + --term-font-size: 13px; /* Layout */ --sidebar-width: 260px;