From cd55732072163c75f2c8598e63e3797c88fdad48 Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 6 Nov 2026 11:11:30 +0000 Subject: [PATCH] feat: add live client OS detection and compatibility warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plugin page now tells the visitor whether an extension supports their machine before they try to install it, which matters for tools like saturn-patcher that ship Windows and Linux builds only. - src/lib/detectOS.ts: pure detection and compatibility logic (Windows, macOS, Linux, Android, plus iOS and Unknown). Runs on the server and in the browser so the two can never disagree. - src/lib/compat-client.ts: browser entry that re-checks against navigator.userAgent and corrects the pill. - src/pages/plugin/[id].astro: renders the pill above the action buttons and adds a "Download anyway for another PC" link when the OS is unsupported. - src/components/Icon.astro: adds check-circle and alert-triangle. The server renders the pill from the request User-Agent, so it is correct on first paint and cannot shift layout. The client only ever toggles `hidden` on nodes that are always in the DOM, so a correction cannot shift layout either. It exists for iPadOS, which reports a Macintosh User-Agent but is really iOS — maxTouchPoints is the only tell and the server cannot see it. An unreadable User-Agent, or a plugin with no platform data, renders nothing rather than guessing. Tests cover both layers, including the iPadOS correction. Co-authored-by: openhands --- src/components/Icon.astro | 3 + src/lib/compat-client.ts | 69 +++++++++++++++ src/lib/detectOS.ts | 105 +++++++++++++++++++++++ src/pages/plugin/[id].astro | 91 ++++++++++++++++++++ tests/compat-client.test.ts | 163 ++++++++++++++++++++++++++++++++++++ tests/detectOS.test.ts | 105 +++++++++++++++++++++++ 6 files changed, 536 insertions(+) create mode 100644 src/lib/compat-client.ts create mode 100644 src/lib/detectOS.ts create mode 100644 tests/compat-client.test.ts create mode 100644 tests/detectOS.test.ts diff --git a/src/components/Icon.astro b/src/components/Icon.astro index 36118cf..e96fa90 100644 --- a/src/components/Icon.astro +++ b/src/components/Icon.astro @@ -26,6 +26,9 @@ const ICONS: Record = { layers: '', 'shield-check': '', + 'alert-triangle': + '', + 'check-circle': '', wrench: '', cpu: '', sparkles: diff --git a/src/lib/compat-client.ts b/src/lib/compat-client.ts new file mode 100644 index 0000000..7d6d699 --- /dev/null +++ b/src/lib/compat-client.ts @@ -0,0 +1,69 @@ +/** + * Browser entry for the plugin compatibility pill. + * + * The server renders the pill from the request `User-Agent` so the layout is + * final on first paint. This re-runs the same check against + * `navigator.userAgent` and corrects the pill if the two disagree — the usual + * cause being iPadOS in desktop mode, which reports a Macintosh User-Agent but + * is really iOS. + * + * Corrections only ever toggle `hidden` on nodes that are always in the DOM, so + * a correction cannot change the element's height and shift the layout. + * + * This module touches the DOM, so it is only imported from `