| author | |
| committer | |
| log | b6d9ce325c4586ea3ab2d2ea5cfd5e8f8c167aeb |
| tree | bf520fc3d368ac9aacd65bb3ecd1995d5db1f7e6 |
| parent | 8e3467fe60c49ee739b278229156108032b72e25 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: codex <codex@users.noreply.github.com>10 files changed, 76 insertions(+), 28 deletions(-)
apps/mobile/src/Stack.tsx+12-6| ... | @@ -59,6 +59,7 @@ import { | ... | @@ -59,6 +59,7 @@ import { |
| 59 | EMPTY_INCOMING_SHARE_PRESENTATION_STATE, | 59 | EMPTY_INCOMING_SHARE_PRESENTATION_STATE, |
| 60 | transitionIncomingSharePresentation, | 60 | transitionIncomingSharePresentation, |
| 61 | } from "./features/sharing/incoming-share-presentation"; | 61 | } from "./features/sharing/incoming-share-presentation"; |
| 62 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "./native/native-glass"; | ||
| 62 | import { nativeHeaderScrollEdgeEffects } from "./native/StackHeader"; | 63 | import { nativeHeaderScrollEdgeEffects } from "./native/StackHeader"; |
| 63 | import { useThreadOutboxDrain } from "./state/use-thread-outbox-drain"; | 64 | import { useThreadOutboxDrain } from "./state/use-thread-outbox-drain"; |
| 64 | 65 | ||
| ... | @@ -78,19 +79,24 @@ type AppScreenOptions = NativeStackNavigationOptions & { | ... | @@ -78,19 +79,24 @@ type AppScreenOptions = NativeStackNavigationOptions & { |
| 78 | // Shared header presets. Screens only override genuinely dynamic values (titles, | 79 | // Shared header presets. Screens only override genuinely dynamic values (titles, |
| 79 | // subtitles, toolbar items, search callbacks) via NativeStackScreenOptions. | 80 | // subtitles, toolbar items, search callbacks) via NativeStackScreenOptions. |
| 80 | // | 81 | // |
| 81 | // GLASS: transparent header over the screen's primary scroll view, with the iOS 26 | 82 | // GLASS: transparent header over the screen's primary scroll view on supported |
| 82 | // scroll-edge blur sampling the content (Home, Thread, Files tree, settings sheet). | 83 | // iOS versions. Pre-glass iOS gets the same solid material as internal-scroll |
| 84 | // surfaces so content is laid out below the bar instead of underlapping it. | ||
| 83 | const GLASS_HEADER_OPTIONS: AppScreenOptions = { | 85 | const GLASS_HEADER_OPTIONS: AppScreenOptions = { |
| 84 | headerBackButtonDisplayMode: "minimal", | 86 | headerBackButtonDisplayMode: "minimal", |
| 85 | headerBackTitle: "", | 87 | headerBackTitle: "", |
| 86 | headerLargeTitle: false, | 88 | headerLargeTitle: false, |
| 87 | headerShadowVisible: false, | 89 | headerShadowVisible: false, |
| 88 | headerShown: true, | 90 | headerShown: true, |
| 89 | headerStyle: Platform.OS === "ios" ? { backgroundColor: "transparent" } : undefined, | 91 | headerStyle: NATIVE_LIQUID_GLASS_SUPPORTED |
| 92 | ? { backgroundColor: "transparent" } | ||
| 93 | : SHEET_BACKGROUND_COLOR !== undefined | ||
| 94 | ? { backgroundColor: SHEET_BACKGROUND_COLOR as unknown as string } | ||
| 95 | : undefined, | ||
| 90 | headerTitleStyle: { fontSize: 18, fontWeight: "800" }, | 96 | headerTitleStyle: { fontSize: 18, fontWeight: "800" }, |
| 91 | headerTransparent: Platform.OS === "ios", | 97 | headerTransparent: NATIVE_LIQUID_GLASS_SUPPORTED, |
| 92 | scrollEdgeEffects: Platform.OS === "ios" ? HEADER_SCROLL_EDGE_EFFECTS : undefined, | 98 | scrollEdgeEffects: NATIVE_LIQUID_GLASS_SUPPORTED ? HEADER_SCROLL_EDGE_EFFECTS : undefined, |
| 93 | unstable_navigationItemStyle: Platform.OS === "ios" ? "editor" : undefined, | 99 | unstable_navigationItemStyle: NATIVE_LIQUID_GLASS_SUPPORTED ? "editor" : undefined, |
| 94 | }; | 100 | }; |
| 95 | 101 | ||
| 96 | // SOLID: opaque sheet-colored header for surfaces whose content scrolls internally | 102 | // SOLID: opaque sheet-colored header for surfaces whose content scrolls internally |
apps/mobile/src/features/files/FileTreeBrowser.tsx+7-11| ... | @@ -1,20 +1,14 @@ | ... | @@ -1,20 +1,14 @@ |
| 1 | import type { ProjectEntry } from "@t3tools/contracts"; | 1 | import type { ProjectEntry } from "@t3tools/contracts"; |
| 2 | import { SymbolView } from "../../components/AppSymbol"; | 2 | import { SymbolView } from "../../components/AppSymbol"; |
| 3 | import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; | 3 | import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| 4 | import { | 4 | import { ActivityIndicator, FlatList, Pressable, RefreshControl, View } from "react-native"; |
| 5 | ActivityIndicator, | ||
| 6 | FlatList, | ||
| 7 | Platform, | ||
| 8 | Pressable, | ||
| 9 | RefreshControl, | ||
| 10 | View, | ||
| 11 | } from "react-native"; | ||
| 12 | import { useSafeAreaInsets } from "react-native-safe-area-context"; | 5 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 13 | 6 | ||
| 14 | import { AppText as Text } from "../../components/AppText"; | 7 | import { AppText as Text } from "../../components/AppText"; |
| 15 | import { PierreEntryIcon } from "../../components/PierreEntryIcon"; | 8 | import { PierreEntryIcon } from "../../components/PierreEntryIcon"; |
| 16 | import { cn } from "../../lib/cn"; | 9 | import { cn } from "../../lib/cn"; |
| 17 | import { useThemeColor } from "../../lib/useThemeColor"; | 10 | import { useThemeColor } from "../../lib/useThemeColor"; |
| 11 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 18 | import { | 12 | import { |
| 19 | buildFileTree, | 13 | buildFileTree, |
| 20 | defaultExpandedTreePaths, | 14 | defaultExpandedTreePaths, |
| ... | @@ -129,7 +123,7 @@ export function FileTreeBrowser(props: { | ... | @@ -129,7 +123,7 @@ export function FileTreeBrowser(props: { |
| 129 | const insets = useSafeAreaInsets(); | 123 | const insets = useSafeAreaInsets(); |
| 130 | // Native transparent-header height ≈ safe-area top + nav bar (~44). Matches the | 124 | // Native transparent-header height ≈ safe-area top + nav bar (~44). Matches the |
| 131 | // observed adjustedContentInset bottom (~102) seen in the native trace. | 125 | // observed adjustedContentInset bottom (~102) seen in the native trace. |
| 132 | const headerInset = Platform.OS === "ios" ? insets.top + 44 : 0; | 126 | const headerInset = NATIVE_LIQUID_GLASS_SUPPORTED ? insets.top + 44 : 0; |
| 133 | const iconColor = String(useThemeColor("--color-icon-muted")); | 127 | const iconColor = String(useThemeColor("--color-icon-muted")); |
| 134 | const { onPreviewFile, onSelectFile, selectedPath: controlledSelectedPath } = props; | 128 | const { onPreviewFile, onSelectFile, selectedPath: controlledSelectedPath } = props; |
| 135 | const controlledSelectedPathRef = useRef(controlledSelectedPath); | 129 | const controlledSelectedPathRef = useRef(controlledSelectedPath); |
| ... | @@ -249,9 +243,11 @@ export function FileTreeBrowser(props: { | ... | @@ -249,9 +243,11 @@ export function FileTreeBrowser(props: { |
| 249 | className="flex-1" | 243 | className="flex-1" |
| 250 | data={visibleNodes} | 244 | data={visibleNodes} |
| 251 | keyExtractor={(item) => item.node.path} | 245 | keyExtractor={(item) => item.node.path} |
| 252 | contentInsetAdjustmentBehavior={Platform.OS === "ios" ? "automatic" : "never"} | 246 | contentInsetAdjustmentBehavior={NATIVE_LIQUID_GLASS_SUPPORTED ? "automatic" : "never"} |
| 253 | scrollIndicatorInsets={ | 247 | scrollIndicatorInsets={ |
| 254 | Platform.OS === "ios" ? { top: headerInset, left: 0, right: 0, bottom: 0 } : undefined | 248 | NATIVE_LIQUID_GLASS_SUPPORTED |
| 249 | ? { top: headerInset, left: 0, right: 0, bottom: 0 } | ||
| 250 | : undefined | ||
| 255 | } | 251 | } |
| 256 | keyboardDismissMode="on-drag" | 252 | keyboardDismissMode="on-drag" |
| 257 | keyboardShouldPersistTaps="handled" | 253 | keyboardShouldPersistTaps="handled" |
apps/mobile/src/features/home/HomeScreen.tsx+4-3| ... | @@ -24,6 +24,7 @@ import { EmptyState } from "../../components/EmptyState"; | ... | @@ -24,6 +24,7 @@ import { EmptyState } from "../../components/EmptyState"; |
| 24 | import type { WorkspaceState } from "../../state/workspaceModel"; | 24 | import type { WorkspaceState } from "../../state/workspaceModel"; |
| 25 | import type { SavedRemoteConnection } from "../../lib/connection"; | 25 | import type { SavedRemoteConnection } from "../../lib/connection"; |
| 26 | import { scopedProjectKey } from "../../lib/scopedEntities"; | 26 | import { scopedProjectKey } from "../../lib/scopedEntities"; |
| 27 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 27 | import { mobilePreferencesAtom, updateMobilePreferencesAtom } from "../../state/preferences"; | 28 | import { mobilePreferencesAtom, updateMobilePreferencesAtom } from "../../state/preferences"; |
| 28 | import type { PendingNewTask } from "../../state/use-pending-new-tasks"; | 29 | import type { PendingNewTask } from "../../state/use-pending-new-tasks"; |
| 29 | import { | 30 | import { |
| ... | @@ -388,7 +389,7 @@ export function HomeScreen(props: HomeScreenProps) { | ... | @@ -388,7 +389,7 @@ export function HomeScreen(props: HomeScreenProps) { |
| 388 | className="flex-1 items-center justify-center bg-screen px-8" | 389 | className="flex-1 items-center justify-center bg-screen px-8" |
| 389 | style={{ | 390 | style={{ |
| 390 | paddingBottom: Math.max(insets.bottom, 24), | 391 | paddingBottom: Math.max(insets.bottom, 24), |
| 391 | paddingTop: Platform.OS === "ios" ? insets.top + 72 : 0, | 392 | paddingTop: NATIVE_LIQUID_GLASS_SUPPORTED ? insets.top + 72 : 0, |
| 392 | }} | 393 | }} |
| 393 | > | 394 | > |
| 394 | <View className="w-full max-w-[430px]"> | 395 | <View className="w-full max-w-[430px]"> |
| ... | @@ -469,8 +470,8 @@ export function HomeScreen(props: HomeScreenProps) { | ... | @@ -469,8 +470,8 @@ export function HomeScreen(props: HomeScreenProps) { |
| 469 | ListHeaderComponent={listHeader} | 470 | ListHeaderComponent={listHeader} |
| 470 | ListEmptyComponent={listEmpty} | 471 | ListEmptyComponent={listEmpty} |
| 471 | style={{ flex: 1 }} | 472 | style={{ flex: 1 }} |
| 472 | automaticallyAdjustsScrollIndicatorInsets={Platform.OS === "ios"} | 473 | automaticallyAdjustsScrollIndicatorInsets={NATIVE_LIQUID_GLASS_SUPPORTED} |
| 473 | contentInsetAdjustmentBehavior={Platform.OS === "ios" ? "automatic" : "never"} | 474 | contentInsetAdjustmentBehavior={NATIVE_LIQUID_GLASS_SUPPORTED ? "automatic" : "never"} |
| 474 | showsVerticalScrollIndicator={false} | 475 | showsVerticalScrollIndicator={false} |
| 475 | keyboardDismissMode="on-drag" | 476 | keyboardDismissMode="on-drag" |
| 476 | keyboardShouldPersistTaps="handled" | 477 | keyboardShouldPersistTaps="handled" |
apps/mobile/src/features/threads/ThreadDetailScreen.tsx+8-1| ... | @@ -28,6 +28,7 @@ import type { StatusTone } from "../../components/StatusPill"; | ... | @@ -28,6 +28,7 @@ import type { StatusTone } from "../../components/StatusPill"; |
| 28 | import type { DraftComposerImageAttachment } from "../../lib/composerImages"; | 28 | import type { DraftComposerImageAttachment } from "../../lib/composerImages"; |
| 29 | import { CHAT_CONTENT_MAX_WIDTH, type LayoutVariant } from "../../lib/layout"; | 29 | import { CHAT_CONTENT_MAX_WIDTH, type LayoutVariant } from "../../lib/layout"; |
| 30 | import { scopedThreadKey } from "../../lib/scopedEntities"; | 30 | import { scopedThreadKey } from "../../lib/scopedEntities"; |
| 31 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 31 | import type { | 32 | import type { |
| 32 | PendingApproval, | 33 | PendingApproval, |
| 33 | PendingUserInput, | 34 | PendingUserInput, |
| ... | @@ -197,7 +198,13 @@ const WorkingDurationPill = memo(function WorkingDurationPill(props: { | ... | @@ -197,7 +198,13 @@ const WorkingDurationPill = memo(function WorkingDurationPill(props: { |
| 197 | entering={FadeInDown.duration(200)} | 198 | entering={FadeInDown.duration(200)} |
| 198 | exiting={FadeOut.duration(140)} | 199 | exiting={FadeOut.duration(140)} |
| 199 | > | 200 | > |
| 200 | <View className="self-start rounded-full border border-neutral-200/80 bg-neutral-50/90 px-3 py-2 dark:border-white/[0.08] dark:bg-white/[0.04]"> | 201 | <View |
| 202 | className={ | ||
| 203 | Platform.OS === "ios" && !NATIVE_LIQUID_GLASS_SUPPORTED | ||
| 204 | ? "self-start rounded-full border border-border bg-card px-3 py-2" | ||
| 205 | : "self-start rounded-full border border-neutral-200/80 bg-neutral-50/90 px-3 py-2 dark:border-white/[0.08] dark:bg-white/[0.04]" | ||
| 206 | } | ||
| 207 | > | ||
| 201 | <View className="flex-row items-center gap-2"> | 208 | <View className="flex-row items-center gap-2"> |
| 202 | <View className="flex-row items-center gap-1"> | 209 | <View className="flex-row items-center gap-1"> |
| 203 | <View className="h-1.5 w-1.5 rounded-full bg-neutral-400 dark:bg-neutral-500" /> | 210 | <View className="h-1.5 w-1.5 rounded-full bg-neutral-400 dark:bg-neutral-500" /> |
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx+5-2| ... | @@ -17,6 +17,7 @@ import Svg, { Defs, LinearGradient, Rect, Stop } from "react-native-svg"; | ... | @@ -17,6 +17,7 @@ import Svg, { Defs, LinearGradient, Rect, Stop } from "react-native-svg"; |
| 17 | import { AppText as Text } from "../../components/AppText"; | 17 | import { AppText as Text } from "../../components/AppText"; |
| 18 | import { ControlPillMenu } from "../../components/ControlPill"; | 18 | import { ControlPillMenu } from "../../components/ControlPill"; |
| 19 | import { SymbolView } from "../../components/AppSymbol"; | 19 | import { SymbolView } from "../../components/AppSymbol"; |
| 20 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 20 | import { NativeStackScreenOptions } from "../../native/StackHeader"; | 21 | import { NativeStackScreenOptions } from "../../native/StackHeader"; |
| 21 | import { scopedProjectKey, scopedThreadKey } from "../../lib/scopedEntities"; | 22 | import { scopedProjectKey, scopedThreadKey } from "../../lib/scopedEntities"; |
| 22 | import { useThemeColor } from "../../lib/useThemeColor"; | 23 | import { useThemeColor } from "../../lib/useThemeColor"; |
| ... | @@ -571,8 +572,10 @@ function ThreadNavigationSidebarPane( | ... | @@ -571,8 +572,10 @@ function ThreadNavigationSidebarPane( |
| 571 | itemsAreEqual={homeListItemsAreEqual} | 572 | itemsAreEqual={homeListItemsAreEqual} |
| 572 | keyExtractor={(item) => item.key} | 573 | keyExtractor={(item) => item.key} |
| 573 | renderItem={renderListItem} | 574 | renderItem={renderListItem} |
| 574 | automaticallyAdjustsScrollIndicatorInsets | 575 | automaticallyAdjustsScrollIndicatorInsets={NATIVE_LIQUID_GLASS_SUPPORTED} |
| 575 | contentInsetAdjustmentBehavior="automatic" | 576 | contentInsetAdjustmentBehavior={ |
| 577 | NATIVE_LIQUID_GLASS_SUPPORTED ? "automatic" : "never" | ||
| 578 | } | ||
| 576 | contentContainerStyle={[ | 579 | contentContainerStyle={[ |
| 577 | styles.threadListContent, | 580 | styles.threadListContent, |
| 578 | { | 581 | { |
apps/mobile/src/features/threads/ThreadRouteScreen.tsx+2-1| ... | @@ -23,6 +23,7 @@ import { | ... | @@ -23,6 +23,7 @@ import { |
| 23 | } from "../../components/AndroidScreenHeader"; | 23 | } from "../../components/AndroidScreenHeader"; |
| 24 | import { LoadingScreen } from "../../components/LoadingScreen"; | 24 | import { LoadingScreen } from "../../components/LoadingScreen"; |
| 25 | import { scopedThreadKey } from "../../lib/scopedEntities"; | 25 | import { scopedThreadKey } from "../../lib/scopedEntities"; |
| 26 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 26 | import { connectionTone } from "../connection/connectionTone"; | 27 | import { connectionTone } from "../connection/connectionTone"; |
| 27 | 28 | ||
| 28 | import { | 29 | import { |
| ... | @@ -278,7 +279,7 @@ function ThreadRouteContent( | ... | @@ -278,7 +279,7 @@ function ThreadRouteContent( |
| 278 | ); | 279 | ); |
| 279 | 280 | ||
| 280 | /* ─── Native header theming ──────────────────────────────────────── */ | 281 | /* ─── Native header theming ──────────────────────────────────────── */ |
| 281 | const usesNativeHeaderGlass = Platform.OS === "ios"; | 282 | const usesNativeHeaderGlass = NATIVE_LIQUID_GLASS_SUPPORTED; |
| 282 | const headerSubtitle = [ | 283 | const headerSubtitle = [ |
| 283 | selectedThreadProject?.title ?? null, | 284 | selectedThreadProject?.title ?? null, |
| 284 | selectedEnvironmentConnection?.environmentLabel ?? null, | 285 | selectedEnvironmentConnection?.environmentLabel ?? null, |
apps/mobile/src/features/threads/sidebar-navigation-shell.tsx+5-4| ... | @@ -11,6 +11,7 @@ import { | ... | @@ -11,6 +11,7 @@ import { |
| 11 | import type { ReactNode } from "react"; | 11 | import type { ReactNode } from "react"; |
| 12 | import { Platform, useColorScheme } from "react-native"; | 12 | import { Platform, useColorScheme } from "react-native"; |
| 13 | 13 | ||
| 14 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; | ||
| 14 | import { nativeHeaderScrollEdgeEffects } from "../../native/StackHeader"; | 15 | import { nativeHeaderScrollEdgeEffects } from "../../native/StackHeader"; |
| 15 | 16 | ||
| 16 | const SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects(Platform.OS, Platform.Version); | 17 | const SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects(Platform.OS, Platform.Version); |
| ... | @@ -33,12 +34,12 @@ const SIDEBAR_SCREEN_OPTIONS: SidebarScreenOptions = { | ... | @@ -33,12 +34,12 @@ const SIDEBAR_SCREEN_OPTIONS: SidebarScreenOptions = { |
| 33 | headerLargeTitle: false, | 34 | headerLargeTitle: false, |
| 34 | headerShadowVisible: false, | 35 | headerShadowVisible: false, |
| 35 | headerShown: true, | 36 | headerShown: true, |
| 36 | headerStyle: { backgroundColor: "transparent" }, | 37 | headerStyle: NATIVE_LIQUID_GLASS_SUPPORTED ? { backgroundColor: "transparent" } : undefined, |
| 37 | headerTitleStyle: { fontSize: 18, fontWeight: "800" }, | 38 | headerTitleStyle: { fontSize: 18, fontWeight: "800" }, |
| 38 | headerTransparent: true, | 39 | headerTransparent: NATIVE_LIQUID_GLASS_SUPPORTED, |
| 39 | scrollEdgeEffects: SCROLL_EDGE_EFFECTS, | 40 | scrollEdgeEffects: NATIVE_LIQUID_GLASS_SUPPORTED ? SCROLL_EDGE_EFFECTS : undefined, |
| 40 | title: "Threads", | 41 | title: "Threads", |
| 41 | unstable_navigationItemStyle: "editor", | 42 | unstable_navigationItemStyle: NATIVE_LIQUID_GLASS_SUPPORTED ? "editor" : undefined, |
| 42 | }; | 43 | }; |
| 43 | 44 | ||
| 44 | const SidebarStack = createNativeStackNavigator(); | 45 | const SidebarStack = createNativeStackNavigator(); |
apps/mobile/src/lib/native-glass-capability.test.ts created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | import { describe, expect, it } from "vite-plus/test"; | ||
| 2 | |||
| 3 | import { supportsNativeLiquidGlass } from "./native-glass-capability"; | ||
| 4 | |||
| 5 | describe("supportsNativeLiquidGlass", () => { | ||
| 6 | it("uses native liquid glass when iOS reports the capability", () => { | ||
| 7 | expect(supportsNativeLiquidGlass("ios", true)).toBe(true); | ||
| 8 | }); | ||
| 9 | |||
| 10 | it("keeps pre-glass iOS on the solid fallback", () => { | ||
| 11 | expect(supportsNativeLiquidGlass("ios", false)).toBe(false); | ||
| 12 | }); | ||
| 13 | |||
| 14 | it("does not enable iOS liquid-glass layout behavior on other platforms", () => { | ||
| 15 | expect(supportsNativeLiquidGlass("android", true)).toBe(false); | ||
| 16 | expect(supportsNativeLiquidGlass("web", true)).toBe(false); | ||
| 17 | }); | ||
| 18 | }); | ||
apps/mobile/src/lib/native-glass-capability.ts created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | export function supportsNativeLiquidGlass( | ||
| 2 | platform: string, | ||
| 3 | nativeCapabilityAvailable: boolean, | ||
| 4 | ): boolean { | ||
| 5 | return platform === "ios" && nativeCapabilityAvailable; | ||
| 6 | } | ||
apps/mobile/src/native/native-glass.ts created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | import { isLiquidGlassSupported } from "@callstack/liquid-glass"; | ||
| 2 | import { Platform } from "react-native"; | ||
| 3 | |||
| 4 | import { supportsNativeLiquidGlass } from "../lib/native-glass-capability"; | ||
| 5 | |||
| 6 | export const NATIVE_LIQUID_GLASS_SUPPORTED = supportsNativeLiquidGlass( | ||
| 7 | Platform.OS, | ||
| 8 | isLiquidGlassSupported, | ||
| 9 | ); | ||