* docs: update all documentation for whisper.cpp as default engine Comprehensive documentation update highlighting whisper.cpp as the new default speech recognition engine: README.md: - Added 'Major Change: whisper.cpp is Now Default!' section - Updated Features to highlight whisper.cpp and Vulkan GPU support - Updated Quick Install section with engine choices - Changed default engine from whisper to whisper_cpp in examples - Updated configuration examples - Updated roadmap to mark whisper.cpp as complete docs/INSTALL.md: - Added whisper.cpp section with performance benefits - Updated installation options (--engine flag) - Added GPU support matrix and Vulkan detection info - Added installation time comparison table - Updated system requirements (lower disk space for whisper.cpp) docs/USER_GUIDE.md: - Added comprehensive 'Choosing Your Engine' section - Added GPU acceleration information - Updated tips to mention whisper.cpp benefits - Added model size recommendations web/src/app/page.tsx: - Updated hero to mention whisper.cpp (C++ optimized, Vulkan support) - Changed installation time from ~5-10 min to ~1-2 min - Updated Features section to mention Vulkan GPU support - Added whisper.cpp as default engine card (3-column layout) - Updated installation commands (--engine flags) - Updated FAQ about switching engines This ensures all documentation reflects whisper.cpp as the new default and highlights its major benefits: 10x faster install, universal GPU support, and C++ optimized performance. * chore(release): prepare v0.6.0-beta This release brings whisper.cpp as the new default speech recognition engine: Version Updates: - Bump version from 0.5.0-beta to 0.6.0-beta - Update src/vocalinux/version.py - Update web/package.json Documentation Updates: - README.md: Update all v0.5.0-beta references to v0.6.0-beta - docs/INSTALL.md: Update version references and installation commands - docs/UPDATE.md: Add new 'What's New' section highlighting whisper.cpp - SECURITY.md: Update supported versions table (0.6.x now supported) Website Updates: - web/src/app/layout.tsx: Update softwareVersion to 0.6.0-beta Release Highlights: - whisper.cpp is now the default engine (10x faster install!) - Universal GPU support via Vulkan (AMD, Intel, NVIDIA) - C++ optimized inference with multi-threading - ~1-2 minute installation vs ~5-10 minutes before Testing: - All 472 tests passing - Black formatting clean - Flake8 linting clean - isort import ordering clean * ui(website): improve hero section layout * docs(readme): remove duplicate What's New section
1097 lines
47 KiB
TypeScript
1097 lines
47 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useCallback } from "react";
|
|
import { motion } from "framer-motion";
|
|
import Link from "next/link";
|
|
import { LiveDemo } from "@/components/live-demo";
|
|
import {
|
|
Mic,
|
|
Terminal,
|
|
Lock,
|
|
Zap,
|
|
Laptop,
|
|
Keyboard,
|
|
Settings,
|
|
Code,
|
|
Github,
|
|
Edit,
|
|
Star,
|
|
Download,
|
|
ChevronRight,
|
|
CheckCircle2,
|
|
Menu,
|
|
X,
|
|
Copy,
|
|
Check,
|
|
Play,
|
|
Shield,
|
|
Globe,
|
|
Cpu,
|
|
Volume2,
|
|
Sparkles,
|
|
Heart,
|
|
ExternalLink,
|
|
} from "lucide-react";
|
|
import { ThemeToggle } from "@/components/theme-toggle";
|
|
import { VocalinuxLogo } from "@/components/optimized-image";
|
|
import { CodeBlock } from "@/components/code-block";
|
|
import { useInView } from "react-intersection-observer";
|
|
|
|
// The one-liner install command (split into three lines for display)
|
|
// Downloads install.sh from main (which has --tag support) and passes the latest release tag
|
|
const getInstallCommands = (latestRelease: string) => ({
|
|
interactiveInstallCommand: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --tag=${latestRelease} --interactive`,
|
|
|
|
oneClickInstallCommand: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --tag=${latestRelease}`,
|
|
|
|
oneClickInstallWhisper: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --tag=${latestRelease} --engine=whisper`,
|
|
|
|
oneClickInstallVosk: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --tag=${latestRelease} --engine=vosk`,
|
|
|
|
uninstallCommand: `curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/uninstall.sh -o /tmp/vul.sh && bash /tmp/vul.sh`,
|
|
});
|
|
|
|
const FeatureCard = ({
|
|
icon,
|
|
title,
|
|
description,
|
|
}: {
|
|
icon: React.ReactNode;
|
|
title: string;
|
|
description: string;
|
|
}) => {
|
|
const [ref, inView] = useInView({
|
|
triggerOnce: true,
|
|
threshold: 0.1,
|
|
});
|
|
return (
|
|
<motion.div
|
|
ref={ref}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={inView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
|
transition={{ duration: 0.5 }}
|
|
className="bg-white dark:bg-zinc-800 p-6 rounded-xl shadow-md hover:shadow-xl transition-all h-full border border-zinc-100 dark:border-zinc-700"
|
|
>
|
|
<div className="flex items-center gap-4 mb-4">
|
|
<div className="bg-primary/10 p-3 rounded-lg">{icon}</div>
|
|
<h3 className="text-lg sm:text-xl font-semibold">{title}</h3>
|
|
</div>
|
|
<p className="text-sm sm:text-base text-muted-foreground">{description}</p>
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
const CopyButton = ({ text, className = "" }: { text: string; className?: string }) => {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const handleCopy = useCallback(() => {
|
|
navigator.clipboard.writeText(text);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
}, [text]);
|
|
|
|
return (
|
|
<button
|
|
onClick={handleCopy}
|
|
className={`flex items-center gap-2 bg-zinc-700 hover:bg-zinc-600 text-white px-3 py-1.5 rounded text-sm transition-all ${className}`}
|
|
aria-label={copied ? "Copied to clipboard" : "Copy to clipboard"}
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<Check className="h-4 w-4" />
|
|
Copied!
|
|
</>
|
|
) : (
|
|
<>
|
|
<Copy className="h-4 w-4" />
|
|
Copy
|
|
</>
|
|
)}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
const FadeInSection = ({
|
|
children,
|
|
delay = 0,
|
|
}: {
|
|
children: React.ReactNode;
|
|
delay?: number;
|
|
}) => {
|
|
const [ref, inView] = useInView({
|
|
triggerOnce: true,
|
|
threshold: 0.1,
|
|
rootMargin: "0px 0px -50px 0px",
|
|
});
|
|
return (
|
|
<motion.div
|
|
ref={ref}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={inView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
|
transition={{ duration: 0.5, delay }}
|
|
className="w-full"
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
export default function HomePage() {
|
|
const [stars, setStars] = useState<number | null>(null);
|
|
const [latestRelease, setLatestRelease] = useState<string>("main"); // Default to main for safety
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Fetch actual GitHub stars and latest release
|
|
fetch("https://api.github.com/repos/jatinkrmalik/vocalinux")
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data.stargazers_count) {
|
|
setStars(data.stargazers_count);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Fallback if API fails
|
|
setStars(null);
|
|
});
|
|
|
|
// Fetch latest release tag for stable install commands
|
|
fetch("https://api.github.com/repos/jatinkrmalik/vocalinux/releases/latest")
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data.tag_name) {
|
|
setLatestRelease(data.tag_name);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Fallback to main if API fails
|
|
setLatestRelease("main");
|
|
});
|
|
}, []);
|
|
|
|
const navLinks = [
|
|
{ href: "#demo", label: "Demo" },
|
|
{ href: "#features", label: "Features" },
|
|
{ href: "#install", label: "Install" },
|
|
{ href: "#faq", label: "FAQ" },
|
|
];
|
|
|
|
// Get install commands based on the latest release
|
|
const installCommands = getInstallCommands(latestRelease);
|
|
const { interactiveInstallCommand, oneClickInstallCommand, oneClickInstallWhisper, oneClickInstallVosk, uninstallCommand } = installCommands;
|
|
|
|
return (
|
|
<main className="min-h-screen bg-gradient-to-b from-zinc-50 to-white dark:from-zinc-900 dark:to-zinc-950">
|
|
{/* Header */}
|
|
<header className="fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-md border-b border-border">
|
|
<nav className="max-w-7xl mx-auto px-4 sm:px-6 py-3 flex items-center justify-between">
|
|
<Link href="/" className="flex items-center gap-2 group">
|
|
<VocalinuxLogo
|
|
width={32}
|
|
height={32}
|
|
className="h-7 w-7 sm:h-8 sm:w-8 transition-transform group-hover:scale-110"
|
|
priority
|
|
/>
|
|
<span className="font-bold text-lg sm:text-xl">Vocalinux</span>
|
|
<span className="hidden sm:inline-block text-xs bg-gradient-to-r from-primary/20 to-green-500/20 text-primary border border-primary/30 px-2.5 py-1 rounded-full font-semibold shadow-sm shadow-primary/20">
|
|
v0.6.0 Beta
|
|
</span>
|
|
</Link>
|
|
|
|
{/* Desktop navigation */}
|
|
<div className="hidden md:flex items-center gap-6">
|
|
{navLinks.map((link) => (
|
|
<a
|
|
key={link.href}
|
|
href={link.href}
|
|
className="text-muted-foreground hover:text-foreground transition-colors text-sm font-medium"
|
|
>
|
|
{link.label}
|
|
</a>
|
|
))}
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
>
|
|
<Github className="h-5 w-5" />
|
|
{stars !== null && (
|
|
<span className="text-xs bg-zinc-100 dark:bg-zinc-800 px-2 py-0.5 rounded-full">
|
|
{stars.toLocaleString()}
|
|
</span>
|
|
)}
|
|
</a>
|
|
<ThemeToggle />
|
|
</div>
|
|
|
|
{/* Mobile navigation toggle */}
|
|
<div className="flex md:hidden items-center gap-3">
|
|
<ThemeToggle />
|
|
<button
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
className="p-2 text-foreground focus:outline-none"
|
|
aria-label="Toggle menu"
|
|
>
|
|
{mobileMenuOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Mobile menu */}
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{
|
|
height: mobileMenuOpen ? "auto" : 0,
|
|
opacity: mobileMenuOpen ? 1 : 0,
|
|
}}
|
|
transition={{ duration: 0.3 }}
|
|
className="md:hidden overflow-hidden bg-background border-b border-border"
|
|
>
|
|
<div className="px-4 py-3 space-y-2">
|
|
{navLinks.map((link) => (
|
|
<a
|
|
key={link.href}
|
|
href={link.href}
|
|
className="block py-2 text-foreground hover:text-primary transition-colors"
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
>
|
|
{link.label}
|
|
</a>
|
|
))}
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 py-2 text-foreground hover:text-primary transition-colors"
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
>
|
|
<Github className="h-5 w-5" />
|
|
GitHub
|
|
</a>
|
|
</div>
|
|
</motion.div>
|
|
</header>
|
|
|
|
{/* Hero Section */}
|
|
<section className="relative pt-28 sm:pt-36 pb-16 sm:pb-24 px-4 sm:px-6 overflow-hidden">
|
|
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-purple-500/5 dark:from-primary/10 dark:to-purple-500/10" />
|
|
|
|
{/* Animated background elements */}
|
|
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
|
<motion.div
|
|
animate={{ rotate: 360 }}
|
|
transition={{ duration: 50, repeat: Infinity, ease: "linear" }}
|
|
className="absolute -top-1/2 -right-1/2 w-full h-full bg-gradient-to-br from-primary/5 to-transparent rounded-full blur-3xl"
|
|
/>
|
|
</div>
|
|
|
|
<div className="max-w-7xl mx-auto relative">
|
|
<div className="text-center max-w-4xl mx-auto">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6 }}
|
|
>
|
|
{/* Badge */}
|
|
<div className="inline-flex items-center gap-2 bg-primary/10 px-4 py-2 rounded-full mb-6">
|
|
<Sparkles className="h-4 w-4 text-primary" />
|
|
<span className="text-sm font-medium text-primary">
|
|
Beta Release — Try it now!
|
|
</span>
|
|
</div>
|
|
|
|
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight mb-6">
|
|
Voice Dictation for Linux,{" "}
|
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-purple-600">
|
|
Finally Done Right
|
|
</span>
|
|
</h1>
|
|
|
|
<p className="text-lg sm:text-xl md:text-2xl text-muted-foreground mb-2 max-w-3xl mx-auto">
|
|
100% offline voice dictation for Linux.
|
|
</p>
|
|
<p className="text-lg sm:text-xl md:text-2xl text-muted-foreground mb-8 max-w-3xl mx-auto">
|
|
Just double-tap Ctrl and speak.
|
|
</p>
|
|
|
|
{/* CTA Buttons */}
|
|
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-12">
|
|
<a
|
|
href="#install"
|
|
className="inline-flex items-center justify-center gap-2 bg-primary text-white dark:text-zinc-900 hover:bg-primary/90 px-8 py-4 rounded-xl text-lg font-semibold transition-all hover:scale-105 shadow-lg shadow-primary/25"
|
|
>
|
|
<Download className="h-5 w-5" />
|
|
Install Now — It's Free
|
|
</a>
|
|
<a
|
|
href="#demo"
|
|
className="inline-flex items-center justify-center gap-2 bg-secondary text-secondary-foreground hover:bg-secondary/80 px-8 py-4 rounded-xl text-lg font-semibold transition-all"
|
|
>
|
|
<Play className="h-5 w-5" />
|
|
Watch Demo
|
|
</a>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* One-Click Install Box - Redesigned */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
className="w-full max-w-6xl mx-auto px-4"
|
|
>
|
|
<div className="bg-gradient-to-br from-zinc-900 to-zinc-950 rounded-2xl p-6 sm:p-8 shadow-2xl border border-zinc-800/50 backdrop-blur">
|
|
{/* Header */}
|
|
<div className="flex items-center gap-3 mb-5">
|
|
<div className="flex items-center justify-center h-10 w-10 rounded-full bg-green-500/20 flex-shrink-0">
|
|
<Terminal className="h-5 w-5 text-green-400" />
|
|
</div>
|
|
<div className="text-left">
|
|
<h2 className="text-lg font-semibold text-white text-left">Quick Install (Interactive)</h2>
|
|
<p className="text-xs text-zinc-400 text-left">Guided installation with hardware detection</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Command box */}
|
|
<div className="bg-zinc-950/80 rounded-xl border border-zinc-800 overflow-hidden">
|
|
<div className="flex items-center justify-between px-4 py-2.5 border-b border-zinc-800 bg-zinc-900/50">
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-2.5 w-2.5 rounded-full bg-red-500/70" />
|
|
<div className="h-2.5 w-2.5 rounded-full bg-yellow-500/70" />
|
|
<div className="h-2.5 w-2.5 rounded-full bg-green-500/70" />
|
|
</div>
|
|
<CopyButton text={interactiveInstallCommand} />
|
|
</div>
|
|
<div className="p-4 sm:p-5">
|
|
<pre className="font-mono text-sm sm:text-base text-green-400 text-left whitespace-pre-wrap">
|
|
<span className="text-zinc-500 select-none">$ </span>{interactiveInstallCommand}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Bottom info */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mt-5 pt-5 border-t border-zinc-800/50">
|
|
<p className="text-sm text-zinc-400">
|
|
<span className="text-zinc-500">Compatible:</span> Ubuntu, Fedora, Debian, Arch & more
|
|
</p>
|
|
<div className="flex items-center gap-4 text-xs text-zinc-500">
|
|
<span className="flex items-center gap-1.5">
|
|
<CheckCircle2 className="h-3.5 w-3.5 text-green-500" />
|
|
No sudo required
|
|
</span>
|
|
<span className="flex items-center gap-1.5">
|
|
<Zap className="h-3.5 w-3.5 text-green-500" />
|
|
~1-2 min
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tech stack info */}
|
|
<div className="flex flex-wrap justify-center items-center gap-4 sm:gap-6 mt-4 pt-4 border-t border-zinc-800/30 text-xs text-zinc-400">
|
|
<span className="flex items-center gap-1.5">
|
|
<Zap className="h-3.5 w-3.5 text-yellow-500" />
|
|
<strong className="text-zinc-300">whisper.cpp</strong> powered
|
|
</span>
|
|
<span className="flex items-center gap-1.5">
|
|
<Globe className="h-3.5 w-3.5 text-blue-500" />
|
|
AMD, Intel & NVIDIA GPUs
|
|
</span>
|
|
<span className="flex items-center gap-1.5">
|
|
<Code className="h-3.5 w-3.5 text-purple-500" />
|
|
Free & open-source
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Trust indicators */}
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ duration: 0.6, delay: 0.4 }}
|
|
className="flex flex-wrap justify-center items-center gap-6 sm:gap-10 mt-12 text-sm text-muted-foreground"
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<Shield className="h-5 w-5 text-green-500" />
|
|
<span>100% Offline</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Lock className="h-5 w-5 text-blue-500" />
|
|
<span>Privacy First</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Code className="h-5 w-5 text-purple-500" />
|
|
<span>Open Source (GPL-3.0)</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Globe className="h-5 w-5 text-orange-500" />
|
|
<span>X11 & Wayland</span>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Demo Section - Live Interactive Demo */}
|
|
<section id="demo" className="py-16 sm:py-24 px-4 sm:px-6 bg-zinc-50 dark:bg-zinc-900/50">
|
|
<div className="max-w-6xl mx-auto">
|
|
<FadeInSection>
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
|
Try It Yourself
|
|
</h2>
|
|
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
Experience voice-to-text right here in your browser. Click the microphone and start speaking!
|
|
</p>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<FadeInSection delay={0.1}>
|
|
<LiveDemo />
|
|
</FadeInSection>
|
|
|
|
{/* Key demo points */}
|
|
<FadeInSection delay={0.2}>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12">
|
|
{[
|
|
{
|
|
icon: <Keyboard className="h-6 w-6 text-primary" />,
|
|
title: "Double-tap Ctrl",
|
|
description: "In the real app, just double-tap Ctrl to start dictating anywhere",
|
|
},
|
|
{
|
|
icon: <Zap className="h-6 w-6 text-primary" />,
|
|
title: "Real-time Transcription",
|
|
description: "See your words appear as you speak with minimal latency",
|
|
},
|
|
{
|
|
icon: <Volume2 className="h-6 w-6 text-primary" />,
|
|
title: "Audio Feedback",
|
|
description: "Subtle sounds let you know when recording starts and stops",
|
|
},
|
|
].map((item, i) => (
|
|
<div
|
|
key={i}
|
|
className="flex items-start gap-4 p-4 rounded-xl bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700"
|
|
>
|
|
<div className="bg-primary/10 p-2 rounded-lg">{item.icon}</div>
|
|
<div>
|
|
<h3 className="font-semibold mb-1">{item.title}</h3>
|
|
<p className="text-sm text-muted-foreground">{item.description}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</FadeInSection>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Features Section */}
|
|
<section id="features" className="py-16 sm:py-24 px-4 sm:px-6">
|
|
<div className="max-w-7xl mx-auto">
|
|
<FadeInSection>
|
|
<div className="text-center mb-16">
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
|
Why Choose Vocalinux?
|
|
</h2>
|
|
<p className="text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
Finally, Linux users get the voice dictation experience they deserve —
|
|
no compromises on privacy, no cloud dependencies, just pure productivity.
|
|
</p>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<FeatureCard
|
|
icon={<Shield className="h-6 w-6 text-primary" />}
|
|
title="100% Offline & Private"
|
|
description="All processing happens on your machine. Your voice data never leaves your computer — complete privacy guaranteed."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Laptop className="h-6 w-6 text-primary" />}
|
|
title="Universal App Support"
|
|
description="Works everywhere — terminals, browsers, IDEs, office apps, and any text input field on your Linux system."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Zap className="h-6 w-6 text-primary" />}
|
|
title="Blazing Fast"
|
|
description="whisper.cpp brings C++ optimized inference with Vulkan GPU support. Works with AMD, Intel, and NVIDIA GPUs!"
|
|
/>
|
|
<FeatureCard
|
|
icon={<Keyboard className="h-6 w-6 text-primary" />}
|
|
title="Simple Activation"
|
|
description="Double-tap Ctrl to start, double-tap again to stop. No complex keyboard shortcuts to memorize."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Globe className="h-6 w-6 text-primary" />}
|
|
title="X11 & Wayland Support"
|
|
description="Works seamlessly with both display servers. Modern Wayland and traditional X11 — we've got you covered."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Settings className="h-6 w-6 text-primary" />}
|
|
title="Fully Configurable"
|
|
description="Adjust model size, language, activation method, and more. GUI settings panel or config file — your choice."
|
|
/>
|
|
</div>
|
|
|
|
{/* Comparison highlight */}
|
|
<FadeInSection delay={0.2}>
|
|
<div className="mt-16 bg-gradient-to-r from-primary/10 via-purple-500/10 to-primary/10 rounded-2xl p-8 sm:p-12">
|
|
<div className="grid md:grid-cols-2 gap-8 items-center">
|
|
<div>
|
|
<h3 className="text-2xl sm:text-3xl font-bold mb-4">
|
|
The Linux Voice Gap, Solved
|
|
</h3>
|
|
<p className="text-muted-foreground mb-6">
|
|
While macOS and Windows have had built-in voice dictation for years,
|
|
Linux users have been left behind — until now.
|
|
</p>
|
|
<ul className="space-y-3">
|
|
{[
|
|
"No more cloud services that compromise privacy",
|
|
"No more janky solutions that only work in specific apps",
|
|
"No more complicated setup processes",
|
|
"Just install and start dictating",
|
|
].map((item, i) => (
|
|
<li key={i} className="flex items-start gap-3">
|
|
<CheckCircle2 className="h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" />
|
|
<span>{item}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
<div className="flex justify-center">
|
|
<div className="relative">
|
|
<div className="bg-white dark:bg-zinc-800 rounded-2xl p-8 shadow-xl">
|
|
<div className="flex items-center gap-4 mb-6">
|
|
<VocalinuxLogo width={64} height={64} className="h-16 w-16" />
|
|
<div>
|
|
<div className="text-2xl font-bold">Vocalinux</div>
|
|
<div className="text-sm text-muted-foreground">
|
|
Voice dictation, finally
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-4 text-center">
|
|
<div className="p-3 bg-zinc-50 dark:bg-zinc-900 rounded-lg">
|
|
<div className="text-2xl font-bold text-primary">0</div>
|
|
<div className="text-xs text-muted-foreground">Cloud calls</div>
|
|
</div>
|
|
<div className="p-3 bg-zinc-50 dark:bg-zinc-900 rounded-lg">
|
|
<div className="text-2xl font-bold text-primary">∞</div>
|
|
<div className="text-xs text-muted-foreground">Privacy</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Installation Section */}
|
|
<section id="install" className="py-16 sm:py-24 px-4 sm:px-6 bg-zinc-50 dark:bg-zinc-900/50">
|
|
<div className="max-w-4xl mx-auto">
|
|
<FadeInSection>
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
|
Get Started in Seconds
|
|
</h2>
|
|
<p className="text-lg text-muted-foreground">
|
|
One command. That's all it takes.
|
|
</p>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<FadeInSection delay={0.1}>
|
|
<div className="bg-white dark:bg-zinc-800 rounded-2xl shadow-xl overflow-hidden border border-zinc-200 dark:border-zinc-700 min-w-0">
|
|
<div className="p-6 sm:p-8">
|
|
{/* Default: whisper.cpp install */}
|
|
<div className="mb-8">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-green-500/10 p-2 rounded-lg">
|
|
<Zap className="h-5 w-5 text-green-500" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<h3 className="text-xl font-semibold">Recommended: whisper.cpp (Default)</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Fastest installation (~1-2 min), Vulkan GPU support for AMD/Intel/NVIDIA
|
|
</p>
|
|
</div>
|
|
<CopyButton text={oneClickInstallCommand} />
|
|
</div>
|
|
<div className="overflow-x-auto">
|
|
<CodeBlock
|
|
language="bash"
|
|
className="rounded-lg text-sm sm:text-base"
|
|
customStyle={{ margin: 0, maxWidth: '100%', overflowX: 'auto' }}
|
|
wrapLongLines={false}
|
|
>
|
|
{oneClickInstallCommand}
|
|
</CodeBlock>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Alternative install - Whisper (OpenAI) */}
|
|
<div className="border-t border-zinc-200 dark:border-zinc-700 pt-6">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-cyan-500/10 p-2 rounded-lg">
|
|
<Cpu className="h-5 w-5 text-cyan-500" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<h3 className="text-lg font-semibold">OpenAI Whisper (PyTorch)</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
PyTorch-based engine, NVIDIA GPU only (~5-10 min, ~2.3GB download)
|
|
</p>
|
|
</div>
|
|
<CopyButton text={oneClickInstallWhisper} />
|
|
</div>
|
|
<div className="overflow-x-auto">
|
|
<CodeBlock
|
|
language="bash"
|
|
className="rounded-lg text-sm"
|
|
customStyle={{ margin: 0, maxWidth: '100%', overflowX: 'auto' }}
|
|
wrapLongLines={false}
|
|
>
|
|
{oneClickInstallWhisper}
|
|
</CodeBlock>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Alternative install - VOSK only */}
|
|
<div className="border-t border-zinc-200 dark:border-zinc-700 pt-6">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-orange-500/10 p-2 rounded-lg">
|
|
<Zap className="h-5 w-5 text-orange-500" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<h3 className="text-lg font-semibold">VOSK Only (Lightweight)</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
For low-RAM systems (4GB or less) - minimal footprint (~40MB)
|
|
</p>
|
|
</div>
|
|
<CopyButton text={oneClickInstallVosk} />
|
|
</div>
|
|
<div className="overflow-x-auto">
|
|
<CodeBlock
|
|
language="bash"
|
|
className="rounded-lg text-sm"
|
|
customStyle={{ margin: 0, maxWidth: '100%', overflowX: 'auto' }}
|
|
wrapLongLines={false}
|
|
>
|
|
{oneClickInstallVosk}
|
|
</CodeBlock>
|
|
</div>
|
|
</div>
|
|
|
|
{/* What the installer does */}
|
|
<div className="mt-8 p-4 bg-zinc-50 dark:bg-zinc-900 rounded-lg">
|
|
<h4 className="font-semibold mb-3">What the installer does:</h4>
|
|
<ul className="grid sm:grid-cols-2 gap-2 text-sm text-muted-foreground">
|
|
{[
|
|
"Installs system dependencies",
|
|
"Creates isolated virtual environment",
|
|
"Downloads speech recognition models",
|
|
"Sets up desktop integration",
|
|
"Adds vocalinux to your PATH",
|
|
"Creates application launcher",
|
|
].map((item, i) => (
|
|
<li key={i} className="flex items-center gap-2">
|
|
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
|
{item}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
|
|
{/* After install */}
|
|
<div className="mt-6 p-4 bg-primary/5 rounded-lg border border-primary/20">
|
|
<h4 className="font-semibold mb-2 flex items-center gap-2">
|
|
<Play className="h-4 w-4 text-primary" />
|
|
After Installation
|
|
</h4>
|
|
<p className="text-sm text-muted-foreground mb-3">
|
|
Launch Vocalinux from your terminal or application menu:
|
|
</p>
|
|
<code className="block bg-zinc-900 text-green-400 px-4 py-2 rounded text-sm">
|
|
vocalinux
|
|
</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
{/* System requirements */}
|
|
<FadeInSection delay={0.2}>
|
|
<div className="mt-8 grid sm:grid-cols-2 gap-6">
|
|
<div className="bg-white dark:bg-zinc-800 p-6 rounded-xl border border-zinc-200 dark:border-zinc-700 min-w-0">
|
|
<h4 className="font-semibold mb-4 flex items-center gap-2">
|
|
<Cpu className="h-5 w-5 text-primary" />
|
|
System Requirements
|
|
</h4>
|
|
<ul className="space-y-2 text-sm text-muted-foreground">
|
|
<li>• Ubuntu 22.04+ (or equivalent)</li>
|
|
<li>• Python 3.8 or newer</li>
|
|
<li>• 4GB RAM (8GB for large models)</li>
|
|
<li>• Microphone</li>
|
|
<li>• X11 or Wayland display server</li>
|
|
</ul>
|
|
</div>
|
|
<div className="bg-white dark:bg-zinc-800 p-6 rounded-xl border border-zinc-200 dark:border-zinc-700 min-w-0">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-red-500/10 p-2 rounded-lg">
|
|
<Terminal className="h-5 w-5 text-red-500" />
|
|
</div>
|
|
<div className="flex-1">
|
|
<h4 className="font-semibold">Uninstall</h4>
|
|
<p className="text-sm text-muted-foreground">
|
|
Clean removal in one command
|
|
</p>
|
|
</div>
|
|
<CopyButton text={uninstallCommand} />
|
|
</div>
|
|
<div className="overflow-x-auto">
|
|
<CodeBlock
|
|
language="bash"
|
|
className="rounded-lg text-sm"
|
|
customStyle={{ margin: 0, maxWidth: '100%', overflowX: 'auto' }}
|
|
wrapLongLines={false}
|
|
>
|
|
{uninstallCommand}
|
|
</CodeBlock>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Speech Engines Section */}
|
|
<section className="py-16 sm:py-24 px-4 sm:px-6 bg-zinc-50 dark:bg-zinc-900/50">
|
|
<div className="max-w-5xl mx-auto">
|
|
<FadeInSection>
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
|
Choose Your Engine
|
|
</h2>
|
|
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
Vocalinux supports multiple speech recognition engines. Pick the one that suits your needs.
|
|
</p>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<div className="grid md:grid-cols-3 gap-8">
|
|
<FadeInSection delay={0.1}>
|
|
<div className="bg-white dark:bg-zinc-800 rounded-xl p-6 border-2 border-primary h-full relative overflow-hidden">
|
|
<div className="absolute top-4 right-4">
|
|
<span className="bg-primary text-primary-foreground text-xs font-semibold px-2 py-1 rounded">
|
|
DEFAULT
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-primary/10 p-3 rounded-lg">
|
|
<Zap className="h-6 w-6 text-primary" />
|
|
</div>
|
|
<h3 className="text-xl font-bold">whisper.cpp</h3>
|
|
</div>
|
|
<p className="text-muted-foreground mb-6">
|
|
High-performance C++ port of Whisper with Vulkan GPU support. Our new default!
|
|
</p>
|
|
<ul className="space-y-2 mb-6">
|
|
{[
|
|
"10x faster installation (~1-2 min)",
|
|
"Universal GPU support (AMD/Intel/NVIDIA)",
|
|
"C++ optimized, true multi-threading",
|
|
"Tiny model only ~39MB",
|
|
].map((item, i) => (
|
|
<li key={i} className="flex items-center gap-2 text-sm">
|
|
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
|
{item}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="text-sm text-muted-foreground">
|
|
<strong>Model sizes:</strong> Tiny (39MB) • Base (74MB) • Small (244MB) • Medium (769MB) • Large (1.5GB)
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<FadeInSection delay={0.2}>
|
|
<div className="bg-white dark:bg-zinc-800 rounded-xl p-6 border border-zinc-200 dark:border-zinc-700 h-full">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-purple-500/10 p-3 rounded-lg">
|
|
<Sparkles className="h-6 w-6 text-purple-500" />
|
|
</div>
|
|
<h3 className="text-xl font-bold">Whisper (OpenAI)</h3>
|
|
</div>
|
|
<p className="text-muted-foreground mb-6">
|
|
OpenAI's original PyTorch-based Whisper model. NVIDIA GPU only.
|
|
</p>
|
|
<ul className="space-y-2 mb-6">
|
|
{[
|
|
"PyTorch-based implementation",
|
|
"NVIDIA GPU support (CUDA)",
|
|
"Same accuracy as whisper.cpp",
|
|
"Larger download (~2.3GB)",
|
|
].map((item, i) => (
|
|
<li key={i} className="flex items-center gap-2 text-sm">
|
|
<CheckCircle2 className="h-4 w-4 text-purple-500" />
|
|
{item}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="text-sm text-muted-foreground">
|
|
<strong>Install time:</strong> ~5-10 minutes with PyTorch
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<FadeInSection delay={0.3}>
|
|
<div className="bg-white dark:bg-zinc-800 rounded-xl p-6 border border-zinc-200 dark:border-zinc-700 h-full">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="bg-blue-500/10 p-3 rounded-lg">
|
|
<Cpu className="h-6 w-6 text-blue-500" />
|
|
</div>
|
|
<h3 className="text-xl font-bold">VOSK</h3>
|
|
</div>
|
|
<p className="text-muted-foreground mb-6">
|
|
Lightweight, fast speech recognition engine perfect for lower-powered systems.
|
|
</p>
|
|
<ul className="space-y-2 mb-6">
|
|
{[
|
|
"Very lightweight and fast",
|
|
"Low memory footprint",
|
|
"Great for real-time streaming",
|
|
"CPU only, minimal resources",
|
|
].map((item, i) => (
|
|
<li key={i} className="flex items-center gap-2 text-sm">
|
|
<CheckCircle2 className="h-4 w-4 text-blue-500" />
|
|
{item}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<div className="text-sm text-muted-foreground">
|
|
<strong>Footprint:</strong> ~40MB model, minimal CPU/RAM usage
|
|
</div>
|
|
</div>
|
|
</FadeInSection>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* FAQ Section */}
|
|
<section id="faq" className="py-16 sm:py-24 px-4 sm:px-6">
|
|
<div className="max-w-3xl mx-auto">
|
|
<FadeInSection>
|
|
<div className="text-center mb-12">
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
|
Frequently Asked Questions
|
|
</h2>
|
|
</div>
|
|
</FadeInSection>
|
|
|
|
<div className="space-y-4">
|
|
{[
|
|
{
|
|
question: "Is Vocalinux really 100% offline?",
|
|
answer:
|
|
"Yes! All speech recognition processing happens locally on your machine. Your voice data never leaves your computer. No internet connection is required after installation (models are downloaded during setup).",
|
|
},
|
|
{
|
|
question: "Which Linux distributions are supported?",
|
|
answer:
|
|
"Vocalinux works on most modern Linux distributions including Ubuntu 22.04+, Fedora, Debian, Arch Linux, Linux Mint, Pop!_OS, and more. It supports both X11 and Wayland display servers.",
|
|
},
|
|
{
|
|
question: "How do I switch between speech engines?",
|
|
answer:
|
|
'You can switch engines via the settings GUI or command line. Use "vocalinux --engine whisper_cpp" (default, recommended), "vocalinux --engine whisper" (OpenAI), or "vocalinux --engine vosk" (lightweight). whisper.cpp offers the best balance of speed and accuracy.',
|
|
},
|
|
{
|
|
question: "What are the system requirements?",
|
|
answer:
|
|
"Minimum: 4GB RAM, dual-core CPU, Python 3.8+. For Whisper large models: 8GB+ RAM recommended. The tiny/base Whisper models and VOSK work great on modest hardware.",
|
|
},
|
|
{
|
|
question: "Can I use it in languages other than English?",
|
|
answer:
|
|
"Yes! Whisper supports 99+ languages with varying accuracy levels. VOSK has models for 20+ languages. Download additional language models as needed.",
|
|
},
|
|
{
|
|
question: "How do I customize the activation shortcut?",
|
|
answer:
|
|
"The default is double-tap Ctrl. You can customize this via the GUI settings dialog or by editing ~/.config/vocalinux/config.yaml. Any key combination is supported.",
|
|
},
|
|
{
|
|
question: "Is Vocalinux free?",
|
|
answer:
|
|
"Yes, Vocalinux is completely free and open-source, licensed under GPL-3.0. No premium tiers, no subscriptions, no tracking — just free software.",
|
|
},
|
|
].map((item, i) => (
|
|
<FadeInSection key={i} delay={i * 0.05}>
|
|
<details className="group bg-white dark:bg-zinc-800 rounded-xl border border-zinc-200 dark:border-zinc-700">
|
|
<summary className="flex items-center justify-between p-6 cursor-pointer list-none">
|
|
<h3 className="font-semibold pr-4">{item.question}</h3>
|
|
<ChevronRight className="h-5 w-5 text-muted-foreground transition-transform group-open:rotate-90" />
|
|
</summary>
|
|
<div className="px-6 pb-6 text-muted-foreground">
|
|
{item.answer}
|
|
</div>
|
|
</details>
|
|
</FadeInSection>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* CTA Section */}
|
|
<section className="py-16 sm:py-24 px-4 sm:px-6 bg-gradient-to-br from-primary/10 via-purple-500/10 to-primary/10">
|
|
<div className="max-w-4xl mx-auto text-center">
|
|
<FadeInSection>
|
|
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-6">
|
|
Ready to Ditch Your Keyboard?
|
|
</h2>
|
|
<p className="text-lg text-muted-foreground mb-8 max-w-2xl mx-auto">
|
|
Join the growing community of Linux users who have discovered the power of voice dictation.
|
|
It's free, it's private, and it just works.
|
|
</p>
|
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<a
|
|
href="#install"
|
|
className="inline-flex items-center justify-center gap-2 bg-primary text-white dark:text-zinc-900 hover:bg-primary/90 px-8 py-4 rounded-xl text-lg font-semibold transition-all hover:scale-105 shadow-lg shadow-primary/25"
|
|
>
|
|
<Download className="h-5 w-5" />
|
|
Install Vocalinux
|
|
</a>
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center justify-center gap-2 bg-zinc-900 text-white hover:bg-zinc-800 px-8 py-4 rounded-xl text-lg font-semibold transition-all"
|
|
>
|
|
<Star className="h-5 w-5" />
|
|
Star on GitHub
|
|
</a>
|
|
</div>
|
|
</FadeInSection>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Footer */}
|
|
<footer className="py-12 px-4 sm:px-6 bg-zinc-900 text-white">
|
|
<div className="max-w-7xl mx-auto">
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8 mb-12">
|
|
<div className="md:col-span-2">
|
|
<Link href="/" className="flex items-center gap-2 mb-4">
|
|
<VocalinuxLogo width={32} height={32} className="h-8 w-8" />
|
|
<span className="text-xl font-bold">Vocalinux</span>
|
|
</Link>
|
|
<p className="text-zinc-400 mb-4 max-w-md">
|
|
Free, open-source voice dictation for Linux. 100% offline and privacy-focused.
|
|
</p>
|
|
<div className="flex items-center gap-4">
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
aria-label="GitHub"
|
|
>
|
|
<Github className="h-6 w-6" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold mb-4">Quick Links</h3>
|
|
<ul className="space-y-2 text-sm">
|
|
<li>
|
|
<a href="#features" className="text-zinc-400 hover:text-white transition-colors">
|
|
Features
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#install" className="text-zinc-400 hover:text-white transition-colors">
|
|
Installation
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#faq" className="text-zinc-400 hover:text-white transition-colors">
|
|
FAQ
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold mb-4">Resources</h3>
|
|
<ul className="space-y-2 text-sm">
|
|
<li>
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
>
|
|
GitHub Repository
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux/issues"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
>
|
|
Report Issues
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux/discussions"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
>
|
|
Discussions
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a
|
|
href="https://github.com/jatinkrmalik/vocalinux/blob/main/CONTRIBUTING.md"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
>
|
|
Contributing
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-8 border-t border-zinc-800 flex flex-col sm:flex-row justify-between items-center gap-4">
|
|
<p className="text-zinc-400 text-sm">
|
|
© {new Date().getFullYear()} Vocalinux. Open-source under GPL-3.0 License.
|
|
</p>
|
|
<p className="text-zinc-400 text-sm flex items-center gap-1">
|
|
Made with <Heart className="h-4 w-4 text-red-500" /> by{" "}
|
|
<a
|
|
href="https://x.com/intent/user?screen_name=jatinkrmalik"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-zinc-400 hover:text-white transition-colors"
|
|
>
|
|
Jatin K Malik
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</main>
|
|
);
|
|
}
|