change to dark mode

This commit is contained in:
2026-06-28 02:09:09 +02:00
parent 5a0a8bdecb
commit d3d52a5432
4 changed files with 596 additions and 351 deletions
+2 -1
View File
@@ -19,7 +19,8 @@
"Bash(grep -qE \"\\(Running \\\\`target|error\\\\[|panicked|terminated\\)\" /tmp/claude-1000/-mnt-data-Development-GitLite/71a8b49f-ac61-4ad9-aaba-dc0219044038/tasks/bt54rikhh.output)",
"Bash(grep -qE \"\\(Running \\\\`target|error\\\\[|panicked|terminated\\)\" /tmp/claude-1000/-mnt-data-Development-GitLite/71a8b49f-ac61-4ad9-aaba-dc0219044038/tasks/b7nyub68s.output)",
"Bash(npm install *)",
"Bash(cargo check *)"
"Bash(cargo check *)",
"Bash(npx vite *)"
]
}
}
+11 -14
View File
@@ -636,7 +636,7 @@
/>
</aside>
<!-- Center: summary + status + commit + file history + compare -->
<!-- Center: summary + status + commit + compare -->
<section class="main-panel" aria-label="Repository status">
<div class="repo-summary">
<div class="repo-meta">
@@ -679,18 +679,6 @@
/>
</div>
<div class="file-history-section">
<FileHistoryPanel
{fileHistory}
{selectedExplorerPath}
selectedExplorerLabel={selectedExplorerPath ? `${selectedExplorerKind === "folder" ? "Folder" : "File"} history` : "File history"}
{hasRepository}
{isBusy}
onDiff={diffSelectedFileFromCommit}
onRestore={restoreSelectedFileFromCommit}
/>
</div>
<ComparePanel
{commits}
{hasRepository}
@@ -707,7 +695,7 @@
/>
</section>
<!-- Right sidebar: commit history -->
<!-- Right sidebar: commit graph + file history -->
<aside class="history-aside" aria-label="Commit history">
<HistoryPanel
{commits}
@@ -722,6 +710,15 @@
expandedCommitHashes = next;
}}
/>
<FileHistoryPanel
{fileHistory}
{selectedExplorerPath}
selectedExplorerLabel={selectedExplorerPath ? `${selectedExplorerKind === "folder" ? "Folder" : "File"} history` : "File history"}
{hasRepository}
{isBusy}
onDiff={diffSelectedFileFromCommit}
onRestore={restoreSelectedFileFromCommit}
/>
</aside>
</section>
</div>
+436 -281
View File
File diff suppressed because it is too large Load Diff
+147 -55
View File
@@ -1,8 +1,14 @@
<script lang="ts">
import { ArrowRight, X } from "@lucide/svelte";
import { ArrowRight, FileCode, X } from "@lucide/svelte";
import type { GitCommitComparison, GitDiffFile, FileStatusKind } from "../types";
type DiffLineKind = "meta" | "hunk" | "add" | "del" | "context";
type SplitRow =
| { type: "span"; kind: "meta" | "hunk"; text: string }
| {
type: "pair";
leftNum?: number; leftText?: string; leftKind: "del" | "context" | "empty";
rightNum?: number; rightText?: string; rightKind: "add" | "context" | "empty";
};
interface Props {
comparison: GitCommitComparison;
@@ -20,69 +26,115 @@
onSelectFile = () => {},
}: Props = $props();
function statusLabel(kind: FileStatusKind): string {
return kind;
}
function displayDiffFile(file: GitDiffFile): string {
return file.old_path ? `${file.old_path} -> ${file.path}` : file.path;
}
function diffLineKind(line: string): DiffLineKind {
if (line.startsWith("diff ") || line.startsWith("index ") || line.startsWith("--- ") ||
line.startsWith("+++ ") || line.startsWith("new file") || line.startsWith("deleted file") ||
line.startsWith("rename ") || line.startsWith("similarity ")) return "meta";
if (line.startsWith("@@")) return "hunk";
if (line.startsWith("+")) return "add";
if (line.startsWith("-")) return "del";
return "context";
}
function diffLines(patch: string): { kind: DiffLineKind; text: string }[] {
return patch.replace(/\n$/, "").split("\n").map((text) => ({ kind: diffLineKind(text), text }));
}
function stripDiffPathPrefix(value: string): string {
const trimmed = value.trim();
if (trimmed === "/dev/null") return trimmed;
if (trimmed.startsWith("a/") || trimmed.startsWith("b/")) return trimmed.slice(2);
return trimmed;
}
function patchFilePath(segment: string): string {
let plusPath = "";
let minusPath = "";
for (const line of segment.split("\n")) {
if (line.startsWith("+++ ")) plusPath = stripDiffPathPrefix(line.slice(4));
else if (line.startsWith("--- ")) minusPath = stripDiffPathPrefix(line.slice(4));
else if (line.startsWith("@@")) break;
}
return (plusPath && plusPath !== "/dev/null") ? plusPath : minusPath;
return file.old_path ? `${file.old_path} ${file.path}` : file.path;
}
function buildDiffByPath(patch: string): Map<string, string> {
const map = new Map<string, string>();
if (!patch.trim()) return map;
const segments: string[] = [];
let current: string[] = [];
for (const line of patch.split("\n")) {
if (line.startsWith("diff --git ") && current.length > 0) {
segments.push(current.join("\n"));
const seg = current.join("\n");
const p = segmentPath(seg);
if (p) map.set(p, seg);
current = [];
}
current.push(line);
}
if (current.length > 0) segments.push(current.join("\n"));
for (const segment of segments) {
const path = patchFilePath(segment);
if (path) map.set(path, segment);
if (current.length > 0) {
const seg = current.join("\n");
const p = segmentPath(seg);
if (p) map.set(p, seg);
}
return map;
}
let diffByPath = $derived(buildDiffByPath(comparison.patch));
let selectedDiffFile = $derived(comparison.files.find((f) => f.path === selectedDiffPath) ?? null);
let selectedDiffPatch = $derived(selectedDiffFile ? (diffByPath.get(selectedDiffFile.path) ?? "") : "");
function segmentPath(seg: string): string {
let plus = "";
let minus = "";
for (const line of seg.split("\n")) {
if (line.startsWith("+++ ")) {
const r = line.slice(4).trim();
plus = r.startsWith("b/") ? r.slice(2) : r;
} else if (line.startsWith("--- ")) {
const r = line.slice(4).trim();
minus = r.startsWith("a/") ? r.slice(2) : r;
} else if (line.startsWith("@@")) break;
}
return (plus && plus !== "/dev/null") ? plus : minus;
}
function buildSplitRows(patch: string): SplitRow[] {
if (!patch.trim()) return [];
const lines = patch.replace(/\n$/, "").split("\n");
const rows: SplitRow[] = [];
let leftNum = 0;
let rightNum = 0;
const dels: string[] = [];
const adds: string[] = [];
function flush() {
if (dels.length === 0 && adds.length === 0) return;
const count = Math.max(dels.length, adds.length);
for (let i = 0; i < count; i++) {
const hasDel = i < dels.length;
const hasAdd = i < adds.length;
if (hasDel) leftNum++;
if (hasAdd) rightNum++;
rows.push({
type: "pair",
leftNum: hasDel ? leftNum : undefined,
leftText: hasDel ? dels[i].slice(1) : undefined,
leftKind: hasDel ? "del" : "empty",
rightNum: hasAdd ? rightNum : undefined,
rightText: hasAdd ? adds[i].slice(1) : undefined,
rightKind: hasAdd ? "add" : "empty",
});
}
dels.length = 0;
adds.length = 0;
}
for (const line of lines) {
const isMeta =
line.startsWith("diff ") || line.startsWith("index ") ||
line.startsWith("--- ") || line.startsWith("+++ ") ||
line.startsWith("new file") || line.startsWith("deleted file") ||
line.startsWith("rename ") || line.startsWith("similarity ");
if (isMeta) {
flush();
rows.push({ type: "span", kind: "meta", text: line });
} else if (line.startsWith("@@")) {
flush();
const m = line.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (m) { leftNum = parseInt(m[1]) - 1; rightNum = parseInt(m[2]) - 1; }
rows.push({ type: "span", kind: "hunk", text: line });
} else if (line.startsWith("-")) {
dels.push(line);
} else if (line.startsWith("+")) {
adds.push(line);
} else {
flush();
leftNum++;
rightNum++;
rows.push({
type: "pair",
leftNum, leftText: line.slice(1), leftKind: "context",
rightNum, rightText: line.slice(1), rightKind: "context",
});
}
}
flush();
return rows;
}
let diffByPath = $derived(buildDiffByPath(comparison.patch));
let selectedFile = $derived(comparison.files.find((f) => f.path === selectedDiffPath) ?? null);
let selectedPatch = $derived(selectedFile ? (diffByPath.get(selectedFile.path) ?? "") : "");
let splitRows = $derived(buildSplitRows(selectedPatch));
</script>
<div
@@ -91,12 +143,13 @@
onclick={(e) => { if (e.target === e.currentTarget) onClose(); }}
>
<div class="dialog" role="dialog" aria-modal="true" aria-label="Commit comparison" tabindex="-1">
<header class="dialog-header">
<div>
<span class="eyebrow">Compare</span>
<h2 class="dialog-range">
<span class="hash">{comparison.from_short}</span>
<ArrowRight size={16} aria-hidden="true" />
<ArrowRight size={14} aria-hidden="true" />
<span class="hash">{comparison.to_short}</span>
</h2>
</div>
@@ -106,9 +159,11 @@
</header>
{#if comparison.files.length === 0}
<div class="blank-state">No differences to show — these versions are identical.</div>
<div class="blank-state">No differences — these versions are identical.</div>
{:else}
<div class="dialog-body">
<!-- File list -->
<aside class="dialog-files" aria-label="Changed files">
{#each comparison.files as file (`${file.old_path ?? ""}:${file.path}`)}
<button
@@ -118,7 +173,7 @@
onclick={() => onSelectFile(file)}
title={displayDiffFile(file)}
>
<span class={`status-badge ${file.status}`}>{statusLabel(file.status)}</span>
<span class={`status-badge ${file.status}`}>{file.status}</span>
<strong>{displayDiffFile(file)}</strong>
<span class="diff-counts">
<span class="adds">+{file.additions}</span>
@@ -128,15 +183,52 @@
{/each}
</aside>
<div class="dialog-diff" aria-label="File diff">
{#if !selectedDiffFile}
<!-- Diff pane -->
<div class="dialog-diff">
{#if !selectedFile}
<div class="blank-state">Select a file to see its changes.</div>
{:else if selectedDiffPatch.trim().length === 0}
{:else if splitRows.length === 0}
<div class="blank-state">No textual changes for this file.</div>
{:else}
<pre class="diff-view" aria-label="Unified diff">{#each diffLines(selectedDiffPatch) as line}<span class={`diff-line ${line.kind}`}>{line.text || " "}</span>{/each}</pre>
<!-- Path bar -->
<div class="diff-header">
<FileCode size={13} aria-hidden="true" />
<span>{displayDiffFile(selectedFile)}</span>
<span class="diff-counts" style="margin-left: auto; flex-shrink: 0;">
<span class="adds">+{selectedFile.additions}</span>
<span class="dels">-{selectedFile.deletions}</span>
</span>
</div>
<!-- Column headers -->
<div class="split-col-headers">
<div class="split-col-label">
<span>Before</span>
<span class="split-col-hash">{comparison.from_short}</span>
</div>
<div class="split-col-label">
<span>After</span>
<span class="split-col-hash">{comparison.to_short}</span>
</div>
</div>
<!-- Split diff grid -->
<div class="split-diff" role="table" aria-label="Side-by-side diff">
{#each splitRows as row, i (i)}
{#if row.type === "span"}
<div class="split-span split-{row.kind}">{row.text}</div>
{:else}
<div class="split-num" class:del={row.leftKind === "del"} class:empty={row.leftKind === "empty"}>{row.leftNum ?? ""}</div>
<div class="split-cell" class:del={row.leftKind === "del"} class:empty={row.leftKind === "empty"}>{row.leftText ?? " "}</div>
<div class="split-divider"></div>
<div class="split-num" class:add={row.rightKind === "add"} class:empty={row.rightKind === "empty"}>{row.rightNum ?? ""}</div>
<div class="split-cell" class:add={row.rightKind === "add"} class:empty={row.rightKind === "empty"}>{row.rightText ?? " "}</div>
{/if}
{/each}
</div>
{/if}
</div>
</div>
{/if}
</div>