# AiTextPartView
## Description
BitAiTextPartView renders a text part of a message as Markdown through a BitAiStreamingMarkdown, with a caret while the part is still growing. Citations anchored to character offsets of the part are woven into the text as numbered markers before it is parsed, so each superscript lands mid-sentence where the claim it supports ends - and a citation whose span ends inside code is left to the source list, where a marker cannot break the code.
## Parameters
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Classes | `BitAiTextPartViewClassStyles?` | null | Custom CSS classes for different parts of the text part view. |
| Message | `BitAiMessage` | | The message the part belongs to (required), whose citations the markers of the text resolve against. Only the citations anchored to this part (their PartId, or none) and whose span the text already reaches are woven in, as numbered markers placed where each span ends; a span that ends inside code gets none. |
| OnCitationClick | `EventCallback<BitAiCitation>` | | The callback for when the user activates an inline citation marker, with the citation it stands for. |
| Part | `BitAiTextPart` | | The part to render (required) - a BitAiTextPart or a BitAiMarkdownPart. Its text is rendered as Markdown, with a caret and its last block left open while the part is streaming; the citations are woven in again only when the part, its version, the message, the number of its citations or BitAiUiOptions.InlineCitations changed. The view does not subscribe to the part: it shows the part as it stands each time its parent renders it. |
| Styles | `BitAiTextPartViewClassStyles?` | null | Custom CSS styles for different parts of the text part view. |
| 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. |
## 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. |
## Enums
### 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. |
## Sub Classes
### BitAiTextPartViewClassStyles Properties
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Root | `string?` | null | Custom CSS classes/styles for the root element of the BitAiTextPartView. |
| Markdown | `BitAiStreamingMarkdownClassStyles?` | null | Custom CSS classes/styles for the BitAiStreamingMarkdown the BitAiTextPartView renders its text with (and, through its own Markdown, for every part of the Markdown view inside it). |
### BitAiTextPart Properties
A run of text that grows as the model writes it - the body of an assistant message. new BitAiTextPart() creates an empty part that expects more text (streaming), new BitAiTextPart(text) a finished one, and new BitAiTextPart(text, partId) a part with an identifier a transport addresses it by (null text: streaming). Appending is amortised constant time, and it is safe for one writer and any number of readers. BitAiMarkdownPart is the same part marked as Markdown, for hosts that render plain text parts verbatim; BitAiReasoningPart and BitAiCodePart derive from it too, so they share every member below.
| Name | Type | Default Value | Description |
| :--- | :--- | :------------ | :---------- |
| Text | `string` | | The text accumulated so far. |
| Length | `int` | 0 | How many characters have arrived, without materialising the string - the cheap read for hot paths such as auto-scroll checks. |
| IsEmpty | `bool` | | Whether no text has arrived yet. |
| IsStreaming | `bool` | | Whether more text is expected: true for a part created without text until Complete is called. |
| Append(string? delta) | `void` | | Appends a delta and notifies the observers of the part (an empty delta is ignored). |
| SetText(string text) | `void` | | Replaces the whole text, for edits and regenerations. |
| Complete() | `void` | | Marks the part finished, which lets the renderers do the work they deferred while it was growing (highlighting a code block, dropping the caret). |
| Kind | `string` | "text" | The part kind (BitAiMessagePartKinds.Text; "markdown" for a BitAiMarkdownPart), which picks the renderer of the part in a message. |
| PartId | `string` | | The identifier of the part, given to the constructor or generated. An anchored citation names it in its PartId. |
| Version | `int` | 0 | Increments on every change, so a renderer compares two integers rather than two texts. |
| Message | `BitAiMessage?` | null | The message the part belongs to, once it has been added to one (BitAiMessage.AddPart). |
| Changed | `event Action<BitAiMessagePart>?` | | Raised after every change, on whichever thread made it; the message the part belongs to raises its own PartChanged too. |
## Examples
\n**Basic**:
```razor
```
```csharp
private const string answerText = """
Blazor can render a component in **two places**:
- on the server, which streams UI diffs over a circuit;
- in the browser, on WebAssembly.
Pick per page with `@rendermode`.
""";
private readonly BitAiMessage answer = new(BitAiRole.Assistant);
private BitAiTextPart answerPart = default!;
protected override void OnInitialized()
{
answerPart = answer.AddPart(new BitAiTextPart(answerText));
}
```
\n**Streaming**:
```razor
@implements IAsyncDisposable
Stream an answer
Stop
@if (streamedPart is not null)
{
}
```
```csharp
private BitAiMessage? streamedMessage;
private BitAiTextPart? streamedPart;
private bool isStreaming;
private CancellationTokenSource? streamCts;
private BitAiRenderScheduler? scheduler;
private async Task StreamAsync()
{
streamCts = new CancellationTokenSource();
scheduler ??= new BitAiRenderScheduler(() => InvokeAsync(StateHasChanged));
streamedMessage = new BitAiMessage(BitAiRole.Assistant);
streamedPart = streamedMessage.AddPart(new BitAiTextPart());
isStreaming = true;
try
{
var request = new BitAiRequest([]);
// AiDemoTransports.Markdown() is the scripted IBitAiTransport of this site: use your own.
await foreach (var evt in AiDemoTransports.Markdown().StreamAsync(request, streamCts.Token))
{
if (evt is BitAiTextDeltaEvent delta)
{
streamedPart.Append(delta.Delta);
scheduler.Request();
}
}
}
catch (OperationCanceledException) { }
finally
{
streamedPart.Complete();
isStreaming = false;
await scheduler.FlushAsync();
}
}
private void StopStreaming() => streamCts?.Cancel();
public async ValueTask DisposeAsync()
{
streamCts?.Cancel();
if (scheduler is not null) await scheduler.DisposeAsync();
}
```
\n**Citations**:
```razor
```
```csharp
// answer, answerPart, citedMessage and citedPart are the ones of the Basic and Citations examples.
private readonly BitAiTextPartViewParams[] textPartViewParams =
[
new()
{
Style = "padding-inline-start: 0.75rem; border-inline-start: 3px solid var(--bit-clr-pri);",
Styles = new() { Markdown = new() { Markdown = new() { Paragraph = "font-style: italic;" } } },
},
];
```
\n**Style & Class**:
```razor
```
```csharp
private const string styleText = "A [link](https://learn.microsoft.com/aspnet/core/blazor), some `inline code` and **bold** text.";
private readonly BitAiMessage styleMessage = new(BitAiRole.Assistant);
private BitAiTextPart stylePart = default!;
protected override void OnInitialized()
{
stylePart = styleMessage.AddPart(new BitAiTextPart(styleText));
}
```
\n**RTL**:
```razor
```
```csharp
private const string rtlText = """
بلیزر یک کامپوننت را در **دو جا** رندر میکند:
- روی سرور، که تغییرات را از راه یک اتصال میفرستد؛
- در مرورگر، روی WebAssembly.
""";
private readonly BitAiMessage rtlMessage = new(BitAiRole.Assistant);
private BitAiTextPart rtlPart = default!;
protected override void OnInitialized()
{
rtlPart = rtlMessage.AddPart(new BitAiTextPart(rtlText));
}
```
Activated: @(clickedSource ?? "none yet")
```
```csharp
private const string citedText =
"Interactive Server keeps component state on the server and streams UI diffs over a SignalR circuit. " +
"Persist what has to survive a dropped circuit with `PersistentComponentState`.";
private readonly BitAiMessage citedMessage = new(BitAiRole.Assistant);
private BitAiTextPart citedPart = default!;
private string? clickedSource;
protected override void OnInitialized()
{
citedPart = citedMessage.AddPart(new BitAiTextPart(citedText));
citedMessage.AddCitation(new BitAiCitation("c1", new BitAiCitationSource("s1", "Blazor render modes")
{
Url = "https://learn.microsoft.com/aspnet/core/blazor/components/render-modes",
SiteName = "Microsoft Learn",
})
{
PartId = citedPart.PartId,
StartIndex = 0,
EndIndex = citedText.IndexOf(" Persist", StringComparison.Ordinal),
});
// This span ends inside the code span, where a marker would be rendered literally: it gets none.
citedMessage.AddCitation(new BitAiCitation("c2", new BitAiCitationSource("s2", "Prerender state persistence")
{
Url = "https://learn.microsoft.com/aspnet/core/blazor/state-management",
SiteName = "Microsoft Learn",
})
{
PartId = citedPart.PartId,
StartIndex = citedText.IndexOf("Persist", StringComparison.Ordinal),
EndIndex = citedText.IndexOf("ComponentState", StringComparison.Ordinal),
});
}
```
\n**Cascading parameters**:
```razor