infinite-agents-public/claude_code_devtools/claude_devtool_8.html

1080 lines
36 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Pattern Search - Claude Code DevTools</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #1a1b26;
--bg-secondary: #24283b;
--bg-tertiary: #414868;
--text-primary: #c0caf5;
--text-secondary: #a9b1d6;
--text-muted: #565f89;
--accent-primary: #7aa2f7;
--accent-secondary: #bb9af7;
--accent-success: #9ece6a;
--accent-warning: #e0af68;
--accent-error: #f7768e;
--border-color: #414868;
--code-bg: #1f2335;
}
body {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
header {
background: var(--bg-secondary);
padding: 2rem;
border-bottom: 2px solid var(--border-color);
}
h1 {
font-size: 2rem;
color: var(--accent-primary);
margin-bottom: 0.5rem;
}
.tagline {
color: var(--text-secondary);
font-size: 1rem;
}
main {
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
.tool-interface {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
.search-controls {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.control-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
label {
color: var(--text-secondary);
font-size: 0.9rem;
font-weight: 600;
}
input[type="text"], textarea {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
padding: 0.75rem;
font-family: inherit;
font-size: 0.95rem;
transition: border-color 0.2s;
}
input[type="text"]:focus, textarea:focus {
outline: none;
border-color: var(--accent-primary);
}
textarea {
resize: vertical;
min-height: 100px;
font-size: 0.9rem;
}
.regex-input-wrapper {
position: relative;
}
.regex-validation {
position: absolute;
right: 0.75rem;
top: 50%;
transform: translateY(-50%);
font-size: 0.8rem;
font-weight: 600;
}
.regex-validation.valid {
color: var(--accent-success);
}
.regex-validation.invalid {
color: var(--accent-error);
}
.flags-container {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.flag-checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.flag-checkbox input[type="checkbox"] {
cursor: pointer;
width: 18px;
height: 18px;
}
.flag-checkbox label {
cursor: pointer;
font-size: 0.9rem;
margin: 0;
}
.flag-description {
color: var(--text-muted);
font-size: 0.75rem;
margin-left: 1.5rem;
}
.button-group {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
button {
background: var(--accent-primary);
color: var(--bg-primary);
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-family: inherit;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
button:hover {
background: var(--accent-secondary);
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
button.secondary {
background: var(--bg-tertiary);
color: var(--text-primary);
}
button.secondary:hover {
background: var(--border-color);
}
.pattern-library {
margin-top: 1.5rem;
padding: 1rem;
background: var(--code-bg);
border-radius: 4px;
border: 1px solid var(--border-color);
}
.pattern-library h3 {
color: var(--accent-secondary);
margin-bottom: 1rem;
font-size: 1rem;
}
.pattern-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 0.75rem;
}
.pattern-item {
background: var(--bg-secondary);
padding: 0.75rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid transparent;
}
.pattern-item:hover {
border-color: var(--accent-primary);
transform: translateY(-2px);
}
.pattern-name {
color: var(--accent-primary);
font-weight: 600;
font-size: 0.85rem;
margin-bottom: 0.25rem;
}
.pattern-regex {
color: var(--accent-success);
font-size: 0.75rem;
margin-bottom: 0.25rem;
word-break: break-all;
}
.pattern-desc {
color: var(--text-muted);
font-size: 0.7rem;
}
.results {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.results-stats {
color: var(--text-secondary);
font-size: 0.9rem;
}
.results-stats .count {
color: var(--accent-primary);
font-weight: 600;
font-size: 1.1rem;
}
.match-item {
background: var(--code-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 1rem;
margin-bottom: 1rem;
transition: border-color 0.2s;
}
.match-item:hover {
border-color: var(--accent-primary);
}
.match-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: 0.75rem;
}
.match-index {
color: var(--accent-warning);
font-weight: 600;
font-size: 0.85rem;
}
.match-position {
color: var(--text-muted);
font-size: 0.75rem;
}
.match-content {
background: var(--bg-primary);
padding: 1rem;
border-radius: 4px;
margin-bottom: 0.75rem;
overflow-x: auto;
font-size: 0.9rem;
line-height: 1.8;
}
.match-highlight {
background: var(--accent-warning);
color: var(--bg-primary);
padding: 2px 4px;
border-radius: 2px;
font-weight: 600;
}
.capture-groups {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.capture-group {
background: var(--bg-secondary);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.85rem;
}
.capture-label {
color: var(--accent-secondary);
font-weight: 600;
margin-right: 0.5rem;
}
.capture-value {
color: var(--accent-success);
}
.regex-tester {
margin-top: 1.5rem;
padding: 1.5rem;
background: var(--code-bg);
border-radius: 4px;
border: 1px solid var(--border-color);
}
.regex-tester h3 {
color: var(--accent-primary);
margin-bottom: 1rem;
font-size: 1rem;
}
.test-results {
margin-top: 1rem;
padding: 1rem;
background: var(--bg-primary);
border-radius: 4px;
min-height: 100px;
max-height: 300px;
overflow-y: auto;
}
.no-results {
text-align: center;
color: var(--text-muted);
padding: 2rem;
font-style: italic;
}
.docs {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 2rem;
}
.docs h2 {
color: var(--accent-primary);
margin-bottom: 1.5rem;
font-size: 1.5rem;
}
.docs h3 {
color: var(--accent-secondary);
margin-top: 1.5rem;
margin-bottom: 0.75rem;
font-size: 1.1rem;
}
.docs p {
color: var(--text-secondary);
margin-bottom: 1rem;
}
.docs ul, .docs ol {
color: var(--text-secondary);
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.docs li {
margin-bottom: 0.5rem;
}
.docs code {
background: var(--code-bg);
color: var(--accent-success);
padding: 2px 6px;
border-radius: 3px;
font-size: 0.9rem;
}
.docs strong {
color: var(--accent-primary);
}
.docs a {
color: var(--accent-primary);
text-decoration: none;
}
.docs a:hover {
text-decoration: underline;
}
footer {
text-align: center;
padding: 2rem;
color: var(--text-muted);
font-size: 0.85rem;
border-top: 1px solid var(--border-color);
}
footer a {
color: var(--accent-primary);
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
.session-selector {
margin-bottom: 1rem;
}
.session-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.session-chip {
background: var(--bg-tertiary);
color: var(--text-primary);
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.85rem;
cursor: pointer;
border: 1px solid transparent;
transition: all 0.2s;
}
.session-chip.active {
background: var(--accent-primary);
color: var(--bg-primary);
border-color: var(--accent-primary);
}
.session-chip:hover {
border-color: var(--accent-primary);
}
.loading {
text-align: center;
padding: 2rem;
color: var(--accent-primary);
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid var(--border-color);
border-top-color: var(--accent-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
</style>
</head>
<body>
<header>
<h1>Advanced Pattern Search</h1>
<p class="tagline">Power-user regex search with lookahead/lookbehind, named groups, and advanced pattern matching</p>
</header>
<main>
<section class="tool-interface">
<div class="search-controls">
<div class="control-group">
<label for="regex-pattern">Regex Pattern</label>
<div class="regex-input-wrapper">
<input type="text" id="regex-pattern" placeholder="e.g., (?<=Error:\s).*?(?=\n)" />
<span class="regex-validation" id="regex-validation"></span>
</div>
</div>
<div class="control-group">
<label>Regex Flags</label>
<div class="flags-container">
<div class="flag-checkbox">
<input type="checkbox" id="flag-g" checked />
<label for="flag-g">g - Global</label>
</div>
<div class="flag-checkbox">
<input type="checkbox" id="flag-i" />
<label for="flag-i">i - Case Insensitive</label>
</div>
<div class="flag-checkbox">
<input type="checkbox" id="flag-m" />
<label for="flag-m">m - Multiline</label>
</div>
<div class="flag-checkbox">
<input type="checkbox" id="flag-s" />
<label for="flag-s">s - Dot All</label>
</div>
<div class="flag-checkbox">
<input type="checkbox" id="flag-u" />
<label for="flag-u">u - Unicode</label>
</div>
</div>
</div>
<div class="button-group">
<button onclick="searchInSampleData()">Search Sample Data</button>
<button class="secondary" onclick="clearResults()">Clear Results</button>
<button class="secondary" onclick="exportMatches()">Export Matches</button>
</div>
</div>
<div class="pattern-library">
<h3>Pattern Library - Click to Use</h3>
<div class="pattern-grid" id="pattern-grid"></div>
</div>
<div class="regex-tester">
<h3>Live Regex Tester</h3>
<div class="control-group">
<label for="test-text">Test Text</label>
<textarea id="test-text" placeholder="Enter text to test your regex pattern..."></textarea>
</div>
<button onclick="testRegex()" style="margin-top: 1rem;">Test Pattern</button>
<div class="test-results" id="test-results"></div>
</div>
</section>
<section class="results">
<div class="results-header">
<h2>Search Results</h2>
<div class="results-stats">
<span class="count" id="match-count">0</span> matches found
</div>
</div>
<div id="results-container">
<div class="no-results">Enter a pattern and search to see results</div>
</div>
</section>
<section class="docs">
<h2>About This Tool</h2>
<h3>Purpose</h3>
<p>Advanced Pattern Search is a power-user tool for performing sophisticated regex-based searches across Claude Code transcripts. It supports modern JavaScript regex features including lookahead/lookbehind assertions, named capturing groups, and all regex flags for maximum search precision.</p>
<h3>Features</h3>
<ul>
<li><strong>Advanced Regex Support</strong> - Lookahead, lookbehind, named groups, unicode escapes</li>
<li><strong>Real-time Validation</strong> - Live validation of regex patterns as you type</li>
<li><strong>All Regex Flags</strong> - Support for g, i, m, s, u flags with descriptions</li>
<li><strong>Pattern Library</strong> - Pre-built patterns for common searches (errors, files, code blocks)</li>
<li><strong>Capture Group Display</strong> - Visual highlighting of matched groups and named captures</li>
<li><strong>Live Regex Tester</strong> - Test patterns against sample text before searching</li>
<li><strong>Match Statistics</strong> - Count, position, and context for every match</li>
<li><strong>Export Capability</strong> - Export matches as JSON for further analysis</li>
</ul>
<h3>Web Research Integration</h3>
<p><strong>Source:</strong> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" target="_blank">MDN - JavaScript Regular Expressions Guide</a></p>
<p><strong>Techniques Applied:</strong></p>
<ul>
<li><strong>Lookahead/Lookbehind Assertions</strong> - Implemented positive/negative lookahead (<code>(?=...)</code>, <code>(?!...)</code>) and lookbehind (<code>(?&lt;=...)</code>, <code>(?&lt;!...)</code>) for context-aware matching without consuming characters</li>
<li><strong>Named Capturing Groups</strong> - Support for <code>(?&lt;name>...)</code> syntax with visual display of named captures and backreferences <code>\k&lt;name></code></li>
<li><strong>Modern Regex Flags</strong> - Complete flag support including dotAll mode (s flag) for matching newlines and unicode mode (u flag) for proper unicode handling</li>
</ul>
<h3>Pattern Library Examples</h3>
<ul>
<li><strong>Error Messages</strong> - <code>(?&lt;=Error:\s).*?(?=\n|$)</code> - Uses lookbehind to match text after "Error: " and lookahead to stop at newline</li>
<li><strong>File Paths</strong> - <code>(?&lt;path>[\w/.-]+\.(?&lt;ext>\w+))</code> - Named groups for full path and extension</li>
<li><strong>Code Blocks</strong> - <code>```(?&lt;lang>\w+)\n(?&lt;code>[\s\S]*?)```</code> - Named captures for language and code content</li>
<li><strong>URLs</strong> - <code>https?://(?&lt;domain>[\w.-]+)(?&lt;path>/[\w/.-]*)?</code> - Separate domain and path captures</li>
<li><strong>Email Addresses</strong> - <code>(?&lt;user>[\w.+-]+)@(?&lt;domain>[\w.-]+)</code> - User and domain named groups</li>
</ul>
<h3>Usage</h3>
<ol>
<li><strong>Enter Pattern</strong> - Type your regex pattern or select from the pattern library</li>
<li><strong>Configure Flags</strong> - Select appropriate flags (global, case-insensitive, multiline, etc.)</li>
<li><strong>Test Pattern</strong> - Use the live tester to verify your pattern works as expected</li>
<li><strong>Search</strong> - Execute search against sample data or loaded transcript sessions</li>
<li><strong>Analyze Results</strong> - Review matches with highlighted capture groups and context</li>
<li><strong>Export</strong> - Export results as JSON for further processing</li>
</ol>
<h3>Advanced Regex Tips</h3>
<ul>
<li><strong>Lookahead</strong> - <code>foo(?=bar)</code> matches "foo" only when followed by "bar", without consuming "bar"</li>
<li><strong>Lookbehind</strong> - <code>(?&lt;=foo)bar</code> matches "bar" only when preceded by "foo"</li>
<li><strong>Negative Lookahead</strong> - <code>foo(?!bar)</code> matches "foo" NOT followed by "bar"</li>
<li><strong>Named Groups</strong> - <code>(?&lt;year>\d{4})-(?&lt;month>\d{2})</code> creates accessible named captures</li>
<li><strong>Lazy Quantifiers</strong> - Add <code>?</code> after <code>*</code> or <code>+</code> for minimal matching</li>
</ul>
</section>
</main>
<footer>
<p>Claude Code DevTools | Advanced Pattern Search Tool (Iteration 8)</p>
<p>Web Source: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" target="_blank">MDN - JavaScript Regular Expressions Guide</a></p>
</footer>
<script>
// Pattern Library - Pre-built patterns for common searches
const patternLibrary = [
{
name: 'Error Messages',
regex: '(?<=Error:\\s).*?(?=\\n|$)',
description: 'Matches error messages after "Error: " prefix',
flags: 'gm'
},
{
name: 'Warning Messages',
regex: '(?<=Warning:\\s).*?(?=\\n|$)',
description: 'Matches warning messages',
flags: 'gm'
},
{
name: 'File Paths',
regex: '(?<path>[\\w/.-]+\\.(?<ext>\\w+))',
description: 'Matches file paths with named groups for path and extension',
flags: 'g'
},
{
name: 'Code Blocks',
regex: '```(?<lang>\\w+)\\n(?<code>[\\s\\S]*?)```',
description: 'Matches markdown code blocks with language and code',
flags: 'g'
},
{
name: 'URLs',
regex: 'https?://(?<domain>[\\w.-]+)(?<path>/[\\w/.-]*)?',
description: 'Matches URLs with domain and path groups',
flags: 'g'
},
{
name: 'Email Addresses',
regex: '(?<user>[\\w.+-]+)@(?<domain>[\\w.-]+)',
description: 'Matches emails with user and domain groups',
flags: 'g'
},
{
name: 'Function Calls',
regex: '(?<func>\\w+)\\((?<args>[^)]*)\\)',
description: 'Matches function calls with name and arguments',
flags: 'g'
},
{
name: 'JSON Keys',
regex: '"(?<key>\\w+)"\\s*:',
description: 'Matches JSON object keys',
flags: 'g'
},
{
name: 'Hex Colors',
regex: '#(?<hex>[0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})',
description: 'Matches hex color codes',
flags: 'gi'
},
{
name: 'IP Addresses',
regex: '(?<ip>\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b)',
description: 'Matches IPv4 addresses',
flags: 'g'
},
{
name: 'Timestamps',
regex: '(?<hour>\\d{2}):(?<minute>\\d{2}):(?<second>\\d{2})',
description: 'Matches HH:MM:SS timestamps',
flags: 'g'
},
{
name: 'ISO Dates',
regex: '(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})',
description: 'Matches ISO date format YYYY-MM-DD',
flags: 'g'
},
{
name: 'Variable Declarations',
regex: '(?<keyword>const|let|var)\\s+(?<name>\\w+)',
description: 'Matches JavaScript variable declarations',
flags: 'g'
},
{
name: 'Import Statements',
regex: 'import\\s+(?<imports>.*?)\\s+from\\s+[\'"](?<module>.*?)[\'"]',
description: 'Matches ES6 import statements',
flags: 'g'
},
{
name: 'Tool Names',
regex: '(?<=tool:\\s)\\w+',
description: 'Matches tool names after "tool: " prefix using lookbehind',
flags: 'g'
},
{
name: 'Session IDs',
regex: '(?<="sessionId":\\s*")(?<id>[a-f0-9-]+)',
description: 'Matches session IDs in JSON',
flags: 'g'
},
{
name: 'UUIDs',
regex: '(?<uuid>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})',
description: 'Matches UUID format',
flags: 'gi'
},
{
name: 'Quoted Strings',
regex: '(?<quote>[\'"])(?<content>(?:\\\\.|(?!\\k<quote>).)*?)\\k<quote>',
description: 'Matches quoted strings with backreference',
flags: 'g'
}
];
// Sample data for testing
const sampleData = `
[2025-10-09 14:23:45] Error: File not found at /home/user/project/config.json
[2025-10-09 14:23:46] Warning: Deprecated API usage detected in module.js
[2025-10-09 14:23:47] Info: Processing https://api.example.com/data/users
[2025-10-09 14:23:48] Debug: Session ID: 3e4a5b6c-7d8e-9f0a-1b2c-3d4e5f6a7b8c
const userName = 'alice@example.com';
let userCount = 42;
var sessionActive = true;
\`\`\`javascript
function processData(input) {
return input.map(item => item.value);
}
\`\`\`
\`\`\`python
def calculate_total(items):
return sum(item.price for item in items)
\`\`\`
Color scheme: #FF6B6B, #4ECDC4, #45B7D1
IP Address: 192.168.1.100
Timestamp: 14:23:45
Date: 2025-10-09
import React from 'react';
import { useState, useEffect } from 'react';
tool: Read
tool: Write
tool: Bash
`;
// Render pattern library
function renderPatternLibrary() {
const grid = document.getElementById('pattern-grid');
grid.innerHTML = patternLibrary.map((pattern, idx) => `
<div class="pattern-item" onclick="usePattern(${idx})">
<div class="pattern-name">${pattern.name}</div>
<div class="pattern-regex">/${pattern.regex}/${pattern.flags}</div>
<div class="pattern-desc">${pattern.description}</div>
</div>
`).join('');
}
// Use a pattern from the library
function usePattern(index) {
const pattern = patternLibrary[index];
document.getElementById('regex-pattern').value = pattern.regex;
// Set flags
const flagChars = pattern.flags.split('');
['g', 'i', 'm', 's', 'u'].forEach(flag => {
document.getElementById(`flag-${flag}`).checked = flagChars.includes(flag);
});
validateRegex();
}
// Validate regex pattern
function validateRegex() {
const patternInput = document.getElementById('regex-pattern');
const validation = document.getElementById('regex-validation');
const pattern = patternInput.value;
if (!pattern) {
validation.textContent = '';
return null;
}
try {
const flags = getFlags();
const regex = new RegExp(pattern, flags);
validation.textContent = '✓ Valid';
validation.className = 'regex-validation valid';
return regex;
} catch (e) {
validation.textContent = '✗ Invalid';
validation.className = 'regex-validation invalid';
return null;
}
}
// Get selected flags
function getFlags() {
const flags = [];
['g', 'i', 'm', 's', 'u'].forEach(flag => {
if (document.getElementById(`flag-${flag}`).checked) {
flags.push(flag);
}
});
return flags.join('');
}
// Search in sample data
function searchInSampleData() {
const regex = validateRegex();
if (!regex) {
alert('Please enter a valid regex pattern');
return;
}
const matches = [];
let match;
// Reset lastIndex for global searches
regex.lastIndex = 0;
while ((match = regex.exec(sampleData)) !== null) {
const matchObj = {
index: match.index,
fullMatch: match[0],
groups: match.groups || {},
captures: match.slice(1).filter(c => c !== undefined),
input: sampleData
};
matches.push(matchObj);
// Prevent infinite loop on zero-width matches
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
}
displayResults(matches);
}
// Display search results
function displayResults(matches) {
const container = document.getElementById('results-container');
const countElem = document.getElementById('match-count');
countElem.textContent = matches.length;
if (matches.length === 0) {
container.innerHTML = '<div class="no-results">No matches found</div>';
return;
}
container.innerHTML = matches.map((match, idx) => {
const contextStart = Math.max(0, match.index - 50);
const contextEnd = Math.min(match.input.length, match.index + match.fullMatch.length + 50);
const beforeContext = match.input.slice(contextStart, match.index);
const afterContext = match.input.slice(match.index + match.fullMatch.length, contextEnd);
const captureGroupsHtml = Object.keys(match.groups).length > 0 ? `
<div class="capture-groups">
${Object.entries(match.groups).map(([name, value]) => `
<div class="capture-group">
<span class="capture-label">${name}:</span>
<span class="capture-value">${escapeHtml(value)}</span>
</div>
`).join('')}
</div>
` : '';
return `
<div class="match-item">
<div class="match-header">
<div class="match-index">Match #${idx + 1}</div>
<div class="match-position">Position: ${match.index}</div>
</div>
<div class="match-content">
${escapeHtml(beforeContext)}<span class="match-highlight">${escapeHtml(match.fullMatch)}</span>${escapeHtml(afterContext)}
</div>
${captureGroupsHtml}
</div>
`;
}).join('');
}
// Test regex in live tester
function testRegex() {
const regex = validateRegex();
const testText = document.getElementById('test-text').value;
const resultsDiv = document.getElementById('test-results');
if (!regex) {
resultsDiv.innerHTML = '<div class="no-results">Please enter a valid regex pattern</div>';
return;
}
if (!testText) {
resultsDiv.innerHTML = '<div class="no-results">Please enter test text</div>';
return;
}
const matches = [];
let match;
regex.lastIndex = 0;
while ((match = regex.exec(testText)) !== null) {
matches.push({
fullMatch: match[0],
groups: match.groups || {},
index: match.index
});
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
}
if (matches.length === 0) {
resultsDiv.innerHTML = '<div class="no-results">No matches found in test text</div>';
return;
}
resultsDiv.innerHTML = `
<div style="color: var(--accent-success); margin-bottom: 1rem;">
Found ${matches.length} match${matches.length !== 1 ? 'es' : ''}
</div>
${matches.map((match, idx) => `
<div class="match-item" style="margin-bottom: 0.75rem;">
<div style="color: var(--accent-warning); font-weight: 600; margin-bottom: 0.5rem;">
Match #${idx + 1} at position ${match.index}
</div>
<div style="background: var(--bg-primary); padding: 0.5rem; border-radius: 4px; margin-bottom: 0.5rem;">
<span class="match-highlight">${escapeHtml(match.fullMatch)}</span>
</div>
${Object.keys(match.groups).length > 0 ? `
<div style="font-size: 0.85rem;">
${Object.entries(match.groups).map(([name, value]) => `
<div>
<span style="color: var(--accent-secondary);">${name}:</span>
<span style="color: var(--accent-success); margin-left: 0.5rem;">${escapeHtml(value)}</span>
</div>
`).join('')}
</div>
` : ''}
</div>
`).join('')}
`;
}
// Clear results
function clearResults() {
document.getElementById('results-container').innerHTML = '<div class="no-results">Enter a pattern and search to see results</div>';
document.getElementById('match-count').textContent = '0';
}
// Export matches
function exportMatches() {
const regex = validateRegex();
if (!regex) {
alert('Please enter a valid regex pattern and perform a search first');
return;
}
const matches = [];
let match;
regex.lastIndex = 0;
while ((match = regex.exec(sampleData)) !== null) {
matches.push({
index: match.index,
fullMatch: match[0],
groups: match.groups || {},
captures: match.slice(1).filter(c => c !== undefined)
});
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
}
if (matches.length === 0) {
alert('No matches to export. Perform a search first.');
return;
}
const exportData = {
pattern: document.getElementById('regex-pattern').value,
flags: getFlags(),
matchCount: matches.length,
timestamp: new Date().toISOString(),
matches: matches
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `regex-search-results-${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
}
// Escape HTML for safe display
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderPatternLibrary();
// Add input listener for live validation
document.getElementById('regex-pattern').addEventListener('input', validateRegex);
// Add enter key support for search
document.getElementById('regex-pattern').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
searchInSampleData();
}
});
// Add enter key support for tester
document.getElementById('test-text').addEventListener('keypress', (e) => {
if (e.key === 'Enter' && e.ctrlKey) {
testRegex();
}
});
});
</script>
</body>
</html>