MVP fertig

This commit is contained in:
knuthtimo-lab 2025-09-04 14:55:04 +02:00
parent bccaefedb3
commit fc62302f97
11 changed files with 515 additions and 223 deletions

View File

@ -32,11 +32,6 @@ export default function ClientSidePage() {
title: "Zero Data Storage",
description: "We don't store any passwords, user data, or personal information. Everything is processed locally and immediately discarded."
},
{
icon: Shield,
title: "Open Source Verification",
description: "All code is publicly available and auditable. You can verify our security claims by reviewing the source code."
}
]
const technicalDetails = [
@ -98,7 +93,7 @@ export default function ClientSidePage() {
className="flex items-center text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<ArrowLeft className="h-5 w-5 mr-2" />
Zurück zu PassMaster
Back to PassMaster
</Link>
</div>
</div>
@ -118,10 +113,10 @@ export default function ClientSidePage() {
</div>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Client-Side Sicherheit
Client-Side Security
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
Maximale Sicherheit durch lokale Verarbeitung. Ihre Passwörter werden ausschließlich in Ihrem Browser generiert und verlassen niemals Ihr Gerät.
Maximum security through local processing. Your passwords are generated exclusively in your browser and never leave your device.
</p>
</motion.div>
@ -135,14 +130,14 @@ export default function ClientSidePage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Sicherheits-Features
Security Features
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Jeder Aspekt von PassMaster ist darauf ausgelegt, Ihre Sicherheit zu maximieren.
Every aspect of PassMaster is designed to maximize your security.
</p>
</motion.div>
<div className="grid md:grid-cols-2 gap-8">
<div className="grid md:grid-cols-3 gap-8">
{securityFeatures.map((feature, index) => (
<motion.div
key={feature.title}
@ -182,10 +177,10 @@ export default function ClientSidePage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Warum Client-Side Sicherheit?
Why Client-Side Security?
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Die Vorteile der lokalen Passwort-Generierung.
The benefits of local password generation.
</p>
</motion.div>
@ -225,10 +220,10 @@ export default function ClientSidePage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Technische Details
Technical Details
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Wie PassMaster Ihre Sicherheit gewährleistet.
How PassMaster ensures your security.
</p>
</motion.div>
@ -270,28 +265,24 @@ export default function ClientSidePage() {
<div className="flex items-center mb-6">
<FileText className="h-8 w-8 text-green-600 mr-3" />
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
Technische Implementierung
Technical Implementation
</h2>
</div>
<div className="prose dark:prose-invert max-w-none">
<h3 className="text-lg font-semibold mb-4">Wie PassMaster funktioniert</h3>
<h3 className="text-lg font-semibold mb-4">How PassMaster Works</h3>
<ul className="space-y-3 text-gray-600 dark:text-gray-300">
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>Lokale Verarbeitung:</strong> Alle Passwort-Generierung erfolgt in Ihrem Browser mit JavaScript</span>
<span><strong>Local Processing:</strong> All password generation happens in your browser using JavaScript</span>
</li>
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>Keine Netzwerk-Anfragen:</strong> Die App funktioniert nach dem ersten Laden vollständig offline</span>
<span><strong>No Network Requests:</strong> The app works completely offline after initial load</span>
</li>
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>Open Source:</strong> Der gesamte Code ist öffentlich auf GitHub verfügbar zur Überprüfung</span>
</li>
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>Keine Abhängigkeiten:</strong> Wir verwenden keine externen Services oder Drittanbieter-Bibliotheken</span>
<span><strong>No Dependencies:</strong> We don't use external services or third-party libraries</span>
</li>
</ul>
</div>
@ -308,26 +299,18 @@ export default function ClientSidePage() {
className="bg-green-50 dark:bg-green-900/20 rounded-lg p-8"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Fragen zur Sicherheit?
Questions About Security?
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-6">
Wir sind verpflichtet zu Transparenz. Wenn Sie Fragen zu unseren Sicherheitspraktiken haben,
überprüfen Sie unseren Quellcode oder kontaktieren Sie uns.
We are committed to transparency. If you have questions about our security practices,
please contact us.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<a
href="https://github.com/your-repo/passmaster"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-green-600 hover:bg-green-700 transition-colors"
>
Quellcode ansehen
</a>
<div className="flex justify-center">
<Link
href="/"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 dark:border-gray-600 text-base font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
Zum Generator
Back to Generator
</Link>
</div>
</motion.div>

View File

@ -17,88 +17,88 @@ export default function ExcludeSimilarPage() {
const readabilityFeatures = [
{
icon: Eye,
title: "Ähnliche Zeichen ausschließen",
description: "Verwirrende Zeichen wie 0/O, 1/l/I werden automatisch ausgeschlossen, um Lesbarkeit zu verbessern."
title: "Exclude Similar Characters",
description: "Confusing characters like 0/O, 1/l/I are automatically excluded to improve readability."
},
{
icon: BookOpen,
title: "Bessere Lesbarkeit",
description: "Passwörter sind leichter zu lesen und zu tippen, ohne die Sicherheit zu beeinträchtigen."
title: "Better Readability",
description: "Passwords are easier to read and type without compromising security."
},
{
icon: Target,
title: "Weniger Fehler",
description: "Reduziert Tippfehler beim manuellen Eingeben von Passwörtern erheblich."
title: "Fewer Errors",
description: "Significantly reduces typing errors when manually entering passwords."
},
{
icon: Users,
title: "Benutzerfreundlich",
description: "Besonders nützlich für ältere Benutzer oder bei der Eingabe auf mobilen Geräten."
title: "User-Friendly",
description: "Especially useful for older users or when typing on mobile devices."
}
]
const excludedCharacters = [
{
category: "Zahlen und Buchstaben",
characters: ["0 (Null)", "O (Großes O)", "1 (Eins)", "l (kleines L)", "I (Großes i)"],
reason: "Diese Zeichen sehen in vielen Schriftarten identisch aus"
category: "Numbers and Letters",
characters: ["0 (Zero)", "O (Capital O)", "1 (One)", "l (lowercase L)", "I (Capital I)"],
reason: "These characters look identical in many fonts"
},
{
category: "Sonderzeichen",
characters: ["| (Pipe)", "` (Backtick)", "' (Apostroph)", "\" (Anführungszeichen)"],
reason: "Können in verschiedenen Kontexten verwirrend sein"
category: "Special Characters",
characters: ["| (Pipe)", "` (Backtick)", "' (Apostrophe)", "\" (Quotation marks)"],
reason: "Can be confusing in different contexts"
},
{
category: "Leerzeichen",
characters: [" (Leerzeichen)", " (Mehrfache Leerzeichen)"],
reason: "Können beim Kopieren/Einfügen Probleme verursachen"
category: "Spaces",
characters: [" (Space)", " (Multiple spaces)"],
reason: "Can cause problems when copying/pasting"
}
]
const benefits = [
{
icon: Zap,
title: "Schnellere Eingabe",
description: "Weniger Verwirrung beim manuellen Tippen von Passwörtern."
title: "Faster Input",
description: "Less confusion when manually typing passwords."
},
{
icon: CheckCircle,
title: "Weniger Fehler",
description: "Reduziert Tippfehler und damit verbundene Frustration."
title: "Fewer Errors",
description: "Reduces typing errors and associated frustration."
},
{
icon: Eye,
title: "Bessere UX",
description: "Verbessert die Benutzererfahrung ohne Sicherheitsverlust."
title: "Better UX",
description: "Improves user experience without security loss."
}
]
const securityImpact = [
{
title: "Sicherheit bleibt hoch",
title: "Security Remains High",
items: [
"Entropie wird nur minimal reduziert",
"Noch immer über 80 Zeichen verfügbar",
"Kryptographisch sichere Generierung",
"Ausreichend für alle praktischen Zwecke"
"Entropy is only minimally reduced",
"Still over 80 characters available",
"Cryptographically secure generation",
"Sufficient for all practical purposes"
]
},
{
title: "Praktische Vorteile",
title: "Practical Benefits",
items: [
"Einfachere manuelle Eingabe",
"Weniger Support-Anfragen",
"Bessere Benutzerakzeptanz",
"Reduzierte Fehlerrate"
"Easier manual input",
"Fewer support requests",
"Better user acceptance",
"Reduced error rate"
]
},
{
title: "Empfohlene Verwendung",
title: "Recommended Usage",
items: [
"Für manuell eingegebene Passwörter",
"Bei älteren Benutzern",
"Auf mobilen Geräten",
"In Umgebungen mit schlechter Sichtbarkeit"
"For manually entered passwords",
"For older users",
"On mobile devices",
"In environments with poor visibility"
]
}
]
@ -114,7 +114,7 @@ export default function ExcludeSimilarPage() {
className="flex items-center text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<ArrowLeft className="h-5 w-5 mr-2" />
Zurück zu PassMaster
Back to PassMaster
</Link>
</div>
</div>
@ -134,11 +134,11 @@ export default function ExcludeSimilarPage() {
</div>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Lesbarkeit & Benutzerfreundlichkeit
Readability & User-Friendliness
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
Verbessern Sie die Lesbarkeit Ihrer Passwörter ohne Sicherheit zu opfern.
Ähnliche Zeichen werden automatisch ausgeschlossen.
Improve the readability of your passwords without sacrificing security.
Similar characters are automatically excluded.
</p>
</motion.div>
@ -152,10 +152,10 @@ export default function ExcludeSimilarPage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Lesbarkeits-Features
Readability Features
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Wie PassMaster die Benutzerfreundlichkeit verbessert.
How PassMaster improves user-friendliness.
</p>
</motion.div>
@ -199,10 +199,10 @@ export default function ExcludeSimilarPage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Ausgeschlossene Zeichen
Excluded Characters
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Diese Zeichen werden automatisch ausgeschlossen, um Verwirrung zu vermeiden.
These characters are automatically excluded to avoid confusion.
</p>
</motion.div>
@ -245,10 +245,10 @@ export default function ExcludeSimilarPage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Vorteile der Lesbarkeit
Benefits of Readability
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Warum lesbare Passwörter wichtig sind.
Why readable passwords are important.
</p>
</motion.div>
@ -288,10 +288,10 @@ export default function ExcludeSimilarPage() {
className="text-center mb-12"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Sicherheit vs. Lesbarkeit
Security vs. Readability
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300">
Wie wir das perfekte Gleichgewicht finden.
How we find the perfect balance.
</p>
</motion.div>
@ -331,20 +331,20 @@ export default function ExcludeSimilarPage() {
className="bg-white dark:bg-gray-800 rounded-lg p-8 shadow-sm border border-gray-200 dark:border-gray-700"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6 text-center">
Beispiel-Vergleich
Example Comparison
</h2>
<div className="grid md:grid-cols-2 gap-8">
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4 flex items-center">
<XCircle className="h-5 w-5 text-red-500 mr-2" />
Ohne Lesbarkeits-Filter
Without Readability Filter
</h3>
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg font-mono text-sm">
<div className="text-red-600 dark:text-red-400 mb-2">Schwer lesbar:</div>
<div className="text-red-600 dark:text-red-400 mb-2">Hard to read:</div>
<div>K9mP0lI|nQ2v</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-2">
Verwirrende Zeichen: 0, l, I, |
Confusing characters: 0, l, I, |
</div>
</div>
</div>
@ -352,13 +352,13 @@ export default function ExcludeSimilarPage() {
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4 flex items-center">
<CheckCircle className="h-5 w-5 text-green-500 mr-2" />
Mit Lesbarkeits-Filter
With Readability Filter
</h3>
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg font-mono text-sm">
<div className="text-green-600 dark:text-green-400 mb-2">Leicht lesbar:</div>
<div className="text-green-600 dark:text-green-400 mb-2">Easy to read:</div>
<div>K9mP3nQ2vX7w</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-2">
Keine verwirrenden Zeichen
No confusing characters
</div>
</div>
</div>
@ -376,24 +376,24 @@ export default function ExcludeSimilarPage() {
className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-8"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Bereit für bessere Lesbarkeit?
Ready for Better Readability?
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-6">
Aktivieren Sie die Lesbarkeits-Option in PassMaster und generieren Sie
benutzerfreundliche, aber dennoch sichere Passwörter.
Enable the readability option in PassMaster and generate
user-friendly, yet secure passwords.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
href="/"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 transition-colors"
>
Jetzt ausprobieren
Try Now
</Link>
<Link
href="/client-side"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 dark:border-gray-600 text-base font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
Sicherheit erfahren
Learn About Security
</Link>
</div>
</motion.div>

346
app/offline/page.tsx Normal file
View File

@ -0,0 +1,346 @@
"use client"
import { useState, useEffect } from 'react'
import { motion } from 'framer-motion'
import {
Wifi,
WifiOff,
Download,
Shield,
CheckCircle,
AlertCircle,
Smartphone,
Monitor
} from 'lucide-react'
export default function OfflinePage() {
const [isOnline, setIsOnline] = useState(true)
const [isInstallable, setIsInstallable] = useState(false)
const [deferredPrompt, setDeferredPrompt] = useState<any>(null)
useEffect(() => {
setIsOnline(navigator.onLine)
const handleOnline = () => setIsOnline(true)
const handleOffline = () => setIsOnline(false)
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
// PWA install prompt
const handleBeforeInstallPrompt = (e: any) => {
e.preventDefault()
setDeferredPrompt(e)
setIsInstallable(true)
}
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
}
}, [])
const handleInstallClick = async () => {
if (deferredPrompt) {
deferredPrompt.prompt()
const { outcome } = await deferredPrompt.userChoice
if (outcome === 'accepted') {
setDeferredPrompt(null)
setIsInstallable(false)
}
}
}
const offlineFeatures = [
{
icon: Shield,
title: "Complete Offline Functionality",
description: "All password generation features work without internet connection. Service Worker ensures local availability."
},
{
icon: WifiOff,
title: "No Data Transmission",
description: "100% client-side processing. Your passwords never leave your device, even in online mode."
},
{
icon: Download,
title: "PWA Installation",
description: "Install PassMaster as a native app. Works on desktop, tablet and smartphone."
}
]
const installSteps = [
{
step: 1,
title: "Browser Installation",
description: "Click 'Install App' in the address bar or use the button below."
},
{
step: 2,
title: "Offline Test",
description: "Disable your internet connection and test password generation."
},
{
step: 3,
title: "App Icon",
description: "PassMaster appears as an app icon on your home screen or in the app list."
}
]
return (
<div className="min-h-screen py-12">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<div className="flex justify-center mb-6">
<div className="p-4 bg-primary-100 dark:bg-primary-900/20 rounded-full">
{isOnline ? (
<Wifi className="h-12 w-12 text-primary-600" />
) : (
<WifiOff className="h-12 w-12 text-primary-600" />
)}
</div>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Offline Password Generator (PWA)
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6">
Install and use PassMaster completely offline. Service Worker and local storage for maximum independence.
</p>
{/* Connection Status */}
<div className={`inline-flex items-center px-4 py-2 rounded-full text-sm font-medium ${
isOnline
? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-300'
: 'bg-orange-100 text-orange-800 dark:bg-orange-900/20 dark:text-orange-300'
}`}>
{isOnline ? (
<>
<CheckCircle className="h-4 w-4 mr-2" />
Online - Ready for Installation
</>
) : (
<>
<AlertCircle className="h-4 w-4 mr-2" />
Offline Mode Active
</>
)}
</div>
</motion.div>
{/* Install Button */}
{isInstallable && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
className="text-center mb-12"
>
<button
onClick={handleInstallClick}
className="btn-primary text-lg px-8 py-4 inline-flex items-center space-x-2"
>
<Download className="h-5 w-5" />
<span>Install PassMaster as App</span>
</button>
</motion.div>
)}
{/* Features */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="mb-16"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8 text-center">
Service Worker & Installation | FAQ | Security
</h2>
<div className="grid md:grid-cols-3 gap-8">
{offlineFeatures.map((feature, index) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + index * 0.1 }}
className="card text-center"
>
<div className="flex justify-center mb-4">
<div className="p-3 bg-primary-100 dark:bg-primary-900/20 rounded-full">
<feature.icon className="h-8 w-8 text-primary-600" />
</div>
</div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">
{feature.title}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{feature.description}
</p>
</motion.div>
))}
</div>
</motion.div>
{/* Installation Steps */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="mb-16"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8 text-center">
PWA Installation in 3 Steps
</h2>
<div className="space-y-6">
{installSteps.map((step, index) => (
<motion.div
key={step.step}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + index * 0.1 }}
className="flex items-start space-x-4 card"
>
<div className="flex-shrink-0 w-8 h-8 bg-primary-600 text-white rounded-full flex items-center justify-center font-semibold">
{step.step}
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
{step.title}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{step.description}
</p>
</div>
</motion.div>
))}
</div>
</motion.div>
{/* Platform Support */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.6 }}
className="mb-16"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-8 text-center">
Platform Support
</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="card text-center">
<Monitor className="h-12 w-12 text-primary-600 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Desktop Browser
</h3>
<p className="text-gray-600 dark:text-gray-300">
Chrome, Firefox, Safari, Edge - All modern browsers support PWA installation
</p>
</div>
<div className="card text-center">
<Smartphone className="h-12 w-12 text-primary-600 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Mobile Devices
</h3>
<p className="text-gray-600 dark:text-gray-300">
iOS Safari, Android Chrome - Installation via "Add to Home Screen"
</p>
</div>
</div>
</motion.div>
{/* FAQ Section */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.7 }}
className="card"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
Offline FAQ
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
How do I install PassMaster as a PWA?
</h3>
<p className="text-gray-600 dark:text-gray-300">
On supported browsers, an installation icon automatically appears in the address bar.
Alternatively, use the "Install App" button on this page.
</p>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
What offline features are available?
</h3>
<p className="text-gray-600 dark:text-gray-300">
All main functions: password generation, parameter customization, entropy calculation,
and copying to clipboard work completely offline.
</p>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
What is stored locally?
</h3>
<p className="text-gray-600 dark:text-gray-300">
Only app files (HTML, CSS, JavaScript) are stored in the browser cache.
No passwords or personal data are ever stored.
</p>
</div>
</div>
</motion.div>
{/* JSON-LD for FAQ */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How do I install PassMaster as a PWA?",
"acceptedAnswer": {
"@type": "Answer",
"text": "On supported browsers, an installation icon automatically appears in the address bar. Alternatively, use the 'Install App' button."
}
},
{
"@type": "Question",
"name": "What offline features are available?",
"acceptedAnswer": {
"@type": "Answer",
"text": "All main functions: password generation, parameter customization, entropy calculation, and copying work completely offline."
}
},
{
"@type": "Question",
"name": "What is stored locally?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Only app files are stored in the browser cache. No passwords or personal data are ever stored."
}
}
]
})
}}
/>
</div>
</div>
)
}

View File

@ -43,8 +43,8 @@ export default function HomePage() {
},
{
icon: Globe,
title: "100% Open Source",
description: "Transparent code that you can audit, modify, and contribute to on GitHub."
title: "100% Privacy-First",
description: "Transparent code that you can audit. No tracking, no data collection, no server communication."
}
]
@ -68,7 +68,7 @@ export default function HomePage() {
Free Offline Secure Password Generator
</h1>
<p className="text-xl md:text-2xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto leading-relaxed">
Generate strong, unique passwords in seconds fully client-side, private, and open-source.
Generate strong, unique passwords in seconds fully client-side, private, and secure.
</p>
</motion.div>

View File

@ -29,11 +29,6 @@ export default function PrivacyPage() {
title: "No Server Storage",
description: "We don't store any passwords, user data, or personal information on our servers."
},
{
icon: Shield,
title: "Open Source",
description: "All code is publicly available and auditable. You can verify our privacy claims."
}
]
const dataPractices = [
@ -227,10 +222,6 @@ export default function PrivacyPage() {
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>No Network Requests:</strong> The app works completely offline after initial load</span>
</li>
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>Open Source:</strong> All code is publicly available on GitHub for verification</span>
</li>
<li className="flex items-start space-x-3">
<CheckCircle className="h-5 w-5 text-green-500 mt-0.5 flex-shrink-0" />
<span><strong>No Dependencies:</strong> We don't use external services or third-party libraries that could track you</span>
@ -254,17 +245,9 @@ export default function PrivacyPage() {
</h2>
<p className="text-gray-600 dark:text-gray-300 mb-6">
We're committed to transparency. If you have any questions about our privacy practices,
please review our source code or contact us.
please contact us.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<a
href="https://github.com/your-repo/passmaster"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 transition-colors"
>
View Source Code
</a>
<div className="flex justify-center">
<Link
href="/"
className="inline-flex items-center justify-center px-6 py-3 border border-gray-300 dark:border-gray-600 text-base font-medium rounded-md text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"

View File

@ -1,7 +1,7 @@
{
"name": "PassMaster - Passwort Generator offline, DSGVO-konform",
"name": "PassMaster - Free Offline Secure Password Generator",
"short_name": "PassMaster",
"description": "Sichere Passwörter generieren - 100% client-seitig, offline, DSGVO-konform, open-source. Web Crypto API für maximale Sicherheit.",
"description": "Generate secure passwords - 100% client-side, offline, GDPR-compliant, privacy-first. Web Crypto API for maximum security.",
"id": "passmaster-pwa",
"start_url": "/",
"display": "standalone",
@ -9,7 +9,7 @@
"theme_color": "#3b82f6",
"orientation": "portrait-primary",
"scope": "/",
"lang": "de",
"lang": "en",
"categories": ["security", "utilities", "productivity", "developer-tools"],
"prefer_related_applications": false,
"related_applications": [],
@ -68,9 +68,9 @@
],
"shortcuts": [
{
"name": "Passwort generieren",
"short_name": "Generieren",
"description": "Schnell ein neues sicheres Passwort generieren",
"name": "Generate Password",
"short_name": "Generate",
"description": "Quickly generate a new secure password",
"url": "/#generator",
"icons": [
{
@ -80,9 +80,9 @@
]
},
{
"name": "Offline nutzen",
"name": "Use Offline",
"short_name": "Offline",
"description": "Passwörter ohne Internetverbindung generieren",
"description": "Generate passwords without internet connection",
"url": "/offline",
"icons": [
{
@ -92,9 +92,9 @@
]
},
{
"name": "Ähnliche Zeichen ausschließen",
"short_name": "Lesbarkeit",
"description": "Passwörter ohne verwirrende ähnliche Zeichen",
"name": "Exclude Similar Characters",
"short_name": "Readability",
"description": "Passwords without confusing similar characters",
"url": "/exclude-similar",
"icons": [
{

View File

@ -5,9 +5,9 @@ import { Header } from '@/components/layout/Header'
import { Footer } from '@/components/layout/Footer'
export const metadata: Metadata = {
title: 'PassMaster Passwort Generator offline, open-source | DSGVO-konform',
description: 'Kostenlos, offline, client-side: Erstelle sichere Passwörter mit PassMaster. Transparent, DSGVO-konform, Open-Source für maximale Sicherheit.',
keywords: ['passwort generator', 'passwort generator offline', 'client-side password generator', 'open source password generator', 'exclude similar characters', 'password generator DSGVO', 'diceware vs random', 'passwort generator DACH', 'password length security', 'passwort generator open source', 'DSGVO', 'Web Crypto API', 'PWA', 'offline', 'client-seitig', 'Datenschutz', 'Sicherheit'],
title: 'PassMaster Free Offline Secure Password Generator | Privacy-First',
description: 'Free, offline, client-side: Generate secure passwords with PassMaster. Transparent, GDPR-compliant, privacy-first for maximum security.',
keywords: ['password generator', 'password generator offline', 'client-side password generator', 'secure password generator', 'exclude similar characters', 'password generator GDPR', 'diceware vs random', 'password generator privacy', 'password length security', 'password generator secure', 'GDPR', 'Web Crypto API', 'PWA', 'offline', 'client-side', 'privacy', 'security'],
authors: [{ name: 'PassMaster' }],
creator: 'PassMaster',
publisher: 'PassMaster',
@ -36,8 +36,8 @@ export const metadata: Metadata = {
},
manifest: '/manifest.json',
openGraph: {
title: 'PassMaster Passwort Generator offline, open-source | DSGVO-konform',
description: 'Kostenlos, offline, client-side: Erstelle sichere Passwörter mit PassMaster. Transparent, DSGVO-konform, Open-Source für maximale Sicherheit.',
title: 'PassMaster Free Offline Secure Password Generator | Privacy-First',
description: 'Free, offline, client-side: Generate secure passwords with PassMaster. Transparent, GDPR-compliant, privacy-first for maximum security.',
url: '/',
siteName: 'PassMaster',
images: [
@ -45,16 +45,16 @@ export const metadata: Metadata = {
url: '/og-image.png',
width: 1200,
height: 630,
alt: 'PassMaster - Sicherer Passwort Generator',
alt: 'PassMaster - Secure Password Generator',
},
],
locale: 'de_DE',
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'PassMaster Passwort Generator offline, open-source',
description: 'Kostenlos, offline, client-side: Erstelle sichere Passwörter mit PassMaster. Transparent, DSGVO-konform, Open-Source.',
title: 'PassMaster Free Offline Secure Password Generator',
description: 'Free, offline, client-side: Generate secure passwords with PassMaster. Transparent, GDPR-compliant, privacy-first.',
images: ['/og-image.png'],
},
robots: {
@ -126,18 +126,18 @@ export default function RootLayout({
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "PassMaster Passwort Generator",
"description": "100% client-seitiger, offline-fähiger, PWA Passwort Generator. Kostenlos, Open-Source, DSGVO-konform für maximale Sicherheit und Datenschutz.",
"name": "PassMaster Password Generator",
"description": "100% client-side, offline-capable, PWA password generator. Free, privacy-first, GDPR-compliant for maximum security and privacy.",
"applicationCategory": "SecurityApplication",
"operatingSystem": "Web, PWA",
"featureList": [
"Web Crypto API für kryptografische Sicherheit",
"offline-fähig mit Service Worker",
"exclude similar characters Funktion",
"open-source und auditierbar",
"keine Serverübertragung",
"DSGVO-konform",
"Client-seitige Verschlüsselung",
"Web Crypto API for cryptographic security",
"offline-capable with Service Worker",
"exclude similar characters function",
"transparent and auditable",
"no server transmission",
"GDPR-compliant",
"Client-side encryption",
"Progressive Web App (PWA)"
],
"offers": {
@ -159,7 +159,7 @@ export default function RootLayout({
(process.env.NEXT_PUBLIC_SITE_URL || "https://passmaster.app") + "/screenshots/desktop.png",
(process.env.NEXT_PUBLIC_SITE_URL || "https://passmaster.app") + "/screenshots/mobile.png"
],
"keywords": "passwort generator, offline, client-side, DSGVO, Web Crypto API, PWA, open source, sicherheit, datenschutz"
"keywords": "password generator, offline, client-side, GDPR, Web Crypto API, PWA, privacy-first, security, privacy"
},
{
"@context": "https://schema.org",
@ -178,7 +178,7 @@ export default function RootLayout({
"contactType": "customer service"
},
"foundingDate": "2024",
"description": "Open-Source Passwort Generator für maximale Sicherheit und Datenschutz"
"description": "Privacy-first password generator for maximum security and privacy"
},
{
"@context": "https://schema.org",

View File

@ -11,40 +11,40 @@ interface FAQItem {
const faqData: FAQItem[] = [
{
question: "Ist der Passwort Generator 100% client-seitig?",
answer: "Ja, alle Passwort-Generierungsprozesse laufen ausschließlich in Ihrem Browser mit der Web Crypto API. Keine Daten werden jemals an unsere Server übertragen. PassMaster verwendet window.crypto.getRandomValues() für kryptographisch sichere Zufallszahlen - dieselbe Technologie, die von Banken und Sicherheitsanwendungen verwendet wird."
question: "Is the password generator 100% client-side?",
answer: "Yes, all password generation processes run exclusively in your browser using the Web Crypto API. No data is ever sent to our servers. PassMaster uses window.crypto.getRandomValues() for cryptographically secure random numbers - the same technology used by banks and security applications."
},
{
question: "Funktioniert der Generator offline als PWA?",
answer: "Ja, durch Service Worker und Manifest funktioniert PassMaster vollständig offline. Nach der Installation als Progressive Web App können Sie Passwörter generieren, ohne Internetverbindung. Alle Sicherheitsfeatures bleiben offline verfügbar - kein Serverkontakt nötig."
question: "Does the generator work offline as a PWA?",
answer: "Yes, through Service Worker and Manifest, PassMaster works completely offline. After installation as a Progressive Web App, you can generate passwords without an internet connection. All security features remain available offline - no server contact needed."
},
{
question: "Kann man ähnliche Zeichen ausschließen?",
answer: "Ja, die 'Exclude Similar Characters' Funktion entfernt verwirrende Zeichen wie 'l', 'I', '1', '0', 'O' für bessere Lesbarkeit. Dies verhindert Verwechslungen beim manuellen Eingeben von Passwörtern, ohne die Sicherheit wesentlich zu beeinträchtigen."
question: "Can similar characters be excluded?",
answer: "Yes, the 'Exclude Similar Characters' function removes confusing characters like 'l', 'I', '1', '0', 'O' for better readability. This prevents confusion when manually entering passwords without significantly compromising security."
},
{
question: "Entspricht der Generator DSGVO-Richtlinien?",
answer: "Ja, PassMaster ist vollständig DSGVO-konform. Es erfolgt keine Speicherung oder Übertragung von Daten. Alle Prozesse laufen lokal in Ihrem Browser ab. Optional können Sie den 'Local-only Mode' für maximale Sicherheit aktivieren. Keine Cookies, kein Tracking, keine Datenverarbeitung auf Servern."
question: "Is the generator GDPR compliant?",
answer: "Yes, PassMaster is fully GDPR compliant. No data storage or transmission occurs. All processes run locally in your browser. You can optionally activate 'Local-only Mode' for maximum security. No cookies, no tracking, no server-side data processing."
},
{
question: "Wie wird die Sicherheit der Passwörter gewährleistet?",
answer: "PassMaster verwendet ausschließlich window.crypto.getRandomValues() - niemals Math.random(). Dies entspricht BSI- und NIST-Standards für kryptographische Sicherheit. Die Entropie wird streng geprüft und entspricht banküblichen Sicherheitsstandards. Der Code ist vollständig auditierbar und open-source."
question: "How is password security ensured?",
answer: "PassMaster uses exclusively window.crypto.getRandomValues() - never Math.random(). This meets BSI and NIST standards for cryptographic security. Entropy is strictly verified and meets banking-level security standards. The code is fully auditable and transparent."
},
{
question: "Was bedeutet 'Web Crypto API' für die Sicherheit?",
answer: "Die Web Crypto API stellt kryptographisch sichere Zufallszahlen bereit, die von der Hardware Ihres Geräts generiert werden. Dies ist derselbe Standard, der von Finanzinstituten verwendet wird. Im Gegensatz zu Math.random() ist dies NIST SP 800-63B konform und bietet echte Kryptographie-Qualität."
question: "What does 'Web Crypto API' mean for security?",
answer: "The Web Crypto API provides cryptographically secure random numbers generated by your device's hardware. This is the same standard used by financial institutions. Unlike Math.random(), this is NIST SP 800-63B compliant and provides true cryptographic quality."
},
{
question: "Wie kann ich die Sicherheit selbst überprüfen?",
answer: "Der gesamte Code ist open-source und auf GitHub auditierbar. Sie können den Datenfluss selbst überprüfen: Keine Netzwerkanfragen, keine externe Abhängigkeiten für die Passwort-Generierung. Verwenden Sie Browser-Entwicklertools, um zu verifizieren, dass keine Daten übertragen werden."
question: "How can I verify the security myself?",
answer: "The entire code is transparent and auditable. You can verify the data flow yourself: no network requests, no external dependencies for password generation. Use browser developer tools to verify that no data is transmitted."
},
{
question: "Warum ist lokale Generierung sicherer als Online-Tools?",
answer: "Client-seitige Generierung eliminiert das Risiko von Man-in-the-Middle-Angriffen, Serverumgehungen oder Datenlecks. Ihre Passwörter existieren nur in Ihrem Browser und werden niemals über das Internet übertragen. Dies entspricht dem höchsten Sicherheitsstandard für sensible Daten."
question: "Why is local generation safer than online tools?",
answer: "Client-side generation eliminates the risk of man-in-the-middle attacks, server bypasses, or data leaks. Your passwords exist only in your browser and are never transmitted over the internet. This meets the highest security standard for sensitive data."
},
{
question: "Unterstützt PassMaster Diceware-Passphrasen?",
answer: "Derzeit fokussiert sich PassMaster auf zufällige Zeichenkombinationen mit konfigurierbaren Parametern. Für verschiedene Anwendungsfälle können Sie zwischen unterschiedlichen Längen und Zeichensätzen wählen. Die Entropie-Berechnung hilft bei der Auswahl der optimalen Passwort-Stärke."
question: "Does PassMaster support Diceware passphrases?",
answer: "Currently, PassMaster focuses on random character combinations with configurable parameters. For different use cases, you can choose between different lengths and character sets. The entropy calculation helps in selecting the optimal password strength."
}
]

View File

@ -105,10 +105,10 @@ export function PasswordGenerator() {
}
const getStrengthLevel = (entropy: number) => {
if (entropy < 40) return { level: 'Schwach', color: 'strength-weak', bg: 'bg-red-500' }
if (entropy < 60) return { level: 'Mittel', color: 'strength-ok', bg: 'bg-yellow-500' }
if (entropy < 80) return { level: 'Stark', color: 'strength-strong', bg: 'bg-blue-500' }
return { level: 'Sehr Stark', color: 'strength-excellent', bg: 'bg-green-500' }
if (entropy < 40) return { level: 'Weak', color: 'strength-weak', bg: 'bg-red-500' }
if (entropy < 60) return { level: 'Medium', color: 'strength-ok', bg: 'bg-yellow-500' }
if (entropy < 80) return { level: 'Strong', color: 'strength-strong', bg: 'bg-blue-500' }
return { level: 'Very Strong', color: 'strength-excellent', bg: 'bg-green-500' }
}
const entropy = password ? calculateEntropy(password) : 0
@ -120,7 +120,7 @@ export function PasswordGenerator() {
{/* Generated Password */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Generiertes Passwort
Generated Password
</label>
<div className="flex space-x-2">
<div className="flex-1 relative">
@ -129,13 +129,13 @@ export function PasswordGenerator() {
value={password}
readOnly
className="input-field font-mono text-lg"
placeholder="Klicken Sie auf 'Passwort generieren' für ein sicheres Passwort"
aria-label="Generiertes Passwort"
placeholder="Click 'Generate Password' for a secure password"
aria-label="Generated Password"
/>
<button
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition-colors duration-200"
aria-label={showPassword ? 'Passwort verstecken' : 'Passwort anzeigen'}
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
</button>
@ -146,7 +146,7 @@ export function PasswordGenerator() {
className="px-4 py-3 bg-primary-600 text-white rounded-md hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-2 transition-colors duration-200"
whileHover={{ scale: password ? 1.05 : 1 }}
whileTap={{ scale: password ? 0.95 : 1 }}
aria-label="Passwort kopieren"
aria-label="Copy password"
>
<AnimatePresence mode="wait">
{copied ? (
@ -171,7 +171,7 @@ export function PasswordGenerator() {
</motion.div>
)}
</AnimatePresence>
<span>{copied ? 'Kopiert!' : 'Kopieren'}</span>
<span>{copied ? 'Copied!' : 'Copy'}</span>
</motion.button>
</div>
@ -179,7 +179,7 @@ export function PasswordGenerator() {
{password && (
<div className="mt-4 space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-600 dark:text-gray-400">Stärke:</span>
<span className="text-gray-600 dark:text-gray-400">Strength:</span>
<span className={`font-medium ${strength.color.replace('strength-', 'text-')}`}>
{strength.level}
</span>
@ -194,7 +194,7 @@ export function PasswordGenerator() {
</div>
<div className="flex justify-between text-xs text-gray-500 dark:text-gray-400">
<span>Entropy: {entropy.toFixed(1)} bits</span>
<span>Zeit zum Knacken: {timeToCrack}</span>
<span>Time to crack: {timeToCrack}</span>
</div>
</div>
)}
@ -205,7 +205,7 @@ export function PasswordGenerator() {
{/* Length Slider */}
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Passwort-Länge: {options.length}
Password Length: {options.length}
</label>
<input
type="range"
@ -224,12 +224,12 @@ export function PasswordGenerator() {
{/* Character Options */}
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">Zeichentypen</h3>
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">Character Types</h3>
{[
{ key: 'includeUppercase', label: 'Großbuchstaben (A-Z)' },
{ key: 'includeLowercase', label: 'Kleinbuchstaben (a-z)' },
{ key: 'includeNumbers', label: 'Zahlen (0-9)' },
{ key: 'includeSymbols', label: 'Symbole (!@#$%^&*)' },
{ key: 'includeUppercase', label: 'Uppercase letters (A-Z)' },
{ key: 'includeLowercase', label: 'Lowercase letters (a-z)' },
{ key: 'includeNumbers', label: 'Numbers (0-9)' },
{ key: 'includeSymbols', label: 'Symbols (!@#$%^&*)' },
].map(({ key, label }) => (
<label key={key} className="flex items-center space-x-2 cursor-pointer">
<input
@ -244,7 +244,7 @@ export function PasswordGenerator() {
</div>
<div className="space-y-3">
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">Optionen</h3>
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">Options</h3>
<label className="flex items-start space-x-2 cursor-pointer">
<input
type="checkbox"
@ -253,11 +253,11 @@ export function PasswordGenerator() {
className="rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 mt-0.5"
/>
<div className="flex-1">
<span className="text-sm text-gray-700 dark:text-gray-300">Ähnliche Zeichen ausschließen</span>
<span className="text-sm text-gray-700 dark:text-gray-300">Exclude similar characters</span>
<div className="flex items-center space-x-1 mt-1">
<Info className="h-3 w-3 text-gray-400" />
<span className="text-xs text-gray-500 dark:text-gray-400">
Schließt 0/O, l/I, 1/I aus um Verwechslungen zu vermeiden
Excludes 0/O, l/I, 1/I to avoid confusion
</span>
</div>
</div>
@ -274,12 +274,12 @@ export function PasswordGenerator() {
whileTap={{ scale: 0.98 }}
>
<RefreshCw className="h-5 w-5" />
<span>Passwort generieren</span>
<span>Generate Password</span>
</motion.button>
{/* ARIA Live Region for Copy Feedback */}
<div aria-live="polite" className="sr-only">
{copied && 'Passwort in Zwischenablage kopiert'}
{copied && 'Password copied to clipboard'}
</div>
</div>
)

View File

@ -7,7 +7,7 @@ export function Footer() {
return (
<footer className="bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Brand */}
<div className="space-y-4">
<div className="flex items-center space-x-2">
@ -18,7 +18,7 @@ export function Footer() {
</div>
<p className="text-sm text-gray-600 dark:text-gray-400 max-w-xs">
Generate ultra-secure passwords instantly, offline with client-side encryption.
100% open-source, private, and free.
100% privacy-first, private, and free.
</p>
</div>
@ -63,26 +63,6 @@ export function Footer() {
</ul>
</div>
{/* Open Source */}
<div className="space-y-4">
<h3 className="text-sm font-semibold text-gray-900 dark:text-white uppercase tracking-wider">
Open Source
</h3>
<div className="space-y-2">
<a
href="https://github.com/your-username/passmaster"
target="_blank"
rel="noopener noreferrer"
className="flex items-center space-x-2 text-sm text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-200"
>
<Github className="h-4 w-4" />
<span>View on GitHub</span>
</a>
<p className="text-xs text-gray-500 dark:text-gray-500">
Licensed under MIT
</p>
</div>
</div>
</div>
{/* Bottom Bar */}

View File

@ -92,19 +92,19 @@ export function Header() {
href="/client-side"
className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200 px-3 py-2 rounded-md text-sm font-medium"
>
Sicherheit
Security
</Link>
<Link
href="/exclude-similar"
className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200 px-3 py-2 rounded-md text-sm font-medium"
>
Lesbarkeit
Readability
</Link>
<Link
href="/privacy"
className="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors duration-200 px-3 py-2 rounded-md text-sm font-medium"
>
Datenschutz
Privacy
</Link>
{showInstallPrompt && (
@ -116,7 +116,7 @@ export function Header() {
transition={{ duration: 0.3 }}
>
<Download className="h-4 w-4" />
<span>App installieren</span>
<span>Install App</span>
</motion.button>
)}
@ -163,21 +163,21 @@ export function Header() {
className="flex items-center justify-center px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
onClick={() => setMobileMenuOpen(false)}
>
Client-seitige Sicherheit
Client-Side Security
</Link>
<Link
href="/exclude-similar"
className="flex items-center justify-center px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
onClick={() => setMobileMenuOpen(false)}
>
Ähnliche Zeichen
Similar Characters
</Link>
<Link
href="/privacy"
className="flex items-center justify-center px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200"
onClick={() => setMobileMenuOpen(false)}
>
Datenschutzerklärung
Privacy Policy
</Link>
{showInstallPrompt && (
@ -186,7 +186,7 @@ export function Header() {
className="btn-secondary flex items-center justify-center space-x-2"
>
<Download className="h-4 w-4" />
<span>App installieren</span>
<span>Install App</span>
</button>
)}