Skip to content

Add Diff File View for instance.patch and test_result.git_patch #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .openhands/microagents/repo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Trajectory Visualizer Repository Information

## Project Overview
The Trajectory Visualizer is a web application for visualizing OpenHands Resolver execution trajectories. It provides a timeline view of actions and observations during the execution of OpenHands agents.

## Repository Structure
- `/src/components/`: React components
- `/src/components/timeline/`: Timeline visualization components
- `/src/components/timeline/components/`: Timeline subcomponents
- `/src/components/artifacts/`: Artifact details components
- `/src/components/diff-viewer.tsx`: Diff viewer component for file changes
- `/src/components/jsonl-viewer/`: JSONL viewer components
- `/src/components/share/`: Shared components for trajectory visualization
- `/src/services/`: API services
- `/src/utils/`: Utility functions
- `/src/types/`: TypeScript type definitions

## Common Commands
- `npm start`: Start development server
- `npm build`: Build production-ready app
- `npm test`: Run tests

## Code Style Preferences
- React functional components with TypeScript
- Tailwind CSS for styling
- React hooks for state management

## Key Components

### Timeline Components
- `Timeline.tsx`: Main timeline component that renders a list of timeline steps
- `TimelineStep.tsx`: Individual timeline step component
- `TimelineEntry`: Interface for timeline entry data

### JSONL Viewer Components
- `JsonlViewer.tsx`: Component for viewing JSONL files with trajectory data
- `JsonlViewerSettings.tsx`: Settings for the JSONL viewer

### Artifact Components
- `ArtifactDetails.tsx`: Component for displaying artifact details, including diff views for patches

### Diff Viewer
- `diff-viewer.tsx`: Component for displaying file diffs using `react-diff-viewer-continued`

## Implementation Details

### Diff File View
- The diff viewer is implemented in `/src/components/diff-viewer.tsx`
- It uses `react-diff-viewer-continued` to display file diffs
- The diff viewer is used in two places:
1. In the `ArtifactDetails` component to display `.instance.patch` and `.test_result.git_patch` files
2. In the `JsonlViewer` component's Entry Metadata panel to display the same patch files
- The `handleFileEditClick` function in `RunDetails.tsx` updates the artifact content with patch data when a file edit is clicked

### Data Flow
1. Timeline entries are loaded from the artifact content
2. When a file edit is clicked, the patch data is extracted from the entry metadata
3. The patch data is added to the artifact content
4. The `ArtifactDetails` component renders the patch data using the `DiffViewer` component
5. The `JsonlViewer` component's Entry Metadata panel also renders the patch data using the `DiffViewer` component
8 changes: 5 additions & 3 deletions src/components/RunDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ const RunDetails: React.FC<RunDetailsProps> = ({ owner, repo, run, initialConten
if (timelineEntries && timelineEntries.length > selectedStepIndex) {
const entry = timelineEntries[selectedStepIndex];

// Show file changes in an alert for now
if (entry.path) {
alert(`File: ${entry.path}\n\nChanges are not available in this view. This would typically show a diff of the changes made to the file.`);
// If the entry has a path and metadata with file edit information, we can show it
if (entry.path && entry.metadata) {
// The diff viewer is now shown directly in the timeline entry via the EntryMetadataPanel
// We just need to ensure the entry is selected
setSelectedStepIndex(selectedStepIndex);
}
}
}, [getTimelineEntries, selectedStepIndex]);
Expand Down
26 changes: 25 additions & 1 deletion src/components/jsonl-viewer/JsonlViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getNestedValue, formatValueForDisplay } from '../../utils/object-utils'
import { TrajectoryItem } from '../../types/share';
import JsonVisualizer from '../json-visualizer/JsonVisualizer';
import { DEFAULT_JSONL_VIEWER_SETTINGS } from '../../config/jsonl-viewer-config';
import { DiffViewer } from '../diff-viewer';
import {
isAgentStateChange,
isUserMessage,
Expand Down Expand Up @@ -356,7 +357,30 @@ const JsonlViewer: React.FC<JsonlViewerProps> = ({ content }) => {
</div>
<div className="flex-1 overflow-y-auto scrollbar scrollbar-w-1.5 scrollbar-thumb-gray-200/75 dark:scrollbar-thumb-gray-700/75 scrollbar-track-transparent hover:scrollbar-thumb-gray-300/75 dark:hover:scrollbar-thumb-gray-600/75 scrollbar-thumb-rounded p-3">
{currentEntryWithoutHistory ? (
<JsonVisualizer data={currentEntryWithoutHistory} initialExpanded={true} />
<div className="space-y-4">
{/* Instance Patch Diff Viewer */}
{entries[currentEntryIndex]?.instance?.patch && (
<div className="mt-4">
<h4 className="text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">Instance Patch</h4>
<div className="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden">
<DiffViewer oldStr="" newStr={entries[currentEntryIndex].instance.patch} splitView={false} />
</div>
</div>
)}

{/* Git Patch Diff Viewer */}
{entries[currentEntryIndex]?.test_result?.git_patch && (
<div className="mt-4">
<h4 className="text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">Git Patch</h4>
<div className="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden">
<DiffViewer oldStr="" newStr={entries[currentEntryIndex].test_result.git_patch} splitView={false} />
</div>
</div>
)}

{/* JSON Visualizer for other metadata */}
<JsonVisualizer data={currentEntryWithoutHistory} initialExpanded={true} />
</div>
) : (
<div className="text-sm text-gray-500 dark:text-gray-400">
No metadata available
Expand Down
43 changes: 43 additions & 0 deletions src/components/timeline/components/EntryMetadataPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { TimelineEntry } from '../types';
import { DiffViewer } from '../../diff-viewer';

interface EntryMetadataPanelProps {
entry: TimelineEntry;
}

const EntryMetadataPanel: React.FC<EntryMetadataPanelProps> = ({ entry }) => {
// Check for patch files in the metadata
const instancePatch = entry.metadata?.instance?.patch;
const gitPatch = entry.metadata?.test_result?.git_patch;

if (!instancePatch && !gitPatch) {
return null;
}

return (
<div className="space-y-4 mt-4">
{/* Instance Patch Diff Viewer */}
{instancePatch && (
<div>
<h4 className="text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">Instance Patch</h4>
<div className="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden">
<DiffViewer oldStr="" newStr={instancePatch} splitView={false} />
</div>
</div>
)}

{/* Git Patch Diff Viewer */}
{gitPatch && (
<div>
<h4 className="text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">Git Patch</h4>
<div className="border border-gray-200 dark:border-gray-700 rounded-md overflow-hidden">
<DiffViewer oldStr="" newStr={gitPatch} splitView={false} />
</div>
</div>
)}
</div>
);
};

export default EntryMetadataPanel;
2 changes: 2 additions & 0 deletions src/components/timeline/components/TimelineStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export const TimelineStep: React.FC<TimelineStepProps> = memo(({
)}
</div>
)}

{/* Entry metadata is now shown in the right panel */}
</div>
</div>
</div>
Expand Down