Design and plan, then implementation: include the details title in
selectableTextItems, restore V2 text selection in the rich bubble,
drop the InstantPageMultiTextAdapter init parameter-name split, and
split LinkHighlightingNode modern-path groups on disjoint rects.
Also includes the IP V2 inline-formula baseline alignment design doc.
drawRectsImageContent's modern path computed nextRadius and
prevRadius as min(outerRadius, floor(|Δx| * 0.5)). When |Δx| < 2
the floor produces 0 and the addArc call becomes a no-op,
leaving an unsmoothed corner at the stair-step. Replace floor
with ceil so any non-zero edge mismatch rounds up to at least
1 px. Exact-equality cases (Δx == 0) are unaffected — they take
the else branch with a straight addLine.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In drawRectsImageContent's modern path the snap loop runs midY
trimming first, leaving rects[i].maxY == rects[i+1].minY for
adjacent line rects. The X-edge snap guard then evaluated
rects[i].insetBy(dx: 0.0, dy: 1.0).intersects(rects[i+1]) — but
positive dy shrinks the rect, so after the trim the guarded
rectangle no longer intersects its neighbor (CGRect.intersects
requires positive-area overlap). Flip dy to -1.0 so the temp
rect grows and touching neighbors satisfy the guard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a base getAnchorRect on ChatMessageBubbleContentNode, the rich-bubble
override (including titleHeight in details recursion), bubble-item
forwarding to content nodes, ChatControllerInteraction.scrollToMessageIdWithAnchor,
and a scrollToAnchor that lands the anchor at the top of the content area /
its line. Threads anchor/scroll params through ChatController and related
call sites.
When a pinToEdgeWithInset item is taller than half of the visible
area (visibleSize.height - insets.top - insets.bottom), cap its
visible portion at halfArea. The remaining height extends past
visibleSize - insets.bottom into the bottom-inset region (occluded
by overlay UI like the chat input panel).
A new private helper `pinToEdgeBottomExtension(forPinnedHeight:)`
returns max(0, pinnedHeight - halfArea). Three sites consume it:
- calculatePinToEdgeTopInset caps the pinned item's contribution
to totalAboveAndPinned via `pinnedHeight - extension`.
- replayOperations isPinToEdgeTarget anchors the pinned item's
apparent maxY at visibleSize - insets.bottom + extension.
- isStrictlyScrolledToPinToEdgeItem matches the new anchor.
Both isPinToEdgeTarget and isStrictlyScrolledToPinToEdgeItem fire
when either calculatePinToEdgeTopInset() > 0 OR extension > 0, so
the cap also applies when items above the pinned item overflow
the visible area (in which case calculatePinToEdgeTopInset returns
0 but extension is still positive).
When the pinned item fits within halfArea, extension == 0 and
behavior is identical to before this change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an experimentalSnapScrollToPinnedItem path that resnaps scroll to
the first pinToEdgeWithInset item after inset/visible-size updates and
on empty itemNodes. Promotes ListViewItem to AnyObject and adds a
pinToEdgeWithInset protocol requirement with a default of false.
Also runs chat history transactions synchronously when updating scroll
around correlated messages, and tightens beganInteractiveDragging weak-
self handling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces a pin-to-edge mode for list items: items whose
pinToEdgeWithInset flag is true are pinned to the bottom edge of the
visible area, with the list geometry (pin inset, scrollToItem,
inset-transition offsetFix, visible-size updates) adjusted to handle
this new mode consistently.
Collected from spec, plan, and 12 iterative fixes during
implementation that refined pin-inset helpers, scroll landings, and
transition math. Pin-inset computation uses frame.size.height;
scrollToItem lands on the item's pinned position via apparentBounds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace characterToGlyphMapping with characterRects ([CGRect]?) on InteractiveTextNodeLine
- Add computeCharacterRects flag to InteractiveTextNodeLayoutArguments
- Compute actual glyph bounding boxes via CTFontGetBoundingRectsForGlyphs during layout
- Implement computeRevealedLines, layoutForCharacterCount, sizeForCharacterCount
- Implement updateRevealCharacterCount to distribute character budget across content item layers
- Update snippet layers to animate per-character rects with SnippetLayer tracking character index
- Mask limit driven by lowest animating snippet index for correct reveal ordering
- Fix listViewAnimationCurveEaseIn to match iOS default ease-in curve (0.42, 0.0, 1.0, 1.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>