diff --git a/src/App.svelte b/src/App.svelte index 6dd6fdb..e871891 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -98,6 +98,7 @@ import type { AiSettings, + AppTheme, AnalyticsSettings, CommitAiPhase, ConflictFile, @@ -165,6 +166,7 @@ const REPO_STATUS_CACHE_KEY = "gitlite.repoStatusCache.v1"; const AI_SETTINGS_KEY = "gitlite.aiSettings.v1"; const ANALYTICS_SETTINGS_KEY = "gitlite.analyticsSettings.v1"; + const APP_THEME_KEY = "gitlite.theme.v1"; const COMMIT_PANEL_HEIGHT_KEY = "gitlite.commitPanelHeight.v1"; const LEFT_SIDEBAR_WIDTH_KEY = "gitlite.leftSidebarWidth.v1"; const LEFT_BRANCH_PANEL_HEIGHT_KEY = "gitlite.leftBranchPanelHeight.v1"; @@ -239,6 +241,7 @@ let appSettingsOpen = false; let analyticsNoticeOpen = false; let analyticsSettings: AnalyticsSettings = defaultAnalyticsSettings(); + let appTheme: AppTheme = loadThemePreference(); let localModelOptions: LocalModelOption[] = []; let errorMessage = ""; let operation = ""; @@ -337,6 +340,7 @@ let fileHistoryResizeStartX = 0; let fileHistoryResizeStartWidth = 0; let fileHistoryCollapsed = true; + let themeMediaQuery: MediaQueryList | undefined; // ── Derived ──────────────────────────────────────────────────────────────── @@ -389,13 +393,18 @@ $: leftSidebarRows = buildLeftSidebarRows(branchPanelCollapsed, stashPanelCollapsed, explorerPanelCollapsed); $: allLeftPanelsCollapsed = branchPanelCollapsed && stashPanelCollapsed && explorerPanelCollapsed; + $: applyThemePreference(appTheme); + // ── Lifecycle ────────────────────────────────────────────────────────────── onMount(() => { + themeMediaQuery = window.matchMedia("(prefers-color-scheme: light)"); + themeMediaQuery.addEventListener("change", handleSystemThemeChange); void runStartupSequence(); }); onDestroy(() => { + themeMediaQuery?.removeEventListener("change", handleSystemThemeChange); if (autoRefreshTimer) clearInterval(autoRefreshTimer); if (backgroundRepoStatusTimer) clearInterval(backgroundRepoStatusTimer); if (backgroundFetchTimer) clearInterval(backgroundFetchTimer); @@ -754,11 +763,46 @@ trackEvent("app_started", { first_run: 1 }); } - function saveAppSettings(next: AnalyticsSettings) { + function loadThemePreference(): AppTheme { + try { + const stored = localStorage.getItem(APP_THEME_KEY); + if (stored === "system" || stored === "light" || stored === "dark") return stored; + } catch { + // Local storage is optional; system theme is a sane default. + } + return "system"; + } + + function persistThemePreference(next: AppTheme) { + try { + localStorage.setItem(APP_THEME_KEY, next); + } catch { + // Ignore storage quota/private-mode errors. + } + } + + function applyThemePreference(next: AppTheme) { + const prefersLight = themeMediaQuery?.matches ?? window.matchMedia("(prefers-color-scheme: light)").matches; + const resolved = next === "system" + ? prefersLight + ? "light" + : "dark" + : next; + document.documentElement.dataset.themePreference = next; + document.documentElement.dataset.theme = resolved; + } + + function handleSystemThemeChange() { + if (appTheme === "system") applyThemePreference(appTheme); + } + + function saveAppSettings(next: AnalyticsSettings, nextTheme: AppTheme) { analyticsSettings = next; + appTheme = nextTheme; persistAnalyticsSettings(next); + persistThemePreference(nextTheme); appSettingsOpen = false; - if (next.enabled) trackEvent("settings_saved", { analytics_enabled: 1 }); + if (next.enabled) trackEvent("settings_saved", { analytics_enabled: 1, theme: nextTheme }); } function updateCommitMessage(message: string) { @@ -3932,6 +3976,7 @@ {#if appSettingsOpen} { appSettingsOpen = false; }} /> diff --git a/src/app.css b/src/app.css index 438e44a..3af3a87 100644 --- a/src/app.css +++ b/src/app.css @@ -29,26 +29,88 @@ --font-mono: "Cascadia Code", "SFMono-Regular", Consolas, monospace; } +:root { + --app-color-scheme: dark; + --app-bg: + radial-gradient(circle at 22% 8%, rgba(189, 52, 254, 0.28), transparent 34%), + radial-gradient(circle at 74% 10%, rgba(65, 209, 255, 0.22), transparent 30%), + radial-gradient(circle at 88% 78%, rgba(255, 211, 67, 0.1), transparent 26%), + linear-gradient(135deg, #090912 0%, #111122 45%, #0b0b14 100%); + --app-button-bg: linear-gradient(180deg, rgba(42, 43, 70, 0.9), rgba(27, 28, 48, 0.92)); + --app-input-bg: rgba(13, 14, 26, 0.78); + --app-select-option-bg: #1c2033; + --app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.045), transparent 72%); + --app-panel-shadow: 0 18px 60px rgba(0,0,0,0.24), inset 0 1px 0 rgba(255,255,255,0.05); + --app-dialog-backdrop: #050712; + --app-dialog-bg: #111321; + --app-dialog-chrome: #171a2b; + --app-dialog-shadow: 0 24px 72px rgba(0, 0, 0, 0.6), 0 2px 12px rgba(0,0,0,0.4); + --app-settings-row-bg: #151827; + --app-scrollbar-thumb: #2a2f45; + --app-scrollbar-thumb-hover: #3a3f58; +} + +:root[data-theme="light"] { + --color-ink: #172033; + --color-ink-muted: #475569; + --color-ink-faint: #728098; + --color-ink-dim: #5f6f89; + --color-ink-quiet: #66758d; + + --color-surface: rgba(255, 255, 255, 0.86); + --color-surface-alt: #f3f6fb; + --color-surface-dim: rgba(244, 247, 252, 0.92); + --color-surface-hover: rgba(226, 233, 245, 0.82); + --color-surface-raised: rgba(255, 255, 255, 0.94); + --color-surface-solid: #ffffff; + + --color-border: rgba(61, 89, 142, 0.24); + --color-border-subtle: rgba(34, 49, 78, 0.11); + --color-border-input: rgba(46, 117, 182, 0.28); + + --color-primary: #315fd6; + --color-primary-dark: #284cb4; + --color-accent: #0f8fb5; + + --color-bar: rgba(248, 251, 255, 0.94); + --color-bar-text: #172033; + --color-bar-muted: #64728a; + + --app-color-scheme: light; + --app-bg: + radial-gradient(circle at 12% 0%, rgba(49, 95, 214, 0.12), transparent 30%), + radial-gradient(circle at 82% 4%, rgba(15, 143, 181, 0.12), transparent 28%), + linear-gradient(135deg, #f7f9fd 0%, #eef3f9 48%, #f8fafc 100%); + --app-button-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(235, 240, 249, 0.95)); + --app-input-bg: rgba(255, 255, 255, 0.9); + --app-select-option-bg: #ffffff; + --app-panel-highlight: linear-gradient(180deg, rgba(255,255,255,0.72), transparent 70%); + --app-panel-shadow: 0 18px 48px rgba(28, 44, 74, 0.12), inset 0 1px 0 rgba(255,255,255,0.86); + --app-dialog-backdrop: rgba(232, 238, 248, 0.96); + --app-dialog-bg: #ffffff; + --app-dialog-chrome: #f4f7fc; + --app-dialog-shadow: 0 24px 72px rgba(28, 44, 74, 0.2), 0 2px 12px rgba(28,44,74,0.1); + --app-settings-row-bg: #f8fafd; + --app-scrollbar-thumb: #c2cada; + --app-scrollbar-thumb-hover: #aeb8cb; +} + @layer base { *, *::before, *::after { box-sizing: border-box; } html, body, #app { width: 100%; height: 100%; margin: 0; } - * { scrollbar-width: thin; scrollbar-color: #2a2f45 transparent; } + * { scrollbar-width: thin; scrollbar-color: var(--app-scrollbar-thumb) transparent; } ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: transparent; } - ::-webkit-scrollbar-thumb { background: #2a2f45; border-radius: 3px; } - ::-webkit-scrollbar-thumb:hover { background: #3a3f58; } + ::-webkit-scrollbar-thumb { background: var(--app-scrollbar-thumb); border-radius: 3px; } + ::-webkit-scrollbar-thumb:hover { background: var(--app-scrollbar-thumb-hover); } - html { color-scheme: dark; } + html { color-scheme: var(--app-color-scheme); } body { overflow: hidden; color: var(--color-ink); - background: - radial-gradient(circle at 22% 8%, rgba(189, 52, 254, 0.28), transparent 34%), - radial-gradient(circle at 74% 10%, rgba(65, 209, 255, 0.22), transparent 30%), - radial-gradient(circle at 88% 78%, rgba(255, 211, 67, 0.1), transparent 26%), - linear-gradient(135deg, #090912 0%, #111122 45%, #0b0b14 100%); + background: var(--app-bg); font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-synthesis: none; text-rendering: optimizeLegibility; @@ -68,7 +130,7 @@ border: 1px solid var(--color-border); border-radius: 8px; color: var(--color-ink-muted); - background: linear-gradient(180deg, rgba(42, 43, 70, 0.9), rgba(27, 28, 48, 0.92)); + background: var(--app-button-bg); cursor: pointer; transition: background 120ms ease, border-color 120ms ease, color 120ms ease; font-size: 13px; @@ -85,7 +147,7 @@ border: 1px solid var(--color-border-input); border-radius: 8px; color: var(--color-ink); - background: rgba(13, 14, 26, 0.78); + background: var(--app-input-bg); outline: none; } input:focus, textarea:focus, select:focus { @@ -117,7 +179,7 @@ border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(90, 140, 248, 0.18); } - select option { background: #1c2033; color: var(--color-ink); } + select option { background: var(--app-select-option-bg); color: var(--color-ink); } } @layer components { @@ -169,9 +231,9 @@ border: 1px solid var(--color-border); border-radius: 12px; background: - linear-gradient(180deg, rgba(255,255,255,0.045), transparent 72%), + var(--app-panel-highlight), var(--color-surface); - box-shadow: 0 18px 60px rgba(0,0,0,0.24), inset 0 1px 0 rgba(255,255,255,0.05); + box-shadow: var(--app-panel-shadow); backdrop-filter: blur(16px); } @@ -2630,7 +2692,7 @@ display: grid; place-items: center; padding: 24px; - background-color: #050712; + background-color: var(--app-dialog-backdrop); background-image: none; } @@ -2645,9 +2707,9 @@ height: min(760px, 100%); border: 1px solid var(--color-border); border-radius: 12px; - background-color: #111321; + background-color: var(--app-dialog-bg); background-image: none; - box-shadow: 0 24px 72px rgba(0, 0, 0, 0.6), 0 2px 12px rgba(0,0,0,0.4); + box-shadow: var(--app-dialog-shadow); overflow: hidden; } .compare-dialog { @@ -2795,6 +2857,56 @@ font-size: 15px; font-weight: 750; } + .settings-segmented { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 6px; + min-width: 0; + padding: 4px; + border: 1px solid var(--color-border-subtle); + border-radius: 8px; + background: var(--app-settings-row-bg); + } + .settings-segmented label { + display: flex; + align-items: center; + justify-content: center; + min-width: 0; + min-height: 32px; + padding: 0 10px; + border: 1px solid transparent; + border-radius: 6px; + color: var(--color-ink-dim); + cursor: pointer; + font-size: 12px; + font-weight: 800; + transition: background 120ms ease, border-color 120ms ease, color 120ms ease; + } + .settings-segmented label:hover { + border-color: var(--color-border-subtle); + color: var(--color-ink); + background: var(--color-surface-hover); + } + .settings-segmented label.active { + border-color: var(--color-border-input); + color: var(--color-ink); + background: var(--color-surface-raised); + box-shadow: 0 1px 4px rgba(28,44,74,0.08); + } + .settings-segmented input { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + opacity: 0; + pointer-events: none; + } + .settings-segmented span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .settings-toggle-row { display: grid; grid-template-columns: auto minmax(0, 1fr); @@ -2804,7 +2916,7 @@ padding: 12px; border: 1px solid var(--color-border-subtle); border-radius: 8px; - background: #151827; + background: var(--app-settings-row-bg); color: var(--color-ink-muted); cursor: pointer; } @@ -2970,7 +3082,7 @@ gap: 8px; } - .dialog-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--color-border-subtle); background: #171a2b; } + .dialog-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--color-border-subtle); background: var(--app-dialog-chrome); } .dialog-header > div:first-child { min-width: 0; } .dialog-header-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex: 0 0 auto; min-width: 0; } .compare-restore { max-width: 170px; min-width: 0; } @@ -2984,7 +3096,7 @@ .dialog-body { display: grid; grid-template-columns: 280px minmax(0, 1fr); min-height: 0; } .compare-dialog .dialog-body { grid-template-columns: minmax(260px, 320px) minmax(0, 1fr); } - .dialog-files { display: grid; align-content: start; gap: 4px; padding: 8px; overflow: auto; border-right: 1px solid var(--color-border-subtle); background: #171a2b; } + .dialog-files { display: grid; align-content: start; gap: 4px; padding: 8px; overflow: auto; border-right: 1px solid var(--color-border-subtle); background: var(--app-dialog-chrome); } .dialog-file-row { display: grid; @@ -3132,7 +3244,7 @@ letter-spacing: 0; } - .dialog-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--color-border-subtle); background: #171a2b; } + .dialog-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--color-border-subtle); background: var(--app-dialog-chrome); } .dialog-footer-info { color: var(--color-ink-dim); font-size: 13px; font-weight: 700; } .prepared-tag { display: inline-flex; align-items: center; gap: 4px; margin-right: auto; color: #4eca76; font-size: 12px; font-weight: 700; } @@ -4388,6 +4500,381 @@ .resolve-path { overflow: hidden; color: var(--color-ink-faint); font-family: var(--font-mono); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } } +:root[data-theme="light"] button:hover:not(:disabled) { + background: var(--color-surface-hover); +} + +:root[data-theme="light"] input:focus, +:root[data-theme="light"] textarea:focus, +:root[data-theme="light"] select:focus { + box-shadow: 0 0 0 3px rgba(49, 95, 214, 0.14), 0 10px 24px rgba(28, 44, 74, 0.06); +} + +:root[data-theme="light"] .btn-primary { + border-color: rgba(49, 95, 214, 0.72); + background: linear-gradient(135deg, #315fd6 0%, #0f8fb5 100%); + box-shadow: 0 10px 24px rgba(49, 95, 214, 0.18); +} + +:root[data-theme="light"] .btn-primary:hover:not(:disabled) { + border-color: rgba(15, 143, 181, 0.72); + background: linear-gradient(135deg, #3d6ee4 0%, #139dc7 100%); +} + +:root[data-theme="light"] .btn-secondary { + 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"] .btn-secondary:hover:not(:disabled) { + color: #0d4f9e; + background: linear-gradient(180deg, rgba(15, 143, 181, 0.13), rgba(49, 95, 214, 0.1)); +} + +:root[data-theme="light"] .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"] .titlebar-brand, +:root[data-theme="light"] .titlebar-right { + border-color: rgba(34,49,78,0.1); +} + +:root[data-theme="light"] .titlebar-brand-icon img { + filter: drop-shadow(0 1px 2px rgba(28,44,74,0.18)); +} + +:root[data-theme="light"] .tb-version { + color: rgba(23,32,51,0.36); +} + +:root[data-theme="light"] .titlebar-info svg { + color: var(--color-accent); +} + +:root[data-theme="light"] .tb-repo, +:root[data-theme="light"] .tb-no-repo { + color: var(--color-bar-muted); +} + +:root[data-theme="light"] .tb-sep { + color: rgba(23,32,51,0.28); +} + +:root[data-theme="light"] .tb-branch { + color: var(--color-bar-text); +} + +:root[data-theme="light"] .tb-action:hover:not(:disabled), +:root[data-theme="light"] .tb-btn:hover:not(:disabled) { + background: rgba(49,95,214,0.08); + color: var(--color-ink); +} + +:root[data-theme="light"] .titlebar-divider { + background: rgba(34,49,78,0.1); +} + +:root[data-theme="light"] .tb-btn { + color: var(--color-bar-muted); +} + +:root[data-theme="light"] .topbar { + border-color: var(--color-border); + background: + linear-gradient(90deg, rgba(49,95,214,0.06), rgba(15,143,181,0.045), rgba(214,156,49,0.035)), + var(--color-surface); +} + +:root[data-theme="light"] .repo-tabbar, +:root[data-theme="light"] .repo-management-tools { + background: rgba(255,255,255,0.78); +} + +:root[data-theme="light"] .repo-tab-wrap { + background: rgba(244,247,252,0.72); +} + +:root[data-theme="light"] .repo-tab-wrap.active { + background: rgba(49,95,214,0.1); +} + +:root[data-theme="light"] .repo-row { + border-bottom-color: rgba(34,49,78,0.08); +} + +:root[data-theme="light"] .repo-row-main:hover:not(:disabled) { + background: rgba(49,95,214,0.07); +} + +:root[data-theme="light"] .repo-row-icon { + border-left-color: rgba(34,49,78,0.08); +} + +:root[data-theme="light"] .notice.error, +:root[data-theme="light"] .cred-error { + color: #b33445; + background: rgba(210, 55, 75, 0.08); +} + +:root[data-theme="light"] .notice.busy { + color: #315fd6; +} + +:root[data-theme="light"] .notice.conflict, +:root[data-theme="light"] .notice-action.conflict, +:root[data-theme="light"] .resolve-status, +:root[data-theme="light"] .resolve-conflict-label { + color: #96620f; +} + +:root[data-theme="light"] .notice.rebase, +:root[data-theme="light"] .notice-action.rebase { + color: #6f3ca8; +} + +:root[data-theme="light"] .update-toast { + box-shadow: 0 22px 56px rgba(28,44,74,0.18), 0 0 0 1px rgba(255,255,255,0.75) inset; +} + +:root[data-theme="light"] .branch-filter-backdrop { + background: rgba(232,238,248,0.96); +} + +:root[data-theme="light"] .branch-filter-dialog { + background: + linear-gradient(180deg, #ffffff 0%, #f4f7fc 72%), + #f4f7fc; + box-shadow: var(--app-dialog-shadow); +} + +:root[data-theme="light"] .graph-branch-dialog-button, +:root[data-theme="light"] .branch-filter-option { + color: #16723b; +} + +:root[data-theme="light"] .graph-branch-dialog-button span { + color: #ffffff; + background: #22834a; +} + +:root[data-theme="light"] .main-panel { + background: var(--color-surface); + box-shadow: var(--app-panel-shadow); +} + +:root[data-theme="light"] .repo-management { + background: var(--color-surface); + box-shadow: var(--app-panel-shadow); +} + +:root[data-theme="light"] .repo-section { + background: rgba(255,255,255,0.62); +} + +:root[data-theme="light"] .repo-section > header { + background: rgba(244,247,252,0.88); +} + +:root[data-theme="light"] .repo-empty { + background: rgba(255,255,255,0.48); +} + +: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: var(--color-surface-dim); +} + +:root[data-theme="light"] .file-row.selected, +:root[data-theme="light"] .branch-row.current, +:root[data-theme="light"] .file-history-row.active { + background: rgba(49,95,214,0.08); +} + +:root[data-theme="light"] .graph-list { + background: rgba(248,250,253,0.9); +} + +:root[data-theme="light"] .graph-gutter { + border-right-color: rgba(34,49,78,0.1); + background: + linear-gradient(90deg, rgba(49,95,214,0.035), rgba(255,255,255,0.18)), + rgba(238,243,249,0.78); +} + +:root[data-theme="light"] .graph-dot { + border-color: #ffffff; + box-shadow: 0 0 0 1px rgba(34,49,78,0.16), 0 2px 6px rgba(28,44,74,0.12); +} + +:root[data-theme="light"] .graph-dot.merge { + background: #ffffff; + box-shadow: 0 0 0 1px rgba(34,49,78,0.14); +} + +:root[data-theme="light"] .commit-body { + background: rgba(255,255,255,0.72); +} + +:root[data-theme="light"] .graph-row + .graph-row .commit-body { + border-top-color: rgba(34,49,78,0.08); +} + +:root[data-theme="light"] .graph-row:hover .commit-body { + background: rgba(235,241,250,0.9); +} + +:root[data-theme="light"] .graph-row.merge-row .commit-body { + background: rgba(248,244,252,0.82); +} + +:root[data-theme="light"] .graph-row.tip-row .commit-body { + background: + linear-gradient(90deg, rgba(49,95,214,0.075), transparent 34%), + rgba(255,255,255,0.82); +} + +:root[data-theme="light"] .commit-summary { + color: var(--color-ink); +} + +:root[data-theme="light"] .commit-avatar { + border-color: rgba(49,95,214,0.18); + color: #315fd6; + background: rgba(232,238,248,0.92); +} + +:root[data-theme="light"] .commit-kind { + background: rgba(244,247,252,0.82); +} + +:root[data-theme="light"] .commit-hash { + color: #315fd6; +} + +:root[data-theme="light"] .commit-branch-chip, +:root[data-theme="light"] .graph-hover-branches span { + border-color: rgba(31,128,76,0.22); + color: #146b3b; + background: rgba(224,246,233,0.92); + box-shadow: 0 8px 22px rgba(28,44,74,0.12); +} + +:root[data-theme="light"] .graph-hover-branches span.remote, +:root[data-theme="light"] .branch-filter-option.remote { + border-color: rgba(49,95,214,0.22); + color: #315fd6; + background: rgba(231,237,255,0.92); +} + +:root[data-theme="light"] .graph-hover-branches span.ahead { + border-color: rgba(150,98,15,0.28); + color: #96620f; + background: rgba(255,244,224,0.95); +} + +:root[data-theme="light"] .graph-hover-branches span.behind { + border-color: rgba(49,95,214,0.26); + color: #315fd6; + background: rgba(231,237,255,0.95); +} + +:root[data-theme="light"] .ref-list .ref-chip.head { + color: #ffffff; + background: linear-gradient(135deg, #0f8fb5, #315fd6); +} + +:root[data-theme="light"] .ref-list .ref-chip.branch { + color: #16723b; + background: rgba(78,202,118,0.12); +} + +:root[data-theme="light"] .ref-list .ref-chip.remote { + color: #315fd6; + background: rgba(49,95,214,0.09); +} + +:root[data-theme="light"] .commit-files, +:root[data-theme="light"] .commit-file-button { + background: rgba(248,250,253,0.78); +} + +:root[data-theme="light"] .file-history-rail-toggle { + background: linear-gradient(180deg, rgba(49,95,214,0.08), rgba(15,143,181,0.04)); +} + +:root[data-theme="light"] .file-history-rail-toggle:hover:not(:disabled) { + background: linear-gradient(180deg, rgba(49,95,214,0.12), rgba(15,143,181,0.08)); +} + +:root[data-theme="light"] .file-history-loading { + background: + radial-gradient(circle at center 42%, rgba(49,95,214,0.1), transparent 52%), + linear-gradient(180deg, rgba(244,247,252,0.88), transparent); +} + +:root[data-theme="light"] .file-history-loading-icon { + filter: + drop-shadow(0 10px 14px rgba(28,44,74,0.18)) + drop-shadow(0 0 12px rgba(49,95,214,0.18)); +} + +:root[data-theme="light"] .branch-context-menu { + box-shadow: 0 18px 50px rgba(28,44,74,0.18); +} + +:root[data-theme="light"] .dialog-file-row:hover, +:root[data-theme="light"] .branch-row:hover, +:root[data-theme="light"] .explorer-row:hover, +:root[data-theme="light"] .commit-files-toggle:hover:not(:disabled) { + background: var(--color-surface-hover); +} + +:root[data-theme="light"] .split-span.split-meta { + background: #e9eef7; +} + +:root[data-theme="light"] .resolve-split, +:root[data-theme="light"] .resolve-num, +:root[data-theme="light"] .resolve-cell.empty, +:root[data-theme="light"] .resolve-num.empty { + background: rgba(234,239,248,0.72); +} + +:root[data-theme="light"] .diff-line.meta, +:root[data-theme="light"] .resolve-split-marker { + color: #64728a; +} + +:root[data-theme="light"] .cred-input input, +:root[data-theme="light"] .cred-expiry input[type="date"] { + background: rgba(255,255,255,0.92); + color-scheme: light; +} + +:root[data-theme="light"] .cred-token-hint code { + color: #0d78a0; +} + +:root[data-theme="light"] .status-panel-overlay { + background: + radial-gradient(circle at 50% 38%, rgba(49, 95, 214, 0.12), transparent 55%), + rgba(244, 247, 252, 0.68); +} + +:root[data-theme="light"] .status-panel-overlay-card { + background: + linear-gradient(180deg, rgba(255,255,255,0.96), rgba(244,247,252,0.94)), + var(--color-surface-raised); + box-shadow: 0 18px 44px rgba(28,44,74,0.16), inset 0 1px 0 rgba(255,255,255,0.9); +} + /* Custom tooltip — appended to body, not clipped by overflow:hidden parents */ .path-tooltip { position: fixed; @@ -4476,6 +4963,10 @@ @media (max-width: 740px) { body { overflow: auto; } .shell-body { min-height: 100%; gap: 6px; } + .titlebar-info { padding: 0 8px; } + .titlebar-actions .tb-action:not(.auto-toggle):not(:last-child) { display: none; } + .tb-action { width: 36px; padding: 0 8px; } + .tb-btn { width: 34px; } .workspace { grid-template-columns: 1fr; gap: 6px; } .left-sidebar-resize-handle { display: none; } .history-aside { grid-template-columns: 1fr; grid-template-rows: minmax(200px, 1fr) minmax(150px, 0.5fr); min-height: 380px; } diff --git a/src/lib/TitleBar.svelte b/src/lib/TitleBar.svelte index e505e47..3d14bea 100644 --- a/src/lib/TitleBar.svelte +++ b/src/lib/TitleBar.svelte @@ -24,22 +24,44 @@ export let onToggleAutoRefresh: () => void = () => {}; export let onOpenSettings: () => void = () => {}; - const win = getCurrentWindow(); + let win: ReturnType | null = null; let isMaximized = false; let unlisten: (() => void) | undefined; let appVersion = ""; onMount(async () => { - isMaximized = await win.isMaximized(); - unlisten = await win.onResized(async () => { + try { + win = getCurrentWindow(); isMaximized = await win.isMaximized(); - }); - appVersion = await getVersion(); + unlisten = await win.onResized(async () => { + isMaximized = await win?.isMaximized() ?? false; + }); + } catch { + win = null; + } + + try { + appVersion = await getVersion(); + } catch { + appVersion = ""; + } }); onDestroy(() => { unlisten?.(); }); + + function minimizeWindow() { + void win?.minimize(); + } + + function toggleMaximizeWindow() { + void win?.toggleMaximize(); + } + + function closeWindow() { + void win?.close(); + }
@@ -200,12 +222,12 @@
- -
diff --git a/src/lib/components/AppSettingsDialog.svelte b/src/lib/components/AppSettingsDialog.svelte index 099f5a4..345d936 100644 --- a/src/lib/components/AppSettingsDialog.svelte +++ b/src/lib/components/AppSettingsDialog.svelte @@ -1,19 +1,22 @@ @@ -38,6 +41,31 @@
{ event.preventDefault(); save(); }}> +
+
+
+ +
+ + + +
+
+