refactor(app): remove file history panel and related logic
This commit is contained in:
+62
-120
@@ -20,7 +20,6 @@
|
|||||||
import CredentialDialog from "./lib/components/CredentialDialog.svelte";
|
import CredentialDialog from "./lib/components/CredentialDialog.svelte";
|
||||||
import DiscardConfirmDialog from "./lib/components/DiscardConfirmDialog.svelte";
|
import DiscardConfirmDialog from "./lib/components/DiscardConfirmDialog.svelte";
|
||||||
import ExplorerPanel from "./lib/components/ExplorerPanel.svelte";
|
import ExplorerPanel from "./lib/components/ExplorerPanel.svelte";
|
||||||
import FileHistoryPanel from "./lib/components/FileHistoryPanel.svelte";
|
|
||||||
import HistoryPanel from "./lib/components/HistoryPanel.svelte";
|
import HistoryPanel from "./lib/components/HistoryPanel.svelte";
|
||||||
import NewBranchDialog from "./lib/components/NewBranchDialog.svelte";
|
import NewBranchDialog from "./lib/components/NewBranchDialog.svelte";
|
||||||
import RenameBranchDialog from "./lib/components/RenameBranchDialog.svelte";
|
import RenameBranchDialog from "./lib/components/RenameBranchDialog.svelte";
|
||||||
@@ -220,7 +219,6 @@
|
|||||||
const STASH_PANEL_COLLAPSED_KEY = "gitlite.stashPanelCollapsed.v2";
|
const STASH_PANEL_COLLAPSED_KEY = "gitlite.stashPanelCollapsed.v2";
|
||||||
const EXPLORER_PANEL_COLLAPSED_KEY = "gitlite.explorerPanelCollapsed.v1";
|
const EXPLORER_PANEL_COLLAPSED_KEY = "gitlite.explorerPanelCollapsed.v1";
|
||||||
const HISTORY_ASIDE_WIDTH_KEY = "gitlite.historyAsideWidth.v1";
|
const HISTORY_ASIDE_WIDTH_KEY = "gitlite.historyAsideWidth.v1";
|
||||||
const FILE_HISTORY_WIDTH_KEY = "gitlite.fileHistoryWidth.v1";
|
|
||||||
const COMMIT_PANEL_DEFAULT_HEIGHT = 220;
|
const COMMIT_PANEL_DEFAULT_HEIGHT = 220;
|
||||||
const COMMIT_PANEL_MIN_HEIGHT = COMMIT_PANEL_DEFAULT_HEIGHT;
|
const COMMIT_PANEL_MIN_HEIGHT = COMMIT_PANEL_DEFAULT_HEIGHT;
|
||||||
const COMMIT_PANEL_MAX_HEIGHT = 640;
|
const COMMIT_PANEL_MAX_HEIGHT = 640;
|
||||||
@@ -237,9 +235,6 @@
|
|||||||
const HISTORY_ASIDE_DEFAULT_WIDTH = 620;
|
const HISTORY_ASIDE_DEFAULT_WIDTH = 620;
|
||||||
const HISTORY_ASIDE_MIN_WIDTH = 560;
|
const HISTORY_ASIDE_MIN_WIDTH = 560;
|
||||||
const HISTORY_ASIDE_MAX_WIDTH = 920;
|
const HISTORY_ASIDE_MAX_WIDTH = 920;
|
||||||
const FILE_HISTORY_DEFAULT_WIDTH = 300;
|
|
||||||
const FILE_HISTORY_MIN_WIDTH = 240;
|
|
||||||
const FILE_HISTORY_MAX_WIDTH = 520;
|
|
||||||
const ERROR_AUTO_HIDE_MS = 6000;
|
const ERROR_AUTO_HIDE_MS = 6000;
|
||||||
const COMMIT_HISTORY_PAGE_SIZE = 50;
|
const COMMIT_HISTORY_PAGE_SIZE = 50;
|
||||||
|
|
||||||
@@ -282,6 +277,8 @@
|
|||||||
let expandedCommitHashes = new Set<string>();
|
let expandedCommitHashes = new Set<string>();
|
||||||
let fileHistory: GitCommit[] = [];
|
let fileHistory: GitCommit[] = [];
|
||||||
let fileHistoryLoading = false;
|
let fileHistoryLoading = false;
|
||||||
|
let fileHistoryError = "";
|
||||||
|
let fileHistoryDialogOpen = false;
|
||||||
let fileHistoryRequestId = 0;
|
let fileHistoryRequestId = 0;
|
||||||
let activeFileHistoryRequestId = "";
|
let activeFileHistoryRequestId = "";
|
||||||
let repoOpenRequestId = 0;
|
let repoOpenRequestId = 0;
|
||||||
@@ -414,11 +411,6 @@
|
|||||||
let resizingHistoryAside = false;
|
let resizingHistoryAside = false;
|
||||||
let historyResizeStartX = 0;
|
let historyResizeStartX = 0;
|
||||||
let historyResizeStartWidth = 0;
|
let historyResizeStartWidth = 0;
|
||||||
let fileHistoryWidth = loadFileHistoryWidth();
|
|
||||||
let resizingFileHistory = false;
|
|
||||||
let fileHistoryResizeStartX = 0;
|
|
||||||
let fileHistoryResizeStartWidth = 0;
|
|
||||||
let fileHistoryCollapsed = true;
|
|
||||||
let themeMediaQuery: MediaQueryList | undefined;
|
let themeMediaQuery: MediaQueryList | undefined;
|
||||||
let appVersion = "";
|
let appVersion = "";
|
||||||
|
|
||||||
@@ -1512,28 +1504,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clampFileHistoryWidth(value: number): number {
|
|
||||||
return Math.min(FILE_HISTORY_MAX_WIDTH, Math.max(FILE_HISTORY_MIN_WIDTH, Math.round(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFileHistoryWidth(): number {
|
|
||||||
try {
|
|
||||||
const stored = Number(localStorage.getItem(FILE_HISTORY_WIDTH_KEY));
|
|
||||||
if (Number.isFinite(stored) && stored > 0) return clampFileHistoryWidth(stored);
|
|
||||||
} catch {
|
|
||||||
// Fall through to the default below.
|
|
||||||
}
|
|
||||||
return FILE_HISTORY_DEFAULT_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
function persistFileHistoryWidth(value: number) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(FILE_HISTORY_WIDTH_KEY, String(value));
|
|
||||||
} catch {
|
|
||||||
// Local storage is best-effort only; resizing must keep working without it.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function startCommitPanelResize(event: PointerEvent) {
|
function startCommitPanelResize(event: PointerEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
resizingCommitPanel = true;
|
resizingCommitPanel = true;
|
||||||
@@ -1715,50 +1685,14 @@
|
|||||||
persistHistoryAsideWidth(historyAsideWidth);
|
persistHistoryAsideWidth(historyAsideWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startFileHistoryResize(event: PointerEvent) {
|
|
||||||
if (fileHistoryCollapsed) return;
|
|
||||||
event.preventDefault();
|
|
||||||
resizingFileHistory = true;
|
|
||||||
fileHistoryResizeStartX = event.clientX;
|
|
||||||
fileHistoryResizeStartWidth = fileHistoryWidth;
|
|
||||||
(event.currentTarget as HTMLElement).setPointerCapture(event.pointerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFileHistoryResizeMove(event: PointerEvent) {
|
|
||||||
if (!resizingFileHistory) return;
|
|
||||||
fileHistoryWidth = clampFileHistoryWidth(fileHistoryResizeStartWidth + (fileHistoryResizeStartX - event.clientX));
|
|
||||||
}
|
|
||||||
|
|
||||||
function endFileHistoryResize(event: PointerEvent) {
|
|
||||||
if (!resizingFileHistory) return;
|
|
||||||
resizingFileHistory = false;
|
|
||||||
persistFileHistoryWidth(fileHistoryWidth);
|
|
||||||
const target = event.currentTarget as HTMLElement;
|
|
||||||
if (target.hasPointerCapture(event.pointerId)) target.releasePointerCapture(event.pointerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFileHistoryResizeKeydown(event: KeyboardEvent) {
|
|
||||||
if (fileHistoryCollapsed || (event.key !== "ArrowLeft" && event.key !== "ArrowRight")) return;
|
|
||||||
event.preventDefault();
|
|
||||||
fileHistoryWidth = clampFileHistoryWidth(fileHistoryWidth + (event.key === "ArrowLeft" ? 20 : -20));
|
|
||||||
persistFileHistoryWidth(fileHistoryWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleFileHistoryCollapsed() {
|
|
||||||
fileHistoryCollapsed = !fileHistoryCollapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function revealFileHistory() {
|
|
||||||
fileHistoryCollapsed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideAndResetFileHistory() {
|
function hideAndResetFileHistory() {
|
||||||
fileHistoryRequestId += 1;
|
fileHistoryRequestId += 1;
|
||||||
cancelActiveFileHistoryLoad();
|
cancelActiveFileHistoryLoad();
|
||||||
activeFileHistoryRequestId = "";
|
activeFileHistoryRequestId = "";
|
||||||
fileHistoryLoading = false;
|
fileHistoryLoading = false;
|
||||||
|
fileHistoryError = "";
|
||||||
fileHistory = [];
|
fileHistory = [];
|
||||||
fileHistoryCollapsed = true;
|
fileHistoryDialogOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rememberRecentRepo(path: string) {
|
function rememberRecentRepo(path: string) {
|
||||||
@@ -1833,6 +1767,9 @@
|
|||||||
expandedExplorerPaths = new Set();
|
expandedExplorerPaths = new Set();
|
||||||
expandedCommitHashes = new Set();
|
expandedCommitHashes = new Set();
|
||||||
fileHistory = [];
|
fileHistory = [];
|
||||||
|
fileHistoryLoading = false;
|
||||||
|
fileHistoryError = "";
|
||||||
|
fileHistoryDialogOpen = false;
|
||||||
compareFrom = "";
|
compareFrom = "";
|
||||||
compareTo = "";
|
compareTo = "";
|
||||||
comparison = null;
|
comparison = null;
|
||||||
@@ -2004,10 +1941,7 @@
|
|||||||
if (selectedExplorerPath && !explorerPathExists(repoFiles, selectedExplorerPath)) {
|
if (selectedExplorerPath && !explorerPathExists(repoFiles, selectedExplorerPath)) {
|
||||||
selectedExplorerPath = "";
|
selectedExplorerPath = "";
|
||||||
selectedExplorerKind = "file";
|
selectedExplorerKind = "file";
|
||||||
cancelActiveFileHistoryLoad();
|
hideAndResetFileHistory();
|
||||||
activeFileHistoryRequestId = "";
|
|
||||||
fileHistoryLoading = false;
|
|
||||||
fileHistory = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2016,7 +1950,7 @@
|
|||||||
await refreshBranchList(path);
|
await refreshBranchList(path);
|
||||||
await refreshTags(path);
|
await refreshTags(path);
|
||||||
await refreshCommitHistory(path);
|
await refreshCommitHistory(path);
|
||||||
if (lastFileHistoryHeadHash !== previousHeadHash && !fileHistoryCollapsed) {
|
if (lastFileHistoryHeadHash !== previousHeadHash && fileHistoryDialogOpen) {
|
||||||
await refreshFileHistory(path);
|
await refreshFileHistory(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2031,7 +1965,9 @@
|
|||||||
void cancelFileHistory(requestId).catch(() => {});
|
void cancelFileHistory(requestId).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshFileHistory(path = activeRepoPath, file = selectedExplorerPath) {
|
async function refreshFileHistory(path = activeRepoPath, file = selectedExplorerPath, force = false) {
|
||||||
|
if (!force && !fileHistoryDialogOpen && !globalSearchOpen) return;
|
||||||
|
|
||||||
const requestId = ++fileHistoryRequestId;
|
const requestId = ++fileHistoryRequestId;
|
||||||
cancelActiveFileHistoryLoad();
|
cancelActiveFileHistoryLoad();
|
||||||
|
|
||||||
@@ -2045,6 +1981,7 @@
|
|||||||
const historyRequestId = `file-history-${requestId}-${Date.now()}`;
|
const historyRequestId = `file-history-${requestId}-${Date.now()}`;
|
||||||
activeFileHistoryRequestId = historyRequestId;
|
activeFileHistoryRequestId = historyRequestId;
|
||||||
fileHistoryLoading = true;
|
fileHistoryLoading = true;
|
||||||
|
fileHistoryError = "";
|
||||||
fileHistory = [];
|
fileHistory = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2056,7 +1993,10 @@
|
|||||||
const message = errorToMessage(error);
|
const message = errorToMessage(error);
|
||||||
if (requestId === fileHistoryRequestId && activeFileHistoryRequestId === historyRequestId) {
|
if (requestId === fileHistoryRequestId && activeFileHistoryRequestId === historyRequestId) {
|
||||||
fileHistory = [];
|
fileHistory = [];
|
||||||
if (!isCancellationMessage(message)) errorMessage = message;
|
if (!isCancellationMessage(message)) {
|
||||||
|
fileHistoryError = message;
|
||||||
|
if (!fileHistoryDialogOpen) errorMessage = message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (requestId === fileHistoryRequestId && activeFileHistoryRequestId === historyRequestId) {
|
if (requestId === fileHistoryRequestId && activeFileHistoryRequestId === historyRequestId) {
|
||||||
@@ -3718,19 +3658,37 @@
|
|||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads history for a selected explorer node without blocking the rest of the UI
|
// Loads history without blocking the rest of the UI. A request id guards
|
||||||
// (isBusy/runOperation would disable every button in the app while this awaits).
|
// against a slower, stale request overwriting a newer file selection.
|
||||||
// A request id guards against a slower, stale request overwriting a newer selection.
|
|
||||||
async function loadSelectedFileHistory(path: string, repo = activeRepoPath) {
|
async function loadSelectedFileHistory(path: string, repo = activeRepoPath) {
|
||||||
await refreshFileHistory(repo, path);
|
await refreshFileHistory(repo, path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openFileHistoryDialog(node: ExplorerNode) {
|
||||||
|
if (!activeRepoPath || node.kind !== "file" || !node.tracked) return;
|
||||||
|
selectedExplorerPath = node.path;
|
||||||
|
selectedExplorerKind = "file";
|
||||||
|
fileHistoryDialogOpen = true;
|
||||||
|
fileHistoryError = "";
|
||||||
|
void loadSelectedFileHistory(node.path);
|
||||||
|
trackEvent("file_history_opened", {
|
||||||
|
source: "explorer_context_menu",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFileHistoryDialog() {
|
||||||
|
fileHistoryRequestId += 1;
|
||||||
|
cancelActiveFileHistoryLoad();
|
||||||
|
activeFileHistoryRequestId = "";
|
||||||
|
fileHistoryLoading = false;
|
||||||
|
fileHistoryError = "";
|
||||||
|
fileHistoryDialogOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectExplorerNode(node: ExplorerNode) {
|
async function selectExplorerNode(node: ExplorerNode) {
|
||||||
if (!activeRepoPath) return;
|
if (!activeRepoPath) return;
|
||||||
selectedExplorerPath = node.path;
|
selectedExplorerPath = node.path;
|
||||||
selectedExplorerKind = node.kind;
|
selectedExplorerKind = node.kind;
|
||||||
revealFileHistory();
|
|
||||||
void loadSelectedFileHistory(node.path);
|
|
||||||
trackEvent("explorer_node_selected", {
|
trackEvent("explorer_node_selected", {
|
||||||
kind: node.kind,
|
kind: node.kind,
|
||||||
tracked: node.tracked ? 1 : 0,
|
tracked: node.tracked ? 1 : 0,
|
||||||
@@ -3742,7 +3700,6 @@
|
|||||||
selectedExplorerPath = file.path;
|
selectedExplorerPath = file.path;
|
||||||
selectedExplorerKind = "file";
|
selectedExplorerKind = "file";
|
||||||
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
expandedExplorerPaths = new Set([...expandedExplorerPaths, ...explorerParentFolders(file.path)]);
|
||||||
revealFileHistory();
|
|
||||||
|
|
||||||
void loadSelectedFileHistory(file.path);
|
void loadSelectedFileHistory(file.path);
|
||||||
trackEvent("explorer_file_selected", {
|
trackEvent("explorer_file_selected", {
|
||||||
@@ -3767,8 +3724,6 @@
|
|||||||
if (!activeRepoPath || node.kind !== "file") return;
|
if (!activeRepoPath || node.kind !== "file") return;
|
||||||
selectedExplorerPath = node.path;
|
selectedExplorerPath = node.path;
|
||||||
selectedExplorerKind = "file";
|
selectedExplorerKind = "file";
|
||||||
revealFileHistory();
|
|
||||||
void loadSelectedFileHistory(node.path);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await openRepositoryFile(activeRepoPath, node.path);
|
await openRepositoryFile(activeRepoPath, node.path);
|
||||||
@@ -4012,7 +3967,8 @@
|
|||||||
helpOpen = false;
|
helpOpen = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.key === "Escape" && repoTabContextMenu) closeRepoTabContextMenu();
|
if (event.key === "Escape" && fileHistoryDialogOpen && !isBusy) closeFileHistoryDialog();
|
||||||
|
else if (event.key === "Escape" && repoTabContextMenu) closeRepoTabContextMenu();
|
||||||
else if (event.key === "Escape" && pendingDiscard && !isBusy) closeDiscardConfirm();
|
else if (event.key === "Escape" && pendingDiscard && !isBusy) closeDiscardConfirm();
|
||||||
else if (event.key === "Escape" && compareDialogOpen) closeCompareDialog();
|
else if (event.key === "Escape" && compareDialogOpen) closeCompareDialog();
|
||||||
else if (event.key === "Escape" && newBranchCommit) newBranchCommit = null;
|
else if (event.key === "Escape" && newBranchCommit) newBranchCommit = null;
|
||||||
@@ -4374,7 +4330,7 @@
|
|||||||
<section
|
<section
|
||||||
class="workspace"
|
class="workspace"
|
||||||
aria-label="Git workspace"
|
aria-label="Git workspace"
|
||||||
style="--left-sidebar-width: {leftSidebarWidth}px; --history-aside-width: {historyAsideWidth}px; --file-history-width: {fileHistoryWidth}px;"
|
style="--left-sidebar-width: {leftSidebarWidth}px; --history-aside-width: {historyAsideWidth}px;"
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Left sidebar: branches + explorer -->
|
<!-- Left sidebar: branches + explorer -->
|
||||||
@@ -4477,6 +4433,7 @@
|
|||||||
onCollapseAllFolders={collapseAllExplorerFolders}
|
onCollapseAllFolders={collapseAllExplorerFolders}
|
||||||
onSelectNode={selectExplorerNode}
|
onSelectNode={selectExplorerNode}
|
||||||
onOpenFile={openFileFromExplorer}
|
onOpenFile={openFileFromExplorer}
|
||||||
|
onFileHistory={openFileHistoryDialog}
|
||||||
onBlame={openBlame}
|
onBlame={openBlame}
|
||||||
collapsed={explorerPanelCollapsed}
|
collapsed={explorerPanelCollapsed}
|
||||||
onToggleCollapsed={toggleExplorerPanelCollapsed}
|
onToggleCollapsed={toggleExplorerPanelCollapsed}
|
||||||
@@ -4601,8 +4558,8 @@
|
|||||||
onkeydown={onHistoryAsideResizeKeydown}
|
onkeydown={onHistoryAsideResizeKeydown}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- Right sidebar: commit graph + file history -->
|
<!-- Right sidebar: commit graph -->
|
||||||
<aside class="history-aside" class:file-history-collapsed={fileHistoryCollapsed} aria-label="Commit history">
|
<aside class="history-aside" aria-label="Commit history">
|
||||||
<HistoryPanel
|
<HistoryPanel
|
||||||
{commits}
|
{commits}
|
||||||
{localBranchNames}
|
{localBranchNames}
|
||||||
@@ -4627,36 +4584,6 @@
|
|||||||
expandedCommitHashes = next;
|
expandedCommitHashes = next;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
||||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
||||||
<div
|
|
||||||
class="file-history-resize-handle"
|
|
||||||
class:resizing={resizingFileHistory}
|
|
||||||
role="separator"
|
|
||||||
aria-orientation="vertical"
|
|
||||||
aria-label="Resize file history width"
|
|
||||||
aria-valuenow={fileHistoryWidth}
|
|
||||||
aria-valuemin={FILE_HISTORY_MIN_WIDTH}
|
|
||||||
aria-valuemax={FILE_HISTORY_MAX_WIDTH}
|
|
||||||
tabindex={fileHistoryCollapsed ? -1 : 0}
|
|
||||||
onpointerdown={startFileHistoryResize}
|
|
||||||
onpointermove={onFileHistoryResizeMove}
|
|
||||||
onpointerup={endFileHistoryResize}
|
|
||||||
onpointercancel={endFileHistoryResize}
|
|
||||||
onkeydown={onFileHistoryResizeKeydown}
|
|
||||||
></div>
|
|
||||||
<FileHistoryPanel
|
|
||||||
{fileHistory}
|
|
||||||
{selectedExplorerPath}
|
|
||||||
selectedExplorerLabel={selectedExplorerPath ? `${selectedExplorerKind === "folder" ? "Folder" : "File"} history` : "File history"}
|
|
||||||
{hasRepository}
|
|
||||||
{isBusy}
|
|
||||||
isLoading={fileHistoryLoading}
|
|
||||||
onDiff={diffSelectedFileFromCommit}
|
|
||||||
onRestore={restoreSelectedFileFromCommit}
|
|
||||||
collapsed={fileHistoryCollapsed}
|
|
||||||
onToggleCollapsed={toggleFileHistoryCollapsed}
|
|
||||||
/>
|
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -4772,6 +4699,21 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if fileHistoryDialogOpen}
|
||||||
|
{#await import("./lib/components/FileHistoryDialog.svelte") then module}
|
||||||
|
<module.default
|
||||||
|
{fileHistory}
|
||||||
|
filePath={selectedExplorerPath}
|
||||||
|
{isBusy}
|
||||||
|
isLoading={fileHistoryLoading}
|
||||||
|
error={fileHistoryError}
|
||||||
|
onDiff={diffSelectedFileFromCommit}
|
||||||
|
onRestore={restoreSelectedFileFromCommit}
|
||||||
|
onClose={closeFileHistoryDialog}
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if globalSearchOpen}
|
{#if globalSearchOpen}
|
||||||
{#await import("./lib/components/GlobalSearchDialog.svelte") then module}
|
{#await import("./lib/components/GlobalSearchDialog.svelte") then module}
|
||||||
<module.default
|
<module.default
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { ChevronLeft, ChevronRight, GitCompare, History, RotateCcw } from "@lucide/svelte";
|
|
||||||
import iconUrl from "../../../src-tauri/icons/icon.png";
|
|
||||||
import type { GitCommit } from "../types";
|
|
||||||
|
|
||||||
const commitDateFormatter = new Intl.DateTimeFormat(undefined, {
|
|
||||||
dateStyle: "medium",
|
|
||||||
timeStyle: "short",
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
fileHistory: GitCommit[];
|
|
||||||
selectedExplorerPath: string;
|
|
||||||
selectedExplorerLabel: string;
|
|
||||||
hasRepository: boolean;
|
|
||||||
isBusy: boolean;
|
|
||||||
isLoading?: boolean;
|
|
||||||
onDiff: (commit: GitCommit) => void;
|
|
||||||
onRestore: (commit: GitCommit) => void;
|
|
||||||
collapsed?: boolean;
|
|
||||||
onToggleCollapsed?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let {
|
|
||||||
fileHistory = [],
|
|
||||||
selectedExplorerPath = "",
|
|
||||||
selectedExplorerLabel = "File history",
|
|
||||||
hasRepository = false,
|
|
||||||
isBusy = false,
|
|
||||||
isLoading = false,
|
|
||||||
onDiff = () => {},
|
|
||||||
onRestore = () => {},
|
|
||||||
collapsed = false,
|
|
||||||
onToggleCollapsed = () => {},
|
|
||||||
}: Props = $props();
|
|
||||||
|
|
||||||
function formatCommitDate(value: string): string {
|
|
||||||
const date = new Date(value);
|
|
||||||
if (Number.isNaN(date.getTime())) return value;
|
|
||||||
return commitDateFormatter.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 file-history-panel grid grid-rows-[auto_1fr] overflow-hidden" class:collapsed aria-label="Selected file history">
|
|
||||||
{#if collapsed}
|
|
||||||
<button
|
|
||||||
class="file-history-rail-toggle"
|
|
||||||
type="button"
|
|
||||||
onclick={onToggleCollapsed}
|
|
||||||
aria-expanded={!collapsed}
|
|
||||||
title="Expand file history"
|
|
||||||
aria-label="Expand file history panel"
|
|
||||||
>
|
|
||||||
<ChevronLeft size={16} aria-hidden="true" />
|
|
||||||
<span>File history</span>
|
|
||||||
{#if selectedExplorerPath}
|
|
||||||
<small>{fileHistory.length}</small>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<div class="section-head file-history-head">
|
|
||||||
<div class="file-history-heading">
|
|
||||||
<span class="eyebrow">{selectedExplorerLabel}</span>
|
|
||||||
<h2
|
|
||||||
class="file-history-name"
|
|
||||||
use:pathTooltip={selectedExplorerPath}
|
|
||||||
>{selectedExplorerPath ? fileName(selectedExplorerPath) : "No file"}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="file-history-head-actions">
|
|
||||||
<span class="pill pill-count file-history-count">{fileHistory.length}</span>
|
|
||||||
<button
|
|
||||||
class="file-history-toggle panel-collapse-toggle"
|
|
||||||
type="button"
|
|
||||||
onclick={onToggleCollapsed}
|
|
||||||
aria-expanded={!collapsed}
|
|
||||||
title="Collapse file history"
|
|
||||||
aria-label="Collapse file history panel"
|
|
||||||
>
|
|
||||||
<ChevronRight size={14} aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</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 isLoading}
|
|
||||||
<div class="file-history-loading" role="status" aria-live="polite">
|
|
||||||
<div class="file-history-loading-mark" aria-hidden="true">
|
|
||||||
<span class="file-history-loading-halo"></span>
|
|
||||||
<svg class="file-history-loading-traces" viewBox="0 0 120 120">
|
|
||||||
<path class="fhl-trace fhl-trace-main" d="M14 82 C38 60, 48 58, 60 60 S84 66, 106 36" />
|
|
||||||
<path class="fhl-trace fhl-trace-branch" d="M28 36 C52 48, 70 70, 91 94" />
|
|
||||||
</svg>
|
|
||||||
<img src={iconUrl} alt="" class="file-history-loading-icon" />
|
|
||||||
</div>
|
|
||||||
<span class="file-history-loading-label">Loading history…</span>
|
|
||||||
<span class="file-history-loading-bar" aria-hidden="true"><span></span></span>
|
|
||||||
</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 file-history-row">
|
|
||||||
<div class="commit-line">
|
|
||||||
<History size={16} aria-hidden="true" />
|
|
||||||
<div class="commit-line-text">
|
|
||||||
<strong title={item.summary}>{item.summary}</strong>
|
|
||||||
<span>{item.short_hash} - {item.author_name}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="commit-actions file-history-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}
|
|
||||||
{/if}
|
|
||||||
</section>
|
|
||||||
Reference in New Issue
Block a user