diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12a9707..3f63bb5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -154,16 +154,24 @@ jobs: run: | VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py) echo "Syncing version: $VERSION" - # Update softwareVersion in layout.tsx - sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/layout.tsx + # Update softwareVersion where JSON-LD currently lives + if grep -q '"softwareVersion": "' web/src/app/page.tsx; then + sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/page.tsx + echo "Updated page.tsx version:" + grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/page.tsx + elif grep -q '"softwareVersion": "' web/src/app/layout.tsx; then + sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/layout.tsx + echo "Updated layout.tsx version:" + grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/layout.tsx + else + echo "::warning::No softwareVersion field found in page.tsx or layout.tsx; skipping sync" + fi # Update package.json version (remove -alpha/beta/rc suffix for npm) NPM_VERSION=${VERSION%-*} NPM_VERSION=${NPM_VERSION%-beta*} NPM_VERSION=${NPM_VERSION%-rc*} sed -i 's/"version": "[^"]*"/"version": "'"$NPM_VERSION"'"/' web/package.json # Verify the changes - echo "Updated layout.tsx version:" - grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/layout.tsx echo "Updated package.json version:" grep -oP '"version":\s*"\K[^"]+' web/package.json diff --git a/.github/workflows/unified-pipeline.yml b/.github/workflows/unified-pipeline.yml index b1bb578..98b4fc5 100644 --- a/.github/workflows/unified-pipeline.yml +++ b/.github/workflows/unified-pipeline.yml @@ -194,14 +194,22 @@ jobs: run: | VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' src/vocalinux/version.py) echo "Syncing version: $VERSION" - # Update softwareVersion in layout.tsx - sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/layout.tsx + # Update softwareVersion where JSON-LD currently lives + if grep -q '"softwareVersion": "' web/src/app/page.tsx; then + sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/page.tsx + echo "Updated page.tsx version:" + grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/page.tsx + elif grep -q '"softwareVersion": "' web/src/app/layout.tsx; then + sed -i 's/"softwareVersion": "[^"]*"/"softwareVersion": "'"$VERSION"'"/' web/src/app/layout.tsx + echo "Updated layout.tsx version:" + grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/layout.tsx + else + echo "::warning::No softwareVersion field found in page.tsx or layout.tsx; skipping sync" + fi # Update package.json version (remove -alpha suffix for npm) NPM_VERSION=${VERSION%-*} sed -i 's/"version": "[^"]*"/"version": "'"$NPM_VERSION"'"/' web/package.json # Verify the changes - echo "Updated layout.tsx version:" - grep -oP '"softwareVersion":\s*"\K[^"]+' web/src/app/layout.tsx echo "Updated package.json version:" grep -oP '"version":\s*"\K[^"]+' web/package.json diff --git a/web/public/robots.txt b/web/public/robots.txt index 8568f6e..7419fb1 100644 --- a/web/public/robots.txt +++ b/web/public/robots.txt @@ -3,6 +3,11 @@ User-agent: * Allow: / +Disallow: /api/ +Disallow: /_next/ +Disallow: /out/ +Disallow: /*?* # Sitemap location Sitemap: https://vocalinux.com/sitemap.xml +Host: vocalinux.com diff --git a/web/public/sitemap.xml b/web/public/sitemap.xml index 5775118..267e8c8 100644 --- a/web/public/sitemap.xml +++ b/web/public/sitemap.xml @@ -2,8 +2,38 @@ https://vocalinux.com/ - 2026-02-05 - monthly + 2026-02-12 + weekly 1.0 + + https://vocalinux.com/install/ + 2026-02-12 + weekly + 0.9 + + + https://vocalinux.com/install/ubuntu/ + 2026-02-12 + monthly + 0.8 + + + https://vocalinux.com/install/fedora/ + 2026-02-12 + monthly + 0.8 + + + https://vocalinux.com/install/arch/ + 2026-02-12 + monthly + 0.8 + + + https://vocalinux.com/compare/ + 2026-02-12 + monthly + 0.8 + diff --git a/web/src/app/__tests__/seo-assets.test.ts b/web/src/app/__tests__/seo-assets.test.ts new file mode 100644 index 0000000..54a855b --- /dev/null +++ b/web/src/app/__tests__/seo-assets.test.ts @@ -0,0 +1,40 @@ +import fs from "fs"; +import path from "path"; + +describe("SEO Assets", () => { + const robotsPath = path.join(process.cwd(), "public", "robots.txt"); + const sitemapPath = path.join(process.cwd(), "public", "sitemap.xml"); + + let robotsContent: string; + let sitemapContent: string; + + beforeAll(() => { + robotsContent = fs.readFileSync(robotsPath, "utf-8"); + sitemapContent = fs.readFileSync(sitemapPath, "utf-8"); + }); + + it("contains sitemap reference in robots.txt", () => { + expect(robotsContent).toContain("Sitemap: https://vocalinux.com/sitemap.xml"); + }); + + it("blocks crawler access to technical paths", () => { + expect(robotsContent).toContain("Disallow: /api/"); + expect(robotsContent).toContain("Disallow: /_next/"); + expect(robotsContent).toContain("Disallow: /out/"); + }); + + it("includes core landing pages in sitemap", () => { + const expectedUrls = [ + "https://vocalinux.com/", + "https://vocalinux.com/install/", + "https://vocalinux.com/install/ubuntu/", + "https://vocalinux.com/install/fedora/", + "https://vocalinux.com/install/arch/", + "https://vocalinux.com/compare/", + ]; + + expectedUrls.forEach((url) => { + expect(sitemapContent).toContain(`${url}`); + }); + }); +}); diff --git a/web/src/app/compare/page.tsx b/web/src/app/compare/page.tsx new file mode 100644 index 0000000..662070e --- /dev/null +++ b/web/src/app/compare/page.tsx @@ -0,0 +1,204 @@ +import Link from "next/link"; +import { type Metadata } from "next"; +import { CheckCircle2, Cpu, Sparkles, Zap } from "lucide-react"; +import { SeoSubpageShell } from "@/components/seo-subpage-shell"; +import { absoluteUrl, buildPageMetadata } from "@/lib/seo"; + +const engineTable = [ + { + engine: "whisper.cpp", + speed: "Fastest startup + low latency", + hardware: "CPU + AMD/Intel/NVIDIA GPU", + accuracy: "High (best overall balance)", + footprint: "Small models available (~39MB tiny)", + bestFor: "Most users who want strong speed + quality", + icon: Zap, + iconColor: "text-amber-500", + iconBg: "bg-amber-500/10", + }, + { + engine: "Whisper (OpenAI)", + speed: "Slower install and startup", + hardware: "CPU or NVIDIA CUDA", + accuracy: "High", + footprint: "Large dependency footprint (~2.3GB)", + bestFor: "Users already standardized on PyTorch stack", + icon: Sparkles, + iconColor: "text-violet-500", + iconBg: "bg-violet-500/10", + }, + { + engine: "VOSK", + speed: "Very fast realtime on low-end systems", + hardware: "CPU", + accuracy: "Good for lightweight use", + footprint: "Very lightweight (~40MB model)", + bestFor: "Older hardware and minimal-resource environments", + icon: Cpu, + iconColor: "text-cyan-500", + iconBg: "bg-cyan-500/10", + }, +]; + +export const metadata: Metadata = buildPageMetadata({ + title: "Whisper.cpp vs Whisper vs VOSK for Linux Voice Dictation", + 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.", + path: "/compare", + keywords: [ + "whisper.cpp vs vosk", + "linux speech recognition comparison", + "best voice dictation engine linux", + "whisper vs vosk linux", + ], +}); + +export default function CompareEnginesPage() { + const articleJsonLd = { + "@context": "https://schema.org", + "@type": "Article", + headline: "Whisper.cpp vs Whisper vs VOSK for Linux Voice Dictation", + description: + "Technical comparison of speech recognition engines for offline Linux voice typing.", + dateModified: "2026-02-12", + 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("/compare"), + }; + + return ( + +