diff --git a/README.md b/README.md
index 14ae5c27..4f847a41 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,3 @@
-> [!IMPORTANT]
-> We're re-writing the codebase, expect a delayed response to feedback until that's shipped. The current version on NPM is stable and we expect to be releasing alpha builds of V2 within days.
-> You can track progress [here](https://github.com/BloopAI/vibe-kanban/tree/deployments).
-
@@ -15,7 +11,7 @@
Get 10X more out of Claude Code, Gemini CLI, Codex, Amp and other coding agents...
-
+

diff --git a/crates/services/src/services/git.rs b/crates/services/src/services/git.rs
index e9b197c4..29557eca 100644
--- a/crates/services/src/services/git.rs
+++ b/crates/services/src/services/git.rs
@@ -342,26 +342,37 @@ impl GitService {
let mut file_diffs = Vec::new();
diff.foreach(
- &mut |delta, _progress| {
- // Skip unreadable entries
+ &mut |delta, _| {
if delta.status() == Delta::Unreadable {
return true;
}
- let old_file = delta
- .old_file()
- .path()
- .map(|path| self.create_file_details(path, &delta.old_file().id(), repo));
+ let status = delta.status();
- let new_file = delta
- .new_file()
- .path()
- .map(|path| self.create_file_details(path, &delta.new_file().id(), repo));
+ // Only build old_file for non-added entries
+ let old_file = if matches!(status, Delta::Added) {
+ None
+ } else {
+ delta
+ .old_file()
+ .path()
+ .map(|p| self.create_file_details(p, &delta.old_file().id(), repo))
+ };
+
+ // Only build new_file for non-deleted entries
+ let new_file = if matches!(status, Delta::Deleted) {
+ None
+ } else {
+ delta
+ .new_file()
+ .path()
+ .map(|p| self.create_file_details(p, &delta.new_file().id(), repo))
+ };
file_diffs.push(Diff {
old_file,
new_file,
- hunks: vec![], // Left empty as requested
+ hunks: vec![], // still empty
});
true
diff --git a/frontend/src/components/tasks/TaskDetails/DiffTab.tsx b/frontend/src/components/tasks/TaskDetails/DiffTab.tsx
index d06bca68..7c6c0dc3 100644
--- a/frontend/src/components/tasks/TaskDetails/DiffTab.tsx
+++ b/frontend/src/components/tasks/TaskDetails/DiffTab.tsx
@@ -1,5 +1,5 @@
import { generateDiffFile } from '@git-diff-view/file';
-import { useDiffStream } from '@/hooks/useDiffStream';
+import { useDiffEntries } from '@/hooks/useDiffEntries';
import { useMemo, useContext, useCallback, useState, useEffect } from 'react';
import { TaskSelectedAttemptContext } from '@/components/context/taskDetailsContext.ts';
import { Diff } from 'shared/types';
@@ -10,13 +10,13 @@ import DiffCard from '@/components/DiffCard';
function DiffTab() {
const { selectedAttempt } = useContext(TaskSelectedAttemptContext);
const [loading, setLoading] = useState(true);
- const { data, error } = useDiffStream(selectedAttempt?.id ?? null, true);
+ const { diffs, error } = useDiffEntries(selectedAttempt?.id ?? null, true);
useEffect(() => {
- if (data && Object.keys(data?.entries).length > 0 && loading) {
+ if (diffs.length > 0 && loading) {
setLoading(false);
}
- }, [data]);
+ }, [diffs, loading]);
const createDiffFile = useCallback((diff: Diff) => {
const oldFileName = diff.oldFile?.fileName || 'old';
@@ -42,12 +42,10 @@ function DiffTab() {
}, []);
const diffFiles = useMemo(() => {
- if (!data) return [];
- return Object.values(data.entries)
- .filter((e: any) => e?.type === 'DIFF')
- .map((e: any) => createDiffFile(e.content as Diff))
+ return diffs
+ .map((diff) => createDiffFile(diff))
.filter((diffFile) => diffFile !== null);
- }, [data, createDiffFile]);
+ }, [diffs, createDiffFile]);
if (error) {
return (
diff --git a/frontend/src/hooks/useDiffEntries.ts b/frontend/src/hooks/useDiffEntries.ts
new file mode 100644
index 00000000..42db1def
--- /dev/null
+++ b/frontend/src/hooks/useDiffEntries.ts
@@ -0,0 +1,27 @@
+import { useMemo } from 'react';
+import { useDiffStream } from './useDiffStream';
+import type { Diff, PatchType } from 'shared/types';
+
+interface UseDiffEntriesResult {
+ diffs: Diff[];
+ isConnected: boolean;
+ error: string | null;
+}
+
+export const useDiffEntries = (
+ attemptId: string | null,
+ enabled: boolean
+): UseDiffEntriesResult => {
+ const { data, isConnected, error } = useDiffStream(attemptId, enabled);
+
+ const diffs = useMemo(() => {
+ if (!data) return [];
+ return Object.values(data.entries)
+ .filter(
+ (e): e is Extract => e?.type === 'DIFF'
+ )
+ .map((e) => e.content);
+ }, [data]);
+
+ return { diffs, isConnected, error };
+};
diff --git a/frontend/src/hooks/useDiffStream.ts b/frontend/src/hooks/useDiffStream.ts
index eb142d0f..ae9dba0b 100644
--- a/frontend/src/hooks/useDiffStream.ts
+++ b/frontend/src/hooks/useDiffStream.ts
@@ -1,9 +1,9 @@
import { useCallback } from 'react';
-import type { Diff } from 'shared/types';
+import type { PatchType } from 'shared/types';
import { useJsonPatchStream } from './useJsonPatchStream';
interface DiffState {
- entries: Record;
+ entries: Record;
}
interface UseDiffStreamResult {
diff --git a/frontend/src/styles/diff-style-overrides.css b/frontend/src/styles/diff-style-overrides.css
index 38a6b459..ffec26de 100644
--- a/frontend/src/styles/diff-style-overrides.css
+++ b/frontend/src/styles/diff-style-overrides.css
@@ -366,7 +366,11 @@
--diff-border--: var(--background);
--diff-add-content--: hsl(var(--console-success) / 0.2);
--diff-del-content--: hsl(var(--console-error) / 0.2);
- --diff-add-lineNumber--: hsl(var(--console-success) / 0.2);
+ --diff-add-lineNumber--: color-mix(
+ in srgb,
+ hsl(var(--console-success)) 20%,
+ hsl(var(--background)) 80%
+ );
--diff-del-lineNumber--: hsl(var(--console-error) / 0.2);
--diff-plain-content--: hsl(var(--background));
--diff-expand-content--: hsl(var(--background));