authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-28 23:27:31-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-28 23:27:31-07:00
log053affbed2659f90cd1b1efaaa7a75865c4131c7
treee1a151cd1682cb866c5afdb2b2615bdb48ea8292
parent074bcd6dc897f9c28b1bbc04737daa6ee3d8e40f
signature Signed by PGP key B5690EEEBB952194

fix(mobile): prevent header overflow and back-button artifacts (#8624)


7 files changed, 96 insertions(+), 100 deletions(-)

apps/mobile/src/components/CompactBrandTitle.tsx+4-38
......@@ -1,32 +1,18 @@
11import Constants from "expo-constants";
2import type {
3 NativeStackHeaderItem,
4 NativeStackNavigationOptions,
5} from "@react-navigation/native-stack";
2import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
63import { Platform, View } from "react-native";
74
85import { AppText as Text } from "./AppText";
96import { T3Wordmark } from "./T3Wordmark";
107import { IPAD_HOME_TITLE_OFFSET } from "../lib/layoutMetrics";
118import { resolveMobileStageLabel } from "../lib/mobileBranding";
12import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../native/native-glass";
13
14// Native leading items inherit different UIKit margins than title views.
15const IOS_NATIVE_LEADING_TITLE_OFFSET = -6;
16const IPAD_NATIVE_LEADING_TITLE_OFFSET = 7;
17
18// Branding and connection status share an identity distinct from navigation buttons.
19export const BRAND_HEADER_ITEM_IDENTIFIER = "workspace-brand";
209
2110/**
2211 * Horizontal correction applied to content rendered in the brand title slot,
2312 * shared with the connection-status swap so both align identically.
2413 */
25export function brandTitleOffset(nativeLeadingItem: boolean): number {
14export function brandTitleOffset(): number {
2615 if (Platform.OS !== "ios") return 0;
27 if (nativeLeadingItem) {
28 return Platform.isPad ? IPAD_NATIVE_LEADING_TITLE_OFFSET : IOS_NATIVE_LEADING_TITLE_OFFSET;
29 }
3016 return Platform.isPad ? IPAD_HOME_TITLE_OFFSET : 0;
3117}
3218
......@@ -36,11 +22,10 @@ export function brandTitleOffset(nativeLeadingItem: boolean): number {
3622export function CompactBrandTitle(
3723 props: {
3824 readonly allowFontScaling?: boolean;
39 readonly nativeLeadingItem?: boolean;
4025 } = {},
4126) {
4227 const stageLabel = resolveMobileStageLabel(Constants.expoConfig?.extra?.appVariant);
43 const titleOffset = brandTitleOffset(props.nativeLeadingItem === true);
28 const titleOffset = brandTitleOffset();
4429
4530 return (
4631 <View
......@@ -74,32 +59,13 @@ export function renderCompactBrandTitle() {
7459 return <CompactBrandTitle allowFontScaling={Platform.OS === "ios"} />;
7560}
7661
77export function renderCompactBrandHeaderItems(): NativeStackHeaderItem[] {
78 return [
79 {
80 element: <CompactBrandTitle nativeLeadingItem />,
81 hidesSharedBackground: true,
82 identifier: BRAND_HEADER_ITEM_IDENTIFIER,
83 type: "custom",
84 },
85 ];
86}
87
8862export function getCompactBrandHeaderOptions(
8963 fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"],
9064): NativeStackNavigationOptions {
91 if (Platform.OS === "ios" && NATIVE_LIQUID_GLASS_SUPPORTED) {
92 return {
93 headerTitle: "Threads",
94 headerTitleStyle: { color: "transparent", fontSize: 18, fontWeight: "800" },
95 title: "Threads",
96 unstable_headerLeftItems: renderCompactBrandHeaderItems,
97 };
98 }
99
10065 return {
10166 headerTitle: renderCompactBrandTitle,
10267 headerTitleStyle: fallbackTitleStyle,
10368 title: "Threads",
69 unstable_headerLeftItems: undefined,
10470 };
10571}
apps/mobile/src/features/home/HomeRouteScreen.tsx+4-1
......@@ -2,7 +2,7 @@ import * as Arr from "effect/Array";
22import * as Order from "effect/Order";
33import { useNavigation } from "@react-navigation/native";
44import { useEffect, useMemo, useState } from "react";
5import { Platform } from "react-native";
5import { Platform, useWindowDimensions } from "react-native";
66
77import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
88import { useProjects, useThreadShells } from "../../state/entities";
......@@ -25,6 +25,7 @@ import { getConnectionAwareBrandHeaderOptions } from "./WorkspaceConnectionTitle
2525/* ─── Route screen ───────────────────────────────────────────────────── */
2626
2727export function HomeRouteScreen() {
28 const { width: windowWidth } = useWindowDimensions();
2829 const { layout } = useAdaptiveWorkspaceLayout();
2930 const projects = useProjects();
3031 const threads = useThreadShells();
......@@ -138,8 +139,10 @@ export function HomeRouteScreen() {
138139 shallow-merged. The brand slot also doubles as the connection
139140 status surface while an environment reconnects. */}
140141 <NativeStackScreenOptions
142 optionsVersion={windowWidth}
141143 options={{
142144 ...getConnectionAwareBrandHeaderOptions({
145 headerWidth: windowWidth,
143146 onOpenEnvironments: () =>
144147 navigation.navigate("SettingsSheet", {
145148 screen: "SettingsContent",
apps/mobile/src/features/home/WorkspaceConnectionTitle.tsx+21-35
......@@ -1,18 +1,14 @@
1import type {
2 NativeStackHeaderItem,
3 NativeStackNavigationOptions,
4} from "@react-navigation/native-stack";
1import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
52import { useEffect, useRef, useState, type ReactNode } from "react";
6import { ActivityIndicator, Animated, Platform, Pressable, View } from "react-native";
3import { ActivityIndicator, Animated, Pressable, View } from "react-native";
74
85import { SymbolView } from "../../components/AppSymbol";
96import { AppText as Text } from "../../components/AppText";
107import {
11 BRAND_HEADER_ITEM_IDENTIFIER,
128 brandTitleOffset,
139 CompactBrandTitle,
10 getCompactBrandHeaderOptions,
1411} from "../../components/CompactBrandTitle";
15import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
1612import { useWorkspaceState } from "../../state/workspace";
1713import {
1814 workspaceConnectionStatusPresentation,
......@@ -55,7 +51,11 @@ function useDelayedConnectionStatus(): WorkspaceConnectionStatusPresentation | n
5551 * native-driver animated nodes blank the re-hosted view entirely. The JS driver
5652 * updates opacity through the ordinary style path, which those subviews handle.
5753 */
58function StatusFadeIn(props: { readonly children: ReactNode; readonly grow?: boolean }) {
54function StatusFadeIn(props: {
55 readonly children: ReactNode;
56 readonly grow?: boolean;
57 readonly maxWidth?: number;
58}) {
5959 const opacity = useRef(new Animated.Value(0)).current;
6060
6161 useEffect(() => {
......@@ -71,7 +71,7 @@ function StatusFadeIn(props: { readonly children: ReactNode; readonly grow?: boo
7171 return (
7272 <Animated.View
7373 style={[
74 { alignItems: "center", flexDirection: "row", opacity },
74 { alignItems: "center", flexDirection: "row", maxWidth: props.maxWidth, opacity },
7575 props.grow ? { flex: 1, minWidth: 0 } : null,
7676 ]}
7777 >
......@@ -100,6 +100,8 @@ export function WorkspaceConnectionTitle(props: {
100100 readonly size?: "navbar" | "pageTitle";
101101 /** Horizontal correction so the status aligns with the brand in native title slots. */
102102 readonly statusOffset?: number;
103 /** Space available beside the native header actions. */
104 readonly maxWidth?: number;
103105}) {
104106 const status = useDelayedConnectionStatus();
105107 const size = props.size ?? "navbar";
......@@ -115,7 +117,7 @@ export function WorkspaceConnectionTitle(props: {
115117 }
116118
117119 return (
118 <StatusFadeIn grow={props.grow}>
120 <StatusFadeIn grow={props.grow} maxWidth={props.maxWidth}>
119121 <Pressable
120122 accessibilityHint="Opens environment settings"
121123 accessibilityLabel={status.label}
......@@ -124,7 +126,7 @@ export function WorkspaceConnectionTitle(props: {
124126 hitSlop={8}
125127 onPress={props.onPress}
126128 className="flex-row items-center gap-2"
127 style={{ marginLeft: props.statusOffset ?? 0 }}
129 style={{ flexShrink: 1, marginLeft: props.statusOffset ?? 0 }}
128130 >
129131 {status.showsProgress ? (
130132 <ActivityIndicator colorClassName={"accent-icon-muted"} size="small" />
......@@ -158,40 +160,24 @@ export function WorkspaceConnectionTitle(props: {
158160 * this over the static brand options at mount.
159161 */
160162export function getConnectionAwareBrandHeaderOptions(opts: {
163 readonly headerWidth: number;
164 readonly trailingItemCount?: number;
161165 readonly onOpenEnvironments: () => void;
162166 readonly fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"];
163167}): NativeStackNavigationOptions {
164 if (Platform.OS === "ios" && NATIVE_LIQUID_GLASS_SUPPORTED) {
165 return {
166 headerTitle: "Threads",
167 headerTitleStyle: { color: "transparent", fontSize: 18, fontWeight: "800" },
168 title: "Threads",
169 unstable_headerLeftItems: (): NativeStackHeaderItem[] => [
170 {
171 element: (
172 <WorkspaceConnectionTitle
173 brand={<CompactBrandTitle nativeLeadingItem />}
174 onPress={opts.onOpenEnvironments}
175 statusOffset={brandTitleOffset(true)}
176 />
177 ),
178 hidesSharedBackground: true,
179 identifier: BRAND_HEADER_ITEM_IDENTIFIER,
180 type: "custom",
181 },
182 ],
183 };
184 }
168 // Leave room for bar margins, title spacing and the 44-point native actions.
169 // Long status labels must not push Settings into UIKit's overflow menu.
170 const maxWidth = Math.max(0, opts.headerWidth - 64 - 44 * (opts.trailingItemCount ?? 1));
185171
186172 return {
173 ...getCompactBrandHeaderOptions(opts.fallbackTitleStyle),
187174 headerTitle: () => (
188175 <WorkspaceConnectionTitle
189176 brand={<CompactBrandTitle />}
177 maxWidth={maxWidth}
190178 onPress={opts.onOpenEnvironments}
191 statusOffset={brandTitleOffset(false)}
179 statusOffset={brandTitleOffset()}
192180 />
193181 ),
194 headerTitleStyle: opts.fallbackTitleStyle,
195 title: "Threads",
196182 };
197183}
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx+3-1
......@@ -1158,12 +1158,14 @@ function ThreadNavigationSidebarPane(
11581158 return (
11591159 <>
11601160 <NativeStackScreenOptions
1161 optionsVersion={nativeHeaderItems}
1161 optionsVersion={[nativeHeaderItems, props.width]}
11621162 options={{
11631163 // Re-applies the shell's static brand slot with the
11641164 // connection-status swap so reconnects surface in the header
11651165 // instead of shifting the list.
11661166 ...getConnectionAwareBrandHeaderOptions({
1167 headerWidth: props.width,
1168 trailingItemCount: nativeHeaderItems.length,
11671169 onOpenEnvironments: props.onOpenEnvironmentSettings,
11681170 fallbackTitleStyle: { fontSize: 18, fontWeight: "800" },
11691171 }),
docs/internals/mobile-navigation.md+24-12
......@@ -5,23 +5,30 @@ The iOS Home and thread routes share the root native stack in
55controller lets UIKit animate the header between routes. The iPad sidebar owns
66a separate, single-screen stack; Android uses its own in-flow headers.
77
8On Liquid Glass iOS, Home keeps a transparent native title and renders its brand
9as a custom leading item. A custom `headerTitle` previously shortened the scroll
10fade and removed the blur behind the logo. Preserve the native title when
11changing the brand or its connection-status replacement.
12
13Both brand states use `BRAND_HEADER_ITEM_IDENTIFIER` from
14[`CompactBrandTitle.tsx`](../../apps/mobile/src/components/CompactBrandTitle.tsx).
15The native-stack and react-native-screens patches forward custom item identifiers
16to `UIBarButtonItem.identifier`, just as they do for native buttons and menus.
17UIKit otherwise matches transition items using their position and content, so
18the brand needs an explicit identity separate from navigation controls. Connection
19status changes retain that identity. See [Apple's identifier documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/identifier).
8Home and the iPad sidebar render their brand and connection status through
9`headerTitle`, with `Threads` retained as the route title. The editor-style native
10bar aligns that title on the leading side. Do not model the brand as a toolbar
11button: on iOS 26.5 UIKit morphs a background-free custom leading item's rectangle
12into the next screen's glass back button, even with distinct item identifiers.
13Using the title slot lets the brand and native back button animate separately.
14
15The connection-status title has a maximum width based on its header's width and
16trailing actions. A long environment name or larger text must not push Settings
17into UIKit's overflow menu. Keep the full status as the accessibility label
18while visually truncating it. The iPad sidebar uses its pane width, not the full
19window width.
20
21On iOS 26, UIKit does not recognize Fabric's custom text views when shaping the
22native scroll-edge fade. The screens patch gives custom title subviews an empty,
23non-interactive native label matching their bounds. This supplies the fade's
24geometry without drawing anything or turning the title back into a toolbar item.
25Check the top scroll fade as well as push/pop when changing these title views.
2026
2127The react-native-screens patch caches leading, trailing, and center item groups
2228independently. A button can belong to only one group: constructing another group
2329with the same button removes it from the previous one. Unrelated menu updates
2430must therefore preserve the other groups while UIKit may be animating them.
31
2532Each cache includes the owning header config, its item values, and custom native
2633item identities, so changed content or remounted event emitters still rebuild.
2734
......@@ -29,3 +36,8 @@ Custom header identifiers require native code generation and a new mobile build;
2936an over-the-air JavaScript update alone is insufficient. The Android view manager
3037implements the generated identifier setter as a no-op because this behavior is
3138specific to iOS 26 and later.
39
40After changing a dependency patch, refresh CocoaPods before rebuilding an
41existing iOS project. pnpm installs each patch hash in a different directory;
42an old Pods project can keep compiling the previous directory even though Metro
43and `apps/mobile/node_modules` resolve to the new patch.
patches/react-native-screens@4.26.2.patch+34-7
......@@ -1107,18 +1107,45 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..a60b96b44e85d3f4b29dc0d190b95ab6
11071107
11081108 if (needsNavigationControllerLayout) {
11091109diff --git a/ios/RNSScreenStackHeaderSubview.mm b/ios/RNSScreenStackHeaderSubview.mm
1110index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a362241a72075 100644
1110index add33c4807e038dcd846f6c72b2fc472ded02809..489afa5fa2da09c1ec6986d1832b58bb595396ae 100644
11111111--- a/ios/RNSScreenStackHeaderSubview.mm
11121112+++ b/ios/RNSScreenStackHeaderSubview.mm
1113@@ -20,6 +20,7 @@ @implementation RNSScreenStackHeaderSubview {
1113@@ -20,7 +20,9 @@ @implementation RNSScreenStackHeaderSubview {
11141114 // This is a strong reference to UIBarButtonItem which creates a retain cycle.
11151115 // The cycle is cleared via `invalidateUIBarButtonItem` method, called by `invalidate` callback.
11161116 UIBarButtonItem *_barButtonItem;
11171117+ NSString *_barButtonItemIdentifier;
11181118 BOOL _hidesSharedBackground;
1119+ UILabel *_titleScrollEdgeEffectGuide;
11191120 }
11201121
1121@@ -124,6 +125,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
1122 #pragma mark - Common
1123@@ -102,5 +104,24 @@ - (void)updateShadowStateWithFrame:(CGRect)frame
1124 - (void)layoutSubviews
1125 {
1126 [super layoutSubviews];
1127+#if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
1128+ if (@available(iOS 26.0, *)) {
1129+ if (_type == RNSScreenStackHeaderSubviewTypeTitle || _type == RNSScreenStackHeaderSubviewTypeCenter) {
1130+ // UIKit shapes the navigation bar's scroll-edge fade around native labels,
1131+ // but does not recognize Fabric's custom text views. An empty, non-interactive
1132+ // label supplies the title's geometry without drawing or becoming a bar item.
1133+ if (_titleScrollEdgeEffectGuide == nil) {
1134+ _titleScrollEdgeEffectGuide = [UILabel new];
1135+ _titleScrollEdgeEffectGuide.accessibilityElementsHidden = YES;
1136+ // Append after Fabric's children so their mount/unmount indices stay intact.
1137+ [self addSubview:_titleScrollEdgeEffectGuide];
1138+ }
1139+ _titleScrollEdgeEffectGuide.frame = self.bounds;
1140+ } else {
1141+ [_titleScrollEdgeEffectGuide removeFromSuperview];
1142+ _titleScrollEdgeEffectGuide = nil;
1143+ }
1144+ }
1145+#endif
1146 [self updateShadowStateInContextOfAncestorView:[self findNavigationBar]];
1147 }
1148@@ -124,6 +145,7 @@ - (void)updateProps:(react::Props::Shared const &)props oldProps:(react::Props::
11221149 const auto &newHeaderSubviewProps = *std::static_pointer_cast<const react::RNSScreenStackHeaderSubviewProps>(props);
11231150
11241151 [self setType:[RNSConvert RNSScreenStackHeaderSubviewTypeFromCppEquivalent:newHeaderSubviewProps.type]];
......@@ -1126,7 +1153,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
11261153 [self setHidesSharedBackground:newHeaderSubviewProps.hidesSharedBackground];
11271154 [self setSynchronousShadowStateUpdatesEnabled:newHeaderSubviewProps.synchronousShadowStateUpdatesEnabled];
11281155 [super updateProps:props oldProps:oldProps];
1129@@ -276,7 +278,14 @@ - (void)configureBarButtonItem
1156@@ -276,7 +298,14 @@ - (void)configureBarButtonItem
11301157 #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
11311158 if (@available(iOS 26.0, *)) {
11321159 if (_barButtonItem != nil) {
......@@ -1142,7 +1169,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
11421169 }
11431170 }
11441171 #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
1145@@ -284,10 +293,22 @@ - (void)configureBarButtonItem
1172@@ -284,10 +313,22 @@ - (void)configureBarButtonItem
11461173
11471174 - (void)setHidesSharedBackground:(BOOL)hidesSharedBackground
11481175 {
......@@ -1166,7 +1193,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
11661193
11671194 // Needed because of this: https://github.com/facebook/react-native/pull/37274
11681195diff --git a/lib/commonjs/components/ScreenStackHeaderConfig.js b/lib/commonjs/components/ScreenStackHeaderConfig.js
1169index 8c509f2657105cfd86448347c1a01fb73651ff9e..5e3b5185c0ea33c6b98f5daed69239d2c0d0ce90 100644
1196index 8c509f2657105cfd86448347c1a01fb73651ff9e..e668e089a1788973cb122a150be7846364a2a2cf 100644
11701197--- a/lib/commonjs/components/ScreenStackHeaderConfig.js
11711198+++ b/lib/commonjs/components/ScreenStackHeaderConfig.js
11721199@@ -26,17 +26,42 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
......@@ -1338,7 +1365,7 @@ index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..9dca2ab640e92161eb647b819a989acd
13381365 //# sourceMappingURL=prepareHeaderBarButtonItems.js.map
13391366\ No newline at end of file
13401367diff --git a/lib/module/components/ScreenStackHeaderConfig.js b/lib/module/components/ScreenStackHeaderConfig.js
1341index 68e3ba6a78314aba908d07098bb6982d11e6ee80..b787b3cf0284ffb81c80360a06c4059277bf4c1e 100644
1368index 68e3ba6a78314aba908d07098bb6982d11e6ee80..db5d17bfdac19ed3e664b55b6a73ce49766ac7c5 100644
13421369--- a/lib/module/components/ScreenStackHeaderConfig.js
13431370+++ b/lib/module/components/ScreenStackHeaderConfig.js
13441371@@ -22,17 +22,42 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
pnpm-lock.yaml+6-6
......@@ -94,7 +94,7 @@ patchedDependencies:
9494 react-native-gesture-handler@2.32.0: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d3
9595 react-native-keyboard-controller@1.21.13: 20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008
9696 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675
97 react-native-screens@4.26.2: 5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d
97 react-native-screens@4.26.2: 149bef30a66351ea9b26b42f87b78c539cb52b880f2387bb323ec80dcac84006
9898
9999importers:
100100
......@@ -248,7 +248,7 @@ importers:
248248 version: 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
249249 '@react-navigation/native-stack':
250250 specifier: 7.17.6
251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(1d8c6f507069f6f6aada9b6419c84a6c)
251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(d307537762dff86bcf277a4ec64a11d8)
252252 '@shikijs/core':
253253 specifier: 4.2.0
254254 version: 4.2.0
......@@ -416,7 +416,7 @@ importers:
416416 version: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
417417 react-native-screens:
418418 specifier: ~4.26.0
419 version: 4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
419 version: 4.26.2(patch_hash=149bef30a66351ea9b26b42f87b78c539cb52b880f2387bb323ec80dcac84006)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
420420 react-native-shiki-engine:
421421 specifier: ^0.3.12
422422 version: 0.3.12(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
......@@ -13880,7 +13880,7 @@ snapshots:
1388013880 optionalDependencies:
1388113881 '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
1388213882
13883 '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(1d8c6f507069f6f6aada9b6419c84a6c)':
13883 '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(d307537762dff86bcf277a4ec64a11d8)':
1388413884 dependencies:
1388513885 '@react-navigation/elements': 2.9.26(c10301b6e0c42fc6434d2b643197a81e)
1388613886 '@react-navigation/native': 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
......@@ -13888,7 +13888,7 @@ snapshots:
1388813888 react: 19.2.3
1388913889 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
1389013890 react-native-safe-area-context: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
13891 react-native-screens: 4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
13891 react-native-screens: 4.26.2(patch_hash=149bef30a66351ea9b26b42f87b78c539cb52b880f2387bb323ec80dcac84006)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
1389213892 sf-symbols-typescript: 2.2.0
1389313893 warn-once: 0.1.1
1389413894 transitivePeerDependencies:
......@@ -19492,7 +19492,7 @@ snapshots:
1949219492 react: 19.2.3
1949319493 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
1949419494
19495 react-native-screens@4.26.2(patch_hash=5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3):
19495 react-native-screens@4.26.2(patch_hash=149bef30a66351ea9b26b42f87b78c539cb52b880f2387bb323ec80dcac84006)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3):
1949619496 dependencies:
1949719497 react: 19.2.3
1949819498 react-freeze: 1.0.4(react@19.2.3)