Commit graph

2 commits

Author SHA1 Message Date
isaac
a731f8bdc0 Implement textDate autoformatter for InstantPage V2 rich bubbles
Render RichText.textDate (API constructor textDate(flags, text, date)) as a
locale/timezone-correct, relative-aware, tappable date inside V2 rich-message
bubbles, reusing the existing stringForEntityFormattedDate autoformatter.

- Model: add RichText.textDate(text📅format:) with Postbox coding,
  FlatBuffers schema (RichText.fbs; flatc regenerates), Equatable and plainText;
  parse flags->DateTimeFormat? at the API boundary (flags==0 => nil, matching
  the messageEntityFormattedDate convention) with a symmetric apiRichText()
  round-trip. The new case is handled in every exhaustive RichText switch
  (SyncCore, ApiUtils, InstantPageTextItem, InstantPageAnchorPath,
  InstantPagePreviewText, BrowserReadability, BrowserMarkdown).
- Render: attributedStringForRichText gains an optional formatDate closure;
  the V2 layout builds it from the message's strings/dateTimeFormat and threads
  it to every text-building call site, formatting the timestamp and tagging the
  run with TelegramTextAttributes.Date.
- Tappable: the Date attribute is added to linkSelectionRects hit-testing and
  mapped in the rich bubble's entityTapContent to the existing .date tap action
  (opens the date context menu).
- Live update: the V2 layout accumulates a page-wide formattedDateUpdatePeriod
  (>=10s) for relative dates; the rich bubble schedules a refresh timer on it,
  recreating it only when the period changes. The currentPageLayout cache is
  bypassed for relative-date pages so the timer-driven relayout actually
  re-formats the date.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:45:48 +02:00
isaac
a81d0ee944 Anchor navigation in InstantPage V2 / rich-content bubbles
Tapping an intra-message #fragment link in a rich-data bubble now scrolls
the chat so the matching anchor lands just below the content-area top,
expanding any enclosing collapsed <details> first. Anchors come from
server/AI-sent InstantPages (block .anchor or inline RichText.anchor); the
compose path is unchanged.

- InstantPageV2View.anchorFrame(name:) resolves an anchor's frame in the
  live layout (text/codeBlock/thinking/details/table), mirroring findTextItem.
- instantPageAnchorPath(in:name:) is a pure model walk returning the
  <details>-sibling-ordinal path to an anchor; its recursion set matches
  exactly the containers the V2 layout flattens through layoutBlock
  (.blockQuote/.cover/.list .blocks), keeping ordinals consistent with the
  layout's detailsIndexCounter.
- InstantPageV2View.firstCollapsedDetails(forOrdinalPath:) maps that path to
  the first not-yet-expanded details' live index (read, never reproduced).
- The rich bubble fills the two stubbed seams: getAnchorRect, and a
  fragment-link route in tapActionAtPoint that drives a resolve -> expand ->
  scroll state machine (pendingScrollAnchor + progress guard + a
  post-relayout hook). Taps are gated off while the message streams.

Verified by the full Bazel build; runtime behavior not yet exercised.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 01:11:27 +02:00