fix(web): clarify speech demo browser support (#449)
This commit is contained in:
@@ -64,7 +64,7 @@
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- GitHub CTA button — icon and text vertically centered -->
|
||||
<!-- GitHub CTA button - icon and text vertically centered -->
|
||||
<g transform="translate(80, 400)">
|
||||
<rect width="280" height="50" rx="12" fill="#18181b"/>
|
||||
<!-- GitHub icon + text centered as a group: total content ~200px wide, button 280px, offset=(280-200)/2=40 -->
|
||||
@@ -95,7 +95,7 @@
|
||||
<!-- URL branding -->
|
||||
<text x="80" y="545" font-family="'Inter', 'Helvetica Neue', Arial, sans-serif" font-size="13" fill="#a1a1aa">vocalinux.com</text>
|
||||
|
||||
<!-- ==================== RIGHT SIDE — FEATURE CARDS ==================== -->
|
||||
<!-- ==================== RIGHT SIDE - FEATURE CARDS ==================== -->
|
||||
<!-- 4 cards, each 118px tall, with 20px gap between them -->
|
||||
<!-- Total cards height: 4*118 + 3*20 = 532. Canvas usable = 630 - 8 (accent lines) = 622 -->
|
||||
<!-- Top offset: (622 - 532) / 2 + 4 = 49 -->
|
||||
@@ -103,7 +103,7 @@
|
||||
<!-- Card 1: 100% Local Processing -->
|
||||
<g transform="translate(620, 49)">
|
||||
<rect width="510" height="118" rx="16" fill="white" stroke="#e4e4e7" stroke-width="1.2"/>
|
||||
<!-- Icon — vertically centered: (118-50)/2 = 34 -->
|
||||
<!-- Icon - vertically centered: (118-50)/2 = 34 -->
|
||||
<g transform="translate(24, 34)">
|
||||
<rect width="50" height="50" rx="13" fill="#f0fdf4"/>
|
||||
<g transform="translate(13, 10)">
|
||||
@@ -111,11 +111,11 @@
|
||||
<path d="M9 12l2 2 4-4" fill="none" stroke="#16a34a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
<!-- Text block — vertically centered: icon center at 34+25=59, text block ~40px tall, starts at 59-20+6=45 -->
|
||||
<!-- Text block - vertically centered: icon center at 34+25=59, text block ~40px tall, starts at 59-20+6=45 -->
|
||||
<text x="90" y="49" font-family="'Inter', 'Helvetica Neue', Arial, sans-serif" font-size="19" font-weight="700" fill="#18181b">100% Local Processing</text>
|
||||
<text x="90" y="72" font-family="'Inter', 'Helvetica Neue', Arial, sans-serif" font-size="14" fill="#71717a">No cloud, no subscriptions, no data</text>
|
||||
<text x="90" y="89" font-family="'Inter', 'Helvetica Neue', Arial, sans-serif" font-size="14" fill="#71717a">leaves your machine</text>
|
||||
<!-- Badge — vertically centered alongside title -->
|
||||
<!-- Badge - vertically centered alongside title -->
|
||||
<rect x="418" y="37" width="72" height="26" rx="6" fill="#f0fdf4" stroke="#bbf7d0" stroke-width="1"/>
|
||||
<text x="454" y="55" font-family="'Inter', 'Helvetica Neue', Arial, sans-serif" font-size="11" font-weight="700" fill="#166534" text-anchor="middle">PRIVATE</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -151,7 +151,7 @@ export default function AutostartPage() {
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Have Vocalinux ready to go the moment you log in. Uses the Linux-standard XDG autostart
|
||||
mechanism—works with GNOME, KDE, Xfce, and all major desktop environments.
|
||||
mechanism - works with GNOME, KDE, Xfce, and all major desktop environments.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -224,7 +224,7 @@ export default function AutostartPage() {
|
||||
Yes
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
<span className="text-muted-foreground"> - </span>
|
||||
)}
|
||||
</td>
|
||||
<td className="py-3 text-muted-foreground">{de.notes}</td>
|
||||
@@ -241,7 +241,7 @@ export default function AutostartPage() {
|
||||
</h2>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-400">
|
||||
Vocalinux autostart uses <strong>XDG desktop entries</strong>, not systemd services. This
|
||||
means it starts as a regular desktop application in your graphical session—which is the
|
||||
means it starts as a regular desktop application in your graphical session - which is the
|
||||
correct way to launch GUI apps. No background services, no root required.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -10,11 +10,11 @@ const releases = [
|
||||
date: "2026-05-30",
|
||||
type: "beta",
|
||||
highlights: [
|
||||
"New Advanced Settings tab with whisper.cpp anti-hallucination parameters — temperature, no_speech_threshold, max segment length, and more (PR #415)",
|
||||
"New Advanced Settings tab with whisper.cpp anti-hallucination parameters - temperature, no_speech_threshold, max segment length, and more (PR #415)",
|
||||
"IBus engine readiness probe at startup with hardened retries (PR #391)",
|
||||
"IBus runtime failure recovery without app restart (PR #411)",
|
||||
"IBus engine instance destruction handled on keyboard layout switch (fixes #388, closes #389)",
|
||||
"Preserve final speech on stop — no more truncated transcriptions (fixes #401)",
|
||||
"Preserve final speech on stop - no more truncated transcriptions (fixes #401)",
|
||||
"Play stop sound immediately on release and after audio thread joins (PR #426, #436)",
|
||||
"Repair pywhispercpp library loading in installer (PR #433)",
|
||||
"Reduce whisper.cpp CPU threads and ensure GPU backend builds in dev mode (PR #439)",
|
||||
@@ -41,7 +41,7 @@ const releases = [
|
||||
"Add missing dependencies for Pop!_OS and Ubuntu 24.04+ including cmake, libcairo2-dev, libgirepository (PR #379)",
|
||||
"Systematic code quality refactor across 20 dimensions (PR #377)",
|
||||
"Clarify missing GNOME AppIndicator support on Debian (PR #385)",
|
||||
"Redesigned OG image for vocalinux.com — cleaner, professional, text-based layout (PR #392)",
|
||||
"Redesigned OG image for vocalinux.com - cleaner, professional, text-based layout (PR #392)",
|
||||
"Test coverage improvements: mock Notify module, tray degraded-startup, IBus socket-readiness branches (PR #384, #386, #390)",
|
||||
],
|
||||
},
|
||||
@@ -82,14 +82,14 @@ const releases = [
|
||||
date: "2026-03-14",
|
||||
type: "beta",
|
||||
highlights: [
|
||||
"Left/right modifier key distinction — choose Left Ctrl vs Right Ctrl for your shortcut",
|
||||
"Sound effects toggle — enable or disable audio feedback from Settings",
|
||||
"Wayland clipboard fallback — auto-copies text when virtual keyboard injection isn't available",
|
||||
"Display availability check — graceful error when running in headless environments",
|
||||
"Left/right modifier key distinction - choose Left Ctrl vs Right Ctrl for your shortcut",
|
||||
"Sound effects toggle - enable or disable audio feedback from Settings",
|
||||
"Wayland clipboard fallback - auto-copies text when virtual keyboard injection isn't available",
|
||||
"Display availability check - graceful error when running in headless environments",
|
||||
"Fixed unwanted leading space at the start of each new transcription session",
|
||||
"Fixed shortcut mode (toggle/push-to-talk) not applying on startup",
|
||||
"Improved Debian/pipx installation guidance and cross-distro error messages",
|
||||
"Grouped shortcut selector UI — shortcuts organised by Either/Left/Right side",
|
||||
"Grouped shortcut selector UI - shortcuts organised by Either/Left/Right side",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ const useCases = [
|
||||
{
|
||||
title: "Git Commit Messages",
|
||||
description:
|
||||
"Speak your commit messages naturally—no more brief, unhelpful commits because typing is slow.",
|
||||
"Speak your commit messages naturally - no more brief, unhelpful commits because typing is slow.",
|
||||
example: "fix authentication timeout when server is under heavy load",
|
||||
icon: Terminal,
|
||||
iconColor: "text-orange-500",
|
||||
@@ -63,7 +63,7 @@ const useCases = [
|
||||
{
|
||||
title: "Pair Programming Notes",
|
||||
description:
|
||||
"Dictate notes while pairing—keep your hands on the keyboard for code, use voice for meta.",
|
||||
"Dictate notes while pairing - keep your hands on the keyboard for code, use voice for meta.",
|
||||
example: "Remember to add unit tests for the edge case we just discussed",
|
||||
icon: Zap,
|
||||
iconColor: "text-amber-500",
|
||||
@@ -99,7 +99,7 @@ const benefits = [
|
||||
},
|
||||
{
|
||||
title: "Maintain Flow State",
|
||||
description: "Speak your thoughts as they come—no context switching to the keyboard for comments and docs.",
|
||||
description: "Speak your thoughts as they come - no context switching to the keyboard for comments and docs.",
|
||||
},
|
||||
{
|
||||
title: "Better Documentation",
|
||||
@@ -107,14 +107,14 @@ const benefits = [
|
||||
},
|
||||
{
|
||||
title: "Universal Compatibility",
|
||||
description: "Works in VS Code, JetBrains IDEs, Vim, terminal, browser—every Linux application.",
|
||||
description: "Works in VS Code, JetBrains IDEs, Vim, terminal, browser - every Linux application.",
|
||||
},
|
||||
];
|
||||
|
||||
export const metadata: Metadata = buildPageMetadata({
|
||||
title: "Voice Dictation for Developers & Coders | Vocalinux",
|
||||
description:
|
||||
"Dictate code comments, documentation, and commit messages hands-free. Voice dictation for Linux developers—works in VS Code, JetBrains, terminal, and all apps.",
|
||||
"Dictate code comments, documentation, and commit messages hands-free. Voice dictation for Linux developers - works in VS Code, JetBrains, terminal, and all apps.",
|
||||
path: "/for-developers",
|
||||
keywords: [
|
||||
"voice dictation for developers",
|
||||
@@ -168,7 +168,7 @@ export default function ForDevelopersPage() {
|
||||
Voice Dictation for Developers
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Write code comments, documentation, commit messages, and more—hands-free. Keep your hands
|
||||
Write code comments, documentation, commit messages, and more - hands-free. Keep your hands
|
||||
on the keyboard for code, use your voice for everything else.
|
||||
</p>
|
||||
</section>
|
||||
@@ -236,12 +236,12 @@ export default function ForDevelopersPage() {
|
||||
|
||||
<section className="mb-12 rounded-2xl border border-blue-200 bg-blue-50 p-6 dark:border-blue-800 dark:bg-blue-900/20">
|
||||
<h2 className="mb-3 text-xl font-bold text-blue-800 dark:text-blue-300">
|
||||
Not Voice Coding—Voice Documentation
|
||||
Not Voice Coding - Voice Documentation
|
||||
</h2>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-400">
|
||||
Vocalinux is designed for <strong>dictating text</strong>, not writing code syntax. For
|
||||
actual code, your keyboard is still faster. But for comments, docs, messages, and
|
||||
notes—voice is a game-changer. Use both together for maximum productivity.
|
||||
notes - voice is a game-changer. Use both together for maximum productivity.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ export default function GpuAccelerationPage() {
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Leverage your GPU for faster speech recognition. Vocalinux supports AMD, Intel, and NVIDIA
|
||||
GPUs via Vulkan—with automatic fallback to CPU when needed.
|
||||
GPUs via Vulkan - with automatic fallback to CPU when needed.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ export default function LanguagesPage() {
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Dictate in your native language. Vocalinux supports 9+ languages with full offline
|
||||
processing—no cloud required, no data leaves your machine.
|
||||
processing - no cloud required, no data leaves your machine.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const benefits = [
|
||||
{
|
||||
title: "No Account Required",
|
||||
description:
|
||||
"No signup, no login, no password to forget. Download and run—that's it.",
|
||||
"No signup, no login, no password to forget. Download and run - that's it.",
|
||||
icon: Lock,
|
||||
iconColor: "text-violet-500",
|
||||
iconBg: "bg-violet-100 dark:bg-violet-900/30",
|
||||
@@ -228,16 +228,16 @@ export default function OfflinePage() {
|
||||
<ul className="space-y-2 text-sm text-emerald-700 dark:text-emerald-400">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>whisper.cpp</strong> — C++ port of OpenAI Whisper, runs locally with GPU
|
||||
<strong>whisper.cpp</strong> - C++ port of OpenAI Whisper, runs locally with GPU
|
||||
acceleration
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>VOSK</strong> — Lightweight offline engine, minimal resources
|
||||
<strong>VOSK</strong> - Lightweight offline engine, minimal resources
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
<strong>OpenAI Whisper</strong> — PyTorch-based, runs on NVIDIA GPU or CPU
|
||||
<strong>OpenAI Whisper</strong> - PyTorch-based, runs on NVIDIA GPU or CPU
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mt-4 text-sm text-emerald-700 dark:text-emerald-400">
|
||||
@@ -251,7 +251,7 @@ export default function OfflinePage() {
|
||||
Verify It Yourself
|
||||
</h2>
|
||||
<p className="mb-3 text-sm text-amber-700 dark:text-amber-400">
|
||||
Don't trust—verify. Vocalinux is open source. You can:
|
||||
Don't trust - verify. Vocalinux is open source. You can:
|
||||
</p>
|
||||
<ul className="space-y-2 text-sm text-amber-700 dark:text-amber-400">
|
||||
<li className="flex items-start gap-2">
|
||||
@@ -273,7 +273,7 @@ export default function OfflinePage() {
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
Monitor with <code className="rounded bg-amber-200 px-1 dark:bg-amber-900">tcpdump</code> or{" "}
|
||||
<code className="rounded bg-amber-200 px-1 dark:bg-amber-900">wireshark</code>—you'll see silence
|
||||
<code className="rounded bg-amber-200 px-1 dark:bg-amber-900">wireshark</code> - you'll see silence
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0" />
|
||||
|
||||
@@ -276,7 +276,7 @@ export default function OpenSourcePage() {
|
||||
</h3>
|
||||
<p className="mb-4 text-sm text-blue-700 dark:text-blue-400">
|
||||
We welcome contributions! Whether it's bug fixes, features, documentation, or
|
||||
translations—join the community.
|
||||
translations - join the community.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/jatinkrmalik/vocalinux"
|
||||
|
||||
@@ -565,10 +565,11 @@ export default function HomePage() {
|
||||
<FadeInSection>
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
|
||||
Try Linux Speech-to-Text in Your Browser
|
||||
Try a Speech-to-Text Preview in Your Browser
|
||||
</h2>
|
||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
||||
Experience voice-to-text right here in your browser. Click the microphone and start speaking!
|
||||
This preview runs when your browser exposes SpeechRecognition. Vocalinux itself
|
||||
runs offline after install.
|
||||
</p>
|
||||
</div>
|
||||
</FadeInSection>
|
||||
|
||||
@@ -144,19 +144,19 @@ export default function PrivacyPage() {
|
||||
<p>When you dictate, here's exactly what happens:</p>
|
||||
<ol className="space-y-3 pl-4">
|
||||
<li className="border-l-2 border-primary pl-4">
|
||||
<strong className="text-foreground">1. Audio Capture</strong> — Your microphone
|
||||
<strong className="text-foreground">1. Audio Capture</strong> - Your microphone
|
||||
records audio temporarily in RAM
|
||||
</li>
|
||||
<li className="border-l-2 border-primary pl-4">
|
||||
<strong className="text-foreground">2. Local Processing</strong> — Audio is
|
||||
<strong className="text-foreground">2. Local Processing</strong> - Audio is
|
||||
processed by whisper.cpp, Whisper, or VOSK on your CPU/GPU
|
||||
</li>
|
||||
<li className="border-l-2 border-primary pl-4">
|
||||
<strong className="text-foreground">3. Text Output</strong> — Transcribed text is
|
||||
<strong className="text-foreground">3. Text Output</strong> - Transcribed text is
|
||||
injected into your active application
|
||||
</li>
|
||||
<li className="border-l-2 border-primary pl-4">
|
||||
<strong className="text-foreground">4. Immediate Deletion</strong> — Audio data is
|
||||
<strong className="text-foreground">4. Immediate Deletion</strong> - Audio data is
|
||||
discarded immediately after processing
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
@@ -24,7 +24,7 @@ const benefits = [
|
||||
{
|
||||
title: "Privacy for Health",
|
||||
description:
|
||||
"Your health data stays private. All voice processing happens locally—no cloud uploads, no third parties.",
|
||||
"Your health data stays private. All voice processing happens locally - no cloud uploads, no third parties.",
|
||||
icon: Shield,
|
||||
iconColor: "text-blue-500",
|
||||
iconBg: "bg-blue-100 dark:bg-blue-900/30",
|
||||
@@ -222,7 +222,7 @@ export default function RsiPreventionPage() {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Take breaks—voice dictation is still work, and vocal rest matters too
|
||||
Take breaks - voice dictation is still work, and vocal rest matters too
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
@@ -235,7 +235,7 @@ export default function RsiPreventionPage() {
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Toggle mode or push-to-talk control—no mouse required
|
||||
Toggle mode or push-to-talk control - no mouse required
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
@@ -247,7 +247,7 @@ export default function RsiPreventionPage() {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 className="mt-0.5 h-4 w-4 flex-shrink-0 text-green-500" />
|
||||
Works in ALL Linux applications—universal compatibility
|
||||
Works in ALL Linux applications - universal compatibility
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -241,17 +241,17 @@ export default function WaylandPage() {
|
||||
<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
|
||||
<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
|
||||
<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" />
|
||||
<strong>X11 Fallback</strong> — xdotool for XWayland applications
|
||||
<strong>X11 Fallback</strong> - xdotool for XWayland applications
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mt-4 text-sm text-blue-700 dark:text-blue-400">
|
||||
|
||||
@@ -247,7 +247,7 @@ export default function WritersPage() {
|
||||
<h2 className="mb-3 text-2xl font-bold">Start Writing with Your Voice</h2>
|
||||
<p className="mb-6 max-w-3xl text-muted-foreground">
|
||||
Install Vocalinux and start dictating novels, blog posts, academic notes, and newsroom
|
||||
drafts directly on Linux—without cloud lock-in.
|
||||
drafts directly on Linux - without cloud lock-in.
|
||||
</p>
|
||||
<Link
|
||||
href="/install/"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { LiveDemo } from "../live-demo";
|
||||
|
||||
describe("LiveDemo unsupported browser state", () => {
|
||||
const originalSpeechRecognition = window.SpeechRecognition;
|
||||
const originalWebkitSpeechRecognition = window.webkitSpeechRecognition;
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, "SpeechRecognition", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined,
|
||||
});
|
||||
Object.defineProperty(window, "webkitSpeechRecognition", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(window, "SpeechRecognition", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: originalSpeechRecognition,
|
||||
});
|
||||
Object.defineProperty(window, "webkitSpeechRecognition", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: originalWebkitSpeechRecognition,
|
||||
});
|
||||
});
|
||||
|
||||
it("explains missing speech recognition support and links to compatibility references", async () => {
|
||||
render(<LiveDemo />);
|
||||
|
||||
expect(
|
||||
await screen.findByText("Browser Speech Demo Not Available"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/does not support the SpeechRecognition API/i),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("Chrome")).toBeInTheDocument();
|
||||
expect(screen.getByText("Edge")).toBeInTheDocument();
|
||||
expect(screen.getByText("Safari")).toBeInTheDocument();
|
||||
expect(screen.getByRole("link", { name: "MDN" })).toHaveAttribute(
|
||||
"href",
|
||||
"https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Mic, MicOff, AlertCircle, Chrome, Globe, Volume2, Sparkles, Keyboard } from "lucide-react";
|
||||
import { Mic, MicOff, AlertCircle, Volume2, Sparkles, Keyboard } from "lucide-react";
|
||||
|
||||
// Define types for Web Speech API
|
||||
interface SpeechRecognitionEvent {
|
||||
@@ -306,28 +306,41 @@ export function LiveDemo() {
|
||||
if (isSupported === false) {
|
||||
return (
|
||||
<div className="bg-zinc-900 rounded-2xl p-6 sm:p-8 border border-zinc-800">
|
||||
<div className="text-center">
|
||||
<div className="text-center max-w-3xl mx-auto">
|
||||
<div className="inline-flex items-center justify-center h-16 w-16 rounded-full bg-yellow-500/10 mb-4">
|
||||
<AlertCircle className="h-8 w-8 text-yellow-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-white mb-2">
|
||||
Browser Demo Not Available
|
||||
Browser Speech Demo Not Available
|
||||
</h3>
|
||||
<p className="text-zinc-400 mb-6 max-w-md mx-auto">
|
||||
Your browser doesn't support the Web Speech API. Try opening this page in:
|
||||
<p className="text-zinc-400 mb-4 max-w-2xl mx-auto leading-relaxed">
|
||||
Your browser does not support the SpeechRecognition API used by this
|
||||
live demo. Firefox supports parts of Web Speech, but speech recognition
|
||||
is still disabled for regular pages.
|
||||
</p>
|
||||
<div className="flex justify-center gap-4 mb-6">
|
||||
<div className="flex items-center gap-2 text-zinc-300">
|
||||
<Chrome className="h-5 w-5" />
|
||||
<span>Chrome</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-zinc-300">
|
||||
<Globe className="h-5 w-5" />
|
||||
<span>Edge</span>
|
||||
</div>
|
||||
<p className="text-sm text-zinc-500 mb-3">
|
||||
Try a browser listed by MDN with SpeechRecognition support:
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-2 mb-4 text-sm max-w-2xl mx-auto">
|
||||
{["Chrome", "Edge", "Safari", "Chrome Android", "Safari iOS", "Samsung Internet"].map((browser) => (
|
||||
<span key={browser} className="px-3 py-1 rounded-full bg-zinc-800 text-zinc-300 border border-zinc-700">
|
||||
{browser}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-sm text-zinc-500 mb-6">
|
||||
Check compatibility on{" "}
|
||||
<a
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition"
|
||||
className="text-primary hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
MDN
|
||||
</a>.
|
||||
</p>
|
||||
<p className="text-sm text-zinc-500">
|
||||
Or better yet — <a href="#install" className="text-primary hover:underline">install Vocalinux</a> for
|
||||
You can still <a href="#install" className="text-primary hover:underline">install Vocalinux</a> for
|
||||
the full offline experience with Whisper AI!
|
||||
</p>
|
||||
</div>
|
||||
@@ -360,7 +373,7 @@ export function LiveDemo() {
|
||||
Live Browser Demo
|
||||
</span>
|
||||
<span className="text-xs bg-primary/20 text-primary px-2 py-0.5 rounded-full">
|
||||
Web Speech API
|
||||
SpeechRecognition API
|
||||
</span>
|
||||
{(() => {
|
||||
const langInfo = getLanguageInfo(detectedLanguage);
|
||||
@@ -514,10 +527,11 @@ export function LiveDemo() {
|
||||
<Sparkles className="h-5 w-5 text-primary flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm">
|
||||
<p className="text-zinc-300">
|
||||
<strong>This is just a preview!</strong> The browser demo uses Web Speech API (cloud-based).
|
||||
<strong>This is just a preview!</strong> The browser demo uses browser
|
||||
SpeechRecognition, which is cloud-based in many browsers.
|
||||
</p>
|
||||
<p className="text-zinc-500 mt-1">
|
||||
Vocalinux runs <strong>100% offline</strong> using Whisper AI — no cloud, no data sharing,
|
||||
Vocalinux runs <strong>100% offline</strong> using Whisper AI - no cloud, no data sharing,
|
||||
complete privacy. <a href="#install" className="text-primary hover:underline">Install it</a> to
|
||||
experience the real deal!
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user