From a3c90e5aacd60100bf20fe301a938f6d1ca61f4f Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:21:09 +0200 Subject: [PATCH] fix: blank chat messages on iOS home screen apps and in-app browsers (#29734) Chat messages are virtualized with content-visibility: auto, which WebKit paints incorrectly and can leave blank. #26805 skipped that on Safari by looking for the Safari token in the user agent, but iOS in-app browsers, home screen apps and iPadOS desktop-class standalone windows send no such token, so those users still get empty assistant responses. Check the navigator vendor string as well, which every WebKit surface reports regardless of user agent. The user agent check stays, because non-Apple WebKit ports can compile a different vendor string while shipping the same paint bug. That earlier fix also withheld the message-listitem class entirely, and the class doubles as the styling hook the sidebar hover preview reaches through, so hover previews have rendered at full chat spacing and width on Safari since v0.11.0. Only the content-visibility rule is gated now, on its own class, and the hook stays on every message. Safari hover previews become compact like every other engine. Verified across fourteen engine cases: virtualization is off on every Apple WebKit surface, unchanged on Chromium, Firefox and Android, the hover preview overrides apply again on Safari, and the screenshot export still captures every message on both. Refs #26712, #29688 --- src/lib/components/chat/Messages/Message.svelte | 12 ++++++++---- src/lib/components/layout/Navbar/Menu.svelte | 2 +- src/lib/components/layout/Sidebar/ChatMenu.svelte | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/components/chat/Messages/Message.svelte b/src/lib/components/chat/Messages/Message.svelte index 9d1bb0672c..97c8cd69a4 100644 --- a/src/lib/components/chat/Messages/Message.svelte +++ b/src/lib/components/chat/Messages/Message.svelte @@ -49,16 +49,20 @@ export let topPadding = false; export let onInsertToNote: ((content: string) => void) | null = null; - // Safari's content-visibility implementation has paint bugs that leave + // WebKit's content-visibility implementation has paint bugs that leave // on-screen messages blank (#26712), so skip virtualization there - const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + // iOS WebViews and home screen apps carry no Safari token, hence the vendor check + const isWebKit = + navigator.vendor === 'Apple Computer, Inc.' || + /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
{#if history.messages[messageId]} {#if history.messages[messageId].role === 'user'} @@ -154,7 +158,7 @@ /* Browser-native virtualization: skip rendering of off-screen messages without destroying their component trees. Replaces the JS-based culling that caused catastrophic mount/destroy thrashing. */ - .message-listitem { + .message-virtualized { content-visibility: auto; contain-intrinsic-size: auto 150px; } diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte index f4f03052d2..d862580ab5 100644 --- a/src/lib/components/layout/Navbar/Menu.svelte +++ b/src/lib/components/layout/Navbar/Menu.svelte @@ -105,7 +105,7 @@ document.body.appendChild(clonedElement); // Override content-visibility so html2canvas can capture all messages - clonedElement.querySelectorAll('.message-listitem').forEach((el) => { + clonedElement.querySelectorAll('.message-virtualized').forEach((el) => { el.style.contentVisibility = 'visible'; }); diff --git a/src/lib/components/layout/Sidebar/ChatMenu.svelte b/src/lib/components/layout/Sidebar/ChatMenu.svelte index c591f46e07..17e8cc4403 100644 --- a/src/lib/components/layout/Sidebar/ChatMenu.svelte +++ b/src/lib/components/layout/Sidebar/ChatMenu.svelte @@ -120,7 +120,7 @@ document.body.appendChild(clonedElement); // Override content-visibility so html2canvas can capture all messages - clonedElement.querySelectorAll('.message-listitem').forEach((el) => { + clonedElement.querySelectorAll('.message-virtualized').forEach((el) => { el.style.contentVisibility = 'visible'; });