Files
GitLite/src/lib/components/FileHistoryPanel.svelte
T
2026-06-28 02:23:30 +02:00

124 lines
4.0 KiB
Svelte

<script lang="ts">
import { GitCompare, History, RotateCcw } from "@lucide/svelte";
import type { GitCommit } from "../types";
interface Props {
fileHistory: GitCommit[];
selectedExplorerPath: string;
selectedExplorerLabel: string;
hasRepository: boolean;
isBusy: boolean;
onDiff: (commit: GitCommit) => void;
onRestore: (commit: GitCommit) => void;
}
let {
fileHistory = [],
selectedExplorerPath = "",
selectedExplorerLabel = "File history",
hasRepository = false,
isBusy = false,
onDiff = () => {},
onRestore = () => {},
}: Props = $props();
function formatCommitDate(value: string): string {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(date);
}
function fileName(path: string): string {
return path.split("/").pop() ?? path;
}
function pathTooltip(node: HTMLElement, text: string) {
let el: HTMLDivElement | null = null;
function show(e: MouseEvent) {
if (!text || !text.includes("/")) return;
el = document.createElement("div");
el.className = "path-tooltip";
el.textContent = text;
document.body.appendChild(el);
move(e);
}
function move(e: MouseEvent) {
if (!el) return;
const tw = el.offsetWidth;
const left = Math.max(8, Math.min(e.clientX - tw / 2, window.innerWidth - tw - 8));
el.style.left = `${left}px`;
el.style.top = `${e.clientY - 44}px`;
}
function hide() {
el?.remove();
el = null;
}
node.addEventListener("mouseenter", show);
node.addEventListener("mousemove", move);
node.addEventListener("mouseleave", hide);
return {
update(v: string) { text = v; },
destroy() {
hide();
node.removeEventListener("mouseenter", show);
node.removeEventListener("mousemove", move);
node.removeEventListener("mouseleave", hide);
},
};
}
</script>
<section class="panel grid grid-rows-[auto_1fr] overflow-hidden" aria-label="Selected file history">
<div class="section-head">
<div class="min-w-0 flex-1 overflow-hidden">
<span class="eyebrow">{selectedExplorerLabel}</span>
<h2
class="mt-0.5 text-ink text-base font-bold leading-tight truncate"
use:pathTooltip={selectedExplorerPath}
>{selectedExplorerPath ? fileName(selectedExplorerPath) : "No file"}</h2>
</div>
<span class="pill pill-count flex-shrink-0">{fileHistory.length}</span>
</div>
{#if !hasRepository}
<div class="blank-state">No repository loaded.</div>
{:else if !selectedExplorerPath}
<div class="blank-state">Select a file in Explorer.</div>
{:else if fileHistory.length === 0}
<div class="blank-state">No history returned for this selection.</div>
{:else}
<div class="history-list overflow-auto p-2">
{#each fileHistory as item (item.hash)}
<article class="commit-row compact">
<div class="commit-line">
<History size={16} aria-hidden="true" />
<div>
<strong title={item.summary}>{item.summary}</strong>
<span>{item.short_hash} - {item.author_name}</span>
</div>
</div>
<div class="commit-actions">
<time datetime={item.date}>{formatCommitDate(item.date)}</time>
<div class="commit-action-buttons">
<button class="btn-sm" type="button" onclick={() => onDiff(item)} disabled={isBusy} title="Show changes vs working tree">
<GitCompare size={15} aria-hidden="true" />
Diff
</button>
<button class="btn-sm" type="button" onclick={() => onRestore(item)} disabled={isBusy} title="Restore selected file from this commit">
<RotateCcw size={15} aria-hidden="true" />
Restore
</button>
</div>
</div>
</article>
{/each}
</div>
{/if}
</section>