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 @@...@@ -1,32 +1,18 @@
1import Constants from "expo-constants";1import Constants from "expo-constants";
2import type {2import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
3 NativeStackHeaderItem,
4 NativeStackNavigationOptions,
5} from "@react-navigation/native-stack";
6import { Platform, View } from "react-native";3import { Platform, View } from "react-native";
74
8import { AppText as Text } from "./AppText";5import { AppText as Text } from "./AppText";
9import { T3Wordmark } from "./T3Wordmark";6import { T3Wordmark } from "./T3Wordmark";
10import { IPAD_HOME_TITLE_OFFSET } from "../lib/layoutMetrics";7import { IPAD_HOME_TITLE_OFFSET } from "../lib/layoutMetrics";
11import { resolveMobileStageLabel } from "../lib/mobileBranding";8import { 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
21/**10/**
22 * Horizontal correction applied to content rendered in the brand title slot,11 * Horizontal correction applied to content rendered in the brand title slot,
23 * shared with the connection-status swap so both align identically.12 * shared with the connection-status swap so both align identically.
24 */13 */
25export function brandTitleOffset(nativeLeadingItem: boolean): number {14export function brandTitleOffset(): number {
26 if (Platform.OS !== "ios") return 0;15 if (Platform.OS !== "ios") return 0;
27 if (nativeLeadingItem) {
28 return Platform.isPad ? IPAD_NATIVE_LEADING_TITLE_OFFSET : IOS_NATIVE_LEADING_TITLE_OFFSET;
29 }
30 return Platform.isPad ? IPAD_HOME_TITLE_OFFSET : 0;16 return Platform.isPad ? IPAD_HOME_TITLE_OFFSET : 0;
31}17}
3218
...@@ -36,11 +22,10 @@ export function brandTitleOffset(nativeLeadingItem: boolean): number {...@@ -36,11 +22,10 @@ export function brandTitleOffset(nativeLeadingItem: boolean): number {
36export function CompactBrandTitle(22export function CompactBrandTitle(
37 props: {23 props: {
38 readonly allowFontScaling?: boolean;24 readonly allowFontScaling?: boolean;
39 readonly nativeLeadingItem?: boolean;
40 } = {},25 } = {},
41) {26) {
42 const stageLabel = resolveMobileStageLabel(Constants.expoConfig?.extra?.appVariant);27 const stageLabel = resolveMobileStageLabel(Constants.expoConfig?.extra?.appVariant);
43 const titleOffset = brandTitleOffset(props.nativeLeadingItem === true);28 const titleOffset = brandTitleOffset();
4429
45 return (30 return (
46 <View31 <View
...@@ -74,32 +59,13 @@ export function renderCompactBrandTitle() {...@@ -74,32 +59,13 @@ export function renderCompactBrandTitle() {
74 return <CompactBrandTitle allowFontScaling={Platform.OS === "ios"} />;59 return <CompactBrandTitle allowFontScaling={Platform.OS === "ios"} />;
75}60}
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
88export function getCompactBrandHeaderOptions(62export function getCompactBrandHeaderOptions(
89 fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"],63 fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"],
90): NativeStackNavigationOptions {64): 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
100 return {65 return {
101 headerTitle: renderCompactBrandTitle,66 headerTitle: renderCompactBrandTitle,
102 headerTitleStyle: fallbackTitleStyle,67 headerTitleStyle: fallbackTitleStyle,
103 title: "Threads",68 title: "Threads",
69 unstable_headerLeftItems: undefined,
104 };70 };
105}71}
apps/mobile/src/features/home/HomeRouteScreen.tsx+4-1
...@@ -2,7 +2,7 @@ import * as Arr from "effect/Array";...@@ -2,7 +2,7 @@ import * as Arr from "effect/Array";
2import * as Order from "effect/Order";2import * as Order from "effect/Order";
3import { useNavigation } from "@react-navigation/native";3import { useNavigation } from "@react-navigation/native";
4import { useEffect, useMemo, useState } from "react";4import { useEffect, useMemo, useState } from "react";
5import { Platform } from "react-native";5import { Platform, useWindowDimensions } from "react-native";
66
7import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";7import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
8import { useProjects, useThreadShells } from "../../state/entities";8import { useProjects, useThreadShells } from "../../state/entities";
...@@ -25,6 +25,7 @@ import { getConnectionAwareBrandHeaderOptions } from "./WorkspaceConnectionTitle...@@ -25,6 +25,7 @@ import { getConnectionAwareBrandHeaderOptions } from "./WorkspaceConnectionTitle
25/* ─── Route screen ───────────────────────────────────────────────────── */25/* ─── Route screen ───────────────────────────────────────────────────── */
2626
27export function HomeRouteScreen() {27export function HomeRouteScreen() {
28 const { width: windowWidth } = useWindowDimensions();
28 const { layout } = useAdaptiveWorkspaceLayout();29 const { layout } = useAdaptiveWorkspaceLayout();
29 const projects = useProjects();30 const projects = useProjects();
30 const threads = useThreadShells();31 const threads = useThreadShells();
...@@ -138,8 +139,10 @@ export function HomeRouteScreen() {...@@ -138,8 +139,10 @@ export function HomeRouteScreen() {
138 shallow-merged. The brand slot also doubles as the connection139 shallow-merged. The brand slot also doubles as the connection
139 status surface while an environment reconnects. */}140 status surface while an environment reconnects. */}
140 <NativeStackScreenOptions141 <NativeStackScreenOptions
142 optionsVersion={windowWidth}
141 options={{143 options={{
142 ...getConnectionAwareBrandHeaderOptions({144 ...getConnectionAwareBrandHeaderOptions({
145 headerWidth: windowWidth,
143 onOpenEnvironments: () =>146 onOpenEnvironments: () =>
144 navigation.navigate("SettingsSheet", {147 navigation.navigate("SettingsSheet", {
145 screen: "SettingsContent",148 screen: "SettingsContent",
apps/mobile/src/features/home/WorkspaceConnectionTitle.tsx+21-35
...@@ -1,18 +1,14 @@...@@ -1,18 +1,14 @@
1import type {1import type { NativeStackNavigationOptions } from "@react-navigation/native-stack";
2 NativeStackHeaderItem,
3 NativeStackNavigationOptions,
4} from "@react-navigation/native-stack";
5import { useEffect, useRef, useState, type ReactNode } from "react";2import { 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
8import { SymbolView } from "../../components/AppSymbol";5import { SymbolView } from "../../components/AppSymbol";
9import { AppText as Text } from "../../components/AppText";6import { AppText as Text } from "../../components/AppText";
10import {7import {
11 BRAND_HEADER_ITEM_IDENTIFIER,
12 brandTitleOffset,8 brandTitleOffset,
13 CompactBrandTitle,9 CompactBrandTitle,
10 getCompactBrandHeaderOptions,
14} from "../../components/CompactBrandTitle";11} from "../../components/CompactBrandTitle";
15import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
16import { useWorkspaceState } from "../../state/workspace";12import { useWorkspaceState } from "../../state/workspace";
17import {13import {
18 workspaceConnectionStatusPresentation,14 workspaceConnectionStatusPresentation,
...@@ -55,7 +51,11 @@ function useDelayedConnectionStatus(): WorkspaceConnectionStatusPresentation | n...@@ -55,7 +51,11 @@ function useDelayedConnectionStatus(): WorkspaceConnectionStatusPresentation | n
55 * native-driver animated nodes blank the re-hosted view entirely. The JS driver51 * native-driver animated nodes blank the re-hosted view entirely. The JS driver
56 * updates opacity through the ordinary style path, which those subviews handle.52 * updates opacity through the ordinary style path, which those subviews handle.
57 */53 */
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}) {
59 const opacity = useRef(new Animated.Value(0)).current;59 const opacity = useRef(new Animated.Value(0)).current;
6060
61 useEffect(() => {61 useEffect(() => {
...@@ -71,7 +71,7 @@ function StatusFadeIn(props: { readonly children: ReactNode; readonly grow?: boo...@@ -71,7 +71,7 @@ function StatusFadeIn(props: { readonly children: ReactNode; readonly grow?: boo
71 return (71 return (
72 <Animated.View72 <Animated.View
73 style={[73 style={[
74 { alignItems: "center", flexDirection: "row", opacity },74 { alignItems: "center", flexDirection: "row", maxWidth: props.maxWidth, opacity },
75 props.grow ? { flex: 1, minWidth: 0 } : null,75 props.grow ? { flex: 1, minWidth: 0 } : null,
76 ]}76 ]}
77 >77 >
...@@ -100,6 +100,8 @@ export function WorkspaceConnectionTitle(props: {...@@ -100,6 +100,8 @@ export function WorkspaceConnectionTitle(props: {
100 readonly size?: "navbar" | "pageTitle";100 readonly size?: "navbar" | "pageTitle";
101 /** Horizontal correction so the status aligns with the brand in native title slots. */101 /** Horizontal correction so the status aligns with the brand in native title slots. */
102 readonly statusOffset?: number;102 readonly statusOffset?: number;
103 /** Space available beside the native header actions. */
104 readonly maxWidth?: number;
103}) {105}) {
104 const status = useDelayedConnectionStatus();106 const status = useDelayedConnectionStatus();
105 const size = props.size ?? "navbar";107 const size = props.size ?? "navbar";
...@@ -115,7 +117,7 @@ export function WorkspaceConnectionTitle(props: {...@@ -115,7 +117,7 @@ export function WorkspaceConnectionTitle(props: {
115 }117 }
116118
117 return (119 return (
118 <StatusFadeIn grow={props.grow}>120 <StatusFadeIn grow={props.grow} maxWidth={props.maxWidth}>
119 <Pressable121 <Pressable
120 accessibilityHint="Opens environment settings"122 accessibilityHint="Opens environment settings"
121 accessibilityLabel={status.label}123 accessibilityLabel={status.label}
...@@ -124,7 +126,7 @@ export function WorkspaceConnectionTitle(props: {...@@ -124,7 +126,7 @@ export function WorkspaceConnectionTitle(props: {
124 hitSlop={8}126 hitSlop={8}
125 onPress={props.onPress}127 onPress={props.onPress}
126 className="flex-row items-center gap-2"128 className="flex-row items-center gap-2"
127 style={{ marginLeft: props.statusOffset ?? 0 }}129 style={{ flexShrink: 1, marginLeft: props.statusOffset ?? 0 }}
128 >130 >
129 {status.showsProgress ? (131 {status.showsProgress ? (
130 <ActivityIndicator colorClassName={"accent-icon-muted"} size="small" />132 <ActivityIndicator colorClassName={"accent-icon-muted"} size="small" />
...@@ -158,40 +160,24 @@ export function WorkspaceConnectionTitle(props: {...@@ -158,40 +160,24 @@ export function WorkspaceConnectionTitle(props: {
158 * this over the static brand options at mount.160 * this over the static brand options at mount.
159 */161 */
160export function getConnectionAwareBrandHeaderOptions(opts: {162export function getConnectionAwareBrandHeaderOptions(opts: {
163 readonly headerWidth: number;
164 readonly trailingItemCount?: number;
161 readonly onOpenEnvironments: () => void;165 readonly onOpenEnvironments: () => void;
162 readonly fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"];166 readonly fallbackTitleStyle?: NativeStackNavigationOptions["headerTitleStyle"];
163}): NativeStackNavigationOptions {167}): NativeStackNavigationOptions {
164 if (Platform.OS === "ios" && NATIVE_LIQUID_GLASS_SUPPORTED) {168 // Leave room for bar margins, title spacing and the 44-point native actions.
165 return {169 // Long status labels must not push Settings into UIKit's overflow menu.
166 headerTitle: "Threads",170 const maxWidth = Math.max(0, opts.headerWidth - 64 - 44 * (opts.trailingItemCount ?? 1));
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 }
185171
186 return {172 return {
173 ...getCompactBrandHeaderOptions(opts.fallbackTitleStyle),
187 headerTitle: () => (174 headerTitle: () => (
188 <WorkspaceConnectionTitle175 <WorkspaceConnectionTitle
189 brand={<CompactBrandTitle />}176 brand={<CompactBrandTitle />}
177 maxWidth={maxWidth}
190 onPress={opts.onOpenEnvironments}178 onPress={opts.onOpenEnvironments}
191 statusOffset={brandTitleOffset(false)}179 statusOffset={brandTitleOffset()}
192 />180 />
193 ),181 ),
194 headerTitleStyle: opts.fallbackTitleStyle,
195 title: "Threads",
196 };182 };
197}183}
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx+3-1
...@@ -1158,12 +1158,14 @@ function ThreadNavigationSidebarPane(...@@ -1158,12 +1158,14 @@ function ThreadNavigationSidebarPane(
1158 return (1158 return (
1159 <>1159 <>
1160 <NativeStackScreenOptions1160 <NativeStackScreenOptions
1161 optionsVersion={nativeHeaderItems}1161 optionsVersion={[nativeHeaderItems, props.width]}
1162 options={{1162 options={{
1163 // Re-applies the shell's static brand slot with the1163 // Re-applies the shell's static brand slot with the
1164 // connection-status swap so reconnects surface in the header1164 // connection-status swap so reconnects surface in the header
1165 // instead of shifting the list.1165 // instead of shifting the list.
1166 ...getConnectionAwareBrandHeaderOptions({1166 ...getConnectionAwareBrandHeaderOptions({
1167 headerWidth: props.width,
1168 trailingItemCount: nativeHeaderItems.length,
1167 onOpenEnvironments: props.onOpenEnvironmentSettings,1169 onOpenEnvironments: props.onOpenEnvironmentSettings,
1168 fallbackTitleStyle: { fontSize: 18, fontWeight: "800" },1170 fallbackTitleStyle: { fontSize: 18, fontWeight: "800" },
1169 }),1171 }),
docs/internals/mobile-navigation.md+24-12
...@@ -5,23 +5,30 @@ The iOS Home and thread routes share the root native stack in...@@ -5,23 +5,30 @@ The iOS Home and thread routes share the root native stack in
5controller lets UIKit animate the header between routes. The iPad sidebar owns5controller lets UIKit animate the header between routes. The iPad sidebar owns
6a separate, single-screen stack; Android uses its own in-flow headers.6a 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 brand8Home and the iPad sidebar render their brand and connection status through
9as a custom leading item. A custom `headerTitle` previously shortened the scroll9`headerTitle`, with `Threads` retained as the route title. The editor-style native
10fade and removed the blur behind the logo. Preserve the native title when10bar aligns that title on the leading side. Do not model the brand as a toolbar
11changing the brand or its connection-status replacement.11button: on iOS 26.5 UIKit morphs a background-free custom leading item's rectangle
1212into the next screen's glass back button, even with distinct item identifiers.
13Both brand states use `BRAND_HEADER_ITEM_IDENTIFIER` from13Using the title slot lets the brand and native back button animate separately.
14[`CompactBrandTitle.tsx`](../../apps/mobile/src/components/CompactBrandTitle.tsx).14
15The native-stack and react-native-screens patches forward custom item identifiers15The connection-status title has a maximum width based on its header's width and
16to `UIBarButtonItem.identifier`, just as they do for native buttons and menus.16trailing actions. A long environment name or larger text must not push Settings
17UIKit otherwise matches transition items using their position and content, so17into UIKit's overflow menu. Keep the full status as the accessibility label
18the brand needs an explicit identity separate from navigation controls. Connection18while visually truncating it. The iPad sidebar uses its pane width, not the full
19status changes retain that identity. See [Apple's identifier documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/identifier).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
21The react-native-screens patch caches leading, trailing, and center item groups27The react-native-screens patch caches leading, trailing, and center item groups
22independently. A button can belong to only one group: constructing another group28independently. A button can belong to only one group: constructing another group
23with the same button removes it from the previous one. Unrelated menu updates29with the same button removes it from the previous one. Unrelated menu updates
24must therefore preserve the other groups while UIKit may be animating them.30must therefore preserve the other groups while UIKit may be animating them.
31
25Each cache includes the owning header config, its item values, and custom native32Each cache includes the owning header config, its item values, and custom native
26item identities, so changed content or remounted event emitters still rebuild.33item 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;...@@ -29,3 +36,8 @@ Custom header identifiers require native code generation and a new mobile build;
29an over-the-air JavaScript update alone is insufficient. The Android view manager36an over-the-air JavaScript update alone is insufficient. The Android view manager
30implements the generated identifier setter as a no-op because this behavior is37implements the generated identifier setter as a no-op because this behavior is
31specific to iOS 26 and later.38specific 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...@@ -1107,18 +1107,45 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..a60b96b44e85d3f4b29dc0d190b95ab6
1107 1107
1108 if (needsNavigationControllerLayout) {1108 if (needsNavigationControllerLayout) {
1109diff --git a/ios/RNSScreenStackHeaderSubview.mm b/ios/RNSScreenStackHeaderSubview.mm1109diff --git a/ios/RNSScreenStackHeaderSubview.mm b/ios/RNSScreenStackHeaderSubview.mm
1110index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a362241a72075 1006441110index add33c4807e038dcd846f6c72b2fc472ded02809..489afa5fa2da09c1ec6986d1832b58bb595396ae 100644
1111--- a/ios/RNSScreenStackHeaderSubview.mm1111--- a/ios/RNSScreenStackHeaderSubview.mm
1112+++ b/ios/RNSScreenStackHeaderSubview.mm1112+++ b/ios/RNSScreenStackHeaderSubview.mm
1113@@ -20,6 +20,7 @@ @implementation RNSScreenStackHeaderSubview {1113@@ -20,7 +20,9 @@ @implementation RNSScreenStackHeaderSubview {
1114 // This is a strong reference to UIBarButtonItem which creates a retain cycle.1114 // This is a strong reference to UIBarButtonItem which creates a retain cycle.
1115 // The cycle is cleared via `invalidateUIBarButtonItem` method, called by `invalidate` callback.1115 // The cycle is cleared via `invalidateUIBarButtonItem` method, called by `invalidate` callback.
1116 UIBarButtonItem *_barButtonItem;1116 UIBarButtonItem *_barButtonItem;
1117+ NSString *_barButtonItemIdentifier;1117+ NSString *_barButtonItemIdentifier;
1118 BOOL _hidesSharedBackground;1118 BOOL _hidesSharedBackground;
1119+ UILabel *_titleScrollEdgeEffectGuide;
1119 }1120 }
1120 1121
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::
1122 const auto &newHeaderSubviewProps = *std::static_pointer_cast<const react::RNSScreenStackHeaderSubviewProps>(props);1149 const auto &newHeaderSubviewProps = *std::static_pointer_cast<const react::RNSScreenStackHeaderSubviewProps>(props);
1123 1150
1124 [self setType:[RNSConvert RNSScreenStackHeaderSubviewTypeFromCppEquivalent:newHeaderSubviewProps.type]];1151 [self setType:[RNSConvert RNSScreenStackHeaderSubviewTypeFromCppEquivalent:newHeaderSubviewProps.type]];
...@@ -1126,7 +1153,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622...@@ -1126,7 +1153,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
1126 [self setHidesSharedBackground:newHeaderSubviewProps.hidesSharedBackground];1153 [self setHidesSharedBackground:newHeaderSubviewProps.hidesSharedBackground];
1127 [self setSynchronousShadowStateUpdatesEnabled:newHeaderSubviewProps.synchronousShadowStateUpdatesEnabled];1154 [self setSynchronousShadowStateUpdatesEnabled:newHeaderSubviewProps.synchronousShadowStateUpdatesEnabled];
1128 [super updateProps:props oldProps:oldProps];1155 [super updateProps:props oldProps:oldProps];
1129@@ -276,7 +278,14 @@ - (void)configureBarButtonItem1156@@ -276,7 +298,14 @@ - (void)configureBarButtonItem
1130 #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)1157 #if RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
1131 if (@available(iOS 26.0, *)) {1158 if (@available(iOS 26.0, *)) {
1132 if (_barButtonItem != nil) {1159 if (_barButtonItem != nil) {
...@@ -1142,7 +1169,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622...@@ -1142,7 +1169,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
1142 }1169 }
1143 }1170 }
1144 #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)1171 #endif // RNS_IPHONE_OS_VERSION_AVAILABLE(26_0)
1145@@ -284,10 +293,22 @@ - (void)configureBarButtonItem1172@@ -284,10 +313,22 @@ - (void)configureBarButtonItem
1146 1173
1147 - (void)setHidesSharedBackground:(BOOL)hidesSharedBackground1174 - (void)setHidesSharedBackground:(BOOL)hidesSharedBackground
1148 {1175 {
...@@ -1166,7 +1193,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622...@@ -1166,7 +1193,7 @@ index add33c4807e038dcd846f6c72b2fc472ded02809..9a7c7a8a775579c85de39f921e7a3622
1166 1193
1167 // Needed because of this: https://github.com/facebook/react-native/pull/372741194 // Needed because of this: https://github.com/facebook/react-native/pull/37274
1168diff --git a/lib/commonjs/components/ScreenStackHeaderConfig.js b/lib/commonjs/components/ScreenStackHeaderConfig.js1195diff --git a/lib/commonjs/components/ScreenStackHeaderConfig.js b/lib/commonjs/components/ScreenStackHeaderConfig.js
1169index 8c509f2657105cfd86448347c1a01fb73651ff9e..5e3b5185c0ea33c6b98f5daed69239d2c0d0ce90 1006441196index 8c509f2657105cfd86448347c1a01fb73651ff9e..e668e089a1788973cb122a150be7846364a2a2cf 100644
1170--- a/lib/commonjs/components/ScreenStackHeaderConfig.js1197--- a/lib/commonjs/components/ScreenStackHeaderConfig.js
1171+++ b/lib/commonjs/components/ScreenStackHeaderConfig.js1198+++ b/lib/commonjs/components/ScreenStackHeaderConfig.js
1172@@ -26,17 +26,42 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_1199@@ -26,17 +26,42 @@ const ScreenStackHeaderConfig = exports.ScreenStackHeaderConfig = /*#__PURE__*/_
...@@ -1338,7 +1365,7 @@ index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..9dca2ab640e92161eb647b819a989acd...@@ -1338,7 +1365,7 @@ index ab93f62e4a7049d63c6681cecbd6cf8b1d07ce10..9dca2ab640e92161eb647b819a989acd
1338 //# sourceMappingURL=prepareHeaderBarButtonItems.js.map1365 //# sourceMappingURL=prepareHeaderBarButtonItems.js.map
1339\ No newline at end of file1366\ No newline at end of file
1340diff --git a/lib/module/components/ScreenStackHeaderConfig.js b/lib/module/components/ScreenStackHeaderConfig.js1367diff --git a/lib/module/components/ScreenStackHeaderConfig.js b/lib/module/components/ScreenStackHeaderConfig.js
1341index 68e3ba6a78314aba908d07098bb6982d11e6ee80..b787b3cf0284ffb81c80360a06c4059277bf4c1e 1006441368index 68e3ba6a78314aba908d07098bb6982d11e6ee80..db5d17bfdac19ed3e664b55b6a73ce49766ac7c5 100644
1342--- a/lib/module/components/ScreenStackHeaderConfig.js1369--- a/lib/module/components/ScreenStackHeaderConfig.js
1343+++ b/lib/module/components/ScreenStackHeaderConfig.js1370+++ b/lib/module/components/ScreenStackHeaderConfig.js
1344@@ -22,17 +22,42 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref1371@@ -22,17 +22,42 @@ export const ScreenStackHeaderConfig = /*#__PURE__*/React.forwardRef((props, ref
pnpm-lock.yaml+6-6
...@@ -94,7 +94,7 @@ patchedDependencies:...@@ -94,7 +94,7 @@ patchedDependencies:
94 react-native-gesture-handler@2.32.0: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d394 react-native-gesture-handler@2.32.0: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d3
95 react-native-keyboard-controller@1.21.13: 20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae00895 react-native-keyboard-controller@1.21.13: 20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008
96 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b608842267596 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675
97 react-native-screens@4.26.2: 5d2e7beefe55f0b7ee157993cbc0ec38d32aa558e4e780764381fe8f96ba728d97 react-native-screens@4.26.2: 149bef30a66351ea9b26b42f87b78c539cb52b880f2387bb323ec80dcac84006
9898
99importers:99importers:
100100
...@@ -248,7 +248,7 @@ importers:...@@ -248,7 +248,7 @@ importers:
248 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)248 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)
249 '@react-navigation/native-stack':249 '@react-navigation/native-stack':
250 specifier: 7.17.6250 specifier: 7.17.6
251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(1d8c6f507069f6f6aada9b6419c84a6c)251 version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(d307537762dff86bcf277a4ec64a11d8)
252 '@shikijs/core':252 '@shikijs/core':
253 specifier: 4.2.0253 specifier: 4.2.0
254 version: 4.2.0254 version: 4.2.0
...@@ -416,7 +416,7 @@ importers:...@@ -416,7 +416,7 @@ importers:
416 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)416 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)
417 react-native-screens:417 react-native-screens:
418 specifier: ~4.26.0418 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)
420 react-native-shiki-engine:420 react-native-shiki-engine:
421 specifier: ^0.3.12421 specifier: ^0.3.12
422 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)422 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:...@@ -13880,7 +13880,7 @@ snapshots:
13880 optionalDependencies:13880 optionalDependencies:
13881 '@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)13881 '@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)':
13884 dependencies:13884 dependencies:
13885 '@react-navigation/elements': 2.9.26(c10301b6e0c42fc6434d2b643197a81e)13885 '@react-navigation/elements': 2.9.26(c10301b6e0c42fc6434d2b643197a81e)
13886 '@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)13886 '@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:...@@ -13888,7 +13888,7 @@ snapshots:
13888 react: 19.2.313888 react: 19.2.3
13889 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)13889 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)
13890 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)13890 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)
13892 sf-symbols-typescript: 2.2.013892 sf-symbols-typescript: 2.2.0
13893 warn-once: 0.1.113893 warn-once: 0.1.1
13894 transitivePeerDependencies:13894 transitivePeerDependencies:
...@@ -19492,7 +19492,7 @@ snapshots:...@@ -19492,7 +19492,7 @@ snapshots:
19492 react: 19.2.319492 react: 19.2.3
19493 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)19493 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):
19496 dependencies:19496 dependencies:
19497 react: 19.2.319497 react: 19.2.3
19498 react-freeze: 1.0.4(react@19.2.3)19498 react-freeze: 1.0.4(react@19.2.3)