docs(web): refresh beta feature documentation (#470)

* docs(web): add beta feature documentation pages

* fix(web): polish responsive documentation layout

* fix(web): restore feature card column layout

* fix(web): refine homepage beta content

* fix(web): soften hero wave gradient

* fix(web): tune hero gradient animation

* Add
This commit is contained in:
Jatin K Malik
2026-06-11 14:13:55 -07:00
committed by GitHub
parent 9c5065f452
commit bcd5313a1e
14 changed files with 2562 additions and 622 deletions
+30 -6
View File
@@ -2,7 +2,7 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://vocalinux.com/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
@@ -32,19 +32,19 @@
</url>
<url>
<loc>https://vocalinux.com/compare/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vocalinux.com/changelog/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://vocalinux.com/faq/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
@@ -62,7 +62,7 @@
</url>
<url>
<loc>https://vocalinux.com/troubleshooting/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
@@ -87,7 +87,31 @@
</url>
<url>
<loc>https://vocalinux.com/wayland/</loc>
<lastmod>2026-02-22</lastmod>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vocalinux.com/remote-api/</loc>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vocalinux.com/voice-activity-detection/</loc>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vocalinux.com/advanced-settings/</loc>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://vocalinux.com/desktop-reliability/</loc>
<lastmod>2026-06-11</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
+7 -1
View File
@@ -14,7 +14,9 @@ describe("SEO Assets", () => {
});
it("contains sitemap reference in robots.txt", () => {
expect(robotsContent).toContain("Sitemap: https://vocalinux.com/sitemap.xml");
expect(robotsContent).toContain(
"Sitemap: https://vocalinux.com/sitemap.xml",
);
});
it("blocks crawler access to technical paths", () => {
@@ -31,6 +33,10 @@ describe("SEO Assets", () => {
"https://vocalinux.com/install/fedora/",
"https://vocalinux.com/install/arch/",
"https://vocalinux.com/compare/",
"https://vocalinux.com/remote-api/",
"https://vocalinux.com/voice-activity-detection/",
"https://vocalinux.com/advanced-settings/",
"https://vocalinux.com/desktop-reliability/",
];
expectedUrls.forEach((url) => {
+244
View File
@@ -0,0 +1,244 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
AlertTriangle,
CheckCircle2,
ChevronRight,
Cpu,
RotateCcw,
Server,
Settings,
SlidersHorizontal,
Sparkles,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const whisperControls = [
{
name: "No Timestamps",
detail:
"Disables timestamp token generation to reduce timestamp-related hallucinations.",
},
{
name: "No Context",
detail:
"Stops whisper.cpp from conditioning on previous text when past context causes error loops.",
},
{
name: "Temperature",
detail:
"Controls decoding randomness. The default 0.0 keeps dictation deterministic.",
},
{
name: "Temperature Increment",
detail:
"Sets fallback steps for retry behavior, or disables fallback entirely with -1.0.",
},
{
name: "Entropy Threshold",
detail: "Helps detect repetition loops during decoding.",
},
{
name: "Logprob Threshold",
detail: "Triggers fallback when average token confidence is too low.",
},
{
name: "No-Speech Threshold",
detail: "Controls how strongly whisper.cpp treats audio as silence.",
},
{
name: "Initial Prompt",
detail:
"Adds names, jargon, punctuation style, or other context to steer transcription.",
},
];
const useCases = [
"Suppress repeated phrases or timestamp artifacts in long dictation sessions.",
"Bias transcription toward project names, technical jargon, or personal vocabulary.",
"Keep whisper.cpp deterministic for notes, code comments, and messages.",
"Move Remote Server settings out of the way until power-user mode is enabled.",
];
export const metadata: Metadata = buildPageMetadata({
title: "Advanced Whisper.cpp Settings for Linux Dictation",
description:
"Tune Vocalinux advanced whisper.cpp settings for anti-hallucination, decoding thresholds, initial prompts, and Remote API configuration.",
path: "/advanced-settings",
keywords: [
"whisper.cpp advanced settings",
"whisper anti hallucination linux dictation",
"vocalinux advanced settings",
"whisper.cpp no speech threshold",
],
});
export default function AdvancedSettingsPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Advanced Whisper.cpp Settings for Linux Dictation",
description:
"Guide to Vocalinux advanced settings for whisper.cpp decoding, anti-hallucination controls, and Remote API configuration.",
dateModified: "2026-06-11",
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("/advanced-settings"),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<section>
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<SlidersHorizontal className="h-4 w-4" />
v0.11.0 Advanced Tab
</p>
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Advanced Whisper.cpp Settings for Linux Dictation
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Vocalinux keeps the default setup simple, but power users can unlock
advanced whisper.cpp decoding controls for anti-hallucination tuning,
initial prompts, and Remote API server configuration.
</p>
</section>
<section className="mb-12 grid gap-6 md:grid-cols-3">
{[
{
icon: Sparkles,
title: "Anti-hallucination controls",
description:
"Tune timestamp, context, confidence, and silence thresholds when a specific setup needs stricter decoding.",
},
{
icon: Server,
title: "Remote Server options",
description:
"Configure the Remote API URL, optional bearer token, and endpoint format from the same advanced area.",
},
{
icon: RotateCcw,
title: "Resettable power-user mode",
description:
"Advanced options are opt-in and can be reset when experimentation produces worse dictation.",
},
].map((item) => {
const Icon = item.icon;
return (
<article
key={item.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="bg-primary/10 mb-4 inline-flex rounded-xl p-3">
<Icon className="h-6 w-6 text-primary" />
</span>
<h2 className="mb-2 text-xl font-semibold">{item.title}</h2>
<p className="text-sm text-muted-foreground">
{item.description}
</p>
</article>
);
})}
</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-5 flex items-center gap-2 text-2xl font-bold">
<Cpu className="h-5 w-5 text-primary" />
Whisper.cpp Decoding Controls
</h2>
<div className="grid gap-4 md:grid-cols-2">
{whisperControls.map((control) => (
<article
key={control.name}
className="rounded-xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-700 dark:bg-zinc-900/60"
>
<h3 className="mb-2 text-lg font-semibold">{control.name}</h3>
<p className="text-sm text-muted-foreground">{control.detail}</p>
</article>
))}
</div>
</section>
<section className="mb-12 grid gap-6 lg:grid-cols-[1fr_1.1fr]">
<div className="rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<Settings className="h-5 w-5 text-blue-500" />
When to Use Advanced Settings
</h2>
<ul className="space-y-3 text-sm text-muted-foreground">
{useCases.map((useCase) => (
<li key={useCase} className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
<span>{useCase}</span>
</li>
))}
</ul>
</div>
<div className="rounded-2xl border border-amber-500/30 bg-amber-500/10 p-6">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<AlertTriangle className="h-5 w-5 text-amber-600 dark:text-amber-400" />
Keep Defaults Unless You Need Them
</h2>
<p className="text-sm text-muted-foreground">
The default whisper.cpp settings are chosen for reliable everyday
dictation. Advanced values can improve niche workflows, but they can
also reduce accuracy if pushed too far. Change one value at a time
and reset if quality drops.
</p>
</div>
</section>
<section className="border-primary/20 bg-primary/5 rounded-2xl border p-8">
<h2 className="mb-4 text-2xl font-bold">Related Guides</h2>
<div className="grid gap-4 md:grid-cols-3">
<Link href="/voice-activity-detection/" className="group">
<h3 className="font-semibold">Silero VAD</h3>
<p className="mt-1 text-sm text-muted-foreground">
Learn how speech and silence are filtered before recognition.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
VAD guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/remote-api/" className="group">
<h3 className="font-semibold">Remote API</h3>
<p className="mt-1 text-sm text-muted-foreground">
Configure self-hosted or compatible remote transcription servers.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Remote guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/whisper-model-guide/" className="group">
<h3 className="font-semibold">Whisper model guide</h3>
<p className="mt-1 text-sm text-muted-foreground">
Choose model sizes before tuning advanced decoding behavior.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Model guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
</div>
</section>
</SeoSubpageShell>
);
}
+59 -13
View File
@@ -1,6 +1,15 @@
import Link from "next/link";
import { type Metadata } from "next";
import { BookOpen, CheckCircle2, ChevronRight, Clock, Download, Sparkles, Tag, Zap } from "lucide-react";
import {
BookOpen,
CheckCircle2,
ChevronRight,
Clock,
Download,
Sparkles,
Tag,
Zap,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
@@ -223,17 +232,20 @@ const getTypeStyles = (type: string) => {
switch (type) {
case "stable":
return {
badge: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
badge:
"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
label: "Stable",
};
case "beta":
return {
badge: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
badge:
"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
label: "Beta",
};
case "alpha":
return {
badge: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400",
badge:
"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400",
label: "Alpha",
};
default:
@@ -264,7 +276,7 @@ export default function ChangelogPage() {
headline: "Vocalinux Changelog - Release History",
description:
"Complete release history for Vocalinux, the offline voice dictation software for Linux.",
dateModified: "2026-06-07",
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
@@ -289,7 +301,7 @@ export default function ChangelogPage() {
/>
<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">
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Clock className="h-4 w-4" />
Release History
</p>
@@ -297,8 +309,8 @@ export default function ChangelogPage() {
Vocalinux Changelog
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Track every release and see how Vocalinux has evolved. From initial alpha to stable
releases, follow the journey of Linux voice dictation.
Track every release and see how Vocalinux has evolved. From initial
alpha to stable releases, follow the journey of Linux voice dictation.
</p>
</section>
@@ -321,23 +333,54 @@ export default function ChangelogPage() {
{styles.label}
</span>
{index === 0 && (
<span className="rounded-full bg-primary/10 px-3 py-1 text-xs font-semibold text-primary">
<span className="bg-primary/10 rounded-full px-3 py-1 text-xs font-semibold text-primary">
Latest
</span>
)}
<span className="text-sm text-muted-foreground">{release.date}</span>
<span className="text-sm text-muted-foreground">
{release.date}
</span>
</div>
<ul className="space-y-2">
{release.highlights.map((highlight) => (
<li key={highlight} className="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>
))}
</ul>
<div className="mt-4 pt-4 border-t border-zinc-100 dark:border-zinc-700">
{index === 0 && (
<div className="mt-5 flex flex-wrap gap-3">
{[
{ href: "/remote-api/", label: "Remote API guide" },
{
href: "/voice-activity-detection/",
label: "Silero VAD guide",
},
{ href: "/advanced-settings/", label: "Advanced settings" },
{
href: "/desktop-reliability/",
label: "Reliability overview",
},
].map((link) => (
<Link
key={link.href}
href={link.href}
className="border-primary/30 bg-primary/10 hover:bg-primary/15 inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-semibold text-primary"
>
{link.label}
<ChevronRight className="h-3.5 w-3.5" />
</Link>
))}
</div>
)}
<div className="mt-4 border-t border-zinc-100 pt-4 dark:border-zinc-700">
<a
href={`https://github.com/jatinkrmalik/vocalinux/releases/tag/${release.version}`}
target="_blank"
@@ -368,7 +411,10 @@ export default function ChangelogPage() {
<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">
<Link
href="/install/"
className="font-semibold text-primary hover:underline"
>
install guide
</Link>{" "}
for update instructions
+146 -44
View File
@@ -1,6 +1,13 @@
import Link from "next/link";
import { type Metadata } from "next";
import { CheckCircle2, Cpu, Sparkles, Zap } from "lucide-react";
import {
CheckCircle2,
ChevronRight,
Cpu,
Server,
Sparkles,
Zap,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
@@ -38,15 +45,27 @@ const engineTable = [
iconColor: "text-cyan-500",
iconBg: "bg-cyan-500/10",
},
{
engine: "Remote API",
speed: "Depends on server + network latency",
hardware: "Client CPU + remote Whisper server",
accuracy: "Depends on remote model",
footprint: "No local model required",
bestFor: "Powerful LAN servers or shared transcription backends",
icon: Server,
iconColor: "text-green-500",
iconBg: "bg-green-500/10",
},
];
export const metadata: Metadata = buildPageMetadata({
title: "Whisper.cpp vs Whisper vs VOSK for Linux Voice Dictation",
title: "Whisper.cpp vs Whisper vs VOSK vs Remote API for Linux",
description:
"Compare whisper.cpp, Whisper, and VOSK for Linux speech-to-text. See speed, hardware requirements, model size, and which engine is best for your workflow.",
"Compare whisper.cpp, Whisper, VOSK, and Remote API for Linux speech-to-text. See speed, hardware requirements, privacy tradeoffs, and best use cases.",
path: "/compare",
keywords: [
"whisper.cpp vs vosk",
"remote api speech recognition linux",
"linux speech recognition comparison",
"best voice dictation engine linux",
"whisper vs vosk linux",
@@ -57,10 +76,11 @@ export default function CompareEnginesPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: "Whisper.cpp vs Whisper vs VOSK for Linux Voice Dictation",
headline:
"Whisper.cpp vs Whisper vs VOSK vs Remote API for Linux Voice Dictation",
description:
"Technical comparison of speech recognition engines for offline Linux voice typing.",
dateModified: "2026-03-30",
"Technical comparison of local and remote speech recognition engines for Linux voice typing.",
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
@@ -85,20 +105,56 @@ export default function CompareEnginesPage() {
/>
<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">
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Sparkles className="h-4 w-4" />
Speech Engine Comparison
</p>
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
whisper.cpp vs Whisper vs VOSK on Linux
whisper.cpp vs Whisper vs VOSK vs Remote API on Linux
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
If you are choosing a Linux speech-to-text engine, this page gives a practical side-by-side
comparison focused on latency, hardware support, install footprint, and real desktop usage.
If you are choosing a Linux speech-to-text engine, this page gives a
practical side-by-side comparison focused on latency, hardware
support, install footprint, privacy boundary, and real desktop usage.
</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">
<section className="space-y-4 md:hidden">
{engineTable.map((row) => {
const Icon = row.icon;
return (
<article
key={row.engine}
className="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-800"
>
<div className="mb-4 flex items-center gap-3">
<span className={`inline-flex rounded-md p-2 ${row.iconBg}`}>
<Icon className={`h-5 w-5 ${row.iconColor}`} />
</span>
<h2 className="text-xl font-semibold">{row.engine}</h2>
</div>
<dl className="grid gap-3 text-sm">
{[
["Speed", row.speed],
["Hardware", row.hardware],
["Accuracy", row.accuracy],
["Footprint", row.footprint],
["Best for", row.bestFor],
].map(([label, value]) => (
<div key={label} className="grid gap-1">
<dt className="text-xs font-semibold uppercase text-muted-foreground">
{label}
</dt>
<dd className="text-foreground">{value}</dd>
</div>
))}
</dl>
</article>
);
})}
</section>
<section className="hidden overflow-x-auto rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-800 md:block">
<table className="w-full border-collapse text-left">
<thead>
<tr className="border-b border-zinc-200 dark:border-zinc-700">
@@ -120,17 +176,29 @@ export default function CompareEnginesPage() {
>
<td className="px-4 py-4 font-semibold">
<span className="inline-flex items-center gap-2">
<span className={`inline-flex rounded-md p-1.5 ${row.iconBg}`}>
<span
className={`inline-flex rounded-md p-1.5 ${row.iconBg}`}
>
<Icon className={`h-4 w-4 ${row.iconColor}`} />
</span>
{row.engine}
</span>
</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.hardware}</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.footprint}</td>
<td className="px-4 py-4 text-sm text-muted-foreground">{row.bestFor}</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.hardware}
</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.footprint}
</td>
<td className="px-4 py-4 text-sm text-muted-foreground">
{row.bestFor}
</td>
</tr>
);
})}
@@ -139,72 +207,106 @@ export default function CompareEnginesPage() {
</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>
<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.
You can switch between whisper.cpp, Whisper, VOSK, and Remote API from
Settings. v0.10.1+ safely stops recognition before switching to
prevent crashes. v0.12.0 adds Remote API configuration under Advanced
settings for compatible transcription servers.
</p>
</section>
<section className="mt-12 grid gap-6 md:grid-cols-3">
<section className="mt-12 grid gap-6 md:grid-cols-2 xl:grid-cols-4">
<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>
<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. Safe engine switching - v0.10.1+ stops
recognition before switching to prevent crashes.
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. Safe engine switching - v0.10.1+ stops recognition before
switching to prevent crashes.
</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 to pick Whisper</h2>
<p className="text-sm text-muted-foreground">
Choose OpenAI Whisper if your environment already depends on PyTorch/CUDA workflows and you
prefer that runtime profile.
Choose OpenAI Whisper if your environment already depends on
PyTorch/CUDA workflows and you prefer that runtime profile.
</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 to pick VOSK</h2>
<p className="text-sm text-muted-foreground">
Choose VOSK on older laptops, low-RAM systems, or lightweight VMs where small model size and
minimal overhead matter most.
Choose VOSK on older laptops, low-RAM systems, or lightweight VMs
where small model size and minimal overhead matter most.
</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 to pick Remote API
</h2>
<p className="text-sm text-muted-foreground">
Choose Remote API when a trusted server has stronger hardware,
larger models, or a shared Whisper backend. Use local engines when
your voice data must stay entirely on-device.
</p>
<Link
href="/remote-api/"
className="mt-4 inline-flex items-center gap-1 text-sm font-semibold text-primary hover:underline"
>
Remote setup <ChevronRight className="h-4 w-4" />
</Link>
</article>
</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">Next steps</h2>
<ul className="space-y-3 text-muted-foreground">
<li>
<span className="inline-flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-500" />
<li className="flex gap-2">
<CheckCircle2 className="mt-1 h-4 w-4 flex-shrink-0 text-green-500" />
<span>
Install by distro:
<Link href="/install/ubuntu/" className="font-semibold text-primary hover:underline">
<Link
href="/install/ubuntu/"
className="font-semibold text-primary hover:underline"
>
Ubuntu
</Link>
,
<Link href="/install/fedora/" className="font-semibold text-primary hover:underline">
<Link
href="/install/fedora/"
className="font-semibold text-primary hover:underline"
>
Fedora
</Link>
,
<Link href="/install/arch/" className="font-semibold text-primary hover:underline">
<Link
href="/install/arch/"
className="font-semibold text-primary hover:underline"
>
Arch Linux
</Link>
.
</span>
</li>
<li>
<span className="inline-flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-500" />
Use interactive install to detect your hardware and pick the best engine defaults.
<li className="flex gap-2">
<CheckCircle2 className="mt-1 h-4 w-4 flex-shrink-0 text-green-500" />
<span>
Use interactive install to detect your hardware and pick the best
engine defaults.
</span>
</li>
<li>
<span className="inline-flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-green-500" />
After install, tune model size for your preferred latency and accuracy level.
<li className="flex gap-2">
<CheckCircle2 className="mt-1 h-4 w-4 flex-shrink-0 text-green-500" />
<span>
After install, tune model size, VAD sensitivity, or Remote API
settings for your preferred latency and accuracy level.
</span>
</li>
</ul>
+245
View File
@@ -0,0 +1,245 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
CheckCircle2,
ChevronRight,
Keyboard,
Languages,
Monitor,
Power,
RefreshCw,
ShieldCheck,
Wrench,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const reliabilityFeatures = [
{
title: "Suspend and resume recovery",
description:
"Vocalinux restarts recognition and shortcut backends after wake so laptop sleep cycles do not require a manual app restart.",
icon: Power,
},
{
title: "Safe engine switching",
description:
"Recognition stops before engine changes, preventing crashes while moving between whisper.cpp, Whisper, VOSK, or Remote API.",
icon: RefreshCw,
},
{
title: "Keyboard layout preservation",
description:
"IBus activation preserves the current XKB layout so dictation does not unexpectedly switch to US keyboard behavior.",
icon: Keyboard,
},
{
title: "IBus runtime recovery",
description:
"Readiness probes, scoped activation, and runtime recovery improve text injection without restarting the whole app.",
icon: Monitor,
},
{
title: "Non-ASCII fallback",
description:
"ydotool can fall back to clipboard paste for accented and non-ASCII characters on paths that cannot type them directly.",
icon: Languages,
},
{
title: "Tray and settings polish",
description:
"Bundled resources, lower dialog height, and an explicit close button improve behavior across desktop environments.",
icon: Wrench,
},
];
const releaseMap = [
{
version: "v0.12.0-beta",
highlights:
"Thread safety hardening for Remote API, IBus, and text injection plus better settings behavior.",
},
{
version: "v0.11.0-beta",
highlights:
"IBus readiness probes, runtime recovery, final speech preservation, installer hardening, and distro compatibility fixes.",
},
{
version: "v0.10.2-beta",
highlights:
"Non-ASCII ydotool fallback, IBus detection on Wayland, startup fixes, and Pop!_OS/Ubuntu dependency coverage.",
},
{
version: "v0.10.1-beta",
highlights:
"Suspend/resume recovery, safe engine switching, keyboard layout preservation, tray resources, and push-to-talk reliability.",
},
];
export const metadata: Metadata = buildPageMetadata({
title: "Linux Desktop Dictation Reliability - IBus, Wayland, Suspend",
description:
"See how Vocalinux handles Linux desktop reliability across IBus, Wayland, suspend/resume recovery, keyboard layouts, tray resources, and non-ASCII text injection.",
path: "/desktop-reliability",
keywords: [
"linux dictation reliability",
"ibus voice dictation wayland",
"vocalinux suspend resume",
"wayland text injection reliability",
],
});
export default function DesktopReliabilityPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Linux Desktop Dictation Reliability",
description:
"Reliability improvements in Vocalinux for IBus, Wayland, suspend/resume, keyboard layout preservation, and text injection.",
dateModified: "2026-06-11",
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("/desktop-reliability"),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<section>
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<ShieldCheck className="h-4 w-4" />
Desktop Reliability
</p>
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Reliable Voice Dictation on Real Linux Desktops
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Recent Vocalinux beta releases focused heavily on the hard parts of
Linux desktop dictation: Wayland text injection, IBus lifecycle
behavior, suspend/resume recovery, keyboard layouts, non-ASCII text,
and settings compatibility.
</p>
</section>
<section className="mb-12 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{reliabilityFeatures.map((feature) => {
const Icon = feature.icon;
return (
<article
key={feature.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="bg-primary/10 mb-4 inline-flex rounded-xl p-3">
<Icon className="h-6 w-6 text-primary" />
</span>
<h2 className="mb-2 text-xl font-semibold">{feature.title}</h2>
<p className="text-sm text-muted-foreground">
{feature.description}
</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-5 text-2xl font-bold">Reliability Work by Release</h2>
<div className="space-y-4">
{releaseMap.map((release) => (
<article
key={release.version}
className="rounded-xl border border-zinc-200 bg-white p-5 dark:border-zinc-700 dark:bg-zinc-800"
>
<h3 className="mb-2 text-lg font-semibold">{release.version}</h3>
<p className="text-sm text-muted-foreground">
{release.highlights}
</p>
</article>
))}
</div>
</section>
<section className="mb-12 grid gap-6 lg:grid-cols-[1fr_1fr]">
<div className="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 This Matters</h2>
<ul className="space-y-3 text-sm text-muted-foreground">
{[
"Dictation should survive laptop lid closes, docking changes, and USB re-enumeration.",
"Text should appear in the focused app without breaking existing IBus engines or keyboard layouts.",
"Accented characters should be handled even when a low-level injection backend cannot type them directly.",
"Settings should fit smaller displays and window managers with different decoration behavior.",
].map((item) => (
<li key={item} className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
<span>{item}</span>
</li>
))}
</ul>
</div>
<div className="border-primary/20 bg-primary/5 rounded-2xl border p-6">
<h2 className="mb-4 text-2xl font-bold">Where to Debug Issues</h2>
<p className="mb-4 text-sm text-muted-foreground">
If dictation works in one app but not another, start with the
display server and input method path. Wayland sessions often depend
on IBus setup, while X11 paths typically use xdotool or compatible
fallbacks.
</p>
<Link
href="/troubleshooting/"
className="inline-flex items-center gap-1 text-sm font-semibold text-primary hover:underline"
>
Open troubleshooting guide <ChevronRight className="h-4 w-4" />
</Link>
</div>
</section>
<section className="border-primary/20 bg-primary/5 rounded-2xl border p-8">
<h2 className="mb-4 text-2xl font-bold">Related Guides</h2>
<div className="grid gap-4 md:grid-cols-3">
<Link href="/wayland/" className="group">
<h3 className="font-semibold">Wayland support</h3>
<p className="mt-1 text-sm text-muted-foreground">
Understand IBus, wtype, ydotool, and compositor-specific setup.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Wayland guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/shortcuts/" className="group">
<h3 className="font-semibold">Shortcut reliability</h3>
<p className="mt-1 text-sm text-muted-foreground">
Configure toggle, push-to-talk, and left/right modifier behavior.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Shortcut guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/changelog/" className="group">
<h3 className="font-semibold">Release history</h3>
<p className="mt-1 text-sm text-muted-foreground">
Review each beta release and the reliability fixes it included.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Changelog <ChevronRight className="h-4 w-4" />
</span>
</Link>
</div>
</section>
</SeoSubpageShell>
);
}
+35 -12
View File
@@ -1,6 +1,16 @@
import Link from "next/link";
import { type Metadata } from "next";
import { BookOpen, ChevronRight, CircleHelp, Cpu, Laptop, Lock, Mic, Volume2, Zap } from "lucide-react";
import {
BookOpen,
ChevronRight,
CircleHelp,
Cpu,
Laptop,
Lock,
Mic,
Volume2,
Zap,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { buildPageMetadata } from "@/lib/seo";
@@ -31,11 +41,11 @@ const faqCategories = [
questions: [
{
q: "Is Vocalinux really 100% offline?",
a: "Yes. All speech recognition runs locally on your Linux machine. Your voice data never leaves your computer. No internet connection is required after installation.",
a: "Yes for local engines. whisper.cpp, Whisper, and VOSK run on your Linux machine. Vocalinux also offers an optional Remote API engine for servers you configure yourself.",
},
{
q: "Does Vocalinux send any data to external servers?",
a: "No. Vocalinux does not collect, transmit, or store any voice data externally. Everything is processed locally using open-source speech recognition engines.",
a: "Local engines do not send voice data externally. Remote API mode sends audio only to the transcription server URL you configure.",
},
{
q: "Is my voice data stored anywhere?",
@@ -50,7 +60,7 @@ const faqCategories = [
questions: [
{
q: "Which speech recognition engines are available?",
a: "Vocalinux supports three engines: whisper.cpp (default, fastest), OpenAI Whisper (high accuracy), and VOSK (lightweight for older hardware).",
a: "Vocalinux supports whisper.cpp (default), OpenAI Whisper, VOSK, and Remote API for compatible self-hosted or trusted transcription servers.",
},
{
q: "How accurate is the transcription?",
@@ -60,6 +70,10 @@ const faqCategories = [
q: "Can I use Vocalinux in languages other than English?",
a: "Yes! whisper.cpp and Whisper support 99+ languages with automatic language detection. VOSK supports several languages with separate models.",
},
{
q: "What is Silero VAD?",
a: "Silero VAD is neural voice activity detection. Vocalinux uses it when ONNX Runtime support is available to drop silence-only buffers before recognition.",
},
],
},
{
@@ -127,7 +141,7 @@ export default function FaqPage() {
const faqJsonLd = {
"@context": "https://schema.org",
"@type": "FAQPage",
dateModified: "2026-03-30",
dateModified: "2026-06-11",
mainEntity: faqCategories.flatMap((cat) =>
cat.questions.map((q) => ({
"@type": "Question",
@@ -136,7 +150,7 @@ export default function FaqPage() {
"@type": "Answer",
text: q.a,
},
}))
})),
),
};
@@ -148,7 +162,7 @@ export default function FaqPage() {
/>
<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">
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<CircleHelp className="h-4 w-4" />
Help Center
</p>
@@ -156,8 +170,8 @@ export default function FaqPage() {
Frequently Asked Questions
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Find answers to common questions about Vocalinux. From installation to advanced features,
we've got you covered.
Find answers to common questions about Vocalinux. From installation to
advanced features, we've got you covered.
</p>
</section>
@@ -178,7 +192,10 @@ export default function FaqPage() {
<div className="space-y-6">
{category.questions.map((item, index) => (
<div key={index} className="border-b border-zinc-100 pb-6 last:border-0 last:pb-0 dark:border-zinc-700">
<div
key={index}
className="border-b border-zinc-100 pb-6 last:border-0 last:pb-0 dark:border-zinc-700"
>
<h3 className="mb-2 text-lg font-semibold">{item.q}</h3>
<p className="text-muted-foreground">{item.a}</p>
</div>
@@ -196,7 +213,10 @@ export default function FaqPage() {
<span className="inline-flex items-center gap-2">
<Laptop className="h-4 w-4 text-primary" />
Check the{" "}
<Link href="/install/" className="font-semibold text-primary hover:underline">
<Link
href="/install/"
className="font-semibold text-primary hover:underline"
>
install guide
</Link>{" "}
for setup help
@@ -206,7 +226,10 @@ export default function FaqPage() {
<span className="inline-flex items-center gap-2">
<BookOpen className="h-4 w-4 text-primary" />
See the{" "}
<Link href="/troubleshooting/" className="font-semibold text-primary hover:underline">
<Link
href="/troubleshooting/"
className="font-semibold text-primary hover:underline"
>
troubleshooting guide
</Link>{" "}
for common issues
+15 -4
View File
@@ -30,7 +30,7 @@ export const metadata: Metadata = {
template: "%s | Vocalinux",
},
description:
"Free and open-source voice dictation for Linux. Convert speech to text offline with whisper.cpp and VOSK on Ubuntu, Fedora, Arch, X11, and Wayland.",
"Free and open-source voice dictation for Linux. Convert speech to text with whisper.cpp, VOSK, Remote API servers, Silero VAD, X11, and Wayland.",
applicationName: SITE_NAME,
keywords: [
"voice dictation linux",
@@ -39,6 +39,10 @@ export const metadata: Metadata = {
"linux voice typing",
"whisper.cpp linux",
"vosk linux",
"remote api speech recognition",
"silero vad linux",
"whisper.cpp advanced settings",
"ibus voice dictation",
"wayland voice dictation",
"x11 speech recognition",
"open source dictation software",
@@ -56,7 +60,9 @@ export const metadata: Metadata = {
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
{ url: "/favicon-96x96.png", sizes: "96x96", type: "image/png" },
],
apple: [{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }],
apple: [
{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" },
],
shortcut: "/vocalinux.png",
},
manifest: "/site.webmanifest",
@@ -149,7 +155,8 @@ const webSiteJsonLd = {
"@type": "SearchAction",
target: {
"@type": "EntryPoint",
urlTemplate: "https://github.com/jatinkrmalik/vocalinux/issues?q={search_term_string}",
urlTemplate:
"https://github.com/jatinkrmalik/vocalinux/issues?q={search_term_string}",
},
"query-input": {
"@type": "PropertyValueSpecification",
@@ -167,7 +174,11 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en" className={`${GeistSans.variable} scroll-smooth`} suppressHydrationWarning>
<html
lang="en"
className={`${GeistSans.variable} scroll-smooth`}
suppressHydrationWarning
>
<head>
<link rel="preconnect" href="https://api.github.com" />
<script
+1016 -453
View File
File diff suppressed because it is too large Load Diff
+280
View File
@@ -0,0 +1,280 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
AlertTriangle,
CheckCircle2,
ChevronRight,
Globe,
Lock,
Network,
Server,
Settings,
Terminal,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const protocolOptions = [
{
name: "OpenAI-compatible",
endpoint: "/v1/audio/transcriptions",
description:
"Use servers that expose the common Whisper transcription API shape, including Speaches, LocalAI, and other compatible backends.",
},
{
name: "whisper.cpp server",
endpoint: "/inference",
description:
"Point Vocalinux at the HTTP server bundled with whisper.cpp when you want a small LAN transcription appliance.",
},
];
const workflowSteps = [
"Vocalinux records microphone audio locally as 16 kHz mono PCM.",
"Voice activity detection decides when an utterance is ready.",
"The utterance is packaged as an in-memory WAV upload.",
"The configured server returns JSON with a text field.",
"Vocalinux injects the transcription into the focused Linux app.",
];
const setupSteps = [
{
title: "Run a trusted transcription server",
description:
"Start a whisper.cpp server or an OpenAI-compatible Whisper service on a machine your Linux desktop can reach.",
},
{
title: "Open Settings -> Advanced",
description:
"Remote API is a power-user engine. Enable advanced settings, then configure the Remote Server section.",
},
{
title: "Choose the endpoint format",
description:
"Select OpenAI-compatible or whisper.cpp so Vocalinux sends the multipart fields your server expects.",
},
{
title: "Test, then dictate",
description:
"Use the connection test as a reachability check, then start dictating with the same shortcut flow as local engines.",
},
];
export const metadata: Metadata = buildPageMetadata({
title: "Remote API Speech Recognition for Linux Dictation",
description:
"Configure Vocalinux Remote API speech recognition with OpenAI-compatible Whisper servers or whisper.cpp server endpoints for Linux voice dictation.",
path: "/remote-api",
keywords: [
"remote api speech recognition linux",
"openai compatible whisper linux dictation",
"whisper.cpp server vocalinux",
"self hosted transcription linux",
],
});
export default function RemoteApiPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Remote API Speech Recognition for Linux Dictation",
description:
"How Vocalinux connects Linux voice dictation to OpenAI-compatible and whisper.cpp remote transcription servers.",
dateModified: "2026-06-11",
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("/remote-api"),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<section>
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Server className="h-4 w-4" />
v0.12.0 Remote Engine
</p>
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Remote API Speech Recognition for Linux
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Vocalinux can offload the heavy transcription step to a trusted HTTP
server while keeping desktop capture, voice activity detection,
shortcut handling, and text injection on your Linux machine.
</p>
</section>
<section className="mb-12 grid gap-6 md:grid-cols-3">
{[
{
icon: Network,
title: "Use stronger hardware",
description:
"Run larger Whisper models on a workstation, mini PC, or server while a lightweight laptop stays responsive.",
},
{
icon: Globe,
title: "Open protocol choices",
description:
"Target OpenAI-compatible transcription services or the native whisper.cpp server endpoint.",
},
{
icon: Settings,
title: "Same desktop workflow",
description:
"Remote API behaves like a Vocalinux engine, so toggle mode, push-to-talk, VAD, and text injection still work normally.",
},
].map((item) => {
const Icon = item.icon;
return (
<article
key={item.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="bg-primary/10 mb-4 inline-flex rounded-xl p-3">
<Icon className="h-6 w-6 text-primary" />
</span>
<h2 className="mb-2 text-xl font-semibold">{item.title}</h2>
<p className="text-sm text-muted-foreground">
{item.description}
</p>
</article>
);
})}
</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-5 text-2xl font-bold">
Supported Remote API Formats
</h2>
<div className="grid gap-4 md:grid-cols-2">
{protocolOptions.map((option) => (
<article
key={option.endpoint}
className="rounded-xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-700 dark:bg-zinc-900/60"
>
<h3 className="mb-2 text-lg font-semibold">{option.name}</h3>
<p className="mb-3 text-sm text-muted-foreground">
{option.description}
</p>
<code className="rounded-md bg-zinc-950 px-2.5 py-1.5 text-sm text-green-400">
{option.endpoint}
</code>
</article>
))}
</div>
</section>
<section className="mb-12 grid gap-6 lg:grid-cols-[1fr_1.1fr]">
<div className="rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<Terminal className="h-5 w-5 text-green-500" />
How the Request Flows
</h2>
<ol className="space-y-3 text-sm text-muted-foreground">
{workflowSteps.map((step, index) => (
<li key={step} className="flex gap-3">
<span className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground dark:text-black">
{index + 1}
</span>
<span>{step}</span>
</li>
))}
</ol>
</div>
<div className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<Lock className="h-5 w-5 text-blue-500" />
Privacy and Security Notes
</h2>
<ul className="space-y-3 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" />
Audio is not written to disk before upload.
</li>
<li className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
Use HTTPS and an API key outside a trusted local network.
</li>
<li className="flex items-start gap-2">
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-amber-500" />
Remote API is not the same as fully offline mode because audio is
sent to the server you configure.
</li>
</ul>
</div>
</section>
<section className="mb-12">
<h2 className="mb-5 text-2xl font-bold">Remote API Setup Path</h2>
<div className="grid gap-5 md:grid-cols-2">
{setupSteps.map((step, index) => (
<article
key={step.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="mb-4 inline-flex h-8 w-8 items-center justify-center rounded-full bg-primary text-sm font-bold text-primary-foreground dark:text-black">
{index + 1}
</span>
<h3 className="mb-2 text-xl font-semibold">{step.title}</h3>
<p className="text-sm text-muted-foreground">
{step.description}
</p>
</article>
))}
</div>
</section>
<section className="border-primary/20 bg-primary/5 rounded-2xl border p-8">
<h2 className="mb-4 text-2xl font-bold">Next Steps</h2>
<div className="grid gap-4 md:grid-cols-3">
<Link href="/advanced-settings/" className="group">
<h3 className="font-semibold">Open advanced settings</h3>
<p className="mt-1 text-sm text-muted-foreground">
See where Remote Server options live in the settings dialog.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Advanced guide <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/compare/" className="group">
<h3 className="font-semibold">Compare all engines</h3>
<p className="mt-1 text-sm text-muted-foreground">
Decide when remote transcription beats local whisper.cpp, Whisper,
or VOSK.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Engine comparison <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/privacy/" className="group">
<h3 className="font-semibold">Review privacy tradeoffs</h3>
<p className="mt-1 text-sm text-muted-foreground">
Understand how local and remote recognition differ for sensitive
dictation.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Privacy policy <ChevronRight className="h-4 w-4" />
</span>
</Link>
</div>
</section>
</SeoSubpageShell>
);
}
+126 -41
View File
@@ -1,15 +1,27 @@
import Link from "next/link";
import { type Metadata } from "next";
import { AlertTriangle, CheckCircle2, ChevronRight, ExternalLink, Lightbulb, Terminal, Wrench } from "lucide-react";
import {
AlertTriangle,
CheckCircle2,
ChevronRight,
ExternalLink,
Lightbulb,
Terminal,
Wrench,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const troubleshootingItems = [
{
title: "Vocalinux won't start",
symptoms: ["Command not found", "Module import errors", "Application crashes on launch"],
symptoms: [
"Command not found",
"Module import errors",
"Application crashes on launch",
],
solutions: [
"Ensure you ran the installer: curl -fsSL https://raw.githubusercontent.com/jatinkrmalik/vocalinux/main/install.sh | bash",
"Ensure you ran the installer from the homepage or install guide",
"Check if the virtual environment is activated: source ~/.local/share/vocalinux/venv/bin/activate",
"Verify Python version (3.9+ required): python3 --version",
"Try reinstalling: ./uninstall.sh && ./install.sh",
@@ -17,7 +29,10 @@ const troubleshootingItems = [
},
{
title: "No audio detected / microphone not working",
symptoms: ["Dictation shows no text", "Audio level indicator stays at zero"],
symptoms: [
"Dictation shows no text",
"Audio level indicator stays at zero",
],
solutions: [
"Check microphone permissions in your system settings",
"Verify microphone is detected: arecord -l (Linux)",
@@ -28,7 +43,10 @@ const troubleshootingItems = [
},
{
title: "Text not appearing in applications",
symptoms: ["Dictation works but text doesn't appear", "Text injection fails"],
symptoms: [
"Dictation works but text doesn't appear",
"Text injection fails",
],
solutions: [
"For Wayland: Ensure IBus is running and Vocalinux IBus is configured",
"For X11: Verify xdotool is installed: sudo apt install xdotool",
@@ -39,7 +57,11 @@ const troubleshootingItems = [
},
{
title: "High CPU/GPU usage",
symptoms: ["System slowdown during dictation", "Fan noise", "Laggy response"],
symptoms: [
"System slowdown during dictation",
"Fan noise",
"Laggy response",
],
solutions: [
"Use a smaller model (tiny or base instead of medium/large)",
"Enable GPU acceleration if available (Vulkan for AMD/Intel, CUDA for NVIDIA)",
@@ -59,6 +81,33 @@ const troubleshootingItems = [
"Set the correct language in settings",
],
},
{
title: "Remote API transcription fails",
symptoms: [
"Connection test fails",
"HTTP 401/403 errors",
"HTTP 404 on transcription",
],
solutions: [
"Confirm the server URL is reachable from the Vocalinux machine",
"Check whether the server expects /inference or /v1/audio/transcriptions",
"Set the API key if your server requires bearer token authentication",
"Use HTTPS and a firewall when the server is outside a trusted LAN",
],
},
{
title: "Silero VAD is not active",
symptoms: [
"Recognition tab shows amplitude VAD",
"Silence-only buffers still appear",
],
solutions: [
'Install neural VAD support: pip install "vocalinux[vad]"',
"Restart Vocalinux after installing ONNX Runtime support",
"Use the VAD sensitivity setting in Recognition to tune quiet speech detection",
"If Silero cannot load, Vocalinux falls back safely to amplitude-based VAD",
],
},
{
title: "Installation fails",
symptoms: ["Dependency errors", "Package not found", "Permission denied"],
@@ -72,7 +121,10 @@ const troubleshootingItems = [
},
{
title: "Keyboard shortcut not working",
symptoms: ["Shortcut mode doesn't start dictation", "Custom shortcuts ignored"],
symptoms: [
"Shortcut mode doesn't start dictation",
"Custom shortcuts ignored",
],
solutions: [
"Check if another application is capturing the shortcut",
"Verify Vocalinux is running (check system tray)",
@@ -134,7 +186,7 @@ export default function TroubleshootingPage() {
headline: "Vocalinux Troubleshooting Guide",
description:
"Complete troubleshooting guide for Vocalinux Linux voice dictation software.",
dateModified: "2026-03-30",
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
@@ -167,8 +219,8 @@ export default function TroubleshootingPage() {
Troubleshooting Guide
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Having issues with Vocalinux? Find solutions to common problems below. Can't find your
issue? Open a GitHub issue for help.
Having issues with Vocalinux? Find solutions to common problems below.
Can't find your issue? Open a GitHub issue for help.
</p>
</section>
@@ -200,7 +252,9 @@ export default function TroubleshootingPage() {
</p>
<ul className="ml-6 list-disc text-sm text-amber-600 dark:text-amber-300">
{item.symptoms.map((symptom) => (
<li key={symptom}>{symptom}</li>
<li key={symptom} className="break-words">
{symptom}
</li>
))}
</ul>
</div>
@@ -212,7 +266,9 @@ export default function TroubleshootingPage() {
</p>
<ul className="ml-6 list-disc text-sm text-green-600 dark:text-green-300">
{item.solutions.map((solution) => (
<li key={solution}>{solution}</li>
<li key={solution} className="break-words">
{solution}
</li>
))}
</ul>
</div>
@@ -226,37 +282,66 @@ export default function TroubleshootingPage() {
Still having issues?
</h2>
<ul className="space-y-3 text-muted-foreground">
<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">
FAQ page
</Link>{" "}
for common questions
<li className="flex items-start gap-2">
<ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
<span>
Check the{" "}
<Link
href="/faq/"
className="font-semibold text-primary hover:underline"
>
FAQ page
</Link>{" "}
for common questions
</span>
</li>
<li className="flex items-center gap-2">
<ChevronRight className="h-4 w-4 text-primary" />
<a
href="https://github.com/jatinkrmalik/vocalinux/issues"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 font-semibold text-primary hover:underline"
>
Search existing issues on GitHub
<ExternalLink className="h-3.5 w-3.5" />
</a>
<li className="flex items-start gap-2">
<ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
<span>
Review{" "}
<Link
href="/remote-api/"
className="font-semibold text-primary hover:underline"
>
Remote API setup
</Link>{" "}
or{" "}
<Link
href="/voice-activity-detection/"
className="font-semibold text-primary hover:underline"
>
Silero VAD behavior
</Link>{" "}
for newer recognition features
</span>
</li>
<li className="flex items-center gap-2">
<ChevronRight className="h-4 w-4 text-primary" />
<a
href="https://github.com/jatinkrmalik/vocalinux/issues/new"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 font-semibold text-primary hover:underline"
>
Open a new issue (include debug logs)
<ExternalLink className="h-3.5 w-3.5" />
</a>
<li className="flex items-start gap-2">
<ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
<span>
<a
href="https://github.com/jatinkrmalik/vocalinux/issues"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 font-semibold text-primary hover:underline"
>
Search existing issues on GitHub
<ExternalLink className="h-3.5 w-3.5" />
</a>
</span>
</li>
<li className="flex items-start gap-2">
<ChevronRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-primary" />
<span>
<a
href="https://github.com/jatinkrmalik/vocalinux/issues/new"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 font-semibold text-primary hover:underline"
>
Open a new issue (include debug logs)
<ExternalLink className="h-3.5 w-3.5" />
</a>
</span>
</li>
</ul>
</section>
@@ -0,0 +1,235 @@
import Link from "next/link";
import { type Metadata } from "next";
import {
Activity,
CheckCircle2,
ChevronRight,
Cpu,
Mic,
Settings,
Shield,
Volume2,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
const vadBenefits = [
{
title: "Drops silence-only buffers",
description:
"Silero VAD helps Vocalinux ignore chunks that do not contain speech before they reach the recognition engine.",
},
{
title: "Cleaner dictation sessions",
description:
"Better speech boundaries reduce empty transcriptions and lower the chance that silence becomes stray text.",
},
{
title: "Same sensitivity control",
description:
"The existing 1-5 VAD sensitivity setting works for both Silero and the amplitude fallback.",
},
];
const signalFlow = [
"Microphone audio is captured locally.",
"Vocalinux checks each short chunk for speech activity.",
"Speech chunks are kept for transcription.",
"Silence-only chunks are dropped before recognition.",
"If Silero is unavailable, amplitude-based VAD takes over.",
];
export const metadata: Metadata = buildPageMetadata({
title: "Silero VAD for Cleaner Linux Voice Dictation",
description:
"Learn how Vocalinux uses Silero neural voice activity detection to drop silence-only buffers and improve Linux speech-to-text dictation.",
path: "/voice-activity-detection",
keywords: [
"silero vad linux dictation",
"voice activity detection speech to text",
"vocalinux vad sensitivity",
"cleaner whisper dictation linux",
],
});
export default function VoiceActivityDetectionPage() {
const articleJsonLd = {
"@context": "https://schema.org",
"@type": "TechArticle",
headline: "Silero VAD for Cleaner Linux Voice Dictation",
description:
"How Vocalinux uses neural voice activity detection to separate speech from silence before transcription.",
dateModified: "2026-06-11",
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-activity-detection"),
};
return (
<SeoSubpageShell>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>
<section>
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Activity className="h-4 w-4" />
v0.12.0 Silero VAD
</p>
<h1 className="mb-5 text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Silero VAD for Cleaner Linux Voice Dictation
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Vocalinux now uses Silero neural voice activity detection when ONNX
Runtime support is available. It identifies speech before
transcription, drops silence-only buffers, and falls back safely when
the neural backend is not installed.
</p>
</section>
<section className="mb-12 grid gap-6 md:grid-cols-3">
{vadBenefits.map((benefit) => (
<article
key={benefit.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="mb-4 inline-flex rounded-xl bg-green-500/10 p-3">
<CheckCircle2 className="h-6 w-6 text-green-500" />
</span>
<h2 className="mb-2 text-xl font-semibold">{benefit.title}</h2>
<p className="text-sm text-muted-foreground">
{benefit.description}
</p>
</article>
))}
</section>
<section className="mb-12 grid gap-6 lg:grid-cols-[1.05fr_1fr]">
<div className="rounded-2xl border border-zinc-200 bg-zinc-50 p-6 dark:border-zinc-700 dark:bg-zinc-900/60">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<Volume2 className="h-5 w-5 text-primary" />
How Speech Detection Fits In
</h2>
<ol className="space-y-3 text-sm text-muted-foreground">
{signalFlow.map((step, index) => (
<li key={step} className="flex gap-3">
<span className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground dark:text-black">
{index + 1}
</span>
<span>{step}</span>
</li>
))}
</ol>
</div>
<div className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
<h2 className="mb-4 flex items-center gap-2 text-2xl font-bold">
<Settings className="h-5 w-5 text-blue-500" />
Settings and Installation
</h2>
<div className="space-y-4 text-sm text-muted-foreground">
<p>
The official installer attempts to install neural VAD support. If
ONNX Runtime is not available, Vocalinux logs the fallback and
continues with amplitude-based VAD.
</p>
<div className="rounded-lg bg-zinc-950 p-4">
<code className="text-green-400">
pip install &quot;vocalinux[vad]&quot;
</code>
</div>
<p>
The Recognition tab shows which backend is active. Higher VAD
sensitivity values are more responsive to quiet speech for both
backends.
</p>
</div>
</div>
</section>
<section className="mb-12 grid gap-6 md:grid-cols-3">
{[
{
icon: Shield,
title: "Local by design",
description:
"Silero runs on your machine. Voice activity detection does not require a cloud service.",
},
{
icon: Cpu,
title: "Small CPU-side helper",
description:
"The VAD step is a lightweight pre-filter before whisper.cpp, Whisper, VOSK, or Remote API recognition.",
},
{
icon: Mic,
title: "Useful for push-to-talk",
description:
"Cleaner speech boundaries also help short recordings and stop-on-release flows avoid silence-only output.",
},
].map((item) => {
const Icon = item.icon;
return (
<article
key={item.title}
className="rounded-2xl border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800"
>
<span className="bg-primary/10 mb-4 inline-flex rounded-xl p-3">
<Icon className="h-6 w-6 text-primary" />
</span>
<h2 className="mb-2 text-xl font-semibold">{item.title}</h2>
<p className="text-sm text-muted-foreground">
{item.description}
</p>
</article>
);
})}
</section>
<section className="border-primary/20 bg-primary/5 rounded-2xl border p-8">
<h2 className="mb-4 text-2xl font-bold">Related Guides</h2>
<div className="grid gap-4 md:grid-cols-3">
<Link href="/shortcuts/" className="group">
<h3 className="font-semibold">Shortcut modes</h3>
<p className="mt-1 text-sm text-muted-foreground">
Pair VAD with toggle or push-to-talk activation.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
View shortcuts <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/advanced-settings/" className="group">
<h3 className="font-semibold">Advanced recognition tuning</h3>
<p className="mt-1 text-sm text-muted-foreground">
Adjust whisper.cpp thresholds and anti-hallucination controls.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Tune settings <ChevronRight className="h-4 w-4" />
</span>
</Link>
<Link href="/compare/" className="group">
<h3 className="font-semibold">Engine comparison</h3>
<p className="mt-1 text-sm text-muted-foreground">
See how VAD works before local and remote recognition engines.
</p>
<span className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-primary group-hover:underline">
Compare engines <ChevronRight className="h-4 w-4" />
</span>
</Link>
</div>
</section>
</SeoSubpageShell>
);
}
+60 -25
View File
@@ -1,6 +1,14 @@
import Link from "next/link";
import { type Metadata } from "next";
import { CheckCircle2, ChevronRight, Keyboard, Monitor, Terminal, Zap } from "lucide-react";
import {
CheckCircle2,
ChevronRight,
Keyboard,
Monitor,
ShieldCheck,
Terminal,
Zap,
} from "lucide-react";
import { SeoSubpageShell } from "@/components/seo-subpage-shell";
import { absoluteUrl, buildPageMetadata } from "@/lib/seo";
@@ -8,7 +16,7 @@ const waylandFeatures = [
{
title: "Native IBus Integration",
description:
"Text injection via IBus engine works seamlessly on Wayland compositors like GNOME, KDE, Sway, and Hyprland.",
"Text injection via IBus includes readiness checks, scoped activation, and runtime recovery for modern Wayland sessions.",
icon: Terminal,
},
{
@@ -29,6 +37,12 @@ const waylandFeatures = [
"Works with GNOME, KDE Plasma, Sway, Hyprland, river, and other Wayland compositors.",
icon: Zap,
},
{
title: "Reliability Hardening",
description:
"Recent betas improve IBus startup, keyboard layout preservation, non-ASCII fallback, and suspend/resume behavior.",
icon: ShieldCheck,
},
];
const compositorSupport = [
@@ -115,7 +129,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-03-30",
dateModified: "2026-06-11",
author: {
"@type": "Person",
name: "Jatin K Malik",
@@ -140,7 +154,7 @@ export default function WaylandPage() {
/>
<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">
<p className="border-primary/30 bg-primary/10 mb-4 inline-flex items-center gap-2 rounded-full border px-4 py-1.5 text-sm font-medium text-primary">
<Monitor className="h-4 w-4" />
Wayland Support
</p>
@@ -148,12 +162,12 @@ export default function WaylandPage() {
Voice Dictation on Wayland
</h1>
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
Full voice dictation support for modern Wayland compositors. Works with GNOME, KDE Plasma,
Sway, Hyprland, and more. No X11 required.
Full voice dictation support for modern Wayland compositors. Works
with GNOME, KDE Plasma, Sway, Hyprland, and more. No X11 required.
</p>
</section>
<section className="mb-12 grid gap-6 sm:grid-cols-3">
<section className="mb-12 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
{waylandFeatures.map((feature) => {
const Icon = feature.icon;
return (
@@ -163,14 +177,18 @@ export default function WaylandPage() {
>
<Icon className="mb-3 h-8 w-8 text-primary" />
<h3 className="mb-2 font-semibold">{feature.title}</h3>
<p className="text-sm text-muted-foreground">{feature.description}</p>
<p className="text-sm text-muted-foreground">
{feature.description}
</p>
</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-6 text-2xl font-bold">Supported Wayland Compositors</h2>
<h2 className="mb-6 text-2xl font-bold">
Supported Wayland Compositors
</h2>
<div className="overflow-x-auto">
<table className="w-full text-left">
<thead>
@@ -188,7 +206,9 @@ export default function WaylandPage() {
className="border-b border-zinc-100 dark:border-zinc-700/70"
>
<td className="py-3 pr-4 font-medium">{item.name}</td>
<td className="py-3 pr-4 text-muted-foreground">{item.method}</td>
<td className="py-3 pr-4 text-muted-foreground">
{item.method}
</td>
<td className="py-3 pr-4">
<span className="inline-flex items-center gap-1.5 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700 dark:bg-green-900/30 dark:text-green-400">
<CheckCircle2 className="h-3 w-3" />
@@ -217,7 +237,9 @@ export default function WaylandPage() {
</span>
<h3 className="font-semibold">{step.title}</h3>
</div>
<p className="mb-3 ml-10 text-sm text-muted-foreground">{step.description}</p>
<p className="mb-3 ml-10 text-sm text-muted-foreground">
{step.description}
</p>
<div className="ml-10 rounded-lg bg-zinc-950 p-3">
<code className="text-sm text-green-400">{step.command}</code>
</div>
@@ -225,8 +247,8 @@ export default function WaylandPage() {
))}
</div>
<p className="mt-4 text-sm text-muted-foreground">
If text doesn&apos;t appear, ensure IBus is your active input method (System Settings →
Keyboard → Input Methods).
If text doesn&apos;t appear, ensure IBus is your active input method
(System Settings → Keyboard → Input Methods).
</p>
</section>
@@ -235,19 +257,21 @@ export default function WaylandPage() {
How Wayland Text Injection Works
</h2>
<p className="mb-4 text-sm text-blue-700 dark:text-blue-400">
Unlike X11 where applications can simulate keyboard input globally, Wayland&apos;s security
model requires a different approach. Vocalinux uses:
Unlike X11 where applications can simulate keyboard input globally,
Wayland&apos;s security model requires a different approach. Vocalinux
uses:
</p>
<ul className="space-y-2 text-sm text-blue-700 dark:text-blue-400">
<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
<strong>IBus Engine</strong> - A custom IBus input method that
receives text from Vocalinux and types it into the focused
application
</li>
<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
<strong>wtype</strong> - Native Wayland typing tool for compositors
that support the virtual keyboard protocol
</li>
<li className="flex items-start gap-2">
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
@@ -255,22 +279,27 @@ export default function WaylandPage() {
</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.
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>
<p className="mt-3 text-sm text-blue-700 dark:text-blue-400">
v0.10.2+ also improves IBus detection without legacy environment
variables and handles non-ASCII ydotool paths with clipboard paste
fallback.
</p>
</section>
<section className="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">Start Dictating on Wayland</h2>
<p className="mb-6 text-muted-foreground">
Vocalinux automatically detects your display server and configures the appropriate text
injection method. Just install and run.
Vocalinux automatically detects your display server and configures the
appropriate text injection method. Just install and run.
</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"
className="hover:bg-primary/90 inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground dark:text-black"
>
Install Now
<ChevronRight className="h-4 w-4" />
@@ -281,6 +310,12 @@ export default function WaylandPage() {
>
Troubleshooting Guide
</Link>
<Link
href="/desktop-reliability/"
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"
>
Reliability Improvements
</Link>
</div>
</section>
</SeoSubpageShell>
+64 -23
View File
@@ -5,6 +5,7 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import {
BookOpen,
Activity,
ChevronDown,
ChevronRight,
Code2,
@@ -16,6 +17,9 @@ import {
Menu,
Monitor,
Power,
Server,
ShieldCheck,
SlidersHorizontal,
Shield,
Sun,
X,
@@ -42,9 +46,25 @@ const navCategories = [
label: "Features",
items: [
{ href: "/compare/", label: "Engine Comparison", icon: Cpu },
{ href: "/remote-api/", label: "Remote API", icon: Server },
{ href: "/languages/", label: "Multilingual", icon: Globe },
{ href: "/wayland/", label: "Wayland Support", icon: Monitor },
{ href: "/gpu-acceleration/", label: "GPU Acceleration", icon: Cpu },
{
href: "/voice-activity-detection/",
label: "Silero VAD",
icon: Activity,
},
{
href: "/advanced-settings/",
label: "Advanced Settings",
icon: SlidersHorizontal,
},
{
href: "/desktop-reliability/",
label: "Desktop Reliability",
icon: ShieldCheck,
},
{ href: "/autostart/", label: "Autostart", icon: Power },
],
},
@@ -61,7 +81,11 @@ const navCategories = [
{
label: "Comparisons",
items: [
{ href: "/vs-nerd-dictation/", label: "vs Nerd Dictation", icon: BookOpen },
{
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 },
],
@@ -85,11 +109,7 @@ const navCategories = [
},
];
function DropdownMenu({
category,
}: {
category: (typeof navCategories)[0];
}) {
function DropdownMenu({ category }: { category: (typeof navCategories)[0] }) {
return (
<div className="group relative">
<button
@@ -97,12 +117,10 @@ function DropdownMenu({
className="inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-zinc-100 hover:text-foreground focus:bg-zinc-100 focus:text-foreground focus:outline-none dark:hover:bg-zinc-800 dark:focus:bg-zinc-800"
>
{category.label}
<ChevronDown
className="h-3.5 w-3.5 transition-transform group-hover:rotate-180 group-focus-within:rotate-180"
/>
<ChevronDown className="h-3.5 w-3.5 transition-transform group-focus-within:rotate-180 group-hover:rotate-180" />
</button>
<div className="pointer-events-none invisible absolute left-0 top-full z-50 mt-0 w-56 rounded-xl border border-zinc-200 bg-white p-2 opacity-0 shadow-lg transition-all duration-150 group-hover:pointer-events-auto group-hover:visible group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:visible group-focus-within:opacity-100 dark:border-zinc-700 dark:bg-zinc-800">
<div className="pointer-events-none invisible absolute left-0 top-full z-50 mt-0 w-56 rounded-xl border border-zinc-200 bg-white p-2 opacity-0 shadow-lg transition-all duration-150 group-focus-within:pointer-events-auto group-focus-within:visible group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:visible group-hover:opacity-100 dark:border-zinc-700 dark:bg-zinc-800">
{category.items.map((item) => {
const Icon = item.icon;
return (
@@ -154,20 +172,20 @@ function Breadcrumbs() {
return (
<nav className="mb-6 flex items-center gap-2 text-sm text-muted-foreground">
<Link href="/" className="hover:text-foreground transition-colors">
<Link href="/" className="transition-colors hover:text-foreground">
Home
</Link>
{breadcrumbs.map((crumb, index) => (
<React.Fragment key={crumb.href}>
<ChevronRight className="h-4 w-4" />
{index === breadcrumbs.length - 1 ? (
<span className="text-foreground font-medium">
<span className="font-medium text-foreground">
{getPageTitle(crumb.href)}
</span>
) : (
<Link
href={crumb.href}
className="hover:text-foreground transition-colors"
className="transition-colors hover:text-foreground"
>
{crumb.label}
</Link>
@@ -200,9 +218,25 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
label: "Features",
items: [
{ href: "/compare/", label: "Engine Comparison", icon: Cpu },
{ href: "/remote-api/", label: "Remote API", icon: Server },
{ href: "/whisper-model-guide/", label: "Whisper Models", icon: Cpu },
{ href: "/wayland/", label: "Wayland", icon: Monitor },
{ href: "/gpu-acceleration/", label: "GPU Acceleration", icon: Cpu },
{
href: "/voice-activity-detection/",
label: "Silero VAD",
icon: Activity,
},
{
href: "/advanced-settings/",
label: "Advanced Settings",
icon: SlidersHorizontal,
},
{
href: "/desktop-reliability/",
label: "Reliability",
icon: ShieldCheck,
},
],
},
{
@@ -218,7 +252,11 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
{
label: "Compare",
items: [
{ href: "/vs-nerd-dictation/", label: "vs Nerd Dictation", icon: BookOpen },
{
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 },
@@ -237,7 +275,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
return (
<div className="min-h-screen bg-gradient-to-b from-zinc-50 to-white dark:from-zinc-900 dark:to-zinc-950">
<header className="sticky top-0 z-50 border-b border-zinc-200/80 bg-background/95 backdrop-blur-md dark:border-zinc-800/80">
<header className="bg-background/95 sticky top-0 z-50 border-b border-zinc-200/80 backdrop-blur-md dark:border-zinc-800/80">
<div className="mx-auto flex max-w-6xl items-center justify-between px-4 py-3 sm:px-6">
<Link href="/" className="group flex items-center gap-2">
<VocalinuxLogo
@@ -249,7 +287,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
</Link>
{/* Desktop nav with dropdowns */}
<nav className="hidden items-center gap-1 md:flex">
<nav className="hidden items-center gap-1 lg:flex">
{mainNavCategories.map((category) => (
<DropdownMenu key={category.label} category={category} />
))}
@@ -260,7 +298,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
{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"
className="hover:bg-muted/80 flex h-8 w-8 items-center justify-center rounded-md bg-muted transition-all sm:h-9 sm:w-9"
aria-label="Toggle theme"
>
{theme === "dark" ? (
@@ -275,7 +313,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
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"
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 lg:hidden"
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
>
{mobileMenuOpen ? (
@@ -289,7 +327,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
{/* Mobile menu */}
{mobileMenuOpen && (
<div className="border-t border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900 md:hidden">
<div className="border-t border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900 lg:hidden">
<nav className="max-h-[calc(100vh-4rem)] overflow-y-auto px-4 py-4">
{mainNavCategories.map((category) => (
<div key={category.label} className="mb-4">
@@ -328,14 +366,17 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-6">
<div className="mb-6 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div>
<p className="text-sm font-semibold">Want the full product overview?</p>
<p className="text-sm font-semibold">
Want the full product overview?
</p>
<p className="text-sm text-muted-foreground">
Go back to the homepage for the live demo and one-command installer.
Go back to the homepage for the live demo and one-command
installer.
</p>
</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 dark:text-black hover:bg-primary/90"
className="hover:bg-primary/90 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"
>
Home Page
<ChevronRight className="h-4 w-4" />
@@ -353,7 +394,7 @@ export function SeoSubpageShell({ children }: SeoSubpageShellProps) {
<li key={item.href}>
<Link
href={item.href}
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
className="text-sm text-muted-foreground transition-colors hover:text-foreground"
>
{item.label}
</Link>