style(ui): Move Buttons under the Repo selection

This commit is contained in:
2026-07-11 18:04:49 +02:00
parent a6e7e991dd
commit fdbff8175e
4 changed files with 259 additions and 218 deletions
+23 -14
View File
@@ -6,6 +6,7 @@
import { AlertCircle, BookOpen, 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 AiSettingsDialog from "./lib/components/AiSettingsDialog.svelte";
import AnalyticsNoticeDialog from "./lib/components/AnalyticsNoticeDialog.svelte";
import AppSettingsDialog from "./lib/components/AppSettingsDialog.svelte";
@@ -3529,20 +3530,6 @@
behind={status?.behind ?? 0}
repoName={activeRepoPath ? (activeRepoPath.split(/[\\/]/).filter(Boolean).slice(-1)[0] ?? "") : ""}
hasRepository={workspaceActive}
{isBusy}
{operation}
{autoRefreshEnabled}
{autoRefreshInFlight}
onFetch={fetchRepo}
onPull={pullRepo}
onPush={pushRepo}
onRefresh={refreshRepo}
onSearch={openGlobalSearchDialog}
onCompare={openCompareSelect}
onInteractiveRebase={openInteractiveRebase}
onReflog={openReflog}
onOpenInExplorer={openActiveRepoInExplorer}
onToggleAutoRefresh={toggleAutoRefresh}
onOpenSettings={() => { appSettingsOpen = true; }}
onOpenHelp={openHelp}
language={appLanguage}
@@ -3610,6 +3597,28 @@
</button>
</header>
<!-- Repo actions live under the tab bar and disappear in Repository
Management, where none of them are applicable. -->
{#if workspaceActive}
<RepoToolbar
hasRepository={workspaceActive}
{isBusy}
{operation}
{autoRefreshEnabled}
{autoRefreshInFlight}
onFetch={fetchRepo}
onPull={pullRepo}
onPush={pushRepo}
onRefresh={refreshRepo}
onSearch={openGlobalSearchDialog}
onCompare={openCompareSelect}
onInteractiveRebase={openInteractiveRebase}
onReflog={openReflog}
onOpenInExplorer={openActiveRepoInExplorer}
onToggleAutoRefresh={toggleAutoRefresh}
/>
{/if}
{#if repoTabContextMenu}
{@const menu = repoTabContextMenu}
{@const contextTab = repoTabs.find((tab) => sameRepoPath(tab.path, menu.path))}
+36 -8
View File
@@ -541,6 +541,7 @@
.shell {
--app-titlebar-height: 42px;
--app-toolbar-height: 34px;
display: flex;
flex-direction: column;
height: 100%;
@@ -562,7 +563,7 @@
position: relative;
z-index: 500;
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-columns: auto 1fr auto auto;
align-items: center;
height: var(--app-titlebar-height);
background:
@@ -639,8 +640,30 @@
.tb-no-repo { color: var(--color-bar-muted); font-size: 12px; font-style: italic; }
.titlebar-right { display: flex; align-items: stretch; height: 100%; border-left: 1px solid rgba(255,255,255,0.06); }
.titlebar-actions { display: flex; align-items: stretch; }
/* App-global actions (help, settings) that must stay reachable in every view,
including Repository Management where the repo toolbar is hidden. */
.titlebar-globals {
display: flex;
align-items: stretch;
height: 100%;
}
/* Repo action toolbar: its own row under the repo tab bar (hidden in Repository
Management). Its own row means a narrow window can never push the window
controls out of the titlebar; overflow-x is the last-resort safety net. */
.repo-toolbar {
display: flex;
align-items: stretch;
height: var(--app-toolbar-height);
border: 1px solid var(--color-border-subtle);
border-radius: 10px;
background: rgba(16, 17, 29, 0.9);
flex-shrink: 0;
overflow-x: auto;
scrollbar-width: none;
user-select: none;
}
.repo-toolbar::-webkit-scrollbar { display: none; }
.tb-action {
display: flex;
@@ -655,6 +678,7 @@
color: var(--color-bar-muted);
font-size: 12px;
cursor: pointer;
flex-shrink: 0;
transition: background 120ms ease, color 120ms ease;
-webkit-app-region: no-drag;
}
@@ -664,9 +688,9 @@
.tb-action.auto-toggle.active:hover:not(:disabled) { color: #6ee090; background: rgba(78,202,118,0.1); }
.tb-action-label { font-size: 12px; font-weight: 600; }
.titlebar-divider { width: 1px; margin: 8px 0; background: rgba(255,255,255,0.08); flex-shrink: 0; }
.titlebar-divider { width: 1px; margin: 8px 6px; background: rgba(255,255,255,0.08); flex-shrink: 0; }
.titlebar-controls { display: flex; align-items: stretch; height: 100%; }
.titlebar-controls { display: flex; align-items: stretch; height: 100%; border-left: 1px solid rgba(255,255,255,0.06); }
.tb-btn {
display: flex;
@@ -4720,10 +4744,15 @@
}
:root[data-theme="light"] .titlebar-brand,
:root[data-theme="light"] .titlebar-right {
:root[data-theme="light"] .titlebar-controls {
border-color: rgba(34,49,78,0.1);
}
:root[data-theme="light"] .repo-toolbar {
border-color: var(--color-border);
background: rgba(255,255,255,0.78);
}
:root[data-theme="light"] .titlebar-brand-icon img {
filter: drop-shadow(0 1px 2px rgba(28,44,74,0.18));
}
@@ -5198,8 +5227,7 @@
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-action { padding: 0 9px; }
.tb-btn { width: 34px; }
.workspace { grid-template-columns: 1fr; gap: 6px; }
.left-sidebar-resize-handle { display: none; }
+156
View File
@@ -0,0 +1,156 @@
<script lang="ts">
import { CloudDownload, Download, FolderOpen, GitCompare, History, ListRestart, LoaderCircle, RefreshCw, Search, Upload } from "@lucide/svelte";
export let hasRepository: boolean = false;
export let isBusy: boolean = false;
export let operation: string = "";
export let autoRefreshEnabled: boolean = true;
export let autoRefreshInFlight: boolean = false;
export let onFetch: () => void = () => {};
export let onPull: () => void = () => {};
export let onPush: () => void = () => {};
export let onRefresh: () => void = () => {};
export let onSearch: () => void = () => {};
export let onCompare: () => void = () => {};
export let onInteractiveRebase: () => void = () => {};
export let onReflog: () => void = () => {};
export let onOpenInExplorer: () => void = () => {};
export let onToggleAutoRefresh: () => void = () => {};
</script>
<div class="repo-toolbar" role="toolbar" aria-label="Repository actions">
<button
class="tb-action"
onclick={onOpenInExplorer}
disabled={!hasRepository || isBusy}
title="Open repository in Explorer"
aria-label="Open repository in Explorer"
>
<FolderOpen size={14} aria-hidden="true" />
<span class="tb-action-label">Explorer</span>
</button>
<button
class="tb-action"
onclick={onSearch}
disabled={!hasRepository || isBusy}
title="Global search"
aria-label="Global search"
>
<Search size={14} aria-hidden="true" />
<span class="tb-action-label">Search</span>
</button>
<button
class="tb-action"
onclick={onCompare}
disabled={!hasRepository || isBusy}
title="Compare commits"
aria-label="Compare commits"
>
<GitCompare size={14} aria-hidden="true" />
<span class="tb-action-label">Compare</span>
</button>
<button
class="tb-action"
onclick={onInteractiveRebase}
disabled={!hasRepository || isBusy}
title="Interactive rebase"
aria-label="Interactive rebase"
>
<ListRestart size={14} aria-hidden="true" />
<span class="tb-action-label">Rebase</span>
</button>
<button
class="tb-action"
onclick={onReflog}
disabled={!hasRepository || isBusy}
title="Reflog"
aria-label="Reflog"
>
<History size={14} aria-hidden="true" />
<span class="tb-action-label">Reflog</span>
</button>
<div class="titlebar-divider" aria-hidden="true"></div>
<button
class="tb-action"
onclick={onFetch}
disabled={!hasRepository || isBusy}
title="Fetch"
aria-label="Fetch"
>
{#if operation === "Fetching"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<CloudDownload size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Fetch</span>
</button>
<button
class="tb-action"
onclick={onPull}
disabled={!hasRepository || isBusy}
title="Pull"
aria-label="Pull"
>
{#if operation === "Pulling"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<Download size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Pull</span>
</button>
<button
class="tb-action"
onclick={onPush}
disabled={!hasRepository || isBusy}
title="Push"
aria-label="Push"
>
{#if operation === "Pushing"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<Upload size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Push</span>
</button>
<div class="titlebar-divider" aria-hidden="true"></div>
<button
class="tb-action"
onclick={onRefresh}
disabled={isBusy || !hasRepository}
title="Refresh"
aria-label="Refresh"
>
<RefreshCw
class={operation === "Refreshing" ? "spin" : ""}
size={14}
aria-hidden="true"
/>
<span class="tb-action-label">Refresh</span>
</button>
<button
class="tb-action auto-toggle"
class:active={autoRefreshEnabled}
onclick={onToggleAutoRefresh}
aria-pressed={autoRefreshEnabled}
title={autoRefreshEnabled ? "Auto-refresh on — click to disable" : "Auto-refresh off — click to enable"}
aria-label="Toggle auto-refresh"
>
<RefreshCw
class={autoRefreshInFlight ? "spin" : ""}
size={14}
aria-hidden="true"
/>
<span class="tb-action-label">Auto</span>
</button>
</div>
+3 -155
View File
@@ -2,7 +2,7 @@
import { onDestroy, onMount } from "svelte";
import { getVersion } from "@tauri-apps/api/app";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { CircleHelp, CloudDownload, Download, FolderOpen, GitBranch, GitCompare, History, ListRestart, LoaderCircle, Minus, RefreshCw, Search, Settings, Upload, X } from "@lucide/svelte";
import { CircleHelp, GitBranch, Minus, Settings, X } from "@lucide/svelte";
import iconUrl from "../../src-tauri/icons/icon.png";
export let branch: string = "";
@@ -10,20 +10,6 @@
export let behind: number = 0;
export let repoName: string = "";
export let hasRepository: boolean = false;
export let isBusy: boolean = false;
export let operation: string = "";
export let autoRefreshEnabled: boolean = true;
export let autoRefreshInFlight: boolean = false;
export let onFetch: () => void = () => {};
export let onPull: () => void = () => {};
export let onPush: () => void = () => {};
export let onRefresh: () => void = () => {};
export let onSearch: () => void = () => {};
export let onCompare: () => void = () => {};
export let onInteractiveRebase: () => void = () => {};
export let onReflog: () => void = () => {};
export let onOpenInExplorer: () => void = () => {};
export let onToggleAutoRefresh: () => void = () => {};
export let onOpenHelp: () => void = () => {};
export let onOpenSettings: () => void = () => {};
export let language: "en" | "de" = "en";
@@ -106,140 +92,8 @@
{/if}
</div>
<!-- Right: actions + window controls -->
<div class="titlebar-right">
<div class="titlebar-actions" role="toolbar" aria-label="Repository actions">
<button
class="tb-action"
onclick={onOpenInExplorer}
disabled={!hasRepository || isBusy}
title="Open repository in Explorer"
aria-label="Open repository in Explorer"
>
<FolderOpen size={14} aria-hidden="true" />
<span class="tb-action-label">Explorer</span>
</button>
<button
class="tb-action"
onclick={onSearch}
disabled={!hasRepository || isBusy}
title="Global search"
aria-label="Global search"
>
<Search size={14} aria-hidden="true" />
<span class="tb-action-label">Search</span>
</button>
<button
class="tb-action"
onclick={onCompare}
disabled={!hasRepository || isBusy}
title="Compare commits"
aria-label="Compare commits"
>
<GitCompare size={14} aria-hidden="true" />
<span class="tb-action-label">Compare</span>
</button>
<button
class="tb-action"
onclick={onInteractiveRebase}
disabled={!hasRepository || isBusy}
title="Interactive rebase"
aria-label="Interactive rebase"
>
<ListRestart size={14} aria-hidden="true" />
<span class="tb-action-label">Rebase</span>
</button>
<button
class="tb-action"
onclick={onReflog}
disabled={!hasRepository || isBusy}
title="Reflog"
aria-label="Reflog"
>
<History size={14} aria-hidden="true" />
<span class="tb-action-label">Reflog</span>
</button>
<button
class="tb-action"
onclick={onFetch}
disabled={!hasRepository || isBusy}
title="Fetch"
aria-label="Fetch"
>
{#if operation === "Fetching"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<CloudDownload size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Fetch</span>
</button>
<button
class="tb-action"
onclick={onPull}
disabled={!hasRepository || isBusy}
title="Pull"
aria-label="Pull"
>
{#if operation === "Pulling"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<Download size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Pull</span>
</button>
<button
class="tb-action"
onclick={onPush}
disabled={!hasRepository || isBusy}
title="Push"
aria-label="Push"
>
{#if operation === "Pushing"}
<LoaderCircle class="spin" size={14} aria-hidden="true" />
{:else}
<Upload size={14} aria-hidden="true" />
{/if}
<span class="tb-action-label">Push</span>
</button>
<button
class="tb-action"
onclick={onRefresh}
disabled={isBusy || !hasRepository}
title="Refresh"
aria-label="Refresh"
>
<RefreshCw
class={operation === "Refreshing" ? "spin" : ""}
size={14}
aria-hidden="true"
/>
<span class="tb-action-label">Refresh</span>
</button>
<button
class="tb-action auto-toggle"
class:active={autoRefreshEnabled}
onclick={onToggleAutoRefresh}
aria-pressed={autoRefreshEnabled}
title={autoRefreshEnabled ? "Auto-refresh on — click to disable" : "Auto-refresh off — click to enable"}
aria-label="Toggle auto-refresh"
>
<RefreshCw
class={autoRefreshInFlight ? "spin" : ""}
size={14}
aria-hidden="true"
/>
<span class="tb-action-label">Auto</span>
</button>
<!-- Right: app-global actions + window controls -->
<div class="titlebar-globals">
<button
class="tb-action"
onclick={onOpenHelp}
@@ -247,9 +101,7 @@
aria-label={language === "de" ? "Hilfe öffnen" : "Open help"}
>
<CircleHelp size={14} aria-hidden="true" />
<span class="tb-action-label">{language === "de" ? "Hilfe" : "Help"}</span>
</button>
<button
class="tb-action"
onclick={onOpenSettings}
@@ -257,12 +109,9 @@
aria-label={language === "de" ? "Einstellungen" : "Settings"}
>
<Settings size={14} aria-hidden="true" />
<span class="tb-action-label">{language === "de" ? "Einstellungen" : "Settings"}</span>
</button>
</div>
<div class="titlebar-divider" aria-hidden="true"></div>
<div class="titlebar-controls">
<button class="tb-btn" onclick={minimizeWindow} title="Minimize" aria-label="Minimize">
<Minus size={12} aria-hidden="true" />
@@ -288,5 +137,4 @@
<X size={12} aria-hidden="true" />
</button>
</div>
</div>
</header>