swiss-army-web-tools / index.html
haluk2525's picture
create me an LLm
13fd935 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swiss Army Web Tools 🛠️</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/qrcode.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
<style>
.tool-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.tool-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.tab-btn.active {
border-bottom-color: #22d3ee;
color: white;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
#qrCodeCanvas {
transition: all 0.3s ease;
}
#bgRemoverPreview {
max-width: 100%;
max-height: 320px;
display: none;
border-radius: 1rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255,255,255,0.1);
}
::selection {
background: rgba(34, 211, 238, 0.2);
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(31, 41, 55, 0.5);
}
::-webkit-scrollbar-thumb {
background: rgba(74, 222, 128, 0.5);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(74, 222, 128, 0.7);
}
</style>
</head>
<body class="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-12">
<h1 class="text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 to-blue-500 mb-3">Swiss Army Web Tools</h1>
<p class="text-xl text-gray-300">All-in-one utility toolkit for your daily web needs</p>
</header>
<div class="bg-gray-800 rounded-2xl shadow-xl overflow-hidden max-w-5xl mx-auto border border-gray-700 backdrop-blur-sm bg-opacity-50">
<div class="flex overflow-x-auto">
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="qr">
<div class="flex items-center gap-2">
<i data-feather="qr-code" class="w-5 h-5"></i>
QR Generator
</div>
</button>
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="counter">
<div class="flex items-center gap-2">
<i data-feather="type" class="w-5 h-5"></i>
Text Counter
</div>
</button>
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="shortener">
<div class="flex items-center gap-2">
<i data-feather="link-2" class="w-5 h-5"></i>
Link Shortener
</div>
</button>
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="bgremover">
<div class="flex items-center gap-2">
<i data-feather="image" class="w-5 h-5"></i>
BG Remover
</div>
</button>
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="password">
<div class="flex items-center gap-2">
<i data-feather="key" class="w-5 h-5"></i>
Password Gen
</div>
</button>
<button class="tab-btn px-6 py-4 font-medium text-gray-300 hover:text-white border-b-2 border-transparent hover:border-cyan-400 transition-all" data-tab="llm">
<div class="flex items-center gap-2">
<i data-feather="message-square" class="w-5 h-5"></i>
AI Chat
</div>
</button>
</div>
<div class="p-8">
<!-- QR Code Generator -->
<div id="qr" class="tab-content active">
<h2 class="text-3xl font-semibold text-white mb-6 flex items-center gap-3">
<i data-feather="qr-code" class="w-8 h-8 text-cyan-400"></i>
QR Code Generator
</h2>
<div class="flex flex-col md:flex-row gap-8">
<div class="flex-1">
<div class="mb-4">
<label for="qrText" class="block text-gray-300 mb-3">Enter text or URL</label>
<textarea id="qrText" rows="3" class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-xl focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-transparent text-white placeholder-gray-400" placeholder="Enter text or URL to generate QR code"></textarea>
</div>
<div class="flex gap-4">
<button id="generateQR" class="bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-600 hover:to-blue-700 text-white px-6 py-3 rounded-xl flex items-center gap-2 shadow-lg hover:shadow-cyan-500/20 transition-all">
<i data-feather="rotate-cw"></i> Generate
</button>
<button id="downloadQR" class="bg-gradient-to-r from-emerald-500 to-green-600 hover:from-emerald-600 hover:to-green-700 text-white px-6 py-3 rounded-xl flex items-center gap-2 shadow-lg hover:shadow-emerald-500/20 transition-all" disabled>
<i data-feather="download"></i> Download
</button>
</div>
</div>
<div class="flex-1 flex flex-col items-center justify-center">
<div id="qrCodeContainer" class="bg-gray-700 p-6 rounded-2xl border border-gray-600 mb-4 flex items-center justify-center">
<div id="qrCodeCanvas" class="w-56 h-56 flex flex-col items-center justify-center text-gray-400">
<i data-feather="image" class="w-16 h-16 mb-2 opacity-50"></i>
<p class="text-center text-gray-400">QR Code will appear here</p>
</div>
</div>
<p class="text-sm text-gray-400 flex items-center justify-center gap-1">
<i data-feather="smartphone" class="w-4 h-4"></i>
Scan the QR code with your phone
</p>
</div>
</div>
</div>
<!-- LLM Chat -->
<div id="llm" class="tab-content">
<div class="text-center py-12">
<h2 class="text-3xl font-semibold text-white mb-4">AI Chat Assistant</h2>
<p class="text-gray-300 mb-6">This tool opens in a new page for better experience.</p>
<a href="llm.html" class="bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-600 hover:to-blue-700 text-white px-6 py-3 rounded-xl inline-flex items-center gap-2 shadow-lg hover:shadow-cyan-500/20 transition-all">
<i data-feather="message-square"></i> Open Chat
</a>
</div>
</div>
<!-- Password Generator -->
<div id="password" class="tab-content">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Password Generator</h2>
<div class="mb-4">
<div class="flex items-center gap-4 mb-4">
<div class="flex-1">
<label for="passwordLength" class="block text-gray-700 mb-2">Length (8-32)</label>
<input type="range" id="passwordLength" min="8" max="32" value="12" class="w-full">
<div class="flex justify-between">
<span>8</span>
<span id="lengthValue">12</span>
<span>32</span>
</div>
</div>
<div class="flex-1 flex justify-center items-center">
<button id="generatePassword" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center gap-2">
<i data-feather="refresh-cw"></i> Generate
</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div class="bg-white p-4 rounded-lg shadow">
<label class="flex items-center gap-2">
<input type="checkbox" id="uppercase" checked class="rounded text-indigo-600">
<span>Uppercase (A-Z)</span>
</label>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<label class="flex items-center gap-2">
<input type="checkbox" id="lowercase" checked class="rounded text-indigo-600">
<span>Lowercase (a-z)</span>
</label>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<label class="flex items-center gap-2">
<input type="checkbox" id="numbers" checked class="rounded text-indigo-600">
<span>Numbers (0-9)</span>
</label>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<label class="flex items-center gap-2">
<input type="checkbox" id="symbols" checked class="rounded text-indigo-600">
<span>Symbols (!@#$%)</span>
</label>
</div>
</div>
<div class="mb-4">
<label for="generatedPassword" class="block text-gray-700 mb-2">Generated Password</label>
<div class="flex gap-2">
<input type="text" id="generatedPassword" class="flex-1 px-3 py-2 border border-gray-300 rounded-lg bg-white" readonly>
<button id="copyPassword" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-2 rounded-lg flex items-center gap-1">
<i data-feather="copy"></i>
</button>
</div>
</div>
<div class="bg-gray-100 p-4 rounded-lg">
<h3 class="text-lg font-medium text-gray-800 mb-2">Password Strength</h3>
<div class="flex items-center gap-2 mb-2">
<div id="strengthMeter" class="h-2 flex-1 bg-gray-300 rounded-full overflow-hidden">
<div class="h-full bg-red-500 w-0" id="strengthBar"></div>
</div>
<span id="strengthText" class="text-sm font-medium">Weak</span>
</div>
<p class="text-sm text-gray-600" id="strengthFeedback">Use a longer password with more character types</p>
</div>
</div>
</div>
<!-- Text Counter -->
<div id="counter" class="tab-content">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Text & Character Counter</h2>
<div class="mb-4">
<label for="textInput" class="block text-gray-700 mb-2">Enter your text</label>
<textarea id="textInput" rows="6" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Type or paste your text here..."></textarea>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 bg-gray-100 p-4 rounded-lg">
<div class="bg-white p-4 rounded-lg shadow text-center">
<p class="text-sm text-gray-500">Characters</p>
<p id="charCount" class="text-3xl font-bold text-indigo-600">0</p>
</div>
<div class="bg-white p-4 rounded-lg shadow text-center">
<p class="text-sm text-gray-500">Words</p>
<p id="wordCount" class="text-3xl font-bold text-indigo-600">0</p>
</div>
<div class="bg-white p-4 rounded-lg shadow text-center">
<p class="text-sm text-gray-500">Sentences</p>
<p id="sentenceCount" class="text-3xl font-bold text-indigo-600">0</p>
</div>
</div>
</div>
<!-- Link Shortener -->
<div id="shortener" class="tab-content">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Link Shortener</h2>
<div class="mb-4">
<label for="longUrl" class="block text-gray-700 mb-2">Enter long URL</label>
<input type="url" id="longUrl" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="https://example.com/very-long-url...">
<div class="mt-2">
<label for="customText" class="block text-gray-700 mb-2">Custom text (optional)</label>
<input type="text" id="customText" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="my-custom-text" maxlength="30">
<p class="text-xs text-gray-500 mt-1">Max 30 characters, letters and numbers only</p>
</div>
</div>
<button id="shortenBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center gap-2 mb-4 hovered-element">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-scissors"><circle cx="6" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><line x1="20" y1="4" x2="8.12" y2="15.88"></line><line x1="14.47" y1="14.48" x2="20" y2="20"></line><line x1="8.12" y1="8.12" x2="12" y2="12"></line></svg> Shorten URL
</button>
<div id="shortUrlResult" class="hidden bg-gray-100 p-4 rounded-lg">
<label class="block text-gray-700 mb-2">Short URL</label>
<div class="flex gap-2">
<input type="text" id="shortUrl" class="flex-1 px-3 py-2 border border-gray-300 rounded-lg bg-white" readonly>
<button id="copyShortUrl" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-3 py-2 rounded-lg flex items-center gap-1">
<i data-feather="copy"></i>
</button>
</div>
</div>
</div>
<!-- Background Remover -->
<div id="bgremover" class="tab-content">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Background Remover</h2>
<div class="mb-4">
<label for="imageUpload" class="block text-gray-700 mb-2">Upload image</label>
<div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center cursor-pointer hover:border-indigo-400 transition" id="dropZone">
<input type="file" id="imageUpload" accept="image/*" class="hidden">
<div class="flex flex-col items-center justify-center">
<i data-feather="upload" class="w-10 h-10 text-gray-400 mb-2"></i>
<p class="text-gray-500">Drag & drop an image or click to browse</p>
<p class="text-sm text-gray-400 mt-1">Supports JPG, PNG (Max 5MB)</p>
</div>
</div>
</div>
<div class="flex justify-center mb-4">
<img id="bgRemoverPreview" alt="Preview" class="rounded-lg">
</div>
<button id="removeBgBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center gap-2 mb-4 hidden">
<i data-feather="edit-3"></i> Remove Background
</button>
<div id="bgRemoverResult" class="hidden">
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-1">
<p class="text-gray-700 mb-2">Original</p>
<img id="originalImage" src="" alt="Original" class="w-full rounded-lg border border-gray-200">
</div>
<div class="flex-1">
<p class="text-gray-700 mb-2">Background Removed</p>
<img id="processedImage" src="" alt="Processed" class="w-full rounded-lg border border-gray-200">
</div>
</div>
<div class="mt-4 flex justify-center">
<button id="downloadProcessed" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg flex items-center gap-2">
<i data-feather="download"></i> Download Result
</button>
</div>
</div>
</div>
</div>
</div>
<footer class="mt-16 text-center text-gray-400 text-sm">
<p class="flex items-center justify-center gap-1">
Made with <i data-feather="heart" class="inline text-pink-500 w-4 h-4"></i> for the web
</p>
<p class="mt-2 text-xs text-gray-500">
v1.0.0 · Swiss Army Web Tools
</p>
</footer>
</div>
<script>
// Tab switching
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', function() {
// Remove active classes from all buttons
document.querySelectorAll('.tab-btn').forEach(t => {
t.classList.remove('border-indigo-600', 'text-indigo-600');
t.classList.add('text-gray-500');
});
// Add active classes to clicked button
this.classList.add('border-indigo-600', 'text-indigo-600');
this.classList.remove('text-gray-500');
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
// Show the selected tab content
const tabId = this.getAttribute('data-tab');
document.getElementById(tabId).classList.add('active');
});
});
// QR Code Generator
document.getElementById('generateQR').addEventListener('click', () => {
const text = document.getElementById('qrText').value.trim();
if (!text) {
alert('Please enter some text or URL');
return;
}
try {
const qr = qrcode(0, 'L');
qr.addData(text);
qr.make();
const qrContainer = document.getElementById('qrCodeCanvas');
qrContainer.innerHTML = qr.createImgTag(4, 0);
qrContainer.querySelector('img').style.maxWidth = '100%';
document.getElementById('downloadQR').disabled = false;
} catch (e) {
console.error('QR generation error:', e);
alert('Failed to generate QR code. Please try different text.');
}
});
document.getElementById('downloadQR').addEventListener('click', () => {
const qrImg = document.querySelector('#qrCodeCanvas img');
if (!qrImg) {
alert('Please generate a QR code first');
return;
}
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = qrImg.src;
link.click();
});
// Text Counter
document.getElementById('textInput').addEventListener('input', () => {
const text = document.getElementById('textInput').value;
// Character count
document.getElementById('charCount').textContent = text.length;
// Word count
const words = text.trim() ? text.trim().split(/\s+/).length : 0;
document.getElementById('wordCount').textContent = words;
// Sentence count (very basic)
const sentences = text.trim() ? text.split(/[.!?]+/).filter(s => s.trim().length > 0).length : 0;
document.getElementById('sentenceCount').textContent = sentences;
});
// Link Shortener using is.gd API as fallback
document.getElementById('shortenBtn').addEventListener('click', async () => {
const longUrl = document.getElementById('longUrl').value.trim();
if (!longUrl) {
alert('Please enter a URL');
return;
}
try {
// First try is.gd
const customText = document.getElementById('customText').value.trim();
let apiUrl = `https://is.gd/create.php?format=json&url=${encodeURIComponent(longUrl)}`;
if (customText) {
if (!/^[a-zA-Z0-9-]+$/.test(customText)) {
alert('Custom text can only contain letters, numbers and hyphens');
return;
}
apiUrl += `&shorturl=${encodeURIComponent(customText)}`;
}
const isgdResponse = await fetch(apiUrl);
if (isgdResponse.ok) {
const isgdData = await isgdResponse.json();
if (isgdData.shorturl) {
document.getElementById('shortUrl').value = isgdData.shorturl;
document.getElementById('shortUrlResult').classList.remove('hidden');
return;
}
}
// Fallback to v.gd
const vgdResponse = await fetch(apiUrl.replace('is.gd', 'v.gd'));
const vgdData = await vgdResponse.json();
if (vgdResponse.ok && vgdData.shorturl) {
document.getElementById('shortUrl').value = vgdData.shorturl;
} else {
throw new Error('Failed to shorten URL with both services');
}
document.getElementById('shortUrlResult').classList.remove('hidden');
} catch (error) {
console.error('Error shortening URL:', error);
alert('Failed to shorten URL. Please try again later or use a different URL.');
}
});
document.getElementById('copyShortUrl').addEventListener('click', () => {
const shortUrl = document.getElementById('shortUrl');
shortUrl.select();
document.execCommand('copy');
const copyBtn = document.getElementById('copyShortUrl');
copyBtn.innerHTML = '<i data-feather="check"></i>';
feather.replace();
setTimeout(() => {
copyBtn.innerHTML = '<i data-feather="copy"></i>';
feather.replace();
}, 2000);
});
// Background Remover using Remove.bg API (demo mode with mock response)
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('imageUpload');
let currentFile = null;
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('border-indigo-400');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('border-indigo-400');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('border-indigo-400');
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
handleImageUpload(e.dataTransfer.files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleImageUpload(e.target.files[0]);
}
});
function handleImageUpload(file) {
currentFile = file;
if (!file.type.match('image.*')) {
alert('Please upload an image file');
return;
}
if (file.size > 5 * 1024 * 1024) {
alert('File size should be less than 5MB');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const preview = document.getElementById('bgRemoverPreview');
preview.src = e.target.result;
preview.style.display = 'block';
document.getElementById('removeBgBtn').classList.remove('hidden');
};
reader.readAsDataURL(file);
}
document.getElementById('removeBgBtn').addEventListener('click', async () => {
const preview = document.getElementById('bgRemoverPreview');
const originalImg = document.getElementById('originalImage');
const processedImg = document.getElementById('processedImage');
try {
// Show loading state
const btn = document.getElementById('removeBgBtn');
const originalText = btn.innerHTML;
btn.innerHTML = '<i data-feather="loader"></i> Processing...';
feather.replace();
// In a real implementation, you would call the Remove.bg API here
// For demo purposes, we'll simulate a successful response after 2 seconds
setTimeout(() => {
originalImg.src = preview.src;
processedImg.src = preview.src.replace(/^data:image\/[^;]+/, 'data:image/png') + '&processed=true';
document.getElementById('bgRemoverResult').classList.remove('hidden');
btn.innerHTML = originalText;
feather.replace();
}, 2000);
} catch (error) {
console.error('Error removing background:', error);
alert('Failed to process image. Please try again later.');
}
});
document.getElementById('downloadProcessed').addEventListener('click', () => {
const processedImg = document.getElementById('processedImage');
const link = document.createElement('a');
link.download = 'background_removed.png';
link.href = processedImg.src;
link.click();
});
// Password Generator - improved with better character selection
document.getElementById('passwordLength').addEventListener('input', function() {
const length = this.value;
document.getElementById('lengthValue').textContent = length;
// Auto-adjust complexity for longer passwords
if (length >= 16) {
document.getElementById('symbols').checked = true;
document.getElementById('uppercase').checked = true;
document.getElementById('numbers').checked = true;
}
});
document.getElementById('generatePassword').addEventListener('click', function() {
const length = document.getElementById('passwordLength').value;
const uppercase = document.getElementById('uppercase').checked;
const lowercase = document.getElementById('lowercase').checked;
const numbers = document.getElementById('numbers').checked;
const symbols = document.getElementById('symbols').checked;
if (!uppercase && !lowercase && !numbers && !symbols) {
alert('Please select at least one character type');
return;
}
// Build character sets separately to ensure at least one of each selected type
const charSets = [];
if (uppercase) charSets.push('ABCDEFGHIJKLMNOPQRSTUVWXYZ');
if (lowercase) charSets.push('abcdefghijklmnopqrstuvwxyz');
if (numbers) charSets.push('0123456789');
if (symbols) charSets.push('!@#$%^&*()_+-=[]{}|;:,.<>?');
// Ensure at least one character from each selected set
let password = '';
charSets.forEach(set => {
password += set.charAt(Math.floor(Math.random() * set.length));
});
// Fill the rest of the password
const allChars = charSets.join('');
for (let i = password.length; i < length; i++) {
password += allChars.charAt(Math.floor(Math.random() * allChars.length));
}
// Shuffle the password to mix the guaranteed characters
password = password.split('').sort(() => 0.5 - Math.random()).join('');
document.getElementById('generatedPassword').value = password;
updatePasswordStrength(password);
});
document.getElementById('copyPassword').addEventListener('click', function() {
const password = document.getElementById('generatedPassword');
password.select();
document.execCommand('copy');
const copyBtn = document.getElementById('copyPassword');
copyBtn.innerHTML = '<i data-feather="check"></i>';
feather.replace();
setTimeout(() => {
copyBtn.innerHTML = '<i data-feather="copy"></i>';
feather.replace();
}, 2000);
});
function updatePasswordStrength(password) {
let strength = 0;
const length = password.length;
// Length score (max 50)
strength += Math.min(length * 2, 50);
// Character variety (max 50)
const hasUpper = /[A-Z]/.test(password);
const hasLower = /[a-z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSymbol = /[^A-Za-z0-9]/.test(password);
const uniqueChars = new Set(password.split('')).size;
strength += (uniqueChars / length) * 20; // Uniqueness bonus
let typeCount = 0;
if (hasUpper) typeCount++;
if (hasLower) typeCount++;
if (hasNumber) typeCount++;
if (hasSymbol) typeCount++;
strength += typeCount * 10;
// Cap at 100
strength = Math.min(strength, 100);
// Update UI
const strengthBar = document.getElementById('strengthBar');
const strengthText = document.getElementById('strengthText');
const strengthFeedback = document.getElementById('strengthFeedback');
let color, text, feedback;
if (strength < 40) {
color = 'bg-red-500';
text = 'Weak';
feedback = 'Consider using a longer password (12+ chars) with more character types';
} else if (strength < 70) {
color = 'bg-yellow-500';
text = 'Moderate';
feedback = 'Good password. Adding symbols or length would improve it';
} else if (strength < 85) {
color = 'bg-blue-500';
text = 'Strong';
feedback = 'Great password!';
} else {
color = 'bg-green-500';
text = 'Very Strong';
feedback = 'Excellent password! Very secure!';
}
strengthBar.className = `h-full ${color}`;
strengthBar.style.width = `${strength}%`;
strengthText.textContent = text;
strengthFeedback.textContent = feedback;
}
// QR Code - Auto-generate when tab is selected
document.querySelector('.tab-btn[data-tab="qr"]').addEventListener('click', () => {
const qrText = document.getElementById('qrText').value.trim();
if (qrText && !document.querySelector('#qrCodeCanvas img')) {
document.getElementById('generateQR').click();
}
});
// Password - Auto-generate first password on tab select
document.querySelector('.tab-btn[data-tab="password"]').addEventListener('click', () => {
if (!document.getElementById('generatedPassword').value) {
document.getElementById('generatePassword').click();
}
});
// Initialize feather icons
feather.replace();
// Add service worker for offline functionality
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').catch(err => {
console.log('ServiceWorker registration failed: ', err);
});
});
}
</script>
</body>
</html>