mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
feat(diffViewer): Improve diff viewer - Show untracked, deleted, modified outgoing changes - Improve modal style
This commit is contained in:
committed by
Sam Chau
parent
ab3c6183c5
commit
650cfa2dc7
@@ -69,36 +69,6 @@ def validate_git_token(repo_url, git_token):
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def parse_diff(diff_text):
|
||||
diff_lines = diff_text.splitlines()
|
||||
parsed_diff = []
|
||||
|
||||
local_version = []
|
||||
incoming_version = []
|
||||
in_local = False
|
||||
in_incoming = False
|
||||
|
||||
for line in diff_lines:
|
||||
if line.startswith('--- a/'):
|
||||
in_local = True
|
||||
in_incoming = False
|
||||
elif line.startswith('+++ b/'):
|
||||
in_incoming = True
|
||||
in_local = False
|
||||
elif line.startswith('@@'):
|
||||
# Context lines that indicate a change
|
||||
parsed_diff.append({'context': line, 'type': 'context'})
|
||||
elif line.startswith('-'):
|
||||
local_version.append(line[1:])
|
||||
parsed_diff.append({'text': line[1:], 'type': 'local'})
|
||||
elif line.startswith('+'):
|
||||
incoming_version.append(line[1:])
|
||||
parsed_diff.append({'text': line[1:], 'type': 'incoming'})
|
||||
else:
|
||||
parsed_diff.append({'text': line, 'type': 'unchanged'})
|
||||
|
||||
return parsed_diff, local_version, incoming_version
|
||||
|
||||
def get_outgoing_changes(repo):
|
||||
status = repo.git.status('--porcelain', '-z').split('\0')
|
||||
logger.debug(f"Raw porcelain status: {status}")
|
||||
@@ -119,7 +89,19 @@ def get_outgoing_changes(repo):
|
||||
|
||||
full_path = os.path.join(repo.working_dir, file_path)
|
||||
|
||||
if os.path.isdir(full_path):
|
||||
if x == 'D' or y == 'D':
|
||||
# Handle deleted files
|
||||
changes.append({
|
||||
'name': 'Deleted File',
|
||||
'id': '',
|
||||
'type': determine_type(file_path),
|
||||
'status': 'Deleted',
|
||||
'file_path': file_path,
|
||||
'staged': x != '?',
|
||||
'modified': False,
|
||||
'deleted': True
|
||||
})
|
||||
elif os.path.isdir(full_path):
|
||||
logger.debug(f"Found directory: {file_path}, going through folder.")
|
||||
for root, dirs, files in os.walk(full_path):
|
||||
for file in files:
|
||||
@@ -139,17 +121,14 @@ def get_outgoing_changes(repo):
|
||||
'file_path': os.path.relpath(file_full_path, repo.working_dir),
|
||||
'staged': x != '?' and x != ' ',
|
||||
'modified': y == 'M',
|
||||
'deleted': x == 'D' or y == 'D'
|
||||
'deleted': False
|
||||
})
|
||||
else:
|
||||
logger.debug(f"No data extracted from file: {file_full_path}")
|
||||
else:
|
||||
logger.debug(f"Found file: {full_path}, going through file.")
|
||||
file_data = extract_data_from_yaml(full_path)
|
||||
if file_data or x == 'D' or y == 'D': # Ensure that deleted files are handled
|
||||
if not file_data:
|
||||
logger.debug(f"No data found, using default file name as name")
|
||||
file_data = {'name': os.path.basename(file_path).replace('.yml', ''), 'id': None}
|
||||
if file_data:
|
||||
logger.debug(f"File contents: {file_data}")
|
||||
logger.debug(f"Found ID: {file_data.get('id')}")
|
||||
logger.debug(f"Found Name: {file_data.get('name')}")
|
||||
@@ -161,7 +140,19 @@ def get_outgoing_changes(repo):
|
||||
'file_path': file_path,
|
||||
'staged': x != '?' and x != ' ',
|
||||
'modified': y == 'M',
|
||||
'deleted': x == 'D' or y == 'D'
|
||||
'deleted': False
|
||||
})
|
||||
else:
|
||||
logger.debug(f"No data found, using default file name as name")
|
||||
changes.append({
|
||||
'name': os.path.basename(file_path).replace('.yml', ''),
|
||||
'id': '',
|
||||
'type': determine_type(file_path),
|
||||
'status': interpret_git_status(x, y),
|
||||
'file_path': file_path,
|
||||
'staged': x != '?' and x != ' ',
|
||||
'modified': y == 'M',
|
||||
'deleted': False
|
||||
})
|
||||
|
||||
logger.debug(f"Final changes: {json.dumps(changes, indent=2)}")
|
||||
@@ -772,8 +763,23 @@ def get_diff():
|
||||
try:
|
||||
repo = git.Repo(settings_manager.repo_path)
|
||||
branch = repo.active_branch.name
|
||||
diff = repo.git.diff(f'HEAD...origin/{branch}', file_path)
|
||||
return jsonify({'success': True, 'diff': diff}), 200
|
||||
|
||||
# Check if the file is untracked
|
||||
untracked_files = repo.untracked_files
|
||||
if file_path in untracked_files:
|
||||
with open(os.path.join(repo.working_dir, file_path), 'r') as file:
|
||||
content = file.read()
|
||||
diff = "\n".join([f"+{line}" for line in content.splitlines()])
|
||||
else:
|
||||
# Check if the file is deleted
|
||||
if not os.path.exists(os.path.join(repo.working_dir, file_path)):
|
||||
diff = "-Deleted File"
|
||||
else:
|
||||
# Get the diff for modified files
|
||||
diff = repo.git.diff(f'HEAD', file_path)
|
||||
|
||||
logger.debug(f"Diff for file {file_path}: {diff}")
|
||||
return jsonify({'success': True, 'diff': diff if diff else ""}), 200
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting diff for file {file_path}: {str(e)}", exc_info=True)
|
||||
return jsonify({'success': False, 'error': f"Error getting diff for file: {str(e)}"}), 400
|
||||
return jsonify({'success': False, 'error': f"Error getting diff for file: {str(e)}"}), 400
|
||||
@@ -1,87 +1,86 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from '../ui/Modal';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Modal from "../ui/Modal";
|
||||
|
||||
const DiffModal = ({ isOpen, onClose, diffContent, type, name, commitMessage, title = "View Diff" }) => {
|
||||
const formatDiffContent = (content) => {
|
||||
return content
|
||||
.split('\n')
|
||||
.filter(line =>
|
||||
!line.startsWith('diff --git') &&
|
||||
!line.startsWith('index') &&
|
||||
!line.startsWith('---') &&
|
||||
!line.startsWith('+++') &&
|
||||
!line.startsWith('@@')
|
||||
)
|
||||
.map((line, index) => {
|
||||
let className = 'pl-2';
|
||||
let lineBackground = '';
|
||||
|
||||
if (line.startsWith('+')) {
|
||||
className += ' text-green-400'; // Green for additions
|
||||
lineBackground = 'bg-green-900'; // Darker green background
|
||||
} else if (line.startsWith('-')) {
|
||||
className += ' text-red-400'; // Red for deletions
|
||||
lineBackground = 'bg-red-900'; // Darker red background
|
||||
} else if (line.startsWith('@@')) {
|
||||
className += ' text-yellow-400'; // Yellow for diff hunk headers
|
||||
lineBackground = 'bg-gray-700'; // Neutral background
|
||||
} else {
|
||||
lineBackground = 'bg-gray-800'; // Default background for unchanged lines
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`${lineBackground} ${className} py-1 flex`}
|
||||
style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }} // Ensure long lines are wrapped properly
|
||||
>
|
||||
<span className="inline-block w-12 text-gray-600 select-none text-right pr-2">{index + 1}</span>
|
||||
<span className="ml-2">{line}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const DiffModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
diffContent,
|
||||
type,
|
||||
name,
|
||||
commitMessage,
|
||||
title = "View Diff",
|
||||
}) => {
|
||||
const formatDiffContent = (content) => {
|
||||
if (!content) return [];
|
||||
return content.split("\n").map((line, index) => {
|
||||
let lineClass = "py-1 pl-4 border-l-2 ";
|
||||
if (line.startsWith("+")) {
|
||||
lineClass += "bg-green-900/30 text-green-400 border-green-500";
|
||||
} else if (line.startsWith("-")) {
|
||||
lineClass += "bg-red-900/30 text-red-400 border-red-500";
|
||||
} else {
|
||||
lineClass += "border-transparent";
|
||||
}
|
||||
return (
|
||||
<div key={index} className={`flex ${lineClass}`}>
|
||||
<span className="w-12 text-gray-500 select-none text-right pr-4 border-r border-gray-700">
|
||||
{index + 1}
|
||||
</span>
|
||||
<code className="flex-1 pl-4 font-mono text-sm">{line}</code>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const formattedContent = formatDiffContent(diffContent);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title={title} size="xl">
|
||||
<div className="mb-4 p-4 bg-gray-700 rounded-lg shadow-md">
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div>
|
||||
<span className="font-bold text-gray-300">Type: </span>
|
||||
<span className="text-gray-100">{type}</span>
|
||||
<Modal isOpen={isOpen} onClose={onClose} title={title} size="4xl">
|
||||
<div className="space-y-4">
|
||||
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg space-y-2 text-sm">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="font-medium text-gray-600 dark:text-gray-300">
|
||||
Type:
|
||||
</span>
|
||||
<span className="bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded">
|
||||
{type}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-bold text-gray-300">Name: </span>
|
||||
<span className="text-gray-100">{name}</span>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="font-medium text-gray-600 dark:text-gray-300">
|
||||
Name:
|
||||
</span>
|
||||
<span className="bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200 px-2 py-1 rounded">
|
||||
{name === "Deleted File" ? "Deleted File" : name}
|
||||
</span>
|
||||
</div>
|
||||
{commitMessage && (
|
||||
<div>
|
||||
<span className="font-bold text-gray-300">Commit Message: </span>
|
||||
<span className="text-gray-100">{commitMessage}</span>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium text-gray-600 dark:text-gray-300 mb-1">
|
||||
Commit Message:
|
||||
</span>
|
||||
<p className="bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200 p-2 rounded">
|
||||
{commitMessage}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="border border-gray-300 dark:border-gray-600 rounded-lg overflow-hidden">
|
||||
<div className="bg-gray-50 dark:bg-gray-800 p-2 text-sm font-medium text-gray-600 dark:text-gray-300 border-b border-gray-300 dark:border-gray-600">
|
||||
Diff Content
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-900 p-4 max-h-[60vh] overflow-y-auto">
|
||||
{formattedContent.length > 0 ? (
|
||||
formattedContent
|
||||
) : (
|
||||
<div className="text-gray-500 dark:text-gray-400 italic">
|
||||
No differences found or file is empty.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bg-gray-900 p-4 rounded-lg shadow-inner"
|
||||
style={{ overflowX: 'auto' }} // Add horizontal scrolling
|
||||
>
|
||||
<div
|
||||
className="text-sm text-gray-100 font-mono"
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap', // Wrap lines
|
||||
wordWrap: 'break-word', // Break long words
|
||||
}}
|
||||
>
|
||||
{formatDiffContent(diffContent || "No differences found.")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -89,7 +88,7 @@ const DiffModal = ({ isOpen, onClose, diffContent, type, name, commitMessage, ti
|
||||
DiffModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
diffContent: PropTypes.string.isRequired,
|
||||
diffContent: PropTypes.string,
|
||||
type: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
commitMessage: PropTypes.string,
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import React from 'react';
|
||||
import Modal from '../ui/Modal';
|
||||
|
||||
const DiffViewer = ({ isOpen, onClose, diffContent = [] }) => {
|
||||
// Ensure diffContent is an array before proceeding
|
||||
if (!Array.isArray(diffContent)) {
|
||||
diffContent = []; // Default to an empty array if diffContent is not an array
|
||||
}
|
||||
|
||||
// Separate content for local and incoming
|
||||
const localContent = diffContent.filter(line => line.type === 'local' || line.type === 'context' || line.type === 'unchanged');
|
||||
const incomingContent = diffContent.filter(line => line.type === 'incoming' || line.type === 'context' || line.type === 'unchanged');
|
||||
|
||||
const renderDiffColumn = (content) => {
|
||||
return content.map((line, index) => {
|
||||
let lineClass = 'text-gray-200'; // Default line class for unchanged lines
|
||||
|
||||
if (line.type === 'local') {
|
||||
lineClass = 'bg-red-400 text-black'; // Highlight local changes
|
||||
} else if (line.type === 'incoming') {
|
||||
lineClass = 'bg-yellow-400 text-black'; // Highlight incoming changes
|
||||
} else if (line.type === 'context') {
|
||||
lineClass = 'text-gray-400'; // Context lines
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={index} className={`p-1 ${lineClass}`}>
|
||||
{line.text}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title="Diff Viewer"
|
||||
size="xl" // Making the modal larger
|
||||
>
|
||||
<div className="bg-gray-900 p-4 rounded-md overflow-auto text-xs flex space-x-4">
|
||||
<div className="w-1/2">
|
||||
<h3 className="text-gray-300 mb-2">Remote</h3>
|
||||
<div className="bg-gray-800 p-2 rounded-md">
|
||||
{renderDiffColumn(localContent)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<h3 className="text-gray-300 mb-2">Local</h3>
|
||||
<div className="bg-gray-800 p-2 rounded-md">
|
||||
{renderDiffColumn(incomingContent)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DiffViewer;
|
||||
@@ -289,13 +289,15 @@ const SettingsPage = () => {
|
||||
};
|
||||
|
||||
const handleViewDiff = async (change) => {
|
||||
setLoadingDiff(true); // Start loading
|
||||
setLoadingDiff(true);
|
||||
try {
|
||||
const response = await getDiff(change.file_path);
|
||||
console.log("Diff response:", response); // Add this line to log the response
|
||||
if (response.success) {
|
||||
setDiffContent(response.diff); // Store the diff content
|
||||
setCurrentChange(change); // Set the current change being viewed
|
||||
setShowDiffModal(true); // Open the modal
|
||||
console.log("Diff content:", response.diff); // Add this line to log the diff content
|
||||
setDiffContent(response.diff);
|
||||
setCurrentChange(change);
|
||||
setShowDiffModal(true);
|
||||
} else {
|
||||
Alert.error(response.error);
|
||||
}
|
||||
@@ -303,7 +305,7 @@ const SettingsPage = () => {
|
||||
Alert.error("An unexpected error occurred while fetching the diff.");
|
||||
console.error("Error fetching diff:", error);
|
||||
} finally {
|
||||
setLoadingDiff(false); // Stop loading
|
||||
setLoadingDiff(false);
|
||||
setLoadingAction("");
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user