Files
voculinux/web/src/app/page.tsx
T
Jatin K MalikandGitHub bba7829201 feat(web): overhaul SEO structure and intent landing pages (#210)
* feat(web): overhaul SEO structure and landing pages

Add distro-specific install pages and an engine comparison hub to capture high-intent organic queries. Centralize metadata and structured data handling to improve crawl consistency, internal linking, and rich-result eligibility.

* feat(web): add subpage navigation and visual polish

Make SEO landing pages easier to discover and navigate by adding persistent subpage header/footer links to home and key routes. Refresh install and comparison pages with colorful icon-driven sections while expanding homepage footer links for stronger internal linking.

* fix(ci): make web version sync robust to schema location

CI assumed softwareVersion lived in layout.tsx, but SEO refactors moved SoftwareApplication JSON-LD to page.tsx. Update workflow sync steps to patch whichever file currently contains softwareVersion so website build and release jobs keep working.

* chore(web): remove SEO audit artifact

Drop the internal SEO audit markdown from the PR so only production website and workflow changes are included.
2026-02-12 19:27:02 -08:00

1295 lines
55 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 {
Terminal,
Lock,
Zap,
Laptop,
Keyboard,
Settings,
Code,
Github,
Star,
Download,
ChevronRight,
CheckCircle2,
Menu,
X,
Copy,
Check,
Play,
Shield,
Globe,
Cpu,
Volume2,
Sparkles,
Heart,
} 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 the specific release tag to ensure version consistency
const getInstallCommands = (latestRelease: string) => ({
interactiveInstallCommand: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/${latestRelease}/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --interactive`,
oneClickInstallCommand: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/${latestRelease}/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh`,
oneClickInstallWhisper: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/${latestRelease}/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --engine=whisper`,
oneClickInstallVosk: `curl -fsSL raw.githubusercontent.com/jatinkrmalik/vocalinux/${latestRelease}/install.sh -o /tmp/vl.sh && bash /tmp/vl.sh --engine=vosk`,
uninstallCommand: `curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/${latestRelease}/uninstall.sh -o /tmp/vul.sh && bash /tmp/vul.sh`,
});
const homeJsonLd = [
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"@id": "https://vocalinux.com/#softwareapplication",
name: "Vocalinux",
applicationCategory: "UtilitiesApplication",
operatingSystem: "Linux",
isAccessibleForFree: true,
license: "https://github.com/jatinkrmalik/vocalinux/blob/main/LICENSE",
offers: {
"@type": "Offer",
price: "0",
priceCurrency: "USD",
},
description:
"Offline voice dictation and speech-to-text for Linux with whisper.cpp and VOSK.",
"softwareVersion": "0.6.1-beta",
author: {
"@type": "Person",
name: "Jatin K Malik",
url: "https://github.com/jatinkrmalik",
},
url: "https://vocalinux.com/",
downloadUrl: "https://github.com/jatinkrmalik/vocalinux",
screenshot: "https://vocalinux.com/og-image.png",
featureList: [
"100% offline speech recognition",
"Works with X11 and Wayland",
"Double-tap Ctrl activation",
"whisper.cpp, Whisper, and VOSK support",
"Linux desktop integration",
],
},
{
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: [
{
"@type": "Question",
name: "Is Vocalinux really 100% offline?",
acceptedAnswer: {
"@type": "Answer",
text: "Yes. All speech recognition runs locally on your Linux machine. Voice data does not leave your computer.",
},
},
{
"@type": "Question",
name: "Which Linux distributions are supported?",
acceptedAnswer: {
"@type": "Answer",
text: "Vocalinux supports Ubuntu, Fedora, Debian, Arch, Linux Mint, Pop!_OS, and other modern Linux distributions across X11 and Wayland.",
},
},
{
"@type": "Question",
name: "How do I switch between speech engines?",
acceptedAnswer: {
"@type": "Answer",
text: "Use the settings GUI or the CLI flags: vocalinux --engine whisper_cpp, vocalinux --engine whisper, or vocalinux --engine vosk.",
},
},
],
},
{
"@context": "https://schema.org",
"@type": "HowTo",
name: "Install Vocalinux on Linux",
description: "Install offline voice dictation on Linux in a few minutes.",
totalTime: "PT10M",
step: [
{
"@type": "HowToStep",
name: "Run the install command",
text: "Copy the install command from vocalinux.com and run it in terminal.",
url: "https://vocalinux.com/#install",
},
{
"@type": "HowToStep",
name: "Complete the guided setup",
text: "Pick your speech engine and model size in the interactive installer.",
url: "https://vocalinux.com/#install",
},
{
"@type": "HowToStep",
name: "Start dictating",
text: "Launch vocalinux and double-tap Ctrl to begin voice dictation.",
url: "https://vocalinux.com/#install",
},
],
},
];
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: "#guides", label: "Guides" },
{ 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">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(homeJsonLd) }}
/>
{/* 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.1 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 Linux Speech-to-Text in Your Browser
</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">
Offline Voice Dictation Features for Linux
</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">
How to Install Voice Dictation on Linux
</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>
{/* SEO Guide Hub Section */}
<section id="guides" className="py-16 sm:py-24 px-4 sm:px-6">
<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">
Linux Voice Dictation Guides by Distribution
</h2>
<p className="text-lg text-muted-foreground max-w-3xl mx-auto">
Follow distro-specific setup instructions for Ubuntu, Fedora, and Arch Linux.
These pages are written for real desktop workflows and include post-install checks.
</p>
</div>
</FadeInSection>
<div className="grid gap-6 md:grid-cols-3">
{[
{
href: "/install/ubuntu/",
title: "Ubuntu Voice Typing Guide",
description:
"Install offline speech-to-text on Ubuntu 22.04+ for GNOME, KDE, X11, and Wayland.",
},
{
href: "/install/fedora/",
title: "Fedora Speech-to-Text Guide",
description:
"Set up Vocalinux on Fedora Workstation with distro-specific notes for a stable dictation flow.",
},
{
href: "/install/arch/",
title: "Arch Linux Dictation Guide",
description:
"Lean setup for Arch, Manjaro, and EndeavourOS with hardware and injection sanity checks.",
},
].map((guide) => (
<Link
key={guide.href}
href={guide.href}
className="group rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm transition-all hover:-translate-y-1 hover:shadow-lg dark:border-zinc-700 dark:bg-zinc-800"
>
<h3 className="mb-3 text-xl font-semibold">{guide.title}</h3>
<p className="mb-4 text-sm text-muted-foreground">{guide.description}</p>
<span className="inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Read guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
))}
</div>
<FadeInSection delay={0.15}>
<div className="mt-8 rounded-2xl border border-primary/20 bg-primary/5 p-6 sm:p-8">
<h3 className="text-2xl font-bold mb-3">Need help choosing an engine?</h3>
<p className="text-muted-foreground mb-4">
Compare whisper.cpp, Whisper, and VOSK for speed, hardware support, and model size.
</p>
<Link
href="/compare/"
className="inline-flex items-center gap-2 text-sm font-semibold text-primary hover:underline"
>
View speech engine comparison <ChevronRight className="h-4 w-4" />
</Link>
</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 Linux Speech Recognition 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>
<li>
<Link href="/install/" className="text-zinc-400 hover:text-white transition-colors">
Linux Install Guides
</Link>
</li>
<li>
<Link href="/install/ubuntu/" className="text-zinc-400 hover:text-white transition-colors">
Ubuntu Guide
</Link>
</li>
<li>
<Link href="/install/fedora/" className="text-zinc-400 hover:text-white transition-colors">
Fedora Guide
</Link>
</li>
<li>
<Link href="/install/arch/" className="text-zinc-400 hover:text-white transition-colors">
Arch Guide
</Link>
</li>
<li>
<Link href="/compare/" className="text-zinc-400 hover:text-white transition-colors">
Engine Comparison
</Link>
</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>
<Link href="/install/" className="text-zinc-400 hover:text-white transition-colors">
Setup by Distribution
</Link>
</li>
<li>
<Link href="/compare/" className="text-zinc-400 hover:text-white transition-colors">
whisper.cpp vs Whisper vs VOSK
</Link>
</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>
);
}