From 791ddac42155b580fadec5e1fcd043b3c1d1d25a Mon Sep 17 00:00:00 2001 From: jalel Date: Tue, 2 Dec 2025 21:19:08 +0000 Subject: [PATCH 01/23] Add interactive tree view to JSON parser - Add Tree View button to visualize JSON structure - Collapsible/expandable nodes with arrow controls - Color-coded data types (strings, numbers, booleans, null, objects, arrays) - Copy path (JSONPath) and value functionality - Expand all / Collapse all buttons - Toast notifications for copy actions - Text/Tree view toggle in output panel - Updated SEO meta tags for tree view feature --- resources/views/tools/json.blade.php | 343 +++++++++++++++++++++++++-- 1 file changed, 327 insertions(+), 16 deletions(-) diff --git a/resources/views/tools/json.blade.php b/resources/views/tools/json.blade.php index 383a322..99f05fa 100644 --- a/resources/views/tools/json.blade.php +++ b/resources/views/tools/json.blade.php @@ -1,16 +1,16 @@ @extends('layouts.app') -@section('title', 'JSON Formatter & Validator - Free Online Tool | Dev Tools') -@section('meta_description', 'Free online JSON formatter, validator, and minifier. Format, beautify, minify, validate, and repair JSON data instantly. See detailed stats and error messages.') -@section('meta_keywords', 'json formatter, json validator, json beautifier, json minifier, format json online, validate json, json parser, free json tool') +@section('title', 'JSON Formatter, Validator & Tree Viewer - Free Online Tool | Dev Tools') +@section('meta_description', 'Free online JSON formatter, validator, and tree viewer. Format, beautify, minify, validate, repair JSON data and visualize structure with interactive tree view. Copy paths and values easily.') +@section('meta_keywords', 'json formatter, json validator, json beautifier, json minifier, json tree view, json visualizer, format json online, validate json, json parser, json path, free json tool') @push('schema') +@endpush + +@push('styles') + +@endpush + +@section('content') +
+ +
+
+

Code Editor

+

Write HTML, CSS, JS with live preview

+
+ ← Back +
+ + +
+
+ +
+ + +
+ + + + +
+ + + +
+ +
+ + + +
+
+ + +
+ +
+ +
+ +
+ + +
+
+ + +
+
+ Preview + +
+
+ +
+
+
+ + +
+
+
+ Ln 1, Col 1 + +
+ UTF-8 +
+
+ + +
+
+ +
+
+
+@endsection + +@push('scripts') + + +@endpush diff --git a/routes/web.php b/routes/web.php index a6e00e4..ebacbfc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -15,6 +15,7 @@ Route::get('/uuid', [ToolController::class, 'uuid'])->name('uuid'); Route::get('/hash', [ToolController::class, 'hash'])->name('hash'); Route::get('/url', [ToolController::class, 'url'])->name('url'); + Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); }); // Static Pages @@ -34,6 +35,7 @@ ['loc' => route('tools.uuid'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.hash'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 511e16b..c51e3c1 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -27,6 +27,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('UUID Generator'); $response->assertSee('Hash Generator'); $response->assertSee('URL Encoder'); + $response->assertSee('Code Editor'); } public function test_home_page_has_tool_links(): void @@ -42,6 +43,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.uuid') . '"', false); $response->assertSee('href="' . route('tools.hash') . '"', false); $response->assertSee('href="' . route('tools.url') . '"', false); + $response->assertSee('href="' . route('tools.code-editor') . '"', false); } public function test_csv_tool_page_loads(): void @@ -207,9 +209,30 @@ public function test_url_tool_has_required_elements(): void $response->assertSee('Parse URL'); } + public function test_code_editor_page_loads(): void + { + $response = $this->get('/tools/code-editor'); + + $response->assertStatus(200); + $response->assertSee('Code Editor'); + $response->assertSee('Write HTML, CSS, JS with live preview'); + } + + public function test_code_editor_has_required_elements(): void + { + $response = $this->get('/tools/code-editor'); + + $response->assertStatus(200); + $response->assertSee('index.html'); + $response->assertSee('style.css'); + $response->assertSee('script.js'); + $response->assertSee('Preview'); + $response->assertSee('monaco-editor'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; foreach ($pages as $page) { $response = $this->get($page); @@ -221,7 +244,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; foreach ($pages as $page) { $response = $this->get($page); @@ -233,7 +256,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; foreach ($pages as $page) { $response = $this->get($page); @@ -245,7 +268,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -298,7 +321,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; foreach ($pages as $page) { $response = $this->get($page); From dc6d07ffe7fb16f736a0f1e60ded11a9fa58df8b Mon Sep 17 00:00:00 2001 From: jalel Date: Wed, 3 Dec 2025 00:59:24 +0000 Subject: [PATCH 03/23] Refactor code editor with standalone Monaco integration - Create standalone layout template without Alpine.js dependency - Use pure vanilla JavaScript for Monaco Editor integration - Fix Alpine.js conflict that caused page freeze in Chrome - Update tests for new standalone structure --- resources/views/layouts/code-editor.blade.php | 365 +++++++++++ resources/views/tools/code-editor.blade.php | 593 ++++++++++-------- tests/Feature/WebRoutesTest.php | 16 +- 3 files changed, 714 insertions(+), 260 deletions(-) create mode 100644 resources/views/layouts/code-editor.blade.php diff --git a/resources/views/layouts/code-editor.blade.php b/resources/views/layouts/code-editor.blade.php new file mode 100644 index 0000000..f62cc15 --- /dev/null +++ b/resources/views/layouts/code-editor.blade.php @@ -0,0 +1,365 @@ + + + + + + + @yield('title', 'Code Editor | Dev Tools') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @stack('schema') + + + + + + + + @yield('content') + + + + + @stack('scripts') + + diff --git a/resources/views/tools/code-editor.blade.php b/resources/views/tools/code-editor.blade.php index 102eea3..64654b8 100644 --- a/resources/views/tools/code-editor.blade.php +++ b/resources/views/tools/code-editor.blade.php @@ -1,4 +1,4 @@ -@extends('layouts.app') +@extends('layouts.code-editor') @section('title', 'Online Code Editor - Live HTML/CSS/JS Editor | Dev Tools') @section('meta_description', 'Free online code editor with live preview. Write HTML, CSS, JavaScript with real-time preview in your browser.') @@ -27,282 +27,315 @@ @endpush -@push('styles') - -@endpush - @section('content') -
- -
-
-

Code Editor

-

Write HTML, CSS, JS with live preview

-
- ← Back + + + +
-
-
- -
- - -
- - +
+
+ + +
- -
- - - -
+
+ +
+ + + +
-
+
- -
- -
+ +
-
+
-
- +
+ + +
- -
+ +
-
-
- Preview -
-
- -
+
-
-
-
- Ln 1, Col 1 - -
- UTF-8 -
-
- - -
-
- +
+
+ Ln 1, Col 1 + index.html
+ UTF-8
+ + +
@endsection @push('scripts') - @endpush diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index c51e3c1..5acbe3a 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -214,8 +214,8 @@ public function test_code_editor_page_loads(): void $response = $this->get('/tools/code-editor'); $response->assertStatus(200); - $response->assertSee('Code Editor'); - $response->assertSee('Write HTML, CSS, JS with live preview'); + $response->assertSee('Dev Tools'); + $response->assertSee('Online Code Editor'); } public function test_code_editor_has_required_elements(): void @@ -227,7 +227,7 @@ public function test_code_editor_has_required_elements(): void $response->assertSee('style.css'); $response->assertSee('script.js'); $response->assertSee('Preview'); - $response->assertSee('monaco-editor'); + $response->assertSee('monaco-container'); } public function test_all_pages_have_navigation(): void @@ -256,19 +256,21 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; + // Code editor uses standalone template without Vite + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; foreach ($pages as $page) { $response = $this->get($page); $response->assertStatus(200); - // Vite assets are loaded via @vite directive - $response->assertSee('build/assets/app', false); + // Vite assets are loaded via @vite directive (with hash in filename) + $response->assertSee('assets/app-', false); } } public function test_all_tool_pages_have_back_link(): void { - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; + // Code editor uses standalone template with home link instead of back + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; foreach ($toolPages as $page) { $response = $this->get($page); From d5b5dd2a14d65ea0cd7a62f6564805964ed5e89b Mon Sep 17 00:00:00 2001 From: jalel Date: Wed, 3 Dec 2025 01:23:20 +0000 Subject: [PATCH 04/23] Add code editor enhancements: dynamic tabs, console, shortcuts modal - Add keyboard shortcuts help modal with common Monaco shortcuts - Add word wrap and minimap toggle controls in toolbar - Add console output panel that captures log/info/warn/error from preview - Add dynamic tabs: create new files (+) and close tabs (x) - Add Download All as ZIP feature using JSZip - Add Ctrl+S keybinding to download current file - Support additional file types: .html, .css, .js, .json, .php, .sql --- resources/views/layouts/code-editor.blade.php | 305 +++++++++++ resources/views/tools/code-editor.blade.php | 489 +++++++++++++++++- 2 files changed, 774 insertions(+), 20 deletions(-) diff --git a/resources/views/layouts/code-editor.blade.php b/resources/views/layouts/code-editor.blade.php index f62cc15..7b505af 100644 --- a/resources/views/layouts/code-editor.blade.php +++ b/resources/views/layouts/code-editor.blade.php @@ -221,6 +221,7 @@ function gtag(){dataLayer.push(arguments);} display: flex; background: var(--bg-tertiary); border-bottom: 1px solid var(--border-color); + overflow-x: auto; } .tab { @@ -232,6 +233,10 @@ function gtag(){dataLayer.push(arguments);} border-bottom: 2px solid transparent; cursor: pointer; transition: all 0.15s; + display: flex; + align-items: center; + gap: 0.5rem; + white-space: nowrap; } .tab:hover { @@ -245,6 +250,40 @@ function gtag(){dataLayer.push(arguments);} border-bottom-color: var(--accent); } + .tab-close { + display: none; + width: 16px; + height: 16px; + line-height: 14px; + text-align: center; + border-radius: 3px; + font-size: 14px; + } + + .tab:hover .tab-close { + display: inline-block; + } + + .tab-close:hover { + background: rgba(239, 68, 68, 0.2); + color: #ef4444; + } + + .tab-add { + padding: 0.75rem 1rem; + font-size: 1.25rem; + color: var(--text-secondary); + background: transparent; + border: none; + cursor: pointer; + transition: all 0.15s; + } + + .tab-add:hover { + color: var(--accent); + background: var(--bg-secondary); + } + /* Monaco Container */ #monaco-container { flex: 1; @@ -283,12 +322,120 @@ function gtag(){dataLayer.push(arguments);} background: #15803d; } + .preview-container { + flex: 1; + display: flex; + overflow: hidden; + } + #preview-frame { flex: 1; border: none; background: white; } + /* Console Panel */ + .console-panel { + border-top: 1px solid var(--border-color); + display: flex; + flex-direction: column; + max-height: 200px; + transition: max-height 0.2s; + } + + .console-panel.collapsed { + max-height: 32px; + } + + .console-panel.collapsed .console-output { + display: none; + } + + .console-panel.collapsed #btn-toggle-console svg { + transform: rotate(180deg); + } + + .console-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 0.75rem; + background: var(--bg-tertiary); + border-bottom: 1px solid var(--border-color); + font-size: 0.75rem; + font-weight: 500; + color: var(--text-secondary); + } + + .console-actions { + display: flex; + gap: 0.5rem; + } + + .btn-console { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + background: transparent; + border: none; + color: var(--text-secondary); + cursor: pointer; + border-radius: 3px; + display: flex; + align-items: center; + } + + .btn-console:hover { + background: var(--bg-secondary); + color: var(--text-primary); + } + + .console-output { + flex: 1; + overflow-y: auto; + padding: 0.5rem; + font-family: 'Monaco', 'Menlo', monospace; + font-size: 0.75rem; + background: var(--bg-secondary); + } + + .console-line { + padding: 0.25rem 0.5rem; + border-radius: 3px; + margin-bottom: 2px; + display: flex; + align-items: flex-start; + gap: 0.5rem; + } + + .console-line.log { + color: var(--text-primary); + } + + .console-line.info { + color: #3b82f6; + background: rgba(59, 130, 246, 0.1); + } + + .console-line.warn { + color: #f59e0b; + background: rgba(245, 158, 11, 0.1); + } + + .console-line.error { + color: #ef4444; + background: rgba(239, 68, 68, 0.1); + } + + .console-line-prefix { + opacity: 0.6; + min-width: 40px; + } + + .console-line-content { + flex: 1; + word-break: break-all; + } + /* Status Bar */ .status-bar { background: var(--bg-secondary); @@ -336,6 +483,164 @@ function gtag(){dataLayer.push(arguments);} to { transform: translateY(0); opacity: 1; } } + /* Modal */ + .modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 1000; + align-items: center; + justify-content: center; + padding: 1rem; + } + + .modal.show { + display: flex; + } + + .modal-content { + background: var(--bg-secondary); + border-radius: 0.75rem; + max-width: 600px; + width: 100%; + max-height: 80vh; + overflow: hidden; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + } + + .modal-sm { + max-width: 400px; + } + + .modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--border-color); + } + + .modal-header h3 { + font-size: 1.125rem; + font-weight: 600; + } + + .modal-close { + background: transparent; + border: none; + font-size: 1.5rem; + color: var(--text-secondary); + cursor: pointer; + line-height: 1; + padding: 0; + } + + .modal-close:hover { + color: var(--text-primary); + } + + .modal-body { + padding: 1.25rem; + overflow-y: auto; + max-height: 60vh; + } + + .modal-footer { + display: flex; + justify-content: flex-end; + gap: 0.75rem; + padding: 1rem 1.25rem; + border-top: 1px solid var(--border-color); + } + + /* Shortcuts Modal */ + .shortcut-section { + margin-bottom: 1.5rem; + } + + .shortcut-section:last-child { + margin-bottom: 0; + } + + .shortcut-section h4 { + font-size: 0.875rem; + font-weight: 600; + color: var(--accent); + margin-bottom: 0.75rem; + } + + .shortcut-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; + font-size: 0.875rem; + border-bottom: 1px solid var(--border-color); + } + + .shortcut-row:last-child { + border-bottom: none; + } + + .keys { + display: flex; + gap: 0.25rem; + } + + kbd { + display: inline-block; + padding: 0.25rem 0.5rem; + background: var(--bg-tertiary); + border: 1px solid var(--border-color); + border-radius: 0.25rem; + font-family: inherit; + font-size: 0.75rem; + } + + /* New File Modal */ + .modal-body label { + display: block; + font-size: 0.875rem; + font-weight: 500; + margin-bottom: 0.5rem; + } + + .modal-body input[type="text"] { + width: 100%; + padding: 0.625rem 0.75rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + background: var(--bg-primary); + color: var(--text-primary); + font-size: 0.875rem; + } + + .modal-body input[type="text"]:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); + } + + .modal-body .hint { + font-size: 0.75rem; + color: var(--text-secondary); + margin-top: 0.5rem; + } + + .btn-primary { + background: var(--accent); + color: white; + border-color: var(--accent); + } + + .btn-primary:hover { + background: var(--accent-hover); + } + /* Responsive */ @media (max-width: 768px) { .editor-layout { diff --git a/resources/views/tools/code-editor.blade.php b/resources/views/tools/code-editor.blade.php index 64654b8..731ebfa 100644 --- a/resources/views/tools/code-editor.blade.php +++ b/resources/views/tools/code-editor.blade.php @@ -72,12 +72,18 @@ Copy - +
+
+ + +
+
+
@@ -138,18 +187,90 @@
+ + + + + + @endsection @push('scripts') + + +@endpush + +@section('content') +
+
+
+

Regex Tester

+

Test and debug regular expressions with live matching

+
+ ← Back +
+ +
+ +
+ +
+ +
+ / + + / + +
+
+ +
+
+
+ + +
+ + +
+ + +
+ +
+ +
+
+
+ + +
+ +
+
+ + + matches + +
+
+
+ + +
+ +
+
+ No matches found +
+
+ +
+
+
+ + +
+ +
+ +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index ebacbfc..7c8961e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,7 @@ Route::get('/hash', [ToolController::class, 'hash'])->name('hash'); Route::get('/url', [ToolController::class, 'url'])->name('url'); Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); + Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); }); // Static Pages @@ -36,6 +37,7 @@ ['loc' => route('tools.hash'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], + ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 5acbe3a..71c4bf8 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -28,6 +28,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Hash Generator'); $response->assertSee('URL Encoder'); $response->assertSee('Code Editor'); + $response->assertSee('Regex Tester'); } public function test_home_page_has_tool_links(): void @@ -44,6 +45,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.hash') . '"', false); $response->assertSee('href="' . route('tools.url') . '"', false); $response->assertSee('href="' . route('tools.code-editor') . '"', false); + $response->assertSee('href="' . route('tools.regex') . '"', false); } public function test_csv_tool_page_loads(): void @@ -230,9 +232,29 @@ public function test_code_editor_has_required_elements(): void $response->assertSee('monaco-container'); } + public function test_regex_tool_page_loads(): void + { + $response = $this->get('/tools/regex'); + + $response->assertStatus(200); + $response->assertSee('Regex Tester'); + $response->assertSee('Test and debug regular expressions'); + } + + public function test_regex_tool_has_required_elements(): void + { + $response = $this->get('/tools/regex'); + + $response->assertStatus(200); + $response->assertSee('Regular Expression'); + $response->assertSee('Test String'); + $response->assertSee('Common Patterns'); + $response->assertSee('Match Details'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex']; foreach ($pages as $page) { $response = $this->get($page); @@ -244,7 +266,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex']; foreach ($pages as $page) { $response = $this->get($page); @@ -257,7 +279,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex']; foreach ($pages as $page) { $response = $this->get($page); @@ -270,7 +292,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex']; foreach ($toolPages as $page) { $response = $this->get($page); From 1b564e7591a23592f6bb641ea83f1cf36eb4c872 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 21:48:40 +0000 Subject: [PATCH 07/23] Add Base Converter tool --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + .../views/tools/base-converter.blade.php | 335 ++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 32 +- 5 files changed, 380 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/base-converter.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 13d83e9..686e23d 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -75,6 +75,12 @@ public function index(): View 'route' => 'tools.regex', 'icon' => 'regex', ], + [ + 'name' => 'Base Converter', + 'description' => 'Convert between binary, octal, decimal, and hex', + 'route' => 'tools.base-converter', + 'icon' => 'calculator', + ], ]; return view('home', compact('tools')); @@ -134,4 +140,9 @@ public function regex(): View { return view('tools.regex'); } + + public function baseConverter(): View + { + return view('tools.base-converter'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index c2600f0..2ac5d9b 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -90,6 +90,11 @@ @break + @case('calculator') + + + + @break @endswitch
diff --git a/resources/views/tools/base-converter.blade.php b/resources/views/tools/base-converter.blade.php new file mode 100644 index 0000000..d871911 --- /dev/null +++ b/resources/views/tools/base-converter.blade.php @@ -0,0 +1,335 @@ +@extends('layouts.app') + +@section('title', 'Base Converter - Binary, Octal, Decimal, Hex | Dev Tools') +@section('meta_description', 'Free online number base converter. Convert between binary, octal, decimal, and hexadecimal. Instant conversion with bit visualization.') +@section('meta_keywords', 'base converter, binary converter, hex converter, octal converter, decimal to binary, binary to hex, number base, radix converter') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Base Converter

+

Convert between binary, octal, decimal, and hexadecimal

+
+ ← Back +
+ +
+ +
+ +
+
+ + 0-1 +
+
+ + +
+

+
+ + +
+
+ + 0-7 +
+
+ + +
+

+
+ + +
+
+ + 0-9 +
+
+ + +
+

+
+ + +
+
+ + 0-9, A-F +
+
+ + +
+

+
+ + + +
+ + +
+ +
+ +
+ +
+

Enter a number to see bit visualization

+

+ bits +

+
+ + +
+ +
+ + + + +
+
+ + +
+ +
+
+ Binary: + 0b +
+
+ Octal: + 0o +
+
+ Decimal: + +
+
+ Hex: + 0x +
+
+

Enter a number to see formatted output

+
+ + +
+

Base Reference

+
+

Binary (2): Uses 0 and 1

+

Octal (8): Uses 0-7

+

Decimal (10): Uses 0-9

+

Hexadecimal (16): Uses 0-9 and A-F

+
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 7c8961e..480c649 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,6 +17,7 @@ Route::get('/url', [ToolController::class, 'url'])->name('url'); Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); + Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter'); }); // Static Pages @@ -38,6 +39,7 @@ ['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 71c4bf8..a788080 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -29,6 +29,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('URL Encoder'); $response->assertSee('Code Editor'); $response->assertSee('Regex Tester'); + $response->assertSee('Base Converter'); } public function test_home_page_has_tool_links(): void @@ -46,6 +47,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.url') . '"', false); $response->assertSee('href="' . route('tools.code-editor') . '"', false); $response->assertSee('href="' . route('tools.regex') . '"', false); + $response->assertSee('href="' . route('tools.base-converter') . '"', false); } public function test_csv_tool_page_loads(): void @@ -252,9 +254,29 @@ public function test_regex_tool_has_required_elements(): void $response->assertSee('Match Details'); } + public function test_base_converter_page_loads(): void + { + $response = $this->get('/tools/base-converter'); + + $response->assertStatus(200); + $response->assertSee('Base Converter'); + $response->assertSee('Convert between binary, octal, decimal, and hexadecimal'); + } + + public function test_base_converter_has_required_elements(): void + { + $response = $this->get('/tools/base-converter'); + + $response->assertStatus(200); + $response->assertSee('Binary'); + $response->assertSee('Octal'); + $response->assertSee('Decimal'); + $response->assertSee('Hexadecimal'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter']; foreach ($pages as $page) { $response = $this->get($page); @@ -266,7 +288,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter']; foreach ($pages as $page) { $response = $this->get($page); @@ -279,7 +301,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter']; foreach ($pages as $page) { $response = $this->get($page); @@ -292,7 +314,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -345,7 +367,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter']; foreach ($pages as $page) { $response = $this->get($page); From da33552a5858978ce1558df1ff8b0206eb469f41 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 21:26:25 +0000 Subject: [PATCH 08/23] Add Slug Generator tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert text to URL-friendly slugs - Separator options: hyphen, underscore, dot, none - Case options: lowercase, uppercase, preserve - Transliterate accented characters (é → e, ñ → n) - Option to remove numbers - Optional length limit - URL preview with generated slug - Example texts for quick testing - Client-side only implementation --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + .../views/tools/slug-generator.blade.php | 328 ++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 32 +- 5 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/slug-generator.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 686e23d..3ce0abe 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -81,6 +81,12 @@ public function index(): View 'route' => 'tools.base-converter', 'icon' => 'calculator', ], + [ + 'name' => 'Slug Generator', + 'description' => 'Convert text to URL-friendly slugs', + 'route' => 'tools.slug-generator', + 'icon' => 'slug', + ], ]; return view('home', compact('tools')); @@ -145,4 +151,9 @@ public function baseConverter(): View { return view('tools.base-converter'); } + + public function slugGenerator(): View + { + return view('tools.slug-generator'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 2ac5d9b..ba4c82f 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -95,6 +95,11 @@ @break + @case('slug') + + + + @break @endswitch
diff --git a/resources/views/tools/slug-generator.blade.php b/resources/views/tools/slug-generator.blade.php new file mode 100644 index 0000000..9e07b15 --- /dev/null +++ b/resources/views/tools/slug-generator.blade.php @@ -0,0 +1,328 @@ +@extends('layouts.app') + +@section('title', 'Slug Generator - Create URL-Friendly Slugs | Dev Tools') +@section('meta_description', 'Free online slug generator. Convert text to URL-friendly slugs. Customize separators, case, and transliteration options.') +@section('meta_keywords', 'slug generator, url slug, seo slug, friendly url, text to slug, slug maker, url generator, permalink generator') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Slug Generator

+

Convert text to URL-friendly slugs

+
+ ← Back +
+ +
+ +
+
+ + +

+ characters +

+
+ + +
+ + +
+ +
+ +
+ + + + +
+
+ + +
+ +
+ + + +
+
+ + +
+ + + +
+ + characters +
+
+
+
+
+ + +
+
+ +
+ + +
+

+ characters +

+
+ + +
+ +
+ https://example.com/ +
+
+ + +
+ +
+ + + +
+
+ + +
+

What is a Slug?

+

+ A slug is a URL-friendly version of a string, typically used in permalinks. It contains only lowercase letters, numbers, and hyphens, making URLs more readable and SEO-friendly. +

+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 480c649..08d35ce 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,6 +18,7 @@ Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter'); + Route::get('/slug-generator', [ToolController::class, 'slugGenerator'])->name('slug-generator'); }); // Static Pages @@ -40,6 +41,7 @@ ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.slug-generator'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index a788080..acf5c6d 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -30,6 +30,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Code Editor'); $response->assertSee('Regex Tester'); $response->assertSee('Base Converter'); + $response->assertSee('Slug Generator'); } public function test_home_page_has_tool_links(): void @@ -48,6 +49,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.code-editor') . '"', false); $response->assertSee('href="' . route('tools.regex') . '"', false); $response->assertSee('href="' . route('tools.base-converter') . '"', false); + $response->assertSee('href="' . route('tools.slug-generator') . '"', false); } public function test_csv_tool_page_loads(): void @@ -274,9 +276,29 @@ public function test_base_converter_has_required_elements(): void $response->assertSee('Hexadecimal'); } + public function test_slug_generator_page_loads(): void + { + $response = $this->get('/tools/slug-generator'); + + $response->assertStatus(200); + $response->assertSee('Slug Generator'); + $response->assertSee('Convert text to URL-friendly slugs'); + } + + public function test_slug_generator_has_required_elements(): void + { + $response = $this->get('/tools/slug-generator'); + + $response->assertStatus(200); + $response->assertSee('Input Text'); + $response->assertSee('Generated Slug'); + $response->assertSee('Separator'); + $response->assertSee('Hyphen'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; foreach ($pages as $page) { $response = $this->get($page); @@ -288,7 +310,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; foreach ($pages as $page) { $response = $this->get($page); @@ -301,7 +323,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; foreach ($pages as $page) { $response = $this->get($page); @@ -314,7 +336,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -367,7 +389,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator']; foreach ($pages as $page) { $response = $this->get($page); From f4d7776520de7e30e8715e797225003b912089b0 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 21:22:16 +0000 Subject: [PATCH 09/23] Add Color Picker tool - Visual color picker with HTML5 color input - Convert between HEX, RGB, HSL, and CMYK formats - Color harmony: complementary, triadic, analogous colors - Generate shades and tints - Display color info: brightness, luminance, suggested text color - Quick-select common colors palette - Copy color values to clipboard - Random color generator - Client-side only implementation --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + resources/views/tools/color-picker.blade.php | 490 +++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 33 +- 5 files changed, 536 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/color-picker.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 3ce0abe..525647c 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -87,6 +87,12 @@ public function index(): View 'route' => 'tools.slug-generator', 'icon' => 'slug', ], + [ + 'name' => 'Color Picker', + 'description' => 'Pick colors and convert between HEX, RGB, HSL, CMYK', + 'route' => 'tools.color-picker', + 'icon' => 'color', + ], ]; return view('home', compact('tools')); @@ -156,4 +162,9 @@ public function slugGenerator(): View { return view('tools.slug-generator'); } + + public function colorPicker(): View + { + return view('tools.color-picker'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index ba4c82f..5218b34 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -100,6 +100,11 @@ @break + @case('color') + + + + @break @endswitch
diff --git a/resources/views/tools/color-picker.blade.php b/resources/views/tools/color-picker.blade.php new file mode 100644 index 0000000..d3cde18 --- /dev/null +++ b/resources/views/tools/color-picker.blade.php @@ -0,0 +1,490 @@ +@extends('layouts.app') + +@section('title', 'Color Picker & Converter - HEX, RGB, HSL, CMYK | Dev Tools') +@section('meta_description', 'Free online color picker and converter. Convert between HEX, RGB, HSL, and CMYK color formats. Generate color palettes and complementary colors.') +@section('meta_keywords', 'color picker, color converter, hex to rgb, rgb to hex, hsl converter, cmyk converter, color palette, color tool, web colors') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Color Picker & Converter

+

Convert between HEX, RGB, HSL, and CMYK formats

+
+ ← Back +
+ +
+ +
+ +
+ + +
+ +
+
+
+
+ +
+ + +
+
+ + +
+ + +
+ +
+ +
+ + +
+
+ + +
+ +
+
+ + + +
+ +
+

+
+ + +
+ +
+
+ + + +
+ +
+

+
+ + +
+ +
+
+ + + + +
+ +
+

+
+
+
+
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ +

Click any color to copy its HEX value

+
+ + +
+ + +
+
+ Brightness: + +
+
+ Luminance: + +
+
+ Suggested Text: + +
+
+
+ + +
+ + +
+ +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 08d35ce..429b128 100644 --- a/routes/web.php +++ b/routes/web.php @@ -19,6 +19,7 @@ Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter'); Route::get('/slug-generator', [ToolController::class, 'slugGenerator'])->name('slug-generator'); + Route::get('/color-picker', [ToolController::class, 'colorPicker'])->name('color-picker'); }); // Static Pages @@ -42,6 +43,7 @@ ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.slug-generator'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.color-picker'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index acf5c6d..4787717 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -31,6 +31,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Regex Tester'); $response->assertSee('Base Converter'); $response->assertSee('Slug Generator'); + $response->assertSee('Color Picker'); } public function test_home_page_has_tool_links(): void @@ -50,6 +51,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.regex') . '"', false); $response->assertSee('href="' . route('tools.base-converter') . '"', false); $response->assertSee('href="' . route('tools.slug-generator') . '"', false); + $response->assertSee('href="' . route('tools.color-picker') . '"', false); } public function test_csv_tool_page_loads(): void @@ -296,9 +298,30 @@ public function test_slug_generator_has_required_elements(): void $response->assertSee('Hyphen'); } + public function test_color_picker_page_loads(): void + { + $response = $this->get('/tools/color-picker'); + + $response->assertStatus(200); + $response->assertSee('Color Picker'); + $response->assertSee('Convert between HEX, RGB, HSL, and CMYK'); + } + + public function test_color_picker_has_required_elements(): void + { + $response = $this->get('/tools/color-picker'); + + $response->assertStatus(200); + $response->assertSee('HEX'); + $response->assertSee('RGB'); + $response->assertSee('HSL'); + $response->assertSee('CMYK'); + $response->assertSee('Color Harmony'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; foreach ($pages as $page) { $response = $this->get($page); @@ -310,7 +333,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; foreach ($pages as $page) { $response = $this->get($page); @@ -323,7 +346,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; foreach ($pages as $page) { $response = $this->get($page); @@ -336,7 +359,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -389,7 +412,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; foreach ($pages as $page) { $response = $this->get($page); From 08d04fd1e893a26ab7edb0b1d072f2000b8fbb62 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 20:53:36 +0000 Subject: [PATCH 10/23] Add QR Code Generator tool - Generate QR codes from text, URLs, email, phone, SMS, WiFi - Customizable size (128-512px) and colors - Error correction level options (L, M, Q, H) - Download as PNG or SVG - Copy QR code image to clipboard - Quick templates for common use cases - Client-side only using qrcode.js library --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 6 + resources/views/tools/qr-code.blade.php | 416 ++++++++++++++++++++++++ routes/web.php | 8 + tests/Feature/WebRoutesTest.php | 39 ++- 5 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/qr-code.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 525647c..3e134d5 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -93,6 +93,12 @@ public function index(): View 'route' => 'tools.color-picker', 'icon' => 'color', ], + [ + 'name' => 'QR Code Generator', + 'description' => 'Generate QR codes for URLs, text, and more', + 'route' => 'tools.qr-code', + 'icon' => 'qrcode', + ], ]; return view('home', compact('tools')); @@ -167,4 +173,9 @@ public function colorPicker(): View { return view('tools.color-picker'); } + + public function qrCode(): View + { + return view('tools.qr-code'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 5218b34..c73b6d2 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -90,6 +90,7 @@ @break + @case('calculator') @@ -103,6 +104,11 @@ @case('color') + + @case('qrcode') + + + @break @endswitch diff --git a/resources/views/tools/qr-code.blade.php b/resources/views/tools/qr-code.blade.php new file mode 100644 index 0000000..f48406f --- /dev/null +++ b/resources/views/tools/qr-code.blade.php @@ -0,0 +1,416 @@ +@extends('layouts.app') + +@section('title', 'QR Code Generator - Create QR Codes Online Free | Dev Tools') +@section('meta_description', 'Free online QR code generator. Create QR codes for URLs, text, email, phone numbers. Customize colors and size. Download as PNG instantly.') +@section('meta_keywords', 'qr code generator, create qr code, qr code maker, free qr code, qr code online, generate qr code, custom qr code, qr code download') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

QR Code Generator

+

Generate QR codes for URLs, text, and more

+
+ ← Back +
+ +
+ +
+
+ + +

+ characters +

+
+ + +
+ +
+ + + + + +
+
+ + +
+ + +
+ +
+ + +
+ 128px + 512px +
+
+ + +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ + +
+ + +
+
+
+
+ + +
+
+ + +
+ +
+ + +
+ + +
+ + + +
+
+
+ + +
+

About QR Codes

+
+

URL: Link directly to websites

+

Email: Use mailto:email@example.com

+

Phone: Use tel:+1234567890

+

SMS: Use sms:+1234567890?body=message

+

WiFi: Use WIFI:T:WPA;S:NetworkName;P:Password;;

+
+
+ + +
+

Error Correction Levels

+
    +
  • L (Low): 7% - Smallest QR code
  • +
  • M (Medium): 15% - Good balance
  • +
  • Q (Quartile): 25% - Better recovery
  • +
  • H (High): 30% - Best for logos
  • +
+
+
+
+
+@endsection + +@push('scripts') + + +@endpush diff --git a/routes/web.php b/routes/web.php index 429b128..b56ba8d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,9 +17,13 @@ Route::get('/url', [ToolController::class, 'url'])->name('url'); Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); + Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter'); Route::get('/slug-generator', [ToolController::class, 'slugGenerator'])->name('slug-generator'); Route::get('/color-picker', [ToolController::class, 'colorPicker'])->name('color-picker'); + + Route::get('/qr-code', [ToolController::class, 'qrCode'])->name('qr-code'); + }); // Static Pages @@ -41,9 +45,13 @@ ['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.slug-generator'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.color-picker'), 'priority' => '0.8', 'changefreq' => 'monthly'], + + ['loc' => route('tools.qr-code'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 4787717..766edc0 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -29,9 +29,13 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('URL Encoder'); $response->assertSee('Code Editor'); $response->assertSee('Regex Tester'); + $response->assertSee('Base Converter'); $response->assertSee('Slug Generator'); $response->assertSee('Color Picker'); + + $response->assertSee('QR Code Generator'); + } public function test_home_page_has_tool_links(): void @@ -49,9 +53,13 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.url') . '"', false); $response->assertSee('href="' . route('tools.code-editor') . '"', false); $response->assertSee('href="' . route('tools.regex') . '"', false); + $response->assertSee('href="' . route('tools.base-converter') . '"', false); $response->assertSee('href="' . route('tools.slug-generator') . '"', false); $response->assertSee('href="' . route('tools.color-picker') . '"', false); + + $response->assertSee('href="' . route('tools.qr-code') . '"', false); + } public function test_csv_tool_page_loads(): void @@ -258,6 +266,7 @@ public function test_regex_tool_has_required_elements(): void $response->assertSee('Match Details'); } + public function test_base_converter_page_loads(): void { $response = $this->get('/tools/base-converter'); @@ -317,11 +326,31 @@ public function test_color_picker_has_required_elements(): void $response->assertSee('HSL'); $response->assertSee('CMYK'); $response->assertSee('Color Harmony'); + + public function test_qr_code_tool_page_loads(): void + { + $response = $this->get('/tools/qr-code'); + + $response->assertStatus(200); + $response->assertSee('QR Code Generator'); + $response->assertSee('Generate QR codes for URLs, text, and more'); + } + + public function test_qr_code_tool_has_required_elements(): void + { + $response = $this->get('/tools/qr-code'); + + $response->assertStatus(200); + $response->assertSee('Content'); + $response->assertSee('Quick Templates'); + $response->assertSee('Download PNG'); + $response->assertSee('Error Correction'); + } public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; foreach ($pages as $page) { $response = $this->get($page); @@ -333,7 +362,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; foreach ($pages as $page) { $response = $this->get($page); @@ -346,7 +375,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; foreach ($pages as $page) { $response = $this->get($page); @@ -359,7 +388,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -412,7 +441,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; foreach ($pages as $page) { $response = $this->get($page); From 22e03b9983c6fd5d592ded85e51da2cc53748242 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 20:39:53 +0000 Subject: [PATCH 11/23] Add HTML Entity Encoder tool - Encode text to HTML entities (named, numeric, hex formats) - Decode HTML entities back to text - Option to encode all characters or only special characters - Common entities reference panel with clickable items - Entity reference table organized by categories - Statistics showing input/output lengths --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 3 + resources/views/tools/html-entity.blade.php | 419 ++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 30 +- 5 files changed, 460 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/html-entity.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 3e134d5..bdbe302 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -99,6 +99,12 @@ public function index(): View 'route' => 'tools.qr-code', 'icon' => 'qrcode', ], + [ + 'name' => 'HTML Entity Encoder', + 'description' => 'Encode/decode HTML entities and special characters', + 'route' => 'tools.html-entity', + 'icon' => 'html-entity', + ], ]; return view('home', compact('tools')); @@ -178,4 +184,9 @@ public function qrCode(): View { return view('tools.qr-code'); } + + public function htmlEntity(): View + { + return view('tools.html-entity'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index c73b6d2..22b824b 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -109,6 +109,9 @@ + @case('html-entity') + + @break @endswitch diff --git a/resources/views/tools/html-entity.blade.php b/resources/views/tools/html-entity.blade.php new file mode 100644 index 0000000..b253fd2 --- /dev/null +++ b/resources/views/tools/html-entity.blade.php @@ -0,0 +1,419 @@ +@extends('layouts.app') + +@section('title', 'HTML Entity Encoder/Decoder - Free Online Tool | Dev Tools') +@section('meta_description', 'Free online HTML entity encoder and decoder. Convert special characters to HTML entities and decode them back. Supports named and numeric entities.') +@section('meta_keywords', 'html entity encoder, html entity decoder, encode html, decode html entities, html special characters, free html tool') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

HTML Entity Encoder/Decoder

+

Encode special characters to HTML entities or decode them back to text

+
+ ← Back +
+ + +
+
+
+ Encoding Mode: + +
+ +
+
+ +
+ +
+
+
+ + +
+ +
+ +
+
+ + +
+
+
+ + +
+
+ +
+ +
+
+ + + + +
+
+
+ Input length: +
+
+ Output length: +
+
+
+
+
+ + +
+

Common HTML Entities

+
+ +
+
+ + +
+

Entity Reference

+ +
+
+ + + + + +
+ +
+ + + + + + + + + + + + + +
CharacterNamedNumericHexDescription
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index b56ba8d..d9ffa41 100644 --- a/routes/web.php +++ b/routes/web.php @@ -24,6 +24,7 @@ Route::get('/qr-code', [ToolController::class, 'qrCode'])->name('qr-code'); + Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity'); }); // Static Pages @@ -52,6 +53,7 @@ ['loc' => route('tools.qr-code'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 766edc0..4cd4af2 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -36,6 +36,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('QR Code Generator'); + $response->assertSee('HTML Entity Encoder'); } public function test_home_page_has_tool_links(): void @@ -60,6 +61,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.qr-code') . '"', false); + $response->assertSee('href="' . route('tools.html-entity') . '"', false); } public function test_csv_tool_page_loads(): void @@ -346,11 +348,29 @@ public function test_qr_code_tool_has_required_elements(): void $response->assertSee('Download PNG'); $response->assertSee('Error Correction'); + public function test_html_entity_tool_page_loads(): void + { + $response = $this->get('/tools/html-entity'); + + $response->assertStatus(200); + $response->assertSee('HTML Entity Encoder/Decoder'); + $response->assertSee('Encode special characters to HTML entities or decode them back to text'); + } + + public function test_html_entity_tool_has_required_elements(): void + { + $response = $this->get('/tools/html-entity'); + + $response->assertStatus(200); + $response->assertSee('Encode'); + $response->assertSee('Decode'); + $response->assertSee('Common HTML Entities'); + $response->assertSee('Entity Reference'); } public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; foreach ($pages as $page) { $response = $this->get($page); @@ -362,7 +382,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; foreach ($pages as $page) { $response = $this->get($page); @@ -375,7 +395,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; foreach ($pages as $page) { $response = $this->get($page); @@ -388,7 +408,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -441,7 +461,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; foreach ($pages as $page) { $response = $this->get($page); From e26a01ee8a9a95f6595a1daaf5a1bd961031d6a6 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 20:33:53 +0000 Subject: [PATCH 12/23] Add Text Case Converter tool - Convert text between 13 different case formats - Supports: lowercase, UPPERCASE, Title Case, Sentence case - Programming cases: camelCase, PascalCase, snake_case, kebab-case - Additional: CONSTANT_CASE, dot.case, path/case - Fun modes: aLtErNaTiNg, Inverse Case - Real-time character/word/line statistics - Quick action buttons for common conversions - Client-side only implementation using Alpine.js --- app/Http/Controllers/ToolController.php | 7 + resources/views/home.blade.php | 4 + resources/views/tools/text-case.blade.php | 305 ++++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 25 ++ 5 files changed, 343 insertions(+) create mode 100644 resources/views/tools/text-case.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index bdbe302..8718fb8 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -104,6 +104,10 @@ public function index(): View 'description' => 'Encode/decode HTML entities and special characters', 'route' => 'tools.html-entity', 'icon' => 'html-entity', + 'name' => 'Text Case Converter', + 'description' => 'Convert text to camelCase, snake_case, and more', + 'route' => 'tools.text-case', + 'icon' => 'text-case', ], ]; @@ -188,5 +192,8 @@ public function qrCode(): View public function htmlEntity(): View { return view('tools.html-entity'); + public function textCase(): View + { + return view('tools.text-case'); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 22b824b..7257a33 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -112,6 +112,10 @@ @case('html-entity') + @case('text-case') + + + @break @endswitch diff --git a/resources/views/tools/text-case.blade.php b/resources/views/tools/text-case.blade.php new file mode 100644 index 0000000..039cdd3 --- /dev/null +++ b/resources/views/tools/text-case.blade.php @@ -0,0 +1,305 @@ +@extends('layouts.app') + +@section('title', 'Text Case Converter - camelCase, snake_case, Title Case | Dev Tools') +@section('meta_description', 'Free online text case converter. Convert text to lowercase, UPPERCASE, Title Case, camelCase, snake_case, kebab-case, PascalCase, and more.') +@section('meta_keywords', 'text case converter, camelcase converter, snake case, kebab case, title case, uppercase, lowercase, pascal case, text transformer') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Text Case Converter

+

Convert text between different case formats

+
+ ← Back +
+ +
+
+
+
+ + +
+ +
+
+ characters + words + lines +
+
+
+ +
+
+ + +
+ +
+
+ +
+
+

Convert To

+
+ +
+
+ +
+

Quick Actions

+
+ + + + +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index d9ffa41..2497e14 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,6 +25,7 @@ Route::get('/qr-code', [ToolController::class, 'qrCode'])->name('qr-code'); Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity'); + Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case'); }); // Static Pages @@ -54,6 +55,7 @@ ['loc' => route('tools.qr-code'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 4cd4af2..a537f09 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -37,6 +37,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('QR Code Generator'); $response->assertSee('HTML Entity Encoder'); + $response->assertSee('Text Case Converter'); } public function test_home_page_has_tool_links(): void @@ -62,6 +63,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.qr-code') . '"', false); $response->assertSee('href="' . route('tools.html-entity') . '"', false); + $response->assertSee('href="' . route('tools.text-case') . '"', false); } public function test_csv_tool_page_loads(): void @@ -366,11 +368,30 @@ public function test_html_entity_tool_has_required_elements(): void $response->assertSee('Decode'); $response->assertSee('Common HTML Entities'); $response->assertSee('Entity Reference'); + public function test_text_case_tool_page_loads(): void + { + $response = $this->get('/tools/text-case'); + + $response->assertStatus(200); + $response->assertSee('Text Case Converter'); + $response->assertSee('Convert text between different case formats'); + } + + public function test_text_case_tool_has_required_elements(): void + { + $response = $this->get('/tools/text-case'); + + $response->assertStatus(200); + $response->assertSee('Input Text'); + $response->assertSee('Convert To'); + $response->assertSee('camelCase'); + $response->assertSee('snake_case'); } public function test_all_pages_have_navigation(): void { $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; foreach ($pages as $page) { $response = $this->get($page); @@ -383,6 +404,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; foreach ($pages as $page) { $response = $this->get($page); @@ -396,6 +418,7 @@ public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; foreach ($pages as $page) { $response = $this->get($page); @@ -409,6 +432,7 @@ public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -462,6 +486,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/text-case']; foreach ($pages as $page) { $response = $this->get($page); From ae48144eb928d1a621aa7889fb1f18a17c820223 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 17:25:47 +0000 Subject: [PATCH 13/23] Add Password Generator tool - Generate secure random passwords using crypto.getRandomValues() - Configurable length (4-64 characters) - Character options: uppercase, lowercase, numbers, symbols - Exclude ambiguous characters option (0OIl1) - Password strength indicator with visual bar - Quick presets: PIN, Simple, Strong, Paranoid - Bulk generation (up to 50 passwords) - Password history with copy functionality --- app/Http/Controllers/ToolController.php | 7 + resources/views/home.blade.php | 3 + .../views/tools/password-generator.blade.php | 446 ++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 25 + 5 files changed, 483 insertions(+) create mode 100644 resources/views/tools/password-generator.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 8718fb8..f6009dd 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -108,6 +108,10 @@ public function index(): View 'description' => 'Convert text to camelCase, snake_case, and more', 'route' => 'tools.text-case', 'icon' => 'text-case', + 'name' => 'Password Generator', + 'description' => 'Generate secure random passwords', + 'route' => 'tools.password', + 'icon' => 'key', ], ]; @@ -195,5 +199,8 @@ public function htmlEntity(): View public function textCase(): View { return view('tools.text-case'); + public function password(): View + { + return view('tools.password-generator'); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 7257a33..d2b06c4 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -116,6 +116,9 @@ + @case('key') + + @break @endswitch diff --git a/resources/views/tools/password-generator.blade.php b/resources/views/tools/password-generator.blade.php new file mode 100644 index 0000000..c87db0c --- /dev/null +++ b/resources/views/tools/password-generator.blade.php @@ -0,0 +1,446 @@ +@extends('layouts.app') + +@section('title', 'Password Generator - Secure Random Passwords | Dev Tools') +@section('meta_description', 'Free online secure password generator. Create strong, random passwords with customizable length and character options. Uses cryptographic randomness.') +@section('meta_keywords', 'password generator, secure password, random password, strong password, password creator, crypto random, password tool') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Password Generator

+

Generate secure random passwords

+
+ ← Back +
+ +
+
+
+ +
+
+ +
+ + +
+
+
+ +
+
+ Strength + +
+
+
+
+
+ + +
+ +
+
+ + +
+
+ +
+
+ +
+

Bulk Generate

+
+ + +
+
+
+ + +
+
+ +
+
+
+
+ +
+
+

Options

+ +
+
+ + +
+ 4 + 64 +
+
+ +
+ + + + + + + + + +
+
+
+ +
+

Quick Presets

+
+ + + + +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 2497e14..414e8bf 100644 --- a/routes/web.php +++ b/routes/web.php @@ -26,6 +26,7 @@ Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity'); Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case'); + Route::get('/password', [ToolController::class, 'password'])->name('password'); }); // Static Pages @@ -56,6 +57,7 @@ ['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.password'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index a537f09..f5426e5 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -38,6 +38,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('HTML Entity Encoder'); $response->assertSee('Text Case Converter'); + $response->assertSee('Password Generator'); } public function test_home_page_has_tool_links(): void @@ -64,6 +65,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.html-entity') . '"', false); $response->assertSee('href="' . route('tools.text-case') . '"', false); + $response->assertSee('href="' . route('tools.password') . '"', false); } public function test_csv_tool_page_loads(): void @@ -386,12 +388,31 @@ public function test_text_case_tool_has_required_elements(): void $response->assertSee('Convert To'); $response->assertSee('camelCase'); $response->assertSee('snake_case'); + public function test_password_tool_page_loads(): void + { + $response = $this->get('/tools/password'); + + $response->assertStatus(200); + $response->assertSee('Password Generator'); + $response->assertSee('Generate secure random passwords'); + } + + public function test_password_tool_has_required_elements(): void + { + $response = $this->get('/tools/password'); + + $response->assertStatus(200); + $response->assertSee('Generated Password'); + $response->assertSee('Strength'); + $response->assertSee('Options'); + $response->assertSee('Quick Presets'); } public function test_all_pages_have_navigation(): void { $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; foreach ($pages as $page) { $response = $this->get($page); @@ -405,6 +426,7 @@ public function test_all_pages_have_theme_toggle(): void { $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; foreach ($pages as $page) { $response = $this->get($page); @@ -419,6 +441,7 @@ public function test_all_pages_load_vite_assets(): void // Code editor uses standalone template without Vite $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; foreach ($pages as $page) { $response = $this->get($page); @@ -433,6 +456,7 @@ public function test_all_tool_pages_have_back_link(): void // Code editor uses standalone template with home link instead of back $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -487,6 +511,7 @@ public function test_pages_have_csrf_token(): void { $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/text-case']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/password']; foreach ($pages as $page) { $response = $this->get($page); From deed4f6ec9f0bef2abff84ecb232128b04709288 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:50:09 +0000 Subject: [PATCH 14/23] Add Lorem Ipsum Generator tool - Generate placeholder text by paragraphs, sentences, or words - Configurable count with slider control - Option to start with classic "Lorem ipsum dolor sit amet..." - Real-time statistics (paragraphs, sentences, words, characters) - Copy to clipboard and download as TXT - Client-side only implementation using Alpine.js --- app/Http/Controllers/ToolController.php | 7 + resources/views/home.blade.php | 3 + resources/views/tools/lorem-ipsum.blade.php | 304 ++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 25 ++ 5 files changed, 341 insertions(+) create mode 100644 resources/views/tools/lorem-ipsum.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index f6009dd..a02002b 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -112,6 +112,10 @@ public function index(): View 'description' => 'Generate secure random passwords', 'route' => 'tools.password', 'icon' => 'key', + 'name' => 'Lorem Ipsum Generator', + 'description' => 'Generate placeholder text for designs', + 'route' => 'tools.lorem', + 'icon' => 'text', ], ]; @@ -202,5 +206,8 @@ public function textCase(): View public function password(): View { return view('tools.password-generator'); + public function lorem(): View + { + return view('tools.lorem-ipsum'); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index d2b06c4..a2c95b2 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -119,6 +119,9 @@ @case('key') + @case('text') + + @break @endswitch diff --git a/resources/views/tools/lorem-ipsum.blade.php b/resources/views/tools/lorem-ipsum.blade.php new file mode 100644 index 0000000..a62ae33 --- /dev/null +++ b/resources/views/tools/lorem-ipsum.blade.php @@ -0,0 +1,304 @@ +@extends('layouts.app') + +@section('title', 'Lorem Ipsum Generator - Placeholder Text Generator | Dev Tools') +@section('meta_description', 'Free online Lorem Ipsum generator. Generate placeholder text by paragraphs, sentences, or words. Perfect for mockups, wireframes, and design projects.') +@section('meta_keywords', 'lorem ipsum generator, placeholder text, dummy text, lipsum, filler text, mockup text, design placeholder, random text generator') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Lorem Ipsum Generator

+

Generate placeholder text for designs and mockups

+
+ ← Back +
+ +
+
+
+

Options

+ +
+
+ +
+ +
+
+ +
+ + +
+ 1 + +
+
+ +
+ + +
+ + +
+
+ +
+

Statistics

+
+
+
+
Paragraphs
+
+
+
+
Sentences
+
+
+
+
Words
+
+
+
+
Characters
+
+
+
+
+ +
+
+
+ +
+ + +
+
+
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 414e8bf..ee52f7f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -27,6 +27,7 @@ Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity'); Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case'); Route::get('/password', [ToolController::class, 'password'])->name('password'); + Route::get('/lorem', [ToolController::class, 'lorem'])->name('lorem'); }); // Static Pages @@ -58,6 +59,7 @@ ['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.password'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.lorem'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index f5426e5..a8989b0 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -39,6 +39,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('HTML Entity Encoder'); $response->assertSee('Text Case Converter'); $response->assertSee('Password Generator'); + $response->assertSee('Lorem Ipsum Generator'); } public function test_home_page_has_tool_links(): void @@ -66,6 +67,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.html-entity') . '"', false); $response->assertSee('href="' . route('tools.text-case') . '"', false); $response->assertSee('href="' . route('tools.password') . '"', false); + $response->assertSee('href="' . route('tools.lorem') . '"', false); } public function test_csv_tool_page_loads(): void @@ -406,6 +408,24 @@ public function test_password_tool_has_required_elements(): void $response->assertSee('Strength'); $response->assertSee('Options'); $response->assertSee('Quick Presets'); + public function test_lorem_tool_page_loads(): void + { + $response = $this->get('/tools/lorem'); + + $response->assertStatus(200); + $response->assertSee('Lorem Ipsum Generator'); + $response->assertSee('Generate placeholder text'); + } + + public function test_lorem_tool_has_required_elements(): void + { + $response = $this->get('/tools/lorem'); + + $response->assertStatus(200); + $response->assertSee('Paragraphs'); + $response->assertSee('Sentences'); + $response->assertSee('Words'); + $response->assertSee('Generate'); } public function test_all_pages_have_navigation(): void @@ -413,6 +433,7 @@ public function test_all_pages_have_navigation(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; foreach ($pages as $page) { $response = $this->get($page); @@ -427,6 +448,7 @@ public function test_all_pages_have_theme_toggle(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; foreach ($pages as $page) { $response = $this->get($page); @@ -442,6 +464,7 @@ public function test_all_pages_load_vite_assets(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; foreach ($pages as $page) { $response = $this->get($page); @@ -457,6 +480,7 @@ public function test_all_tool_pages_have_back_link(): void $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -512,6 +536,7 @@ public function test_pages_have_csrf_token(): void $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/text-case']; $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/password']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; foreach ($pages as $page) { $response = $this->get($page); From ed47802915938272dff24f6ae658b7856f227dad Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:39:40 +0000 Subject: [PATCH 15/23] Add Cron Expression Parser tool - Parse and validate cron expressions with human-readable explanations - Display field breakdown (minute, hour, day, month, weekday) - Calculate and show next 5 scheduled run times - Include common patterns library for quick reference - Add syntax reference panel for cron special characters - Client-side only implementation using Alpine.js --- app/Http/Controllers/ToolController.php | 7 + resources/views/home.blade.php | 3 + resources/views/tools/cron-parser.blade.php | 547 ++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 25 + 5 files changed, 584 insertions(+) create mode 100644 resources/views/tools/cron-parser.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index a02002b..629968c 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -116,6 +116,10 @@ public function index(): View 'description' => 'Generate placeholder text for designs', 'route' => 'tools.lorem', 'icon' => 'text', + 'name' => 'Cron Parser', + 'description' => 'Parse and explain cron expressions', + 'route' => 'tools.cron', + 'icon' => 'clock', ], ]; @@ -209,5 +213,8 @@ public function password(): View public function lorem(): View { return view('tools.lorem-ipsum'); + public function cron(): View + { + return view('tools.cron-parser'); } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index a2c95b2..fed9b9f 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -122,6 +122,9 @@ @case('text') + @case('clock') + + @break @endswitch diff --git a/resources/views/tools/cron-parser.blade.php b/resources/views/tools/cron-parser.blade.php new file mode 100644 index 0000000..475ef07 --- /dev/null +++ b/resources/views/tools/cron-parser.blade.php @@ -0,0 +1,547 @@ +@extends('layouts.app') + +@section('title', 'Cron Expression Parser - Explain & Validate Cron Jobs | Dev Tools') +@section('meta_description', 'Free online cron expression parser. Understand cron syntax, validate expressions, see next run times, and get human-readable explanations of your cron schedules.') +@section('meta_keywords', 'cron parser, cron expression, cron validator, cron schedule, crontab, cron syntax, cron job, cron generator, cron explainer, cron next run') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Cron Expression Parser

+

Parse and explain cron expressions with next run times

+
+ ← Back +
+ +
+
+
+ +
+ + +
+ +
+ +
+
+ +
+

Human Readable

+

+
+ +
+
+ + + + +
+
+ +
+

Field Breakdown

+
+ +
+
+ +
+

Next Run Times

+
+ +
+
+
+ +
+
+

Common Patterns

+
+ +
+
+ +
+

Syntax Reference

+
+
+

Field Values

+ + + + + + + + + + + + + + + + + + + + + + + +
Minute0-59
Hour0-23
Day of Month1-31
Month1-12
Day of Week0-6 (Sun-Sat)
+
+
+

Special Characters

+ + + + + + + + + + + + + + + + + + + +
*Any value
,Value list (1,3,5)
-Range (1-5)
/Step (*/15)
+
+
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index ee52f7f..ee8cb4e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -28,6 +28,7 @@ Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case'); Route::get('/password', [ToolController::class, 'password'])->name('password'); Route::get('/lorem', [ToolController::class, 'lorem'])->name('lorem'); + Route::get('/cron', [ToolController::class, 'cron'])->name('cron'); }); // Static Pages @@ -60,6 +61,7 @@ ['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.password'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.lorem'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.cron'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index a8989b0..5cd285b 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -40,6 +40,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Text Case Converter'); $response->assertSee('Password Generator'); $response->assertSee('Lorem Ipsum Generator'); + $response->assertSee('Cron Parser'); } public function test_home_page_has_tool_links(): void @@ -68,6 +69,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.text-case') . '"', false); $response->assertSee('href="' . route('tools.password') . '"', false); $response->assertSee('href="' . route('tools.lorem') . '"', false); + $response->assertSee('href="' . route('tools.cron') . '"', false); } public function test_csv_tool_page_loads(): void @@ -426,6 +428,24 @@ public function test_lorem_tool_has_required_elements(): void $response->assertSee('Sentences'); $response->assertSee('Words'); $response->assertSee('Generate'); + public function test_cron_tool_page_loads(): void + { + $response = $this->get('/tools/cron'); + + $response->assertStatus(200); + $response->assertSee('Cron Expression Parser'); + $response->assertSee('Parse and explain cron expressions'); + } + + public function test_cron_tool_has_required_elements(): void + { + $response = $this->get('/tools/cron'); + + $response->assertStatus(200); + $response->assertSee('Cron Expression'); + $response->assertSee('Common Patterns'); + $response->assertSee('Syntax Reference'); + $response->assertSee('Next Run Times'); } public function test_all_pages_have_navigation(): void @@ -434,6 +454,7 @@ public function test_all_pages_have_navigation(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -449,6 +470,7 @@ public function test_all_pages_have_theme_toggle(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -465,6 +487,7 @@ public function test_all_pages_load_vite_assets(): void $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -481,6 +504,7 @@ public function test_all_tool_pages_have_back_link(): void $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/cron']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -537,6 +561,7 @@ public function test_pages_have_csrf_token(): void $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/text-case']; $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/password']; $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); From cd213db84327185aa12058fb65852d8b83ecbcfd Mon Sep 17 00:00:00 2001 From: jalel Date: Mon, 15 Dec 2025 00:41:42 +0000 Subject: [PATCH 16/23] Fix syntax errors in merged tool files Resolve missing closing braces in ToolController methods and closing tags in home.blade.php icon cases. Also fix duplicate array assignments in WebRoutesTest by consolidating all tool pages into single arrays. --- app/Http/Controllers/ToolController.php | 16 ++++++++++ resources/views/home.blade.php | 14 +++++++-- tests/Feature/WebRoutesTest.php | 40 ++++++++++--------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 629968c..eb8c3a2 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -104,18 +104,26 @@ public function index(): View 'description' => 'Encode/decode HTML entities and special characters', 'route' => 'tools.html-entity', 'icon' => 'html-entity', + ], + [ 'name' => 'Text Case Converter', 'description' => 'Convert text to camelCase, snake_case, and more', 'route' => 'tools.text-case', 'icon' => 'text-case', + ], + [ 'name' => 'Password Generator', 'description' => 'Generate secure random passwords', 'route' => 'tools.password', 'icon' => 'key', + ], + [ 'name' => 'Lorem Ipsum Generator', 'description' => 'Generate placeholder text for designs', 'route' => 'tools.lorem', 'icon' => 'text', + ], + [ 'name' => 'Cron Parser', 'description' => 'Parse and explain cron expressions', 'route' => 'tools.cron', @@ -204,15 +212,23 @@ public function qrCode(): View public function htmlEntity(): View { return view('tools.html-entity'); + } + public function textCase(): View { return view('tools.text-case'); + } + public function password(): View { return view('tools.password-generator'); + } + public function lorem(): View { return view('tools.lorem-ipsum'); + } + public function cron(): View { return view('tools.cron-parser'); diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index fed9b9f..2a21f80 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -104,24 +104,34 @@ @case('color') - + + @break @case('qrcode') - + + @break @case('html-entity') + + @break @case('text-case') + + @break @case('key') + + @break @case('text') + + @break @case('clock') diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 5cd285b..ae1bc1b 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -336,6 +336,7 @@ public function test_color_picker_has_required_elements(): void $response->assertSee('HSL'); $response->assertSee('CMYK'); $response->assertSee('Color Harmony'); + } public function test_qr_code_tool_page_loads(): void { @@ -355,6 +356,7 @@ public function test_qr_code_tool_has_required_elements(): void $response->assertSee('Quick Templates'); $response->assertSee('Download PNG'); $response->assertSee('Error Correction'); + } public function test_html_entity_tool_page_loads(): void { @@ -374,6 +376,8 @@ public function test_html_entity_tool_has_required_elements(): void $response->assertSee('Decode'); $response->assertSee('Common HTML Entities'); $response->assertSee('Entity Reference'); + } + public function test_text_case_tool_page_loads(): void { $response = $this->get('/tools/text-case'); @@ -392,6 +396,8 @@ public function test_text_case_tool_has_required_elements(): void $response->assertSee('Convert To'); $response->assertSee('camelCase'); $response->assertSee('snake_case'); + } + public function test_password_tool_page_loads(): void { $response = $this->get('/tools/password'); @@ -410,6 +416,8 @@ public function test_password_tool_has_required_elements(): void $response->assertSee('Strength'); $response->assertSee('Options'); $response->assertSee('Quick Presets'); + } + public function test_lorem_tool_page_loads(): void { $response = $this->get('/tools/lorem'); @@ -428,6 +436,8 @@ public function test_lorem_tool_has_required_elements(): void $response->assertSee('Sentences'); $response->assertSee('Words'); $response->assertSee('Generate'); + } + public function test_cron_tool_page_loads(): void { $response = $this->get('/tools/cron'); @@ -450,11 +460,7 @@ public function test_cron_tool_has_required_elements(): void public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -466,11 +472,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/text-case']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/password']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -483,11 +485,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); @@ -500,11 +498,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/text-case']; - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/password']; - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/lorem']; - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/cron']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -557,11 +551,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity']; - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/text-case']; - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/password']; - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/lorem']; - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/cron']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; foreach ($pages as $page) { $response = $this->get($page); From 0278624d8ce575898da652b9d3c762382a7fa959 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:13:03 +0000 Subject: [PATCH 17/23] Add JWT Decoder tool - Decode and display JWT header and payload - Show token structure with color-coded parts - Expiration status indicator (valid/expired) - Human-readable timestamps for exp, iat, nbf claims - Registered claims reference section - Sample JWT for testing - Copy header/payload to clipboard - Security notice about client-side decoding --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 6 +- resources/views/tools/jwt.blade.php | 336 ++++++++++++++++++++++++ routes/web.php | 8 +- tests/Feature/WebRoutesTest.php | 38 ++- 5 files changed, 381 insertions(+), 18 deletions(-) create mode 100644 resources/views/tools/jwt.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index eb8c3a2..00800dd 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -129,6 +129,12 @@ public function index(): View 'route' => 'tools.cron', 'icon' => 'clock', ], + [ + 'name' => 'JWT Decoder', + 'description' => 'Decode and inspect JSON Web Tokens', + 'route' => 'tools.jwt', + 'icon' => 'jwt', + ], ]; return view('home', compact('tools')); @@ -233,4 +239,9 @@ public function cron(): View { return view('tools.cron-parser'); } + + public function jwt(): View + { + return view('tools.jwt'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 2a21f80..ee0052f 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -90,7 +90,6 @@ @break - @case('calculator') @@ -137,6 +136,11 @@ @break + @case('jwt') + + + + @break @endswitch
diff --git a/resources/views/tools/jwt.blade.php b/resources/views/tools/jwt.blade.php new file mode 100644 index 0000000..4910a1b --- /dev/null +++ b/resources/views/tools/jwt.blade.php @@ -0,0 +1,336 @@ +@extends('layouts.app') + +@section('title', 'JWT Decoder - Decode JSON Web Tokens Online | Dev Tools') +@section('meta_description', 'Free online JWT decoder. Decode and inspect JSON Web Tokens, view header and payload, check expiration status. No data sent to server.') +@section('meta_keywords', 'jwt decoder, json web token, jwt parser, jwt debugger, decode jwt, jwt viewer, token decoder, jwt online') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

JWT Decoder

+

Decode and inspect JSON Web Tokens

+
+ ← Back +
+ +
+ +
+
+
+ + +
+ +
+
+ + +
+ +
+ .. +
+
+
+ + Header +
+
+ + Payload +
+
+ + Signature +
+
+
+ + +
+
+ + + +
+

Security Note

+

JWTs are decoded client-side. No data is sent to any server. Never share tokens containing sensitive information.

+
+
+
+
+ + +
+ +
+
+ + + +
+

+
+ + +
+
+ + +
+

+                
Paste a JWT to see the header
+
+ + +
+
+ + +
+

+                
Paste a JWT to see the payload
+
+ + +
+ +
+ +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index ee8cb4e..b7cfa7b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,18 +17,16 @@ Route::get('/url', [ToolController::class, 'url'])->name('url'); Route::get('/code-editor', [ToolController::class, 'codeEditor'])->name('code-editor'); Route::get('/regex', [ToolController::class, 'regex'])->name('regex'); - Route::get('/base-converter', [ToolController::class, 'baseConverter'])->name('base-converter'); Route::get('/slug-generator', [ToolController::class, 'slugGenerator'])->name('slug-generator'); Route::get('/color-picker', [ToolController::class, 'colorPicker'])->name('color-picker'); - Route::get('/qr-code', [ToolController::class, 'qrCode'])->name('qr-code'); - Route::get('/html-entity', [ToolController::class, 'htmlEntity'])->name('html-entity'); Route::get('/text-case', [ToolController::class, 'textCase'])->name('text-case'); Route::get('/password', [ToolController::class, 'password'])->name('password'); Route::get('/lorem', [ToolController::class, 'lorem'])->name('lorem'); Route::get('/cron', [ToolController::class, 'cron'])->name('cron'); + Route::get('/jwt', [ToolController::class, 'jwt'])->name('jwt'); }); // Static Pages @@ -50,18 +48,16 @@ ['loc' => route('tools.url'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.code-editor'), 'priority' => '0.9', 'changefreq' => 'monthly'], ['loc' => route('tools.regex'), 'priority' => '0.8', 'changefreq' => 'monthly'], - ['loc' => route('tools.base-converter'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.slug-generator'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.color-picker'), 'priority' => '0.8', 'changefreq' => 'monthly'], - ['loc' => route('tools.qr-code'), 'priority' => '0.8', 'changefreq' => 'monthly'], - ['loc' => route('tools.html-entity'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.text-case'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.password'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.lorem'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.cron'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.jwt'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index ae1bc1b..20d26ab 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -29,18 +29,16 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('URL Encoder'); $response->assertSee('Code Editor'); $response->assertSee('Regex Tester'); - $response->assertSee('Base Converter'); $response->assertSee('Slug Generator'); $response->assertSee('Color Picker'); - $response->assertSee('QR Code Generator'); - $response->assertSee('HTML Entity Encoder'); $response->assertSee('Text Case Converter'); $response->assertSee('Password Generator'); $response->assertSee('Lorem Ipsum Generator'); $response->assertSee('Cron Parser'); + $response->assertSee('JWT Decoder'); } public function test_home_page_has_tool_links(): void @@ -58,18 +56,16 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.url') . '"', false); $response->assertSee('href="' . route('tools.code-editor') . '"', false); $response->assertSee('href="' . route('tools.regex') . '"', false); - $response->assertSee('href="' . route('tools.base-converter') . '"', false); $response->assertSee('href="' . route('tools.slug-generator') . '"', false); $response->assertSee('href="' . route('tools.color-picker') . '"', false); - $response->assertSee('href="' . route('tools.qr-code') . '"', false); - $response->assertSee('href="' . route('tools.html-entity') . '"', false); $response->assertSee('href="' . route('tools.text-case') . '"', false); $response->assertSee('href="' . route('tools.password') . '"', false); $response->assertSee('href="' . route('tools.lorem') . '"', false); $response->assertSee('href="' . route('tools.cron') . '"', false); + $response->assertSee('href="' . route('tools.jwt') . '"', false); } public function test_csv_tool_page_loads(): void @@ -458,9 +454,29 @@ public function test_cron_tool_has_required_elements(): void $response->assertSee('Next Run Times'); } + public function test_jwt_tool_page_loads(): void + { + $response = $this->get('/tools/jwt'); + + $response->assertStatus(200); + $response->assertSee('JWT Decoder'); + $response->assertSee('Decode and inspect JSON Web Tokens'); + } + + public function test_jwt_tool_has_required_elements(): void + { + $response = $this->get('/tools/jwt'); + + $response->assertStatus(200); + $response->assertSee('JWT Token'); + $response->assertSee('Header'); + $response->assertSee('Payload'); + $response->assertSee('Load sample'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; foreach ($pages as $page) { $response = $this->get($page); @@ -472,7 +488,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; foreach ($pages as $page) { $response = $this->get($page); @@ -485,7 +501,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; foreach ($pages as $page) { $response = $this->get($page); @@ -498,7 +514,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -551,7 +567,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; foreach ($pages as $page) { $response = $this->get($page); From 234edb929456fea1a5719f9ca79a309caf82322b Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:20:00 +0000 Subject: [PATCH 18/23] Add Timestamp Converter tool - Live current Unix timestamp display - Timestamp to date conversion (seconds/milliseconds) - Date to timestamp conversion with timezone support - Multiple output formats (local, UTC, ISO 8601, relative) - Quick reference with common timestamps - Copy timestamps to clipboard --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + resources/views/tools/timestamp.blade.php | 383 ++++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 31 +- 5 files changed, 427 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/timestamp.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 00800dd..0534827 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -135,6 +135,12 @@ public function index(): View 'route' => 'tools.jwt', 'icon' => 'jwt', ], + [ + 'name' => 'Timestamp Converter', + 'description' => 'Convert Unix timestamps to dates and vice versa', + 'route' => 'tools.timestamp', + 'icon' => 'timestamp', + ], ]; return view('home', compact('tools')); @@ -244,4 +250,9 @@ public function jwt(): View { return view('tools.jwt'); } + + public function timestamp(): View + { + return view('tools.timestamp'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index ee0052f..71ea7a4 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -141,6 +141,11 @@ @break + @case('timestamp') + + + + @break @endswitch
diff --git a/resources/views/tools/timestamp.blade.php b/resources/views/tools/timestamp.blade.php new file mode 100644 index 0000000..7f78f80 --- /dev/null +++ b/resources/views/tools/timestamp.blade.php @@ -0,0 +1,383 @@ +@extends('layouts.app') + +@section('title', 'Unix Timestamp Converter - Convert Timestamps Online | Dev Tools') +@section('meta_description', 'Free online Unix timestamp converter. Convert timestamps to human-readable dates and vice versa. Supports seconds, milliseconds, and multiple timezones.') +@section('meta_keywords', 'unix timestamp, timestamp converter, epoch converter, date to timestamp, timestamp to date, unix time, epoch time') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Timestamp Converter

+

Convert Unix timestamps to dates and vice versa

+
+ ← Back +
+ + +
+
+

Current Unix Timestamp

+
+ + +
+

+
+
+ +
+ +
+

Timestamp to Date

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+
+ Local Time + +
+
+ UTC + +
+
+ ISO 8601 + +
+
+ Relative + +
+
+
+
+ + +
+

Date to Timestamp

+ +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ + + +
+
+ Seconds +
+ + +
+
+
+ Milliseconds +
+ + +
+
+
+
+
+
+ + +
+

Quick Reference

+
+ +
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index b7cfa7b..b2c2d4f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -27,6 +27,7 @@ Route::get('/lorem', [ToolController::class, 'lorem'])->name('lorem'); Route::get('/cron', [ToolController::class, 'cron'])->name('cron'); Route::get('/jwt', [ToolController::class, 'jwt'])->name('jwt'); + Route::get('/timestamp', [ToolController::class, 'timestamp'])->name('timestamp'); }); // Static Pages @@ -58,6 +59,7 @@ ['loc' => route('tools.lorem'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.cron'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.jwt'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.timestamp'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 20d26ab..7fe2697 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -39,6 +39,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Lorem Ipsum Generator'); $response->assertSee('Cron Parser'); $response->assertSee('JWT Decoder'); + $response->assertSee('Timestamp Converter'); } public function test_home_page_has_tool_links(): void @@ -66,6 +67,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.lorem') . '"', false); $response->assertSee('href="' . route('tools.cron') . '"', false); $response->assertSee('href="' . route('tools.jwt') . '"', false); + $response->assertSee('href="' . route('tools.timestamp') . '"', false); } public function test_csv_tool_page_loads(): void @@ -474,9 +476,28 @@ public function test_jwt_tool_has_required_elements(): void $response->assertSee('Load sample'); } + public function test_timestamp_tool_page_loads(): void + { + $response = $this->get('/tools/timestamp'); + + $response->assertStatus(200); + $response->assertSee('Timestamp Converter'); + $response->assertSee('Convert Unix timestamps to dates'); + } + + public function test_timestamp_tool_has_required_elements(): void + { + $response = $this->get('/tools/timestamp'); + + $response->assertStatus(200); + $response->assertSee('Timestamp to Date'); + $response->assertSee('Date to Timestamp'); + $response->assertSee('Current Unix Timestamp'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; foreach ($pages as $page) { $response = $this->get($page); @@ -488,7 +509,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; foreach ($pages as $page) { $response = $this->get($page); @@ -501,7 +522,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; foreach ($pages as $page) { $response = $this->get($page); @@ -514,7 +535,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -567,7 +588,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; foreach ($pages as $page) { $response = $this->get($page); From fa9acbc5fb159a5990efd486cffa146d58764351 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:26:08 +0000 Subject: [PATCH 19/23] Add Diff Checker tool - Side-by-side text comparison with LCS algorithm - Highlight additions (green) and deletions (red) - Line numbers for both sides - Statistics showing added, removed, unchanged lines - Ignore whitespace and case options - Swap texts functionality - Sample code for testing --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + resources/views/tools/diff.blade.php | 336 ++++++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 31 ++- 5 files changed, 380 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/diff.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index 0534827..d0a6454 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -141,6 +141,12 @@ public function index(): View 'route' => 'tools.timestamp', 'icon' => 'timestamp', ], + [ + 'name' => 'Diff Checker', + 'description' => 'Compare two texts and highlight differences', + 'route' => 'tools.diff', + 'icon' => 'diff', + ], ]; return view('home', compact('tools')); @@ -255,4 +261,9 @@ public function timestamp(): View { return view('tools.timestamp'); } + + public function diff(): View + { + return view('tools.diff'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 71ea7a4..4e1851a 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -146,6 +146,11 @@ @break + @case('diff') + + + + @break @endswitch
diff --git a/resources/views/tools/diff.blade.php b/resources/views/tools/diff.blade.php new file mode 100644 index 0000000..3b2869c --- /dev/null +++ b/resources/views/tools/diff.blade.php @@ -0,0 +1,336 @@ +@extends('layouts.app') + +@section('title', 'Diff Checker - Compare Text Online | Dev Tools') +@section('meta_description', 'Free online diff checker. Compare two texts side by side, highlight differences, and see additions, deletions, and changes. Perfect for code review.') +@section('meta_keywords', 'diff checker, text compare, diff tool, compare files, code diff, text difference, online diff') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Diff Checker

+

Compare two texts and highlight differences

+
+ ← Back +
+ + +
+
+
+ + +
+ +
+ +
+
+ + +
+ +
+
+ + +
+
+ + + +
+ +
+ + + +
+
+ + +
+
+ added +
+
+ removed +
+
+ unchanged +
+
+ + +
+
+
+ Original +
+
+ Modified +
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+ + +
+ + + +

Enter text in both fields and click Compare to see differences

+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index b2c2d4f..63e04f2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -28,6 +28,7 @@ Route::get('/cron', [ToolController::class, 'cron'])->name('cron'); Route::get('/jwt', [ToolController::class, 'jwt'])->name('jwt'); Route::get('/timestamp', [ToolController::class, 'timestamp'])->name('timestamp'); + Route::get('/diff', [ToolController::class, 'diff'])->name('diff'); }); // Static Pages @@ -60,6 +61,7 @@ ['loc' => route('tools.cron'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.jwt'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.timestamp'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.diff'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index 7fe2697..d99e52e 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -40,6 +40,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('Cron Parser'); $response->assertSee('JWT Decoder'); $response->assertSee('Timestamp Converter'); + $response->assertSee('Diff Checker'); } public function test_home_page_has_tool_links(): void @@ -68,6 +69,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.cron') . '"', false); $response->assertSee('href="' . route('tools.jwt') . '"', false); $response->assertSee('href="' . route('tools.timestamp') . '"', false); + $response->assertSee('href="' . route('tools.diff') . '"', false); } public function test_csv_tool_page_loads(): void @@ -495,9 +497,28 @@ public function test_timestamp_tool_has_required_elements(): void $response->assertSee('Current Unix Timestamp'); } + public function test_diff_tool_page_loads(): void + { + $response = $this->get('/tools/diff'); + + $response->assertStatus(200); + $response->assertSee('Diff Checker'); + $response->assertSee('Compare two texts and highlight differences'); + } + + public function test_diff_tool_has_required_elements(): void + { + $response = $this->get('/tools/diff'); + + $response->assertStatus(200); + $response->assertSee('Original Text'); + $response->assertSee('Modified Text'); + $response->assertSee('Compare'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; foreach ($pages as $page) { $response = $this->get($page); @@ -509,7 +530,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; foreach ($pages as $page) { $response = $this->get($page); @@ -522,7 +543,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; foreach ($pages as $page) { $response = $this->get($page); @@ -535,7 +556,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -588,7 +609,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; foreach ($pages as $page) { $response = $this->get($page); From df918947dc0d55e5947bcc439a19f2d46e710ade Mon Sep 17 00:00:00 2001 From: jalel Date: Mon, 15 Dec 2025 01:09:08 +0000 Subject: [PATCH 20/23] Prepare v1.2.0 release - Update CHANGELOG.md with 13 new tools and enhancements - Update version to v1.2.0 in footer --- CHANGELOG.md | 94 +++++++++++++++++++++++++++ resources/views/layouts/app.blade.php | 2 +- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 187aaba..4a98f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,99 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2025-12-15 + +### Added + +- **Regex Tester**: Live regular expression testing with match highlighting + - Real-time pattern matching with visual feedback + - Support for global, case-insensitive, and multiline flags + - Match groups extraction and display + +- **JWT Decoder**: Decode and inspect JSON Web Tokens + - Header and payload extraction + - Expiration time validation + - Signature verification status + +- **Timestamp Converter**: Unix timestamp to human-readable date conversion + - Bidirectional conversion (timestamp ↔ date) + - Multiple timezone support + - Current timestamp display + +- **Diff Checker**: Side-by-side text comparison tool + - Line-by-line diff visualization + - Added/removed line highlighting + - Unified and split view modes + +- **Cron Expression Parser**: Parse and explain cron expressions + - Human-readable cron schedule explanation + - Next execution times preview + - Common cron presets + +- **Lorem Ipsum Generator**: Generate placeholder text + - Paragraphs, sentences, or words generation + - Configurable output length + - Start with "Lorem ipsum" option + +- **Password Generator**: Generate secure random passwords + - Configurable length and character sets + - Uppercase, lowercase, numbers, symbols options + - Password strength indicator + +- **Text Case Converter**: Convert between 13 text case formats + - lowercase, UPPERCASE, Title Case, Sentence case + - camelCase, PascalCase, snake_case, kebab-case + - CONSTANT_CASE, dot.case, path/case, and more + +- **HTML Entity Encoder**: Encode and decode HTML entities + - Named entities (e.g., &, <, >) + - Numeric entities support + - Bulk encoding/decoding + +- **QR Code Generator**: Generate QR codes from text or URLs + - Customizable size and error correction + - Download as PNG + - Real-time preview + +- **Color Picker**: Color format converter + - HEX, RGB, HSL, CMYK formats + - Visual color picker + - Color palette suggestions + +- **Slug Generator**: Create URL-friendly slugs from text + - Configurable separator (hyphen, underscore) + - Unicode transliteration + - Length limiting option + +- **Base Converter**: Number base conversion tool + - Binary, Octal, Decimal, Hexadecimal + - Bit visualization + - Instant conversion between bases + +### Enhanced + +- **Code Editor**: Major enhancements + - Dynamic tab system with add/close functionality + - Console output capture (log, info, warn, error) + - Keyboard shortcuts help modal + - Word wrap and minimap toggle controls + - Download all files as ZIP + +- **JSON Parser**: Added interactive tree view + - Collapsible tree structure + - Node type indicators + - Copy path to clipboard + +- **Theme Toggle**: Redesigned with animated day/night scene + - Smooth transition animations + - Sun/moon visual elements + +### Other + +- Added Privacy Policy page +- Added About page +- Integrated Google Analytics 4 tracking + ## [1.1.0] - 2024-11-30 ### Added @@ -85,5 +178,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RESTful API endpoints for all tools - 146 tests with 386 assertions +[1.2.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.2.0 [1.1.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.1.0 [1.0.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.0.0 diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 4baa0d6..ca2ea7e 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -126,7 +126,7 @@ class="absolute top-1 w-6 h-6 rounded-full shadow-lg transition-all duration-500 About Privacy GitHub - v1.1.0 + v1.2.0
From 9f406f9eaf9e5b3e8592d6881cf6c08f2d2c3503 Mon Sep 17 00:00:00 2001 From: jalel Date: Mon, 15 Dec 2025 09:30:58 +0000 Subject: [PATCH 21/23] Fix PHP parse error in code editor - bump to v1.2.1 --- CHANGELOG.md | 7 +++++++ resources/views/layouts/app.blade.php | 2 +- resources/views/tools/code-editor.blade.php | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a98f09..0566d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.1] - 2025-12-15 + +### Fixed + +- Code Editor: Fix PHP parse error when loading the page (`About Privacy GitHub - v1.2.0 + v1.2.1
diff --git a/resources/views/tools/code-editor.blade.php b/resources/views/tools/code-editor.blade.php index 731ebfa..09699b4 100644 --- a/resources/views/tools/code-editor.blade.php +++ b/resources/views/tools/code-editor.blade.php @@ -575,7 +575,7 @@ function getDefaultContent(language) { case 'css': return '/* Styles */\n'; case 'javascript': return '// JavaScript\n'; case 'json': return '{\n \n}'; - case 'php': return ' Date: Mon, 15 Dec 2025 10:03:22 +0000 Subject: [PATCH 22/23] Add Sort Lines tool --- app/Http/Controllers/ToolController.php | 11 + resources/views/home.blade.php | 5 + resources/views/tools/sort-lines.blade.php | 351 +++++++++++++++++++++ routes/web.php | 2 + tests/Feature/WebRoutesTest.php | 33 +- 5 files changed, 397 insertions(+), 5 deletions(-) create mode 100644 resources/views/tools/sort-lines.blade.php diff --git a/app/Http/Controllers/ToolController.php b/app/Http/Controllers/ToolController.php index d0a6454..682b4c9 100644 --- a/app/Http/Controllers/ToolController.php +++ b/app/Http/Controllers/ToolController.php @@ -147,6 +147,12 @@ public function index(): View 'route' => 'tools.diff', 'icon' => 'diff', ], + [ + 'name' => 'Sort Lines', + 'description' => 'Sort, deduplicate, reverse, and shuffle lines', + 'route' => 'tools.sort-lines', + 'icon' => 'sort', + ], ]; return view('home', compact('tools')); @@ -266,4 +272,9 @@ public function diff(): View { return view('tools.diff'); } + + public function sortLines(): View + { + return view('tools.sort-lines'); + } } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 4e1851a..972ddb3 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -151,6 +151,11 @@ @break + @case('sort') + + + + @break @endswitch
diff --git a/resources/views/tools/sort-lines.blade.php b/resources/views/tools/sort-lines.blade.php new file mode 100644 index 0000000..ba2cd1d --- /dev/null +++ b/resources/views/tools/sort-lines.blade.php @@ -0,0 +1,351 @@ +@extends('layouts.app') + +@section('title', 'Sort Lines - Sort, Dedupe, Reverse, Shuffle Text | Dev Tools') +@section('meta_description', 'Free online line sorter. Sort lines alphabetically, numerically, remove duplicates, reverse order, or shuffle randomly. Fast and private - no data stored.') +@section('meta_keywords', 'sort lines, line sorter, sort text, remove duplicates, dedupe lines, reverse lines, shuffle lines, alphabetical sort, natural sort') + +@push('schema') + +@endpush + +@section('content') +
+
+
+

Sort Lines

+

Sort, deduplicate, reverse, and shuffle text lines

+
+ ← Back +
+ +
+
+
+
+ +
+ + +
+
+ +
+
+ lines + unique + duplicates +
+
+
+ +
+
+ + +
+ +
+
+ lines +
+
+
+
+ +
+
+

Sort Options

+
+ +
+
+ +
+

Options

+
+ + + +
+
+ +
+

Quick Actions

+
+ + + + +
+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 63e04f2..de71750 100644 --- a/routes/web.php +++ b/routes/web.php @@ -29,6 +29,7 @@ Route::get('/jwt', [ToolController::class, 'jwt'])->name('jwt'); Route::get('/timestamp', [ToolController::class, 'timestamp'])->name('timestamp'); Route::get('/diff', [ToolController::class, 'diff'])->name('diff'); + Route::get('/sort-lines', [ToolController::class, 'sortLines'])->name('sort-lines'); }); // Static Pages @@ -62,6 +63,7 @@ ['loc' => route('tools.jwt'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.timestamp'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('tools.diff'), 'priority' => '0.8', 'changefreq' => 'monthly'], + ['loc' => route('tools.sort-lines'), 'priority' => '0.8', 'changefreq' => 'monthly'], ['loc' => route('about'), 'priority' => '0.5', 'changefreq' => 'monthly'], ['loc' => route('privacy'), 'priority' => '0.3', 'changefreq' => 'yearly'], ]; diff --git a/tests/Feature/WebRoutesTest.php b/tests/Feature/WebRoutesTest.php index d99e52e..b4c1203 100644 --- a/tests/Feature/WebRoutesTest.php +++ b/tests/Feature/WebRoutesTest.php @@ -41,6 +41,7 @@ public function test_home_page_displays_all_tools(): void $response->assertSee('JWT Decoder'); $response->assertSee('Timestamp Converter'); $response->assertSee('Diff Checker'); + $response->assertSee('Sort Lines'); } public function test_home_page_has_tool_links(): void @@ -70,6 +71,7 @@ public function test_home_page_has_tool_links(): void $response->assertSee('href="' . route('tools.jwt') . '"', false); $response->assertSee('href="' . route('tools.timestamp') . '"', false); $response->assertSee('href="' . route('tools.diff') . '"', false); + $response->assertSee('href="' . route('tools.sort-lines') . '"', false); } public function test_csv_tool_page_loads(): void @@ -516,9 +518,30 @@ public function test_diff_tool_has_required_elements(): void $response->assertSee('Compare'); } + public function test_sort_lines_page_loads(): void + { + $response = $this->get('/tools/sort-lines'); + + $response->assertStatus(200); + $response->assertSee('Sort Lines'); + $response->assertSee('Sort, deduplicate, reverse, and shuffle text lines'); + } + + public function test_sort_lines_has_required_elements(): void + { + $response = $this->get('/tools/sort-lines'); + + $response->assertStatus(200); + $response->assertSee('Input Text'); + $response->assertSee('Sort Options'); + $response->assertSee('Sort A-Z'); + $response->assertSee('Remove Duplicates'); + $response->assertSee('Shuffle'); + } + public function test_all_pages_have_navigation(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff', '/tools/sort-lines']; foreach ($pages as $page) { $response = $this->get($page); @@ -530,7 +553,7 @@ public function test_all_pages_have_navigation(): void public function test_all_pages_have_theme_toggle(): void { - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff', '/tools/sort-lines']; foreach ($pages as $page) { $response = $this->get($page); @@ -543,7 +566,7 @@ public function test_all_pages_have_theme_toggle(): void public function test_all_pages_load_vite_assets(): void { // Code editor uses standalone template without Vite - $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; + $pages = ['/', '/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff', '/tools/sort-lines']; foreach ($pages as $page) { $response = $this->get($page); @@ -556,7 +579,7 @@ public function test_all_pages_load_vite_assets(): void public function test_all_tool_pages_have_back_link(): void { // Code editor uses standalone template with home link instead of back - $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; + $toolPages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff', '/tools/sort-lines']; foreach ($toolPages as $page) { $response = $this->get($page); @@ -609,7 +632,7 @@ public function test_api_routes_reject_get_requests(): void public function test_pages_have_csrf_token(): void { - $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff']; + $pages = ['/tools/csv', '/tools/yaml', '/tools/markdown', '/tools/sql', '/tools/base64', '/tools/uuid', '/tools/hash', '/tools/url', '/tools/code-editor', '/tools/regex', '/tools/base-converter', '/tools/slug-generator', '/tools/color-picker', '/tools/qr-code', '/tools/html-entity', '/tools/text-case', '/tools/password', '/tools/lorem', '/tools/cron', '/tools/jwt', '/tools/timestamp', '/tools/diff', '/tools/sort-lines']; foreach ($pages as $page) { $response = $this->get($page); From 3d88fe8171b5c3a91b11e7d505ba9f3b660b0d7c Mon Sep 17 00:00:00 2001 From: jalel Date: Mon, 15 Dec 2025 10:17:21 +0000 Subject: [PATCH 23/23] Prepare v1.3.0 release --- CHANGELOG.md | 15 +++++++++++++++ resources/views/layouts/app.blade.php | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0566d58..05b5551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2025-12-15 + +### Added + +- **Sort Lines**: New text line manipulation tool + - Sort alphabetically (A-Z, Z-A) + - Natural sort for alphanumeric strings (file1, file2, file10) + - Numeric sort (ascending/descending) + - Sort by line length + - Reverse line order + - Remove duplicates (dedupe) + - Shuffle/randomize lines + - Options: case sensitive, trim whitespace, remove empty lines + ## [1.2.1] - 2025-12-15 ### Fixed @@ -184,6 +198,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RESTful API endpoints for all tools - 146 tests with 386 assertions +[1.3.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.3.0 [1.2.1]: https://github.com/GhDj/dev-tools/releases/tag/v1.2.1 [1.2.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.2.0 [1.1.0]: https://github.com/GhDj/dev-tools/releases/tag/v1.1.0 diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 0238e01..31ae7c0 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -126,7 +126,7 @@ class="absolute top-1 w-6 h-6 rounded-full shadow-lg transition-all duration-500 About Privacy GitHub - v1.2.1 + v1.3.0