From 0278624d8ce575898da652b9d3c762382a7fa959 Mon Sep 17 00:00:00 2001 From: jalel Date: Sun, 14 Dec 2025 15:13:03 +0000 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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);