feat(blame): add file blame dialog with git porcelain parsing
This change introduces a new backend command to fetch file blame using git's line-porcelain output and returns structured per-line metadata. The UI adds a BlameDialog that groups lines by commit, highlights uncommitted changes, and styles the dialog to match the updated theme. - Add get_file_blame command and parsing with uncommitted detection - Wire BlameDialog into the explorer file node actions - Add blame UI component and supporting types and styles
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import TitleBar from "./lib/TitleBar.svelte";
|
||||
import AiSettingsDialog from "./lib/components/AiSettingsDialog.svelte";
|
||||
import BranchDeleteConfirmDialog from "./lib/components/BranchDeleteConfirmDialog.svelte";
|
||||
import BlameDialog from "./lib/components/BlameDialog.svelte";
|
||||
import BranchPanel from "./lib/components/BranchPanel.svelte";
|
||||
import CloneRepositoryDialog from "./lib/components/CloneRepositoryDialog.svelte";
|
||||
import CommitPanel from "./lib/components/CommitPanel.svelte";
|
||||
@@ -50,6 +51,7 @@
|
||||
diffFileAgainstWorkingTree,
|
||||
compareFileToParent,
|
||||
fetchRemote,
|
||||
getFileBlame,
|
||||
getStatus,
|
||||
lastCommitMessage,
|
||||
listBranches,
|
||||
@@ -97,6 +99,7 @@
|
||||
ConflictFile,
|
||||
ExplorerNode,
|
||||
ExplorerNodeKind,
|
||||
GitBlameLine,
|
||||
GitBranch as GitBranchInfo,
|
||||
GitCommit,
|
||||
GitCommitFile,
|
||||
@@ -218,6 +221,11 @@
|
||||
let linePatchText = "";
|
||||
let linePatchLoading = false;
|
||||
let linePatchError = "";
|
||||
let blameOpen = false;
|
||||
let blameFilePath = "";
|
||||
let blameLines: GitBlameLine[] = [];
|
||||
let blameLoading = false;
|
||||
let blameError = "";
|
||||
let pendingDiscard: PendingDiscard | null = null;
|
||||
let globalSearchOpen = false;
|
||||
let lastSearchQuery = "";
|
||||
@@ -1921,6 +1929,33 @@
|
||||
linePatchError = "";
|
||||
}
|
||||
|
||||
async function openBlame(node: ExplorerNode) {
|
||||
if (!activeRepoPath || node.kind !== "file") return;
|
||||
blameOpen = true;
|
||||
blameFilePath = node.path;
|
||||
blameLines = [];
|
||||
blameError = "";
|
||||
blameLoading = true;
|
||||
|
||||
try {
|
||||
const result = await getFileBlame(activeRepoPath, node.path);
|
||||
blameLines = result.lines;
|
||||
} catch (error) {
|
||||
blameError = errorToMessage(error);
|
||||
errorMessage = blameError;
|
||||
} finally {
|
||||
blameLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function closeBlame() {
|
||||
if (isBusy) return;
|
||||
blameOpen = false;
|
||||
blameFilePath = "";
|
||||
blameLines = [];
|
||||
blameError = "";
|
||||
}
|
||||
|
||||
function patchOperationLabel(action: PatchApplyAction, file: GitFileStatus): string {
|
||||
switch (action) {
|
||||
case "stage":
|
||||
@@ -2726,6 +2761,7 @@
|
||||
onCollapseAllFolders={collapseAllExplorerFolders}
|
||||
onSelectNode={selectExplorerNode}
|
||||
onOpenFile={openFileFromExplorer}
|
||||
onBlame={openBlame}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
@@ -2886,6 +2922,17 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if blameOpen}
|
||||
<BlameDialog
|
||||
filePath={blameFilePath}
|
||||
lines={blameLines}
|
||||
{isBusy}
|
||||
isLoading={blameLoading}
|
||||
error={blameError}
|
||||
onClose={closeBlame}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if pendingDiscard}
|
||||
<DiscardConfirmDialog
|
||||
file={pendingDiscard.file}
|
||||
|
||||
Reference in New Issue
Block a user