Skip to content

AI

AiMessagePartView

Bit.BlazorUI.AI

BitAiMessagePartView renders one part of a message with the view of its kind: Markdown text, a reasoning panel, a code block, a tool call card, a tool result, an image, a file chip, an artifact card, structured data, a generated component, a source list, the card of a citation or an error. It listens to its part rather than to the message, so a token arriving in the last paragraph of a long answer re-renders that paragraph alone, at most once per render interval. A renderer the app registers for a part kind replaces the built-in view, inside a boundary that contains its failures; a kind no view knows is shown as the text it carries.

Notes

To use this component, you need to install the Bit.BlazorUI.AI(opens in a new tab) nuget package, as described in the Optional steps of the Getting started page.

BitAiChatMessage renders every part of a message through this view; render it yourself to lay the parts of a message out your own way. It needs AddBitBlazorUIAIServices at startup, whose BitAiRendererRegistry it looks a renderer up in (unless a registry is cascaded to it), even where the app registers no renderer. It is the extension point for content the library has no view for: give the part a kind of its own (BitAiCustomPart<T>, BitAiCustomJsonPart) and register a renderer for that kind with BitAiRendererRegistry at startup. A registered renderer is handed what the model produced, so it is rendered inside a BitAiRendererBoundary: a shape it did not expect replaces that one part with a notice. See the Streaming, Custom tool UI and Performance scenarios.

The same registry holds the other renderers of the package, each shown on the page of the view that uses it: tools (AiToolCallView), artifact kinds (AiArtifactView), structured-data schemas (AiStructuredDataView), and code languages, mathematics and citation chips (AiMarkdownView). Every AI component finds its renderers, like its options, through the base class they share - a registry cascaded to it, else the registered one (see BitAiComponentBase).

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

Hand it a Part and the Message it belongs to, and render one per part of the message: each part is drawn by the view of its kind - here two text parts around a tool call.

Let me check the weather in Oslo.

It is -3 °C and snowing in Oslo - bring a warm coat.

Streaming

Each view listens to its own part. As the answer below streams in, its text part is the only thing that re-renders - at most once per RenderInterval of the options, however fast the tokens come - while the page, and the tool card above, are left alone.



Part kinds

A reasoning part is a collapsed panel, a code part a code block, a tool result a card of its own and an error part a compact error in the flow of the answer - see the BitAiMessagePartKinds table for every kind. A part of a kind no view knows and no renderer is registered for is shown as the Text it carries, rather than dropped.

Program.cs
builder.Services.AddBitBlazorUIAIServices(ai => ai.UseTransport<MyTransport>());
read_logs
No errors in the last hour.

Poll: which day suits you, Tuesday or Thursday?

Citations

The bracketed numbers of a text part are chips for the citations of its message (BitAiMessage.AddCitation numbers them). A BitAiCitationPart places one citation where it sits between the parts instead: the card of its source, numbered as the message numbers it, with the Quote and the Locator of the citation under it. OnCitationClick reports the one the user activates, a chip or the card; while it is not set, the card is a link to the source.

Interactive Server keeps the components on the server , and prerendering paints them once before they become interactive .

2
Prerendering
Product documentation
Prerendering renders the page on the server first, so it is painted before its components become interactive.
Section 3: Prerendering

Activated: none yet

Conversation & Session

Two kinds of part need more than their message. An artifact part only names its artifact, which belongs to the conversation: handed the Conversation, the view finds it there and shows it in a panel - without one, a placeholder tag (see the Missing artifacts example of AiArtifactPartView). A tool call waiting for approval hands the decision to the Session waiting for it. A registered part renderer is handed both (BitAiMessagePartContext). Below, a session continues a conversation whose first answer wrote a plan: ask it to delete the archived records, and allow or decline the call it makes - the turn waits for your decision.



You

Plan the clean-up of the old records.

Assistant

Here is the plan:

Clean-up plan

Clean-up plan

  1. Archive the records nobody opened this year.

  2. Delete the archived records.

Custom part renderer

A renderer registered for a kind (BitAiRendererRegistry.RegisterPart, see the tables below) replaces the built-in view of every part of that kind. Here an invoice the model produced travels as a BitAiCustomJsonPart of the kind demo:invoice, and an InvoiceCard component is registered for it: the registry renders it with the part as its Part parameter. RegisterPart(kind, context => builder => ...) takes a BitAiMessagePartRenderer delegate instead, handed the part, its message, the conversation and the session (the Renderers of a region example below). A renderer registered for a built-in kind replaces its built-in view the same way. An app registers its renderers once, at startup (the Program.cs tab); this page registers its own kind as it loads instead, under a kind nothing else uses.

Here is the invoice for October:

Invoice INV-1042
  • Design review 1,200.00 EUR
  • Hosting, 3 months 90.00 EUR
Total 1,290.00 EUR

Renderer failure

A registered renderer is handed what a model produced, so the view renders it inside a BitAiRendererBoundary. The second invoice below has no lines, which the card was not written for: it throws, and that one part is replaced by a notice naming the kind whose renderer failed (point at it for the exception). The rest of the message is unaffected.

Here are the invoices for October and November:

Invoice INV-1042
  • Design review 1,200.00 EUR
  • Hosting, 3 months 90.00 EUR
Total 1,290.00 EUR
The renderer for 'demo:invoice' failed

Both are due at the end of the month.

Renderers of a region

The registered registry is one instance for the whole app - for every user of a server-side app - so a region with renderers of its own (a page, a test, one chat among others) cascades a registry of its own: a CascadingValue of a BitAiRendererRegistry, which every AI component inside it uses in place of the registered one. It replaces the registered registry rather than adding to it, so it holds every renderer the region needs. The renderer here is the delegate form, RegisterPart(kind, context => builder => ...): handed a BitAiMessagePartContext - the part, its message, the conversation and the session - it returns the markup to draw. The same message is rendered twice: outside the region its poll is the text of a kind no view knows, as in the Part kinds example; inside it, a list of choices.

Let us find a day for the review.

Poll: which day suits you, Tuesday or Thursday?


Let us find a day for the review.

Which day suits you?

Cascading parameters

BitParams with a BitAiMessagePartViewParams sets the look of every part view inside it - inside a chat, of every part of every message. The part, its message, the conversation, the session and OnCitationClick are not among them: they are what makes one part view the one it is. It is a default, not an override: a view keeps what it sets itself (the last one sets Styles of its own, which replace the cascaded ones).

Interactive Server keeps the components on the server , and prerendering paints them once before they become interactive .

2
Prerendering
Product documentation
Prerendering renders the page on the server first, so it is painted before its components become interactive.
Section 3: Prerendering
2
Prerendering
Product documentation
Prerendering renders the page on the server first, so it is painted before its components become interactive.
Section 3: Prerendering

Style & Class

Style and Class land on the root, the block each part is laid out in; Styles.Fallback and Classes.Fallback on the text shown for a kind no view knows; Citation, CitationCard, CitationQuote and CitationLocator on what a citation part shows, which the public --bit-AiMessagePartView-* variables (see the CSS variables table) restyle too. What any other part shows is drawn by the view of its kind, restyled through that view's own parameters and variables.

It is -3 °C and snowing in Oslo.

It is -3 °C and snowing in Oslo.

Poll: which day suits you, Tuesday or Thursday?

Poll: which day suits you, Tuesday or Thursday?

1
Oslo forecast
Weather service
Snow showers through the afternoon, -3 °C.
Today, 14:00
1
Oslo forecast
Weather service
Snow showers through the afternoon, -3 °C.
Today, 14:00

RTL

In right-to-left text each part reads from the right - the Markdown of a text part, the text of a kind no view knows. Dir writes the direction on the root of the view, and the view of the part inherits it.

هوای تهران امروز ۱۸ درجه و آفتابی است.

نظرسنجی: کدام روز برایتان مناسب است، سه‌شنبه یا پنج‌شنبه؟

CSS variables

The public custom properties this component reads off its root, for what no parameter covers.

Every variable is read with a fallback and never declared by the component, so it inherits like any other custom property: set one on :root (or in a [bit-theme] block) to restyle every instance, on any ancestor to restyle the ones inside it, or on the Style of one instance to restyle it - and any instance nested inside it, which inherits the value too; a parameter changes one alone, as does the real CSS property on the instance's Style / Styles when that property does not inherit (opacity: 1 keeps one disabled instance undimmed without undimming a disabled one inside it), while an inherited one such as color or font-family also reaches the nested instances that do not set it themselves. Leave a variable unset and the component falls back to the theme token beside it.

BitAiMessagePartView CSS variables

Name Default value Description
--bit-AiMessagePartView-citation-gap spacing(0.75) Room between the card, the quote and the locator of a citation part.
--bit-AiMessagePartView-quote-color var(--bit-clr-fg-sec) Text color of the quote of a citation part.
--bit-AiMessagePartView-quote-border-color var(--bit-clr-brd-pri) Bar down the start edge of the quote of a citation part.
--bit-AiMessagePartView-locator-color var(--bit-clr-fg-sec) Text color of the locator (a page, a section) of a citation part.

API

Every parameter, public member, sub-class and enum this component exposes.

BitAiMessagePartView parameters

Name Type Default value Description
Classes BitAiMessagePartViewClassStyles? null Custom CSS classes for different parts of the message part view.
Conversation BitAiConversation? null The conversation, when the part is rendered inside one. An artifact part is resolved against it (the artifact lives on the conversation, the part only points at it), and a registered part renderer is handed it (BitAiMessagePartContext.Conversation).
Message BitAiMessage The message the part belongs to (required). A text part reads its citations from it, a tool call and structured data hand it on, and a registered part renderer is handed it.
OnCitationClick EventCallback<BitAiCitation> Raised when the user activates a citation inside this view: a citation chip of a text part, or the card of a citation part. While it is not set, the card of a citation part is a link to its source instead.
Part BitAiMessagePart The part to render (required), drawn by the view of its kind - or by the renderer registered for its Kind, which is looked up again every time the view's parameters are set. The view subscribes to the part and re-renders whenever it changes, at most once per BitAiUiOptions.RenderInterval; handing it another part moves the subscription to that one.
Session BitAiChatSession? null The session driving the conversation, when there is one. A tool call hands its approval decisions to it, and a registered part renderer is handed it (BitAiMessagePartContext.Session), so it can act as well as display.
Styles BitAiMessagePartViewClassStyles? null Custom CSS styles for different parts of the message part view.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
Disabled bool false Gets or sets a value indicating whether the component is disabled and cannot respond to user interaction.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. An attribute written on the component wins over an entry of the same name.
Id string? null Gets or sets the unique identifier for the component's root element.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitAiMessagePartViewClassStyles properties

Name Type Default value Description
Root string? null Custom CSS classes/styles for the root element of the BitAiMessagePartView.
Fallback string? null Custom CSS classes/styles for the text the BitAiMessagePartView shows for a part of a kind it does not know.
Citation string? null Custom CSS classes/styles for the container of a citation part of the BitAiMessagePartView.
CitationCard BitAiSourceCardClassStyles? null Custom CSS classes/styles for the BitAiSourceCard of a citation part of the BitAiMessagePartView.
CitationQuote string? null Custom CSS classes/styles for the quote of a citation part of the BitAiMessagePartView.
CitationLocator string? null Custom CSS classes/styles for the locator (a page, a section) of a citation part of the BitAiMessagePartView.

BitAiMessagePart properties

The base class of every part of a message, with an identity and a change notification of its own. Only the part that grew raises Changed while a reply streams, which is what lets one view re-render per token instead of the whole message.

Name Type Default value Description
PartId string The identifier of the part, generated unless the transport addresses parts explicitly. A good @key for its view.
Kind string The discriminator the view is chosen by, and the one a part renderer is registered against. The built-in kinds are the BitAiMessagePartKinds constants; a custom part takes any other.
Version int 0 Increments on every change, for a cheap ShouldRender of a view of the app's own: compare it with the version seen at the last render.
Message BitAiMessage? null The message the part belongs to, once it has been added to one.
Changed event Action<BitAiMessagePart> Raised after the part changes, synchronously on the thread that changed it - a component marshals onto the renderer with InvokeAsync, which the built-in views do for you.

BitAiMessagePartKinds properties

The Kind constants the library ships with, and what this view renders each as. Use them, rather than string literals, when registering a part renderer.

Name Type Default value Description
Text "text" A BitAiTextPart, the ordinary body of a reply: rendered as streaming Markdown by a BitAiTextPartView, with its citation chips.
Markdown "markdown" A BitAiMarkdownPart, text explicitly marked as Markdown: rendered like Text.
Reasoning "reasoning" A BitAiReasoningPart, the model's reasoning: a BitAiReasoningPartView, collapsed unless CollapseReasoning is turned off.
Code "code" A BitAiCodePart, a standalone code block with a language and an optional file name: a BitAiCodeBlockView.
Image "image" A BitAiImagePart: a BitAiImagePartView.
File "file" A BitAiFilePart: a BitAiAttachmentChip of its attachment.
Citation "citation" A BitAiCitationPart, a single citation placed where it sits between the parts: the BitAiSourceCard of its source, numbered as the message numbers it, with the Quote and the Locator of the citation under it. The card raises OnCitationClick when that is set, and links to the source otherwise.
Sources "sources" A BitAiSourcesPart, where the source list of the message goes: a BitAiSourceList of the citations it names, or of all the message's citations.
ToolCall "tool-call" A BitAiToolCallPart: a BitAiToolCallView, or the renderer registered for its tool.
ToolResult "tool-result" A BitAiToolResultPart, a result carried on its own rather than on its call (the stream processor never adds one; an app does, for a conversation it imports): a BitAiToolResultView.
Artifact "artifact" A BitAiArtifactPart, pointing at an artifact of the Conversation: a BitAiArtifactPartView.
StructuredData "structured-data" A BitAiStructuredDataPart, a JSON value that may still be streaming: a BitAiStructuredDataView, or the renderer registered for its schema.
GenerativeUi "generative-ui" A BitAiGenerativeUiPart, a component the model chose with the properties it supplied: a BitAiGenerativeUiView.
Form "form" A form the user fills in and sends back. No built-in part carries this kind: a form arrives as structured data (rendered with BitAiForm by a structured renderer), or as a custom part of this kind with a part renderer registered for it.
Error "error" A BitAiErrorPart, a failure inside an answer that is otherwise worth keeping: a BitAiErrorView.

BitAiCustomPart<T> / BitAiCustomJsonPart properties

The parts for content the library has no concept of. Give one a kind of its own and register a renderer for that kind: it is then rendered, copied and virtualized like a built-in part. A kind with no renderer is shown as its Text.

Name Type Default value Description
BitAiCustomPart<T>(kind, data, partId?) constructor A part carrying a typed payload, created by the app.
BitAiCustomJsonPart(kind, data, partId?) constructor A part carrying a JsonElement payload, for one that arrives as JSON - from a model or a transport.
Kind string The discriminator a renderer is registered against. Namespace it (acme:invoice) so it never collides with a built-in kind.
Data T / JsonElement The payload. Setting Data of a BitAiCustomPart<T> notifies its view; after changing the payload in place, call its Refresh(). The data of a BitAiCustomJsonPart is fixed.
Text string? null The plain-text projection of the part, used when it is copied or exported - and shown by this view when no renderer is registered for the kind.

BitAiRendererRegistry properties

Where an app registers its own renderers, which the components resolve before their built-in views. A singleton: register at startup (AddBitBlazorUIAIServices(ai => ai.ConfigureRenderers(renderers => ...))) and treat the entries as fixed afterwards - every entry is app-wide. A region with renderers of its own cascades a registry of its own (new BitAiRendererRegistry(), in a CascadingValue), which every AI component inside it uses in place of the registered one - in place of it, not on top of it. These are its part members; RegisterTool is on the AiToolCallView page, RegisterArtifact on the AiArtifactView page, RegisterStructured on the AiStructuredDataView page, and RegisterCodeLanguage, MathRenderer and CitationRenderer on the AiMarkdownView page.

Name Type Default value Description
RegisterPart(kind, renderer) void Registers a BitAiMessagePartRenderer for a part kind (case does not matter). Every BitAiMessagePartView of a part of that kind renders it in place of its built-in view, inside a BitAiRendererBoundary - a built-in kind included; a later registration replaces an earlier one.
RegisterPart<TComponent>(kind) void Registers a component as the renderer of a part kind. The component is handed the part as a parameter named Part; declare it with the part type it expects (BitAiCustomJsonPart, BitAiCustomPart<Invoice>).
FindPart(kind) BitAiMessagePartRenderer? The renderer registered for a part kind, or null when the built-in view draws it.

BitAiMessagePartContext properties

What a custom part renderer is handed (a sealed record). The session is included so a renderer can act as well as display - a custom approval card can call back into the session without the app threading state through cascading values.

Name Type Default value Description
Part BitAiMessagePart The part to render.
Message BitAiMessage The message the part belongs to.
Conversation BitAiConversation? null The conversation, when the part is rendered inside one.
Session BitAiChatSession? null The session driving the conversation, when there is one.

BitAiMessagePartRenderer properties

The delegate a custom part renderer is: public delegate RenderFragment BitAiMessagePartRenderer(BitAiMessagePartContext context). It is called every time the view's parameters are set, so it builds markup from the context rather than caching it.

Name Type Default value Description
context BitAiMessagePartContext The part and its surroundings.
(return value) RenderFragment The markup drawn in place of the built-in view. For a failure to be contained by the boundary, the code that can throw has to run inside a component the fragment renders, as RegisterPart<TComponent> does.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.