1080 lines
36 KiB
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>(?<=...)</code>, <code>(?<!...)</code>) for context-aware matching without consuming characters</li>
|
|
<li><strong>Named Capturing Groups</strong> - Support for <code>(?<name>...)</code> syntax with visual display of named captures and backreferences <code>\k<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>(?<=Error:\s).*?(?=\n|$)</code> - Uses lookbehind to match text after "Error: " and lookahead to stop at newline</li>
|
|
<li><strong>File Paths</strong> - <code>(?<path>[\w/.-]+\.(?<ext>\w+))</code> - Named groups for full path and extension</li>
|
|
<li><strong>Code Blocks</strong> - <code>```(?<lang>\w+)\n(?<code>[\s\S]*?)```</code> - Named captures for language and code content</li>
|
|
<li><strong>URLs</strong> - <code>https?://(?<domain>[\w.-]+)(?<path>/[\w/.-]*)?</code> - Separate domain and path captures</li>
|
|
<li><strong>Email Addresses</strong> - <code>(?<user>[\w.+-]+)@(?<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>(?<=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>(?<year>\d{4})-(?<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>
|