diff --git a/patches/react-native-keyboard-controller@1.21.13.patch b/patches/react-native-keyboard-controller@1.21.13.patch index 3dee935cda3..a8a505282a4 100644 --- a/patches/react-native-keyboard-controller@1.21.13.patch +++ b/patches/react-native-keyboard-controller@1.21.13.patch @@ -1,12 +1,48 @@ +diff --git a/ios/views/ClippingScrollViewDecoratorViewManager.mm b/ios/views/ClippingScrollViewDecoratorViewManager.mm +index d960900fa..26101809e 100644 +--- a/ios/views/ClippingScrollViewDecoratorViewManager.mm ++++ b/ios/views/ClippingScrollViewDecoratorViewManager.mm +@@ -78,6 +78,30 @@ static void KCApplyNoopScrollRectToVisible(UIScrollView *scrollView) + method_getTypeEncoding(original)); + } + ++ // Applying a new raw contentInset makes UIKit re-clamp contentOffset against ++ // the raw (not adjusted) insets, which snaps chat lists resting at a negative ++ // offset under a translucent header back to 0. The JS side owns offset ++ // management here, so preserve the offset across inset applications. ++ Method originalSetInset = class_getInstanceMethod(originalClass, @selector(setContentInset:)); ++ if (originalSetInset) { ++ void (*callOriginalSetInset)(id, SEL, UIEdgeInsets) = ++ (void (*)(id, SEL, UIEdgeInsets))method_getImplementation(originalSetInset); ++ IMP preserveOffsetImp = imp_implementationWithBlock( ++ ^(__unsafe_unretained UIScrollView *self, UIEdgeInsets inset) { ++ CGPoint before = self.contentOffset; ++ callOriginalSetInset(self, @selector(setContentInset:), inset); ++ if (!CGPointEqualToPoint(before, self.contentOffset) && !self.isDragging && ++ !self.isDecelerating) { ++ self.contentOffset = before; ++ } ++ }); ++ class_addMethod( ++ subclass, ++ @selector(setContentInset:), ++ preserveOffsetImp, ++ method_getTypeEncoding(originalSetInset)); ++ } ++ + objc_registerClassPair(subclass); + } + diff --git a/lib/commonjs/components/KeyboardChatScrollView/index.js b/lib/commonjs/components/KeyboardChatScrollView/index.js -index db8cfb1d289f91563f13c4dd842c783c99facc32..940e1dd80c6c9dfc42eab916445be414372ce52e 100644 +index db8cfb1d2..d4c99886b 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/index.js +++ b/lib/commonjs/components/KeyboardChatScrollView/index.js -@@ -26,9 +26,11 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ +@@ -26,9 +26,12 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE, + adjustedInsetCompensation = 0, ++ contentInsetStartCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp, @@ -14,13 +50,14 @@ index db8cfb1d289f91563f13c4dd842c783c99facc32..940e1dd80c6c9dfc42eab916445be414 onEndVisible, ...rest }, ref) => { -@@ -50,13 +52,15 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ +@@ -50,13 +53,17 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ freeze: freezeSV, offset, blankSpace, - extraContentPadding + extraContentPadding, -+ adjustedInsetCompensation ++ adjustedInsetCompensation, ++ contentInsetStartCompensation }); (0, _useExtraContentPadding.useExtraContentPadding)({ scrollViewRef, @@ -28,10 +65,11 @@ index db8cfb1d289f91563f13c4dd842c783c99facc32..940e1dd80c6c9dfc42eab916445be414 keyboardPadding: padding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation, scroll, layout, size, -@@ -82,10 +86,21 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ +@@ -82,10 +89,21 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ // a bug for you, please open an issue. const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); @@ -54,21 +92,52 @@ index db8cfb1d289f91563f13c4dd842c783c99facc32..940e1dd80c6c9dfc42eab916445be414 const onLayout = (0, _react.useCallback)(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); +diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +index 4d33b3fe4..ea710c1ca 100644 +--- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js ++++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +@@ -206,7 +206,7 @@ const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, + * ``` + */ + exports.clampedScrollTarget = clampedScrollTarget; +-const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { ++const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInset = 0) => { + "worklet"; + + const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; +@@ -214,8 +214,13 @@ const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, + const maxScroll = Math.max(contentHeight - layoutHeight, 0); + return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); + } +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ // Non-inverted lists under an adjusted top inset (translucent header / ++ // automatic safe-area) rest at a NEGATIVE offset (-startInset). Flooring at ++ // 0 would force short content up under the header and strand it there, ++ // because the offset can never return below 0 through this math. ++ const minScroll = -startInset; ++ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); ++ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); + }; + exports.computeIOSContentOffset = computeIOSContentOffset; + //# sourceMappingURL=helpers.js.map +\ No newline at end of file diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -index 2073da84b8b2be291aa3181700c2b18a75d0fc56..f43f2efdc4f0eda0460720839544dc6e7f4a54e0 100644 +index 2073da84b..0c9ad761e 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -@@ -32,7 +32,8 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -32,7 +32,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace, - extraContentPadding + extraContentPadding, -+ adjustedInsetCompensation ++ adjustedInsetCompensation, ++ contentInsetStartCompensation = 0 } = options; const padding = (0, _reactNativeReanimated.useSharedValue)(0); const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0); -@@ -66,7 +67,7 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -66,7 +68,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed); @@ -77,28 +146,62 @@ index 2073da84b8b2be291aa3181700c2b18a75d0fc56..f43f2efdc4f0eda0460720839544dc6e // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) { -@@ -134,7 +135,7 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -76,8 +78,9 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, minScroll); ++ contentOffsetY.value = Math.max(minScroll, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -91,8 +94,9 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, minScroll); ++ contentOffsetY.value = Math.max(minScroll, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -118,7 +122,7 @@ function useChatKeyboard(scrollViewRef, options) { + contentOffsetY.value = scroll.value; + return; + } +- contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); ++ contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, contentInsetStartCompensation); + }, + onMove: () => { + "worklet"; +@@ -134,7 +138,7 @@ function useChatKeyboard(scrollViewRef, options) { const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); padding.value = effective; } - }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); -+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation]); ++ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, contentInsetStartCompensation]); return { padding, currentHeight, diff --git a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js -index 0d50bdfeb7bbc5c14a31ed344f8a690e4fd340b3..22ca193257ab0068f732c088b09145dabf39a05e 100644 +index 0d50bdfeb..d20279bcf 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js +++ b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js -@@ -29,6 +29,7 @@ function useExtraContentPadding(options) { +@@ -29,6 +29,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation = 0, scroll, layout, size, -@@ -68,8 +69,8 @@ function useExtraContentPadding(options) { +@@ -68,8 +70,8 @@ function useExtraContentPadding(options) { } // Compute effective delta considering blankSpace floor @@ -109,17 +212,26 @@ index 0d50bdfeb7bbc5c14a31ed344f8a690e4fd340b3..22ca193257ab0068f732c088b09145da const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change -@@ -92,6 +93,6 @@ function useExtraContentPadding(options) { - const target = Math.min(scroll.value + effectiveDelta, maxScroll); +@@ -88,10 +90,13 @@ function useExtraContentPadding(options) { + const target = Math.max(scroll.value - effectiveDelta, -currentTotal); + scrollToTarget(target); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); +- const target = Math.min(scroll.value + effectiveDelta, maxScroll); ++ // Respect the adjusted top inset: short content rests at a negative ++ // offset (-startInset), and the scrollable range never extends past it. ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); ++ const target = Math.max(Math.min(scroll.value + effectiveDelta, maxScroll), minScroll); scrollToTarget(target); } - }, [inverted, keyboardLiftBehavior]); -+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation]); ++ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, contentInsetStartCompensation]); } //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/lib/module/components/KeyboardChatScrollView/index.js b/lib/module/components/KeyboardChatScrollView/index.js -index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..ac79433fdf0b36f89525b9840447a116da63d58c 100644 +index 612dd8bd9..7daa4ede5 100644 --- a/lib/module/components/KeyboardChatScrollView/index.js +++ b/lib/module/components/KeyboardChatScrollView/index.js @@ -1,7 +1,7 @@ @@ -131,11 +243,12 @@ index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..ac79433fdf0b36f89525b9840447a116 import Reanimated from "react-native-reanimated"; import useCombinedRef from "../hooks/useCombinedRef"; import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding"; -@@ -19,9 +19,11 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ +@@ -19,9 +19,12 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE, + adjustedInsetCompensation = 0, ++ contentInsetStartCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp, @@ -143,13 +256,14 @@ index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..ac79433fdf0b36f89525b9840447a116 onEndVisible, ...rest }, ref) => { -@@ -43,13 +45,15 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ +@@ -43,13 +46,17 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ freeze: freezeSV, offset, blankSpace, - extraContentPadding + extraContentPadding, -+ adjustedInsetCompensation ++ adjustedInsetCompensation, ++ contentInsetStartCompensation }); useExtraContentPadding({ scrollViewRef, @@ -157,10 +271,11 @@ index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..ac79433fdf0b36f89525b9840447a116 keyboardPadding: padding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation, scroll, layout, size, -@@ -75,10 +79,21 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ +@@ -75,10 +82,21 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ // a bug for you, please open an issue. const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); @@ -183,21 +298,51 @@ index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..ac79433fdf0b36f89525b9840447a116 const onLayout = useCallback(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); +diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +index 295e2219b..aa5d7b165 100644 +--- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js ++++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +@@ -193,7 +193,7 @@ export const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentH + * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 + * ``` + */ +-export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { ++export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInset = 0) => { + "worklet"; + + const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; +@@ -201,7 +201,12 @@ export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentH + const maxScroll = Math.max(contentHeight - layoutHeight, 0); + return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); + } +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ // Non-inverted lists under an adjusted top inset (translucent header / ++ // automatic safe-area) rest at a NEGATIVE offset (-startInset). Flooring at ++ // 0 would force short content up under the header and strand it there, ++ // because the offset can never return below 0 through this math. ++ const minScroll = -startInset; ++ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); ++ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); + }; + //# sourceMappingURL=helpers.js.map +\ No newline at end of file diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -index 52943c3a7d6a68fe2094dc1d112c07e6b9d890e4..c8685c18a53ad078c24fc3e3b6572669b2b63397 100644 +index 52943c3a7..139fa4dd5 100644 --- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js -@@ -25,7 +25,8 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -25,7 +25,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace, - extraContentPadding + extraContentPadding, -+ adjustedInsetCompensation ++ adjustedInsetCompensation, ++ contentInsetStartCompensation = 0 } = options; const padding = useSharedValue(0); const currentHeight = useSharedValue(0); -@@ -59,7 +60,7 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -59,7 +61,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed); @@ -206,28 +351,62 @@ index 52943c3a7d6a68fe2094dc1d112c07e6b9d890e4..c8685c18a53ad078c24fc3e3b6572669 // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) { -@@ -127,7 +128,7 @@ function useChatKeyboard(scrollViewRef, options) { +@@ -69,8 +71,9 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, minScroll); ++ contentOffsetY.value = Math.max(minScroll, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -84,8 +87,9 @@ function useChatKeyboard(scrollViewRef, options) { + const maxScroll = Math.max(size.value.height - layout.value.height, 0); + contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); +- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, minScroll); ++ contentOffsetY.value = Math.max(minScroll, Math.min(scroll.value, maxScroll)); + } + return; + } +@@ -111,7 +115,7 @@ function useChatKeyboard(scrollViewRef, options) { + contentOffsetY.value = scroll.value; + return; + } +- contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); ++ contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, contentInsetStartCompensation); + }, + onMove: () => { + "worklet"; +@@ -127,7 +131,7 @@ function useChatKeyboard(scrollViewRef, options) { const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); padding.value = effective; } - }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); -+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation]); ++ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, contentInsetStartCompensation]); return { padding, currentHeight, diff --git a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js -index 1afa50987a8d2a5fe3f36b20945efe804d48a873..e2966d89d4329a7dc1233ff060cab6f365f745d6 100644 +index 1afa50987..8c1e4c02c 100644 --- a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js +++ b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js -@@ -23,6 +23,7 @@ function useExtraContentPadding(options) { +@@ -23,6 +23,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation = 0, scroll, layout, size, -@@ -62,8 +63,8 @@ function useExtraContentPadding(options) { +@@ -62,8 +64,8 @@ function useExtraContentPadding(options) { } // Compute effective delta considering blankSpace floor @@ -238,57 +417,88 @@ index 1afa50987a8d2a5fe3f36b20945efe804d48a873..e2966d89d4329a7dc1233ff060cab6f3 const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change -@@ -86,7 +87,7 @@ function useExtraContentPadding(options) { - const target = Math.min(scroll.value + effectiveDelta, maxScroll); +@@ -82,11 +84,14 @@ function useExtraContentPadding(options) { + const target = Math.max(scroll.value - effectiveDelta, -currentTotal); + scrollToTarget(target); + } else { +- const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0); +- const target = Math.min(scroll.value + effectiveDelta, maxScroll); ++ // Respect the adjusted top inset: short content rests at a negative ++ // offset (-startInset), and the scrollable range never extends past it. ++ const minScroll = -contentInsetStartCompensation; ++ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); ++ const target = Math.max(Math.min(scroll.value + effectiveDelta, maxScroll), minScroll); scrollToTarget(target); } - }, [inverted, keyboardLiftBehavior]); -+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation]); ++ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, contentInsetStartCompensation]); } export { useExtraContentPadding }; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/lib/typescript/components/KeyboardChatScrollView/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/types.d.ts -index a036b431f03efb9d5379527c17db6fce62bfee09..6ca6fdf60fc9fae1e28a86cf24e21beed99b3a76 100644 +index a036b431f..49a6cad04 100644 --- a/lib/typescript/components/KeyboardChatScrollView/types.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/types.d.ts -@@ -86,6 +86,8 @@ export type KeyboardChatScrollViewProps = { +@@ -86,6 +86,18 @@ export type KeyboardChatScrollViewProps = { * Default is `undefined` (equivalent to `0` — no minimum floor). */ blankSpace?: SharedValue; + /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ + adjustedInsetCompensation?: number; ++ /** ++ * Adjusted TOP inset UIKit applies to this scroll view (safe area / ++ * translucent header under `contentInsetAdjustmentBehavior="automatic"`). ++ * Non-inverted lists rest at `-contentInsetStartCompensation`, so all ++ * scroll-offset clamps floor there instead of 0. Used ONLY in scroll-offset ++ * math — never written into `contentInset`. ++ * ++ * Default is `0`. ++ */ ++ contentInsetStartCompensation?: number; /** * Fires whenever the effective content inset changes — the static `contentInset` * prop combined with the dynamic keyboard-driven padding. +diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +index 2b51cc473..8e4b08345 100644 +--- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts ++++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +@@ -129,4 +129,4 @@ export declare const clampedScrollTarget: (offsetBeforeScroll: number, keyboardH + * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 + * ``` + */ +-export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number) => number; ++export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number, startInset?: number) => number; diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts -index aff9b5a8dbc2464546396437eaf6c5ae955b9f29..67edbe1a1eac27b5a571611979753ebf3134bc8b 100644 +index aff9b5a8d..cf6d63741 100644 --- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts -@@ -9,6 +9,8 @@ type UseChatKeyboardOptions = { +@@ -9,6 +9,9 @@ type UseChatKeyboardOptions = { blankSpace: SharedValue; /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ extraContentPadding: SharedValue; + /** Safe-area extra beyond raw contentInset. Offset math only. */ + adjustedInsetCompensation: number; ++ contentInsetStartCompensation?: number; }; type UseChatKeyboardReturn = { /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */ diff --git a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts -index ec73f70544a062fbecfc1d8be92d839d3e0bea6f..6fe16cd0b1200a2f4d4d8eeb9b555747186dbfe0 100644 +index ec73f7054..85ba62f04 100644 --- a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts -@@ -8,6 +8,8 @@ type UseExtraContentPaddingOptions = { +@@ -8,6 +8,9 @@ type UseExtraContentPaddingOptions = { keyboardPadding: SharedValue; /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ blankSpace: SharedValue; + /** Safe-area extra beyond raw contentInset. Offset math only. */ + adjustedInsetCompensation: number; ++ contentInsetStartCompensation?: number; /** Current vertical scroll offset. */ scroll: SharedValue; /** Visible viewport dimensions. */ diff --git a/src/components/KeyboardChatScrollView/index.tsx b/src/components/KeyboardChatScrollView/index.tsx -index 03f5f74e9aaaabc75db1c01643a655ee4fdfa5f2..d657002ebbce53c47e3a713c0921c35dca6056f3 100644 +index 03f5f74e9..bc67b7b9c 100644 --- a/src/components/KeyboardChatScrollView/index.tsx +++ b/src/components/KeyboardChatScrollView/index.tsx @@ -2,6 +2,8 @@ import React, { forwardRef, useCallback, useMemo } from "react"; @@ -300,11 +510,12 @@ index 03f5f74e9aaaabc75db1c01643a655ee4fdfa5f2..d657002ebbce53c47e3a713c0921c35d useAnimatedRef, useAnimatedStyle, useDerivedValue, -@@ -35,9 +37,11 @@ const KeyboardChatScrollView = forwardRef< +@@ -35,9 +37,12 @@ const KeyboardChatScrollView = forwardRef< offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE, + adjustedInsetCompensation = 0, ++ contentInsetStartCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp, @@ -312,23 +523,25 @@ index 03f5f74e9aaaabc75db1c01643a655ee4fdfa5f2..d657002ebbce53c47e3a713c0921c35d onEndVisible, ...rest }, -@@ -64,6 +68,7 @@ const KeyboardChatScrollView = forwardRef< +@@ -64,6 +69,8 @@ const KeyboardChatScrollView = forwardRef< offset, blankSpace, extraContentPadding, + adjustedInsetCompensation, ++ contentInsetStartCompensation, }); useExtraContentPadding({ -@@ -71,6 +76,7 @@ const KeyboardChatScrollView = forwardRef< +@@ -71,6 +78,8 @@ const KeyboardChatScrollView = forwardRef< extraContentPadding, keyboardPadding: padding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation, scroll, layout, size, -@@ -102,13 +108,25 @@ const KeyboardChatScrollView = forwardRef< +@@ -102,13 +111,25 @@ const KeyboardChatScrollView = forwardRef< ), ); @@ -360,10 +573,10 @@ index 03f5f74e9aaaabc75db1c01643a655ee4fdfa5f2..d657002ebbce53c47e3a713c0921c35d (e: LayoutChangeEvent) => { onLayoutInternal(e); diff --git a/src/components/KeyboardChatScrollView/types.ts b/src/components/KeyboardChatScrollView/types.ts -index dd222b57bc7a71729524670bad3812f30920bd73..40249a4357991b7a9c69b87214db2ceb6ff4ba7d 100644 +index dd222b57b..1a4a308d4 100644 --- a/src/components/KeyboardChatScrollView/types.ts +++ b/src/components/KeyboardChatScrollView/types.ts -@@ -90,6 +90,15 @@ export type KeyboardChatScrollViewProps = { +@@ -90,6 +90,25 @@ export type KeyboardChatScrollViewProps = { * Default is `undefined` (equivalent to `0` — no minimum floor). */ blankSpace?: SharedValue; @@ -376,22 +589,74 @@ index dd222b57bc7a71729524670bad3812f30920bd73..40249a4357991b7a9c69b87214db2ceb + * Default is `0`. + */ + adjustedInsetCompensation?: number; ++ /** ++ * Adjusted TOP inset UIKit applies to this scroll view (safe area / ++ * translucent header under `contentInsetAdjustmentBehavior="automatic"`). ++ * Non-inverted lists rest at `-contentInsetStartCompensation`, so all ++ * scroll-offset clamps floor there instead of 0. Used ONLY in scroll-offset ++ * math — never written into `contentInset`. ++ * ++ * Default is `0`. ++ */ ++ contentInsetStartCompensation?: number; /** * Fires whenever the effective content inset changes — the static `contentInset` * prop combined with the dynamic keyboard-driven padding. +diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts +index 37eacd108..0e47c74a8 100644 +--- a/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts ++++ b/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts +@@ -232,6 +232,7 @@ export const clampedScrollTarget = ( + * @param layoutHeight - Visible height of the scroll view. + * @param inverted - Whether the list is inverted. + * @param totalPaddingForMaxScroll - Total padding to use for maxScroll calculation. When provided, used instead of keyboardHeight for the scrollable range. Defaults to keyboardHeight. ++ * @param startInset - Adjusted top inset; non-inverted offsets floor at `-startInset` instead of 0. + * @returns The absolute contentOffset.y to set. + * @example + * ```ts +@@ -245,6 +246,7 @@ export const computeIOSContentOffset = ( + layoutHeight: number, + inverted: boolean, + totalPaddingForMaxScroll?: number, ++ startInset: number = 0, + ): number => { + "worklet"; + +@@ -262,7 +264,18 @@ export const computeIOSContentOffset = ( + ); + } + +- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); ++ // Non-inverted lists under an adjusted top inset (translucent header / ++ // automatic safe-area) rest at a NEGATIVE offset (-startInset). Flooring at ++ // 0 would force short content up under the header and strand it there, ++ // because the offset can never return below 0 through this math. ++ const minScroll = -startInset; ++ const maxScroll = Math.max( ++ contentHeight - layoutHeight + paddingForMax, ++ minScroll, ++ ); + +- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); ++ return Math.min( ++ Math.max(keyboardHeight + relativeScroll, minScroll), ++ maxScroll, ++ ); + }; diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts -index 560df54bae1a8c41a2e9ac0e2a8d2fd9b843968a..a0cb412692cf47fee372a01132e6a670b59bcd66 100644 +index 560df54ba..ef8b4ec8e 100644 --- a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts +++ b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts -@@ -43,6 +43,7 @@ function useChatKeyboard( +@@ -43,6 +43,8 @@ function useChatKeyboard( offset, blankSpace, extraContentPadding, + adjustedInsetCompensation, ++ contentInsetStartCompensation = 0, } = options; const padding = useSharedValue(0); -@@ -104,10 +105,12 @@ function useChatKeyboard( +@@ -104,10 +106,12 @@ function useChatKeyboard( effective, minimumPaddingAbsorbed, ); @@ -408,50 +673,95 @@ index 560df54bae1a8c41a2e9ac0e2a8d2fd9b843968a..a0cb412692cf47fee372a01132e6a670 // persistent mode: when keyboard shrinks, clamp to valid range if ( -@@ -242,7 +245,7 @@ function useChatKeyboard( +@@ -128,13 +132,14 @@ function useChatKeyboard( + Math.min(scroll.value, maxScroll), + ); + } else { ++ const minScroll = -contentInsetStartCompensation; + const maxScroll = Math.max( + size.value.height - layout.value.height + actualTotalPadding, +- 0, ++ minScroll, + ); + + contentOffsetY.value = Math.max( +- 0, ++ minScroll, + Math.min(scroll.value, maxScroll), + ); + } +@@ -163,13 +168,14 @@ function useChatKeyboard( + Math.min(scroll.value, maxScroll), + ); + } else { ++ const minScroll = -contentInsetStartCompensation; + const maxScroll = Math.max( + size.value.height - layout.value.height + actualTotalPadding, +- 0, ++ minScroll, + ); + + contentOffsetY.value = Math.max( +- 0, ++ minScroll, + Math.min(scroll.value, maxScroll), + ); + } +@@ -219,6 +225,7 @@ function useChatKeyboard( + layout.value.height, + inverted, + actualTotalPadding, ++ contentInsetStartCompensation, + ); + }, + onMove: () => { +@@ -242,7 +249,7 @@ function useChatKeyboard( padding.value = effective; }, }, - [inverted, keyboardLiftBehavior, offset, extraContentPadding], -+ [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation], ++ [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, contentInsetStartCompensation], ); return { diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts -index 02abf5cd9490900826678175462b234e07e30e92..3f261fa8f1913a79fa1de2004bbac20415a89acc 100644 +index 02abf5cd9..cd18b205d 100644 --- a/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts +++ b/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts -@@ -11,6 +11,8 @@ type UseChatKeyboardOptions = { +@@ -11,6 +11,9 @@ type UseChatKeyboardOptions = { blankSpace: SharedValue; /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ extraContentPadding: SharedValue; + /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ + adjustedInsetCompensation: number; ++ contentInsetStartCompensation?: number; }; type UseChatKeyboardReturn = { diff --git a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts -index 833acbe78f1b1245251ddd3431d6546decdd0ade..49d679446b03199217faa16a503d8d15e83a29b9 100644 +index 833acbe78..143baf951 100644 --- a/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +++ b/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts -@@ -16,6 +16,8 @@ type UseExtraContentPaddingOptions = { +@@ -16,6 +16,9 @@ type UseExtraContentPaddingOptions = { keyboardPadding: SharedValue; /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */ blankSpace: SharedValue; + /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ + adjustedInsetCompensation: number; ++ contentInsetStartCompensation?: number; /** Current vertical scroll offset. */ scroll: SharedValue; /** Visible viewport dimensions. */ -@@ -49,6 +51,7 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { +@@ -49,6 +52,8 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { extraContentPadding, keyboardPadding, blankSpace, + adjustedInsetCompensation, ++ contentInsetStartCompensation = 0, scroll, layout, size, -@@ -97,14 +100,12 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { +@@ -97,14 +102,12 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { } // Compute effective delta considering blankSpace floor @@ -472,12 +782,29 @@ index 833acbe78f1b1245251ddd3431d6546decdd0ade..49d679446b03199217faa16a503d8d15 const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { -@@ -146,7 +147,7 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { +@@ -137,16 +140,22 @@ function useExtraContentPadding(options: UseExtraContentPaddingOptions): void { + + scrollToTarget(target); + } else { ++ // Respect the adjusted top inset: short content rests at a negative ++ // offset (-startInset), and the scrollable range never extends past it. ++ const minScroll = -contentInsetStartCompensation; + const maxScroll = Math.max( + size.value.height - layout.value.height + currentTotal, +- 0, ++ minScroll, ++ ); ++ const target = Math.max( ++ Math.min(scroll.value + effectiveDelta, maxScroll), ++ minScroll, + ); +- const target = Math.min(scroll.value + effectiveDelta, maxScroll); + scrollToTarget(target); } }, - [inverted, keyboardLiftBehavior], -+ [inverted, keyboardLiftBehavior, adjustedInsetCompensation], ++ [inverted, keyboardLiftBehavior, adjustedInsetCompensation, contentInsetStartCompensation], ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee737f9e750..607e25392db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,7 +77,7 @@ patchedDependencies: effect@4.0.0-beta.78: c502bc684210b707dfceb87d8fe6ad6843395af6e19cfc02cd65854898bde2c5 expo-modules-jsi@56.0.10: 9170f8074ae4e35a0a086e756c8f815794fd3abe51eac67ca3ba02804225ec1f react-native-gesture-handler@2.31.2: 808eb26f9e57cf4945efd3985af4d9c764da6f91f4c9764433cc868602bbf4d3 - react-native-keyboard-controller@1.21.13: 20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008 + react-native-keyboard-controller@1.21.13: 001d3934d3afc439db85f6918a5816301e50689d701d7ffbc86ece2abba767b7 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675 react-native-screens@4.25.2: 47a07b0849ebf1ae454be3cb9fde7700c763584afa403d381e11e06e36187de8 @@ -374,7 +374,7 @@ importers: version: 0.2.2(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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) react-native-keyboard-controller: specifier: 1.21.13 - version: 1.21.13(patch_hash=20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008)(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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) + version: 1.21.13(patch_hash=001d3934d3afc439db85f6918a5816301e50689d701d7ffbc86ece2abba767b7)(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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) react-native-nitro-markdown: specifier: ^0.5.0 version: 0.5.8(react-native-nitro-modules@0.35.9(patch_hash=825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native-svg@15.15.4(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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) @@ -19694,7 +19694,7 @@ snapshots: react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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.6)(utf-8-validate@6.0.6) optional: true - react-native-keyboard-controller@1.21.13(patch_hash=20be72c84d74253acdcfefbc6defe36dc396944f1a44cab2bdd0e3cd572ae008)(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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): + react-native-keyboard-controller@1.21.13(patch_hash=001d3934d3afc439db85f6918a5816301e50689d701d7ffbc86ece2abba767b7)(react-native-reanimated@4.3.1(react-native-worklets@0.8.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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))(react-native@0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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): dependencies: react: 19.2.3 react-native: 0.85.3(@babel/core@7.29.7)(@react-native/metro-config@0.85.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)