Merge task attempt 92c56515-8aa3-48e8-ad77-dfc1b0dbd0eb into main
This commit is contained in:
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
@@ -2974,9 +2974,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.167",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz",
|
||||
"integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==",
|
||||
"version": "1.5.170",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
||||
"integrity": "sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -4234,9 +4234,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
|
||||
"integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useState, useEffect } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, FileText } from "lucide-react";
|
||||
import { ArrowLeft, FileText, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import { makeRequest } from "@/lib/api";
|
||||
import type { WorktreeDiff, DiffChunkType } from "shared/types";
|
||||
import type { WorktreeDiff, DiffChunkType, DiffChunk } from "shared/types";
|
||||
|
||||
interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
@@ -25,6 +25,7 @@ export function TaskAttemptComparePage() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [merging, setMerging] = useState(false);
|
||||
const [mergeSuccess, setMergeSuccess] = useState(false);
|
||||
const [expandedSections, setExpandedSections] = useState<Set<string>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && taskId && attemptId) {
|
||||
@@ -119,6 +120,141 @@ export function TaskAttemptComparePage() {
|
||||
}
|
||||
};
|
||||
|
||||
interface ProcessedLine {
|
||||
content: string;
|
||||
chunkType: DiffChunkType;
|
||||
lineNumber: number;
|
||||
}
|
||||
|
||||
interface ProcessedSection {
|
||||
type: 'context' | 'change' | 'expanded';
|
||||
lines: ProcessedLine[];
|
||||
expandKey?: string;
|
||||
expandedAbove?: boolean;
|
||||
expandedBelow?: boolean;
|
||||
}
|
||||
|
||||
const processFileChunks = (chunks: DiffChunk[], fileIndex: number) => {
|
||||
const CONTEXT_LINES = 3;
|
||||
const lines: ProcessedLine[] = [];
|
||||
let currentLineNumber = 1;
|
||||
|
||||
// Convert chunks to lines with line numbers
|
||||
chunks.forEach(chunk => {
|
||||
const chunkLines = chunk.content.split('\n');
|
||||
chunkLines.forEach((line, index) => {
|
||||
if (index < chunkLines.length - 1 || line !== '') { // Skip empty last line from split
|
||||
lines.push({
|
||||
content: line,
|
||||
chunkType: chunk.chunk_type,
|
||||
lineNumber: currentLineNumber++
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const sections: ProcessedSection[] = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < lines.length) {
|
||||
const line = lines[i];
|
||||
|
||||
if (line.chunkType === 'Equal') {
|
||||
// Look for the next change or end of file
|
||||
let nextChangeIndex = i + 1;
|
||||
while (nextChangeIndex < lines.length && lines[nextChangeIndex].chunkType === 'Equal') {
|
||||
nextChangeIndex++;
|
||||
}
|
||||
|
||||
const contextLength = nextChangeIndex - i;
|
||||
const hasNextChange = nextChangeIndex < lines.length;
|
||||
const hasPrevChange = sections.length > 0 && sections[sections.length - 1].type === 'change';
|
||||
|
||||
if (contextLength <= CONTEXT_LINES * 2 || (!hasPrevChange && !hasNextChange)) {
|
||||
// Show all context if it's short or if there are no changes around it
|
||||
sections.push({
|
||||
type: 'context',
|
||||
lines: lines.slice(i, nextChangeIndex)
|
||||
});
|
||||
} else {
|
||||
// Split into context sections with expandable middle
|
||||
if (hasPrevChange) {
|
||||
// Add context after previous change
|
||||
sections.push({
|
||||
type: 'context',
|
||||
lines: lines.slice(i, i + CONTEXT_LINES)
|
||||
});
|
||||
i += CONTEXT_LINES;
|
||||
}
|
||||
|
||||
if (hasNextChange) {
|
||||
// Add expandable section
|
||||
const expandStart = hasPrevChange ? i : i + CONTEXT_LINES;
|
||||
const expandEnd = nextChangeIndex - CONTEXT_LINES;
|
||||
|
||||
if (expandEnd > expandStart) {
|
||||
const expandKey = `${fileIndex}-${expandStart}-${expandEnd}`;
|
||||
const isExpanded = expandedSections.has(expandKey);
|
||||
|
||||
if (isExpanded) {
|
||||
sections.push({
|
||||
type: 'expanded',
|
||||
lines: lines.slice(expandStart, expandEnd),
|
||||
expandKey
|
||||
});
|
||||
} else {
|
||||
sections.push({
|
||||
type: 'context',
|
||||
lines: [],
|
||||
expandKey
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add context before next change
|
||||
sections.push({
|
||||
type: 'context',
|
||||
lines: lines.slice(nextChangeIndex - CONTEXT_LINES, nextChangeIndex)
|
||||
});
|
||||
} else if (!hasPrevChange) {
|
||||
// No changes around, just show first few lines
|
||||
sections.push({
|
||||
type: 'context',
|
||||
lines: lines.slice(i, i + CONTEXT_LINES)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
i = nextChangeIndex;
|
||||
} else {
|
||||
// Found a change, collect all consecutive changes
|
||||
const changeStart = i;
|
||||
while (i < lines.length && lines[i].chunkType !== 'Equal') {
|
||||
i++;
|
||||
}
|
||||
|
||||
sections.push({
|
||||
type: 'change',
|
||||
lines: lines.slice(changeStart, i)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
const toggleExpandSection = (expandKey: string) => {
|
||||
setExpandedSections(prev => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(expandKey)) {
|
||||
newSet.delete(expandKey);
|
||||
} else {
|
||||
newSet.add(expandKey);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
@@ -199,16 +335,50 @@ export function TaskAttemptComparePage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-h-[600px] overflow-y-auto">
|
||||
{file.chunks.map((chunk, chunkIndex) =>
|
||||
chunk.content.split('\n').map((line, lineIndex) => (
|
||||
<div
|
||||
key={`${chunkIndex}-${lineIndex}`}
|
||||
className={getChunkClassName(chunk.chunk_type)}
|
||||
>
|
||||
{getChunkPrefix(chunk.chunk_type)}{line}
|
||||
{processFileChunks(file.chunks, fileIndex).map((section, sectionIndex) => {
|
||||
if (section.type === 'context' && section.lines.length === 0 && section.expandKey) {
|
||||
// Render expand button
|
||||
const lineCount = parseInt(section.expandKey.split('-')[2]) - parseInt(section.expandKey.split('-')[1]);
|
||||
return (
|
||||
<div key={`expand-${section.expandKey}`}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => toggleExpandSection(section.expandKey!)}
|
||||
className="w-full h-8 text-xs text-blue-600 hover:text-blue-800 hover:bg-blue-50 border-t border-b border-gray-200 rounded-none"
|
||||
>
|
||||
<ChevronDown className="h-3 w-3 mr-1" />
|
||||
Show {lineCount} more lines
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Render lines (context, change, or expanded)
|
||||
return (
|
||||
<div key={`section-${sectionIndex}`}>
|
||||
{section.type === 'expanded' && section.expandKey && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => toggleExpandSection(section.expandKey!)}
|
||||
className="w-full h-8 text-xs text-blue-600 hover:text-blue-800 hover:bg-blue-50 border-t border-b border-gray-200 rounded-none"
|
||||
>
|
||||
<ChevronUp className="h-3 w-3 mr-1" />
|
||||
Hide expanded lines
|
||||
</Button>
|
||||
)}
|
||||
{section.lines.map((line, lineIndex) => (
|
||||
<div
|
||||
key={`${sectionIndex}-${lineIndex}`}
|
||||
className={getChunkClassName(line.chunkType)}
|
||||
>
|
||||
{getChunkPrefix(line.chunkType)}{line.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
996
package-lock.json
generated
996
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,8 @@
|
||||
"generate-types": "cd backend && cargo run --bin generate_types"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2"
|
||||
"concurrently": "^8.2.2",
|
||||
"vite": "^6.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
|
||||
Reference in New Issue
Block a user