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>
Upgrade InstantPageBlock.blockQuote from (text, caption) to
(blocks: [InstantPageBlock], caption). Legacy inbound shapes (API
pageBlockBlockquote, Postbox "t", FlatBuffers text) lift into
[.paragraph(text)]; outbound stays on the legacy wire constructor for
empty/single-paragraph quotes and uses pageBlockBlockquoteBlocks
otherwise. V1/V2 renderers recurse into child blocks with a fixed 10pt
inter-child gap; markdown forward/reverse, entity-expressibility, and
preview text updated. pullQuote is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a first-class `checked: Bool?` to InstantPageListItem (orthogonal to the
ordered-list `num`), replacing the prior sentinel-string-in-num prototype.
- Data model: enum third associated value on .text/.blocks; Postbox + a new
FlatBuffers `checkState` tri-state field (0=nil,1=unchecked,2=checked,
backward-compatible); Equatable.
- Transmission: read/write the native API `checkbox`=flags.0 / `checked`=flags.1
bits on all four PageListItem / PageListOrderedItem constructors, so checkbox
state survives the server round-trip for sender and recipients.
- Markdown: forward parser routes detected `[ ]`/`[x]` into `checked` (keeping
the real number for ordered items); reverse converter emits `- [ ]`/`- [x]`
so editing a rich message round-trips.
- Display: V1 + V2 layout detect via `item.checked`; the V2 renderer draws real
CheckNode artwork (was a placeholder); preview text shows a checkbox glyph
instead of leaking the old sentinel.
Spec: docs/superpowers/specs/2026-05-27-instantpage-list-checkbox-design.md
Plan: docs/superpowers/plans/2026-05-27-instantpage-list-checkbox.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the RichText.textSpoiler case end-to-end: model + Postbox coding,
FlatBuffers schema/codec, and Api.RichText parsing/serialization (lossless).
Display attaches the spoiler attribute, collects per-line spoiler rects in
the V2 layout, clips obscured glyphs and draws an energy-gated dust overlay
in the V2 text view, propagates the reveal flag across the nested V2 tree
(building dust for freshly-created text views), and wires tap-to-reveal
routing in the rich-data bubble.
Also seeds the streaming reveal cursor across chunk rebuilds and refines
status date positioning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implement the nine message-entity RichText cases (textMention,
textMentionName, textHashtag, textCashtag, textBotCommand, textBankCard,
textAutoUrl, textAutoEmail, textAutoPhone): model + Postbox coding,
FlatBuffers schema/codec, Api.RichText parsing/serialization (lossless),
InstantPage display attaching the matching TelegramTextAttributes keys, and
tap routing in the rich-data bubble mirroring ChatMessageTextBubbleContentNode.
mentionName display resolves via EnginePeer.Id (PeerId not in scope).
Also lets rich-data text selection reach a line's trailing edge and fixes
the date/status node positioning to match TextBubble.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the RichText.textCustomEmoji(fileId:alt:) case end-to-end: model +
Postbox coding, FlatBuffers schema/codec, and Api.RichText
parsing/serialization (lossless), plus display in the InstantPage V2
renderer. The emoji renders as an InlineStickerItemLayer that participates
in the streaming reveal (pops in as the reveal cursor crosses it) and is
gated by the bubble's visibility rect, propagated recursively through the
nested V2 view tree. Also frees the CTRunDelegate extent buffers for the
image/formula/custom-emoji attachment arms and documents the feature in
CLAUDE.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>