From 59c2eca268f63318dfae44db338a2aa3c29f066f Mon Sep 17 00:00:00 2001
From: Qichen Zhu <57348009+QichenZhu@users.noreply.github.com>
Date: Wed, 6 May 2026 16:56:11 +1200
Subject: [PATCH] Follow up improvements for inline code blocks on iOS

---
 patches/react-native/details.md               |   7 +
 ...1+036+rounded-inline-code-background.patch | 155 ++++++++++++++++++
 2 files changed, 162 insertions(+)
 create mode 100644 patches/react-native/react-native+0.83.1+036+rounded-inline-code-background.patch

diff --git a/patches/react-native/details.md b/patches/react-native/details.md
index 413aa3abfa41..e8b35d0abe50 100644
--- a/patches/react-native/details.md
+++ b/patches/react-native/details.md
@@ -264,3 +264,10 @@
 - Reason: Fixes an Android-specific issue (reproducible on certain Samsung models) where `onPress` events do not trigger for `Pressable` components when used inside a `Tooltip`. The root cause is that in the new architecture, `Pressability.measure()` reads stale layout information from the shadow tree instead of the actual native view hierarchy. This patch introduces a new `measureAsyncOnUI` method that measures the view asynchronously using the native layout hierarchy on the UI thread, bypassing stale shadow tree data.
 - Upstream PR/issue: [facebook/react-native#51835](https://github.com/facebook/react-native/pull/51835)
 - E/App issue: [#59953](https://github.com/Expensify/App/issues/59953)
+
+### [react-native+0.83.1+036+rounded-inline-code-background.patch](react-native+0.83.1+036+rounded-inline-code-background.patch)
+
+- Reason: Draws inline code block background with rounded corners on iOS when `borderTopLeftRadius` is set.
+- Upstream PR/issue: 🛑
+- E/App issue: https://github.com/Expensify/App/issues/57556
+- PR introducing patch: https://github.com/Expensify/App/pull/79815
diff --git a/patches/react-native/react-native+0.83.1+036+rounded-inline-code-background.patch b/patches/react-native/react-native+0.83.1+036+rounded-inline-code-background.patch
new file mode 100644
index 000000000000..2f8403c6596c
--- /dev/null
+++ b/patches/react-native/react-native+0.83.1+036+rounded-inline-code-background.patch
@@ -0,0 +1,155 @@
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp
+index 9cf89bf..845f6c1 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp
++++ b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.cpp
+@@ -25,6 +25,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
+   backgroundColor = textAttributes.backgroundColor
+       ? textAttributes.backgroundColor
+       : backgroundColor;
++  borderTopLeftRadius = !std::isnan(textAttributes.borderTopLeftRadius)
++      ? textAttributes.borderTopLeftRadius
++      : borderTopLeftRadius;
+   opacity =
+       !std::isnan(textAttributes.opacity) ? textAttributes.opacity : opacity;
+ 
+@@ -171,6 +174,7 @@ bool TextAttributes::operator==(const TextAttributes& rhs) const {
+       floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
+       floatEquality(letterSpacing, rhs.letterSpacing) &&
+       floatEquality(lineHeight, rhs.lineHeight) &&
++      floatEquality(borderTopLeftRadius, rhs.borderTopLeftRadius) &&
+       floatEquality(textShadowRadius, rhs.textShadowRadius);
+ }
+ 
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.h b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.h
+index b664524..dce4e3b 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.h
++++ b/node_modules/react-native/ReactCommon/react/renderer/attributedstring/TextAttributes.h
+@@ -41,6 +41,7 @@ class TextAttributes : public DebugStringConvertible {
+   // Color
+   SharedColor foregroundColor{};
+   SharedColor backgroundColor{};
++  Float borderTopLeftRadius{std::numeric_limits<Float>::quiet_NaN()};
+   Float opacity{std::numeric_limits<Float>::quiet_NaN()};
+ 
+   // Font
+@@ -112,6 +113,7 @@ struct hash<facebook::react::TextAttributes> {
+     return facebook::react::hash_combine(
+         textAttributes.foregroundColor,
+         textAttributes.backgroundColor,
++        textAttributes.borderTopLeftRadius,
+         textAttributes.opacity,
+         textAttributes.fontFamily,
+         textAttributes.fontSize,
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp
+index f20cd3c..8a9cedc 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp
++++ b/node_modules/react-native/ReactCommon/react/renderer/components/text/BaseTextProps.cpp
+@@ -222,6 +222,12 @@ static TextAttributes convertRawProp(
+       "backgroundColor",
+       sourceTextAttributes.backgroundColor,
+       defaultTextAttributes.backgroundColor);
++  textAttributes.borderTopLeftRadius = convertRawProp(
++      context,
++      rawProps,
++      "borderTopLeftRadius",
++      sourceTextAttributes.borderTopLeftRadius,
++      defaultTextAttributes.borderTopLeftRadius);
+ 
+   return textAttributes;
+ }
+@@ -334,6 +340,8 @@ void BaseTextProps::setProp(
+         defaults, value, textAttributes, opacity, "opacity");
+     REBUILD_FIELD_SWITCH_CASE(
+         defaults, value, textAttributes, backgroundColor, "backgroundColor");
++    REBUILD_FIELD_SWITCH_CASE(
++        defaults, value, textAttributes, borderTopLeftRadius, "borderTopLeftRadius");
+   }
+ }
+ 
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h
+index dac572b..87cc9f0 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h
++++ b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.h
+@@ -18,6 +18,7 @@ NSString *const RCTAttributedStringEventEmitterKey = @"EventEmitter";
+ 
+ // String representation of either `role` or `accessibilityRole`
+ NSString *const RCTTextAttributesAccessibilityRoleAttributeName = @"AccessibilityRole";
++NSString *const RCTTextBackgroundBorderRadiusAttributeName = @"TextBackgroundBorderRadius";
+ 
+ /*
+  * Creates `NSTextAttributes` from given `facebook::react::TextAttributes`
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm
+index 520a24c..452a0fe 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm
++++ b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm
+@@ -179,7 +179,12 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
+   }
+ 
+   if (textAttributes.backgroundColor || !isnan(textAttributes.opacity)) {
+-    attributes[NSBackgroundColorAttributeName] = RCTEffectiveBackgroundColorFromTextAttributes(textAttributes);
++    UIColor *bgColor = RCTEffectiveBackgroundColorFromTextAttributes(textAttributes) ?: [UIColor clearColor];
++    if (!isnan(textAttributes.borderTopLeftRadius) && textAttributes.borderTopLeftRadius > 0) {
++      attributes[RCTTextBackgroundBorderRadiusAttributeName] = @[@(textAttributes.borderTopLeftRadius), bgColor];
++    } else {
++      attributes[NSBackgroundColorAttributeName] = bgColor;
++    }
+   }
+ 
+   // Kerning
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
+index ef50487..df9290f 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
++++ b/node_modules/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
+@@ -17,6 +17,8 @@
+ 
+ using namespace facebook::react;
+ 
++static const CGFloat inlineCodeBackgroundTopInset = 3;
++
+ @implementation RCTTextLayoutManager {
+   SimpleThreadSafeCache<AttributedString, std::shared_ptr<void>, 256> _cache;
+ }
+@@ -86,6 +88,43 @@ - (void)drawAttributedString:(AttributedString)attributedString
+ 
+   NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
+ 
++  NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
++  [textStorage enumerateAttribute:RCTTextBackgroundBorderRadiusAttributeName
++                          inRange:characterRange
++                          options:0
++                       usingBlock:^(NSArray *value, NSRange range, BOOL *stop) {
++                         if (!value) {
++                           return;
++                         }
++                         CGFloat radius = [value[0] floatValue];
++                         UIColor *color = value[1];
++                         NSRange rangeGlyphRange = [layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL];
++                         NSMutableArray<NSValue *> *rects = [NSMutableArray array];
++                         [layoutManager enumerateEnclosingRectsForGlyphRange:rangeGlyphRange
++                                                     withinSelectedGlyphRange:rangeGlyphRange
++                                                              inTextContainer:textContainer
++                                                                   usingBlock:^(CGRect enclosingRect, BOOL *anotherStop) {
++                             [rects addObject:[NSValue valueWithCGRect:enclosingRect]];
++                         }];
++                         NSUInteger count = rects.count;
++                         for (NSUInteger i = 0; i < count; i++) {
++                             CGRect rect = UIEdgeInsetsInsetRect(
++                                 CGRectOffset([rects[i] CGRectValue], frame.origin.x, frame.origin.y),
++                                 UIEdgeInsetsMake(inlineCodeBackgroundTopInset, 0, 0, 0));
++                             UIRectCorner corners = 0;
++                             if (count == 1) {
++                                 corners = UIRectCornerAllCorners;
++                             } else if (i == 0) {
++                                 corners = UIRectCornerTopLeft | UIRectCornerBottomLeft;
++                             } else if (i == count - 1) {
++                                 corners = UIRectCornerTopRight | UIRectCornerBottomRight;
++                             }
++                             UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
++                             [color setFill];
++                             [path fill];
++                         }
++                       }];
++
+   [self processTruncatedAttributedText:textStorage textContainer:textContainer layoutManager:layoutManager];
+ 
+   [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:frame.origin];
