Spaces:
Running
Running
File size: 18,964 Bytes
8bc8450 |
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 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern Regex Generator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%);
min-height: 100vh;
}
.card {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15);
}
.input-box {
background: linear-gradient(90deg, rgba(245, 245, 245, 0.8) 0%, rgba(255, 255, 255, 0.9) 100%);
border-radius: 12px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(118, 75, 162, 0.4);
}
.regex-output {
font-family: 'Courier New', monospace;
background: rgba(0, 0, 0, 0.05);
padding: 0.5rem;
border-radius: 6px;
word-break: break-all;
}
.match-highlight {
background-color: rgba(102, 126, 234, 0.2);
padding: 0.2rem;
border-radius: 4px;
border-left: 3px solid #667eea;
display: inline-block; /* For better spacing of multiple matches */
margin-right: 4px; /* Spacing between matches */
margin-bottom: 4px; /* Spacing if matches wrap */
}
</style>
</head>
<body class="flex items-center justify-center p-4 md:p-8">
<div class="w-full max-w-4xl space-y-6">
<div class="text-center mb-8">
<h1 class="text-3xl md:text-4xl font-bold text-white drop-shadow-lg">Regex Generator</h1>
<p class="text-white/90 mt-2">Transform your natural language requests into regular expressions</p>
</div>
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-semibold text-gray-800">Chatbox</h2>
<button id="generateBtn" class="btn-primary px-4 py-2 rounded-lg font-medium flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v3.586L7.707 9.293a1 1 0 00-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 10.586V7z" clip-rule="evenodd" />
</svg>
Generate
</button>
</div>
<textarea id="chatbox" placeholder="e.g. extract emails, find phone numbers, match dates" class="input-box w-full p-4 border border-gray-200 focus:border-purple-300 focus:ring-2 focus:ring-purple-200 outline-none transition duration-200 h-24"></textarea>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="card p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Regex Output</h2>
<div id="regexOutput" class="regex-output p-4 bg-gray-50 rounded-lg">/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/</div>
<div class="mt-4 flex space-x-3">
<button id="copyRegexBtn" class="btn-primary px-4 py-2 rounded-lg font-medium flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
Copy
</button>
<button id="testRegexBtn" class="bg-white border border-gray-200 px-4 py-2 rounded-lg font-medium flex items-center hover:bg-gray-50 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
Test
</button>
</div>
</div>
<div class="card p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Test Input</h2>
<textarea id="testInput" placeholder="Text to test your regex against..." class="input-box w-full p-4 border border-gray-200 focus:border-purple-300 focus:ring-2 focus:ring-purple-200 outline-none transition duration-200 h-28"></textarea>
<div class="mt-4">
<h3 class="font-medium text-gray-700 mb-2">Applied Regex Output:</h3>
<div id="appliedRegexOutput" class="p-3 bg-gray-50 rounded-lg min-h-[3rem]">
</div>
</div>
</div>
</div>
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-semibold text-gray-800">Saved Regex Patterns</h2>
<button id="addPatternBtn" class="btn-primary px-3 py-1 rounded-lg font-medium flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
</svg>
</button>
</div>
<div id="savedPatterns" class="space-y-2">
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const generateBtn = document.getElementById('generateBtn');
const chatboxEl = document.getElementById('chatbox');
const regexOutputEl = document.getElementById('regexOutput');
const copyRegexBtn = document.getElementById('copyRegexBtn');
const testRegexBtn = document.getElementById('testRegexBtn');
const testInputEl = document.getElementById('testInput');
const appliedRegexOutputEl = document.getElementById('appliedRegexOutput');
const addPatternBtn = document.getElementById('addPatternBtn');
const savedPatternsEl = document.getElementById('savedPatterns');
// Generate button click handler
generateBtn.addEventListener('click', function() {
const chatboxValue = chatboxEl.value.trim();
if (chatboxValue) {
regexOutputEl.textContent = generateRegexFromPrompt(chatboxValue);
testRegexBtn.click(); // Automatically test after generating
} else {
chatboxEl.focus();
}
});
// Copy regex button
copyRegexBtn.addEventListener('click', function() {
const regex = regexOutputEl.textContent;
if (!regex) return;
navigator.clipboard.writeText(regex).then(() => {
const originalHTML = this.innerHTML; // Save current content
const btn = this;
btn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
Copied!
`;
setTimeout(() => {
btn.innerHTML = originalHTML; // Restore original content
}, 2000);
}).catch(err => {
console.error('Failed to copy regex: ', err);
alert('Failed to copy. Please copy manually.');
});
});
// Test regex button
testRegexBtn.addEventListener('click', function() {
const regexStringSource = regexOutputEl.textContent.trim();
const testInputValue = testInputEl.value;
appliedRegexOutputEl.innerHTML = ''; // Clear previous results
if (!regexStringSource) {
appliedRegexOutputEl.textContent = 'No regex to test.';
return;
}
try {
let pattern;
let flagsAttribute = ''; // Flags found in the /pattern/flags string
// Try to parse /pattern/flags format first
// This regex captures the pattern between slashes and any flags after the second slash.
// It expects at least one character for the pattern: /.+/
const regexParts = regexStringSource.match(/^\/(.+)\/([gimyus]*)$/);
if (regexParts) {
// Successfully parsed /pattern/flags (e.g., "/abc/gi" or "/xyz/")
pattern = regexParts[1]; // Content between slashes
flagsAttribute = regexParts[2]; // Flags after the last slash (can be empty)
} else if (regexStringSource.startsWith('/') && regexStringSource.endsWith('/') && regexStringSource.length >= 2) {
// Handles "/pattern/" case more explicitly if regexParts didn't match (e.g. empty pattern like "//")
// This extracts content between the first and last slash.
pattern = regexStringSource.substring(1, regexStringSource.length - 1);
// flagsAttribute remains ""
} else {
// Not in /.../... format (e.g. "abc" or "/abc" (no trailing slash))
// Treat the whole string as the pattern, no flags from string
pattern = regexStringSource;
}
if (pattern === "" && !regexStringSource.match(/^\/\/([gimyus]*)$/)) { // Allow empty pattern only if it was explicitly `//` or `///flags`
appliedRegexOutputEl.textContent = 'Pattern is effectively empty or malformed.';
return;
}
// Combine flags from string with a default 'g' for global matching
let finalFlags = flagsAttribute;
if (!finalFlags.includes('g')) {
finalFlags += 'g';
}
// Ensure unique flags and valid order (though order doesn't strictly matter for RegExp constructor)
finalFlags = Array.from(new Set(finalFlags.split(''))).join('');
const regex = new RegExp(pattern, finalFlags);
const matches = testInputValue.match(regex); // .match with 'g' flag returns array of matched strings or null
if (matches && matches.length > 0) {
matches.forEach(matchText => {
const span = document.createElement('span');
span.className = 'match-highlight';
span.textContent = matchText;
appliedRegexOutputEl.appendChild(span);
});
} else {
appliedRegexOutputEl.textContent = 'No matches found';
}
} catch (e) {
console.error("Regex error:", e);
appliedRegexOutputEl.textContent = 'Error: ' + e.message;
}
});
// Add pattern button
addPatternBtn.addEventListener('click', function() {
const regex = regexOutputEl.textContent.trim();
if (regex) {
const newPatternDiv = document.createElement('div');
// Add a specific class to identify these dynamically added items
newPatternDiv.className = 'p-3 bg-gray-50 rounded-lg flex justify-between items-center saved-pattern-item';
const regexSpan = document.createElement('span');
regexSpan.className = 'regex-output';
regexSpan.textContent = regex;
const controlsDiv = document.createElement('div');
controlsDiv.className = 'flex space-x-2';
const useBtn = document.createElement('button');
useBtn.className = 'p-1 text-gray-500 hover:text-purple-600 use-pattern-btn';
useBtn.title = "Use Pattern";
useBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
</svg>`;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'p-1 text-gray-500 hover:text-red-500 delete-pattern-btn';
deleteBtn.title = "Delete Pattern";
deleteBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>`;
controlsDiv.appendChild(useBtn);
controlsDiv.appendChild(deleteBtn);
newPatternDiv.appendChild(regexSpan);
newPatternDiv.appendChild(controlsDiv);
savedPatternsEl.appendChild(newPatternDiv);
}
});
// Event delegation for dynamically added "Use" and "Delete" buttons in saved patterns
savedPatternsEl.addEventListener('click', function(event) {
const button = event.target.closest('button'); // Get the button element
if (!button) return; // Exit if the click was not on a button or its child
const patternItem = button.closest('.saved-pattern-item');
if (!patternItem) return; // Exit if not part of a saved pattern item
const regexText = patternItem.querySelector('.regex-output').textContent;
if (button.classList.contains('use-pattern-btn')) {
regexOutputEl.textContent = regexText;
testInputEl.focus(); // Focus input for convenience
testRegexBtn.click(); // Automatically test when a saved pattern is used
} else if (button.classList.contains('delete-pattern-btn')) {
patternItem.remove();
}
});
// Simple regex generator for demo purposes
function generateRegexFromPrompt(prompt) {
prompt = prompt.toLowerCase();
if (prompt.includes('email') || prompt.includes('e-mail')) {
return '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/';
} else if (prompt.includes('phone') || prompt.includes('number')) {
return '/(?:\\+?\\d{1,3}[-.\\s]?)?(?:\\(?\\d{3}\\)?[-.\\s]?)?\\d{3}[-.\\s]?\\d{4}/';
} else if (prompt.includes('url') || prompt.includes('website') || prompt.includes('web address')) {
return '/https?:\/\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%._\\+.~#?&//=]*)/';
} else if (prompt.includes('date')) {
return '/(?:\\d{1,2}[\\/\\-.](?:0?[1-9]|1[0-2])[\\/\\-.]\\d{2,4})|(?:\\d{4}[\\/\\-.](?:0?[1-9]|1[0-2])[\\/\\-.]\\d{1,2})|(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s\\d{1,2}(?:st|nd|rd|th)?,?\\s\\d{4})/i';
} else if (prompt.includes('ip address') || prompt.includes('ipv4')) {
return '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b/';
} else if (prompt.includes('hex color') || prompt.includes('hexcode')) {
return '/#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})\\b/';
} else if (prompt.includes('html tag')) {
return '/<([a-z][a-z0-9]*)\\b[^>]*>(.*?)<\\/\\1>/is';
}
else {
// Default regex for extraction - any sequence of word characters
return '/\\b\\w+\\b/g';
}
}
// Set an example in testInput for initial demo if it's empty
if (!testInputEl.value) {
testInputEl.value = "My email is user@example.com, test.user@sub.example.org.\nMy site is http://example.com and https://www.another-site.co.uk/path?query=value.\nCall +1 (555) 123-4567 or 555-123-1234.\nDates: 12/31/2025, 2024-01-15, Feb 14th, 2023.\nIP: 192.168.1.1. Color: #F00 or #A2B3C4.\nTags: <p>Hello</p> <div>World</div>";
}
// Clear any hardcoded initial placeholder content in appliedRegexOutput
appliedRegexOutputEl.innerHTML = '';
// Perform an initial test if there's default regex and test input
if (regexOutputEl.textContent && testInputEl.value) {
testRegexBtn.click();
}
});
</script>
</body>
</html> |