feat: language picker with flags, paging and follow mode, translation run in the floating window
CI / frontend (push) Failing after 27s
CI / backend (push) Failing after 49s

Language picker with search and SVG flags, real pagination with locate, one global machine translation run shown next to uploads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Sonnet 5.5 committed 2026-10-25 19:24:20 +03:00
1 parent 5f6bd4508e
commit 7fa88e4025
82 files changed
+978 -187

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
import assert from "node:assert/strict";
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { test } from "node:test";
import { build } from "esbuild";
const out = await build({ entryPoints: [fileURLToPath(new URL("../src/ui/language-catalog.ts", import.meta.url))],
bundle: true, write: false, format: "esm", platform: "node", logLevel: "silent" });
const { languageCatalog, searchLanguages, describeLanguage, BCP47 } =
await import(`data:text/javascript;base64,${Buffer.from(out.outputFiles[0].text).toString("base64")}`);
const all = languageCatalog();
const codes = (text) => searchLanguages(all, text).map((o) => o.code);
test("a language is found by its Russian name, its own name, English name or code", () => {
assert.equal(codes("немец")[0], "de");
assert.equal(codes("deutsch")[0], "de");
assert.equal(codes("german")[0], "de");
assert.equal(codes("ja")[0], "ja");
assert.ok(codes("португал").includes("pt-BR"));
});
test("names that begin with the text come before names that merely contain it", () => {
const found = codes("но"); // «Норвежский» starts with it; «Македонский» or «Иврит»-like names only contain it
assert.equal(found[0], "no");
assert.ok(found.length > 1, "words that only contain the text are still offered");
});
test("empty search shows everything, nonsense shows nothing", () => {
assert.equal(searchLanguages(all, " ").length, all.length);
assert.deepEqual(codes("qqqqzzz"), []);
});
test("every flag the catalog points to exists, and every offered code is valid BCP-47", () => {
for (const o of all) {
assert.match(o.code, BCP47, o.code);
if (o.flag) assert.ok(existsSync(fileURLToPath(new URL(`../public/flags/${o.flag}.svg`, import.meta.url))), `${o.code} -> ${o.flag}`);
}
assert.equal(describeLanguage("ru").name, "Русский");
});
+25
View File
@@ -0,0 +1,25 @@
import assert from "node:assert/strict";
import { fileURLToPath } from "node:url";
import { test } from "node:test";
import { build } from "esbuild";
const out = await build({ entryPoints: [fileURLToPath(new URL("../src/ui/paging.ts", import.meta.url))],
bundle: true, write: false, format: "esm", platform: "node", logLevel: "silent" });
const { pageNumbers } = await import(`data:text/javascript;base64,${Buffer.from(out.outputFiles[0].text).toString("base64")}`);
test("few pages are all listed", () => {
assert.deepEqual(pageNumbers(0, 1), [0]);
assert.deepEqual(pageNumbers(2, 5), [0, 1, 2, 3, 4]);
assert.deepEqual(pageNumbers(0, 7), [0, 1, 2, null, 6]);
});
test("a long list keeps the ends and the neighbourhood, with gaps between", () => {
assert.deepEqual(pageNumbers(0, 100), [0, 1, 2, null, 99]);
assert.deepEqual(pageNumbers(50, 100), [0, null, 48, 49, 50, 51, 52, null, 99]);
assert.deepEqual(pageNumbers(99, 100), [0, null, 97, 98, 99]);
});
test("a gap of exactly one page shows that page instead of dots", () => {
assert.deepEqual(pageNumbers(4, 10), [0, 1, 2, 3, 4, 5, 6, null, 9]);
assert.deepEqual(pageNumbers(3, 10), [0, 1, 2, 3, 4, 5, null, 9]);
});