fix(web): honest fork-status notes on all packaged-artefact surfaces

Every download/install surface now states that fork AppImage, .deb/.rpm,
Flatpak, Snap, AUR and xbps artefacts are not published yet, labels
upstream GitHub Releases links as upstream, corrects 'Then launch
voculinux' to the upstream vocalinux binary the install script ships
today, and softens the NixOS xbps line to future tense. Adds AC1 honesty
tests to voculinux-rebrand.test.ts.

Fixes #24
This commit is contained in:
soubarnak
2026-11-06 09:04:49 +05:30
parent da97b676a7
commit 9a289548ba
6 changed files with 174 additions and 81 deletions
+5
View File
@@ -204,6 +204,11 @@ export default function FlatpakPage() {
<Download className="h-5 w-5 text-primary" aria-hidden="true" />
Install from the VocaHQ remote
</h2>
<p className="mb-6 text-sm text-muted-foreground">
Fork status: the fork Flatpak remote below is not published yet —
these commands resolve once the first fork release lands. The install
script installs upstream Vocalinux today.
</p>
<ol className="space-y-6">
<li className="rounded-[12px] border border-border bg-background p-5">
<h3 className="font-semibold">1. {installSteps[0].name}</h3>
+8 -8
View File
@@ -51,19 +51,19 @@ bash /tmp/vl.sh`;
const DEB_OPTION: InstallOption = {
label: "Native .deb package",
note: "Download the .deb for your CPU from GitHub Releases (arm64 files are attached too). It is a thin package: Python and GTK come from the distro, whisper.cpp runs on the CPU, and there is no auto-update. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
note: "Fork status: the fork .deb is not published yet. Download the .deb for your CPU from upstream GitHub Releases in the meantime (arm64 files are attached too). It is a thin package: Python and GTK come from the distro, whisper.cpp runs on the CPU, and there is no auto-update. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
command: "sudo apt install ./voculinux_*_amd64.deb",
};
const RPM_OPTION: InstallOption = {
label: "Native .rpm package",
note: "Download the .rpm for your CPU from GitHub Releases (aarch64 files are attached too). It is a thin package: Python and GTK come from Fedora, whisper.cpp runs on the CPU, and there is no auto-update or COPR yet. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
note: "Fork status: the fork .rpm is not published yet. Download the .rpm for your CPU from upstream GitHub Releases in the meantime (aarch64 files are attached too). It is a thin package: Python and GTK come from Fedora, whisper.cpp runs on the CPU, and there is no auto-update or COPR yet. On Wayland, add yourself to the input group (sudo usermod -aG input $USER, then log out and back in) or the default hotkey stays off.",
command: "sudo dnf install ./voculinux-*.x86_64.rpm",
};
const FLATPAK_OPTION: InstallOption = {
label: "Flatpak from the VocaHQ remote",
note: "Not on Flathub. The self-hosted vocahq remote updates with flatpak update and ships whisper.cpp only.",
note: "Fork status: the fork Flatpak remote is not published yet. Not on Flathub. The self-hosted vocahq remote updates with flatpak update and ships whisper.cpp only once published.",
command:
"flatpak install https://vocahq.github.io/voculinux-flatpak/com.voculinux.Voculinux.flatpakref",
};
@@ -185,7 +185,7 @@ const distroConfig: Record<DistroSlug, DistroConfig> = {
otherInstallOptions: [
{
label: "AUR packages",
note: "voculinux builds the canonical release sdist against Arch dependencies. voculinux-bin installs the prebuilt release AppImage. voculinux-git is a community package that tracks main. The three conflict, so install only one.",
note: "Fork status: fork AUR packages are not published yet. Once published, voculinux builds the canonical release sdist against Arch dependencies. voculinux-bin installs the prebuilt release AppImage. voculinux-git is a community package that tracks main. The three conflict, so install only one.",
command: `yay -S voculinux # canonical release sdist, built against Arch dependencies
yay -S voculinux-git # community package; tracks main`,
},
@@ -223,7 +223,7 @@ yay -S voculinux-git # community package; tracks main`,
FLATPAK_OPTION,
{
label: "AppImage",
note: "Download the x86_64 or aarch64 AppImage from GitHub Releases. It starts on Tumbleweed and uses Vulkan through the host driver, but still needs host text-injection tools (xdotool on X11; wtype, ydotool or clipboard tools on Wayland).",
note: "Download the x86_64 or aarch64 AppImage from upstream GitHub Releases until fork releases land. It starts on Tumbleweed and uses Vulkan through the host driver, but still needs host text-injection tools (xdotool on X11; wtype, ydotool or clipboard tools on Wayland).",
command: `chmod +x Voculinux-*-x86_64.AppImage # or aarch64
./Voculinux-*-x86_64.AppImage`,
},
@@ -535,9 +535,9 @@ export default async function DistroInstallPage({
</code>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so
it installs Vocalinux (upstream), not Voculinux. Fork release
artefacts are not published yet.
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
</p>
</div>
</section>
+4 -4
View File
@@ -217,12 +217,12 @@ export default function NixosInstallPage() {
</h1>
<p className="mb-4 max-w-4xl text-lg text-muted-foreground">
Voculinux does not yet publish its own Nix flake. The flake below is
the upstream Vocalinux flake: it builds the app against nixpkgs,
ships a NixOS module and a Home Manager module, and wires every
the upstream Vocalinux flake: it builds the app against nixpkgs, ships
a NixOS module and a Home Manager module, and wires every
text-injection and clipboard tool onto the wrapped binary&rsquo;s
PATH. Its snippets install Vocalinux (upstream), not Voculinux. For
Voculinux on Void Linux, use the signed xbps repository or the
install script.
Voculinux on Void Linux, use the install script today; a signed xbps
repository will ship with fork releases once published.
</p>
</section>
+36 -24
View File
@@ -37,7 +37,8 @@ const distroGuides = [
{
name: "Debian",
href: "/install/debian/",
description: "Debian 12+ with the install script or the native .deb package.",
description:
"Debian 12+ with the install script or the native .deb package.",
icon: Terminal,
},
{
@@ -49,7 +50,8 @@ const distroGuides = [
{
name: "Linux Mint",
href: "/install/linux-mint/",
description: "Voice dictation on Linux Mint releases built on Ubuntu 24.04.",
description:
"Voice dictation on Linux Mint releases built on Ubuntu 24.04.",
icon: Laptop,
},
{
@@ -88,7 +90,8 @@ export default function InstallGuidesPage() {
<h1>Install Voice Dictation on Linux</h1>
<p className="subpage-lede">
Pick your distro guide and follow a proven setup flow for offline
speech-to-text on Linux. Every guide is written for real desktop usage.
speech-to-text on Linux. Every guide is written for real desktop
usage.
</p>
<div className="mt-8">
@@ -99,12 +102,13 @@ export default function InstallGuidesPage() {
/>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Then launch <code>voculinux</code>.
Then launch <code>vocalinux</code> — the upstream binary the script
installs today. Fork releases will ship <code>voculinux</code>.
</p>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so
it installs Vocalinux (upstream), not Voculinux. Fork release
artefacts are not published yet.
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
</p>
<div className="install-alts">
<a
@@ -115,7 +119,7 @@ export default function InstallGuidesPage() {
<p className="install-alt-kicker">No installer</p>
<h2>AppImage</h2>
<p>
x86_64 or aarch64 from GitHub Releases. No root. Host
x86_64 or aarch64 from upstream GitHub Releases. No root. Host
text-injection tools are still required.
</p>
</a>
@@ -127,18 +131,15 @@ export default function InstallGuidesPage() {
<p className="install-alt-kicker">New</p>
<h2>Native packages</h2>
<p>
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora.
x86_64 and aarch64 on every release.
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora. x86_64 and
aarch64 on every fork release once published.
</p>
</a>
<a
className="install-alt"
href="/flatpak/"
>
<a className="install-alt" href="/flatpak/">
<p className="install-alt-kicker">Auto-updates</p>
<h2>Flatpak</h2>
<p>
Install once from the Flatpak remote and{" "}
Install once from the fork Flatpak remote once published and{" "}
<code>flatpak update</code> carries each stable release. Or
sideload the .flatpak bundle.
</p>
@@ -151,8 +152,9 @@ export default function InstallGuidesPage() {
<p className="install-alt-kicker">Snap Store</p>
<h2>Snap</h2>
<p>
<code>snap install voculinux --edge</code>. amd64 only for
now; a .snap is attached to every release too.
<code>snap install voculinux --edge</code>, once the fork Snap is
published (not published yet). amd64 only; a .snap will be
attached to fork releases too.
</p>
</a>
<a
@@ -163,8 +165,8 @@ export default function InstallGuidesPage() {
<p className="install-alt-kicker">Arch</p>
<h2>AUR</h2>
<p>
<code>yay -S voculinux</code>, or voculinux-bin for the
prebuilt package.
<code>yay -S voculinux</code>, or voculinux-bin for the prebuilt
package. Fork packages are not published yet.
</p>
</a>
<a
@@ -175,11 +177,16 @@ export default function InstallGuidesPage() {
<p className="install-alt-kicker">From source</p>
<h2>Build it yourself</h2>
<p>
Clone the repo and run <code>./install.sh</code>. The install
guide has the full source path.
Clone the repo and run <code>./install.sh</code>. The upstream
install guide has the full source path.
</p>
</a>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: packaged artefacts (AppImage, .deb/.rpm, Flatpak, Snap,
AUR) are not published yet — the cards above link upstream artefacts
or reserved fork names until the first fork release lands.
</p>
</section>
<section className="mt-12 grid gap-4 md:grid-cols-3">
@@ -191,7 +198,7 @@ export default function InstallGuidesPage() {
href={guide.href}
className="subpage-card group p-5"
>
<div className="mb-3 inline-flex rounded-[10px] bg-primary/10 p-2.5">
<div className="bg-primary/10 mb-3 inline-flex rounded-[10px] p-2.5">
<Icon className="h-5 w-5 text-primary" />
</div>
<h2 className="mb-2 text-xl font-semibold">{guide.name}</h2>
@@ -212,13 +219,18 @@ export default function InstallGuidesPage() {
<ul className="space-y-3 text-muted-foreground">
<li>
Launch with{" "}
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm text-foreground">
vocalinux
</code>{" "}
(the upstream binary; fork releases will use{" "}
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm text-foreground">
voculinux
</code>
.
).
</li>
<li>
Hold Right Alt (Option) to dictate, or switch to toggle mode in Settings.
Hold Right Alt (Option) to dictate, or switch to toggle mode in
Settings.
</li>
<li>
Choose whisper.cpp for speed, Whisper for maximum model
+56 -45
View File
@@ -292,8 +292,8 @@ export default function HomePage() {
<em>In any app.</em>
</h1>
<p className="hero-lede">
Hold Right Alt. Text lands in the focused window. Local engines
by default.
Hold Right Alt. Text lands in the focused window. Local engines by
default.
</p>
<div className="hero-cta">
<div className="hero-actions">
@@ -480,9 +480,9 @@ export default function HomePage() {
<p className="story-label">model / path</p>
<h3>Choose where the model runs.</h3>
<p>
On-device mode is the default: download a speech-to-text
model and process here. If you want shared compute, point
Remote API at a server you run.
On-device mode is the default: download a speech-to-text model
and process here. If you want shared compute, point Remote API
at a server you run.
</p>
</div>
<div
@@ -626,7 +626,8 @@ export default function HomePage() {
/>
<figcaption>
<strong>Engine and model controls</strong>
Pick whisper.cpp, Faster Whisper, Whisper, VOSK, Parakeet, or a Remote API you trust.
Pick whisper.cpp, Faster Whisper, Whisper, VOSK, Parakeet, or a
Remote API you trust.
</figcaption>
</figure>
<figure className="shot shot-frame">
@@ -720,19 +721,15 @@ export default function HomePage() {
<article className="boundary">
<h3>What you can inspect</h3>
<p>
The application is open source under AGPL-3.0. Settings, engines,
and the injection path are in the repository.
The application is open source under AGPL-3.0. Settings,
engines, and the injection path are in the repository.
</p>
</article>
</div>
</div>
</section>
<section
className="band"
id="install"
aria-labelledby="install-title"
>
<section className="band" id="install" aria-labelledby="install-title">
<div className="shell min-w-0">
<div className="section-head">
<h2 className="section-title" id="install-title">
@@ -741,8 +738,8 @@ export default function HomePage() {
<p className="section-lede">
The interactive installer detects hardware, lets you pick an
engine, and wires the desktop app. Ubuntu, Fedora, Debian, Arch,
and openSUSE. Native .deb, .rpm, Snap, and Flatpak packages
ship with every release.
and openSUSE. Native .deb, .rpm, Snap, and Flatpak packages are
planned for every fork release.
</p>
</div>
<TerminalBlock
@@ -750,11 +747,14 @@ export default function HomePage() {
displayCommand={interactiveInstallDisplayCommand}
label="install.sh --interactive"
/>
<p className="section-lede">Then launch <code>voculinux</code>.</p>
<p className="section-lede">
Then launch <code>vocalinux</code> — the upstream binary the script
installs today. Fork releases will ship <code>voculinux</code>.
</p>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: this command fetches the upstream install script, so
it installs Vocalinux (upstream), not Voculinux. Fork release
artefacts are not published yet.
Fork status: this command fetches the upstream install script, so it
installs Vocalinux (upstream), not Voculinux. Fork release artefacts
are not published yet.
</p>
<div className="install-alts">
<a
@@ -766,8 +766,8 @@ export default function HomePage() {
<h3>xbps repository</h3>
<p>
Signed xbps repository data will ship with releases once the
packaging pipeline lands. Until then, use the install script
or build from source.
packaging pipeline lands. Until then, use the install script or
build from source.
</p>
</a>
<a
@@ -778,7 +778,7 @@ export default function HomePage() {
<p className="install-alt-kicker">No installer</p>
<h3>AppImage</h3>
<p>
x86_64 or aarch64 from GitHub Releases. No root. Host
x86_64 or aarch64 from upstream GitHub Releases. No root. Host
text-injection tools are still required.
</p>
</a>
@@ -790,8 +790,8 @@ export default function HomePage() {
<p className="install-alt-kicker">New</p>
<h3>Native packages</h3>
<p>
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora.
x86_64 and aarch64 on every release.
.deb for Debian 12+ and Ubuntu 24.04+. .rpm for Fedora. x86_64
and aarch64 on every fork release once published.
</p>
</a>
<a
@@ -802,7 +802,7 @@ export default function HomePage() {
<p className="install-alt-kicker">Auto-updates</p>
<h3>Flatpak</h3>
<p>
Install once from the Flatpak remote and{" "}
Install once from the fork Flatpak remote once published and{" "}
<code>flatpak update</code> carries each stable release. Or
sideload the .flatpak bundle.
</p>
@@ -815,8 +815,9 @@ export default function HomePage() {
<p className="install-alt-kicker">Snap Store</p>
<h3>Snap</h3>
<p>
<code>snap install voculinux --edge</code>. amd64 only for
now; a .snap is attached to every release too.
<code>snap install voculinux --edge</code>, once the fork Snap
is published (not published yet). amd64 only; a .snap will be
attached to fork releases too.
</p>
</a>
<a
@@ -827,8 +828,8 @@ export default function HomePage() {
<p className="install-alt-kicker">Arch</p>
<h3>AUR</h3>
<p>
<code>yay -S voculinux</code>, or voculinux-bin for the
prebuilt package.
<code>yay -S voculinux</code>, or voculinux-bin for the prebuilt
package. Fork packages are not published yet.
</p>
</a>
<a
@@ -839,11 +840,16 @@ export default function HomePage() {
<p className="install-alt-kicker">From source</p>
<h3>Build it yourself</h3>
<p>
Clone the repo and run <code>./install.sh</code>. The install
guide has the full source path.
Clone the repo and run <code>./install.sh</code>. The upstream
install guide has the full source path.
</p>
</a>
</div>
<p className="mt-4 text-sm text-muted-foreground">
Fork status: packaged artefacts (AppImage, .deb/.rpm, Flatpak, Snap,
AUR, xbps) are not published yet — the cards above link upstream
artefacts or reserved fork names until the first fork release lands.
</p>
<p className="section-link" style={{ marginTop: "1.6rem" }}>
<Link href="/install/ubuntu/">Ubuntu guide</Link>
{" · "}
@@ -854,7 +860,10 @@ export default function HomePage() {
<Link href="/install/">All distro notes</Link>
</p>
<div className="section-head" style={{ marginTop: "3.5rem" }}>
<h3 className="section-title" style={{ fontSize: "clamp(1.6rem, 3vw, 2.2rem)" }}>
<h3
className="section-title"
style={{ fontSize: "clamp(1.6rem, 3vw, 2.2rem)" }}
>
Uninstall
</h3>
</div>
@@ -881,9 +890,8 @@ export default function HomePage() {
answer: (
<>
Local engines (whisper.cpp, Faster Whisper, Whisper, VOSK,
and Parakeet) process speech on your machine. Remote API is
optional and only
talks to servers you configure.{" "}
and Parakeet) process speech on your machine. Remote API
is optional and only talks to servers you configure.{" "}
<Link href="/offline/">Offline details</Link>.
</>
),
@@ -908,9 +916,9 @@ export default function HomePage() {
question: "How do I switch between speech engines?",
answer: (
<>
Settings dialog or CLI:{" "}
<code>--engine whisper_cpp</code>, <code>whisper</code>,{" "}
<code>vosk</code>, or <code>remote_api</code>.{" "}
Settings dialog or CLI: <code>--engine whisper_cpp</code>,{" "}
<code>whisper</code>, <code>vosk</code>, or{" "}
<code>remote_api</code>.{" "}
<Link href="/compare/">Compare engines</Link>.
</>
),
@@ -981,9 +989,9 @@ export default function HomePage() {
</h2>
<p className="section-lede">
Voculinux is a hard fork of Vocalinux for Void Linux (glibc,
Wayland-primary under niri). The internal Python module keeps
the upstream name so fixes stay cheap to port by hand; upstream
links below are retained as provenance.
Wayland-primary under niri). The internal Python module keeps the
upstream name so fixes stay cheap to port by hand; upstream links
below are retained as provenance.
</p>
</div>
<div className="family-grid">
@@ -1025,8 +1033,8 @@ export default function HomePage() {
</div>
<h3>Vocalinux (upstream)</h3>
<p>
The upstream Linux dictation app this fork is ported from.
Fixes are taken by hand, one at a time.
The upstream Linux dictation app this fork is ported from. Fixes
are taken by hand, one at a time.
</p>
</a>
<a
@@ -1060,7 +1068,9 @@ export default function HomePage() {
<section className="band band-bright final-cta">
<div className="shell">
<h2 className="section-title">Install and try a real dictation session.</h2>
<h2 className="section-title">
Install and try a real dictation session.
</h2>
<p className="section-lede">
Free and open source under AGPL-3.0. Local engines by default.
</p>
@@ -1078,7 +1088,8 @@ export default function HomePage() {
</a>
</div>
<p className="hero-cta-note">
Then hold Right Alt and speak into the app already in front of you.
Then hold Right Alt and speak into the app already in front of
you.
</p>
</div>
</div>
@@ -1,5 +1,10 @@
import fs from "fs";
import path from "path";
import { SITE_NAME, SITE_URL } from "../seo";
const readPage = (...segments: string[]): string =>
fs.readFileSync(path.join(process.cwd(), "src", "app", ...segments), "utf-8");
describe("voculinux rebrand (ticket #24)", () => {
it("names the fork site", () => {
expect(SITE_NAME).toBe("Voculinux");
@@ -9,3 +14,63 @@ describe("voculinux rebrand (ticket #24)", () => {
expect(SITE_URL).toBe("https://voculinux.com");
});
});
describe("fork install honesty (ticket #24, AC1)", () => {
const home = readPage("page.tsx");
const install = readPage("install", "page.tsx");
const distro = readPage("install", "[distro]", "page.tsx");
const nixos = readPage("install", "nixos", "page.tsx");
const flatpak = readPage("flatpak", "page.tsx");
it("does not promise packaged artefacts with every release", () => {
for (const [name, source] of [
["home", home],
["install", install],
] as const) {
expect(`${name}: ${source}`).not.toContain("ship with every release");
}
});
it("does not tell users to launch the fork binary after the upstream script", () => {
for (const [name, source] of [
["home", home],
["install", install],
] as const) {
expect(`${name}: ${source}`).not.toContain(
"Then launch <code>voculinux</code>",
);
}
});
it("discloses fork status on every packaged-artefact surface", () => {
for (const [name, source] of [
["home", home],
["install", install],
["distro", distro],
["flatpak", flatpak],
] as const) {
expect(`${name} discloses fork status: ${source}`).toContain(
"Fork status:",
);
expect(`${name} states artefacts are unpublished: ${source}`).toMatch(
/not published yet|not yet published|once published/,
);
}
});
it("does not present the signed xbps repository as available today", () => {
expect(`nixos: ${nixos}`).not.toContain("use the signed xbps repository");
});
it("labels upstream release links as upstream until fork releases land", () => {
for (const [name, source] of [
["home", home],
["install", install],
["distro", distro],
] as const) {
expect(`${name} marks upstream artefacts: ${source}`).toMatch(
/upstream GitHub Releases|upstream artefacts|upstream install script/,
);
}
});
});