The structure of the application title bar has been updated to utilize modern CSS layout techniques like Grid and Flexbox. This refactoring improves the responsiveness and organization of key elements such as repository name, branch indicator, and synchronization status. The changes ensure better alignment and handling of varying content lengths across different viewports. - Restructures repo/branch info into a dedicated context container - Uses flex properties for dynamic sizing of title bar sections - Groups sync indicators into a cohesive unit
257 lines
8.0 KiB
Svelte
257 lines
8.0 KiB
Svelte
<script lang="ts">
|
|
import { onDestroy, onMount } from "svelte";
|
|
import { getVersion } from "@tauri-apps/api/app";
|
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
|
import { CloudDownload, Download, FolderOpen, GitBranch, GitCompare, LoaderCircle, Minus, RefreshCw, Search, Settings, Upload, X } from "@lucide/svelte";
|
|
import iconUrl from "../../src-tauri/icons/icon.png";
|
|
|
|
export let branch: string = "";
|
|
export let ahead: number = 0;
|
|
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 onOpenInExplorer: () => void = () => {};
|
|
export let onToggleAutoRefresh: () => void = () => {};
|
|
export let onOpenSettings: () => void = () => {};
|
|
|
|
let win: ReturnType<typeof getCurrentWindow> | null = null;
|
|
let isMaximized = false;
|
|
let unlisten: (() => void) | undefined;
|
|
let appVersion = "";
|
|
|
|
onMount(async () => {
|
|
try {
|
|
win = getCurrentWindow();
|
|
isMaximized = await win.isMaximized();
|
|
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();
|
|
}
|
|
</script>
|
|
|
|
<header class="titlebar" data-tauri-drag-region>
|
|
<!-- Brand -->
|
|
<div class="titlebar-brand" data-tauri-drag-region>
|
|
<span class="titlebar-brand-icon" data-tauri-drag-region>
|
|
<img src={iconUrl} alt="" data-tauri-drag-region />
|
|
</span>
|
|
<span data-tauri-drag-region>Gitty</span>
|
|
{#if appVersion}
|
|
<span class="tb-version" data-tauri-drag-region title="Version {appVersion}">v{appVersion}</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Center: repo + branch info -->
|
|
<div class="titlebar-info" data-tauri-drag-region>
|
|
{#if hasRepository}
|
|
<div class="titlebar-context" data-tauri-drag-region>
|
|
{#if repoName}
|
|
<span class="tb-repo" data-tauri-drag-region>{repoName}</span>
|
|
<span class="tb-sep" data-tauri-drag-region aria-hidden="true">/</span>
|
|
{/if}
|
|
<GitBranch size={12} aria-hidden="true" />
|
|
<span class="tb-branch" data-tauri-drag-region title={branch}>{branch}</span>
|
|
</div>
|
|
{#if ahead > 0 || behind > 0}
|
|
<div class="tb-sync-group" aria-label="Branch synchronization status">
|
|
{#if ahead > 0}
|
|
<span class="tb-sync ahead" title="{ahead} commits ahead">↑{ahead}</span>
|
|
{/if}
|
|
{#if behind > 0}
|
|
<span class="tb-sync behind" title="{behind} commits behind">↓{behind}</span>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
{:else}
|
|
<span class="tb-no-repo" data-tauri-drag-region>No repository open</span>
|
|
{/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={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>
|
|
|
|
<button
|
|
class="tb-action"
|
|
onclick={onOpenSettings}
|
|
title="Settings"
|
|
aria-label="Settings"
|
|
>
|
|
<Settings size={14} aria-hidden="true" />
|
|
<span class="tb-action-label">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" />
|
|
</button>
|
|
<button
|
|
class="tb-btn"
|
|
onclick={toggleMaximizeWindow}
|
|
title={isMaximized ? "Restore" : "Maximize"}
|
|
aria-label={isMaximized ? "Restore" : "Maximize"}
|
|
>
|
|
{#if isMaximized}
|
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
<rect x="3" y="1" width="8" height="8" rx="1" stroke="currentColor" stroke-width="1.5" />
|
|
<path d="M1 3v7a1 1 0 0 0 1 1h7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
</svg>
|
|
{:else}
|
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
<rect x="1" y="1" width="10" height="10" rx="1" stroke="currentColor" stroke-width="1.5" />
|
|
</svg>
|
|
{/if}
|
|
</button>
|
|
<button class="tb-btn close" onclick={closeWindow} title="Close" aria-label="Close">
|
|
<X size={12} aria-hidden="true" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|