From a3ea15c96254cc948e8a520720321ef2a80a79d1 Mon Sep 17 00:00:00 2001 From: eRAW Development Date: Wed, 19 Aug 2026 23:37:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=89=E5=85=A8=EF=BC=9A=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20CodeQL=20=E9=A6=96=E8=BD=AE=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/help-window.ts | 19 ++++++++++++++++--- src/main.ts | 11 ++++++++++- src/statistics-chart.ts | 4 ++-- tests/help-window.test.mjs | 5 ++++- tests/image-statistics.test.mjs | 1 + 5 files changed, 33 insertions(+), 7 deletions(-) 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=/);