feat(web): add v0.10.1 updates and 5 new SEO pages (#383)
* feat(web): add v0.10.1 updates and 5 new SEO pages ## New SEO Pages (5) - /vs-nerd-dictation/ - Vocalinux vs Nerd Dictation comparison - /whisper-model-guide/ - Whisper model sizes explained - /voice-typing-vscode/ - How to dictate in VS Code on Linux - /writers/ - Voice dictation for writers on Linux - /gnome-kde/ - Voice typing on GNOME vs KDE Plasma ## Existing Page Updates - homepage: Update JSON-LD featureList with v0.10.1 features, add new FAQs, link new pages in footer - troubleshooting: Add suspend/resume recovery, tray resources, settings dialog fixes - shortcuts: Add sound effects toggle, left/right modifier distinction, PTT improvements - wayland: Add keyboard layout preservation, clipboard fallback note - compare: Add engine switching safety notes - faq: Add suspend/resume and keyboard layout FAQs - alternatives: Link to new Nerd Dictation comparison - seo-subpage-shell: Add new pages to navigation - sitemap: Add 5 new URLs Fixes #257 (SEO expansion) * fix(web): add theme toggle to subpages, fix dark mode button contrast, simplify footer nav - Add light/dark theme toggle to SeoSubpageShell header (all subpages) - Fix duplicate useState/useEffect imports causing build error - Fix unclosed div in header markup - Add dark:text-black to CTA buttons across all 15 subpages for contrast on bright green bg - Add dark:text-black to footer CTA button in SeoSubpageShell - Simplify subpage footer to mainNavCategories (5 condensed categories) instead of full navCategories * fix(web): fix list items stacking, add all new pages to nav, restructure footer - Fix inline-flex → flex on all li elements across 13 pages so bullet points always stack on separate lines instead of running together horizontally - Add all 5 new SEO pages to subpage nav mainNavCategories (Whisper Models, For Writers, VS Code, GNOME vs KDE under Features/Use Cases; new Compare dropdown with vs Nerd Dictation, Alternatives, Offline, Open Source) - Add /vs-nerd-dictation/ to homepage footer Learn column - Restructure homepage footer from 2 crowded columns into 4 lean categorised columns (Get Started, Learn, Use Cases, Support) — responsive 2-col on mobile/tablet, 5-col on desktop
This commit is contained in:
@@ -127,4 +127,34 @@
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://vocalinux.com/vs-nerd-dictation/</loc>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://vocalinux.com/whisper-model-guide/</loc>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://vocalinux.com/voice-typing-vscode/</loc>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://vocalinux.com/writers/</loc>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://vocalinux.com/gnome-kde/</loc>
|
||||
<lastmod>2026-03-30</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import { CheckCircle2, ChevronRight, Cloud, DollarSign, Lock, Server, Shield, XCircle, Zap } from "lucide-react";
|
||||
import { CheckCircle2, ChevronRight, DollarSign, Lock, Server, Shield, XCircle, Zap } from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
@@ -232,6 +232,29 @@ export default function AlternativesPage() {
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section className="mt-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-2xl font-bold">Read the Nerd Dictation comparison</h2>
|
||||
<p className="mb-5 text-muted-foreground">
|
||||
Want a direct Linux-focused breakdown of CLI vs GUI workflows, Wayland behavior, and GPU
|
||||
support? Read our dedicated page.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Link
|
||||
href="/vs-nerd-dictation/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Read the Nerd Dictation comparison
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/compare/"
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2 text-sm font-semibold hover:bg-zinc-50 dark:border-zinc-600 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
Engine comparison
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-12 rounded-2xl border border-zinc-200 bg-zinc-50 p-8 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-4 text-2xl font-bold">The Bottom Line</h2>
|
||||
<p className="mb-6 text-muted-foreground">
|
||||
@@ -241,7 +264,7 @@ export default function AlternativesPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -287,7 +287,7 @@ export default function AutostartPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -273,7 +273,7 @@ export default function ChangelogPage() {
|
||||
|
||||
<ul className="space-y-2">
|
||||
{release.highlights.map((highlight) => (
|
||||
<li key={highlight} className="inline-flex items-start gap-2 text-muted-foreground">
|
||||
<li key={highlight} className="flex items-start gap-2 text-muted-foreground">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{highlight}
|
||||
</li>
|
||||
@@ -300,15 +300,15 @@ export default function ChangelogPage() {
|
||||
<section className="mt-12 rounded-2xl border border-zinc-200 bg-zinc-50 p-8 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-4 text-2xl font-bold">Stay Updated</h2>
|
||||
<ul className="space-y-3 text-muted-foreground">
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-primary" />
|
||||
Watch the repository on GitHub for release notifications
|
||||
</li>
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<Zap className="h-4 w-4 text-primary" />
|
||||
Re-run the installer to update to the latest version
|
||||
</li>
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<BookOpen className="h-4 w-4 text-primary" />
|
||||
Check the{" "}
|
||||
<Link href="/install/" className="font-semibold text-primary hover:underline">
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function CompareEnginesPage() {
|
||||
headline: "Whisper.cpp vs Whisper vs VOSK for Linux Voice Dictation",
|
||||
description:
|
||||
"Technical comparison of speech recognition engines for offline Linux voice typing.",
|
||||
dateModified: "2026-02-12",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
@@ -138,12 +138,22 @@ export default function CompareEnginesPage() {
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="mt-8 rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-3 text-2xl font-semibold">Switching Between Engines</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You can switch between whisper.cpp, Whisper, and VOSK at any time from Settings. v0.10.1+
|
||||
safely stops recognition before switching to prevent segfaults. Each engine has its own
|
||||
model files.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mt-12 grid gap-6 md:grid-cols-3">
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-2xl font-semibold">When to pick whisper.cpp</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Choose whisper.cpp when you want the best speed-to-accuracy ratio and broad hardware support.
|
||||
It is the default in Vocalinux for a reason.
|
||||
It is the default in Vocalinux for a reason. Safe engine switching - v0.10.1+ stops
|
||||
recognition before switching to prevent crashes.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import { BookOpen, ChevronRight, CircleHelp, Cpu, Laptop, Lock, Mic, Volume2, Zap } from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
import { buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const faqCategories = [
|
||||
{
|
||||
@@ -75,6 +75,14 @@ const faqCategories = [
|
||||
q: "Does Vocalinux work with Wayland?",
|
||||
a: "Yes. Vocalinux supports both X11 and Wayland, including IBus-based flows on Wayland with improved active-method detection for more reliable text injection.",
|
||||
},
|
||||
{
|
||||
q: "What happens when I close my laptop lid?",
|
||||
a: "Vocalinux v0.10.1+ automatically recovers speech recognition after system suspend/resume. The app detects when your system wakes up and restarts the recognition engine and keyboard shortcuts. No manual restart needed - just start dictating as usual.",
|
||||
},
|
||||
{
|
||||
q: "Does Vocalinux preserve my keyboard layout?",
|
||||
a: "Yes! v0.10.1+ preserves your XKB keyboard layout when activating IBus for text injection. Unlike other tools that switch to US layout, Vocalinux keeps your configured layout so you can dictate without unexpected character changes.",
|
||||
},
|
||||
{
|
||||
q: "Can I use voice commands?",
|
||||
a: "Yes. Vocalinux supports commands like 'new line', 'delete that', 'select all', and more. Voice commands are optional and can be enabled or disabled in Settings.",
|
||||
@@ -119,6 +127,7 @@ export default function FaqPage() {
|
||||
const faqJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "FAQPage",
|
||||
dateModified: "2026-03-30",
|
||||
mainEntity: faqCategories.flatMap((cat) =>
|
||||
cat.questions.map((q) => ({
|
||||
"@type": "Question",
|
||||
|
||||
@@ -282,7 +282,7 @@ export default function ForDevelopersPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import {
|
||||
CheckCircle2,
|
||||
ChevronRight,
|
||||
Keyboard,
|
||||
Monitor,
|
||||
Settings2,
|
||||
Waypoints,
|
||||
Wrench,
|
||||
} from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const comparisonRows = [
|
||||
{
|
||||
feature: "Default display stack",
|
||||
gnome: "Wayland by default on most modern GNOME distributions",
|
||||
kde: "Wayland or X11, with X11 still common in some setups",
|
||||
icon: Monitor,
|
||||
},
|
||||
{
|
||||
feature: "Text injection approach",
|
||||
gnome: "IBus-first for native Wayland dictation",
|
||||
kde: "IBus-first on Wayland, with xdotool fallback on X11",
|
||||
icon: Keyboard,
|
||||
},
|
||||
{
|
||||
feature: "Input method setup",
|
||||
gnome: "Typically straightforward with existing IBus defaults",
|
||||
kde: "May need explicit IBus selection in System Settings",
|
||||
icon: Settings2,
|
||||
},
|
||||
{
|
||||
feature: "Tray indicator behavior",
|
||||
gnome: "Usually needs AppIndicator extension for tray visibility",
|
||||
kde: "Native system tray support out of the box",
|
||||
icon: Waypoints,
|
||||
},
|
||||
{
|
||||
feature: "Troubleshooting profile",
|
||||
gnome: "Extension + IBus engine activation checks",
|
||||
kde: "Session type checks (X11 vs Wayland) + input method routing",
|
||||
icon: Wrench,
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "Voice Typing on GNOME vs KDE Plasma: Linux Desktop Comparison",
|
||||
description:
|
||||
"Compare voice dictation on GNOME vs KDE Plasma. How desktop environment affects text injection, IBus setup, and Wayland compatibility for Linux voice typing.",
|
||||
path: "/gnome-kde",
|
||||
keywords: [
|
||||
"voice typing GNOME linux",
|
||||
"voice dictation KDE plasma",
|
||||
"linux desktop voice recognition comparison",
|
||||
"GNOME vs KDE voice input",
|
||||
],
|
||||
});
|
||||
|
||||
export default function GnomeVsKdePage() {
|
||||
const articleJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: "Voice Typing on GNOME vs KDE Plasma: Linux Desktop Comparison",
|
||||
description:
|
||||
"Compare voice dictation on GNOME vs KDE Plasma. How desktop environment affects text injection, IBus setup, and Wayland compatibility for Linux voice typing.",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
url: "https://github.com/jatinkrmalik",
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Vocalinux",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: absoluteUrl("/vocalinux.png"),
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: absoluteUrl("/gnome-kde"),
|
||||
};
|
||||
|
||||
return (
|
||||
<SeoSubpageShell>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<p className="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary">
|
||||
<Monitor className="h-4 w-4" />
|
||||
Linux Desktop Comparison
|
||||
</p>
|
||||
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
|
||||
Voice Typing on GNOME vs KDE Plasma: Linux Desktop Comparison
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
GNOME and KDE Plasma both support excellent Linux voice dictation, but the desktop environment
|
||||
changes how text injection behaves, how IBus is configured, and what setup steps you need for a
|
||||
smooth daily workflow.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-4 text-2xl font-bold">Why desktop environment matters for voice dictation</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Voice typing quality is not just about the speech engine. Your desktop environment determines
|
||||
session type defaults, tray behavior, and input method routing. That affects whether dictation
|
||||
appears reliably in every app, especially under Wayland.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-4 text-2xl font-bold">GNOME: what to expect</h2>
|
||||
<ul className="space-y-3 text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>Wayland is the default</strong> on modern GNOME sessions, so IBus-backed text
|
||||
injection is usually the primary path.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>IBus integration is central</strong> for reliable cross-app dictation behavior on
|
||||
Wayland.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>Tray icon often needs AppIndicator support</strong> via GNOME extension, because
|
||||
legacy tray behavior differs from KDE.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>Known issues and workarounds:</strong> if the tray icon is missing, install/enable
|
||||
AppIndicator extension; if text does not appear in apps, verify IBus daemon/engine state.
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-4 text-2xl font-bold">KDE Plasma: what to expect</h2>
|
||||
<ul className="space-y-3 text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>X11 vs Wayland matters</strong>: Plasma supports both widely, so behavior can differ
|
||||
between sessions.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>IBus setup may be more explicit</strong> compared with GNOME defaults, depending on
|
||||
distro and prior input method configuration.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
<span>
|
||||
<strong>System Settings integration is strong</strong>, making it easier to inspect session
|
||||
type, input method behavior, and tray settings from one place.
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 overflow-x-auto rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="px-4 pb-2 pt-5 text-2xl font-bold">GNOME vs KDE Plasma comparison</h2>
|
||||
<table className="w-full border-collapse text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-200 dark:border-zinc-700">
|
||||
<th className="px-4 py-3 text-sm font-semibold">Feature</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">GNOME</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">KDE Plasma</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{comparisonRows.map((row) => {
|
||||
const Icon = row.icon;
|
||||
return (
|
||||
<tr key={row.feature} className="border-b border-zinc-100 align-top dark:border-zinc-700/70">
|
||||
<td className="px-4 py-4 font-semibold">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="rounded-md bg-primary/10 p-1.5">
|
||||
<Icon className="h-4 w-4 text-primary" />
|
||||
</span>
|
||||
{row.feature}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.gnome}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.kde}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 grid gap-6 md:grid-cols-2">
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-2xl font-semibold">When GNOME is a better fit</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Choose GNOME when you want a Wayland-first environment with familiar IBus defaults and a
|
||||
minimal, focused workflow. Add AppIndicator extension once and day-to-day dictation is
|
||||
typically very consistent.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-2xl font-semibold">When KDE Plasma is a better fit</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Choose KDE Plasma when you want deeper system configurability, flexible X11/Wayland session
|
||||
choices, and richer control over desktop behaviors while tuning a dictation setup.
|
||||
</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-4 text-2xl font-bold">Helpful guides</h2>
|
||||
<div className="grid gap-3 text-sm text-muted-foreground sm:grid-cols-3">
|
||||
<Link href="/wayland/" className="font-semibold text-primary hover:underline">
|
||||
Wayland setup and architecture
|
||||
</Link>
|
||||
<Link href="/troubleshooting/" className="font-semibold text-primary hover:underline">
|
||||
Troubleshooting checklist
|
||||
</Link>
|
||||
<Link href="/install/" className="font-semibold text-primary hover:underline">
|
||||
Install guide by distro
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-8">
|
||||
<h2 className="mb-4 text-2xl font-bold">Ready to set up voice typing on your desktop?</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Install Vocalinux and get reliable offline voice dictation on GNOME or KDE Plasma, with the
|
||||
right text injection path for your session type.
|
||||
</p>
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</section>
|
||||
</SeoSubpageShell>
|
||||
);
|
||||
}
|
||||
@@ -237,7 +237,7 @@ export default function GpuAccelerationPage() {
|
||||
<p className="mb-4 text-sm text-muted-foreground">{backend.description}</p>
|
||||
<ul className="space-y-1.5">
|
||||
{backend.advantages.map((advantage) => (
|
||||
<li key={advantage} className="inline-flex items-start gap-2 text-sm">
|
||||
<li key={advantage} className="flex items-start gap-2 text-sm">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{advantage}
|
||||
</li>
|
||||
@@ -337,7 +337,7 @@ export default function GpuAccelerationPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -323,7 +323,7 @@ export default async function DistroInstallPage({
|
||||
</h2>
|
||||
<ul className="space-y-2 text-muted-foreground">
|
||||
{config.prerequisites.map((item) => (
|
||||
<li key={item} className="inline-flex items-start gap-2">
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{item}
|
||||
</li>
|
||||
@@ -338,7 +338,7 @@ export default async function DistroInstallPage({
|
||||
</h2>
|
||||
<ul className="space-y-2 text-muted-foreground">
|
||||
{config.testedOn.map((item) => (
|
||||
<li key={item} className="inline-flex items-start gap-2">
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{item}
|
||||
</li>
|
||||
@@ -354,7 +354,7 @@ export default async function DistroInstallPage({
|
||||
</h2>
|
||||
<ol className="space-y-3 text-muted-foreground">
|
||||
{config.setupNotes.map((item) => (
|
||||
<li key={item} className="inline-flex items-start gap-2">
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{item}
|
||||
</li>
|
||||
@@ -369,7 +369,7 @@ export default async function DistroInstallPage({
|
||||
</h2>
|
||||
<ul className="space-y-3 text-muted-foreground">
|
||||
{config.postInstallChecks.map((item) => (
|
||||
<li key={item} className="inline-flex items-start gap-2">
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
{item}
|
||||
</li>
|
||||
|
||||
@@ -226,15 +226,15 @@ export default function LanguagesPage() {
|
||||
<div>
|
||||
<h3 className="mb-3 font-semibold">Language Selection</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Select your language in Settings → Speech Engine → Language
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Use "Auto" for automatic language detection (whisper.cpp/Whisper)
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Switch languages on-the-fly without restarting
|
||||
</li>
|
||||
@@ -243,15 +243,15 @@ export default function LanguagesPage() {
|
||||
<div>
|
||||
<h3 className="mb-3 font-semibold">Model Selection</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Larger models provide better accuracy for all languages
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
"tiny" model works for all supported languages
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Models are downloaded automatically on first use
|
||||
</li>
|
||||
@@ -269,7 +269,7 @@ export default function LanguagesPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -226,16 +226,16 @@ export default function OfflinePage() {
|
||||
Vocalinux uses speech recognition models that run entirely on your CPU or GPU:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-emerald-700 dark:text-emerald-400">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>whisper.cpp</strong> — C++ port of OpenAI Whisper, runs locally with GPU
|
||||
acceleration
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>VOSK</strong> — Lightweight offline engine, minimal resources
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>OpenAI Whisper</strong> — PyTorch-based, runs on NVIDIA GPU or CPU
|
||||
</li>
|
||||
@@ -254,7 +254,7 @@ export default function OfflinePage() {
|
||||
Don't trust—verify. Vocalinux is open source. You can:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-amber-700 dark:text-amber-400">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
Read every line of code on{" "}
|
||||
<a
|
||||
@@ -266,16 +266,16 @@ export default function OfflinePage() {
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
Run with <code className="rounded bg-amber-200 px-1 dark:bg-amber-900">--debug</code> to see all network activity (there is none)
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
Monitor with <code className="rounded bg-amber-200 px-1 dark:bg-amber-900">tcpdump</code> or{" "}
|
||||
<code className="rounded bg-amber-200 px-1 dark:bg-amber-900">wireshark</code>—you'll see silence
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
Disconnect from the internet and verify dictation still works perfectly
|
||||
</li>
|
||||
@@ -291,7 +291,7 @@ export default function OfflinePage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -163,7 +163,7 @@ export default function OpenSourcePage() {
|
||||
"Free to share with others",
|
||||
"Modifications must remain open",
|
||||
].map((item) => (
|
||||
<li key={item} className="inline-flex items-center gap-2 text-sm text-emerald-700 dark:text-emerald-400">
|
||||
<li key={item} className="flex items-center gap-2 text-sm text-emerald-700 dark:text-emerald-400">
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
{item}
|
||||
</li>
|
||||
@@ -299,7 +299,7 @@ export default function OpenSourcePage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Free
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
+69
-122
@@ -83,6 +83,10 @@ const homeJsonLd = [
|
||||
"Clipboard fallback for unsupported Wayland compositors",
|
||||
"Sound effects toggle for audio feedback",
|
||||
"whisper.cpp, Whisper, and VOSK support",
|
||||
"Auto-recover speech recognition after system suspend/resume",
|
||||
"Safe engine switching without segfaults",
|
||||
"Keyboard layout preserved during IBus activation",
|
||||
"Push-to-talk mode with improved silence detection",
|
||||
"Linux desktop integration",
|
||||
],
|
||||
},
|
||||
@@ -114,6 +118,22 @@ const homeJsonLd = [
|
||||
text: "Use the settings GUI or the CLI flags: vocalinux --engine whisper_cpp, vocalinux --engine whisper, or vocalinux --engine vosk.",
|
||||
},
|
||||
},
|
||||
{
|
||||
"@type": "Question",
|
||||
name: "What happens when I close my laptop lid?",
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: "Vocalinux v0.10.1+ automatically recovers speech recognition and keyboard shortcuts after system suspend/resume. No manual restart needed.",
|
||||
},
|
||||
},
|
||||
{
|
||||
"@type": "Question",
|
||||
name: "Does Vocalinux preserve my keyboard layout?",
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: "Yes! v0.10.1+ preserves your XKB keyboard layout when activating IBus, so you won't unexpectedly switch to US layout mid-dictation.",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1130,6 +1150,16 @@ export default function HomePage() {
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
question: "What happens when I close my laptop lid?",
|
||||
answer:
|
||||
"Vocalinux v0.10.1+ automatically recovers speech recognition and keyboard shortcuts after system suspend/resume. No manual restart needed.",
|
||||
},
|
||||
{
|
||||
question: "Does Vocalinux preserve my keyboard layout?",
|
||||
answer:
|
||||
"Yes! v0.10.1+ preserves your XKB keyboard layout when activating IBus, so you won't unexpectedly switch to US layout mid-dictation.",
|
||||
},
|
||||
{
|
||||
question: "What are the system requirements?",
|
||||
answer: (
|
||||
@@ -1403,13 +1433,13 @@ export default function HomePage() {
|
||||
{/* 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">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-5 gap-8 mb-12">
|
||||
<div className="col-span-2 lg:col-span-1 mb-4 lg:mb-0">
|
||||
<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">
|
||||
<p className="text-zinc-400 mb-4 text-sm max-w-xs">
|
||||
Free, open-source voice dictation for Linux. 100% offline and privacy-focused.
|
||||
</p>
|
||||
<div className="flex items-center gap-4">
|
||||
@@ -1426,129 +1456,46 @@ export default function HomePage() {
|
||||
</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>
|
||||
<li>
|
||||
<Link href="/use-cases/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Use Cases
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/alternatives/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Alternatives
|
||||
</Link>
|
||||
</li>
|
||||
<h3 className="font-semibold mb-4 text-xs uppercase tracking-wider text-zinc-400">Get Started</h3>
|
||||
<ul className="space-y-2.5 text-sm">
|
||||
<li><a href="#features" className="text-zinc-400 hover:text-white transition-colors">Features</a></li>
|
||||
<li><Link href="/install/" className="text-zinc-400 hover:text-white transition-colors">Install Guide</Link></li>
|
||||
<li><Link href="/changelog/" className="text-zinc-400 hover:text-white transition-colors">Changelog</Link></li>
|
||||
<li><Link href="/use-cases/" className="text-zinc-400 hover:text-white transition-colors">Use Cases</Link></li>
|
||||
<li><Link href="/autostart/" className="text-zinc-400 hover:text-white transition-colors">Autostart</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4">Resources</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li>
|
||||
<Link href="/faq/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
FAQ
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/changelog/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Changelog
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/shortcuts/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Keyboard Shortcuts
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/troubleshooting/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Troubleshooting
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/privacy/" className="text-zinc-400 hover:text-white transition-colors">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</li>
|
||||
<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>
|
||||
<h3 className="font-semibold mb-4 text-xs uppercase tracking-wider text-zinc-400">Learn</h3>
|
||||
<ul className="space-y-2.5 text-sm">
|
||||
<li><Link href="/compare/" className="text-zinc-400 hover:text-white transition-colors">Engine Comparison</Link></li>
|
||||
<li><Link href="/whisper-model-guide/" className="text-zinc-400 hover:text-white transition-colors">Whisper Models</Link></li>
|
||||
<li><Link href="/vs-nerd-dictation/" className="text-zinc-400 hover:text-white transition-colors">vs Nerd Dictation</Link></li>
|
||||
<li><Link href="/wayland/" className="text-zinc-400 hover:text-white transition-colors">Wayland Support</Link></li>
|
||||
<li><Link href="/gpu-acceleration/" className="text-zinc-400 hover:text-white transition-colors">GPU Acceleration</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4 text-xs uppercase tracking-wider text-zinc-400">Use Cases</h3>
|
||||
<ul className="space-y-2.5 text-sm">
|
||||
<li><Link href="/for-developers/" className="text-zinc-400 hover:text-white transition-colors">For Developers</Link></li>
|
||||
<li><Link href="/writers/" className="text-zinc-400 hover:text-white transition-colors">For Writers</Link></li>
|
||||
<li><Link href="/voice-typing-vscode/" className="text-zinc-400 hover:text-white transition-colors">VS Code</Link></li>
|
||||
<li><Link href="/gnome-kde/" className="text-zinc-400 hover:text-white transition-colors">GNOME vs KDE</Link></li>
|
||||
<li><Link href="/rsi-prevention/" className="text-zinc-400 hover:text-white transition-colors">RSI Prevention</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-semibold mb-4 text-xs uppercase tracking-wider text-zinc-400">Support</h3>
|
||||
<ul className="space-y-2.5 text-sm">
|
||||
<li><Link href="/faq/" className="text-zinc-400 hover:text-white transition-colors">FAQ</Link></li>
|
||||
<li><Link href="/troubleshooting/" className="text-zinc-400 hover:text-white transition-colors">Troubleshooting</Link></li>
|
||||
<li><Link href="/shortcuts/" className="text-zinc-400 hover:text-white transition-colors">Voice Commands</Link></li>
|
||||
<li><Link href="/alternatives/" className="text-zinc-400 hover:text-white transition-colors">Alternatives</Link></li>
|
||||
<li><Link href="/privacy/" className="text-zinc-400 hover:text-white transition-colors">Privacy Policy</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,23 +111,23 @@ export default function PrivacyPage() {
|
||||
includes:
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
No voice recordings or audio data
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
No transcribed text
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
No usage analytics or telemetry
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
No device fingerprints
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
No IP addresses or location data
|
||||
</li>
|
||||
@@ -177,15 +177,15 @@ export default function PrivacyPage() {
|
||||
Vocalinux stores configuration locally at <code className="rounded bg-zinc-100 px-1.5 py-0.5 dark:bg-zinc-700">~/.config/vocalinux/</code>:
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Settings like preferred engine, model size, and language
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Custom keyboard shortcuts
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Window and tray preferences
|
||||
</li>
|
||||
|
||||
@@ -212,19 +212,19 @@ export default function RsiPreventionPage() {
|
||||
<div>
|
||||
<h3 className="mb-3 font-semibold">Tips for Transitioning</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Start by dictating emails and documents, then expand to other tasks
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Use voice commands for punctuation to maintain natural speech flow
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Take breaks—voice dictation is still work, and vocal rest matters too
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Combine with ergonomic equipment for maximum benefit
|
||||
</li>
|
||||
@@ -233,19 +233,19 @@ export default function RsiPreventionPage() {
|
||||
<div>
|
||||
<h3 className="mb-3 font-semibold">Vocalinux Advantages</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Toggle mode or push-to-talk control—no mouse required
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
System tray indicator shows recording status
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Pleasant audio feedback confirms start/stop
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Works in ALL Linux applications—universal compatibility
|
||||
</li>
|
||||
@@ -266,7 +266,7 @@ export default function RsiPreventionPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import { ChevronRight, Command, Keyboard, Mouse, Settings, Space, Zap } from "lucide-react";
|
||||
import { ChevronRight, Keyboard, Mouse, Settings, Volume2, Zap } from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
@@ -12,6 +12,16 @@ const shortcutCategories = [
|
||||
shortcuts: [
|
||||
{ keys: ["Ctrl", "Ctrl"], action: "Toggle mode: start/stop dictation", note: "Default mode" },
|
||||
{ keys: ["Hold Ctrl"], action: "Push-to-talk mode: speak while held", note: "Release to stop" },
|
||||
{
|
||||
keys: ["Settings"],
|
||||
action: "Choose Left Ctrl vs Right Ctrl in Settings for more control",
|
||||
note: "Modifier key side selection",
|
||||
},
|
||||
{
|
||||
keys: ["PTT"],
|
||||
action: "v0.10.1+ includes fixes for premature transcription during PTT silence",
|
||||
note: "Improved push-to-talk reliability",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -28,6 +38,28 @@ const shortcutCategories = [
|
||||
{ keys: ["\"undo\""], action: "Undo last action", note: "Voice command" },
|
||||
{ keys: ["\"redo\""], action: "Redo last action", note: "Voice command" },
|
||||
{ keys: ["Settings Toggle"], action: "Enable/disable voice commands", note: "Optional in v0.8.0-beta" },
|
||||
{
|
||||
keys: ["Voice Commands"],
|
||||
action: "Voice commands are optional since v0.8.0 - auto-enabled for VOSK, disabled for Whisper by default",
|
||||
note: "Engine-specific defaults",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: "Audio Feedback",
|
||||
icon: Volume2,
|
||||
iconColor: "text-sky-500",
|
||||
shortcuts: [
|
||||
{
|
||||
keys: ["Settings Toggle"],
|
||||
action: "Enable/disable sound effects from Settings dialog",
|
||||
note: "Audio feedback control",
|
||||
},
|
||||
{
|
||||
keys: ["v0.9.0+"],
|
||||
action: "Sound effects now toggleable - smooth gliding tones for start/stop",
|
||||
note: "Pleasant audio cues",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -79,7 +111,7 @@ export default function ShortcutsPage() {
|
||||
headline: "Vocalinux Keyboard Shortcuts & Voice Commands",
|
||||
description:
|
||||
"Complete guide to Vocalinux keyboard shortcuts and voice commands for efficient Linux voice dictation.",
|
||||
dateModified: "2026-03-01",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
|
||||
@@ -80,16 +80,38 @@ const troubleshootingItems = [
|
||||
"On Wayland, ensure keyboard shortcuts are not blocked by compositor settings",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Vocalinux stops working after system suspend/resume",
|
||||
symptoms: [
|
||||
"Dictation doesn't start after laptop wakes",
|
||||
"Keyboard shortcuts stop working",
|
||||
"App appears running but not responding",
|
||||
],
|
||||
solutions: [
|
||||
"v0.10.1+ automatically recovers after suspend - ensure you're on latest version",
|
||||
"Check system tray icon status after resume",
|
||||
"If issues persist, restart Vocalinux from tray menu",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Tray icon not appearing",
|
||||
symptoms: ["No system tray icon", "Cannot access settings"],
|
||||
solutions: [
|
||||
"v0.10.1+ bundles resources to prevent missing tray icons - ensure you're on latest version",
|
||||
"Ensure your desktop environment supports system trays (GNOME requires extension)",
|
||||
"Install AppIndicator extension for GNOME: sudo apt install gnome-shell-extension-appindicator",
|
||||
"Check if ayatana-appindicator is installed: sudo apt install libayatana-appindicator3-1",
|
||||
"Try launching from terminal to see any errors: vocalinux --debug",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Settings dialog missing close button",
|
||||
symptoms: ["Cannot close settings window", "Close button not visible"],
|
||||
solutions: [
|
||||
"v0.10.1+ includes dedicated close button - update to latest version",
|
||||
"Settings dialog now forces window decorations",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
@@ -112,7 +134,7 @@ export default function TroubleshootingPage() {
|
||||
headline: "Vocalinux Troubleshooting Guide",
|
||||
description:
|
||||
"Complete troubleshooting guide for Vocalinux Linux voice dictation software.",
|
||||
dateModified: "2026-02-19",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
@@ -204,7 +226,7 @@ export default function TroubleshootingPage() {
|
||||
Still having issues?
|
||||
</h2>
|
||||
<ul className="space-y-3 text-muted-foreground">
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<ChevronRight className="h-4 w-4 text-primary" />
|
||||
Check the{" "}
|
||||
<Link href="/faq/" className="font-semibold text-primary hover:underline">
|
||||
@@ -212,7 +234,7 @@ export default function TroubleshootingPage() {
|
||||
</Link>{" "}
|
||||
for common questions
|
||||
</li>
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<ChevronRight className="h-4 w-4 text-primary" />
|
||||
<a
|
||||
href="https://github.com/jatinkrmalik/vocalinux/issues"
|
||||
@@ -224,7 +246,7 @@ export default function TroubleshootingPage() {
|
||||
<ExternalLink className="h-3.5 w-3.5" />
|
||||
</a>
|
||||
</li>
|
||||
<li className="inline-flex items-center gap-2">
|
||||
<li className="flex items-center gap-2">
|
||||
<ChevronRight className="h-4 w-4 text-primary" />
|
||||
<a
|
||||
href="https://github.com/jatinkrmalik/vocalinux/issues/new"
|
||||
|
||||
@@ -172,7 +172,7 @@ export default function UseCasesPage() {
|
||||
<h3 className="mb-3 font-semibold">Key Benefits</h3>
|
||||
<ul className="space-y-2">
|
||||
{useCase.benefits.map((benefit) => (
|
||||
<li key={benefit} className="inline-flex items-start gap-2 text-sm text-muted-foreground">
|
||||
<li key={benefit} className="flex items-start gap-2 text-sm text-muted-foreground">
|
||||
<ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
|
||||
{benefit}
|
||||
</li>
|
||||
@@ -207,7 +207,7 @@ export default function UseCasesPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import {
|
||||
Bug,
|
||||
CheckCircle2,
|
||||
ChevronRight,
|
||||
Code2,
|
||||
Keyboard,
|
||||
Lightbulb,
|
||||
MessageSquare,
|
||||
Mic,
|
||||
ToggleLeft,
|
||||
} from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const setupSteps = [
|
||||
{
|
||||
title: "Install Vocalinux",
|
||||
description:
|
||||
"Follow the install guide, launch Vocalinux, and confirm your microphone and language are configured.",
|
||||
href: "/install/",
|
||||
linkLabel: "Open installation guide",
|
||||
icon: CheckCircle2,
|
||||
},
|
||||
{
|
||||
title: "Configure your activation shortcut",
|
||||
description:
|
||||
"Pick a shortcut that does not conflict with VS Code keybindings. Most developers keep Ctrl-based activation for muscle-memory.",
|
||||
href: "/shortcuts/",
|
||||
linkLabel: "View shortcuts and commands",
|
||||
icon: Keyboard,
|
||||
},
|
||||
];
|
||||
|
||||
const workflows = [
|
||||
{
|
||||
title: "Toggle mode for quick dictation",
|
||||
description:
|
||||
"Use toggle mode when you need short bursts: comments, TODO notes, commit message drafts, or quick variable descriptions.",
|
||||
tips: [
|
||||
"Double-tap your shortcut to start and stop.",
|
||||
"Best for short phrases between normal typing.",
|
||||
"Great when context-switching between keyboard and voice.",
|
||||
],
|
||||
icon: ToggleLeft,
|
||||
iconColor: "text-blue-500",
|
||||
iconBg: "bg-blue-100 dark:bg-blue-900/30",
|
||||
},
|
||||
{
|
||||
title: "Push-to-talk for continuous coding (recommended)",
|
||||
description:
|
||||
"Use push-to-talk when writing longer explanations in docs, code reviews, and comments. Hold key to speak, release to stop.",
|
||||
tips: [
|
||||
"Keeps accidental transcription low in noisy environments.",
|
||||
"Predictable start/stop behavior for focused coding sessions.",
|
||||
"Preferred by many developers for precise control.",
|
||||
],
|
||||
icon: Mic,
|
||||
iconColor: "text-emerald-500",
|
||||
iconBg: "bg-emerald-100 dark:bg-emerald-900/30",
|
||||
},
|
||||
{
|
||||
title: "Voice commands for code editing",
|
||||
description:
|
||||
"Combine dictation with editing commands like punctuation and structure helpers to move faster through repetitive formatting.",
|
||||
tips: [
|
||||
"Use commands like \"new line\", \"tab\", and \"semicolon\".",
|
||||
"Treat voice as an editor assistant, not a full code generator.",
|
||||
"Use keyboard for dense syntax-heavy sections.",
|
||||
],
|
||||
icon: MessageSquare,
|
||||
iconColor: "text-purple-500",
|
||||
iconBg: "bg-purple-100 dark:bg-purple-900/30",
|
||||
},
|
||||
];
|
||||
|
||||
const vscodeTips = [
|
||||
"Dictate comments first: inline comments and docstrings are ideal voice targets.",
|
||||
"For strings, speak full sentence chunks and then fix quotes/escaping with keyboard.",
|
||||
"Use \"new line\" and \"tab\" commands for indentation rhythm in blocks.",
|
||||
"When writing markdown in VS Code, voice dictation is often faster than typing.",
|
||||
];
|
||||
|
||||
const troubleshootingTips = [
|
||||
"No text appears in VS Code: verify Vocalinux is active and the editor window is focused.",
|
||||
"Shortcut conflicts: remap Vocalinux activation to avoid overlapping VS Code keybinds.",
|
||||
"Unexpected punctuation: review your voice command phrases and disable commands if needed.",
|
||||
"Low accuracy: use a quieter mic setup and test another model size/engine in settings.",
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "How to Dictate in VS Code on Linux (Voice Coding Workflow)",
|
||||
description:
|
||||
"Learn how to use voice dictation in VS Code on Linux. Setup Vocalinux for programming, recommended workflows for coding, PTT vs toggle mode, and troubleshooting tips.",
|
||||
path: "/voice-typing-vscode",
|
||||
keywords: [
|
||||
"voice typing VS Code Linux",
|
||||
"voice coding linux",
|
||||
"dictate code linux",
|
||||
"speech to text programming linux",
|
||||
"vocalinux VS Code",
|
||||
],
|
||||
});
|
||||
|
||||
export default function VoiceTypingVsCodePage() {
|
||||
const articleJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: "How to Dictate in VS Code on Linux (Voice Coding Workflow)",
|
||||
description:
|
||||
"Learn how to use voice dictation in VS Code on Linux. Setup Vocalinux for programming, recommended workflows for coding, PTT vs toggle mode, and troubleshooting tips.",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
url: "https://github.com/jatinkrmalik",
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Vocalinux",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: absoluteUrl("/vocalinux.png"),
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: absoluteUrl("/voice-typing-vscode"),
|
||||
};
|
||||
|
||||
return (
|
||||
<SeoSubpageShell>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<p className="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary">
|
||||
<Code2 className="h-4 w-4" />
|
||||
VS Code Workflow
|
||||
</p>
|
||||
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
|
||||
How to Dictate in VS Code on Linux (Voice Coding Workflow)
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Learn how to use Vocalinux for practical, low-friction voice coding in VS Code on Linux.
|
||||
This guide covers setup, toggle vs push-to-talk, editing workflows, and common fixes.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 flex items-center gap-2 text-2xl font-bold">
|
||||
<Lightbulb className="h-5 w-5 text-amber-500" />
|
||||
Why use voice coding in VS Code?
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Voice dictation helps developers reduce repetitive typing, keep momentum during documentation,
|
||||
and lower hand strain. For most workflows, the sweet spot is hybrid: keyboard for syntax,
|
||||
voice for comments, explanations, notes, and refactors written in plain language.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-6 text-2xl font-bold">Setup in 2 steps</h2>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{setupSteps.map((step) => {
|
||||
const Icon = step.icon;
|
||||
return (
|
||||
<article
|
||||
key={step.title}
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-800"
|
||||
>
|
||||
<div className="mb-3 inline-flex rounded-lg bg-primary/10 p-2">
|
||||
<Icon className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<h3 className="mb-2 font-semibold">{step.title}</h3>
|
||||
<p className="mb-3 text-sm text-muted-foreground">{step.description}</p>
|
||||
<Link href={step.href} className="text-sm font-semibold text-primary hover:underline">
|
||||
{step.linkLabel}
|
||||
</Link>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-6 text-2xl font-bold">Recommended workflows</h2>
|
||||
<div className="space-y-4">
|
||||
{workflows.map((workflow) => {
|
||||
const Icon = workflow.icon;
|
||||
|
||||
return (
|
||||
<article
|
||||
key={workflow.title}
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-800"
|
||||
>
|
||||
<h3 className="mb-2 flex items-center gap-3 text-lg font-semibold">
|
||||
<span className={`rounded-lg p-2 ${workflow.iconBg}`}>
|
||||
<Icon className={`h-4 w-4 ${workflow.iconColor}`} />
|
||||
</span>
|
||||
{workflow.title}
|
||||
</h3>
|
||||
<p className="mb-3 text-sm text-muted-foreground">{workflow.description}</p>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
{workflow.tips.map((tip) => (
|
||||
<li key={tip} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
|
||||
<span>{tip}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-4 text-2xl font-bold">VS Code-specific tips</h2>
|
||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||
{vscodeTips.map((tip) => (
|
||||
<li key={tip} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
|
||||
<span>{tip}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-orange-200 bg-orange-50 p-6 dark:border-orange-800 dark:bg-orange-900/20">
|
||||
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold text-orange-800 dark:text-orange-300">
|
||||
<Bug className="h-5 w-5" />
|
||||
Troubleshooting
|
||||
</h2>
|
||||
<ul className="space-y-3 text-sm text-orange-700 dark:text-orange-300">
|
||||
{troubleshootingTips.map((tip) => (
|
||||
<li key={tip} className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0" />
|
||||
<span>{tip}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="mt-5 flex flex-wrap gap-4 text-sm">
|
||||
<Link href="/for-developers/" className="font-semibold text-primary hover:underline">
|
||||
Developer guide
|
||||
</Link>
|
||||
<Link href="/shortcuts/" className="font-semibold text-primary hover:underline">
|
||||
Shortcuts and commands
|
||||
</Link>
|
||||
<Link href="/install/" className="font-semibold text-primary hover:underline">
|
||||
Installation docs
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-primary/20 bg-primary/5 p-8">
|
||||
<h2 className="mb-4 text-2xl font-bold">Ready to voice-type in VS Code?</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Install Vocalinux and use a hybrid keyboard + voice workflow that fits real-world Linux
|
||||
development.
|
||||
</p>
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</section>
|
||||
</SeoSubpageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import {
|
||||
CheckCircle2,
|
||||
ChevronRight,
|
||||
Command,
|
||||
Cpu,
|
||||
Gauge,
|
||||
Headphones,
|
||||
PanelTop,
|
||||
Trophy,
|
||||
Waypoints,
|
||||
} from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const comparisonRows = [
|
||||
{
|
||||
feature: "Interface and daily workflow",
|
||||
vocalinux: "Full GTK GUI + tray controls + keyboard shortcuts",
|
||||
nerdDictation: "CLI-first terminal workflow",
|
||||
winner: "Vocalinux",
|
||||
icon: PanelTop,
|
||||
},
|
||||
{
|
||||
feature: "Offline voice dictation",
|
||||
vocalinux: "100% offline with multiple engine options",
|
||||
nerdDictation: "Offline dictation, single CLI-centered flow",
|
||||
winner: "Vocalinux",
|
||||
icon: Command,
|
||||
},
|
||||
{
|
||||
feature: "Wayland text injection",
|
||||
vocalinux: "Native IBus integration for dictation in any app",
|
||||
nerdDictation: "IBus-based text injection on Wayland",
|
||||
winner: "Vocalinux",
|
||||
icon: Waypoints,
|
||||
},
|
||||
{
|
||||
feature: "GPU acceleration",
|
||||
vocalinux: "Vulkan acceleration across AMD, Intel, and NVIDIA",
|
||||
nerdDictation: "CPU-focused workflow",
|
||||
winner: "Vocalinux",
|
||||
icon: Cpu,
|
||||
},
|
||||
{
|
||||
feature: "Push-to-talk",
|
||||
vocalinux: "Built-in push-to-talk and toggle mode",
|
||||
nerdDictation: "Terminal-first trigger workflow",
|
||||
winner: "Vocalinux",
|
||||
icon: Gauge,
|
||||
},
|
||||
{
|
||||
feature: "Audio feedback",
|
||||
vocalinux: "Built-in pleasant sound effects for start/stop/error",
|
||||
nerdDictation: "No native desktop sound-feedback layer",
|
||||
winner: "Vocalinux",
|
||||
icon: Headphones,
|
||||
},
|
||||
{
|
||||
feature: "System tray integration",
|
||||
vocalinux: "Tray icon with status, controls, and settings access",
|
||||
nerdDictation: "No native system tray app",
|
||||
winner: "Vocalinux",
|
||||
icon: Trophy,
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "Vocalinux vs Nerd Dictation: Best Offline Voice Dictation for Linux",
|
||||
description:
|
||||
"Comparing Vocalinux and Nerd Dictation for offline Linux voice dictation. CLI vs GUI, GPU vs CPU-only, Wayland support, and more.",
|
||||
path: "/vs-nerd-dictation",
|
||||
keywords: [
|
||||
"vocalinux vs nerd dictation",
|
||||
"linux voice dictation comparison",
|
||||
"offline speech to text linux",
|
||||
],
|
||||
});
|
||||
|
||||
export default function VsNerdDictationPage() {
|
||||
const articleJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: "Vocalinux vs Nerd Dictation: Best Offline Voice Dictation for Linux",
|
||||
description:
|
||||
"Comparing Vocalinux and Nerd Dictation for offline Linux voice dictation. CLI vs GUI, GPU vs CPU-only, Wayland support, and more.",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
url: "https://github.com/jatinkrmalik",
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Vocalinux",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: absoluteUrl("/vocalinux.png"),
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: absoluteUrl("/vs-nerd-dictation"),
|
||||
};
|
||||
|
||||
return (
|
||||
<SeoSubpageShell>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<p className="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary">
|
||||
<Trophy className="h-4 w-4" />
|
||||
Vocalinux Comparison
|
||||
</p>
|
||||
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
|
||||
Vocalinux vs Nerd Dictation
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Nerd Dictation is a popular offline CLI tool for Linux. But if you want a complete desktop
|
||||
dictation experience with GUI controls, tray integration, push-to-talk, and broad GPU support,
|
||||
Vocalinux is the better choice.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="overflow-x-auto rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="px-4 pb-2 pt-5 text-2xl font-bold">Feature-by-feature comparison</h2>
|
||||
<table className="w-full border-collapse text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-200 dark:border-zinc-700">
|
||||
<th className="px-4 py-3 text-sm font-semibold">Feature</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Vocalinux</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Nerd Dictation</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Winner</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{comparisonRows.map((row) => {
|
||||
const Icon = row.icon;
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={row.feature}
|
||||
className="border-b border-zinc-100 align-top dark:border-zinc-700/70"
|
||||
>
|
||||
<td className="px-4 py-4 font-semibold">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="rounded-md bg-primary/10 p-1.5">
|
||||
<Icon className="h-4 w-4 text-primary" />
|
||||
</span>
|
||||
{row.feature}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.vocalinux}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.nerdDictation}</td>
|
||||
<td className="px-4 py-4 text-sm font-semibold text-green-600 dark:text-green-400">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
{row.winner}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="mt-10 rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
<h2 className="mb-4 text-2xl font-bold">Go deeper</h2>
|
||||
<div className="grid gap-3 text-sm text-muted-foreground sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Link href="/offline/" className="font-semibold text-primary hover:underline">
|
||||
Why offline dictation matters
|
||||
</Link>
|
||||
<Link href="/gpu-acceleration/" className="font-semibold text-primary hover:underline">
|
||||
Vulkan GPU acceleration details
|
||||
</Link>
|
||||
<Link href="/wayland/" className="font-semibold text-primary hover:underline">
|
||||
Wayland + IBus integration guide
|
||||
</Link>
|
||||
<Link href="/compare/" className="font-semibold text-primary hover:underline">
|
||||
Engine comparison (whisper.cpp vs Whisper vs VOSK)
|
||||
</Link>
|
||||
<Link href="/for-developers/" className="font-semibold text-primary hover:underline">
|
||||
Developer docs and architecture
|
||||
</Link>
|
||||
<Link href="/rsi-prevention/" className="font-semibold text-primary hover:underline">
|
||||
RSI prevention with voice typing
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-10 rounded-2xl border border-primary/20 bg-primary/5 p-8">
|
||||
<h2 className="mb-4 text-2xl font-bold">Ready to switch from terminal-only dictation?</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Install Vocalinux for a native Linux voice dictation workflow with offline privacy, better
|
||||
usability, and stronger performance options.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/compare/"
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-zinc-300 bg-white px-4 py-2 text-sm font-semibold hover:bg-zinc-50 dark:border-zinc-600 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
Engine comparison
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
</SeoSubpageShell>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import { CheckCircle2, ChevronRight, Monitor, Terminal, Zap } from "lucide-react";
|
||||
import { CheckCircle2, ChevronRight, Keyboard, Monitor, Terminal, Zap } from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
@@ -11,6 +11,12 @@ const waylandFeatures = [
|
||||
"Text injection via IBus engine works seamlessly on Wayland compositors like GNOME, KDE, Sway, and Hyprland.",
|
||||
icon: Terminal,
|
||||
},
|
||||
{
|
||||
title: "Keyboard Layout Preservation",
|
||||
description:
|
||||
"v0.10.1+ preserves your XKB keyboard layout when activating IBus. No more switching to US layout unexpectedly.",
|
||||
icon: Keyboard,
|
||||
},
|
||||
{
|
||||
title: "X11 Fallback Support",
|
||||
description:
|
||||
@@ -109,7 +115,7 @@ export default function WaylandPage() {
|
||||
headline: "Wayland Voice Dictation Support for Linux",
|
||||
description:
|
||||
"Complete guide to Vocalinux Wayland support. Voice dictation for GNOME, KDE, Sway, Hyprland, and other Wayland compositors.",
|
||||
dateModified: "2026-02-22",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
@@ -218,6 +224,10 @@ export default function WaylandPage() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
If text doesn't appear, ensure IBus is your active input method (System Settings →
|
||||
Keyboard → Input Methods).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-blue-200 bg-blue-50 p-6 dark:border-blue-800 dark:bg-blue-900/20">
|
||||
@@ -229,21 +239,26 @@ export default function WaylandPage() {
|
||||
model requires a different approach. Vocalinux uses:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-blue-700 dark:text-blue-400">
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>IBus Engine</strong> — A custom IBus input method that receives text from
|
||||
Vocalinux and types it into the focused application
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>wtype</strong> — Native Wayland typing tool for compositors that support the
|
||||
virtual keyboard protocol
|
||||
</li>
|
||||
<li className="inline-flex items-start gap-2">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>X11 Fallback</strong> — xdotool for XWayland applications
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mt-4 text-sm text-blue-700 dark:text-blue-400">
|
||||
v0.9.0+ includes clipboard fallback for Wayland compositors without virtual keyboard
|
||||
support. Copy-to-clipboard is now disabled by default for privacy - enable in Settings
|
||||
if needed.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-zinc-200 bg-zinc-50 p-8 dark:border-zinc-700 dark:bg-zinc-900/60">
|
||||
@@ -255,7 +270,7 @@ export default function WaylandPage() {
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Now
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import {
|
||||
ArrowRight,
|
||||
CheckCircle2,
|
||||
Cpu,
|
||||
Gauge,
|
||||
Rocket,
|
||||
Sparkles,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const modelComparison = [
|
||||
{
|
||||
model: "Tiny",
|
||||
size: "~39MB",
|
||||
ramCpu: "~0.8-1.2GB",
|
||||
ramGpu: "~0.5-0.8GB VRAM",
|
||||
accuracy: "Good",
|
||||
speed: "Fastest",
|
||||
bestFor: "Older laptops, lowest latency, quick notes",
|
||||
},
|
||||
{
|
||||
model: "Base",
|
||||
size: "~74MB",
|
||||
ramCpu: "~1.2-1.8GB",
|
||||
ramGpu: "~0.8-1.2GB VRAM",
|
||||
accuracy: "Good+",
|
||||
speed: "Very fast",
|
||||
bestFor: "Balanced real-time dictation on most systems",
|
||||
},
|
||||
{
|
||||
model: "Small",
|
||||
size: "~244MB",
|
||||
ramCpu: "~2-3GB",
|
||||
ramGpu: "~1.5-2.5GB VRAM",
|
||||
accuracy: "High",
|
||||
speed: "Fast",
|
||||
bestFor: "Most users wanting stronger punctuation and names",
|
||||
},
|
||||
{
|
||||
model: "Medium",
|
||||
size: "~769MB",
|
||||
ramCpu: "~4-6GB",
|
||||
ramGpu: "~3-5GB VRAM",
|
||||
accuracy: "Very high",
|
||||
speed: "Moderate",
|
||||
bestFor: "Accuracy-focused dictation, technical writing",
|
||||
},
|
||||
{
|
||||
model: "Large",
|
||||
size: "~1.5GB",
|
||||
ramCpu: "~8GB+",
|
||||
ramGpu: "~6-10GB VRAM",
|
||||
accuracy: "Best",
|
||||
speed: "Slowest",
|
||||
bestFor: "Maximum quality with strong GPU hardware",
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "Whisper Model Sizes Explained: Tiny, Base, Small, Medium, Large for Linux",
|
||||
description:
|
||||
"Complete guide to Whisper model sizes for Linux voice dictation. Compare tiny vs base vs small vs medium vs large models - download size, RAM usage, accuracy, GPU requirements, and speed.",
|
||||
path: "/whisper-model-guide",
|
||||
keywords: [
|
||||
"whisper model sizes",
|
||||
"whisper tiny vs base",
|
||||
"which whisper model",
|
||||
"whisper.cpp models",
|
||||
"linux dictation model selection",
|
||||
],
|
||||
});
|
||||
|
||||
export default function WhisperModelGuidePage() {
|
||||
const articleJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: "Whisper Model Sizes Explained: Tiny, Base, Small, Medium, Large for Linux",
|
||||
description:
|
||||
"Complete guide to Whisper model sizes for Linux voice dictation. Compare tiny vs base vs small vs medium vs large models for size, memory, speed, and accuracy.",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
url: "https://github.com/jatinkrmalik",
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Vocalinux",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: absoluteUrl("/vocalinux.png"),
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: absoluteUrl("/whisper-model-guide"),
|
||||
};
|
||||
|
||||
return (
|
||||
<SeoSubpageShell>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<p className="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
Whisper Model Guide
|
||||
</p>
|
||||
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
|
||||
Whisper Model Sizes Explained: Tiny, Base, Small, Medium, Large for Linux
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Picking the right model is the biggest performance decision for Linux voice dictation.
|
||||
Use this guide to choose between tiny, base, small, medium, and large based on RAM,
|
||||
speed, and real-world transcription accuracy in Vocalinux.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="overflow-x-auto rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<table className="w-full border-collapse text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-200 dark:border-zinc-700">
|
||||
<th className="px-4 py-3 text-sm font-semibold">Model</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Size</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">RAM (CPU)</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">RAM (GPU)</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Accuracy</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Speed</th>
|
||||
<th className="px-4 py-3 text-sm font-semibold">Best For</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{modelComparison.map((row) => (
|
||||
<tr
|
||||
key={row.model}
|
||||
className="border-b border-zinc-100 align-top dark:border-zinc-700/70"
|
||||
>
|
||||
<td className="px-4 py-4 font-semibold">{row.model}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.size}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.ramCpu}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.ramGpu}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.accuracy}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.speed}</td>
|
||||
<td className="px-4 py-4 text-sm text-muted-foreground">{row.bestFor}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="mt-12">
|
||||
<h2 className="mb-6 text-2xl font-bold">Which model should you choose?</h2>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 text-xl font-semibold">
|
||||
<Zap className="h-5 w-5 text-amber-500" />
|
||||
Tiny or Base
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Best if you care about low latency over absolute transcription quality.
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Great for older CPUs, battery-conscious laptops, and quick chat replies.
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 text-xl font-semibold">
|
||||
<Gauge className="h-5 w-5 text-blue-500" />
|
||||
Small (recommended default)
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Best balance for most Linux dictation workflows.
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Better punctuation and proper nouns while staying near real-time.
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 text-xl font-semibold">
|
||||
<Cpu className="h-5 w-5 text-violet-500" />
|
||||
Medium
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Use when accuracy is more important than response time.
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Ideal for technical dictation, multilingual speech, and noisy environments.
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 text-xl font-semibold">
|
||||
<Rocket className="h-5 w-5 text-rose-500" />
|
||||
Large
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Choose only if you have strong GPU hardware and want maximum quality.
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Best for polished long-form writing where errors are costly.
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-12 grid gap-6 md:grid-cols-3">
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-xl font-semibold">Need more speed?</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Enable GPU acceleration to run larger models faster, especially medium and large.
|
||||
</p>
|
||||
<Link href="/gpu-acceleration/" className="inline-flex items-center gap-1 text-primary hover:underline">
|
||||
GPU acceleration guide <ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-xl font-semibold">Comparing engines?</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
See whisper.cpp vs Whisper vs VOSK before deciding your full dictation stack.
|
||||
</p>
|
||||
<Link href="/compare/" className="inline-flex items-center gap-1 text-primary hover:underline">
|
||||
Engine comparison <ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-3 text-xl font-semibold">Ready to install?</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Use the install guide to get Vocalinux running with the model size that fits your hardware.
|
||||
</p>
|
||||
<Link href="/install/" className="inline-flex items-center gap-1 text-primary hover:underline">
|
||||
Installation guide <ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section className="mt-12 rounded-2xl border border-primary/30 bg-primary/10 p-8">
|
||||
<h2 className="mb-4 text-2xl font-bold">Install Vocalinux and start dictating today</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Start with <strong>small</strong> if unsure, then move up or down after a day of use.
|
||||
Vocalinux makes model changes simple, so you can tune for your exact speed/accuracy
|
||||
preference.
|
||||
</p>
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</section>
|
||||
</SeoSubpageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
import Link from "next/link";
|
||||
import { type Metadata } from "next";
|
||||
import {
|
||||
BookOpen,
|
||||
ChevronRight,
|
||||
FileText,
|
||||
Globe,
|
||||
Heart,
|
||||
Lock,
|
||||
Mic,
|
||||
PenLine,
|
||||
ToggleLeft,
|
||||
} from "lucide-react";
|
||||
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
|
||||
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
|
||||
|
||||
const writingModes = [
|
||||
{
|
||||
title: "Toggle mode for natural writing flow",
|
||||
description:
|
||||
"Use toggle mode for long-form drafting. Start dictation, speak continuously, and stay in creative flow while writing chapters, essays, and long notes.",
|
||||
icon: ToggleLeft,
|
||||
iconColor: "text-violet-500",
|
||||
iconBg: "bg-violet-100 dark:bg-violet-900/30",
|
||||
},
|
||||
{
|
||||
title: "Push-to-talk for structured writing",
|
||||
description:
|
||||
"Use push-to-talk when precision matters: outlines, headlines, bullet lists, or source notes. Hold to speak, release to review and edit.",
|
||||
icon: Mic,
|
||||
iconColor: "text-blue-500",
|
||||
iconBg: "bg-blue-100 dark:bg-blue-900/30",
|
||||
},
|
||||
];
|
||||
|
||||
const writingCommands = [
|
||||
"new line",
|
||||
"new paragraph",
|
||||
"period",
|
||||
"comma",
|
||||
"question mark",
|
||||
"colon",
|
||||
"quote",
|
||||
"delete that",
|
||||
"undo",
|
||||
"capitalize",
|
||||
];
|
||||
|
||||
const integrations = [
|
||||
{
|
||||
app: "Obsidian",
|
||||
description:
|
||||
"Capture ideas directly into your vault, journal entries, or daily notes without breaking thought flow.",
|
||||
},
|
||||
{
|
||||
app: "Notion",
|
||||
description:
|
||||
"Draft blog outlines, content calendars, and research notes with hands-free voice input.",
|
||||
},
|
||||
{
|
||||
app: "LibreOffice",
|
||||
description:
|
||||
"Dictate manuscripts, essays, and article drafts in Writer with standard punctuation commands.",
|
||||
},
|
||||
{
|
||||
app: "Terminal Editors",
|
||||
description:
|
||||
"Use Vocalinux with terminal apps and editors to write notes, commit messages, and drafts quickly.",
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "Voice Dictation for Writers on Linux: Dictate Novels, Articles & Notes",
|
||||
description:
|
||||
"Complete guide to voice dictation for writers on Linux. How to dictate novels, blog posts, and notes using Vocalinux. Hands-free writing workflow tips.",
|
||||
path: "/writers",
|
||||
keywords: [
|
||||
"voice dictation for writers linux",
|
||||
"dictate novel linux",
|
||||
"voice typing obsidian linux",
|
||||
"voice to text writing linux",
|
||||
"linux dictation for authors",
|
||||
],
|
||||
});
|
||||
|
||||
export default function WritersPage() {
|
||||
const articleJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: "Voice Dictation for Writers on Linux: Dictate Novels, Articles & Notes",
|
||||
description:
|
||||
"Complete guide to voice dictation for writers on Linux. How to dictate novels, blog posts, and notes using Vocalinux. Hands-free writing workflow tips.",
|
||||
dateModified: "2026-03-30",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
url: "https://github.com/jatinkrmalik",
|
||||
},
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Vocalinux",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: absoluteUrl("/vocalinux.png"),
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: absoluteUrl("/writers"),
|
||||
};
|
||||
|
||||
return (
|
||||
<SeoSubpageShell>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<p className="mb-4 inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary">
|
||||
<PenLine className="h-4 w-4" />
|
||||
For Writers
|
||||
</p>
|
||||
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
|
||||
Voice Dictation for Writers on Linux: Dictate Novels, Articles & Notes
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Write at the speed of thought. Vocalinux helps novelists, bloggers, students, and
|
||||
journalists dictate drafts hands-free in their favorite Linux apps.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-4 text-2xl font-bold">Why Writers Should Use Voice Dictation</h2>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<div>
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 font-semibold">
|
||||
<Heart className="h-5 w-5 text-rose-500" /> RSI prevention and comfort
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Long writing sessions can strain wrists and fingers. Dictating reduces repetitive
|
||||
keyboard stress and helps you keep creating without pain.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="mb-3 inline-flex items-center gap-2 font-semibold">
|
||||
<FileText className="h-5 w-5 text-emerald-500" /> More words, less friction
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Most people speak faster than they type. Voice dictation helps produce first drafts,
|
||||
lecture notes, and article outlines faster while preserving momentum.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-6 text-2xl font-bold">Recommended Workflow for Writers</h2>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
{writingModes.map((mode) => {
|
||||
const Icon = mode.icon;
|
||||
return (
|
||||
<article
|
||||
key={mode.title}
|
||||
className="rounded-xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
|
||||
>
|
||||
<div className={`mb-3 inline-flex rounded-lg p-2.5 ${mode.iconBg}`}>
|
||||
<Icon className={`h-5 w-5 ${mode.iconColor}`} />
|
||||
</div>
|
||||
<h3 className="mb-2 font-semibold">{mode.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{mode.description}</p>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
||||
<h2 className="mb-4 text-2xl font-bold">Voice Commands Useful for Writing</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
These commands help maintain writing rhythm while dictating punctuation and formatting.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{writingCommands.map((cmd) => (
|
||||
<kbd
|
||||
key={cmd}
|
||||
className="rounded-lg border border-zinc-300 bg-zinc-100 px-2 py-1 text-xs font-mono dark:border-zinc-600 dark:bg-zinc-900"
|
||||
>
|
||||
"{cmd}"
|
||||
</kbd>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12">
|
||||
<h2 className="mb-6 text-2xl font-bold">App Integrations for Writing Workflows</h2>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{integrations.map((integration) => (
|
||||
<article
|
||||
key={integration.app}
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-800"
|
||||
>
|
||||
<h3 className="mb-2 font-semibold">{integration.app}</h3>
|
||||
<p className="text-sm text-muted-foreground">{integration.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Link
|
||||
href="/languages/"
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<Globe className="mb-3 h-6 w-6 text-blue-500" />
|
||||
<h3 className="mb-1 font-semibold">Multilingual Writing</h3>
|
||||
<p className="text-sm text-muted-foreground">See supported languages for dictation.</p>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/rsi-prevention/"
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<Heart className="mb-3 h-6 w-6 text-rose-500" />
|
||||
<h3 className="mb-1 font-semibold">Health Benefits</h3>
|
||||
<p className="text-sm text-muted-foreground">Learn how dictation helps prevent RSI.</p>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/offline/"
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<Lock className="mb-3 h-6 w-6 text-emerald-500" />
|
||||
<h3 className="mb-1 font-semibold">Privacy Benefits</h3>
|
||||
<p className="text-sm text-muted-foreground">Keep drafts private with offline dictation.</p>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/use-cases/"
|
||||
className="rounded-xl border border-zinc-200 bg-white p-5 transition-colors hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<BookOpen className="mb-3 h-6 w-6 text-violet-500" />
|
||||
<h3 className="mb-1 font-semibold">More Use Cases</h3>
|
||||
<p className="text-sm text-muted-foreground">Explore all workflows powered by Vocalinux.</p>
|
||||
</Link>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-primary/30 bg-primary/10 p-8">
|
||||
<h2 className="mb-3 text-2xl font-bold">Start Writing with Your Voice</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Install Vocalinux and start dictating novels, blog posts, academic notes, and newsroom
|
||||
drafts directly on Linux—without cloud lock-in.
|
||||
</p>
|
||||
<Link
|
||||
href="/install/"
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Install Vocalinux
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</section>
|
||||
</SeoSubpageShell>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import {
|
||||
@@ -17,10 +17,13 @@ import {
|
||||
Monitor,
|
||||
Power,
|
||||
Shield,
|
||||
Sun,
|
||||
X,
|
||||
Zap,
|
||||
Moon,
|
||||
} from "lucide-react";
|
||||
import { VocalinuxLogo } from "@/components/optimized-image";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
interface SeoSubpageShellProps {
|
||||
children: React.ReactNode;
|
||||
@@ -50,9 +53,19 @@ const navCategories = [
|
||||
items: [
|
||||
{ href: "/for-developers/", label: "For Developers", icon: Code2 },
|
||||
{ href: "/rsi-prevention/", label: "RSI Prevention", icon: Heart },
|
||||
{ href: "/writers/", label: "For Writers", icon: Heart },
|
||||
{ href: "/gnome-kde/", label: "GNOME vs KDE", icon: Monitor },
|
||||
{ href: "/use-cases/", label: "All Use Cases", icon: Zap },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Comparisons",
|
||||
items: [
|
||||
{ href: "/vs-nerd-dictation/", label: "vs Nerd Dictation", icon: BookOpen },
|
||||
{ href: "/whisper-model-guide/", label: "Whisper Models", icon: Cpu },
|
||||
{ href: "/voice-typing-vscode/", label: "VS Code", icon: Code2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Privacy & Ethics",
|
||||
items: [
|
||||
@@ -167,6 +180,60 @@ function Breadcrumbs() {
|
||||
|
||||
export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const mainNavCategories = [
|
||||
{
|
||||
label: "Get Started",
|
||||
items: [
|
||||
{ href: "/", label: "Home", icon: Home },
|
||||
{ href: "/install/", label: "Install", icon: BookOpen },
|
||||
{ href: "/changelog/", label: "Changelog", icon: Zap },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Features",
|
||||
items: [
|
||||
{ href: "/compare/", label: "Engine Comparison", icon: Cpu },
|
||||
{ href: "/whisper-model-guide/", label: "Whisper Models", icon: Cpu },
|
||||
{ href: "/wayland/", label: "Wayland", icon: Monitor },
|
||||
{ href: "/gpu-acceleration/", label: "GPU Acceleration", icon: Cpu },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Use Cases",
|
||||
items: [
|
||||
{ href: "/for-developers/", label: "For Developers", icon: Code2 },
|
||||
{ href: "/writers/", label: "For Writers", icon: Heart },
|
||||
{ href: "/voice-typing-vscode/", label: "VS Code", icon: Code2 },
|
||||
{ href: "/gnome-kde/", label: "GNOME vs KDE", icon: Monitor },
|
||||
{ href: "/rsi-prevention/", label: "RSI Prevention", icon: Heart },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Compare",
|
||||
items: [
|
||||
{ href: "/vs-nerd-dictation/", label: "vs Nerd Dictation", icon: BookOpen },
|
||||
{ href: "/alternatives/", label: "Alternatives", icon: BookOpen },
|
||||
{ href: "/offline/", label: "100% Offline", icon: Shield },
|
||||
{ href: "/open-source/", label: "Open Source", icon: Code2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Support",
|
||||
items: [
|
||||
{ href: "/faq/", label: "FAQ", icon: HelpCircle },
|
||||
{ href: "/troubleshooting/", label: "Troubleshooting", icon: Zap },
|
||||
{ href: "/shortcuts/", label: "Commands", icon: BookOpen },
|
||||
{ href: "/privacy/", label: "Privacy Policy", icon: Shield },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-zinc-50 to-white dark:from-zinc-900 dark:to-zinc-950">
|
||||
@@ -183,33 +250,48 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
|
||||
|
||||
{/* Desktop nav with dropdowns */}
|
||||
<nav className="hidden items-center gap-1 md:flex">
|
||||
{navCategories.map((category) => (
|
||||
{mainNavCategories.map((category) => (
|
||||
<DropdownMenu key={category.label} category={category} />
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setMobileMenuOpen(!mobileMenuOpen);
|
||||
}}
|
||||
className="flex items-center justify-center rounded-lg p-2 text-muted-foreground transition-colors hover:bg-zinc-100 hover:text-foreground dark:hover:bg-zinc-800 md:hidden"
|
||||
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<X className="h-6 w-6" />
|
||||
) : (
|
||||
<Menu className="h-6 w-6" />
|
||||
{/* Theme toggle and mobile menu */}
|
||||
<div className="flex items-center gap-2">
|
||||
{mounted && (
|
||||
<button
|
||||
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
||||
className="w-8 h-8 sm:w-9 sm:h-9 bg-muted hover:bg-muted/80 rounded-md flex items-center justify-center transition-all"
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
{theme === "dark" ? (
|
||||
<Sun className="h-4 w-4 text-yellow-400" />
|
||||
) : (
|
||||
<Moon className="h-4 w-4 text-slate-700" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setMobileMenuOpen(!mobileMenuOpen);
|
||||
}}
|
||||
className="flex items-center justify-center rounded-lg p-2 text-muted-foreground transition-colors hover:bg-zinc-100 hover:text-foreground dark:hover:bg-zinc-800 md:hidden"
|
||||
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<X className="h-6 w-6" />
|
||||
) : (
|
||||
<Menu className="h-6 w-6" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="border-t border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900 md:hidden">
|
||||
<nav className="max-h-[calc(100vh-4rem)] overflow-y-auto px-4 py-4">
|
||||
{navCategories.map((category) => (
|
||||
{mainNavCategories.map((category) => (
|
||||
<div key={category.label} className="mb-4">
|
||||
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
{category.label}
|
||||
@@ -253,16 +335,15 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
|
||||
</div>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground hover:bg-primary/90"
|
||||
className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black hover:bg-primary/90"
|
||||
>
|
||||
Home Page
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Footer links - all pages organized */}
|
||||
<div className="grid gap-6 border-t border-zinc-200 pt-6 dark:border-zinc-700 sm:grid-cols-2 lg:grid-cols-5">
|
||||
{navCategories.map((category) => (
|
||||
{mainNavCategories.map((category) => (
|
||||
<div key={category.label}>
|
||||
<h4 className="mb-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
{category.label}
|
||||
|
||||
Reference in New Issue
Block a user