Implement InstantPageBlock.thinking (pageBlockThinking) parsing & serialization

Add a first-class InstantPageBlock.thinking(RichText) case mirroring .kicker:
- enum case + InstantPageBlockType.thinking = 30; Postbox coding & equality
- FlatBuffers InstantPageBlock_Thinking schema (appended last) + Swift codecs
- API parse at InstantPage.swift; output-only on send (apiInputBlock returns nil)
- no-op stubs in the two exhaustive switches (preview text, V2 layout)

Rendering is out of scope. Verified by a full Bazel build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-05-29 16:07:09 +02:00
parent bd8886b0f0
commit 354e1c7a66
5 changed files with 38 additions and 3 deletions

View file

@ -889,6 +889,8 @@ private func layoutBlock(
context: &context)
// Block kinds filled in by later tasks:
case .thinking:
return []
case .unsupported:
return []
}

View file

@ -36,7 +36,8 @@ union InstantPageBlock_Value {
InstantPageBlock_RelatedArticles,
InstantPageBlock_Map,
InstantPageBlock_Heading,
InstantPageBlock_Formula
InstantPageBlock_Formula,
InstantPageBlock_Thinking
}
table InstantPageBlock {
@ -197,6 +198,10 @@ table InstantPageBlock_Formula {
latex:string (id: 0, required);
}
table InstantPageBlock_Thinking {
text:RichText (id: 0, required);
}
table InstantPageCaption {
text:RichText (id: 0, required);
credit:RichText (id: 1, required);

View file

@ -328,14 +328,16 @@ extension InstantPageBlock {
self = .heading(text: RichText(apiText: pageBlockHeading6.text), level: 6)
case let .pageBlockMath(pageBlockMath):
self = .formula(latex: pageBlockMath.source)
case .inputPageBlockMap, .pageBlockThinking:
case let .pageBlockThinking(pageBlockThinking):
self = .thinking(RichText(apiText: pageBlockThinking.text))
case .inputPageBlockMap:
self = .unsupported
}
}
func apiInputBlock() -> Api.PageBlock? {
switch self {
case .unsupported, .title, .subtitle, .kicker, .header, .subheader, .cover, .channelBanner, .authorDate, .relatedArticles, .webEmbed, .postEmbed:
case .unsupported, .title, .subtitle, .kicker, .header, .subheader, .cover, .channelBanner, .authorDate, .relatedArticles, .webEmbed, .postEmbed, .thinking:
return nil
case let .heading(text, level):
let block: Api.PageBlock

View file

@ -34,6 +34,7 @@ private enum InstantPageBlockType: Int32 {
case map = 27
case heading = 28
case formula = 29
case thinking = 30
}
private func decodeListItems(_ decoder: PostboxDecoder) -> [InstantPageListItem] {
@ -82,6 +83,7 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
case slideshow(items: [InstantPageBlock], caption: InstantPageCaption)
case channelBanner(TelegramChannel?)
case kicker(RichText)
case thinking(RichText)
case table(title: RichText, rows: [InstantPageTableRow], bordered: Bool, striped: Bool)
case details(title: RichText, blocks: [InstantPageBlock], expanded: Bool)
case relatedArticles(title: RichText, articles: [InstantPageRelatedArticle])
@ -168,6 +170,8 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
self = .audio(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decodeCaption(decoder))
case InstantPageBlockType.kicker.rawValue:
self = .kicker(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText)
case InstantPageBlockType.thinking.rawValue:
self = .thinking(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText)
case InstantPageBlockType.table.rawValue:
self = .table(title: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, rows: decoder.decodeObjectArrayWithDecoderForKey("r"), bordered: decoder.decodeInt32ForKey("b", orElse: 0) != 0, striped: decoder.decodeInt32ForKey("s", orElse: 0) != 0)
case InstantPageBlockType.details.rawValue:
@ -339,6 +343,9 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
case let .kicker(text):
encoder.encodeInt32(InstantPageBlockType.kicker.rawValue, forKey: "r")
encoder.encodeObject(text, forKey: "t")
case let .thinking(text):
encoder.encodeInt32(InstantPageBlockType.thinking.rawValue, forKey: "r")
encoder.encodeObject(text, forKey: "t")
case let .table(title, rows, bordered, striped):
encoder.encodeInt32(InstantPageBlockType.table.rawValue, forKey: "r")
encoder.encodeObject(title, forKey: "t")
@ -530,6 +537,12 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
} else {
return false
}
case let .thinking(text):
if case .thinking(text) = rhs {
return true
} else {
return false
}
case let .table(lhsTitle, lhsRows, lhsBordered, lhsStriped):
if case let .table(rhsTitle, rhsRows, rhsBordered, rhsStriped) = rhs, lhsTitle == rhsTitle, lhsRows == rhsRows, lhsBordered == rhsBordered, lhsStriped == rhsStriped {
return true
@ -692,6 +705,11 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
throw FlatBuffersError.missingRequiredField()
}
self = .kicker(try RichText(flatBuffersObject: value.text))
case .instantpageblockThinking:
guard let value = flatBuffersObject.value(type: TelegramCore_InstantPageBlock_Thinking.self) else {
throw FlatBuffersError.missingRequiredField()
}
self = .thinking(try RichText(flatBuffersObject: value.text))
case .instantpageblockTable:
guard let value = flatBuffersObject.value(type: TelegramCore_InstantPageBlock_Table.self) else {
throw FlatBuffersError.missingRequiredField()
@ -937,6 +955,12 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable {
let start = TelegramCore_InstantPageBlock_Kicker.startInstantPageBlock_Kicker(&builder)
TelegramCore_InstantPageBlock_Kicker.add(text: textOffset, &builder)
offset = TelegramCore_InstantPageBlock_Kicker.endInstantPageBlock_Kicker(&builder, start: start)
case let .thinking(text):
valueType = .instantpageblockThinking
let textOffset = text.encodeToFlatBuffers(builder: &builder)
let start = TelegramCore_InstantPageBlock_Thinking.startInstantPageBlock_Thinking(&builder)
TelegramCore_InstantPageBlock_Thinking.add(text: textOffset, &builder)
offset = TelegramCore_InstantPageBlock_Thinking.endInstantPageBlock_Thinking(&builder, start: start)
case let .table(title, rows, bordered, striped):
valueType = .instantpageblockTable
let titleOffset = title.encodeToFlatBuffers(builder: &builder)

View file

@ -151,6 +151,8 @@ extension InstantPageBlock {
return ""
case .kicker:
return ""
case .thinking:
return ""
case .table:
//TODO:localize
return "Table"