Files
voculinux/web/src/app/page.tsx
T
Jatin K MalikandGitHub 03d55a989f chore(release): prepare v0.6.0-beta with whisper.cpp as default (#181)
* 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
2026-02-11 20:33:28 -08:00

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&apos;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 &amp; 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&apos;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&apos;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&apos;s free, it&apos;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>
);
}