From 3477070ec3b862d15c50185121460c253334d683 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Sun, 23 Aug 2026 22:20:25 +0200 Subject: [PATCH 1/2] style(theme): refresh dark theme palette and control contrast Update the dark theme tokens and UI surfaces to improve legibility and clarify interactive boundaries across the app. Controls such as buttons, inputs, and focus outlines were standardized and simplified to reduce visual noise while preserving hierarchy. - Overhaul color tokens and surface backgrounds for consistent tones - Simplify primary button treatment and adjust focus/outline behavior - Add targeted dark-mode rules to separate chrome (tabs, toolbars) --- src/app.css | 235 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 173 insertions(+), 62 deletions(-) diff --git a/src/app.css b/src/app.css index 4ef92c9..c470f9a 100644 --- a/src/app.css +++ b/src/app.css @@ -1,57 +1,57 @@ @import "tailwindcss"; @theme { - --color-ink: #edf3ff; - --color-ink-muted: #b1bad0; - --color-ink-faint: #6f7c96; - --color-ink-dim: #8895ad; - --color-ink-quiet: #98a4bb; + --color-ink: #f0f1f2; + --color-ink-muted: #c1c3c6; + --color-ink-faint: #7f8389; + --color-ink-dim: #9da1a6; + --color-ink-quiet: #aeb1b5; - --color-surface: rgba(18, 23, 34, 0.88); - --color-surface-alt: #090d14; - --color-surface-dim: rgba(15, 20, 30, 0.94); - --color-surface-hover: rgba(36, 45, 64, 0.78); - --color-surface-raised: rgba(23, 29, 43, 0.92); - --color-surface-solid: #171d2b; + --color-surface: #2b2e34; + --color-surface-alt: #1b1c1e; + --color-surface-dim: #2e3137; + --color-surface-hover: #363a41; + --color-surface-raised: #32363d; + --color-surface-solid: #222328; - --color-border: rgba(90, 111, 154, 0.3); - --color-border-subtle: rgba(226, 232, 240, 0.08); - --color-border-input: rgba(77, 182, 214, 0.26); + --color-border: #3a3e45; + --color-border-subtle: #34383e; + --color-border-input: #465159; - --color-primary: #6f8cff; - --color-primary-dark: #5d74e8; - --color-accent: #4db6d6; + --color-primary: #25a7c7; + --color-primary-dark: #188ca9; + --color-accent: #2eb5d1; - --color-bar: rgba(7, 10, 16, 0.94); - --color-bar-text: #f1f5ff; - --color-bar-muted: #7f8ba3; + --color-bar: #292c30; + --color-bar-text: #f0f1f2; + --color-bar-muted: #8b8f94; --font-mono: "Cascadia Code", "SFMono-Regular", Consolas, monospace; } :root { --app-color-scheme: dark; - --app-bg: linear-gradient(135deg, #070a10 0%, #0c111a 48%, #090e16 100%); - --app-button-bg: linear-gradient(180deg, rgba(36, 45, 64, 0.92), rgba(22, 28, 42, 0.94)); - --app-input-bg: rgba(10, 14, 22, 0.82); - --app-select-option-bg: #171d2b; + --app-bg: #222328; + --app-button-bg: #32363d; + --app-input-bg: #22252a; + --app-select-option-bg: #32363d; --app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.038), transparent 70%); --app-panel-shadow: 0 18px 48px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.045); - --app-dialog-backdrop: rgba(4, 7, 12, 0.58); - --app-dialog-bg: #101622; - --app-dialog-chrome: #141b29; + --app-dialog-backdrop: rgba(12, 13, 15, 0.7); + --app-dialog-bg: #22252a; + --app-dialog-chrome: #2b2e34; --app-dialog-shadow: 0 24px 68px rgba(0, 0, 0, 0.54), 0 2px 12px rgba(0,0,0,0.32); - --app-settings-row-bg: #141b29; - --app-scrollbar-thumb: #303a4f; - --app-scrollbar-thumb-hover: #44506a; + --app-settings-row-bg: #2b2e34; + --app-scrollbar-thumb: #4a4e55; + --app-scrollbar-thumb-hover: #60656d; - --code-surface: #111321; - --code-surface-raised: #171a2b; - --code-surface-subtle: #0d101a; - --code-surface-muted: #10131e; - --code-surface-meta: #0c0e18; - --code-input-bg: #0b0e18; - --code-hover-bg: #151a2b; + --code-surface: #222328; + --code-surface-raised: #2b2e34; + --code-surface-subtle: #1f2024; + --code-surface-muted: #26282d; + --code-surface-meta: #1d1e22; + --code-input-bg: #22252a; + --code-hover-bg: #30343a; --code-add-text: #5dd88a; --code-add-strong: #4eca76; --code-add-bg: rgba(78, 202, 118, 0.09); @@ -190,7 +190,7 @@ } input:focus, textarea:focus, select:focus { border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(100, 108, 255, 0.22), 0 0 32px rgba(65, 209, 255, 0.08); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent), 0 0 30px color-mix(in srgb, var(--color-accent) 7%, transparent); } input { height: 34px; padding: 0 11px; } textarea { resize: vertical; min-height: 132px; padding: 10px 11px; line-height: 1.45; } @@ -334,15 +334,15 @@ } .btn-primary { - border-color: rgba(111, 140, 255, 0.72); + border-color: color-mix(in srgb, var(--color-primary) 82%, white); color: #ffffff; - background: linear-gradient(135deg, #5f7df2 0%, #238eb4 100%); - box-shadow: 0 10px 24px rgba(77, 182, 214, 0.16); + background: var(--color-primary); + box-shadow: none; font-weight: 600; } .btn-primary:hover:not(:disabled) { - border-color: rgba(77, 182, 214, 0.78); - background: linear-gradient(135deg, #6f8cff 0%, #2da0c7 100%); + border-color: color-mix(in srgb, var(--color-accent) 78%, white); + background: var(--color-accent); color: #ffffff; } @@ -6227,22 +6227,23 @@ --ui-radius-sm: 5px; --ui-control-height: 32px; --ui-shadow: 0 10px 30px rgba(0, 0, 0, 0.18); - --app-bg: #0d1118; - --app-button-bg: #171d27; - --app-input-bg: #101620; + --app-bg: #222328; + --app-button-bg: #32363d; + --app-input-bg: #22252a; --app-panel-highlight: none; --app-panel-shadow: none; - --color-surface: #111720; - --color-surface-alt: #0d121a; - --color-surface-dim: #141b25; - --color-surface-hover: #1b2431; - --color-surface-raised: #151c27; - --color-surface-solid: #111720; - --color-border: #2a3546; - --color-border-subtle: #222c3a; - --color-primary: #4d8dff; - --color-primary-dark: #3477ed; - --color-accent: #65a2ff; + --color-surface: #2b2e34; + --color-surface-alt: #1b1c1e; + --color-surface-dim: #2e3137; + --color-surface-hover: #363a41; + --color-surface-raised: #32363d; + --color-surface-solid: #222328; + --color-border: #3a3e45; + --color-border-subtle: #34383e; + --color-border-input: #465159; + --color-primary: #25a7c7; + --color-primary-dark: #188ca9; + --color-accent: #2eb5d1; } :root[data-theme="light"] { @@ -6279,8 +6280,8 @@ button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible { - outline: 2px solid color-mix(in srgb, var(--color-primary) 65%, transparent); - outline-offset: 1px; + outline: 2px solid color-mix(in srgb, var(--color-accent) 88%, transparent); + outline-offset: 2px; } input, textarea, select { border-radius: var(--ui-radius-sm); } input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 16%, transparent); } @@ -6289,7 +6290,7 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s .shell-body { gap: 0; padding: 0; } .titlebar { border-bottom: 1px solid var(--color-border-subtle); - background: var(--color-surface-solid); + background: var(--color-bar); } .titlebar-brand { min-width: 140px; padding-left: 14px; border-right: 0; font-size: 14px; } .titlebar-brand-icon, @@ -6332,7 +6333,7 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s border: 0; border-bottom: 1px solid var(--color-border); border-radius: 0; - background: var(--color-surface-solid); + background: var(--color-surface-raised); } .repo-toolbar-sync { grid-template-rows: 10px 36px; } .repo-toolbar-group-label { padding-left: 1px; font-size: 8.5px; letter-spacing: .13em; } @@ -6348,7 +6349,7 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s background: var(--color-border-subtle); row-gap: 0; } -.left-sidebar { background: var(--color-surface-solid); } +.left-sidebar { background: var(--color-surface); } .main-panel { border: 0; border-radius: 0; background: var(--color-surface-solid); } .history-aside { background: var(--color-border-subtle); row-gap: 0; } @@ -6360,6 +6361,7 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s backdrop-filter: none; } .left-sidebar .panel + .panel { border-top: 1px solid var(--color-border); } +.left-sidebar .panel { background: var(--color-surface); } .section-head { min-height: 50px; padding: 8px 12px; @@ -6720,6 +6722,115 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s .repo-section > header { background: var(--color-surface-dim); } .repo-row-main:hover:not(:disabled) { background: var(--color-surface-hover); } +/* Dark control contrast -------------------------------------------------- + Keep GitKraken's restrained graphite palette while making interactive + boundaries and text states unambiguous on every dark surface. */ +:root:not([data-theme="light"]) .btn-primary { + border-color: #2eb5d1; + color: #ffffff; + background: #126f84; +} +:root:not([data-theme="light"]) .btn-primary:hover:not(:disabled) { + border-color: #55c5dc; + color: #ffffff; + background: #147c94; +} +:root:not([data-theme="light"]) .btn-secondary { + border-color: #596169; + color: #d7dadc; + background: #32363d; +} +:root:not([data-theme="light"]) .btn-secondary:hover:not(:disabled) { + border-color: #707982; + color: #ffffff; + background: #41464d; +} +:root:not([data-theme="light"]) input, +:root:not([data-theme="light"]) textarea, +:root:not([data-theme="light"]) select, +:root:not([data-theme="light"]) .select-menu-trigger { + border-color: #596169; +} +:root:not([data-theme="light"]) input:hover:not(:disabled), +:root:not([data-theme="light"]) textarea:hover:not(:disabled), +:root:not([data-theme="light"]) select:hover:not(:disabled), +:root:not([data-theme="light"]) .select-menu-trigger:hover:not(:disabled) { + border-color: #707982; +} +:root:not([data-theme="light"]) input:focus, +:root:not([data-theme="light"]) textarea:focus, +:root:not([data-theme="light"]) select:focus, +:root:not([data-theme="light"]) .select-menu.open .select-menu-trigger { + border-color: var(--color-accent); +} +:root:not([data-theme="light"]) .repo-action-group, +:root:not([data-theme="light"]) .dialog-icon-button { + border-color: #535a62; +} +:root:not([data-theme="light"]) .repo-action:hover:not(:disabled), +:root:not([data-theme="light"]) .repo-action.active, +:root:not([data-theme="light"]) .dialog-icon-button:hover:not(:disabled) { + border-color: #707982; + background: #3b4047; +} + +/* Dark app chrome separation ------------------------------------------- + Tabs, command toolbar and workspace should read as three adjacent + layers instead of one uninterrupted gray strip. */ +:root:not([data-theme="light"]) .repo-tabbar { + border-bottom-color: #505760; + background: #26292f; + box-shadow: + inset 0 -1px 0 #191b1f, + 0 1px 0 rgba(255, 255, 255, 0.035); +} +:root:not([data-theme="light"]) .repo-tabs-scroll { + border-left-color: #454b53; +} +:root:not([data-theme="light"]) .repo-tab-wrap { + border-right-color: #454b53; + background: #292c32; +} +:root:not([data-theme="light"]) .repo-tab-wrap:hover { + background: #30343a; +} +:root:not([data-theme="light"]) .repo-tab-wrap.active, +:root:not([data-theme="light"]) .repo-tab.management.active { + background: #343941; + box-shadow: + inset 0 -3px 0 var(--color-accent), + inset 1px 0 0 #4c535b, + inset -1px 0 0 #4c535b, + inset 0 1px 0 rgba(255, 255, 255, 0.045); +} +:root:not([data-theme="light"]) .repo-tab-add { + border-left-color: #454b53; + background: #292c32; +} +:root:not([data-theme="light"]) .repo-toolbar { + border-top: 1px solid #424850; + border-bottom-color: #505760; + background: #30343a; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.035), + 0 2px 0 #191b1f; +} +:root:not([data-theme="light"]) .repo-action-group { + border-color: #596169; + background: #292d32; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035); +} +:root:not([data-theme="light"]) .repo-action-group > .repo-action, +:root:not([data-theme="light"]) .repo-action-group > .repo-history-wrap > .repo-action { + border-right-color: #484e55; +} +:root:not([data-theme="light"]) .repo-toolbar-divider { + background: #555c64; +} +:root:not([data-theme="light"]) .workspace { + box-shadow: inset 0 1px 0 #17191c; +} + @media (max-width: 1180px) { .repo-action { padding-inline: 10px; } .repo-toolbar-divider { margin-inline: 2px; } From 58027f5e2209c4c98a480b367508ce2621397aef Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Sun, 23 Aug 2026 23:14:07 +0200 Subject: [PATCH 2/2] feat(settings): add appearance and custom theme support Introduce an appearance preference with modern, classic, and custom modes and a persisted custom theme palette. Add helpers to load, persist, and apply appearance and custom colors, and wire them into the app settings lifecycle. Implement a comprehensive light theme and appearance presets using CSS variables so custom palettes are applied consistently across the UI, and include appearance in analytics when settings are saved. - Persist and apply appearance and custom color palette to :root - Add muted light theme plus classic/modern presets and custom mapping - Include appearance and customTheme in settings save and analytics payload --- src/App.svelte | 108 +++- src/app.css | 540 ++++++++++++++++++++ src/lib/components/AppSettingsDialog.svelte | 104 +++- src/lib/types.ts | 8 + 4 files changed, 756 insertions(+), 4 deletions(-) diff --git a/src/App.svelte b/src/App.svelte index 762c0a0..d110032 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -146,10 +146,12 @@ AiReviewResult, AiCommitPlan, AiSettings, + AppAppearance, AppLanguage, AppTheme, AnalyticsSettings, CommitAiPhase, + CustomThemeColors, ConflictFile, DetectedExternalTool, ExplorerNode, @@ -253,6 +255,8 @@ const AI_SETTINGS_KEY = "gitlite.aiSettings.v1"; const ANALYTICS_SETTINGS_KEY = "gitlite.analyticsSettings.v1"; const APP_THEME_KEY = "gitlite.theme.v1"; + const APP_APPEARANCE_KEY = "gitlite.appearance.v1"; + const CUSTOM_THEME_KEY = "gitlite.customTheme.v1"; const APP_LANGUAGE_KEY = "gitlite.language.v1"; const EXTERNAL_TOOLS_SETTINGS_KEY = "gitlite.externalTools.v1"; const AUTO_REFRESH_ENABLED_KEY = "gitlite.autoRefreshEnabled.v1"; @@ -364,6 +368,8 @@ let analyticsNoticeOpen = false; let analyticsSettings: AnalyticsSettings = defaultAnalyticsSettings(); let appTheme: AppTheme = loadThemePreference(); + let appAppearance: AppAppearance = loadAppearancePreference(); + let customTheme: CustomThemeColors = loadCustomTheme(); let appLanguage: AppLanguage = loadLanguagePreference(); let externalToolsSettings: ExternalToolsSettings = loadExternalToolsSettings(); let externalToolsConfigured = hasStoredExternalToolsSettings(); @@ -555,6 +561,7 @@ $: fileManagerToolName = externalToolDisplayName("fileManager", externalToolsSettings.fileManager, detectedExternalTools); $: applyThemePreference(appTheme); + $: applyAppearancePreference(appAppearance, customTheme); $: applyLanguagePreference(appLanguage); // ── Lifecycle ────────────────────────────────────────────────────────────── @@ -1119,6 +1126,98 @@ } } + function defaultCustomTheme(): CustomThemeColors { + return { + background: "#cfd5dc", + surface: "#e2e6ea", + accent: "#2eb5d1", + text: "#222328", + }; + } + + function isHexColor(value: unknown): value is string { + return typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value); + } + + function loadAppearancePreference(): AppAppearance { + try { + const stored = localStorage.getItem(APP_APPEARANCE_KEY); + if (stored === "modern" || stored === "classic" || stored === "custom") return stored; + } catch { + // Local storage is optional; the current design remains the default. + } + return "modern"; + } + + function loadCustomTheme(): CustomThemeColors { + const fallback = defaultCustomTheme(); + try { + const stored = JSON.parse(localStorage.getItem(CUSTOM_THEME_KEY) ?? "null") as Partial | null; + if (!stored) return fallback; + return { + background: isHexColor(stored.background) ? stored.background : fallback.background, + surface: isHexColor(stored.surface) ? stored.surface : fallback.surface, + accent: isHexColor(stored.accent) ? stored.accent : fallback.accent, + text: isHexColor(stored.text) ? stored.text : fallback.text, + }; + } catch { + return fallback; + } + } + + function persistAppearancePreference(next: AppAppearance, colors: CustomThemeColors) { + try { + localStorage.setItem(APP_APPEARANCE_KEY, next); + localStorage.setItem(CUSTOM_THEME_KEY, JSON.stringify(colors)); + } catch { + // Ignore storage quota/private-mode errors. + } + } + + function applyAppearancePreference(next: AppAppearance, colors: CustomThemeColors) { + const root = document.documentElement; + root.dataset.appearance = next; + const customProperties = [ + "--app-bg", "--app-button-bg", "--app-input-bg", "--app-dialog-bg", "--app-dialog-chrome", + "--app-settings-row-bg", "--color-surface", "--color-surface-alt", "--color-surface-dim", + "--color-surface-hover", "--color-surface-raised", "--color-surface-solid", "--color-border", + "--color-border-subtle", "--color-border-input", "--color-primary", "--color-primary-dark", + "--color-accent", "--color-ink", "--color-ink-muted", "--color-ink-faint", "--color-ink-dim", + "--color-bar-text", "--color-bar-muted", + ]; + customProperties.forEach((property) => root.style.removeProperty(property)); + if (next !== "custom") return; + + const { background, surface, accent, text } = colors; + const values: Record = { + "--app-bg": background, + "--app-button-bg": surface, + "--app-input-bg": `color-mix(in srgb, ${surface} 88%, white)`, + "--app-dialog-bg": surface, + "--app-dialog-chrome": `color-mix(in srgb, ${surface} 90%, ${background})`, + "--app-settings-row-bg": `color-mix(in srgb, ${surface} 92%, ${background})`, + "--color-surface": surface, + "--color-surface-alt": `color-mix(in srgb, ${surface} 82%, ${background})`, + "--color-surface-dim": `color-mix(in srgb, ${surface} 88%, ${background})`, + "--color-surface-hover": `color-mix(in srgb, ${surface} 88%, ${text})`, + "--color-surface-raised": `color-mix(in srgb, ${surface} 92%, white)`, + "--color-surface-solid": surface, + "--color-border": `color-mix(in srgb, ${text} 32%, ${surface})`, + "--color-border-subtle": `color-mix(in srgb, ${text} 18%, ${surface})`, + "--color-border-input": `color-mix(in srgb, ${text} 38%, ${surface})`, + "--color-primary": accent, + "--color-primary-dark": `color-mix(in srgb, ${accent} 82%, black)`, + "--color-accent": accent, + "--color-ink": text, + "--color-ink-muted": `color-mix(in srgb, ${text} 76%, ${surface})`, + "--color-ink-faint": `color-mix(in srgb, ${text} 58%, ${surface})`, + "--color-ink-dim": `color-mix(in srgb, ${text} 68%, ${surface})`, + "--color-bar-text": text, + "--color-bar-muted": `color-mix(in srgb, ${text} 62%, ${surface})`, + }; + Object.entries(values).forEach(([property, value]) => root.style.setProperty(property, value)); + } + function loadLanguagePreference(): AppLanguage { try { const stored = localStorage.getItem(APP_LANGUAGE_KEY); @@ -1157,15 +1256,18 @@ if (appTheme === "system") applyThemePreference(appTheme); } - function saveAppSettings(next: AnalyticsSettings, nextTheme: AppTheme, nextLanguage: AppLanguage, nextAutoRefresh: boolean, nextExternalTools: ExternalToolsSettings) { + function saveAppSettings(next: AnalyticsSettings, nextTheme: AppTheme, nextAppearance: AppAppearance, nextCustomTheme: CustomThemeColors, nextLanguage: AppLanguage, nextAutoRefresh: boolean, nextExternalTools: ExternalToolsSettings) { const autoRefreshWasEnabled = autoRefreshEnabled; analyticsSettings = next; appTheme = nextTheme; + appAppearance = nextAppearance; + customTheme = nextCustomTheme; appLanguage = nextLanguage; autoRefreshEnabled = nextAutoRefresh; externalToolsSettings = nextExternalTools; persistAnalyticsSettings(next); persistThemePreference(nextTheme); + persistAppearancePreference(nextAppearance, nextCustomTheme); persistLanguagePreference(nextLanguage); persistStoredBoolean(AUTO_REFRESH_ENABLED_KEY, nextAutoRefresh); persistExternalToolsSettings(nextExternalTools); @@ -1173,7 +1275,7 @@ setTelemetryEnabled(next.enabled); appSettingsOpen = false; if (nextAutoRefresh && !autoRefreshWasEnabled) void autoRefreshTick(); - if (next.enabled) trackEvent("settings_saved", { analytics_enabled: 1, theme: nextTheme, language: nextLanguage, auto_refresh: nextAutoRefresh ? 1 : 0 }); + if (next.enabled) trackEvent("settings_saved", { analytics_enabled: 1, theme: nextTheme, appearance: nextAppearance, language: nextLanguage, auto_refresh: nextAutoRefresh ? 1 : 0 }); } function updateCommitMessage(message: string) { @@ -5599,6 +5701,8 @@ .repo-action, +:root[data-theme="light"] .repo-action-group > .repo-history-wrap > .repo-action { + border-right-color: #c8d0da; +} + +:root[data-theme="light"] .repo-toolbar-divider { + background: #b5bec9; +} + +:root[data-theme="light"] .workspace { + box-shadow: inset 0 1px 0 #c9d1db; +} + @media (max-width: 1180px) { .repo-action { padding-inline: 10px; } .repo-toolbar-divider { margin-inline: 2px; } @@ -7983,3 +8052,474 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s .status-lane-head { align-items: flex-start; flex-direction: column; } .status-lane-actions { width: 100%; justify-content: flex-start; } } + +/* Muted light theme ----------------------------------------------------- + A lower-luminance light palette for long sessions. The hierarchy stays + light and readable without using pure white for every working surface. */ +:root[data-theme="light"] { + --ui-shadow: 0 10px 26px rgba(29, 39, 52, 0.12); + --app-bg: #cfd5dc; + --app-button-bg: #e2e7eb; + --app-input-bg: #e9edf0; + --app-select-option-bg: #e7ebef; + --app-dialog-backdrop: rgba(91, 103, 117, 0.38); + --app-dialog-bg: #e2e6ea; + --app-dialog-chrome: #d7dde3; + --app-settings-row-bg: #dce2e7; + --app-scrollbar-thumb: #aab4be; + --app-scrollbar-thumb-hover: #929eaa; + + --color-ink: #202832; + --color-ink-muted: #4d5a68; + --color-ink-faint: #687583; + --color-ink-dim: #596674; + --color-ink-quiet: #62707e; + --color-surface: #e2e6ea; + --color-surface-alt: #d8dde3; + --color-surface-dim: #dce1e6; + --color-surface-hover: #cfd6de; + --color-surface-raised: #e7ebee; + --color-surface-solid: #e1e5e9; + --color-border: #9faab5; + --color-border-subtle: #bbc3cc; + --color-border-input: #98a5b2; + --color-bar: #d6dce2; + --color-bar-text: #202832; + --color-bar-muted: #596674; + + --code-surface: #e1e5e9; + --code-surface-raised: #d9dfe5; + --code-surface-subtle: #dce2e7; + --code-surface-muted: #d4dae0; + --code-surface-meta: #ced5dc; + --code-input-bg: #e8ecef; + --code-hover-bg: #cfd7df; +} + +:root[data-theme="light"] .titlebar { + background: + linear-gradient(90deg, rgba(49, 95, 214, 0.035), rgba(15, 143, 181, 0.025)), + var(--color-bar); +} + +:root[data-theme="light"] .repo-tabbar { + border-bottom-color: #929da8; + background: #cbd2d9; + box-shadow: inset 0 -1px 0 #aeb7c0; +} + +:root[data-theme="light"] .repo-tab-wrap, +:root[data-theme="light"] .repo-tab-add { + border-color: #aeb8c2; + background: #d3d9df; +} + +:root[data-theme="light"] .repo-tab-wrap:hover, +:root[data-theme="light"] .repo-tab.management:hover:not(:disabled) { + background: #c8d0d8; +} + +:root[data-theme="light"] .repo-tab-wrap.active, +:root[data-theme="light"] .repo-tab.management.active { + background: #e3e7eb; + box-shadow: + inset 0 -3px 0 var(--color-primary), + inset 1px 0 0 #9faab5, + inset -1px 0 0 #9faab5; +} + +:root[data-theme="light"] .repo-toolbar { + border-top-color: #aeb7c0; + border-bottom-color: #929da8; + background: #d5dbe1; + box-shadow: 0 2px 0 #b9c1c9; +} + +:root[data-theme="light"] .repo-action-group { + border-color: #98a4af; + background: #e0e5e9; + box-shadow: 0 1px 2px rgba(29, 39, 52, 0.1); +} + +:root[data-theme="light"] .repo-action-group > .repo-action, +:root[data-theme="light"] .repo-action-group > .repo-history-wrap > .repo-action { + border-right-color: #aeb8c2; +} + +:root[data-theme="light"] .workspace { + box-shadow: inset 0 1px 0 #aab4be; +} + +:root[data-theme="light"] .repo-management, +:root[data-theme="light"] .main-panel { + background: var(--color-surface); +} + +:root[data-theme="light"] .repo-section { + background: #dbe0e5; +} + +:root[data-theme="light"] .repo-section > header, +:root[data-theme="light"] .repo-summary, +:root[data-theme="light"] .lane-header, +:root[data-theme="light"] .stash-toolbar, +:root[data-theme="light"] .dialog-footer, +:root[data-theme="light"] .cred-header { + background: #d2d8de; +} + +:root[data-theme="light"] .repo-empty, +:root[data-theme="light"] .graph-list, +:root[data-theme="light"] .commit-body, +:root[data-theme="light"] .commit-files, +:root[data-theme="light"] .commit-file-button, +:root[data-theme="light"] .commit-ref-detail-item, +:root[data-theme="light"] .cred-card, +:root[data-theme="light"] .cred-body { + background: #e0e4e8; +} + +:root[data-theme="light"] .graph-gutter { + background: #d2d8de; +} + +:root[data-theme="light"] .graph-row:hover .commit-body { + background: #d3dae1; +} + +:root[data-theme="light"] .branch-filter-dialog, +:root[data-theme="light"] .status-panel-overlay-card { + background: #e2e6ea; +} + +:root[data-theme="light"] .cred-input input, +:root[data-theme="light"] .cred-expiry input[type="date"] { + background: var(--app-input-bg); +} + +/* Shared dark-mode accent language in the muted light theme. */ +:root[data-theme="light"] { + --color-primary: #25a7c7; + --color-primary-dark: #188ca9; + --color-accent: #2eb5d1; + --color-ink: #222328; + --color-ink-muted: #3f4852; + --color-ink-faint: #66707a; + --color-ink-dim: #515b65; + --color-ink-quiet: #5c6670; + --color-bar-text: #222328; + --color-bar-muted: #59636d; +} + +:root[data-theme="light"] .btn-primary { + border-color: #2eb5d1; + color: #f0f1f2; + background: #126f84; + box-shadow: none; +} + +:root[data-theme="light"] .btn-primary:hover:not(:disabled) { + border-color: #55c5dc; + color: #ffffff; + background: #147c94; +} + +:root[data-theme="light"] .btn-secondary, +:root[data-theme="light"] .dialog-icon-button { + border-color: #909ca8; + color: #2f3943; + background: #d2d8de; +} + +:root[data-theme="light"] .btn-secondary:hover:not(:disabled), +:root[data-theme="light"] .dialog-icon-button:hover:not(:disabled) { + border-color: #778591; + color: #222328; + background: #c3cbd3; +} + +:root[data-theme="light"] .repo-action-group { + border-color: #909ca8; + background: #d2d8de; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.32); +} + +:root[data-theme="light"] .repo-action { + color: #35404a; +} + +:root[data-theme="light"] .repo-action-group > .repo-action, +:root[data-theme="light"] .repo-action-group > .repo-history-wrap > .repo-action { + border-right-color: #aab4be; +} + +:root[data-theme="light"] .repo-action:hover:not(:disabled), +:root[data-theme="light"] .repo-action.active { + color: #222328; + background: #c3cbd3; +} + +:root[data-theme="light"] .repo-action.sync-primary, +:root[data-theme="light"] .repo-action.sync-primary svg { + color: #2eb5d1; +} + +/* Repository tabs stay light while sharing the cyan dark-theme accent. */ +:root[data-theme="light"] .repo-tabbar { + border-bottom-color: #929da8; + background: #cbd2d9; + box-shadow: inset 0 -1px 0 #aeb7c0; +} + +:root[data-theme="light"] .repo-tabs-scroll { + border-left-color: #aeb8c2; +} + +:root[data-theme="light"] .repo-tab-wrap, +:root[data-theme="light"] .repo-tab.management, +:root[data-theme="light"] .repo-tab-add { + border-color: #aeb8c2; + color: #3f4852; + background: #d3d9df; +} + +:root[data-theme="light"] .repo-tab-wrap:hover, +:root[data-theme="light"] .repo-tab.management:hover:not(:disabled), +:root[data-theme="light"] .repo-tab-add:hover:not(:disabled) { + color: #222328; + background: #c5ced6; +} + +:root[data-theme="light"] .repo-tab-wrap.active, +:root[data-theme="light"] .repo-tab.management.active { + color: #222328; + background: #e3e7eb; + box-shadow: + inset 0 -3px 0 var(--color-accent), + inset 1px 0 0 #9faab5, + inset -1px 0 0 #9faab5; +} + +:root[data-theme="light"] .repo-tab > svg, +:root[data-theme="light"] .repo-tab-close, +:root[data-theme="light"] .repo-tab-add { + color: #66707a; +} + +:root[data-theme="light"] .repo-tab-wrap.active .repo-tab, +:root[data-theme="light"] .repo-tab-wrap.active .repo-tab > svg, +:root[data-theme="light"] .repo-tab.management.active > svg, +:root[data-theme="light"] .repo-tab-add:hover:not(:disabled) { + color: #2eb5d1; +} + +:root[data-theme="light"] .repo-tab-wrap.active .repo-tab span, +:root[data-theme="light"] .repo-tab.management.active span { + color: #222328; +} + +:root[data-theme="light"] .repo-tab-close:hover:not(:disabled), +:root[data-theme="light"] .repo-tab-close:focus-visible:not(:disabled) { + color: #222328; + background: #c3cbd3; +} + +/* Appearance presets --------------------------------------------------- */ +:root[data-theme="light"][data-appearance="classic"] { + --ui-shadow: 0 10px 28px rgba(23, 40, 72, 0.09); + --app-bg: #f4f6f9; + --app-button-bg: #ffffff; + --app-input-bg: #ffffff; + --app-dialog-bg: #ffffff; + --app-dialog-chrome: #f4f7fc; + --app-settings-row-bg: #f8fafd; + --color-ink: #172033; + --color-ink-muted: #475569; + --color-ink-faint: #728098; + --color-ink-dim: #5f6f89; + --color-surface: #ffffff; + --color-surface-alt: #f7f8fa; + --color-surface-dim: #f8f9fb; + --color-surface-hover: #f0f4fa; + --color-surface-raised: #ffffff; + --color-surface-solid: #ffffff; + --color-border: #cfd6e1; + --color-border-subtle: #e3e7ed; + --color-border-input: #c9d1dd; + --color-primary: #0b63e6; + --color-primary-dark: #0755c8; + --color-accent: #0b63e6; +} + +:root[data-theme="light"][data-appearance="classic"] .titlebar { + background: + linear-gradient(90deg, rgba(49, 95, 214, 0.08), rgba(15, 143, 181, 0.05), rgba(214, 156, 49, 0.04)), + var(--color-bar); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-tabbar, +:root[data-theme="light"][data-appearance="classic"] .repo-management-tools { + border-color: var(--color-border); + background: rgba(247, 249, 252, 0.96); + box-shadow: none; +} + +:root[data-theme="light"][data-appearance="classic"] .repo-tab-wrap, +:root[data-theme="light"][data-appearance="classic"] .repo-tab.management, +:root[data-theme="light"][data-appearance="classic"] .repo-tab-add { + border-color: var(--color-border-subtle); + color: var(--color-ink-muted); + background: rgba(255, 255, 255, 0.5); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-tab-wrap:hover, +:root[data-theme="light"][data-appearance="classic"] .repo-tab.management:hover:not(:disabled), +:root[data-theme="light"][data-appearance="classic"] .repo-tab-add:hover:not(:disabled) { + color: var(--color-ink); + background: rgba(255, 255, 255, 0.9); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-tab-wrap.active, +:root[data-theme="light"][data-appearance="classic"] .repo-tab.management.active { + color: var(--color-ink); + background: #ffffff; + box-shadow: inset 0 -3px 0 var(--color-primary); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-tab-wrap.active .repo-tab span, +:root[data-theme="light"][data-appearance="classic"] .repo-tab.management.active span { + color: var(--color-ink); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-toolbar { + border-color: var(--color-border); + background: rgba(255, 255, 255, 0.78); + box-shadow: none; +} + +:root[data-theme="light"][data-appearance="classic"] .repo-action-group { + border-color: var(--color-border); + background: var(--color-surface-raised); + box-shadow: none; +} + +:root[data-theme="light"][data-appearance="classic"] .repo-action { + color: var(--color-ink-muted); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-action:hover:not(:disabled), +:root[data-theme="light"][data-appearance="classic"] .repo-action.active { + color: var(--color-ink); + background: var(--color-surface-hover); +} + +:root[data-theme="light"][data-appearance="classic"] .btn-primary { + border-color: rgba(49, 95, 214, 0.72); + color: #ffffff; + background: linear-gradient(135deg, #315fd6 0%, #0f8fb5 100%); + box-shadow: 0 10px 24px rgba(49, 95, 214, 0.18); +} + +:root[data-theme="light"][data-appearance="classic"] .btn-secondary, +:root[data-theme="light"][data-appearance="classic"] .dialog-icon-button { + border-color: rgba(15, 143, 181, 0.28); + color: #0c6683; + background: linear-gradient(180deg, rgba(15, 143, 181, 0.08), rgba(49, 95, 214, 0.06)); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-management, +:root[data-theme="light"][data-appearance="classic"] .main-panel, +:root[data-theme="light"][data-appearance="classic"] .cred-card, +:root[data-theme="light"][data-appearance="classic"] .cred-body { + background: var(--color-surface); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-section { + background: rgba(255, 255, 255, 0.62); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-section > header, +:root[data-theme="light"][data-appearance="classic"] .repo-summary, +:root[data-theme="light"][data-appearance="classic"] .lane-header, +:root[data-theme="light"][data-appearance="classic"] .stash-toolbar, +:root[data-theme="light"][data-appearance="classic"] .dialog-footer, +:root[data-theme="light"][data-appearance="classic"] .cred-header { + background: var(--color-surface-dim); +} + +:root[data-theme="light"][data-appearance="classic"] .repo-empty { + background: rgba(255, 255, 255, 0.48); +} + +:root[data-theme="light"][data-appearance="classic"] .graph-list { + background: rgba(248, 250, 253, 0.9); +} + +:root[data-theme="light"][data-appearance="classic"] .commit-body, +:root[data-theme="light"][data-appearance="classic"] .commit-files, +:root[data-theme="light"][data-appearance="classic"] .commit-file-button { + background: rgba(255, 255, 255, 0.72); +} + +:root[data-appearance="custom"] .titlebar, +:root[data-appearance="custom"] .repo-tabbar, +:root[data-appearance="custom"] .repo-toolbar { + border-color: var(--color-border); + background: var(--color-surface-alt); + box-shadow: none; +} + +:root[data-appearance="custom"] .repo-tab-wrap, +:root[data-appearance="custom"] .repo-tab.management, +:root[data-appearance="custom"] .repo-tab-add, +:root[data-appearance="custom"] .repo-action-group, +:root[data-appearance="custom"] .btn-secondary, +:root[data-appearance="custom"] .dialog-icon-button { + border-color: var(--color-border); + color: var(--color-ink-muted); + background: var(--color-surface-raised); +} + +:root[data-appearance="custom"] .repo-tab-wrap.active, +:root[data-appearance="custom"] .repo-tab.management.active { + color: var(--color-ink); + background: var(--color-surface-raised); + box-shadow: inset 0 -3px 0 var(--color-accent); +} + +:root[data-appearance="custom"] .repo-tab-wrap.active .repo-tab span, +:root[data-appearance="custom"] .repo-tab.management.active span { + color: var(--color-ink); +} + +:root[data-appearance="custom"] .btn-primary { + border-color: var(--color-accent); + color: var(--color-surface-raised); + background: var(--color-primary-dark); + box-shadow: none; +} + +:root[data-appearance="custom"] .repo-management, +:root[data-appearance="custom"] .main-panel, +:root[data-appearance="custom"] .repo-section, +:root[data-appearance="custom"] .repo-empty, +:root[data-appearance="custom"] .graph-list, +:root[data-appearance="custom"] .commit-body, +:root[data-appearance="custom"] .commit-files, +:root[data-appearance="custom"] .commit-file-button, +:root[data-appearance="custom"] .cred-card, +:root[data-appearance="custom"] .cred-body, +:root[data-appearance="custom"] .branch-filter-dialog, +:root[data-appearance="custom"] .status-panel-overlay-card { + background: var(--color-surface); +} + +:root[data-appearance="custom"] .repo-section > header, +:root[data-appearance="custom"] .repo-summary, +:root[data-appearance="custom"] .lane-header, +:root[data-appearance="custom"] .stash-toolbar, +:root[data-appearance="custom"] .dialog-footer, +:root[data-appearance="custom"] .cred-header, +:root[data-appearance="custom"] .graph-gutter { + background: var(--color-surface-dim); +} diff --git a/src/lib/components/AppSettingsDialog.svelte b/src/lib/components/AppSettingsDialog.svelte index a448fc2..2a93ef8 100644 --- a/src/lib/components/AppSettingsDialog.svelte +++ b/src/lib/components/AppSettingsDialog.svelte @@ -31,8 +31,10 @@ } from "../externalTools"; import type { AnalyticsSettings, + AppAppearance, AppLanguage, AppTheme, + CustomThemeColors, DetectedExternalTool, ExternalToolsSettings, ToolOpenMode, @@ -44,6 +46,8 @@ interface Props { analytics: AnalyticsSettings; theme: AppTheme; + appearance: AppAppearance; + customTheme: CustomThemeColors; language: AppLanguage; autoRefresh: boolean; externalTools: ExternalToolsSettings; @@ -51,13 +55,15 @@ detectionPending: boolean; detectionUnavailable: boolean; onRefreshDetectedTools: () => void | Promise; - onSave: (settings: AnalyticsSettings, theme: AppTheme, language: AppLanguage, autoRefresh: boolean, externalTools: ExternalToolsSettings) => void; + onSave: (settings: AnalyticsSettings, theme: AppTheme, appearance: AppAppearance, customTheme: CustomThemeColors, language: AppLanguage, autoRefresh: boolean, externalTools: ExternalToolsSettings) => void; onClose: () => void; } let { analytics, theme = "system", + appearance = "modern", + customTheme = { background: "#cfd5dc", surface: "#e2e6ea", accent: "#2eb5d1", text: "#222328" }, language = "en", autoRefresh = true, externalTools, @@ -76,6 +82,8 @@ let advancedOpen = $state(false); let analyticsEnabled = $state(true); let selectedTheme = $state("system"); + let selectedAppearance = $state("modern"); + let customColors = $state({ background: "#cfd5dc", surface: "#e2e6ea", accent: "#2eb5d1", text: "#222328" }); let selectedLanguage = $state("en"); let autoRefreshEnabled = $state(true); let tools = $state(defaultExternalToolsSettings()); @@ -84,6 +92,8 @@ $effect(() => { analyticsEnabled = analytics.enabled; selectedTheme = theme; + selectedAppearance = appearance; + customColors = structuredClone(customTheme); selectedLanguage = language; autoRefreshEnabled = autoRefresh; tools = structuredClone(externalTools); @@ -94,7 +104,46 @@ ...analytics, enabled: analyticsEnabled, noticeSeen: true, - }, selectedTheme, selectedLanguage, autoRefreshEnabled, $state.snapshot(tools)); + }, selectedTheme, selectedAppearance, $state.snapshot(customColors), selectedLanguage, autoRefreshEnabled, $state.snapshot(tools)); + } + + function resetCustomColors() { + customColors = selectedTheme === "dark" + ? { background: "#222328", surface: "#2b2e34", accent: "#2eb5d1", text: "#f0f1f2" } + : { background: "#cfd5dc", surface: "#e2e6ea", accent: "#2eb5d1", text: "#222328" }; + } + + function cssColorToHex(value: string, fallback: string): string { + const color = value.trim(); + if (/^#[0-9a-f]{6}$/i.test(color)) return color.toLowerCase(); + if (!color || typeof document === "undefined") return fallback; + const probe = document.createElement("span"); + probe.style.color = color; + if (!probe.style.color) return fallback; + probe.style.display = "none"; + document.body.appendChild(probe); + const match = getComputedStyle(probe).color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i); + probe.remove(); + if (!match) return fallback; + return `#${match.slice(1, 4).map((part) => Number(part).toString(16).padStart(2, "0")).join("")}`; + } + + function currentThemeColors(): CustomThemeColors { + const styles = getComputedStyle(document.documentElement); + const fallback = selectedTheme === "dark" + ? { background: "#222328", surface: "#2b2e34", accent: "#2eb5d1", text: "#f0f1f2" } + : { background: "#cfd5dc", surface: "#e2e6ea", accent: "#2eb5d1", text: "#222328" }; + return { + background: cssColorToHex(styles.getPropertyValue("--app-bg"), fallback.background), + surface: cssColorToHex(styles.getPropertyValue("--color-surface"), fallback.surface), + accent: cssColorToHex(styles.getPropertyValue("--color-accent"), fallback.accent), + text: cssColorToHex(styles.getPropertyValue("--color-ink"), fallback.text), + }; + } + + function selectAppearance(next: AppAppearance) { + if (next === "custom" && selectedAppearance !== "custom") customColors = currentThemeColors(); + selectedAppearance = next; } function toolLabel(kind: ExternalToolKind): string { @@ -292,6 +341,40 @@ +
+

{isGerman ? "Darstellungsstil" : "Design style"}

{isGerman ? "Aktuell, klassisch oder selbst gestaltet." : "Current, classic, or designed by you."}

+
+ + + +
+
+ + {#if selectedAppearance === "custom"} +
+
+ +

{isGerman ? "Theme-Generator" : "Theme generator"}

{isGerman ? "Erstelle dein eigenes Farbprofil." : "Create your own color profile."}

+ +
+
+ + +
+
+ + + + +
+

{isGerman ? "Die Farben werden beim Speichern auf die gesamte Oberfläche angewendet." : "The colors are applied across the interface when you save."}

+
+ {/if} +

{isGerman ? "Sprache" : "Language"}

{isGerman ? "Sprache der Oberfläche." : "Language used by the interface."}

@@ -534,6 +617,22 @@ .settings-segmented label { display: flex; align-items: center; justify-content: center; min-height: 31px; border: 1px solid transparent; border-radius: 6px; color: var(--color-ink-dim); font-size: 10.5px; font-weight: 750; } .settings-segmented label.active { border-color: var(--color-border-input); color: var(--color-ink); background: var(--color-surface-hover); } .settings-segmented input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; } + .custom-theme-panel > header { align-items: center; } + .custom-theme-panel > header > div { min-width: 0; } + .theme-reset-button { display: inline-flex; align-items: center; gap: 5px; min-height: 27px; margin-left: auto; padding: 0 8px; border: 1px solid var(--color-border); color: var(--color-ink-muted); background: var(--color-surface-raised); font-size: 9.5px; font-weight: 750; } + .theme-reset-button:hover:not(:disabled) { border-color: var(--color-border-input); color: var(--color-ink); background: var(--color-surface-hover); } + .theme-preview { display: grid; grid-template-columns: 28% 1fr; min-height: 78px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--preview-text) 30%, var(--preview-surface)); background: var(--preview-bg); } + .theme-preview-sidebar { border-right: 1px solid color-mix(in srgb, var(--preview-text) 24%, var(--preview-surface)); background: color-mix(in srgb, var(--preview-surface) 86%, var(--preview-bg)); } + .theme-preview-content { display: grid; grid-template-columns: 1fr auto; align-content: start; gap: 8px; margin: 10px; padding: 10px; border: 1px solid color-mix(in srgb, var(--preview-text) 22%, var(--preview-surface)); color: var(--preview-text); background: var(--preview-surface); } + .theme-preview-content i { display: block; width: 54%; height: 7px; background: var(--preview-text); opacity: .82; } + .theme-preview-content b { display: block; width: 34px; height: 18px; grid-row: 1 / 3; grid-column: 2; background: var(--preview-accent); } + .theme-preview-content em { display: block; width: 76%; height: 5px; background: var(--preview-text); opacity: .32; } + .theme-color-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; } + .theme-color-grid label { display: grid; grid-template-columns: minmax(0, 1fr) 32px auto; align-items: center; gap: 8px; min-width: 0; min-height: 38px; padding: 5px 7px; border: 1px solid var(--color-border-subtle); background: var(--color-surface-raised); } + .theme-color-grid label > span { color: var(--color-ink-muted); font-size: 10px; font-weight: 750; } + .theme-color-grid input[type="color"] { width: 32px; height: 25px; padding: 2px; border: 1px solid var(--color-border-input); background: transparent; cursor: pointer; } + .theme-color-grid code { color: var(--color-ink-faint); font: 9px var(--font-mono); text-transform: uppercase; } + .theme-generator-note { margin: -5px 0 0 !important; color: var(--color-ink-faint) !important; } .settings-switch-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 14px; } .settings-switch-row span { display: grid; gap: 3px; } .settings-switch-row strong { color: var(--color-ink); font-size: 11px; } @@ -569,6 +668,7 @@ .tool-rescan-button { align-self: flex-start; } .general-settings-grid { grid-template-columns: 1fr; } .general-setting-panel.general-setting-wide { grid-column: auto; } + .theme-color-grid { grid-template-columns: 1fr; } .tool-config-panel { padding: 13px; } .tool-usage-callout { grid-template-columns: 1fr; gap: 4px; } } diff --git a/src/lib/types.ts b/src/lib/types.ts index 6b13ea8..d1bb00a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -13,8 +13,16 @@ export type CommitAiPhase = "idle" | "loading" | "ready" | "error"; export type CommitAiProvider = "local" | "openai" | "anthropic" | "custom"; export type CommitAiLocalProfile = "fast" | "balanced" | "detailed"; export type AppTheme = "system" | "light" | "dark"; +export type AppAppearance = "modern" | "classic" | "custom"; export type AppLanguage = "en" | "de"; +export interface CustomThemeColors { + background: string; + surface: string; + accent: string; + text: string; +} + export interface CommitAiStatus { phase: CommitAiPhase; model_id: string | null;