diff --git a/src/help-window.ts b/src/help-window.ts
index 4da2d80..f8dd7de 100644
--- a/src/help-window.ts
+++ b/src/help-window.ts
@@ -4,7 +4,6 @@ import { getHelpCatalog, type HelpCatalog } from "./help-content-localized";
import { renderHelpMath } from "./help-math";
import {
getResolvedLocale,
- isLanguagePreference,
setLanguagePreference,
t,
type LanguagePreference,
@@ -16,13 +15,27 @@ interface HelpWindowPayload {
theme: AppTheme;
}
+function queryLanguagePreference(value: string | null): LanguagePreference | null {
+ switch (value) {
+ case "system": return "system";
+ case "en": return "en";
+ case "zh-CN": return "zh-CN";
+ case "zh-TW": return "zh-TW";
+ case "ja": return "ja";
+ case "es": return "es";
+ case "fr": return "fr";
+ case "de": return "de";
+ default: return null;
+ }
+}
+
export class HelpWindowApp {
private catalog: HelpCatalog;
private activeSection = "";
constructor(private readonly root: HTMLElement) {
- const requestedLanguage = new URLSearchParams(window.location.search).get("lang");
- if (requestedLanguage && isLanguagePreference(requestedLanguage)) setLanguagePreference(requestedLanguage);
+ const requestedLanguage = queryLanguagePreference(new URLSearchParams(window.location.search).get("lang"));
+ if (requestedLanguage) setLanguagePreference(requestedLanguage);
this.catalog = getHelpCatalog(getResolvedLocale());
this.activeSection = this.catalog.sections[0]?.id ?? "";
root.addEventListener("contextmenu", (event) => event.preventDefault());
diff --git a/src/main.ts b/src/main.ts
index 3d2401f..aeb2716 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,7 +8,16 @@ if (!root) throw new Error(t("error.rootMissing"));
const appRoot = root;
function showFatalError(error: unknown): void {
- appRoot.innerHTML = `${t("runtime.fatalTitle")}
${String(error)}
${t("runtime.fatalHint")}`;
+ const fatal = document.createElement("main");
+ fatal.className = "fatal-error";
+ const title = document.createElement("h1");
+ title.textContent = t("runtime.fatalTitle");
+ const detail = document.createElement("p");
+ detail.textContent = String(error);
+ const hint = document.createElement("small");
+ hint.textContent = t("runtime.fatalHint");
+ fatal.append(title, detail, hint);
+ appRoot.replaceChildren(fatal);
}
try {
diff --git a/src/statistics-chart.ts b/src/statistics-chart.ts
index 1b112d2..11105a6 100644
--- a/src/statistics-chart.ts
+++ b/src/statistics-chart.ts
@@ -364,7 +364,7 @@ export class StatisticsCharts {
{
id: `${chartKey}-x-slider`, type: "slider", xAxisIndex: 0, filterMode: "none",
height: 16, bottom: 12, borderColor: border, backgroundColor: surface,
- fillerColor: colors.all.replace("rgb(", "rgb(").replace(")", " / .16)"),
+ fillerColor: colors.all.replace(/\)$/, " / .16)"),
handleStyle: { color: colors.all, borderColor: colors.all },
textStyle: { color: dim, fontSize: 9 }, showDetail: false, ...range(xRange),
},
@@ -375,7 +375,7 @@ export class StatisticsCharts {
{
id: `${chartKey}-y-slider`, type: "slider", yAxisIndex: 0, orient: "vertical", filterMode: "none",
width: 14, right: 8, top: 38, bottom: 66, borderColor: border, backgroundColor: surface,
- fillerColor: colors.all.replace("rgb(", "rgb(").replace(")", " / .12)"),
+ fillerColor: colors.all.replace(/\)$/, " / .12)"),
handleStyle: { color: colors.all, borderColor: colors.all },
textStyle: { color: dim, fontSize: 9 }, showDetail: false, ...range(yRange),
},
diff --git a/tests/help-window.test.mjs b/tests/help-window.test.mjs
index c828dd8..ba330c4 100644
--- a/tests/help-window.test.mjs
+++ b/tests/help-window.test.mjs
@@ -32,6 +32,8 @@ test("help is available from the utility menu and F1 through one window lifecycl
test("help page is routed independently and receives language and theme updates", () => {
assert.match(entrySource, /page\.get\("help"\) === "1"/);
assert.match(entrySource, /import\("\.\/help-window"\)/);
+ assert.match(entrySource, /appRoot\.replaceChildren\(fatal\)/);
+ assert.doesNotMatch(entrySource, /appRoot\.innerHTML/);
assert.match(windowSource, /listen\("help:state"/);
assert.match(windowSource, /setLanguagePreference\(payload\.language\)/);
assert.match(windowSource, /document\.documentElement\.dataset\.theme = payload\.theme/);
@@ -89,7 +91,8 @@ test("all seven interface locales provide a complete localized manual", () => {
assert.match(localizedContentSource, /if \(locale === "zh-CN"\)/);
assert.match(localizedContentSource, /V0\.5\.5/g);
assert.match(windowSource, /data-help-locale="\$\{getResolvedLocale\(\)\}"/);
- assert.match(windowSource, /isLanguagePreference\(requestedLanguage\)/);
+ assert.match(windowSource, /queryLanguagePreference\(new URLSearchParams/);
+ assert.doesNotMatch(windowSource, /isLanguagePreference\(requestedLanguage\)/);
assert.match(windowSource, /this\.catalog = getHelpCatalog\(getResolvedLocale\(\)\)/);
assert.match(windowSource, /if \(getResolvedLocale\(\) !== previousLocale\)/);
assert.match(windowSource, /this\.render\(\)/);
diff --git a/tests/image-statistics.test.mjs b/tests/image-statistics.test.mjs
index 61f0d43..2dfc89f 100644
--- a/tests/image-statistics.test.mjs
+++ b/tests/image-statistics.test.mjs
@@ -97,6 +97,7 @@ test("statistics view supports three presentations, independent curves, two-axis
assert.match(chartSource, /zoomOnMouseWheel:\s*"ctrl"/);
assert.match(chartSource, /id: `\$\{chartKey\}-y-inside`[\s\S]*?zoomOnMouseWheel:\s*"shift"/);
assert.match(chartSource, /orient: "vertical"/);
+ assert.doesNotMatch(chartSource, /\.replace\("rgb\(", "rgb\("\)/);
assert.match(chartSource, /backgroundColor: "transparent"/);
assert.match(panelSource, /data-stat-group-chart=/);
assert.match(panelSource, /data-stat-y-reset=/);