Summary
DiffsView requires its parent to supply explicit dimensions (typically flex: 1 or a fixed height). When embedded in a content-sized parent — e.g. a chat bubble or a <View> with no flex — it renders at 0 height and content is invisible.
Reproduce
<View style={{ padding: 16 }}>
<DiffsView content="```ts\nconst x = 1;\n```" colorScheme="light" />
</View>Expected: bubble grows to fit the rendered code block. Actual: bubble has 0 height.
Root cause
Nitro 0.35 does not propagate the underlying UIView's intrinsic size to Yoga. Confirmed by reading the nitrogen output:
nitrogen/generated/shared/c++/views/HybridDiffsComponent.hppdeclares the shadow node as a plainConcreteViewShadowNode<...>, without theMeasurableYogaNode/LeafYogaNodetraits.- Yoga's
LayoutableShadowNode::measureContentdefault returns zero, and is only consulted whenMeasurableYogaNodeis set on the shadow node traits (react-native YogaLayoutableShadowNode.cpp).
The framework-level fix is tracked at mrousavy/nitro#1199 — Support measureContent(...) for Nitro Views, opened by Marc Rousavy himself. As of writing it has 0 comments and no implementation.
Until Nitro adds measureContent, every Nitro view has the same limitation. The pragmatic mitigation at the consumer-library level is to surface the measured height through a callback so the JS parent can apply it as inline style.height.
Use case
Embedding DiffsView in a chat-style transcript: one Claude assistant turn per "bubble", each bubble's height driven by the rendered markdown. flex: 1 is not applicable because bubbles must be content-sized so they stack and the parent ScrollView can scroll across multiple turns.
Proposed solution
Add an optional callback prop to DiffsView:
export interface ContentSize {
width: number;
height: number;
}
export interface DiffsProps extends HybridViewProps {
// ... existing props
onContentSizeChange?: (size: ContentSize) => void;
}Implementation outline (iOS):
- Outer
viewbecomes a smallUIViewsubclass that fires a callback onlayoutSubviewsbounds change. - After
setMarkdown(switched tosetMarkdownManuallyfor synchronous Core Text layout), callmarkdownTextView.boundingSize(for: width)and invoke the callback. - Coalesce same-size reports to avoid feedback loops when JS reflows on each report.
Caller pattern (sidecode wrapper, simplified):
function MarkdownView(props) {
const [height, setHeight] = useState<number>();
return (
<DiffsView
{...props}
onContentSizeChange={{ f: (size) => setHeight(size.height) }}
style={{ height }}
/>
);
}Backward compatible: existing flex: 1 consumers (e.g. v0.app/ios) are not affected; the new prop is opt-in.
Next step
Happy to submit a PR with the spec change, iOS implementation, and an updated example/ showcasing the chat-bubble pattern. Reference the work I've already done in a private fork — full diff is small (~50 LOC excluding nitrogen-generated output). Would you accept this approach?
Activity
0No activity yet.