AI
AiStreamingMarkdown
BitAiStreamingMarkdown renders Markdown that is still being written, as a BitAiMarkdownView that follows the text while it grows. It holds one incremental parser for its lifetime, so the text already seen is parsed once however many deltas arrive - a long streamed answer costs linear work rather than quadratic. While the text streams, a caret trails it, its last block stays open, a code block is highlighted only once its fence closes and a dangling emphasis or code delimiter is hidden until its partner arrives.
Notes
Usage
Every example is live. Open its code to see exactly what produced the component running underneath.
Basic
Two render modes
Blazor renders on the server, over a circuit, or in the browser, on WebAssembly. Pick one per page with @rendermode, or let InteractiveAuto start on the server and move to the browser once it has downloaded.
Streaming
Citations
Interactive Server keeps component state on the server and streams UI diffs over a SignalR circuit . If that circuit drops, the state goes with it unless you have persisted it yourself . A number no citation has, such as [3], stays the text it is.
Markdown options
Server rendering is fast to first paint. WebAssembly works offline. Auto starts on one and moves to the other.
The short answer is that it depends on where
Cascading parameters
A styled answer
a first point
a second point
A styled answer
a first point
a second point
Style & Class
A styled answer
a first point
a second point
A styled answer
a first point
a second point
A styled answer
a first point
a second point
A styled answer
a first point
a second point
A styled answer
a first point
a second point
RTL
دو حالت رندر
بلیزر روی سرور یا در مرورگر رندر میکند و پاسخ هنوز در حال نوشته شدن است
API
Every parameter, public member, sub-class and enum this component exposes.
BitAiStreamingMarkdown parameters
| Name | Type | Default value | Description |
|---|---|---|---|
| Citations | IReadOnlyList<BitAiCitation>? | null | The citations that the inline citation markers of the text resolve against: a bracketed number such as [1] is drawn as a BitAiCitationMarker when a citation of that Number is here (and BitAiUiOptions.InlineCitations is on); one that matches none stays the literal text. |
| Classes | BitAiStreamingMarkdownClassStyles? | null | Custom CSS classes for different parts of the streaming Markdown. |
| IsStreaming | bool | false | Whether more text is expected. While it is true the streaming caret trails the text (unless BitAiUiOptions.ShowStreamingCaret is off), the last block is left open - a code block is not highlighted, a dangling emphasis or code delimiter is hidden - and turning it false settles the document. |
| OnCitationClick | EventCallback<BitAiCitation> | The callback for when the user activates an inline citation marker, with the citation it stands for. | |
| Styles | BitAiStreamingMarkdownClassStyles? | null | Custom CSS styles for different parts of the streaming Markdown. |
| Text | string? | null | The Markdown received so far. Only the text after the last block boundary already seen is parsed again when it grows; a text that is not a continuation of the previous one (an edit, a new answer) is parsed afresh. |
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. |
BitAiStreamingMarkdownClassStyles properties
| Name | Type | Default value | Description |
|---|---|---|---|
| Root | string? | null | Custom CSS classes/styles for the root element of the BitAiStreamingMarkdown. |
| Markdown | BitAiMarkdownViewClassStyles? | null | Custom CSS classes/styles for the parts of the BitAiMarkdownView the BitAiStreamingMarkdown renders its text with. |
BitAiIncrementalMarkdownParser properties
The parser a BitAiStreamingMarkdown holds for its lifetime, for an app that renders streamed Markdown with a BitAiMarkdownView of its own (ShowCaret while it streams). It keeps the blocks before the last point no later text can change - the start of a line after a blank line that begins a new top-level block - and parses only the tail again on every call, so a long answer costs linear work rather than quadratic. A text with a link reference or footnote definition, which makes any text depend on any other, is parsed whole on every call. An instance belongs to one streaming text and is not thread-safe.
| Name | Type | Default value | Description |
|---|---|---|---|
| BitAiIncrementalMarkdownParser(BitAiMarkdownOptions? options = null) | constructor | Creates a parser for the given extensions (BitAiMarkdownOptions.Default when null). Make a new one when the options change. | |
| Parse(string? source, bool isComplete = false) | BitAiMarkdownDocument | Parses the whole text received so far - not just the new part - and returns a new document whose blocks may be shared with the previous one. isComplete false leaves the last block open; a source that does not continue the previous one is parsed afresh. | |
| Reset() | void | Discards all cached state, so the next parse starts from scratch. | |
| StableLength | int | 0 | How many characters of the source have been promoted to the stable prefix that is never parsed again - for diagnostics and tests: the measure of how much work is being reused. |
| StableBlockCount | int | 0 | How many blocks are in the stable prefix. |
BitAiRenderScheduler properties
Coalesces the change notifications of a stream - hundreds of deltas a second - into a bounded number of renders, the way the AI components do with BitAiUiOptions.RenderInterval. Rendering every delta is wasted work in WebAssembly and a flood of diffs down the circuit of Blazor Server; the scheduler makes the frame rate matter rather than the throughput. While a render is in flight, further requests only mark it dirty and one more render follows, so a slow renderer falls behind in render count, never in memory. Dispose it with the page (IAsyncDisposable).
| Name | Type | Default value | Description |
|---|---|---|---|
| BitAiRenderScheduler(Func<Task> flush, TimeSpan? interval = null) | constructor | Creates a scheduler that calls flush when a render is due - in a component, () => InvokeAsync(StateHasChanged) - at most once per interval (DefaultInterval when null). | |
| DefaultInterval | TimeSpan | 50 ms | Static: one render every 50 milliseconds, twenty a second - continuous to the eye, and comfortable over a Blazor Server circuit on a poor connection. |
| Request() | void | Asks for a render. Cheap and safe to call from any thread, on every token. | |
| FlushAsync() | Task | Renders at once, bypassing the interval: call it when the stream ends, so the final state is never left waiting behind a timer. | |
| RenderCount | int | 0 | How many renders the scheduler has performed, for tests and diagnostics. |
| CoalescedCount | int | 0 | How many requests have been coalesced away. |
| DisposeAsync() | ValueTask | Stops the scheduler: a pending render is dropped, and later requests are ignored. |
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.