Spaces:
Running
Running
<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> |