mirror of
https://github.com/veeso/termscp.git
synced 2026-09-25 21:41:20 -07:00
refactor(site): drop in-site manual, link to external docs.termscp.rs
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
dist/
|
dist/
|
||||||
.astro/
|
.astro/
|
||||||
node_modules/
|
node_modules/
|
||||||
src/content/man/
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"prebuild": "node scripts/fetch-man.mjs",
|
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"check": "astro check",
|
"check": "astro check",
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
import { mkdir, writeFile } from "node:fs/promises";
|
|
||||||
import { dirname, join } from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
/** Pinned ref for reproducible builds. Bump when cutting a release. */
|
|
||||||
export const MAN_REF = "v1.0.0";
|
|
||||||
|
|
||||||
export const LOCALES = ["en", "zh-CN", "it", "fr", "es"];
|
|
||||||
|
|
||||||
const REPO = "veeso/termscp";
|
|
||||||
|
|
||||||
export function manUrl(locale) {
|
|
||||||
const path = locale === "en" ? "docs/man.md" : `docs/${locale}/man.md`;
|
|
||||||
return `https://raw.githubusercontent.com/${REPO}/${MAN_REF}/${path}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchText(url, { retries = 2 } = {}) {
|
|
||||||
const headers = process.env.GITHUB_TOKEN
|
|
||||||
? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
|
|
||||||
: {};
|
|
||||||
for (let attempt = 0; ; attempt += 1) {
|
|
||||||
try {
|
|
||||||
// Abort hung requests and surface them as retryable failures.
|
|
||||||
const res = await fetch(url, { headers, signal: AbortSignal.timeout(15000) });
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`);
|
|
||||||
}
|
|
||||||
const text = await res.text();
|
|
||||||
if (text.trim().length === 0) {
|
|
||||||
throw new Error(`fetch ${url} returned empty body`);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
} catch (err) {
|
|
||||||
if (attempt >= retries) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, 500 * (attempt + 1));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const here = dirname(fileURLToPath(import.meta.url));
|
|
||||||
const outDir = join(here, "..", "src", "content", "man");
|
|
||||||
await mkdir(outDir, { recursive: true });
|
|
||||||
|
|
||||||
for (const locale of LOCALES) {
|
|
||||||
let md;
|
|
||||||
try {
|
|
||||||
md = await fetchText(manUrl(locale));
|
|
||||||
} catch (err) {
|
|
||||||
if (locale === "en") throw err; // en is required — fail the build
|
|
||||||
console.warn(`[fetch-man] ${locale} missing, falling back to en: ${err.message}`);
|
|
||||||
md = await fetchText(manUrl("en"));
|
|
||||||
}
|
|
||||||
await writeFile(join(outDir, `${locale}.md`), md, "utf8");
|
|
||||||
console.log(`[fetch-man] wrote ${locale}.md`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run only when invoked directly (not when imported by tests).
|
|
||||||
// Compare via fileURLToPath so paths with spaces (percent-encoded in
|
|
||||||
// import.meta.url, raw in argv[1]) still match.
|
|
||||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
||||||
main().catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { manUrl, MAN_REF } from "./fetch-man.mjs";
|
|
||||||
|
|
||||||
describe("manUrl", () => {
|
|
||||||
it("uses the root man.md for en", () => {
|
|
||||||
expect(manUrl("en")).toBe(
|
|
||||||
`https://raw.githubusercontent.com/veeso/termscp/${MAN_REF}/docs/man.md`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses the per-locale path for non-en", () => {
|
|
||||||
expect(manUrl("it")).toBe(
|
|
||||||
`https://raw.githubusercontent.com/veeso/termscp/${MAN_REF}/docs/it/man.md`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("pins to a concrete ref, not a moving branch", () => {
|
|
||||||
expect(MAN_REF).not.toBe("main");
|
|
||||||
expect(MAN_REF.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { localizePath, useTranslations, type Locale } from "../i18n/ui";
|
import { localizePath, useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||||
interface Props { locale: Locale; }
|
interface Props { locale: Locale; }
|
||||||
const { locale } = Astro.props;
|
const { locale } = Astro.props;
|
||||||
const t = useTranslations(locale);
|
const t = useTranslations(locale);
|
||||||
@@ -56,7 +56,7 @@ const remote = [
|
|||||||
<p class="mx-auto mt-2 max-w-xl text-subtext">{t("hero.subtitle")}</p>
|
<p class="mx-auto mt-2 max-w-xl text-subtext">{t("hero.subtitle")}</p>
|
||||||
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
<div class="mt-5 flex flex-wrap justify-center gap-3">
|
||||||
<a href={p("/install")} class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">{t("cta.install")}</a>
|
<a href={p("/install")} class="rounded-lg bg-green px-5 py-2.5 font-bold text-crust">{t("cta.install")}</a>
|
||||||
<a href={p("/user-manual")} class="rounded-lg border border-line px-5 py-2.5 text-text">{t("cta.manual")}</a>
|
<a href={DOCS_URL} class="rounded-lg border border-line px-5 py-2.5 text-text">{t("cta.manual")}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { useTranslations, type Locale } from "../i18n/ui";
|
import { useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||||
interface Props { locale: Locale; }
|
interface Props { locale: Locale; }
|
||||||
const { locale } = Astro.props;
|
const { locale } = Astro.props;
|
||||||
const t = useTranslations(locale);
|
const t = useTranslations(locale);
|
||||||
@@ -10,6 +10,7 @@ const year = new Date().getFullYear();
|
|||||||
<div class="flex gap-5">
|
<div class="flex gap-5">
|
||||||
<a href="https://github.com/veeso/termscp" class="hover:text-text">GitHub</a>
|
<a href="https://github.com/veeso/termscp" class="hover:text-text">GitHub</a>
|
||||||
<a href="https://crates.io/crates/termscp" class="hover:text-text">crates.io</a>
|
<a href="https://crates.io/crates/termscp" class="hover:text-text">crates.io</a>
|
||||||
|
<a href={DOCS_URL} class="hover:text-text">{t("nav.manual")}</a>
|
||||||
<a href="https://ko-fi.com/veeso" class="hover:text-text">{t("footer.support")}</a>
|
<a href="https://ko-fi.com/veeso" class="hover:text-text">{t("footer.support")}</a>
|
||||||
</div>
|
</div>
|
||||||
<p>© {year} Christian Visintin · {t("footer.rights")}</p>
|
<p>© {year} Christian Visintin · {t("footer.rights")}</p>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { localizePath, useTranslations, type Locale } from "../i18n/ui";
|
import { localizePath, useTranslations, DOCS_URL, type Locale } from "../i18n/ui";
|
||||||
import ThemeToggle from "./ThemeToggle.astro";
|
import ThemeToggle from "./ThemeToggle.astro";
|
||||||
import LangPicker from "./LangPicker.astro";
|
import LangPicker from "./LangPicker.astro";
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ const p = (sub: string) => localizePath(locale, sub);
|
|||||||
<!-- Desktop -->
|
<!-- Desktop -->
|
||||||
<div class="hidden items-center gap-5 text-sm sm:flex">
|
<div class="hidden items-center gap-5 text-sm sm:flex">
|
||||||
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
||||||
<a href={p("/user-manual")} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
<a href={DOCS_URL} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
||||||
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
||||||
<LangPicker locale={locale} path={path} />
|
<LangPicker locale={locale} path={path} />
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
@@ -32,7 +32,7 @@ const p = (sub: string) => localizePath(locale, sub);
|
|||||||
<summary class="cursor-pointer list-none rounded p-2 text-text hover:bg-mantle" aria-label="Menu">☰</summary>
|
<summary class="cursor-pointer list-none rounded p-2 text-text hover:bg-mantle" aria-label="Menu">☰</summary>
|
||||||
<div class="absolute right-0 mt-2 flex w-48 flex-col gap-3 rounded-lg border border-line bg-mantle p-4 text-sm shadow-xl">
|
<div class="absolute right-0 mt-2 flex w-48 flex-col gap-3 rounded-lg border border-line bg-mantle p-4 text-sm shadow-xl">
|
||||||
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
<a href={p("/install")} class="text-overlay hover:text-text">{t("nav.install")}</a>
|
||||||
<a href={p("/user-manual")} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
<a href={DOCS_URL} class="text-overlay hover:text-text">{t("nav.manual")}</a>
|
||||||
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
<a href="https://github.com/veeso/termscp" class="text-overlay hover:text-text">{t("nav.github")}</a>
|
||||||
<LangPicker locale={locale} path={path} />
|
<LangPicker locale={locale} path={path} />
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import it from "./it.json";
|
|||||||
import fr from "./fr.json";
|
import fr from "./fr.json";
|
||||||
import es from "./es.json";
|
import es from "./es.json";
|
||||||
|
|
||||||
|
/** External documentation site (mdbook), deployed separately. */
|
||||||
|
export const DOCS_URL = "https://docs.termscp.rs";
|
||||||
|
|
||||||
export const locales = ["en", "zh-CN", "it", "fr", "es"] as const;
|
export const locales = ["en", "zh-CN", "it", "fr", "es"] as const;
|
||||||
export type Locale = (typeof locales)[number];
|
export type Locale = (typeof locales)[number];
|
||||||
export const defaultLocale: Locale = "en";
|
export const defaultLocale: Locale = "en";
|
||||||
|
|||||||
Reference in New Issue
Block a user