Skip to content

docs(ui-kit/ios): rich-text toolbar trailing buttons - #523

Open
shreeyajoshi-cometchat wants to merge 3 commits into
mainfrom
docs/ios-composer-trailing-buttons
Open

shreeyajoshi-cometchat wants to merge 3 commits into
mainfrom
docs/ios-composer-trailing-buttons

Conversation

@shreeyajoshi-cometchat

@shreeyajoshi-cometchat shreeyajoshi-cometchat commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Documents the custom composer trailing buttons that shipped on the iOS UI Kit (ashfaq/eng-38638/merge-aditya, head 66ebac808, open as cometchat-team/uikit-ios#980) and had no documentation on any iOS page.

What's covered

One new section, Trailing toolbar actions, in ui-kit/ios/compact-message-composer.mdx, placed after RichTextToolbarStyle where the toolbar context already is:

  • set(richTextToolbarActions:) — what it appends, and how it differs from replacing the toolbar
  • CometChatRichTextToolbarAction — property table (id, icon, onClick, tint, accessibilityLabel)
  • CometChatComposerInput — member table for the live read/mutate handle
  • A Swift example, plus Leaving Kit-owned runs alone and Colour that survives send subsections

The API is on CometChatCompactMessageComposer only, not CometChatMessageComposer, so this is the only page that changes. The page is already in docs.json nav — no nav change needed.

Colour, and why it needed its own subsection

The composer serializes to markdown on send, so most attributes applied through CometChatComposerInput are composer-local and dropped. Text colour is the exception — it has a wire representation, <color=#rrggbb>, serialized by convertToMarkdown and parsed back in the bubble.

Reaching it requires RichTextFormatterManager.textColorKey alongside .foregroundColor. .foregroundColor alone is written by six different things (mentions, links, inline code, code blocks, ordinary text, the user) and records nothing about who set it, so the serializer keys off the marker instead. Both symbols are public, and textColorKey carries a usage contract in its doc comment — "Always write and remove it together with .foregroundColor" — yet it appeared in no .mdx file on any platform.

This matters because a colour button is the most obvious thing anyone builds with a trailing-action slot, and the Kit ships no colour button of its own. Documenting the slot without the marker is what produces the silent-loss bug: colour renders in the composer, then vanishes on send with no error. So the docs cover the pairing rule, clearing colour, and why #rrggbbaa is rejected (a fully transparent run is an invisible-message vector).

Cross-platform context

Modelled on the Flutter section in #461, which is the closest analogue (both use an action-list rather than a view/template slot). Where iOS genuinely differs, the docs follow the iOS API rather than Flutter's:

  • iOS protects Kit-owned runs by default via protectingKitRuns: true — mentions, links (including a URL still being typed), inline code, code blocks, monospaced runs and fully transparent runs — so the subsection documents the opt-out instead of Flutter's hand-rolled overlap maths, which covers mentions only.
  • iOS has no web-blur caveat, so Flutter's lastNonCollapsedSelection note is omitted.
  • Documents one iOS-specific behaviour no other platform has: actions register per composer instance, so a thread composer can offer a different set from the main one.

On colour, iOS is one of three platforms with Kit-side support — and the only one that documents it.

  • iOS<color=#rrggbb> serialized by RichTextFormatterManager, keyed off textColorKey. Documented here for the first time.
  • Flutter — ships the same wire format on master-v6: rich_text_span.dart wraps coloured ranges in <color=#RRGGBB>…</color>, with tests asserting the output, and master_app drives it from a trailing action via onToolbarTap. Undocumented — applyInlineStyle / removeInlineStyle appear in no .mdx file, and docs(flutter): thread subscriptions, pin & save, composer trailing to… #461 documents only text insertion.
  • React NativeapplyInlineStyle('color', …) / removeInlineStyle, bridged to native on both platforms; StyleRange.color describes #rrggbb as "the wire format's only value-carrying style". Undocumented, and RN has no trailing-button section yet.
  • React / Angular — no Kit colour. Their guides declare a {color=…} regex in consumer code, so the integrator invents the marker and writes the formatter. Documented, but as a build-your-own guide rather than an API reference.
  • AndroidComposerInputController exposes no attribute API at all (insertAtCursor, replaceSelection, toggleFormat, mentionRanges()), so colour can't arise from a trailing button.

Consequence worth flagging beyond this PR: Flutter's and RN's colour support is undocumented, and the React/Angular guide is not a template for them — copying it would have integrators hand-rolling a {color=…} marker while the Kit's own <color=#RRGGBB> sits unused, and the two don't interoperate.

Trailing-button slot status elsewhere: Android, React and Angular are merged; Flutter is open in #461; React Native has a design-doc branch (eng-37949-composer-trailing-buttons-dd) but no shipped slot.

Verification

  • MDX <Tabs>/<Tab>/<Note> tags and code fences balanced
  • All API names, signatures, defaults and caveats taken from the shipped source (CometChatRichTextToolbarAction.swift, CometChatComposerInput.swift, RichTextFormatterManager.swift), not paraphrased from another platform
  • RichTextFormatterManager and textColorKey confirmed public, so the documented route is genuinely integrator-reachable
  • Durability claim taken from the serializer itself (convertToMarkdown enumerating textColorKey), not inferred
  • Cross-platform claims above checked against each platform's repo, not against its docs
  • Branch name satisfies the ^docs/ CI rule

Merge order

This documents API that ships when cometchat-team/uikit-ios#980 merges to dev-v5 and reaches a release. Worth holding until then, so the docs don't describe an API that isn't in a released version yet.

Note that #980 has moved during its own life — protectingKitRuns was renamed from skippingMentions, and textColorKey did not exist at the commit this description originally cited — so the page is worth re-verifying against the final merge commit on dev-v5 rather than any interim head, 66ebac808 included. An "Available since" note goes in once it ships.

🤖 Generated with Claude Code

Documents set(richTextToolbarActions:), CometChatRichTextToolbarAction and
the CometChatComposerInput handle on CometChatCompactMessageComposer, which
shipped without docs on any iOS page.

Follows the Flutter section's shape (open PR #461): what the API appends and
how it differs from replacing the toolbar, a handle member table, a Swift
example, and a "Leaving mentions alone" subsection. The iOS API skips mentions
by default via skippingMentions, so that subsection documents the opt-out
rather than hand-rolled range maths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mintlify

mintlify Bot commented Sep 15, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
cometchat 🟢 Ready View Preview Sep 15, 2026, 1:32 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

… survive send

The first draft warned that attributes are dropped on send without saying that
colour is the exception, which left the most likely use case — a colour button —
looking impossible when the Kit supports it natively.

Text colour has a wire representation (<color=#rrggbb>), serialized by
convertToMarkdown and parsed back in the bubble. Reaching it requires the public
RichTextFormatterManager.textColorKey alongside .foregroundColor, since
.foregroundColor alone is written by six different things and records nothing
about who set it. That key was documented nowhere.

Adds a "Colour that survives send" subsection, rewrites the Note to separate
durable colour from genuinely composer-local attributes, and corrects the
mentions example, which used .backgroundColor and so modelled the lossy pattern.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@jitvarpatil jitvarpatil left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: iOS compact composer — trailing toolbar actions

Requesting changes / hold. The page is well written and most of it matches the source, but the API isn't in any release yet, and one documented parameter has already been renamed, so the examples that use it won't compile. Checked against the current head of cometchat-team/uikit-ios#980 (ashfaq/eng-38638/merge-aditya @ 66ebac808), not only the commit cited in the description.

🔴 Blockers

1. The API isn't in any release

CometChatRichTextToolbarAction, CometChatComposerInput and textColorKey have 0 hits in release-v5-5.1.22, dev-v5 and master-v5. uikit-ios#980 ("Enterprise Readiness: Tracks 1, 2 and 3 combined") is still open. As the description already suggests, please hold until it ships, then add an "Available since vX" note like the other pin/save/thread pages.

2. skippingMentions: no longer exists — it's now protectingKitRuns:

Renamed in 30bc8955e ("fix(composer): preserve user text colour across code block transitions"). 234a9faf4 had skippingMentions ×11; the branch head has 0. Anyone copying from the member table or Leaving mentions alone gets a compile error on all four mutators:

public func applyAttributes(_:to:protectingKitRuns: Bool = true)
public func removeAttributes(_:from:protectingKitRuns: Bool = true)
public func applyAttributesToSelection(_:protectingKitRuns: Bool = true) -> Bool
public func removeAttributesFromSelection(_:protectingKitRuns: Bool = true) -> Bool

The meaning is also broader: it protects mentions, links, inline code, code blocks and the blockquote bar ("runs the kit paints on its own schedule"), not just mentions. That subsection needs rewording, not only a rename. (Side note for the UI Kit team: the mentionRanges doc comment in CometChatComposerInput.swift still says "the mutating methods skip these by default", which is now stale.)

3. "A colour that cannot be sent is not applied locally either" is wrong for this API

That rule is enforced only for hex strings (RichTextWireFormat.isValidHex). applyAttributes / applyAttributesToSelection don't validate colours, so a UIColor with alpha < 1 is rendered in the composer. On send, wireFormatHex returns nil when alpha < 1 (RichTextWireFormat.swift:199) and the colour is silently dropped — exactly the "renders in the composer, vanishes on send" bug this section sets out to prevent. Suggest: "Use opaque colours. A translucent UIColor renders in the composer but is dropped on send; on the wire only #rgb and #rrggbb are accepted (#rrggbbaa is rejected, since a transparent run would be an invisible message)."

🟠 Should fix

  • Commit in the description — it cites 234a9faf4, but textColorKey doesn't exist at that commit; it was added later on the branch. Please re-verify against the final merged SHA before merging.
  • Nit — the "highlight" example actually turns the text red. Rename it (e.g. id: "red_text") so readers don't expect a background highlight.

✅ Verified correct at branch head

  • CometChatRichTextToolbarAction fields and init (tint/accessibilityLabel default nil, onClick last) — the trailing-closure examples compile.
  • set(richTextToolbarActions:) closure signature; trailingActionButtons is public private(set); showRichTextFormattingOptions / enableRichTextFormatting names.
  • CometChatComposerInput: text, attributedText, selectedRange, hasSelection, typingAttributes, mentionRanges, attributes(at:), insertAtCaret, setAttributedText(_:preservingSelection:), setSelectedRange, commit(). Valid only during onClick (text view held weakly), and commit() runs automatically after onClick (CometChatRichTextToolbar.swift:237-238).
  • RichTextFormatterManager.textColorKey is public with the "write and remove together with .foregroundColor" contract; <color=#rrggbb> wire format; #rgb/#rrggbb accepted, #rrggbbaa rejected.

The member table and "Leaving mentions alone" named the mutators'
parameter `skippingMentions`, which does not exist on the shipped
branch — it is `protectingKitRuns`. Anyone copying the documented
signature got a compile error.

The meaning was understated too. The flag protects every run the kit
repaints on its own schedule, not just mentions: links (including a
URL still being typed), inline code, code blocks, monospaced runs,
and fully transparent runs. Reworked the subsection around that and
renamed it accordingly; blockquote is deliberately absent, since the
composer draws its quote bar as a view rather than as text.

Also corrected the colour note: a translucent UIColor is not rejected
locally. It renders in the composer and is dropped on send, which is
the silent loss the section exists to prevent. Renamed the example
from "highlight" to "red_text", which is what it actually does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shreeyajoshi-cometchat

Copy link
Copy Markdown
Contributor Author

Thanks — all three blockers confirmed against the branch head, and the parameter rename is now pushed.

Fixed in 9beb518c:

  1. skippingMentions:protectingKitRuns: in all four places. The subsection is reworked rather than renamed, since the flag protects more than mentions — links (including a URL still being typed), inline code, code blocks, monospaced runs, and fully transparent runs.
  2. The translucent-colour note. Your reading is right: CometChatComposerInput has no colour validation at all, so a translucent UIColor renders locally and is dropped on send. Wording now says exactly that.
  3. id: "highlight"id: "red_text".

On the blockquote bar — I left it out of the protected list deliberately. kitOwnedRanges has no isBlockquoteKey branch, so blockquote isn't among the protected runs. The composer draws its quote bar as a UIView (blockquoteBarView) rather than as text, so an action can't reach it — ComposerColorPreservationTests.swift:141-143 states this directly: "The composer draws its blockquote bar as a view, so this guards the general rule rather than a run the composer produces today." A colour action over quoted text overwrites the grey, but isBlockquoteKey is a separate key, so the > prefix still serializes and the quote survives send. Listing it as protected would have been inaccurate.

Holding on blocker 1 as you asked. All four symbols are absent from release-v5-5.1.22, dev-v5 and master-v5; I'll add an "Available since" note once #980 ships. One thing worth pinning down: this branch renamed a parameter and added textColorKey mid-life (textColorKey doesn't exist at 234a9faf4, which is why the description's SHA misled), so I'd suggest re-verifying against the final merge commit on dev-v5 rather than any interim head, 66ebac808 included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants