From f9ee39dfd0eb32a3c9e2ec0b0d4af4f753e7d834 Mon Sep 17 00:00:00 2001 From: Zaid Humaimidi Date: Thu, 26 Mar 2026 13:20:19 -0700 Subject: [PATCH] Fix device ID parsing regression in Mithril 2.3.8 Device IDs containing literal % characters (e.g., "202BC1-CODA%2D5519-000000") are double-encoded in URLs. Mithril 2.3.8 incorrectly double-decodes route parameters, corrupting device IDs with special characters. Workaround: Extract device ID directly from window.location.hash and decode once to get the correct value. See: https://github.com/MithrilJS/mithril.js/issues/3064 --- ui/device-page.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/ui/device-page.ts b/ui/device-page.ts index 55cf984..70e19db 100644 --- a/ui/device-page.ts +++ b/ui/device-page.ts @@ -3,6 +3,29 @@ import { m } from "./components.ts"; import config from "./config.ts"; import * as store from "./store.ts"; +// Workaround for Mithril 2.3.8 regression +// https://github.com/MithrilJS/mithril.js/issues/3064 +function getPathParamFromHash(hash: string, prefix: string): string | null { + if (!hash.startsWith(prefix)) return null; + + const pathWithQuery = hash.slice(prefix.length); + + // Stop at query string or hash fragment (mirrors Mithril's parsePathname) + const queryIdx = pathWithQuery.indexOf("?"); + const hashIdx = pathWithQuery.indexOf("#"); + let pathEnd = pathWithQuery.length; + if (queryIdx >= 0) pathEnd = Math.min(pathEnd, queryIdx); + if (hashIdx >= 0) pathEnd = Math.min(pathEnd, hashIdx); + + const rawPath = pathWithQuery.slice(0, pathEnd); + + // Normalize: collapse slashes, remove trailing slash (matches Mithril) + const normalizedPath = rawPath.replace(/\/{2,}/g, "/").replace(/\/$/, ""); + + // Decode once (Mithril already decoded with decodeURIComponentSafe) + return decodeURIComponent(normalizedPath); +} + export function init( args: Record, ): Promise> { @@ -12,9 +35,13 @@ export function init( ); } + const deviceId = + getPathParamFromHash(window.location.hash, "#!/devices/") ?? + (args.id as string); + return Promise.resolve({ - deviceId: args.id, - deviceFilter: ["=", ["PARAM", "DeviceID.ID"], args.id], + deviceId: deviceId, + deviceFilter: ["=", ["PARAM", "DeviceID.ID"], deviceId], }); }