-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
268 lines (241 loc) · 12.8 KB
/
index.html
File metadata and controls
268 lines (241 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Requirements Diff Tool</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts - Inter -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
font-size: 12pt; /* Set font size to 12pt */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: transparent; /* Ensures the body background is transparent */
display: flex;
justify-content: center;
align-items: flex-start; /* Align to top */
min-height: 100vh;
padding: 2rem 1rem;
}
.container {
background-color: transparent; /* Ensures the main container background is transparent */
border-radius: 0.75rem; /* rounded-xl */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-xl */
padding: 2.5rem; /* p-10 */
max-width: 90%; /* Responsive width */
width: 1024px; /* lg:w-3/4 on large screens, or max width */
display: flex;
flex-direction: column;
gap: 1.5rem; /* space-y-6 */
}
textarea {
min-height: 200px;
resize: vertical;
}
/* Custom highlight styles */
.text-strikethrough-default { /* Renamed class for clarity */
text-decoration: line-through;
background-color: #f0f0f0; /* Added light grey background for strikethrough */
}
.message-box {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: #333;
color: white;
padding: 10px 20px;
border-radius: 5px;
opacity: 0;
transition: opacity 0.3s ease-in-out;
z-index: 1000;
}
.message-box.show {
opacity: 1;
}
/* Explicitly force transparent background on the output diff box and set font */
#outputDiff {
background-color: transparent !important;
font-family: 'Inter', sans-serif; /* Ensure font is Inter */
font-size: 12pt; /* Ensure font size is 12pt */
}
</style>
</head>
<body class="p-8">
<div class="container mx-auto p-8 rounded-xl shadow-lg flex flex-col items-center">
<h1 class="text-4xl font-extrabold text-gray-900 mb-6 text-center">Requirements Diff Tool</h1>
<div class="w-full grid grid-cols-2 gap-6 mb-8">
<div>
<label for="originalText" class="block text-lg font-medium text-gray-700 mb-2">Current requirements (paste from site):</label>
<textarea id="originalText"
class="w-full p-4 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 transition duration-150 ease-in-out text-gray-800"
placeholder="Paste your original text here..."></textarea>
</div>
<div>
<label for="newText" class="block text-lg font-medium text-gray-700 mb-2">Preview requirements (paste from proposal):</label>
<textarea id="newText"
class="w-full p-4 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 transition duration-150 ease-in-out text-gray-800"
placeholder="Paste the new or modified text here..."></textarea>
</div>
</div>
<div class="flex flex-col sm:flex-row gap-4 mb-8">
<button id="compareButton"
class="px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105">
Compare Texts
</button>
<button id="copyDiffButton"
class="px-8 py-4 bg-gray-600 text-white font-semibold rounded-lg shadow-md hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition duration-300 ease-in-out transform hover:scale-105">
Copy Diff
</button>
</div>
<div id="outputDiff" class="w-full p-6 bg-transparent border border-gray-300 rounded-lg shadow-sm min-h-[150px] text-gray-800 leading-relaxed"> <!-- Removed text-lg -->
<p class="text-center text-gray-500">Your comparison results will appear here.</p>
</div>
</div>
<div id="messageBox" class="message-box"></div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const originalTextarea = document.getElementById('originalText');
const newTextarea = document.getElementById('newText');
const compareButton = document.getElementById('compareButton');
const copyDiffButton = document.getElementById('copyDiffButton');
const outputDiffDiv = document.getElementById('outputDiff');
const messageBox = document.getElementById('messageBox');
// Set some initial placeholder text for demonstration
originalTextarea.value = `This is the original text. It has a few sentences. Some words are removed. And some lines might change significantly.
GMS-123: This is a requirement for the new system.
CDD-001: Another important detail is noted here.
CDD-002: This requirement is being removed.
BUG-456: Fix login validation issue.
Final paragraph of original document.`;
newTextarea.value = `This is the modified text. It has several new sentences. New words are added. And some lines might change drastically.
GMS-123: This is an updated requirement for the revised system.
CDD-001: Another important detail is updated here.
FEAT-789: Add password reset functionality.
BUG-456: Login validation issue has been resolved.
Final paragraph of modified document. With a new sentence.`;
compareButton.addEventListener('click', () => {
const originalText = originalTextarea.value;
const newText = newTextarea.value;
const diffHtml = generateDiffHtml(originalText, newText);
outputDiffDiv.innerHTML = diffHtml || '<p class="text-center text-gray-500">No differences found, or texts are empty.</p>';
});
copyDiffButton.addEventListener('click', () => {
// Get the HTML content from the diff output
const diffHtmlContent = outputDiffDiv.innerHTML;
// Create a temporary, hidden div to hold the HTML content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = diffHtmlContent;
tempDiv.style.position = 'absolute';
tempDiv.style.left = '-9999px'; // Move it off-screen
document.body.appendChild(tempDiv);
// Select the content of the temporary div
const range = document.createRange();
range.selectNodeContents(tempDiv);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
// Execute the copy command
document.execCommand('copy');
showMessage('Diff text with formatting copied to clipboard!');
} catch (err) {
console.error('Failed to copy formatted text:', err);
showMessage('Failed to copy formatted text. Please try again or copy manually.');
} finally {
// Clean up the selection and remove the temporary div
selection.removeAllRanges();
document.body.removeChild(tempDiv);
}
});
/**
* Displays a temporary message box.
* @param {string} message The message to display.
*/
function showMessage(message) {
messageBox.textContent = message;
messageBox.classList.add('show');
setTimeout(() => {
messageBox.classList.remove('show');
}, 3000); // Message disappears after 3 seconds
}
/**
* Generates HTML highlighting differences between two texts using a word-by-word diff.
* New content is highlighted in forest green, removed content is struck through.
* @param {string} originalText
* @param {string} newText
* @returns {string} HTML string with highlighted differences.
*/
function generateDiffHtml(originalText, newText) {
// Tokenizer that splits text into words, punctuation, and individual whitespace characters (including newlines)
const tokenize = (text) => {
// Regex to split by:
// 1. Groups of word characters (\w+)
// 2. Individual non-word, non-whitespace characters ([^\w\s]) - captures punctuation
// 3. Individual whitespace characters (\s) - captures ' ', '\n', '\t'
// Wrapping them in parentheses ensures `split` includes them in the result.
return text.split(/(\w+|[^\w\s]|\s)/).filter(Boolean); // Filter(Boolean) removes empty strings
};
const originalTokens = tokenize(originalText);
const newTokens = tokenize(newText);
const m = originalTokens.length;
const n = newTokens.length;
// Create a 2D array for dynamic programming table (LCS lengths)
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
// Fill the dp table
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
// Compare tokens directly. Trim for comparison to handle minor whitespace differences.
if (originalTokens[i - 1].trim() === newTokens[j - 1].trim()) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
// Reconstruct the diff by backtracking through the dp table
let htmlResult = [];
let i = m;
let j = n;
while (i > 0 || j > 0) {
const currentOriginalToken = i > 0 ? originalTokens[i - 1] : null;
const currentNewToken = j > 0 ? newTokens[j - 1] : null;
// Compare trimmed versions for matching
const tokensMatch = (currentOriginalToken && currentNewToken && currentOriginalToken.trim() === currentNewToken.trim());
if (tokensMatch) {
// Common token: add as is (use original string for display)
if (currentOriginalToken === '\n') {
htmlResult.unshift('<br>');
} else {
htmlResult.unshift(currentOriginalToken);
}
i--;
j--;
} else if (i > 0 && (j === 0 || dp[i - 1][j] >= dp[i][j - 1])) {
// Token removed from originalText (deletion)
if (currentOriginalToken === '\n') {
htmlResult.unshift('<br>'); // Keep newlines as is, don't strike out
} else {
htmlResult.unshift(`<span class="text-strikethrough-default">${currentOriginalToken}</span>`); // Removed inline style
}
i--;
} else if (j > 0 && (i === 0 || dp[i][j - 1] > dp[i - 1][j])) {
// Token added in newText (insertion)
if (currentNewToken === '\n') {
htmlResult.unshift('<br>'); // Keep newlines as is, don't highlight
} else {
htmlResult.unshift(`<span style="background-color: #d9ead3; padding: 0 2px; border-radius: 0.25rem;">${currentNewToken}</span>`);
}
j--;
}
}
return htmlResult.join('');
}
});
</script>
</body>
</html>