Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ Use them in the browser, clone them locally, or extend them as building blocks i
| 🔍 Pretty JSON | Prettify and explore JSON in a tree view. | JSON | [Get it here](./pretty-json.html) |
| 📝 ReadMark | Markdown editor with live preview and templates. | Markdown | [Get it here](./markdown-editor.html) |
| 📡 JWT Decoder | Decode and inspect JWT tokens with details. | Encoder/Decoder | [Get it here](./jwt-decoder.html) |
| 📝 Regex Tester | Test, validate, and explore your regular expressions.| Regex | [Get it here](./Regex/regex-tester.html) |


## 🛣️ Planned Tools

We're just getting started! Some ideas in the pipeline:

- 📝 Regex Tester (pattern + test string)
- 🔄 Text Case Converter (camelCase, snake_case, Title Case)
- 🧮 UUID / Hash Generator
- ⚖️ Text Diff Viewer
Expand Down
363 changes: 363 additions & 0 deletions Regex/regex-tester.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Regex Tester | Tiny Dev Tools</title>
<style>
:root {
--primary-color: #3498db;
--secondary-color: #2c3e50;
--accent-color: #e74c3c;
--light-color: #ecf0f1;
--dark-color: #34495e;
--success-color: #2ecc71;
--warning-color: #f39c12;
}

* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

body {
background-color: #f5f5f5;
color: var(--dark-color);
line-height: 1.6;
padding: 20px;
}

.container {
max-width: 1000px;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
}

header {
margin-bottom: 20px;
text-align: center;
}

h1 {
color: var(--secondary-color);
margin-bottom: 10px;
}

p {
color: var(--dark-color);
}

.input-section {
display: flex;
flex-direction: column;
gap: 15px;
background-color: var(--light-color);
padding: 20px;
border-radius: 6px;
margin-bottom: 20px;
}

.input-group {
display: flex;
flex-direction: column;
gap: 5px;
}

label {
font-weight: bold;
font-size: 14px;
color: var(--secondary-color);
}

.regex-wrapper {
display: flex;
align-items: stretch;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}

.regex-prefix, .regex-suffix {
background-color: #f8f9fa;
color: var(--dark-color);
padding: 10px 15px;
font-family: monospace;
border-right: 1px solid #ddd;
display: flex;
align-items: center;
font-size: 16px;
}

.regex-suffix {
border-right: none;
border-left: 1px solid #ddd;
}

input[type="text"] {
flex: 1;
padding: 10px;
border: none;
font-family: monospace;
font-size: 14px;
outline: none;
}

.flags-input {
width: 60px !important;
text-align: center;
background-color: #f8f9fa;
border-left: 1px solid #ddd;
}

textarea {
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
resize: vertical;
min-height: 120px;
outline: none;
}

textarea:focus, .regex-wrapper:focus-within {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}

.error {
background-color: #ffecec;
color: var(--accent-color);
border: 1px solid #f5aca6;
padding: 10px 15px;
border-radius: 4px;
font-size: 14px;
display: none;
margin-top: 5px;
}

.error.visible {
display: block;
}

.results-container {
margin-top: 20px;
}

.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}

h3 {
color: var(--secondary-color);
}

.match-count {
background-color: var(--light-color);
color: var(--secondary-color);
padding: 5px 12px;
border-radius: 12px;
font-size: 14px;
font-weight: bold;
}

.match-count.has-matches {
background-color: #e9ffd9;
color: var(--success-color);
border: 1px solid #a6ca8a;
}

.results-content {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
border: 1px solid #ddd;
white-space: pre-wrap;
font-family: monospace;
min-height: 150px;
font-size: 14px;
color: var(--dark-color);
}

.match {
background-color: #d4edda;
color: #155724;
border-radius: 3px;
padding: 0 2px;
border-bottom: 2px solid var(--success-color);
}

.empty-state {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #7f8c8d;
font-style: italic;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

</style>
</head>
<body>

<div class="container">
<header>
<h1>Regex Tester</h1>
<p>Test, validate, and explore your regular expressions instantly</p>
</header>

<div class="input-section">
<div class="input-group">
<label for="regex">Regular Expression</label>
<div class="regex-wrapper">
<div class="regex-prefix">/</div>
<input type="text" id="regex" placeholder="[a-zA-Z0-9]+" value="[a-zA-Z0-9]+">
<div class="regex-suffix">/</div>
<input type="text" id="flags" class="flags-input" placeholder="g" value="g" title="Regex Flags (e.g., g, i, m)">
</div>
<div id="regex-error" class="error">Invalid regular expression</div>
</div>

<div class="input-group">
<label for="test-string">Test String</label>
<textarea id="test-string" placeholder="Enter text to test your regex against...">Welcome to Tiny Dev Tools Regex Tester!
This tool helps you extract 123 numbers, emails like user@example.com, and more.
It's built in 2026 and aims to be 100% awesome.</textarea>
</div>
</div>

<div class="results-container">
<div class="results-header">
<h3>Match Results</h3>
<span class="match-count" id="match-count">0 Matches</span>
</div>
<div class="results-content" id="results">
<!-- Matches will be highlighted here -->
</div>
</div>
</div>

<script>
const regexInput = document.getElementById('regex');
const flagsInput = document.getElementById('flags');
const testStringInput = document.getElementById('test-string');
const resultsDiv = document.getElementById('results');
const matchCountSpan = document.getElementById('match-count');
const errorMsg = document.getElementById('regex-error');

function updateResults() {
const regexStr = regexInput.value;
let flagsStr = flagsInput.value;
const testStr = testStringInput.value;

errorMsg.classList.remove('visible');

if (!regexStr) {
resultsDiv.innerHTML = '<div class="empty-state">Enter a regular expression to see matches.</div>';
matchCountSpan.textContent = '0 Matches';
matchCountSpan.classList.remove('has-matches');
return;
}

if (!testStr) {
resultsDiv.innerHTML = '<div class="empty-state">Enter a test string to test against.</div>';
matchCountSpan.textContent = '0 Matches';
matchCountSpan.classList.remove('has-matches');
return;
}

try {
// Remove invalid flags
const validFlags = ['g', 'i', 'm', 's', 'u', 'y'];
let cleanFlags = '';
for (let char of flagsStr) {
if (validFlags.includes(char) && !cleanFlags.includes(char)) {
cleanFlags += char;
}
}

const regex = new RegExp(regexStr, cleanFlags);

let highlightedHtml = '';
let matchCount = 0;

if (regex.global) {
let lastIndex = 0;
let match;

regex.lastIndex = 0;

const matches = [];
while ((match = regex.exec(testStr)) !== null) {
matches.push(match);
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
}

matchCount = matches.length;

for (const m of matches) {
highlightedHtml += escapeHtml(testStr.slice(lastIndex, m.index));
highlightedHtml += `<span class="match">${escapeHtml(m[0])}</span>`;
lastIndex = m.index + m[0].length;
}
highlightedHtml += escapeHtml(testStr.slice(lastIndex));

} else {
const match = regex.exec(testStr);
if (match) {
matchCount = 1;
highlightedHtml += escapeHtml(testStr.slice(0, match.index));
highlightedHtml += `<span class="match">${escapeHtml(match[0])}</span>`;
highlightedHtml += escapeHtml(testStr.slice(match.index + match[0].length));
} else {
highlightedHtml = escapeHtml(testStr);
}
}

resultsDiv.innerHTML = highlightedHtml || escapeHtml(testStr);
matchCountSpan.textContent = `${matchCount} Match${matchCount !== 1 ? 'es' : ''}`;

if (matchCount > 0) {
matchCountSpan.classList.add('has-matches');
} else {
matchCountSpan.classList.remove('has-matches');
}

} catch (e) {
errorMsg.textContent = e.message;
errorMsg.classList.add('visible');
resultsDiv.innerHTML = escapeHtml(testStr);
matchCountSpan.textContent = 'Error';
matchCountSpan.classList.remove('has-matches');
}
}

function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

regexInput.addEventListener('input', updateResults);
flagsInput.addEventListener('input', updateResults);
testStringInput.addEventListener('input', updateResults);

updateResults();
</script>
</body>
</html>