Diff reload is quite jumpy (vibe-kanban) (#104)
* Perfect! I have successfully fixed the jumpy diff reload issue. Here's a summary of what I implemented: ## Fix Summary The problem was that when the coding agent was running, the diff refreshed every 2 seconds and showed a full loading spinner that replaced the entire diff content, creating a jarring user experience. **Key Changes Made:** 1. **Added separate loading state** - Created `isBackgroundRefreshing` state to track background updates separately from initial loading 2. **Modified `fetchDiff` function** - Added an `isBackgroundRefresh` parameter to differentiate between initial loads and background refreshes 3. **Updated diff rendering** - The full loading spinner now only appears on initial load, while background refreshes show a subtle spinning indicator with "Updating..." text 4. **Improved user experience** - The diff content remains visible during background refreshes, with just a small indicator showing the update is in progress **The fix ensures:** - Initial diff loads still show the full loading spinner for proper UX - Background refreshes (every 2 seconds during coding agent execution) show a subtle updating indicator - The diff content remains visible and stable during background updates - No more jarring disappearing/reappearing of the entire diff section The build and linting all pass successfully, confirming the implementation is solid and ready for use. * Fixed! The formatting issues in `TaskDetailsPanel.tsx` have been resolved. All files now use Prettier code style.
This commit is contained in:
committed by
GitHub
parent
76d03564ad
commit
5368d827ae
@@ -88,6 +88,7 @@ export function TaskDetailsPanel({
|
||||
const [diff, setDiff] = useState<WorktreeDiff | null>(null);
|
||||
const [diffLoading, setDiffLoading] = useState(true);
|
||||
const [diffError, setDiffError] = useState<string | null>(null);
|
||||
const [isBackgroundRefreshing, setIsBackgroundRefreshing] = useState(false);
|
||||
const [expandedSections, setExpandedSections] = useState<Set<string>>(
|
||||
new Set()
|
||||
);
|
||||
@@ -132,43 +133,54 @@ export function TaskDetailsPanel({
|
||||
const diffLoadingRef = useRef(false);
|
||||
|
||||
// Fetch diff when attempt changes
|
||||
const fetchDiff = useCallback(async () => {
|
||||
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) {
|
||||
setDiff(null);
|
||||
setDiffLoading(false);
|
||||
return;
|
||||
}
|
||||
const fetchDiff = useCallback(
|
||||
async (isBackgroundRefresh = false) => {
|
||||
if (!projectId || !selectedAttempt?.id || !selectedAttempt?.task_id) {
|
||||
setDiff(null);
|
||||
setDiffLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent multiple concurrent requests
|
||||
if (diffLoadingRef.current) {
|
||||
return;
|
||||
}
|
||||
// Prevent multiple concurrent requests
|
||||
if (diffLoadingRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
diffLoadingRef.current = true;
|
||||
setDiffLoading(true);
|
||||
setDiffError(null);
|
||||
const response = await makeRequest(
|
||||
`/api/projects/${projectId}/tasks/${selectedAttempt.task_id}/attempts/${selectedAttempt.id}/diff`
|
||||
);
|
||||
try {
|
||||
diffLoadingRef.current = true;
|
||||
if (isBackgroundRefresh) {
|
||||
setIsBackgroundRefreshing(true);
|
||||
} else {
|
||||
setDiffLoading(true);
|
||||
}
|
||||
setDiffError(null);
|
||||
const response = await makeRequest(
|
||||
`/api/projects/${projectId}/tasks/${selectedAttempt.task_id}/attempts/${selectedAttempt.id}/diff`
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const result: ApiResponse<WorktreeDiff> = await response.json();
|
||||
if (result.success && result.data) {
|
||||
setDiff(result.data);
|
||||
if (response.ok) {
|
||||
const result: ApiResponse<WorktreeDiff> = await response.json();
|
||||
if (result.success && result.data) {
|
||||
setDiff(result.data);
|
||||
} else {
|
||||
setDiffError('Failed to load diff');
|
||||
}
|
||||
} else {
|
||||
setDiffError('Failed to load diff');
|
||||
}
|
||||
} else {
|
||||
} catch (err) {
|
||||
setDiffError('Failed to load diff');
|
||||
} finally {
|
||||
diffLoadingRef.current = false;
|
||||
if (isBackgroundRefresh) {
|
||||
setIsBackgroundRefreshing(false);
|
||||
} else {
|
||||
setDiffLoading(false);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setDiffError('Failed to load diff');
|
||||
} finally {
|
||||
diffLoadingRef.current = false;
|
||||
setDiffLoading(false);
|
||||
}
|
||||
}, [projectId, selectedAttempt?.id, selectedAttempt?.task_id]);
|
||||
},
|
||||
[projectId, selectedAttempt?.id, selectedAttempt?.task_id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
@@ -185,11 +197,11 @@ export function TaskDetailsPanel({
|
||||
|
||||
if (isCodingAgentRunning) {
|
||||
// Immediately refresh diff when coding agent starts running
|
||||
fetchDiff();
|
||||
fetchDiff(true);
|
||||
|
||||
// Then refresh diff every 2 seconds while coding agent is active
|
||||
const interval = setInterval(() => {
|
||||
fetchDiff();
|
||||
fetchDiff(true);
|
||||
}, 2000);
|
||||
|
||||
return () => {
|
||||
@@ -867,9 +879,19 @@ export function TaskDetailsPanel({
|
||||
<div
|
||||
className={`flex items-center justify-between ${areAllFilesCollapsed() ? 'mb-1' : 'mb-3'}`}
|
||||
>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{diff.files.length} file
|
||||
{diff.files.length !== 1 ? 's' : ''} changed
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{diff.files.length} file
|
||||
{diff.files.length !== 1 ? 's' : ''} changed
|
||||
</div>
|
||||
{isBackgroundRefreshing && (
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="animate-spin h-3 w-3 border border-blue-500 border-t-transparent rounded-full"></div>
|
||||
<span className="text-xs text-blue-600 dark:text-blue-400">
|
||||
Updating...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user