- import Badge from '$lib/components/ui/badge/badge.svelte';
- import Button from '$lib/components/ui/button/button.svelte';
- import * as Card from '$lib/components/ui/card/index.js';
- import createBackup from '$lib/createBackup';
- import Trophy from '@lucide/svelte/icons/trophy';
- import Star from '@lucide/svelte/icons/star';
- import TriangleAlert from '@lucide/svelte/icons/triangle-alert';
-
- import { page } from '$app/state';
- import clsx from 'clsx';
-
- import type { Mirror } from './mirrors.config';
- import { mirrors } from './mirrors.config';
- import { toast } from 'svelte-sonner';
-
-
-
-
Mirrors
-
- {#each mirrors as mirror}
- {@const selected = (() => {
- try {
- return page.url.hostname == new URL(mirror.url).hostname;
- } catch (e) {
- return false;
- }
- })()}
-
-
-
- {(() => {
- try {
- return new URL(mirror.url).hostname;
- } catch (e) {
- return 'Invalid URL';
- }
- })()}
-
-
- {#if selected}
- Current
- {/if}
- {#if mirror.quality == 'highlyrecommended'}
- Highly Recommended
- {:else if mirror.quality == 'recommended'}
- Recommended
- {:else if mirror.quality == 'notrecommended'}
- Not Recommended
- {/if}
-
- {#if mirror.notes}
- {mirror.notes}
- {/if}
-
- {#if !selected}
-
-
-
-
- {/if}
-
- {/each}
-
-
diff --git a/src/routes/mirrors/host/+page.svelte b/src/routes/mirrors/host/+page.svelte
deleted file mode 100644
index 43a00266..00000000
--- a/src/routes/mirrors/host/+page.svelte
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
Host a mirror
- Coming soon
-
diff --git a/src/routes/mirrors/mirrors.config.ts b/src/routes/mirrors/mirrors.config.ts
deleted file mode 100644
index 7e8e28f2..00000000
--- a/src/routes/mirrors/mirrors.config.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export type Mirror = {
- url: string;
- quality?: 'highlyrecommended' | 'recommended' | 'notrecommended';
- notes?: string;
-};
-
-export const mirrors: Mirror[] = [
- { url: 'https://edutools.ingo.au', quality: 'highlyrecommended' },
- { url: 'https://edutools.ingowolf.au', quality: 'recommended' },
- { url: 'https://educationaltools.github.io', quality: 'recommended' },
- { url: 'https://educationaltools.vercel.app' },
- { url: 'https://edutools.infinityfreeapp.com', quality: 'notrecommended' }
-];
diff --git a/src/routes/tools/ascii-art/+page.svelte b/src/routes/tools/ascii-art/+page.svelte
new file mode 100644
index 00000000..72aea053
--- /dev/null
+++ b/src/routes/tools/ascii-art/+page.svelte
@@ -0,0 +1,147 @@
+
+
+
+
+
+ ASCII Art Generator
+ Convert text into ASCII art
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if output}
+
+ {/if}
+
+
+ {#if output}
+
+ {/if}
+
+
+
Supported characters: A-Z, space, !, ?
+
Tip: Keep text short (max 20 characters) for best results
+
Note: ASCII art works best with monospace fonts
+
+
+
+
diff --git a/src/routes/tools/base64-converter/+page.svelte b/src/routes/tools/base64-converter/+page.svelte
new file mode 100644
index 00000000..2c8a9a5a
--- /dev/null
+++ b/src/routes/tools/base64-converter/+page.svelte
@@ -0,0 +1,107 @@
+
+
+
+
+
+ Base64 Encoder/Decoder
+ Encode text to Base64 or decode Base64 back to text
+
+
+
+
+ Encode
+ Decode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if outputText}
+
+ {/if}
+
+
+ {#if error}
+
+ {error}
+
+ {/if}
+
+ {#if outputText}
+
+
Result:
+
+
+ {/if}
+
+
+
diff --git a/src/routes/tools/calculator/+page.svelte b/src/routes/tools/calculator/+page.svelte
index a079395f..2fd58bd8 100644
--- a/src/routes/tools/calculator/+page.svelte
+++ b/src/routes/tools/calculator/+page.svelte
@@ -1,7 +1,7 @@
+
+
+
+
+ Color Picker & Palette
+ Pick colors and get values in different formats
+
+
+
+
+
+
+
+
+
Color Values
+
+
+
+
HEX:
+
+ {selectedColor.toUpperCase()}
+
+
+
+
+ {#if rgb()}
+
+
RGB:
+
+ rgb({rgb()?.r}, {rgb()?.g}, {rgb()?.b})
+
+
+
+ {/if}
+
+ {#if hsl()}
+
+
HSL:
+
+ hsl({hsl()?.h}, {hsl()?.s}%, {hsl()?.l}%)
+
+
+
+ {/if}
+
+
+
+
+
+ {#if complementaryColors().length > 0}
+
+
Complementary Color
+
+ {#each complementaryColors() as color}
+
+ {/each}
+
+
+ {/if}
+
+
+ {#if colorHistory.length > 0}
+
+
Recent Colors
+
+ {#each colorHistory as color}
+
+ {/each}
+
+
+ {/if}
+
+
+
diff --git a/src/routes/tools/converter/+page.svelte b/src/routes/tools/converter/+page.svelte
index 2fcee8c4..5bad41dd 100644
--- a/src/routes/tools/converter/+page.svelte
+++ b/src/routes/tools/converter/+page.svelte
@@ -1,4 +1,6 @@
+
+
+
+
+ Hash Generator
+ Generate MD5, SHA1, SHA256, and CRC32 hashes from text
+
+
+
+
+
+
+
+
+
+
+
+ {#if Object.keys(hashes).length > 0}
+
+
Generated Hashes:
+
+ {#each Object.entries(hashes) as [algorithm, hash]}
+
+
+ {algorithm}
+
+
+
+ {hash}
+
+
+ {/each}
+
+ {/if}
+
+ {#if inputText.trim()}
+
+
Input length: {inputText.length} characters
+
Input bytes: {new TextEncoder().encode(inputText).length} bytes
+
+ {/if}
+
+
+
Hash Information:
+
+ -
+ MD5: 128-bit hash, commonly used but not cryptographically secure
+
+ - SHA1: 160-bit hash, deprecated for cryptographic use
+ -
+ SHA256: 256-bit hash, part of SHA-2 family, cryptographically secure
+
+ -
+ CRC32: 32-bit checksum, used for error detection, not cryptographically
+ secure
+
+
+
+
+
+
diff --git a/src/routes/tools/json-formatter/+page.svelte b/src/routes/tools/json-formatter/+page.svelte
new file mode 100644
index 00000000..b2393183
--- /dev/null
+++ b/src/routes/tools/json-formatter/+page.svelte
@@ -0,0 +1,145 @@
+
+
+
+
+
+ JSON Formatter & Validator
+ Format, validate, and minify JSON data
+
+
+
+
+
+
Input JSON
+
+ {#if inputJson.trim()}
+
+ {isValid ? 'Valid' : 'Invalid'}
+
+ {/if}
+
+
+
+
+
+
+
Formatted Output
+
+
+
+
+ {#if error}
+
+ Error:
+ {error}
+
+ {/if}
+
+
+
+
+
+ {#if outputJson}
+
+ {/if}
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/tools/lorem-generator/+page.svelte b/src/routes/tools/lorem-generator/+page.svelte
new file mode 100644
index 00000000..ea549944
--- /dev/null
+++ b/src/routes/tools/lorem-generator/+page.svelte
@@ -0,0 +1,265 @@
+
+
+
+
+
+ Lorem Ipsum Generator
+ Generate placeholder text for your designs and layouts
+
+
+
+
+
+
+
+ {#if output}
+
+ {/if}
+
+
+ {#if output}
+
+
Generated Text:
+
+
+ {/if}
+
+
+
+ Tip: Lorem ipsum is placeholder text commonly used in the printing and typesetting
+ industry since the 1500s.
+
+ {#if output}
+
+ Generated:
+ {count} paragraph{count !== 1 ? 's' : ''}, {output.split(' ').length} words, {output
+ .split(/[.!?]+/)
+ .filter((s) => s.trim()).length} sentences
+
+ {/if}
+
+
+
+
diff --git a/src/routes/tools/markdown-preview/+page.svelte b/src/routes/tools/markdown-preview/+page.svelte
new file mode 100644
index 00000000..139e8bcd
--- /dev/null
+++ b/src/routes/tools/markdown-preview/+page.svelte
@@ -0,0 +1,314 @@
+
+
+
+
+
+ Markdown Preview
+ Write markdown and see the live preview
+
+
+
+
+
+ Split View
+ Markdown Only
+ Preview Only
+
+
+
+
+
+
+
+
+
+
+
+
Markdown Input:
+
+
+
+
Live Preview:
+
+ {@html htmlOutput}
+
+
+
+
+
+
+
+
+
+
+ {@html htmlOutput}
+
+
+
+
+
+
+
+
diff --git a/src/routes/tools/qr-code-generator/+page.svelte b/src/routes/tools/qr-code-generator/+page.svelte
new file mode 100644
index 00000000..7144343d
--- /dev/null
+++ b/src/routes/tools/qr-code-generator/+page.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+ QR Code Generator
+ Generate QR codes from text, URLs, or any other data
+
+
+
+
+
+
+
+
+
+
+
+ {#if qrCodeUrl}
+
+

+
+
+ {/if}
+
+
+
diff --git a/src/routes/tools/random-number-generator/+page.svelte b/src/routes/tools/random-number-generator/+page.svelte
index ce5f517f..9cda8ae0 100644
--- a/src/routes/tools/random-number-generator/+page.svelte
+++ b/src/routes/tools/random-number-generator/+page.svelte
@@ -1,9 +1,9 @@
+
+
+
+
+ Regular Expression Tester
+ Test and debug regular expressions with live matching
+
+
+
+
+
+
+
+
+ {isValid ? 'Valid' : 'Invalid'}
+
+ {#if pattern && flags}
+ /{pattern}/{flags}
+ {/if}
+ {#if matches.length > 0}
+ {matches.length} match{matches.length !== 1 ? 'es' : ''}
+ {/if}
+
+
+ {#if error}
+
+ Error:
+ {error}
+
+ {/if}
+
+
+
+
Common Patterns:
+
+ {#each commonPatterns as commonPattern}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+ {#if testString}
+
+
Text with Matches Highlighted:
+
+ {@html getHighlightedText()}
+
+
+ {/if}
+
+
+ {#if matches.length > 0}
+
+
Matches ({matches.length}):
+
+ {#each matches as match, index}
+
+
+ {match[0]}
+
+ Position: {match.index}
+
+
+ {#if match.length > 1}
+
+ Groups: {match
+ .slice(1)
+ .map((group, i) => `$${i + 1}: "${group}"`)
+ .join(', ')}
+
+ {/if}
+
+ {/each}
+
+
+ {/if}
+
+
+
+
+
+ {#if pattern}
+
+ {/if}
+ {#if matches.length > 0}
+
+ {/if}
+
+
+
+
+
+ Common flags: g (global), i (case-insensitive), m (multiline), s (dotall)
+
+
Tip: Use capture groups () to extract parts of matches
+
+
+
+
diff --git a/src/routes/tools/url-tools/+page.svelte b/src/routes/tools/url-tools/+page.svelte
new file mode 100644
index 00000000..67e59aff
--- /dev/null
+++ b/src/routes/tools/url-tools/+page.svelte
@@ -0,0 +1,202 @@
+
+
+
+
+
+ URL Tools
+ Shorten URLs and analyze URL components
+
+
+
+
+ Shorten
+ Analyze
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if urlAnalysis()}
+
+
URL Components:
+
+
+ Protocol:
+ {urlAnalysis()?.protocol}
+
+
+ Hostname:
+ {urlAnalysis()?.hostname}
+
+ {#if urlAnalysis()?.port}
+
+ Port:
+ {urlAnalysis()?.port}
+
+ {/if}
+ {#if urlAnalysis()?.pathname && urlAnalysis()?.pathname !== '/'}
+
+ Path:
+ {urlAnalysis()?.pathname}
+
+ {/if}
+ {#if urlAnalysis()?.search}
+
+ Query:
+ {urlAnalysis()?.search}
+
+ {/if}
+ {#if urlAnalysis()?.hash}
+
+ Fragment:
+ {urlAnalysis()?.hash}
+
+ {/if}
+
+
+ {/if}
+
+
+
+
+
+ {#if outputUrl}
+
+ {/if}
+
+
+ {#if error}
+
+ {error}
+
+ {/if}
+
+ {#if outputUrl && outputUrl !== inputUrl}
+
+ {/if}
+
+
+