From f9c0c0061806e11e9d548a641599f648af681fb6 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Sun, 12 Jul 2026 22:56:39 +0200 Subject: [PATCH] refactor(ui): extract repository tab bar into dedicated component The complex logic and markup for rendering the repository tabs have been extracted from App.svelte into a new, reusable RepoTabs component. This refactoring significantly cleans up the main application view, improves separation of concerns, and makes the UI structure easier to maintain and extend. Corresponding CSS updates were applied to ensure the visual fidelity and responsiveness of the tab bar remain consistent across different states. - Encapsulates all tab rendering logic in src/lib/RepoTabs.svelte - Simplifies App.svelte by replacing large block of HTML with component usage - Updates styling for better alignment and modern aesthetics --- src/App.svelte | 74 ++++++-------------------------- src/app.css | 95 +++++++++++++++++++++++++---------------- src/lib/RepoTabs.svelte | 77 +++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 96 deletions(-) create mode 100644 src/lib/RepoTabs.svelte diff --git a/src/App.svelte b/src/App.svelte index 435115a..94d1185 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -3,10 +3,11 @@ import { invoke } from "@tauri-apps/api/core"; import { open as openDialog } from "@tauri-apps/plugin-dialog"; import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater"; - import { AlertCircle, BookOpen, Cherry, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte"; + import { AlertCircle, Cherry, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte"; import TitleBar from "./lib/TitleBar.svelte"; import RepoToolbar from "./lib/RepoToolbar.svelte"; + import RepoTabs from "./lib/RepoTabs.svelte"; import AiSettingsDialog from "./lib/components/AiSettingsDialog.svelte"; import AnalyticsNoticeDialog from "./lib/components/AnalyticsNoticeDialog.svelte"; import AppSettingsDialog from "./lib/components/AppSettingsDialog.svelte"; @@ -3537,65 +3538,18 @@
-
- - -
- {#each repoTabs as repo (repo.path)} - - {/each} -
- - -
+ activeView === "repository" && sameRepoPath(activeRepoPath, path)} + onSelect={selectRepoTab} + onClose={closeRepoTab} + onContextMenu={openRepoTabContextMenu} + onAdd={chooseRepositoryFolder} + /> diff --git a/src/app.css b/src/app.css index a396146..385d6f9 100644 --- a/src/app.css +++ b/src/app.css @@ -896,10 +896,10 @@ display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: stretch; - min-height: 40px; + min-height: 44px; border: 1px solid var(--color-border-subtle); border-radius: 10px; - background: rgba(16, 17, 29, 0.9); + background: rgba(13, 15, 25, 0.92); overflow: hidden; } @@ -907,76 +907,89 @@ display: flex; align-items: stretch; min-width: 0; + border-left: 1px solid var(--color-border-subtle); overflow-x: auto; overflow-y: hidden; + scrollbar-width: thin; } .repo-tab-wrap { display: flex; align-items: stretch; - min-width: 0; + flex: 0 1 230px; + min-width: 150px; + max-width: 260px; + position: relative; border-right: 1px solid var(--color-border-subtle); - background: rgba(255,255,255,0.015); + background: rgba(255,255,255,0.012); + transition: background 120ms ease; } + .repo-tab-wrap:hover { background: rgba(255,255,255,0.035); } .repo-tab-wrap.active { - background: rgba(90, 140, 248, 0.16); - box-shadow: inset 0 -2px 0 var(--color-primary); + background: linear-gradient(180deg, rgba(90,140,248,0.14), rgba(23,29,43,0.96)); + box-shadow: inset 0 -3px 0 var(--color-primary); } .repo-tab { - min-height: 38px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 8px; + flex: 1 1 auto; + min-height: 42px; min-width: 0; - max-width: 250px; - padding: 0 10px; + max-width: none; + padding: 0 8px 0 13px; border: 0; - border-right: 1px solid var(--color-border-subtle); border-radius: 0; background: transparent; color: var(--color-ink-muted); font-size: 12px; - font-weight: 700; + font-weight: 720; + text-align: left; } + .repo-tab > svg { flex: 0 0 auto; color: var(--color-ink-faint); } .repo-tab.management { - max-width: none; - min-width: 190px; + min-width: 184px; + border: 0; + box-shadow: inset 0 -3px 0 transparent; } - .repo-tab.active, + .repo-tab.management.active { + color: var(--color-ink); + background: linear-gradient(180deg, rgba(90,140,248,0.14), rgba(23,29,43,0.96)); + box-shadow: inset 0 -3px 0 var(--color-primary); + } + .repo-tab.management:hover:not(:disabled) { background: var(--color-surface-hover); } .repo-tab-wrap.active .repo-tab { color: var(--color-ink); } + .repo-tab-wrap.active .repo-tab > svg, + .repo-tab.management.active > svg { color: var(--color-accent); } .repo-tab span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .repo-tab strong { - flex: 0 0 auto; - max-width: 90px; - overflow: hidden; - padding: 2px 6px; - border-radius: 4px; - background: rgba(94, 110, 156, 0.18); - color: var(--color-ink-dim); - font-size: 10.5px; - font-family: var(--font-mono); - text-overflow: ellipsis; - white-space: nowrap; - } - .repo-tab-close, .repo-tab-add { - min-height: 38px; - min-width: 38px; + min-height: 42px; + min-width: 36px; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--color-ink-faint); } + .repo-tab-close { opacity: 0.58; transition: opacity 120ms ease, color 120ms ease, background 120ms ease; } + .repo-tab-wrap:hover .repo-tab-close, + .repo-tab-wrap.active .repo-tab-close { opacity: 1; } + .repo-tab-close:hover:not(:disabled) { color: var(--color-ink); background: var(--color-surface-hover); } .repo-tab-add { + min-width: 44px; border-left: 1px solid var(--color-border-subtle); } + .repo-tab-add:hover:not(:disabled) { color: var(--color-accent); background: var(--color-surface-hover); } .repo-management { display: grid; @@ -4958,15 +4971,24 @@ :root[data-theme="light"] .repo-tabbar, :root[data-theme="light"] .repo-management-tools { - background: rgba(255,255,255,0.78); + background: rgba(247,249,252,0.96); } :root[data-theme="light"] .repo-tab-wrap { - background: rgba(244,247,252,0.72); + background: rgba(255,255,255,0.5); } :root[data-theme="light"] .repo-tab-wrap.active { - background: rgba(49,95,214,0.1); + background: #ffffff; +} + +:root[data-theme="light"] .repo-tab-wrap:hover, +:root[data-theme="light"] .repo-tab.management:hover:not(:disabled) { + background: rgba(255,255,255,0.9); +} + +:root[data-theme="light"] .repo-tab.management.active { + background: #ffffff; } :root[data-theme="light"] .repo-row { @@ -5419,9 +5441,10 @@ } .top-section { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 14px var(--commit-panel-height, 190px); } .repo-form { grid-template-columns: 1fr; } - .repo-tabbar { grid-template-columns: minmax(0, 1fr) auto; } - .repo-tab.management { min-width: 0; } - .repo-tabs-scroll { grid-column: 1 / -1; order: 2; border-top: 1px solid var(--color-border-subtle); } + .repo-tabbar { grid-template-columns: auto minmax(0, 1fr) auto; } + .repo-tab.management { min-width: 44px; width: 44px; padding: 0; justify-content: center; } + .repo-tab.management span { display: none; } + .repo-tabs-scroll { grid-column: auto; order: initial; border-top: 0; } .repo-management-head, .repo-management-tools { align-items: stretch; flex-direction: column; } .repo-management-actions { justify-content: flex-start; } diff --git a/src/lib/RepoTabs.svelte b/src/lib/RepoTabs.svelte new file mode 100644 index 0000000..09c0e61 --- /dev/null +++ b/src/lib/RepoTabs.svelte @@ -0,0 +1,77 @@ + + +
+ + +
+ {#each repoTabs as repo (repo.path)} + + {/each} +
+ + +