Make an app that calculates that allows the user to upload a bunch of files, and then analyzes all of those files content and compares them to each other and create unique indentifier tags for each identical largest common similarity among all files and makes a key that states how big the lcs's are (next to the icon or letter ) for example:
Browse files- README.md +8 -5
- components/footer.js +31 -0
- components/header.js +53 -0
- index.html +97 -19
- script.js +263 -0
- style.css +42 -19
README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: CodeFingerprint Detective 🔍
|
| 3 |
+
colorFrom: purple
|
| 4 |
+
colorTo: purple
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
components/footer.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
width: 100%;
|
| 9 |
+
}
|
| 10 |
+
footer {
|
| 11 |
+
background: linear-gradient(135deg, #155e75 0%, #164e63 100%);
|
| 12 |
+
}
|
| 13 |
+
</style>
|
| 14 |
+
<footer class="text-white py-8 mt-12">
|
| 15 |
+
<div class="container mx-auto px-4">
|
| 16 |
+
<div class="flex flex-col md:flex-row justify-between items-center">
|
| 17 |
+
<div class="flex items-center space-x-2 mb-4 md:mb-0">
|
| 18 |
+
<i data-feather="code" class="w-6 h-6"></i>
|
| 19 |
+
<p class="text-sm">© 2024 CodeFingerprint Detective. Uncover the patterns in your code.</p>
|
| 20 |
+
</div>
|
| 21 |
+
<div class="flex space-x-4">
|
| 22 |
+
<a href="#" class="text-cyan-200 hover:text-white transition-colors duration-300">
|
| 23 |
+
<i data-feather="github" class="w-5 h-5"></i>
|
| 24 |
+
</div>
|
| 25 |
+
</div>
|
| 26 |
+
</footer>
|
| 27 |
+
`;
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
customElements.define('custom-footer', CustomFooter);
|
components/header.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomHeader extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
width: 100%;
|
| 9 |
+
}
|
| 10 |
+
header {
|
| 11 |
+
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
|
| 12 |
+
box-shadow: 0 4px 20px rgba(6, 182, 212, 0.3);
|
| 13 |
+
}
|
| 14 |
+
.nav-link {
|
| 15 |
+
transition: all 0.3s ease;
|
| 16 |
+
position: relative;
|
| 17 |
+
}
|
| 18 |
+
.nav-link:hover {
|
| 19 |
+
transform: translateY(-2px);
|
| 20 |
+
}
|
| 21 |
+
.nav-link::after {
|
| 22 |
+
content: '';
|
| 23 |
+
position: absolute;
|
| 24 |
+
bottom: -2px;
|
| 25 |
+
left: 0;
|
| 26 |
+
width: 0;
|
| 27 |
+
height: 2px;
|
| 28 |
+
background: white;
|
| 29 |
+
transition: width 0.3s ease;
|
| 30 |
+
}
|
| 31 |
+
.nav-link:hover::after {
|
| 32 |
+
width: 100%;
|
| 33 |
+
}
|
| 34 |
+
@media (max-width: 768px) {
|
| 35 |
+
.mobile-hidden {
|
| 36 |
+
display: none;
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
</style>
|
| 40 |
+
<header class="text-white">
|
| 41 |
+
<nav class="container mx-auto px-4 py-4">
|
| 42 |
+
<div class="flex items-center justify-between">
|
| 43 |
+
<div class="flex items-center space-x-2">
|
| 44 |
+
<i data-feather="search" class="w-8 h-8"></i>
|
| 45 |
+
<h1 class="text-2xl font-bold">CodeFingerprint Detective</h1>
|
| 46 |
+
</div>
|
| 47 |
+
</nav>
|
| 48 |
+
</header>
|
| 49 |
+
`;
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
customElements.define('custom-header', CustomHeader);
|
index.html
CHANGED
|
@@ -1,19 +1,97 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>CodeFingerprint Detective</title>
|
| 7 |
+
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
| 8 |
+
<link rel="stylesheet" href="style.css">
|
| 9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
theme: {
|
| 15 |
+
extend: {
|
| 16 |
+
colors: {
|
| 17 |
+
cyan: {
|
| 18 |
+
50: '#ecfeff',
|
| 19 |
+
100: '#cffafe',
|
| 20 |
+
200: '#a5f3fc',
|
| 21 |
+
300: '#67e8f9',
|
| 22 |
+
400: '#22d3ee',
|
| 23 |
+
500: '#06b6d4',
|
| 24 |
+
600: '#0891b2',
|
| 25 |
+
700: '#0e7490',
|
| 26 |
+
800: '#155e75',
|
| 27 |
+
900: '#164e63',
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
</script>
|
| 34 |
+
</head>
|
| 35 |
+
<body class="bg-gradient-to-br from-cyan-50 to-cyan-100 min-h-screen">
|
| 36 |
+
<custom-header></custom-header>
|
| 37 |
+
|
| 38 |
+
<main class="container mx-auto px-4 py-8">
|
| 39 |
+
<div class="max-w-4xl mx-auto">
|
| 40 |
+
<!-- Upload Section -->
|
| 41 |
+
<section class="bg-white rounded-2xl shadow-xl p-8 mb-8 border border-cyan-200">
|
| 42 |
+
<div class="text-center mb-8">
|
| 43 |
+
<h1 class="text-4xl font-bold text-cyan-800 mb-4">CodeFingerprint Detective</h1>
|
| 44 |
+
<p class="text-cyan-600 text-lg">Upload multiple code files to detect their largest common similarities</p>
|
| 45 |
+
</div>
|
| 46 |
+
|
| 47 |
+
<div class="border-2 border-dashed border-cyan-300 rounded-xl p-8 text-center bg-cyan-50 transition-all duration-300 hover:border-cyan-400 hover:bg-cyan-100">
|
| 48 |
+
<i data-feather="upload-cloud" class="w-16 h-16 text-cyan-500 mx-auto mb-4"></i>
|
| 49 |
+
<input type="file" id="fileInput" multiple accept=".py,.js,.java,.cpp,.c,.html,.css,.txt,.md,.json,.xml" class="hidden">
|
| 50 |
+
<button id="uploadBtn" class="bg-cyan-500 hover:bg-cyan-600 text-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300 flex items-center justify-center mx-auto">
|
| 51 |
+
<i data-feather="upload" class="w-5 h-5 mr-2"></i>
|
| 52 |
+
Choose Files to Analyze
|
| 53 |
+
</button>
|
| 54 |
+
<p class="text-cyan-600 mt-4 text-sm">Supported: Python, JavaScript, Java, C++, C, HTML, CSS, Text, Markdown, JSON, XML</p>
|
| 55 |
+
</div>
|
| 56 |
+
|
| 57 |
+
<div id="fileList" class="mt-6 space-y-3 hidden">
|
| 58 |
+
<h3 class="text-lg font-semibold text-cyan-800 mb-3">Selected Files:</h3>
|
| 59 |
+
<div id="fileItems" class="space-y-2"></div>
|
| 60 |
+
</div>
|
| 61 |
+
|
| 62 |
+
<div class="mt-6 text-center">
|
| 63 |
+
<button id="analyzeBtn" class="bg-gradient-to-r from-cyan-500 to-cyan-600 hover:from-cyan-600 hover:to-cyan-700 text-white font-bold py-4 px-12 rounded-xl transition-all duration-300 transform hover:scale-105 shadow-lg hidden">
|
| 64 |
+
<i data-feather="search" class="w-5 h-5 mr-2 inline"></i>
|
| 65 |
+
Analyze Common Patterns
|
| 66 |
+
</button>
|
| 67 |
+
</div>
|
| 68 |
+
</section>
|
| 69 |
+
|
| 70 |
+
<!-- Results Section -->
|
| 71 |
+
<section id="resultsSection" class="bg-white rounded-2xl shadow-xl p-8 border border-cyan-200 hidden">
|
| 72 |
+
<h2 class="text-3xl font-bold text-cyan-800 mb-6 text-center">Analysis Results</h2>
|
| 73 |
+
<div id="resultsContainer" class="space-y-6"></div>
|
| 74 |
+
</section>
|
| 75 |
+
|
| 76 |
+
<!-- Loading State -->
|
| 77 |
+
<div id="loadingState" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
| 78 |
+
<div class="bg-white rounded-2xl p-8 text-center max-w-md">
|
| 79 |
+
<div class="animate-spin rounded-full h-16 w-16 border-b-2 border-cyan-500 mx-auto mb-4"></div>
|
| 80 |
+
<h3 class="text-xl font-semibold text-cyan-800 mb-2">Analyzing Files</h3>
|
| 81 |
+
<p class="text-cyan-600">Detecting largest common similarities...</p>
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
+
</main>
|
| 86 |
+
|
| 87 |
+
<custom-footer></custom-footer>
|
| 88 |
+
|
| 89 |
+
<script src="components/header.js"></script>
|
| 90 |
+
<script src="components/footer.js"></script>
|
| 91 |
+
<script src="script.js"></script>
|
| 92 |
+
<script>
|
| 93 |
+
feather.replace();
|
| 94 |
+
</script>
|
| 95 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 96 |
+
</body>
|
| 97 |
+
</html>
|
script.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class FileAnalyzer {
|
| 2 |
+
constructor() {
|
| 3 |
+
this.files = [];
|
| 4 |
+
this.results = [];
|
| 5 |
+
this.initializeEventListeners();
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
initializeEventListeners() {
|
| 9 |
+
const uploadBtn = document.getElementById('uploadBtn');
|
| 10 |
+
const fileInput = document.getElementById('fileInput');
|
| 11 |
+
const analyzeBtn = document.getElementById('analyzeBtn');
|
| 12 |
+
|
| 13 |
+
uploadBtn.addEventListener('click', () => fileInput.click());
|
| 14 |
+
fileInput.addEventListener('change', (e) => this.handleFileSelection(e));
|
| 15 |
+
analyzeBtn.addEventListener('click', () => this.analyzeFiles());
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
handleFileSelection(event) {
|
| 19 |
+
const files = Array.from(event.target.files);
|
| 20 |
+
if (files.length === 0) return;
|
| 21 |
+
|
| 22 |
+
this.files = files;
|
| 23 |
+
this.displayFileList();
|
| 24 |
+
document.getElementById('analyzeBtn').classList.remove('hidden');
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
displayFileList() {
|
| 28 |
+
const fileList = document.getElementById('fileList');
|
| 29 |
+
const fileItems = document.getElementById('fileItems');
|
| 30 |
+
|
| 31 |
+
fileItems.innerHTML = '';
|
| 32 |
+
|
| 33 |
+
this.files.forEach((file, index) => {
|
| 34 |
+
const fileItem = document.createElement('div');
|
| 35 |
+
fileItem.className = 'file-item flex items-center justify-between bg-cyan-50 rounded-lg p-3 border border-cyan-200';
|
| 36 |
+
fileItem.innerHTML = `
|
| 37 |
+
<div class="flex items-center">
|
| 38 |
+
<i data-feather="file-text" class="w-5 h-5 text-cyan-600 mr-3"></i>
|
| 39 |
+
<span class="text-cyan-800 font-medium code-font">${file.name}</span>
|
| 40 |
+
</div>
|
| 41 |
+
<span class="text-cyan-600 text-sm">${this.formatFileSize(file.size)}</span>
|
| 42 |
+
`;
|
| 43 |
+
fileItems.appendChild(fileItem);
|
| 44 |
+
});
|
| 45 |
+
|
| 46 |
+
fileList.classList.remove('hidden');
|
| 47 |
+
feather.replace();
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
formatFileSize(bytes) {
|
| 51 |
+
if (bytes === 0) return '0 Bytes';
|
| 52 |
+
const k = 1024;
|
| 53 |
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
| 54 |
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
| 55 |
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
async analyzeFiles() {
|
| 59 |
+
if (this.files.length < 2) {
|
| 60 |
+
this.showNotification('Please select at least 2 files to analyze', 'error');
|
| 61 |
+
return;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
this.showLoading(true);
|
| 65 |
+
|
| 66 |
+
try {
|
| 67 |
+
const fileContents = await this.readAllFiles();
|
| 68 |
+
this.results = this.findLCSAcrossFiles(fileContents);
|
| 69 |
+
this.displayResults();
|
| 70 |
+
} catch (error) {
|
| 71 |
+
this.showNotification('Error analyzing files: ' + error.message, 'error');
|
| 72 |
+
} finally {
|
| 73 |
+
this.showLoading(false);
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
readAllFiles() {
|
| 78 |
+
const promises = this.files.map(file => {
|
| 79 |
+
return new Promise((resolve, reject) => {
|
| 80 |
+
const reader = new FileReader();
|
| 81 |
+
reader.onload = e => resolve({
|
| 82 |
+
name: file.name,
|
| 83 |
+
content: e.target.result,
|
| 84 |
+
size: file.size
|
| 85 |
+
});
|
| 86 |
+
reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));
|
| 87 |
+
reader.readAsText(file);
|
| 88 |
+
});
|
| 89 |
+
});
|
| 90 |
+
|
| 91 |
+
return Promise.all(promises);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
findLCSAcrossFiles(fileContents) {
|
| 95 |
+
const results = [];
|
| 96 |
+
const lcsMap = new Map();
|
| 97 |
+
let tagCounter = 0;
|
| 98 |
+
|
| 99 |
+
// Generate all possible file pairs
|
| 100 |
+
for (let i = 0; i < fileContents.length; i++) {
|
| 101 |
+
for (let j = i + 1; j < fileContents.length; j++) {
|
| 102 |
+
const file1 = fileContents[i];
|
| 103 |
+
const file2 = fileContents[j];
|
| 104 |
+
|
| 105 |
+
const lcs = this.findLCS(file1.content, file2.content);
|
| 106 |
+
|
| 107 |
+
if (lcs.length > 10) { // Only consider significant LCS
|
| 108 |
+
const key = this.normalizeLCS(lcs);
|
| 109 |
+
if (!lcsMap.has(key)) {
|
| 110 |
+
const tag = String.fromCharCode(65 + (tagCounter++ % 26)); // A, B, C, ...
|
| 111 |
+
lcsMap.set(key, {
|
| 112 |
+
tag: tag,
|
| 113 |
+
content: lcs,
|
| 114 |
+
length: lcs.length,
|
| 115 |
+
files: new Set([file1.name, file2.name])
|
| 116 |
+
});
|
| 117 |
+
} else {
|
| 118 |
+
const existing = lcsMap.get(key);
|
| 119 |
+
existing.files.add(file1.name);
|
| 120 |
+
existing.files.add(file2.name);
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
// Convert map to array and sort by length
|
| 126 |
+
return Array.from(lcsMap.values())
|
| 127 |
+
.sort((a, b) => b.length - a.length)
|
| 128 |
+
.slice(0, 10); // Limit to top 10 results
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
findLCS(str1, str2) {
|
| 132 |
+
const m = str1.length;
|
| 133 |
+
const n = str2.length;
|
| 134 |
+
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
|
| 135 |
+
let maxLength = 0;
|
| 136 |
+
let endIndex = 0;
|
| 137 |
+
|
| 138 |
+
for (let i = 1; i <= m; i++) {
|
| 139 |
+
for (let j = 1; j <= n; j++) {
|
| 140 |
+
if (str1[i - 1] === str2[j - 1]) {
|
| 141 |
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
| 142 |
+
if (dp[i][j] > maxLength) {
|
| 143 |
+
maxLength = dp[i][j];
|
| 144 |
+
endIndex = i - 1;
|
| 145 |
+
}
|
| 146 |
+
} else {
|
| 147 |
+
dp[i][j] = 0;
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
return str1.substring(endIndex - maxLength + 1, endIndex + 1);
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
normalizeLCS(lcs) {
|
| 156 |
+
// Remove whitespace and normalize for comparison
|
| 157 |
+
return lcs.replace(/\s+/g, '').trim();
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
displayResults() {
|
| 161 |
+
const resultsSection = document.getElementById('resultsSection');
|
| 162 |
+
const resultsContainer = document.getElementById('resultsContainer');
|
| 163 |
+
|
| 164 |
+
resultsContainer.innerHTML = '';
|
| 165 |
+
|
| 166 |
+
if (this.results.length === 0) {
|
| 167 |
+
resultsContainer.innerHTML = `
|
| 168 |
+
<div class="text-center py-8">
|
| 169 |
+
<i data-feather="alert-circle" class="w-16 h-16 text-cyan-500 mx-auto mb-4"></i>
|
| 170 |
+
<p class="text-cyan-600 text-lg">No significant common patterns found</p>
|
| 171 |
+
</div>
|
| 172 |
+
`;
|
| 173 |
+
} else {
|
| 174 |
+
this.results.forEach((result, index) => {
|
| 175 |
+
const resultCard = document.createElement('div');
|
| 176 |
+
resultCard.className = 'fade-in gradient-border rounded-xl p-6 bg-white';
|
| 177 |
+
resultCard.style.animationDelay = `${index * 0.1}s`;
|
| 178 |
+
|
| 179 |
+
const fileList = Array.from(result.files).join(', ');
|
| 180 |
+
const preview = result.content.length > 200
|
| 181 |
+
? result.content.substring(0, 200) + '...'
|
| 182 |
+
: result.content;
|
| 183 |
+
|
| 184 |
+
resultCard.innerHTML = `
|
| 185 |
+
<div class="flex items-center justify-between mb-4">
|
| 186 |
+
<div class="flex items-center">
|
| 187 |
+
<div class="lcs-tag bg-gradient-to-r from-cyan-500 to-cyan-600 text-white rounded-full w-12 h-12 flex items-center justify-center font-bold text-lg shadow-lg">
|
| 188 |
+
${result.tag}
|
| 189 |
+
</div>
|
| 190 |
+
<div class="ml-4">
|
| 191 |
+
<h3 class="text-xl font-bold text-cyan-800">Pattern ${result.tag}</h3>
|
| 192 |
+
<p class="text-cyan-600">${result.length} characters</p>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
<span class="text-cyan-500 font-semibold">${result.files.size} files</span>
|
| 196 |
+
</div>
|
| 197 |
+
|
| 198 |
+
<div class="mb-4">
|
| 199 |
+
<h4 class="font-semibold text-cyan-700 mb-2">Files:</h4>
|
| 200 |
+
<p class="text-cyan-800 code-font text-sm bg-cyan-50 p-3 rounded-lg">
|
| 201 |
+
${fileList}
|
| 202 |
+
</p>
|
| 203 |
+
</div>
|
| 204 |
+
|
| 205 |
+
<div>
|
| 206 |
+
<h4 class="font-semibold text-cyan-700 mb-2">Common Content Preview:</h4>
|
| 207 |
+
<pre class="code-font bg-gray-900 text-cyan-100 p-4 rounded-lg overflow-x-auto text-sm">${this.escapeHtml(preview)}</pre>
|
| 208 |
+
</div>
|
| 209 |
+
`;
|
| 210 |
+
|
| 211 |
+
resultsContainer.appendChild(resultCard);
|
| 212 |
+
});
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
resultsSection.classList.remove('hidden');
|
| 216 |
+
feather.replace();
|
| 217 |
+
|
| 218 |
+
// Scroll to results
|
| 219 |
+
resultsSection.scrollIntoView({ behavior: 'smooth' });
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
escapeHtml(text) {
|
| 223 |
+
const div = document.createElement('div');
|
| 224 |
+
div.textContent = text;
|
| 225 |
+
return div.innerHTML;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
showLoading(show) {
|
| 229 |
+
const loadingState = document.getElementById('loadingState');
|
| 230 |
+
if (show) {
|
| 231 |
+
loadingState.classList.remove('hidden');
|
| 232 |
+
} else {
|
| 233 |
+
loadingState.classList.add('hidden');
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
showNotification(message, type = 'info') {
|
| 238 |
+
// Create notification element
|
| 239 |
+
const notification = document.createElement('div');
|
| 240 |
+
notification.className = `fixed top-4 right-4 p-4 rounded-xl shadow-lg z-50 fade-in ${
|
| 241 |
+
type === 'error' ? 'bg-red-500 text-white' : 'bg-cyan-500 text-white'
|
| 242 |
+
}`;
|
| 243 |
+
notification.innerHTML = `
|
| 244 |
+
<div class="flex items-center">
|
| 245 |
+
<i data-feather="${type === 'error' ? 'alert-triangle' : 'info'}" class="w-5 h-5 mr-2"></i>
|
| 246 |
+
<span>${message}</span>
|
| 247 |
+
</div>
|
| 248 |
+
`;
|
| 249 |
+
|
| 250 |
+
document.body.appendChild(notification);
|
| 251 |
+
feather.replace();
|
| 252 |
+
|
| 253 |
+
// Remove after 5 seconds
|
| 254 |
+
setTimeout(() => {
|
| 255 |
+
notification.remove();
|
| 256 |
+
}, 5000);
|
| 257 |
+
}
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
// Initialize the application when DOM is loaded
|
| 261 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 262 |
+
new FileAnalyzer();
|
| 263 |
+
});
|
style.css
CHANGED
|
@@ -1,28 +1,51 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
margin-top: 5px;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
.
|
| 19 |
-
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
.
|
| 27 |
-
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap');
|
| 2 |
+
|
| 3 |
+
* {
|
| 4 |
+
font-family: 'Inter', sans-serif;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
.code-font {
|
| 8 |
+
font-family: 'JetBrains Mono', monospace;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
.fade-in {
|
| 12 |
+
animation: fadeIn 0.5s ease-in-out;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
@keyframes fadeIn {
|
| 16 |
+
from { opacity: 0; transform: translateY(20px); }
|
| 17 |
+
to { opacity: 1; transform: translateY(0); }
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.pulse-cyan {
|
| 21 |
+
animation: pulseCyan 2s infinite;
|
| 22 |
}
|
| 23 |
|
| 24 |
+
@keyframes pulseCyan {
|
| 25 |
+
0%, 100% { box-shadow: 0 0 0 0 rgba(6, 182, 212, 0.7); }
|
| 26 |
+
50% { box-shadow: 0 0 0 10px rgba(6, 182, 212, 0); }
|
| 27 |
}
|
| 28 |
|
| 29 |
+
.gradient-border {
|
| 30 |
+
background: linear-gradient(white, white) padding-box,
|
| 31 |
+
linear-gradient(45deg, #06b6d4, #0891b2) border-box;
|
| 32 |
+
border: 2px solid transparent;
|
|
|
|
| 33 |
}
|
| 34 |
|
| 35 |
+
.file-item {
|
| 36 |
+
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
}
|
| 38 |
|
| 39 |
+
.file-item:hover {
|
| 40 |
+
transform: translateX(4px);
|
| 41 |
+
background-color: #f0fdfa;
|
| 42 |
}
|
| 43 |
+
|
| 44 |
+
.lcs-tag {
|
| 45 |
+
transition: all 0.3s ease;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.lcs-tag:hover {
|
| 49 |
+
transform: scale(1.05);
|
| 50 |
+
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.3);
|
| 51 |
+
}
|