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>