Improve InstantPage V2 layout: true font-height line boxes + code-block style

- layoutTextItem now sizes a text item to the true font line height (A + D)
  instead of the cap box: the line stack starts at lineBoxTopInset (the
  ascender headroom, max(0, fontAscent - fontLineHeight)) and the returned
  height is padded by the last line's descender. Inter-line advance is
  unchanged and per-line frames stay the cap box, so the baseline draw,
  decorations, reveal mask, and inline attachments translate consistently;
  the page grows.
- Size inline custom emoji to ~the font line height
  (font.ascender - font.descender + 4*pointSize/17) so they fit the taller
  line box instead of overflowing it; the line is not inflated.
- Add a dedicated code-block text style for rich messages (monospace font +
  codeBlock theme attribute) threaded through the rich-data bubble and send
  preview; unify the bubble code-block/separator colors.
- Adjust inter-block spacings and assorted V2 layout/divider details.
- Document the true-font-height box and emoji sizing in CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-06-01 17:33:01 +02:00
parent 5403ffa8b0
commit 1872832e28
8 changed files with 154 additions and 83 deletions

View file

@ -35,10 +35,12 @@ private func setupStyleStack(_ stack: InstantPageTextStyleStack, theme: InstantP
stack.push(.linkColor(theme.linkColor))
stack.push(.linkMarkerColor(theme.linkHighlightColor))
switch attributes.font.style {
case .sans:
stack.push(.fontSerif(false))
case .serif:
stack.push(.fontSerif(true))
case .sans:
stack.push(.fontSerif(false))
case .serif:
stack.push(.fontSerif(true))
case .monospace:
stack.push(.fontFixed(true))
}
stack.push(.fontSize(attributes.font.size))
stack.push(.lineSpacingFactor(attributes.font.lineSpacingFactor))
@ -85,6 +87,16 @@ private func instantPageFont(style: InstantPageTextAttributes, bold: Bool = fals
} else {
return Font.regular(size)
}
case .monospace:
if bold && italic {
return Font.semiboldItalicMonospace(size)
} else if bold {
return Font.semiboldMonospace(size)
} else if italic {
return Font.italicMonospace(size)
} else {
return Font.monospace(size)
}
}
}

View file

@ -16,7 +16,7 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
return 0.0
case (.divider, _), (_, .divider):
if fitToWidth {
return 20.0
return 21.0
} else {
return 25.0
}
@ -54,18 +54,14 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
} else {
return 31.0
}
case (.preformatted, .paragraph):
return 19.0
case (.formula, .paragraph):
return 19.0
case (.paragraph, .paragraph):
if fitToWidth {
return 10.0
return 2.0
} else {
return 25.0
}
case (_, .paragraph):
return 20.0
case (.title, .formula), (.authorDate, .formula):
return 34.0
case (.header, .formula), (.subheader, .formula), (.heading, .formula):
@ -76,8 +72,6 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
}
case (.list, .formula):
return 31.0
case (.preformatted, .formula):
return 19.0
case (.paragraph, .formula):
return 19.0
case (_, .formula):
@ -86,8 +80,12 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
return 34.0
case (.header, .list), (.subheader, .list), (.heading, .list):
return 31.0
case (.preformatted, .list):
return 19.0
case (.preformatted, _), (_, .preformatted):
if fitToWidth {
return 12.0
} else {
return 19.0
}
case (.formula, .list):
if fitToWidth {
return 10.0
@ -100,12 +98,6 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
} else {
return 25.0
}
case (.paragraph, .preformatted):
return 19.0
case (.formula, .preformatted):
return 19.0
case (_, .preformatted):
return 20.0
case (_, .header), (_, .subheader), (_, .heading):
return 32.0
default:
@ -121,9 +113,11 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
case .topLevel:
switch lower {
case .heading:
return 13.0
default:
return 6.0
case .table:
return 10.0
default:
return 5.0
}
case .cell:
return 0.0
@ -139,6 +133,8 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?, fi
case .topLevel:
if case .relatedArticles = upper {
return 0.0
} else if case .thinking = upper {
return 2.0
} else {
if fitToWidth {
return 5.0

View file

@ -960,7 +960,11 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
}
let attributes = styleStack.textAttributes()
let font = (attributes[NSAttributedString.Key.font] as? UIFont) ?? UIFont.systemFont(ofSize: 17.0)
let itemSize = font.pointSize * 24.0 / 17.0
// Size the inline emoji to the font's line height (A + D) plus a 4pt bump at the 17pt
// body font (scaled proportionally). Must match the V2 layout's emoji cell size
// (InstantPageV2Layout.swift). The run delegate still reports the font's own
// ascent/descent (below), so the line height is unchanged only the emoji width changes.
let itemSize = font.ascender - font.descender + 4.0 * font.pointSize / 17.0
let extentBuffer = UnsafeMutablePointer<RunStruct>.allocate(capacity: 1)
extentBuffer.initialize(to: RunStruct(ascent: font.ascender, descent: font.descender, width: itemSize))
var callbacks = CTRunDelegateCallbacks(version: kCTRunDelegateVersion1, dealloc: { pointer in

View file

@ -7,6 +7,7 @@ import TelegramUIPreferences
public enum InstantPageFontStyle {
case sans
case serif
case monospace
}
public struct InstantPageFont {
@ -50,6 +51,7 @@ enum InstantPageTextCategoryType {
case credit
case table
case article
case codeBlock
}
public struct InstantPageTextCategories {
@ -61,8 +63,9 @@ public struct InstantPageTextCategories {
let credit: InstantPageTextAttributes
let table: InstantPageTextAttributes
let article: InstantPageTextAttributes
let codeBlock: InstantPageTextAttributes
public init(kicker: InstantPageTextAttributes, header: InstantPageTextAttributes, subheader: InstantPageTextAttributes, paragraph: InstantPageTextAttributes, caption: InstantPageTextAttributes, credit: InstantPageTextAttributes, table: InstantPageTextAttributes, article: InstantPageTextAttributes) {
public init(kicker: InstantPageTextAttributes, header: InstantPageTextAttributes, subheader: InstantPageTextAttributes, paragraph: InstantPageTextAttributes, caption: InstantPageTextAttributes, credit: InstantPageTextAttributes, table: InstantPageTextAttributes, article: InstantPageTextAttributes, codeBlock: InstantPageTextAttributes) {
self.kicker = kicker
self.header = header
self.subheader = subheader
@ -71,26 +74,29 @@ public struct InstantPageTextCategories {
self.credit = credit
self.table = table
self.article = article
self.codeBlock = codeBlock
}
func attributes(type: InstantPageTextCategoryType, link: Bool) -> InstantPageTextAttributes {
switch type {
case .kicker:
return self.kicker.withUnderline(link)
case .header:
return self.header.withUnderline(link)
case .subheader:
return self.subheader.withUnderline(link)
case .paragraph:
return self.paragraph.withUnderline(link)
case .caption:
return self.caption.withUnderline(link)
case .credit:
return self.credit.withUnderline(link)
case .table:
return self.table.withUnderline(link)
case .article:
return self.article.withUnderline(link)
case .kicker:
return self.kicker.withUnderline(link)
case .header:
return self.header.withUnderline(link)
case .subheader:
return self.subheader.withUnderline(link)
case .paragraph:
return self.paragraph.withUnderline(link)
case .caption:
return self.caption.withUnderline(link)
case .credit:
return self.credit.withUnderline(link)
case .table:
return self.table.withUnderline(link)
case .article:
return self.article.withUnderline(link)
case .codeBlock:
return self.codeBlock.withUnderline(link)
}
}
@ -103,7 +109,8 @@ public struct InstantPageTextCategories {
caption: self.caption.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif),
credit: self.credit.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif),
table: self.table.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif),
article: self.article.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif)
article: self.article.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif),
codeBlock: self.codeBlock.withUpdatedFontStyles(sizeMultiplier: sizeMultiplier, lineSpacingFactor: lineSpacingFactor, forceSerif: forceSerif)
)
}
}
@ -214,7 +221,8 @@ private let lightTheme = InstantPageTheme(
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x79828b)),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x79828b)),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: .black),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: .black)
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: .black),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: .black)
),
serif: false,
codeBlockBackgroundColor: UIColor(rgb: 0xf5f8fc),
@ -247,7 +255,8 @@ private let sepiaTheme = InstantPageTheme(
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x927e6b)),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x927e6b)),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x4f321d)),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x4f321d))
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x4f321d)),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x4f321d))
),
serif: false,
codeBlockBackgroundColor: UIColor(rgb: 0xefe7d6),
@ -280,7 +289,8 @@ private let grayTheme = InstantPageTheme(
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xa0a0a0)),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xa0a0a0)),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xcecece)),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xcecece))
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xcecece)),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xcecece))
),
serif: false,
codeBlockBackgroundColor: UIColor(rgb: 0x555556),
@ -313,7 +323,8 @@ private let darkTheme = InstantPageTheme(
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x6a6a6a)),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0x6a6a6a)),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xb0b0b0)),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xb0b0b0))
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xb0b0b0)),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: UIColor(rgb: 0xb0b0b0))
),
serif: false,
codeBlockBackgroundColor: UIColor(rgb: 0x131313),

View file

@ -440,7 +440,7 @@ public func lastTextLineFrame(in layout: InstantPageV2Layout) -> CGRect? {
/// Also returns `trailingBottomPadding`: the renderer draws the baseline at the line frame's maxY,
/// so the visible text of a plain line sits ~5pt below it. A status that *trails on the line* should
/// anchor at `maxY + trailingBottomPadding` to align with where the text actually renders. The pad
/// is 0 when the line is taller than its font line height (an inline animated emoji, ~pointSize·24/17,
/// is 0 when the line is taller than its font line height (a tall inline attachment, e.g. a formula,
/// already pushes maxY down to the right spot). Callers should NOT apply the pad when the status
/// wraps onto its own line below the text there it should sit at the bare maxY.
public func lastTextLineFrameIfLastItemIsText(in layout: InstantPageV2Layout) -> (frame: CGRect, trailingBottomPadding: CGFloat)? {
@ -1899,17 +1899,14 @@ private func layoutDivider(
boundingWidth: CGFloat,
context: LayoutContext
) -> [InstantPageV2LaidOutItem] {
// Geometry matches V1 InstantPageLayout.swift lines 361363:
// lineWidth = floor(boundingWidth / 2.0), x = floor((boundingWidth - lineWidth) / 2.0), h = 1pt.
// Color matches V1: theme.textCategories.caption.color.
let lineWidth = floor(boundingWidth / 2.0)
let frame = CGRect(
x: floor((boundingWidth - lineWidth) / 2.0),
y: 0.0,
width: lineWidth,
height: 1.0
height: UIScreenPixel
)
return [.divider(InstantPageV2DividerItem(frame: frame, color: context.theme.textCategories.caption.color))]
return [.divider(InstantPageV2DividerItem(frame: frame, color: context.theme.separatorColor))]
}
// MARK: - Code block layout (ported from V1 InstantPageLayout.swift lines 329351)
@ -1921,11 +1918,8 @@ private func layoutCodeBlock(
horizontalInset: CGFloat,
context: inout LayoutContext
) -> [InstantPageV2LaidOutItem] {
// V1 InstantPageLayout.swift line 330: backgroundInset = 14.0 (top + bottom padding).
let backgroundInset: CGFloat = 14.0
// V1 line 342: text x offset is 17.0 (hardcoded, not backgroundInset).
let textXOffset: CGFloat = 17.0
// V1 line 348: shape is .rect no corner radius.
let backgroundInset: CGFloat = 15.0
let textXOffset: CGFloat = 11.0
let cornerRadius: CGFloat = 0.0
let attributedString: NSAttributedString
@ -1940,7 +1934,7 @@ private func layoutCodeBlock(
} else {
// V1 lines 335338: fall back to plain paragraph style when no language.
let styleStack = InstantPageTextStyleStack()
setupStyleStack(styleStack, theme: context.theme, category: .paragraph, link: false)
setupStyleStack(styleStack, theme: context.theme, category: .codeBlock, link: false)
attributedString = attributedStringForRichText(text, styleStack: styleStack)
}
@ -2528,10 +2522,12 @@ private func setupStyleStack(_ stack: InstantPageTextStyleStack, theme: InstantP
stack.push(.linkColor(theme.linkColor))
stack.push(.linkMarkerColor(theme.linkHighlightColor))
switch attributes.font.style {
case .sans:
stack.push(.fontSerif(false))
case .serif:
stack.push(.fontSerif(true))
case .sans:
stack.push(.fontSerif(false))
case .serif:
stack.push(.fontSerif(true))
case .monospace:
stack.push(.fontFixed(true))
}
stack.push(.fontSize(attributes.font.size))
stack.push(.lineSpacingFactor(attributes.font.lineSpacingFactor))
@ -2578,6 +2574,16 @@ private func instantPageFont(style: InstantPageTextAttributes, bold: Bool = fals
} else {
return Font.regular(size)
}
case .monospace:
if bold && italic {
return Font.semiboldItalicMonospace(size)
} else if bold {
return Font.semiboldMonospace(size)
} else if italic {
return Font.italicMonospace(size)
} else {
return Font.monospace(size)
}
}
}
@ -2745,10 +2751,18 @@ func layoutTextItem(
let fontLineHeight = floor(fontAscent + fontDescent)
let fontLineSpacing = floor(fontLineHeight * lineSpacingFactor)
let fontDescentBelowBaseline = max(0.0, -fontDescent)
// True font-height line box: shift the whole line stack down by the ascender headroom above
// the cap line (A L) and pad the final height by the descender (D) below the last baseline,
// so a single-line item measures exactly A + D. Exact (not pixel-snapped): this is an
// intra-item line offset; crispness rides on the item's own pixel-snapped frame origin, and
// intra-item line positions may already be fractional (e.g. after a non-integral extraDescent).
// Inter-line advance is unchanged. (Named `lineBoxTopInset` to avoid colliding with the
// formula-bleed `topInset` local near the end of this function.)
let lineBoxTopInset = max(0.0, fontAscent - fontLineHeight)
let baselineToNextTopSlack = max(0.0, fontLineSpacing - 4.0)
var lastIndex: CFIndex = 0
var currentLineOrigin = CGPoint()
var currentLineOrigin = CGPoint(x: 0.0, y: lineBoxTopInset)
var hasAnchors = false
var maxLineWidth: CGFloat = 0.0
@ -2831,7 +2845,12 @@ func layoutTextItem(
} else if let emoji = attributes[ChatTextInputAttributes.customEmoji] as? ChatTextInputTextCustomEmojiAttribute {
let xOffset = CTLineGetOffsetForStringIndex(line, range.location, nil)
let font = (attributes[NSAttributedString.Key.font] as? UIFont) ?? UIFont.systemFont(ofSize: 17.0)
let itemSize = font.pointSize * 24.0 / 17.0
// Size the inline emoji to the font's line height (A + D = the true
// line-box height) plus a 4pt bump at the 17pt body font (scaled
// proportionally) so it reads a touch larger than the bare line box.
// The line is NOT inflated (lineAscent stays fontLineHeight). Must match
// the run-delegate width in attributedStringForRichText (InstantPageTextItem.swift).
let itemSize = font.ascender - font.descender + 4.0 * font.pointSize / 17.0
pendingEmoji.append(PendingV2EmojiAttachment(xOffset: xOffset, range: range, emoji: emoji, size: itemSize))
}
}
@ -2897,7 +2916,7 @@ func layoutTextItem(
extraDescent = max(0.0, lineDescent - baselineToNextTopSlack)
// A centered attachment taller than the line bleeds below the baseline; grow the
// descent so the following line isn't overlapped (mirrors V1's extraDescent handling).
// Emoji at the default 24/17 ratio stay within the line slack and contribute nothing.
// Emoji sized to the font line height (A + D) fit the line box, so they contribute nothing.
for imageItem in lineImageItems {
extraDescent = max(extraDescent, imageItem.frame.maxY - (baselineY + baselineToNextTopSlack))
}
@ -3088,7 +3107,9 @@ func layoutTextItem(
var height: CGFloat = 0.0
if !lines.isEmpty && !(string.string == "\u{200b}" && hasAnchors) {
height = lines.last!.frame.maxY + extraDescent
// + fontDescentBelowBaseline: contain the last line's descender below its baseline, so
// (with the topInset shift) a single-line item measures exactly A + D = true font height.
height = lines.last!.frame.maxY + extraDescent + fontDescentBelowBaseline
}
var textWidth = boundingWidth

View file

@ -228,9 +228,9 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
nameColors = nil
}
let codeBlockBackgroundColor: UIColor
let codeBlockTitleColor: UIColor
let codeBlockAccentColor: UIColor
let codeBlockBackgroundColor: UIColor
if !isIncoming {
mainColor = messageTheme.accentTextColor
if let _ = nameColors?.secondary {
@ -243,12 +243,12 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
if item.presentationData.theme.theme.overallDarkAppearance {
codeBlockTitleColor = .white
codeBlockAccentColor = UIColor(white: 1.0, alpha: 0.5)
codeBlockBackgroundColor = UIColor(white: 0.0, alpha: 0.25)
} else {
codeBlockTitleColor = mainColor
codeBlockAccentColor = mainColor
codeBlockBackgroundColor = mainColor.withMultipliedAlpha(0.1)
}
codeBlockBackgroundColor = mainColor.withMultipliedAlpha(0.1)
} else {
let authorNameColor = nameColors?.main
secondaryColor = nameColors?.secondary
@ -263,11 +263,7 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
codeBlockTitleColor = mainColor
codeBlockAccentColor = mainColor
if item.presentationData.theme.theme.overallDarkAppearance {
codeBlockBackgroundColor = UIColor(white: 0.0, alpha: 0.65)
} else {
codeBlockBackgroundColor = UIColor(white: 0.0, alpha: 0.05)
}
codeBlockBackgroundColor = mainColor.withMultipliedAlpha(0.1)
}
let _ = secondaryColor
@ -284,7 +280,8 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: messageTheme.secondaryTextColor),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: messageTheme.secondaryTextColor),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor)
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor),
)
let pageTheme = InstantPageTheme(
type: isDark ? .dark : .light,
@ -306,8 +303,8 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
controlColor: messageTheme.accentControlColor,
imageTintColor: nil,
overlayPanelColor: isDark ? UIColor(white: 0.0, alpha: 0.13) : UIColor(white: 1.0, alpha: 0.13),
separatorColor: isIncoming ? UIColor(white: 0.0, alpha: 0.25): messageTheme.accentControlColor.withMultipliedAlpha(0.25),
secondaryControlColor: messageTheme.secondaryTextColor
separatorColor: messageTheme.secondaryTextColor.mixedWith(mainColor.withMultipliedAlpha(0.2), alpha: 0.3),
secondaryControlColor: messageTheme.secondaryTextColor.mixedWith(mainColor.withMultipliedAlpha(0.2), alpha: 0.3)
)
var hasDraft = false
@ -320,6 +317,15 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
}
if let attribute = item.message.richText {
#if DEBUG && false
let instantPage = InstantPage(blocks: [.thinking(.concat([
.textCustomEmoji(fileId: 5384559872899555845, alt: "a"),
.plain("Thinking...")
]))], media: [:], isComplete: true, rtl: false, url: "", views: nil)
#else
let instantPage = attribute.instantPage
#endif
let webpage = TelegramMediaWebpage(webpageId: EngineMedia.Id(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(
url: "",
displayUrl: "",
@ -339,7 +345,7 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
file: nil,
story: nil,
attributes: [],
instantPage: attribute.instantPage
instantPage: instantPage
)))
pageWebpage = webpage
@ -352,9 +358,17 @@ public class ChatMessageRichDataBubbleContentNode: ChatMessageBubbleContentNode
current.messageStableVersion == currentMessageStableVersion {
pageLayout = current.layout
} else {
#if DEBUG && false
let instantPage = InstantPage(blocks: [.thinking(.concat([
.textCustomEmoji(fileId: 5384559872899555845, alt: "a"),
.plain("Thinking...")
]))], media: [:], isComplete: true, rtl: false, url: "", views: nil)
#else
let instantPage = attribute.instantPage
#endif
pageLayout = layoutInstantPageV2(
webpage: webpage,
instantPage: attribute.instantPage,
instantPage: instantPage,
userLocation: .other,
boundingWidth: suggestedBoundingWidth - 2.0,
horizontalInset: pageHorizontalInset,

View file

@ -96,7 +96,8 @@ final class ChatSendMessageRichTextPreview: ChatSendMessageContextScreenRichText
caption: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: messageTheme.secondaryTextColor),
credit: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 13.0, lineSpacingFactor: 1.0), color: messageTheme.secondaryTextColor),
table: InstantPageTextAttributes(font: InstantPageFont(style: .sans, size: 15.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor),
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor)
article: InstantPageTextAttributes(font: InstantPageFont(style: .serif, size: 18.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor),
codeBlock: InstantPageTextAttributes(font: InstantPageFont(style: .monospace, size: 14.0, lineSpacingFactor: 1.0), color: messageTheme.primaryTextColor)
)
let pageTheme = InstantPageTheme(
type: isDark ? .dark : .light,
@ -119,7 +120,7 @@ final class ChatSendMessageRichTextPreview: ChatSendMessageContextScreenRichText
tableHeaderColor: messageTheme.accentControlColor.withMultipliedAlpha(0.1),
controlColor: messageTheme.accentControlColor,
imageTintColor: nil,
overlayPanelColor: isDark ? UIColor(white: 0.0, alpha: 0.13) : UIColor(white: 1.0, alpha: 0.13),
overlayPanelColor: messageTheme.accentControlColor.withMultipliedAlpha(0.25),
separatorColor: messageTheme.accentControlColor.withMultipliedAlpha(0.25),
secondaryControlColor: messageTheme.secondaryTextColor
)