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:
+30
-6
@@ -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>
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 "vocalinux[vad]"
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@@ -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't appear, ensure IBus is your active input method (System Settings →
|
||||
Keyboard → Input Methods).
|
||||
If text doesn'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's security
|
||||
model requires a different approach. Vocalinux uses:
|
||||
Unlike X11 where applications can simulate keyboard input globally,
|
||||
Wayland'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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user