From 805c7ae7d9e0eb05dff06f11dab62af0a625dd9d Mon Sep 17 00:00:00 2001 From: Leonabcd123 <156839416+Leonabcd123@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:00:02 +0200 Subject: [PATCH 1/5] fix(hide-extra-letters): extra letters with hideExtraLetters enabled causing caret problems (@Leonabcd123) (#7272) ### Description Same solution as #7254 --- frontend/src/ts/utils/caret.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/utils/caret.ts b/frontend/src/ts/utils/caret.ts index 1b2b658af..47408db78 100644 --- a/frontend/src/ts/utils/caret.ts +++ b/frontend/src/ts/utils/caret.ts @@ -300,7 +300,7 @@ export class Caret { if (options.letterIndex >= letters.length) { side = "afterLetter"; - if (Config.blindMode) { + if (Config.blindMode || Config.hideExtraLetters) { options.letterIndex = wordText?.length - 1; } else { options.letterIndex = letters.length - 1; From 65e490c96160ea33bddbf5be443a4fd364e94f59 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Fri, 19 Dec 2025 19:42:34 +0100 Subject: [PATCH 2/5] chore: use innerText on generate data modal (@fehmer) (#7276) --- frontend/src/ts/modals/simple-modals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index c22d32068..1bf579504 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -1212,7 +1212,7 @@ list.devGenerateData = new SimpleModal({ const span = document.querySelector( "#devGenerateData_1 + span", ) as HTMLInputElement; - span.innerHTML = `if checked, user will be created with ${target.value}@example.com and password: password`; + span.innerText = `if checked, user will be created with ${target.value}@example.com and password: password`; return; }, validation: { From 47712857f9eb37cffae8f22fcaaf81511bb14148 Mon Sep 17 00:00:00 2001 From: Leonabcd123 <156839416+Leonabcd123@users.noreply.github.com> Date: Fri, 19 Dec 2025 20:42:51 +0200 Subject: [PATCH 3/5] fix(blind-mode): make blind mode "on" text invisible (@Leonabcd123) (#7274) ### Description Use ` ` instead of the current, visible character. --- frontend/src/html/pages/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index 3640a70d7..50d329dcb 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -192,7 +192,7 @@
From eb92e1af0df49c5f3eb6002095a200b1305cd0cc Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 19 Dec 2025 22:29:56 +0100 Subject: [PATCH 4/5] impr: replace vite-plugin-checker with ~~vibe~~ (@miodec) (#7271) vite plugin checker seems to happily spawn a new linting process per file save, causing issues. This vibe coded solution kills the previously running process. It also splits linting into two steps to get some fast fail behavior. I (AI) tried to merge it into one file but the overlay refused to show that way. !nuf --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/package.json | 1 - frontend/vite-plugins/oxlint-checker.ts | 312 ++++++++++++++++++++++++ frontend/vite-plugins/oxlint-overlay.ts | 126 ++++++++++ frontend/vite.config.ts | 13 +- pnpm-lock.yaml | 142 +++++------ 5 files changed, 500 insertions(+), 94 deletions(-) create mode 100644 frontend/vite-plugins/oxlint-checker.ts create mode 100644 frontend/vite-plugins/oxlint-overlay.ts diff --git a/frontend/package.json b/frontend/package.json index fa7acf55b..45e8aa050 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -91,7 +91,6 @@ "unplugin-inject-preload": "3.0.0", "vite": "7.1.12", "vite-bundle-visualizer": "1.2.1", - "vite-plugin-checker": "0.11.0", "vite-plugin-filter-replace": "0.1.14", "vite-plugin-html-inject": "1.1.2", "vite-plugin-inspect": "11.3.3", diff --git a/frontend/vite-plugins/oxlint-checker.ts b/frontend/vite-plugins/oxlint-checker.ts new file mode 100644 index 000000000..5af753d29 --- /dev/null +++ b/frontend/vite-plugins/oxlint-checker.ts @@ -0,0 +1,312 @@ +import { Plugin, ViteDevServer, normalizePath } from "vite"; +import { spawn, execSync, ChildProcess } from "child_process"; +import { fileURLToPath } from "url"; + +export type OxlintCheckerOptions = { + /** Debounce delay in milliseconds before running lint after file changes. @default 125 */ + debounceDelay?: number; + /** Run type-aware checks (slower but more thorough). @default true */ + typeAware?: boolean; + /** Show browser overlay with lint status. @default true */ + overlay?: boolean; + /** File extensions to watch for changes. @default ['.ts', '.tsx', '.js', '.jsx'] */ + extensions?: string[]; +}; + +type LintResult = { + errorCount: number; + warningCount: number; + running: boolean; + hadIssues: boolean; + typeAware?: boolean; +}; + +const OXLINT_SUMMARY_REGEX = /Found (\d+) warnings? and (\d+) errors?/; + +export function oxlintChecker(options: OxlintCheckerOptions = {}): Plugin { + const { + debounceDelay = 125, + typeAware = true, + overlay = true, + extensions = [".ts", ".tsx", ".js", ".jsx"], + } = options; + + let currentProcess: ChildProcess | null = null; + let debounceTimer: NodeJS.Timeout | null = null; + let server: ViteDevServer | null = null; + let isProduction = false; + let currentRunId = 0; + let lastLintResult: LintResult = { + errorCount: 0, + warningCount: 0, + running: false, + hadIssues: false, + }; + + const killCurrentProcess = (): boolean => { + if ((currentProcess && !currentProcess.killed) || currentProcess !== null) { + currentProcess.kill(); + currentProcess = null; + return true; + } + return false; + }; + + const clearDebounceTimer = (): void => { + if (debounceTimer) { + clearTimeout(debounceTimer); + debounceTimer = null; + } + }; + + const parseLintOutput = ( + output: string, + ): Pick => { + const summaryMatch = output.match(OXLINT_SUMMARY_REGEX); + if (summaryMatch?.[1] !== undefined && summaryMatch?.[2] !== undefined) { + return { + warningCount: parseInt(summaryMatch[1], 10), + errorCount: parseInt(summaryMatch[2], 10), + }; + } + return { errorCount: 0, warningCount: 0 }; + }; + + const sendLintResult = (result: Partial): void => { + const previousHadIssues = lastLintResult.hadIssues; + + const payload: LintResult = { + errorCount: result.errorCount ?? lastLintResult.errorCount, + warningCount: result.warningCount ?? lastLintResult.warningCount, + running: result.running ?? false, + hadIssues: previousHadIssues, + typeAware: result.typeAware, + }; + + // Only update hadIssues when we have actual lint results (not just running status) + if (result.running === false) { + const currentHasIssues = + (result.errorCount ?? 0) > 0 || (result.warningCount ?? 0) > 0; + lastLintResult = { ...payload, hadIssues: currentHasIssues }; + } else { + // Keep hadIssues unchanged when just updating running status + lastLintResult = { ...payload, hadIssues: previousHadIssues }; + } + + if (server) { + server.ws.send("vite-plugin-oxlint", payload); + } + }; + + /** + * Runs an oxlint process with the given arguments and captures its combined output. + * + * This function is responsible for managing the lifecycle of the current lint process: + * - It spawns a new child process via `npx oxlint . ...args`. + * - It assigns the spawned process to the shared {@link currentProcess} variable so that + * other parts of the plugin can cancel or track the active lint run. + * - On process termination (either "error" or "close"), it clears {@link currentProcess} + * if it still refers to this child, avoiding interference with any newer process that + * may have started in the meantime. + * + * @param args Additional command-line arguments to pass to `oxlint`. + * @returns A promise that resolves with the process exit code (or `null` if + * the process exited due to a signal) and the full stdout/stderr output + * produced by the lint run. + */ + const runLintProcess = async ( + args: string[], + ): Promise<{ code: number | null; output: string }> => { + return new Promise((resolve) => { + const childProcess = spawn("npx", ["oxlint", ".", ...args], { + cwd: process.cwd(), + shell: true, + env: { ...process.env, FORCE_COLOR: "3" }, + }); + + currentProcess = childProcess; + let output = ""; + + childProcess.stdout?.on("data", (data: Buffer) => { + output += data.toString(); + }); + + childProcess.stderr?.on("data", (data: Buffer) => { + output += data.toString(); + }); + + childProcess.on("error", (error: Error) => { + output += `\nError: ${error.message}`; + if (currentProcess === childProcess) { + currentProcess = null; + } + resolve({ code: 1, output }); + }); + + childProcess.on("close", (code: number | null) => { + if (currentProcess === childProcess) { + currentProcess = null; + } + resolve({ code, output }); + }); + }); + }; + + const runOxlint = async (): Promise => { + const wasKilled = killCurrentProcess(); + const runId = ++currentRunId; + + console.log( + wasKilled + ? "\x1b[36mRunning oxlint...\x1b[0m \x1b[90m(killed previous process)\x1b[0m" + : "\x1b[36mRunning oxlint...\x1b[0m", + ); + + sendLintResult({ running: true }); + + // First pass: fast oxlint without type checking + const { code, output } = await runLintProcess([]); + + // Check if we were superseded by a newer run + if (runId !== currentRunId) { + return; + } + + if (output) { + console.log(output); + } + + // If first pass had errors, send them immediately (fast-fail) + if (code !== 0) { + const counts = parseLintOutput(output); + if (counts.errorCount > 0 || counts.warningCount > 0) { + sendLintResult({ ...counts, running: false }); + return; + } + } + + // First pass clean - run type-aware check if enabled + if (!typeAware) { + sendLintResult({ errorCount: 0, warningCount: 0, running: false }); + return; + } + + console.log("\x1b[36mRunning type-aware checks...\x1b[0m"); + sendLintResult({ running: true, typeAware: true }); + const typeResult = await runLintProcess(["--type-check", "--type-aware"]); + + // Check if we were superseded by a newer run + if (runId !== currentRunId) { + return; + } + + if (typeResult.output) { + console.log(typeResult.output); + } + + const counts = + typeResult.code !== 0 + ? parseLintOutput(typeResult.output) + : { errorCount: 0, warningCount: 0 }; + sendLintResult({ ...counts, running: false }); + }; + + const debouncedLint = (): void => { + clearDebounceTimer(); + sendLintResult({ running: true }); + debounceTimer = setTimeout(() => void runOxlint(), debounceDelay); + }; + + return { + name: "vite-plugin-oxlint-checker", + + config(_, { command }) { + isProduction = command === "build"; + }, + + configureServer(devServer: ViteDevServer) { + server = devServer; + + // Send current lint status to new clients on connection + devServer.ws.on("connection", () => { + devServer.ws.send("vite-plugin-oxlint", lastLintResult); + }); + + // Run initial lint + void runOxlint(); + + // Listen for file changes + devServer.watcher.on("change", (file: string) => { + // Only lint on relevant file changes + if (extensions.some((ext) => file.endsWith(ext))) { + debouncedLint(); + } + }); + }, + + transformIndexHtml() { + if (!overlay) { + return []; + } + + // Inject import to the overlay module (actual .ts file processed by Vite) + const overlayPath = normalizePath( + fileURLToPath(new URL("./oxlint-overlay.ts", import.meta.url)), + ); + return [ + { + tag: "script", + attrs: { + type: "module", + src: `/@fs${overlayPath}`, + }, + injectTo: "body-prepend", + }, + ]; + }, + + buildStart() { + // Only run during production builds, not dev server startup + if (!isProduction) { + return; + } + + // Run oxlint synchronously during build + console.log("\n\x1b[1mRunning oxlint...\x1b[0m"); + + try { + const output = execSync( + "npx oxlint . && npx oxlint . --type-aware --type-check", + { + cwd: process.cwd(), + encoding: "utf-8", + env: { ...process.env, FORCE_COLOR: "3" }, + }, + ); + + if (output) { + console.log(output); + } + console.log(` \x1b[32m✓ No linting issues found\x1b[0m\n`); + } catch (error) { + // execSync throws on non-zero exit code (linting errors found) + if (error instanceof Error && "stdout" in error) { + const execError = error as Error & { + stdout?: string; + stderr?: string; + }; + if (execError.stdout !== undefined) console.log(execError.stdout); + if (execError.stderr !== undefined) console.error(execError.stderr); + } + console.error("\n\x1b[31mBuild aborted due to linting errors\x1b[0m\n"); + process.exit(1); + } + }, + + closeBundle() { + // Cleanup on server close + killCurrentProcess(); + clearDebounceTimer(); + }, + }; +} diff --git a/frontend/vite-plugins/oxlint-overlay.ts b/frontend/vite-plugins/oxlint-overlay.ts new file mode 100644 index 000000000..a1d734031 --- /dev/null +++ b/frontend/vite-plugins/oxlint-overlay.ts @@ -0,0 +1,126 @@ +// Oxlint overlay client-side code +let overlay: HTMLDivElement | null = null; +let hideTimeout: ReturnType | null = null; + +function createOverlay(): HTMLDivElement { + if (overlay) return overlay; + + overlay = document.createElement("div"); + overlay.id = "oxlint-error-overlay"; + overlay.style.cssText = ` + position: fixed; + bottom: 20px; + left: 20px; + background: #323437; + color: #e4dec8ff; + padding: 12px 16px; + border-radius: 8px; + font-family: 'Roboto Mono', monospace; + font-size: 14px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + z-index: 10000; + display: none; + align-items: center; + gap: 8px; + cursor: pointer; + transition: opacity 0.2s ease; + `; + + overlay.addEventListener("mouseenter", () => { + if (overlay) overlay.style.opacity = "0.5"; + }); + + overlay.addEventListener("mouseleave", () => { + if (overlay) overlay.style.opacity = "1"; + }); + + overlay.addEventListener("click", () => { + if (overlay) overlay.style.display = "none"; + }); + + document.body.appendChild(overlay); + return overlay; +} + +function updateOverlay(data: { + errorCount?: number; + warningCount?: number; + running?: boolean; + hadIssues?: boolean; + typeAware?: boolean; +}): void { + const overlayEl = createOverlay(); + + // Clear any pending hide timeout + if (hideTimeout !== null) { + clearTimeout(hideTimeout); + hideTimeout = null; + } + + // Show running icon if linting is running and there were issues before + if (data.running) { + if (data.hadIssues) { + const message = data.typeAware ? "checking type aware..." : "checking..."; + overlayEl.innerHTML = ` + + oxlint: ${message} + `; + overlayEl.style.display = "flex"; + overlayEl.style.color = "#e4dec8ff"; + } else { + overlayEl.style.display = "none"; + } + return; + } + + const { errorCount = 0, warningCount = 0, hadIssues = false } = data; + const total = errorCount + warningCount; + + if (total > 0) { + overlayEl.innerHTML = ` + 🚨 + oxlint: ${errorCount} error${errorCount !== 1 ? "s" : ""}, ${warningCount} warning${warningCount !== 1 ? "s" : ""} + `; + overlayEl.style.display = "flex"; + + if (errorCount > 0) { + overlayEl.style.color = "#e4dec8ff"; + } + } else { + // Only show success if the previous lint had issues + if (hadIssues) { + overlayEl.innerHTML = ` + + oxlint: ok + `; + overlayEl.style.display = "flex"; + overlayEl.style.color = "#e4dec8ff"; + + // Hide after 3 seconds + hideTimeout = setTimeout(() => { + overlayEl.style.display = "none"; + hideTimeout = null; + }, 3000); + } else { + // Two good lints in a row - don't show anything + overlayEl.style.display = "none"; + } + } +} + +// Initialize overlay on load +createOverlay(); + +if (import.meta.hot) { + import.meta.hot.on( + "vite-plugin-oxlint", + (data: { + errorCount?: number; + warningCount?: number; + running?: boolean; + hadIssues?: boolean; + }) => { + updateOverlay(data); + }, + ); +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index ed0eb5c81..0b81b5466 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -19,7 +19,7 @@ import { languageHashes } from "./vite-plugins/language-hashes"; import { minifyJson } from "./vite-plugins/minify-json"; import { versionFile } from "./vite-plugins/version-file"; import { jqueryInject } from "./vite-plugins/jquery-inject"; -import { checker } from "vite-plugin-checker"; +import { oxlintChecker } from "./vite-plugins/oxlint-checker"; import Inspect from "vite-plugin-inspect"; import { ViteMinifyPlugin } from "vite-plugin-minify"; import { VitePWA } from "vite-plugin-pwa"; @@ -81,13 +81,10 @@ function getPlugins({ const plugins: PluginOption[] = [ envConfig({ isDevelopment, clientVersion, env }), languageHashes({ skip: isDevelopment }), - checker({ - oxlint: { - lintCommand: "oxlint . --type-aware --type-check", - }, - overlay: { - initialIsOpen: false, - }, + oxlintChecker({ + debounceDelay: 125, + typeAware: true, + overlay: true, }), jqueryInject(), injectHTML(), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89ee68fc4..17e59006f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,7 +399,7 @@ importers: version: 5.0.2 '@vitest/coverage-v8': specifier: 4.0.15 - version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 4.0.15(vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) autoprefixer: specifier: 10.4.20 version: 10.4.20(postcss@8.4.31) @@ -460,9 +460,6 @@ importers: vite-bundle-visualizer: specifier: 1.2.1 version: 1.2.1(rollup@2.79.2) - vite-plugin-checker: - specifier: 0.11.0 - version: 0.11.0(eslint@9.39.1)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.34.0(oxlint-tsgolint@0.9.2))(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) vite-plugin-filter-replace: specifier: 0.1.14 version: 0.1.14 @@ -480,7 +477,7 @@ importers: version: 1.1.0(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))(workbox-build@7.1.1)(workbox-window@7.1.0) vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) packages/contracts: dependencies: @@ -7090,10 +7087,6 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. @@ -8632,9 +8625,6 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tiny-lru@8.0.2: resolution: {integrity: sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==} engines: {node: '>=6'} @@ -8959,10 +8949,6 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -9107,43 +9093,6 @@ packages: peerDependencies: vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 - vite-plugin-checker@0.11.0: - resolution: {integrity: sha512-iUdO9Pl9UIBRPAragwi3as/BXXTtRu4G12L3CMrjx+WVTd9g/MsqNakreib9M/2YRVkhZYiTEwdH2j4Dm0w7lw==} - engines: {node: '>=16.11'} - peerDependencies: - '@biomejs/biome': '>=1.7' - eslint: '>=7' - meow: ^13.2.0 - optionator: ^0.9.4 - oxlint: '>=1' - stylelint: '>=16' - typescript: '*' - vite: '>=5.4.20' - vls: '*' - vti: '*' - vue-tsc: ~2.2.10 || ^3.0.0 - peerDependenciesMeta: - '@biomejs/biome': - optional: true - eslint: - optional: true - meow: - optional: true - optionator: - optional: true - oxlint: - optional: true - stylelint: - optional: true - typescript: - optional: true - vls: - optional: true - vti: - optional: true - vue-tsc: - optional: true - vite-plugin-filter-replace@0.1.14: resolution: {integrity: sha512-3onEAYgjJQTCJN+/f8+1vOUH3X/tRmXsfxmgH3QtpReGKi+xtP1jMpAEnYsNqw9mI2shZmxFm3LS+n52XczwYQ==} @@ -9254,9 +9203,6 @@ packages: vlq@0.2.3: resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==} - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - walkdir@0.4.1: resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} engines: {node: '>=6.0.0'} @@ -12589,6 +12535,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.15 + ast-v8-to-istanbul: 0.3.8 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + '@vitest/expect@4.0.15': dependencies: '@standard-schema/spec': 1.0.0 @@ -17021,11 +16984,6 @@ snapshots: dependencies: path-key: 4.0.0 - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - npmlog@5.0.1: dependencies: are-we-there-yet: 2.0.0 @@ -18904,8 +18862,6 @@ snapshots: through@2.3.8: {} - tiny-invariant@1.3.3: {} - tiny-lru@8.0.2: {} tinybench@2.9.0: {} @@ -19196,8 +19152,6 @@ snapshots: unicorn-magic@0.1.0: {} - unicorn-magic@0.3.0: {} - unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 @@ -19349,24 +19303,6 @@ snapshots: dependencies: vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) - vite-plugin-checker@0.11.0(eslint@9.39.1)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.34.0(oxlint-tsgolint@0.9.2))(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)): - dependencies: - '@babel/code-frame': 7.27.1 - chokidar: 4.0.3 - npm-run-path: 6.0.0 - picocolors: 1.1.1 - picomatch: 4.0.3 - tiny-invariant: 1.3.3 - tinyglobby: 0.2.15 - vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) - vscode-uri: 3.1.0 - optionalDependencies: - eslint: 9.39.1 - meow: 13.2.0 - optionator: 0.9.4 - oxlint: 1.34.0(oxlint-tsgolint@0.9.2) - typescript: 5.9.3 - vite-plugin-filter-replace@0.1.14: dependencies: magic-string: 0.30.17 @@ -19514,9 +19450,45 @@ snapshots: - tsx - yaml - vlq@0.2.3: {} + vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.9.1 + happy-dom: 20.0.10 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml - vscode-uri@3.1.0: {} + vlq@0.2.3: {} walkdir@0.4.1: {} From 8148b05dd6aae0a6e668179e1bb1e417a20aa22a Mon Sep 17 00:00:00 2001 From: Leonabcd123 <156839416+Leonabcd123@users.noreply.github.com> Date: Sat, 20 Dec 2025 00:40:48 +0200 Subject: [PATCH 5/5] fix(preset): Fix qsa warning on preset modal (@Leonabcd123) (#7277) ### Description Use a more specific selector. --- frontend/src/ts/modals/edit-preset.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/modals/edit-preset.ts b/frontend/src/ts/modals/edit-preset.ts index 7f3771994..443df2f24 100644 --- a/frontend/src/ts/modals/edit-preset.ts +++ b/frontend/src/ts/modals/edit-preset.ts @@ -48,7 +48,7 @@ export function show(action: string, id?: string, name?: string): void { $("#editPresetModal .modal .text").addClass("hidden"); addCheckBoxes(); presetNameEl ??= new ValidatedHtmlInputElement( - qsr("#editPresetModal .modal input"), + qsr("#editPresetModal .modal input[type=text]"), { schema: PresetNameSchema, },