The header badge fetched the star count of VocaHQ/vocalinux at build time and labelled it as Voculinux's. The fetch and the count are gone; the header, footer and social "source" links now go to the fork repository at git.bongbetic.com/xavierk/voculinux, as do the issue links, the docs anchors, the home page JSON-LD and the xbps release card. Upstream GitHub stays only where it is the real artefact or provenance and is labelled as such: the install script, the AppImage release card, the upstream card and the changelog release links. The changelog now says its entries are upstream history, the open-source page no longer claims VocaHQ membership or an active GitHub community, and the organization JSON-LD drops the upstream author as founder. DESIGN.md and PRODUCT.md follow.
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import { type Metadata } from "next";
|
|
|
|
export const SITE_URL = "https://voculinux.com";
|
|
export const SITE_NAME = "Voculinux";
|
|
export const DEFAULT_OG_IMAGE_PATH = "/og-image.png";
|
|
export const DEFAULT_OG_IMAGE_ALT =
|
|
"Dictate locally. In any app. Offline voice dictation for Linux.";
|
|
export const TWITTER_HANDLE = "@vocahq";
|
|
export const VOCAHQ_URL = "https://vocahq.com";
|
|
// The fork's own source, issue and release host. Everything that says "our
|
|
// source", "our issues" or "our releases" resolves here.
|
|
export const SOURCE_REPO_URL = "https://git.bongbetic.com/xavierk/voculinux";
|
|
// Upstream Vocalinux on GitHub. Retained only as labelled provenance (the
|
|
// upstream install script, the AppImage and packages published there, and the
|
|
// upstream release history behind the changelog).
|
|
export const UPSTREAM_REPO_URL = "https://github.com/VocaHQ/vocalinux";
|
|
export const X_URL = "https://x.com/vocahq";
|
|
export const DISCORD_URL = "https://discord.gg/t6muquAJbm";
|
|
export const EMAIL_URL = "mailto:hello@vocahq.com";
|
|
|
|
interface PageMetadataOptions {
|
|
title: string;
|
|
description: string;
|
|
path?: string;
|
|
keywords?: string[];
|
|
}
|
|
|
|
const normalizePath = (path: string): string => {
|
|
if (path === "/") {
|
|
return "/";
|
|
}
|
|
|
|
const trimmed = path.replace(/^\/+|\/+$/g, "");
|
|
|
|
// Files such as /voculinux.png must not get the page trailing slash:
|
|
// /voculinux.png/ is a 404 on GitHub Pages.
|
|
if (/\.[a-z0-9]+$/i.test(trimmed)) {
|
|
return `/${trimmed}`;
|
|
}
|
|
|
|
return `/${trimmed}/`;
|
|
};
|
|
|
|
export const absoluteUrl = (path = "/"): string => {
|
|
return new URL(normalizePath(path), SITE_URL).toString();
|
|
};
|
|
|
|
export const buildPageMetadata = ({
|
|
title,
|
|
description,
|
|
path = "/",
|
|
keywords = [],
|
|
}: PageMetadataOptions): Metadata => {
|
|
const canonical = absoluteUrl(path);
|
|
|
|
// The root layout applies the "%s | Voculinux" template. Titles that already
|
|
// carry the brand suffix are emitted as-is so search results do not show
|
|
// "| Voculinux | Voculinux".
|
|
const pageTitle: Metadata["title"] = new RegExp(
|
|
`[|\\-–—]\\s*${SITE_NAME}$`,
|
|
).test(title)
|
|
? { absolute: title }
|
|
: title;
|
|
|
|
return {
|
|
title: pageTitle,
|
|
description,
|
|
keywords,
|
|
alternates: {
|
|
canonical,
|
|
languages: {
|
|
"en-US": canonical,
|
|
},
|
|
},
|
|
openGraph: {
|
|
type: "website",
|
|
locale: "en_US",
|
|
siteName: SITE_NAME,
|
|
url: canonical,
|
|
title,
|
|
description,
|
|
images: [
|
|
{
|
|
url: DEFAULT_OG_IMAGE_PATH,
|
|
width: 1200,
|
|
height: 630,
|
|
alt: DEFAULT_OG_IMAGE_ALT,
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title,
|
|
description,
|
|
images: [DEFAULT_OG_IMAGE_PATH],
|
|
creator: TWITTER_HANDLE,
|
|
site: TWITTER_HANDLE,
|
|
},
|
|
};
|
|
};
|