diff --git a/src/apps/chat/components/ChatMessageList.tsx b/src/apps/chat/components/ChatMessageList.tsx index ec287a394..28fc0557c 100644 --- a/src/apps/chat/components/ChatMessageList.tsx +++ b/src/apps/chat/components/ChatMessageList.tsx @@ -17,7 +17,6 @@ import { createDMessageFromFragments, createDMessageTextContent, DMessage, DMess import { createTextContentFragment, DMessageFragment, DMessageFragmentId } from '~/common/stores/chat/chat.fragments'; import { openFileForAttaching } from '~/common/components/ButtonAttachFiles'; import { optimaOpenPreferences } from '~/common/layout/optima/useOptima'; -import { useBrowserTranslationWarning } from '~/common/components/useIsBrowserTranslating'; import { useCapabilityElevenLabs } from '~/common/components/useCapabilities'; import { useChatOverlayStore } from '~/common/chat-overlay/store-perchat_vanilla'; import { useChatStore } from '~/common/stores/chat/store-chats'; @@ -65,7 +64,6 @@ export function ChatMessageList(props: { const { notifyBooting } = useScrollToBottom(); const danger_experimentalHtmlWebUi = useChatAutoSuggestHTMLUI(); const [showSystemMessages] = useChatShowSystemMessages(); - const optionalTranslationWarning = useBrowserTranslationWarning(); const { conversationMessages, historyTokenCount } = useChatStore(useShallow(({ conversations }) => { const conversation = conversations.find(conversation => conversation.id === props.conversationId); return { @@ -326,8 +324,6 @@ export function ChatMessageList(props: { return ( - {optionalTranslationWarning} - {props.isMessageSelectionMode && ( 0} diff --git a/src/common/components/useIsBrowserInPWADesktop.tsx b/src/common/components/useIsBrowserInPWADesktop.tsx new file mode 100644 index 000000000..7e5217b8f --- /dev/null +++ b/src/common/components/useIsBrowserInPWADesktop.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; + +import { Alert, IconButton } from '@mui/joy'; +import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; +import WarningRoundedIcon from '@mui/icons-material/WarningRounded'; + +import { Is, isBrowser, isPwa } from '~/common/util/pwaUtils'; +import { useUICounter } from '~/common/stores/store-ui'; + + +/** + * Detects if a mobile PWA is running in Desktop Mode (which causes layout issues). + * This happens when Chrome's "Request Desktop Site" is enabled on mobile devices. + * + * Shows a dismissible warning when: + * - App is running as a PWA (standalone mode) + * - Device OS is mobile (iOS or Android) + * - Viewport width is >= 900px (indicating desktop mode override) + */ +export function usePWADesktopModeWarning() { + + // state + const [hideWarning, setHideWarning] = React.useState(false); + + // external state + const { novel: lessThanFive, touch } = useUICounter('acknowledge-pwa-desktop-mode-warning', 5); + + // detect PWA in desktop mode + const isInDesktopMode = React.useMemo(() => { + if (!isBrowser) return false; + + // if PWA + const isInPwaMode = isPwa(); + if (!isInPwaMode) return false; + + // if OS is mobile + const isMobileOS = Is.OS.iOS || Is.OS.Android; + if (!isMobileOS) return false; + + // Check if viewport width suggests desktop mode (>= 900px) + // This matches the mobile breakpoint used in useMatchMedia.ts + return window.matchMedia('(min-width: 900px)').matches; + }, []); + + const showWarning = isInDesktopMode && !hideWarning && lessThanFive; + + return React.useMemo(() => showWarning ? ( + } + endDecorator={ + + { + setHideWarning(true); + touch(); + }} /> + + } + > + This Browser is running in Desktop Mode, which may cause layout issues.
+ To fix: Close this app, open Chrome, visit this site, disable "Desktop site" in the menu, then reopen the app. +
+ ) : null, [showWarning, touch]); +} diff --git a/src/common/layout/optima/OptimaLayout.tsx b/src/common/layout/optima/OptimaLayout.tsx index d4a0d98ba..e4b3a8cda 100644 --- a/src/common/layout/optima/OptimaLayout.tsx +++ b/src/common/layout/optima/OptimaLayout.tsx @@ -5,8 +5,10 @@ import { PanelGroup } from 'react-resizable-panels'; import { GlobalDragOverlay } from '~/common/components/dnd-dt/GlobalDragOverlay'; import { Is } from '~/common/util/pwaUtils'; import { checkVisibleNav, navItems } from '~/common/app.nav'; +import { useBrowserTranslationWarning } from '~/common/components/useIsBrowserTranslating'; import { useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts'; import { useIsMobile } from '~/common/components/useMatchMedia'; +import { usePWADesktopModeWarning } from '~/common/components/useIsBrowserInPWADesktop'; import { useUIPreferencesStore } from '~/common/stores/store-ui'; import { ScratchClip } from './scratchclip/ScratchClip'; @@ -61,6 +63,10 @@ export function OptimaLayout(props: { suspendAutoModelsSetup?: boolean, children // derived state const currentApp = navItems.apps.find(item => item.route === route); + // global warnings + const translationWarning = useBrowserTranslationWarning(); + const pwaDesktopModeWarning = usePWADesktopModeWarning(); + // global shortcuts for Optima useGlobalShortcuts('OptimaApp', React.useMemo(() => [ // Preferences & Model dialogs @@ -78,6 +84,10 @@ export function OptimaLayout(props: { suspendAutoModelsSetup?: boolean, children return <> + {/* Global Warnings */} + {translationWarning} + {pwaDesktopModeWarning} + diff --git a/src/common/stores/store-ui.ts b/src/common/stores/store-ui.ts index 7da40375c..f9f517b77 100644 --- a/src/common/stores/store-ui.ts +++ b/src/common/stores/store-ui.ts @@ -245,6 +245,7 @@ export function uiSetPanelGroupCollapsed(key: string, collapsed: boolean): void // 'export-share' // used the export function // 'share-chat-link' // not shared a Chat Link yet type KnownKeys = + | 'acknowledge-pwa-desktop-mode-warning' // displayed if mobile PWA is in desktop mode (layout issues) | 'acknowledge-translation-warning' // displayed if Chrome is translating the page (may crash) | 'beam-wizard' // first Beam | 'call-wizard' // first Call