python-code-linter / index.html
S-Dreamer's picture
Add 3 files
f79270c verified
raw
history blame
26.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python Code Linter | PEP 8 & Flake8 Compliance</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.code-editor {
min-height: 300px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
tab-size: 4;
}
.report-item {
transition: all 0.2s ease;
}
.report-item:hover {
transform: translateX(5px);
}
.tab-active {
border-bottom: 3px solid #3b82f6;
}
.error-badge {
position: absolute;
top: -8px;
right: -8px;
font-size: 12px;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="mb-8 text-center">
<h1 class="text-4xl font-bold text-blue-600 mb-2">
<i class="fas fa-code mr-2"></i>Python Code Linter
</h1>
<p class="text-gray-600 text-lg">
Improve your Python code to meet Flake8 and PEP 8 standards
</p>
</header>
<!-- Main Content -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Tabs -->
<div class="flex border-b">
<button id="paste-tab" class="tab-active px-6 py-4 font-medium text-blue-600 focus:outline-none">
<i class="fas fa-paste mr-2"></i>Paste Code
</button>
<button id="upload-tab" class="px-6 py-4 font-medium text-gray-500 hover:text-blue-600 focus:outline-none">
<i class="fas fa-upload mr-2"></i>Upload File
</button>
<div class="ml-auto px-6 py-4">
<span id="error-count" class="hidden bg-red-100 text-red-800 text-xs font-medium px-2.5 py-0.5 rounded-full">
<span id="error-count-value">0</span> issues found
</span>
</div>
</div>
<!-- Tab Content -->
<div class="p-6">
<!-- Paste Code Tab Content -->
<div id="paste-content" class="space-y-6">
<div class="flex space-x-4">
<div class="flex-1 relative">
<label for="python-code" class="block mb-2 text-sm font-medium text-gray-700">
Original Python Code
<span class="text-gray-500 text-xs">(Ctrl+Enter to lint)</span>
</label>
<div class="relative">
<textarea id="python-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Paste your Python code here..."></textarea>
<button id="clear-code" class="absolute top-2 right-2 p-2 text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="flex-1 relative">
<label for="linted-code" class="block mb-2 text-sm font-medium text-gray-700">
Linted Python Code
</label>
<div class="relative">
<pre id="linted-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto" style="white-space: pre-wrap;"></pre>
<button id="copy-code" class="absolute top-2 right-2 p-2 text-gray-400 hover:text-gray-600">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
<div class="flex justify-center">
<button id="lint-button" class="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors">
<i class="fas fa-magic mr-2"></i>Lint My Code
</button>
</div>
</div>
<!-- Upload File Tab Content -->
<div id="upload-content" class="hidden space-y-6">
<div class="flex items-center justify-center w-full">
<label for="file-upload" class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<i class="fas fa-file-upload text-4xl text-gray-400 mb-3"></i>
<p class="mb-2 text-sm text-gray-500">
<span class="font-semibold">Click to upload</span> or drag and drop
</p>
<p class="text-xs text-gray-500">.py files only</p>
</div>
<input id="file-upload" type="file" class="hidden" accept=".py" />
</label>
</div>
<div class="hidden" id="upload-preview">
<div class="flex items-center justify-between mb-4">
<div>
<span id="uploaded-filename" class="font-medium text-gray-700"></span>
<span id="uploaded-filesize" class="text-sm text-gray-500 ml-2"></span>
</div>
<button id="remove-file" class="text-red-500 hover:text-red-700">
<i class="fas fa-trash mr-1"></i>Remove
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h3 class="text-sm font-medium text-gray-700 mb-2">Original Code</h3>
<pre id="upload-original-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto max-h-64"></pre>
</div>
<div>
<h3 class="text-sm font-medium text-gray-700 mb-2">Linted Code</h3>
<pre id="upload-linted-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto max-h-64"></pre>
</div>
</div>
<div class="mt-4 flex justify-center">
<button id="lint-upload-button" class="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors">
<i class="fas fa-magic mr-2"></i>Lint Uploaded File
</button>
</div>
</div>
</div>
<!-- Linting Report -->
<div id="report-section" class="hidden mt-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">
<i class="fas fa-clipboard-list mr-2"></i>Linting Report
</h2>
<div class="flex mb-4">
<div class="flex-1">
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<i class="fas fa-check-circle mr-1"></i>
<span id="fixed-count">0</span> issues fixed
</div>
</div>
<div class="flex-1 text-right">
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800">
<i class="fas fa-exclamation-triangle mr-1"></i>
<span id="remaining-count">0</span> issues remaining
</div>
</div>
</div>
<div class="bg-gray-50 rounded-lg border border-gray-200 overflow-hidden">
<div id="report-items" class="divide-y divide-gray-200 max-h-96 overflow-y-auto">
<!-- Report items will be added here dynamically -->
</div>
</div>
<div class="mt-4 text-sm text-gray-500">
<i class="fas fa-info-circle mr-1"></i> PEP 8 is the official style guide for Python code. Flake8 is a tool that checks your code against PEP 8 and other quality standards.
</div>
</div>
</div>
</div>
<!-- Features Section -->
<div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white p-6 rounded-lg shadow-md">
<div class="text-blue-500 text-2xl mb-3">
<i class="fas fa-broom"></i>
</div>
<h3 class="font-semibold text-lg mb-2">Code Cleanup</h3>
<p class="text-gray-600">Automatically fix indentation, whitespace, line length, and other PEP 8 violations to make your code more readable.</p>
</div>
<div class="bg-white p-6 rounded-lg shadow-md">
<div class="text-blue-500 text-2xl mb-3">
<i class="fas fa-search"></i>
</div>
<h3 class="font-semibold text-lg mb-2">Comprehensive Checks</h3>
<p class="text-gray-600">Detects and reports on style issues, syntax errors, and potential bugs using Flake8's powerful analysis.</p>
</div>
<div class="bg-white p-6 rounded-lg shadow-md">
<div class="text-blue-500 text-2xl mb-3">
<i class="fas fa-graduation-cap"></i>
</div>
<h3 class="font-semibold text-lg mb-2">Learn Best Practices</h3>
<p class="text-gray-600">Detailed explanations help you understand and remember Python coding standards for future projects.</p>
</div>
</div>
<!-- Footer -->
<footer class="mt-12 text-center text-gray-500 text-sm">
<p>Made with <i class="fas fa-heart text-red-500"></i> for Python developers</p>
<p class="mt-1">This tool helps improve code quality but doesn't replace thorough testing and code reviews.</p>
</footer>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Tab switching
const pasteTab = document.getElementById('paste-tab');
const uploadTab = document.getElementById('upload-tab');
const pasteContent = document.getElementById('paste-content');
const uploadContent = document.getElementById('upload-content');
pasteTab.addEventListener('click', function() {
pasteTab.classList.add('tab-active');
pasteTab.classList.remove('text-gray-500');
pasteTab.classList.add('text-blue-600');
uploadTab.classList.remove('tab-active');
uploadTab.classList.add('text-gray-500');
uploadTab.classList.remove('text-blue-600');
pasteContent.classList.remove('hidden');
uploadContent.classList.add('hidden');
});
uploadTab.addEventListener('click', function() {
uploadTab.classList.add('tab-active');
uploadTab.classList.remove('text-gray-500');
uploadTab.classList.add('text-blue-600');
pasteTab.classList.remove('tab-active');
pasteTab.classList.add('text-gray-500');
pasteTab.classList.remove('text-blue-600');
uploadContent.classList.remove('hidden');
pasteContent.classList.add('hidden');
});
// Code editor functionality
const pythonCode = document.getElementById('python-code');
const lintedCode = document.getElementById('linted-code');
const clearCode = document.getElementById('clear-code');
const copyCode = document.getElementById('copy-code');
const lintButton = document.getElementById('lint-button');
const errorCount = document.getElementById('error-count');
const errorCountValue = document.getElementById('error-count-value');
const reportSection = document.getElementById('report-section');
const reportItems = document.getElementById('report-items');
const fixedCount = document.getElementById('fixed-count');
const remainingCount = document.getElementById('remaining-count');
// Sample linting function (in a real app, this would call a backend service)
function lintPythonCode(code) {
// This is a mock implementation
// In reality, you would send this to a backend that runs flake8 and autopep8
// Mock fixes
const mockFixes = [
{ line: 2, column: 5, message: "E302 expected 2 blank lines before function definition", fixed: true },
{ line: 5, column: 80, message: "E501 line too long (82 > 79 characters)", fixed: true },
{ line: 7, column: 1, message: "E303 too many blank lines (3)", fixed: true },
{ line: 9, column: 17, message: "E225 missing whitespace around operator", fixed: true },
{ line: 12, column: 1, message: "W391 blank line at end of file", fixed: true },
{ line: 3, column: 1, message: "E265 block comment should start with '# '", fixed: false },
];
// Mock linted code (just some basic formatting for demo)
let linted = code;
// Add two blank lines before functions
linted = linted.replace(/def (\w+)\(/g, '\n\n$&');
// Fix line length (very simplistic)
linted = linted.split('\n').map(line => {
if (line.length > 79) {
return line.substring(0, 76) + '...';
}
return line;
}).join('\n');
// Trim trailing whitespace
linted = linted.split('\n').map(line => line.trimEnd()).join('\n');
// Ensure exactly one blank line at end of file
linted = linted.trimEnd() + '\n';
return {
lintedCode: linted,
issues: mockFixes,
originalCode: code
};
}
// Generate a report item
function createReportItem(issue, index) {
const item = document.createElement('div');
item.className = 'report-item p-4 hover:bg-gray-100';
const badgeClass = issue.fixed ?
'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800';
const icon = issue.fixed ? 'check-circle' : 'exclamation-triangle';
item.innerHTML = `
<div class="flex items-start">
<div class="flex-shrink-0 pt-0.5">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${badgeClass}">
<i class="fas fa-${icon} mr-1"></i>
${issue.fixed ? 'Fixed' : 'Warning'}
</span>
</div>
<div class="ml-3">
<div class="text-sm font-medium text-gray-900">
Line ${issue.line}, Column ${issue.column}
</div>
<div class="text-sm text-gray-500">
${issue.message}
</div>
</div>
</div>
`;
return item;
}
// Clear code editor
clearCode.addEventListener('click', function() {
pythonCode.value = '';
lintedCode.textContent = '';
reportSection.classList.add('hidden');
errorCount.classList.add('hidden');
});
// Copy linted code
copyCode.addEventListener('click', function() {
if (lintedCode.textContent.trim()) {
navigator.clipboard.writeText(lintedCode.textContent);
// Show feedback
const originalIcon = copyCode.querySelector('i');
originalIcon.classList.remove('fa-copy');
originalIcon.classList.add('fa-check');
setTimeout(() => {
originalIcon.classList.remove('fa-check');
originalIcon.classList.add('fa-copy');
}, 2000);
}
});
// Lint code
function performLint() {
const code = pythonCode.value.trim();
if (!code) return;
// Show loading state
lintButton.innerHTML = '<i class="fas fa-spinner animate-spin mr-2"></i> Linting...';
lintButton.disabled = true;
// Simulate API call delay
setTimeout(() => {
const result = lintPythonCode(code);
// Update UI with results
lintedCode.textContent = result.lintedCode;
// Show error count
const totalIssues = result.issues.length;
const fixedIssues = result.issues.filter(i => i.fixed).length;
if (totalIssues > 0) {
errorCountValue.textContent = totalIssues;
errorCount.classList.remove('hidden');
} else {
errorCount.classList.add('hidden');
}
// Generate report
reportItems.innerHTML = '';
result.issues.forEach((issue, index) => {
reportItems.appendChild(createReportItem(issue, index));
});
// Update counts
fixedCount.textContent = fixedIssues;
remainingCount.textContent = totalIssues - fixedIssues;
// Show report
reportSection.classList.remove('hidden');
// Reset button
lintButton.innerHTML = '<i class="fas fa-magic mr-2"></i>Lint My Code';
lintButton.disabled = false;
}, 1000);
}
lintButton.addEventListener('click', performLint);
// Add Ctrl+Enter shortcut to lint code
pythonCode.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'Enter') {
performLint();
}
});
// File upload functionality
const fileUpload = document.getElementById('file-upload');
const uploadPreview = document.getElementById('upload-preview');
const uploadedFilename = document.getElementById('uploaded-filename');
const uploadedFilesize = document.getElementById('uploaded-filesize');
const uploadOriginalCode = document.getElementById('upload-original-code');
const uploadLintedCode = document.getElementById('upload-linted-code');
const removeFile = document.getElementById('remove-file');
const lintUploadButton = document.getElementById('lint-upload-button');
fileUpload.addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
if (!file.name.endsWith('.py')) {
alert('Please upload a Python (.py) file');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
// Update UI
uploadedFilename.textContent = file.name;
uploadedFilesize.textContent = formatFileSize(file.size);
uploadOriginalCode.textContent = content;
uploadLintedCode.textContent = '';
// Show preview
uploadPreview.classList.remove('hidden');
};
reader.readAsText(file);
});
removeFile.addEventListener('click', function() {
fileUpload.value = '';
uploadPreview.classList.add('hidden');
});
lintUploadButton.addEventListener('click', function() {
const code = uploadOriginalCode.textContent.trim();
if (!code) return;
// Show loading state
lintUploadButton.innerHTML = '<i class="fas fa-spinner animate-spin mr-2"></i> Linting...';
lintUploadButton.disabled = true;
// Simulate API call delay
setTimeout(() => {
const result = lintPythonCode(code);
// Update UI with results
uploadLintedCode.textContent = result.lintedCode;
// Generate report
reportItems.innerHTML = '';
result.issues.forEach((issue, index) => {
reportItems.appendChild(createReportItem(issue, index));
});
// Update counts
const totalIssues = result.issues.length;
const fixedIssues = result.issues.filter(i => i.fixed).length;
fixedCount.textContent = fixedIssues;
remainingCount.textContent = totalIssues - fixedIssues;
// Show report
reportSection.classList.remove('hidden');
// Reset button
lintUploadButton.innerHTML = '<i class="fas fa-magic mr-2"></i>Lint Uploaded File';
lintUploadButton.disabled = false;
}, 1000);
});
// Helper function to format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Sample code for first-time users
if (!pythonCode.value.trim()) {
pythonCode.value = `# This is a sample Python code with some style issues
def calculate_sum(a,b):
result=a+b
return result
def print_message(msg):
#This function prints a message
print("Message:",msg)
print_message("Hello World!")
total=calculate_sum(5,7)
print("The total is:",total)
`;
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=S-Dreamer/python-code-linter" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>