Skip to content
# AiPromptInput **Also known as:** Composer, ChatInput ## Description BitAiPromptInput is the composer of an AI chat: a text box that grows with what is typed, where Enter sends and Shift+Enter starts a new line; files picked, dragged or pasted in, all checked against one type filter, one size limit and one count; slash commands and mentions (typed after a / or an at sign) offered from a menu the arrow keys drive; optional dictation; and one button that sends, then stops the turn while it is in flight. Hand it a BitAiChatSession, or handle OnSubmit for a pipeline of your own. ## Parameters | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Accept | `string?` | null | The file types to accept, in the syntax of the HTML accept attribute: extensions such as .pdf, MIME types such as image/png, or wildcards such as image/*. Applied to the file picker and also checked for pasted and dropped files, which the browser does not filter. When null, the types follow the capabilities of the Model (or Capabilities) when one is given - image/* for Vision, audio/* for Audio, application/pdf, .pdf and text/* for Documents - and every type is accepted otherwise. A file already staged that the accepted types no longer take - another model was picked - is taken off and named in the alert, as a file turned away (TypeNotAllowed). | | AllowAttachments | `bool` | true | Offers the attach button and accepts dropped or pasted files. A Model (or Capabilities) declaring none of Vision, Documents and Audio hides them whatever this says. | | AllowDictation | `bool` | false | Offers the dictation button (a BitAiVoiceInput) where the browser has speech recognition; it renders nothing where it has none. A Model (or Capabilities) that does not declare Audio hides it. Each finished phrase is appended to the text (cut at MaxLength), and the provisional one is shown under the text meanwhile. | | Capabilities | `BitAiModelCapabilities?` | null | The capabilities to assume when there is no Model record, or to use in place of the capabilities of the one there is. They decide whether attachments and dictation are offered and which file types the picker accepts. | | Classes | `BitAiPromptInputClassStyles?` | null | Custom CSS classes for different parts of the prompt input. | | Commands | `IReadOnlyList<BitAiSlashCommand>?` | null | The commands offered when the user types a forward slash at the start of the text, filtered by what is typed after it. Choosing one inserts its Template, or runs its Handler instead. | | DictationLanguage | `string?` | null | The dictation language, as a BCP 47 language tag such as en-US. When null, the language of the browser is used. | | DraftKey | `string?` | null | A key under which the draft text is kept in the session storage of the browser, so it survives a reload or a dropped circuit. It is restored when the composer first renders with an empty value, saved on every change and cleared on submit. Use a key that identifies the conversation, so drafts do not bleed between them. Drafts are not kept when it is null. | | Generating | `bool?` | null | Whether a turn is in flight, which turns the send button into a stop button and lets Escape stop the turn. When null, the state of the Session is used. | | HintTemplate | `RenderFragment?` | null | A hint rendered under the composer, beside the character counter of a MaxLength. | | MaxAttachmentSize | `long` | 10 * 1024 * 1024 | The largest file the composer stages, in bytes (10 MB by default, DefaultMaxAttachmentSize). Checked in the browser before a pasted or dropped file is read, so an oversized file costs nothing to turn away. The files within it are streamed rather than sent in one message, so it is not tied to the SignalR message size of Blazor Server. | | MaxAttachments | `int` | 20 | The most files that can be staged at once (DefaultMaxAttachments). A file past it is turned away as TooMany. | | MaxInlineSize | `long` | 256 * 1024 | The largest file whose bytes are held in memory, in bytes (256 KB by default, DefaultMaxInlineSize). A file within it is buffered and carries BitAiAttachment.Data and a data URL; a larger one is kept in the browser and read through BitAiAttachment.ContentFactory when something opens it, so a circuit never holds megabytes per attachment. Image thumbnails are object URLs made in the browser either way. | | MaxLength | `int?` | null | The most characters the text may hold. Enforced by the textarea and shown as a counter under the composer, which turns to the warning color as the limit gets close and is then announced politely. A text past it (set through Value) cannot be sent. When null, the text is unlimited. | | Mentions | `IReadOnlyList<BitAiMentionItem>?` | null | The items offered when the user types an at sign, filtered by what is typed after it. Choosing one inserts @ and its label. | | Model | `BitAiModel?` | null | The model the prompt is for. Its capabilities (unless Capabilities is set) decide whether attachments and dictation are offered and which file types the picker accepts. | | OnAttachmentRejected | `EventCallback<BitAiAttachmentRejection>` | | Raised for each file the composer turns away: too large, too many, not an accepted type, or unreadable. The composer already tells the user with an alert above the text (the last three files it turned away); handle this to log the rejection or to offer another route, such as uploading the file to the app's own storage. | | OnStop | `EventCallback` | | Raised when the user asks to stop the current turn: the stop button, or Escape in the text. The Session, when there is one, is stopped first. | | OnSubmit | `EventCallback<BitAiPromptSubmission>` | | Raised when the user submits, with the trimmed text and the staged attachments; the composer then empties itself. When it is not set, the submission goes to the Session. | | Placeholder | `string?` | null | The placeholder of the text, which also names it for assistive technologies unless an AriaLabel is given. When null, the PromptPlaceholder of the strings in force is used ("Send a message..."). | | PrefixTemplate | `RenderFragment?` | null | The content placed before the text, such as a model picker. | | Session | `BitAiChatSession?` | null | The session, which supplies the generation state (unless Generating is set) and receives the submissions while OnSubmit is not set. Stopping a turn stops it. | | Styles | `BitAiPromptInputClassStyles?` | null | Custom CSS styles for different parts of the prompt input. | | SuffixTemplate | `RenderFragment?` | null | The content placed between the tools (the attach and dictation buttons) and the send button. | | Value | `string` | string.Empty | The text of the composer. Use @bind-Value to keep it in step. | | ValueChanged | `EventCallback<string>` | | Raised whenever the text changes - typed, dictated, inserted from the menu, restored from a draft or emptied on submit - so the value can be bound. | | 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 | | :--- | :--- | :------------ | :---------- | | Attachments | `IReadOnlyList<BitAiAttachment>` | [] | The attachments currently staged in the composer. | | EffectiveCapabilities | `BitAiModelCapabilities?` | null | The capabilities in force: Capabilities, else those of Model, else null (everything offered). | | FocusAsync | `ValueTask` | | Puts the caret in the text of the composer. | | DefaultMaxAttachmentSize | `const long` | 10 * 1024 * 1024 | The default of MaxAttachmentSize: 10 MB. | | DefaultMaxAttachments | `const int` | 20 | The default of MaxAttachments. | | DefaultMaxInlineSize | `const long` | 256 * 1024 | The default of MaxInlineSize: 256 KB. | | 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 ### BitAiAttachmentRejectionReason Enum | Name | Value | Description | | :--- | :--- | :---------- | | TooLarge | 0 | The file is larger than MaxAttachmentSize. | | TypeNotAllowed | 1 | The file does not match Accept (or the types the capabilities of the model take) - or a staged file no longer does, once the accepted types moved under it (another model was picked). | | ReadFailed | 2 | The browser could not deliver the content, or the transfer was interrupted. | | TooMany | 3 | Staging the file would exceed MaxAttachments. | ### 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 ### BitAiPromptInputClassStyles Properties | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Root | `string?` | null | Custom CSS classes/styles for the root element of the BitAiPromptInput, the frame of the composer. | | DropOverlay | `string?` | null | Custom CSS classes/styles for the overlay of the BitAiPromptInput shown while files are dragged over it. | | Attachments | `string?` | null | Custom CSS classes/styles for the list of the staged attachments of the BitAiPromptInput. | | AttachmentChip | `BitAiAttachmentChipClassStyles?` | null | Custom CSS classes/styles for each BitAiAttachmentChip of the staged attachments of the BitAiPromptInput. | | Notice | `BitMessageClassStyles?` | null | Custom CSS classes/styles for the BitMessage of the BitAiPromptInput that names the files it turned away. | | Menu | `string?` | null | Custom CSS classes/styles for the menu of the slash commands and the mentions of the BitAiPromptInput. | | MenuItem | `string?` | null | Custom CSS classes/styles for each option of the menu of the BitAiPromptInput. | | MenuItemIcon | `string?` | null | Custom CSS classes/styles for the icon of each option of the menu of the BitAiPromptInput. | | MenuItemLabel | `string?` | null | Custom CSS classes/styles for the label of each option of the menu of the BitAiPromptInput. | | MenuItemHint | `string?` | null | Custom CSS classes/styles for the description of each option of the menu of the BitAiPromptInput. | | Body | `string?` | null | Custom CSS classes/styles for the row of the BitAiPromptInput that holds the prefix, the text and the tools. | | Prefix | `string?` | null | Custom CSS classes/styles for the container of the PrefixTemplate of the BitAiPromptInput. | | Input | `string?` | null | Custom CSS classes/styles for the textarea of the BitAiPromptInput. | | Tools | `string?` | null | Custom CSS classes/styles for the container of the tools of the BitAiPromptInput: the attach button, the dictation, the SuffixTemplate and the send button. | | AttachButton | `BitButtonClassStyles?` | null | Custom CSS classes/styles for the BitButton of the BitAiPromptInput that opens the file picker. | | VoiceInput | `BitAiVoiceInputClassStyles?` | null | Custom CSS classes/styles for the BitAiVoiceInput of the BitAiPromptInput. | | SendButton | `BitButtonClassStyles?` | null | Custom CSS classes/styles for the BitButton of the BitAiPromptInput that sends the prompt, or stops the turn in flight. | | Interim | `string?` | null | Custom CSS classes/styles for the provisional text of the dictation of the BitAiPromptInput. | | Footer | `string?` | null | Custom CSS classes/styles for the footer of the BitAiPromptInput that holds the hint and the character counter. | | Hint | `string?` | null | Custom CSS classes/styles for the hint of the BitAiPromptInput. | | Counter | `string?` | null | Custom CSS classes/styles for the character counter of the BitAiPromptInput. | | LiveRegion | `string?` | null | Custom CSS classes/styles for the visually hidden live region of the BitAiPromptInput that announces the characters left once the limit is close. | ### BitAiSlashCommand Properties A command offered when the user types a forward slash at the start of the text: new BitAiSlashCommand(name, description, icon) { Template = ... } or { Handler = ... }. A Template suits a prompt shortcut, a Handler an app action - clearing the conversation, regenerating the answer - that never reaches the model. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Name | `string` | | The command word, without the slash. It is what the text typed after the slash is matched against (case-insensitively, anywhere in it), and the menu shows it as /Name. | | Description | `string?` | null | What it does, shown beside the name. | | Icon | `string?` | null | The name of a Fluent UI icon of the built-in set (BitIconName), shown before the name. | | Template | `string?` | null | Text that replaces the slash and what was typed after it when the command is chosen; the caret lands at its end. When null (and there is no Handler), "/Name " is inserted. | | Handler | `Func<Task>?` | null | Invoked when the command is chosen, instead of inserting text: the slash and what was typed after it are removed from the text first. It runs inside the composer's own event, so a handler that changes the state of the page renders the page itself (StateHasChanged). | ### BitAiMentionItem Properties Something the user can reference by typing an at sign - a file, a person, a data source: new BitAiMentionItem(id, label, description, icon). The composer inserts the label; resolving it to content is the app's job, done by Id rather than by asking the model to guess what was meant. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Id | `string` | | The stable identifier the app resolves the mention by later. | | Label | `string` | | What the user sees, what the text typed after the at sign is matched against, and what is inserted ("@Label "). | | Description | `string?` | null | Extra detail shown beside the label. | | Icon | `string?` | null | The name of a Fluent UI icon of the built-in set (BitIconName), shown before the label. | ### BitAiPromptSubmission Properties What the composer hands OnSubmit when the user submits. The submission owns the attachments from then on: the composer no longer releases their browser streams or previews when it is discarded. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Text | `string` | | The trimmed text. It may be empty when attachments carry the request. | | Attachments | `IReadOnlyList<BitAiAttachment>` | | The files staged alongside it. | ### BitAiAttachmentRejection Properties A file the composer did not stage, and why - the same thing the alert in the composer, above the text, tells the user, for the app to log or to offer another route for, such as uploading the file to its own storage. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | FileName | `string` | | The file name. It is empty when the picker handed over more files than could be read at all. | | ContentType | `string` | | The MIME type the browser reported, which may be empty. | | Size | `long` | | The size in bytes. | | Reason | `BitAiAttachmentRejectionReason` | | Why the file was turned away. | ### BitAiComposerFile Properties A file the browser hands the composer from a paste or a drop: described first, its bytes streamed only when they are read, since one interop message carrying a whole file would exceed the SignalR message limit of Blazor Server (32 KB by default). The composer makes BitAiAttachments of these itself, so an app rarely meets the type: it is public because the script of the composer calls into it, and for an app that gets the bytes of a file in the browser its own way - new BitAiComposerFile(name, contentType, size, base64).ToAttachment() makes a BitAiAttachment of one sent inline, and ToAttachmentAsync one whose Content is an IJSStreamReference of its own JS interop - to hand to a session or a message itself. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Name | `string` | | The file name. | | ContentType | `string` | | The MIME type. | | Size | `long` | | The size in bytes. | | Data | `string?` | null | The content, base64 encoded, when it was sent inline. The browser helpers send Content instead. | | Content | `IJSStreamReference?` | null | The content as a browser stream, or null when it was sent inline in Data or withheld because the file was over the size limit. | | PreviewUrl | `string?` | null | A short object URL (URL.createObjectURL) for the preview of an image, or null for any other file. It becomes the ThumbnailUrl of the attachment: the bytes stay in the browser, only the URL crosses the circuit. | | ToAttachment() | `BitAiAttachment` | | Converts a file sent inline (Data) into an attachment with its content as a data URL. Throws an InvalidOperationException for a file that was not sent inline. | | ToAttachmentAsync(long maxAllowedSize, long maxInlineSize, CancellationToken cancellationToken) | `Task<BitAiAttachment>` | | Turns the file into an attachment: buffered, with the content as a data URL, when it is no larger than maxInlineSize (everything by default), and otherwise read on demand through BitAiAttachment.ContentFactory. A longer stream than maxAllowedSize fails the read. A buffered file releases the browser stream; a lazy one keeps it, and the caller releases it when the attachment is discarded. | ## CSS Variables Public CSS custom properties read off the component's root. Set one on `:root` to restyle every instance, on any ancestor to restyle the ones inside it, or on the `Style` of one instance to restyle it and every instance nested inside it, which inherits the value too - a parameter changes one alone, as does the real CSS property on its `Style` / `Styles` when that property does not inherit (`opacity`); an inherited one (`color`, `font-family`) reaches the nested instances that do not set it themselves. | Name | Default Value | Description | | :--- | :------------ | :---------- | | `--bit-AiPromptInput-background` | var(--bit-clr-bg-pri) | Fill of the frame. A disabled composer is filled with --bit-AiPromptInput-disabled-background instead. | | `--bit-AiPromptInput-border-color` | var(--bit-clr-brd-pri) | Color of the frame. While files are dragged over the composer it is --bit-AiPromptInput-drop-color, and a disabled one takes the disabled border color of the theme. | | `--bit-AiPromptInput-border-width` | var(--bit-shp-brd-width) | Thickness of the frame. | | `--bit-AiPromptInput-radius` | var(--bit-shp-radius-control) | Corner of the frame, which the drop overlay follows. | | `--bit-AiPromptInput-shadow` | none | Elevation of the frame, kept under the focus ring while the focus is in the composer. | | `--bit-AiPromptInput-padding` | spacing(1) | Inset of the frame. | | `--bit-AiPromptInput-gap` | spacing(1) | Room between the rows inside the frame: the staged files, the alert, the text, the provisional dictation and the footer. | | `--bit-AiPromptInput-focus-color` | the global focus ring | Color of the ring drawn around the frame while the focus is anywhere in the composer (the text has no frame of its own). | | `--bit-AiPromptInput-color` | var(--bit-clr-fg-pri) | Color of the typed text and of the options of the menu. A disabled composer writes it in --bit-AiPromptInput-disabled-color instead. | | `--bit-AiPromptInput-placeholder-color` | var(--bit-clr-fg-sec) | Color of the placeholder. | | `--bit-AiPromptInput-font-size` | var(--bit-tpg-fs-md) | Size of the typed text. | | `--bit-AiPromptInput-line-height` | 1.5 | Leading of the typed text, the unit the growing text counts its rows in (up to the MaxComposerRows of BitAiUiOptions, 12 by default). | | `--bit-AiPromptInput-drop-color` | var(--bit-clr-pri) | Color of the dashed frame and of the text of the overlay while files are dragged over the composer. | | `--bit-AiPromptInput-drop-background` | var(--bit-clr-bg-pri) at 86% | Fill of the overlay shown while files are dragged over the composer. | | `--bit-AiPromptInput-menu-background` | var(--bit-clr-bg-pri) | Fill of the menu of commands and mentions. | | `--bit-AiPromptInput-menu-border-color` | var(--bit-clr-brd-sec) | Color of the rule around the menu. | | `--bit-AiPromptInput-menu-shadow` | var(--bit-shd-popup) | Elevation of the menu. | | `--bit-AiPromptInput-menu-max-height` | var(--bit-siz-popup-max-height) | Height past which the menu scrolls. | | `--bit-AiPromptInput-item-hover-background` | var(--bit-clr-bg-pri-hover) | Fill of the option under the pointer, and of the one the arrow keys have reached. | | `--bit-AiPromptInput-hint-color` | var(--bit-clr-fg-sec) | Color of the hint, the character counter, the descriptions of the options and the provisional dictation. | | `--bit-AiPromptInput-near-limit-color` | var(--bit-clr-wrn) | Color of the counter of a MaxLength once the text is close to it. | | `--bit-AiPromptInput-over-limit-color` | var(--bit-clr-err) | Color of the counter of a MaxLength once the text is past it. | | `--bit-AiPromptInput-disabled-color` | var(--bit-clr-fg-dis) | Color of the text and of the placeholder of a composer whose Disabled is true. | | `--bit-AiPromptInput-disabled-background` | var(--bit-clr-bg-dis) | Fill of the frame of a composer whose Disabled is true. | ## Examples \n**Basic**: ```razor
Sent: @basicSubmission
``` ```csharp private string basicSubmission = "nothing yet"; private void HandleBasicSubmit(BitAiPromptSubmission submission) { basicSubmission = submission.Text; } ``` \n**Value & MaxLength**: ```razor
Value: @boundValue
Sent: @valueSubmission
Fill in a suggestion Focus the composer ``` ```csharp private BitAiPromptInput? valueComposer; private string boundValue = string.Empty; private string valueSubmission = "nothing yet"; private void HandleValueSubmit(BitAiPromptSubmission submission) { valueSubmission = submission.Text; } private void FillSuggestion() { boundValue = "Which of my invoices are still unpaid?"; } private async Task FocusComposerAsync() { if (valueComposer is null) return; await valueComposer.FocusAsync(); } ``` \n**Session**: ```razor @implements IAsyncDisposable
``` ```csharp // Any IBitAiTransport will do; this one streams a long Markdown answer, slow enough to stop halfway. // A session made by the BitAiChatSessionFactory service picks up the registered transport and tools instead. private readonly BitAiChatSession session = new(AiDemoTransports.Markdown()); public async ValueTask DisposeAsync() { await session.DisposeAsync(); } ``` \n**Generating & OnStop**: ```razor
@generationStatus
``` ```csharp private bool isGenerating; private CancellationTokenSource? generationCts; private string generationStatus = "Send something to start a turn of your own; it takes five seconds unless you stop it."; private async Task StartGenerationAsync(BitAiPromptSubmission submission) { generationCts?.Dispose(); generationCts = new(); isGenerating = true; generationStatus = $"Working on \"{submission.Text}\"..."; StateHasChanged(); try { await Task.Delay(5000, generationCts.Token); generationStatus = $"Done with \"{submission.Text}\"."; } catch (OperationCanceledException) { generationStatus = $"Stopped \"{submission.Text}\"."; } finally { isGenerating = false; } } private void StopGeneration() { generationCts?.Cancel(); } ``` \n**Attachments**: ```razor
Sent: @attachmentsSubmission
@foreach (var attachment in sentAttachments) {
@(attachment.HasInlineData ? "held in memory (Data)" : "kept in the browser, read on demand (ContentFactory)")
}
``` ```csharp private const long InlineSize = 64 * 1024; private string attachmentsSubmission = "nothing yet"; private IReadOnlyList sentAttachments = []; private void HandleAttachmentsSubmit(BitAiPromptSubmission submission) { attachmentsSubmission = $"\"{submission.Text}\" with {submission.Attachments.Count} file(s)"; sentAttachments = submission.Attachments; } ``` \n**Limits & rejections**: ```razor
Sent: @limitsSubmission
Turned away:
@if (rejections.Count == 0) {
Nothing yet. Try a file over 1 MB, a fourth file, or a .txt file.
} else {
    @foreach (var rejection in rejections) {
  • @rejection.FileName (@rejection.Size bytes): @rejection.Reason
  • }
} ``` ```csharp private const long MaxSize = 1024 * 1024; private string limitsSubmission = "nothing yet"; private readonly List rejections = []; private void HandleLimitsSubmit(BitAiPromptSubmission submission) { limitsSubmission = $"\"{submission.Text}\" with {submission.Attachments.Count} file(s)"; } // The composer already tells the user; this is where an app would log or count it. private void HandleRejected(BitAiAttachmentRejection rejection) { rejections.Insert(0, rejection); if (rejections.Count > 5) rejections.RemoveAt(rejections.Count - 1); } ``` \n**Dictation**: ```razor
Sent: @dictationSubmission
``` ```csharp private string dictationSubmission = "nothing yet"; private void HandleDictationSubmit(BitAiPromptSubmission submission) { dictationSubmission = submission.Text; } ``` \n**Model & capabilities**: ```razor
Sent: @capabilitiesSubmission
``` ```csharp // The catalog AddBitBlazorUIAIServices registers with ai.AddModels(...). [Inject] private IBitAiModelCatalog modelCatalog { get; set; } = default!; private string? capabilityModelId = "demo-model-1"; private string capabilitiesSubmission = "nothing yet"; private BitAiModel? FindModel(string? modelId) => modelId is null ? null : modelCatalog.FindModel(modelId); private void HandleCapabilitiesSubmit(BitAiPromptSubmission submission) { capabilitiesSubmission = $"\"{submission.Text}\" with {submission.Attachments.Count} file(s)"; } ``` \n**Slash commands & mentions**: ```razor
    @foreach (var prompt in sentPrompts) {
  • @prompt
  • }
``` ```csharp private IReadOnlyList commands = []; private readonly List sentPrompts = []; private readonly IReadOnlyList mentions = [ new("file-roadmap", "roadmap.md", "Product roadmap, updated Monday", BitIconName.Page), new("file-pricing", "pricing.xlsx", "Pricing model, three tiers", BitIconName.Page), new("user-priya", "priya", "Priya Raman, product lead", BitIconName.Contact), new("user-tomas", "tomas", "Tomás Silva, engineering", BitIconName.Contact), ]; protected override void OnInitialized() { commands = [ new("summarize", "Summarize the conversation so far", BitIconName.BulletedList) { Template = "Summarize the conversation so far in three bullet points.", }, new("explain", "Explain something for a newcomer", BitIconName.Lightbulb) { Template = "Explain this as if I were new to the codebase: ", }, new("translate", "Translate the next text", BitIconName.Translate) { Template = "Translate into French: ", }, new("clear", "Clear the list of sent prompts", BitIconName.Delete) { Handler = () => { sentPrompts.Clear(); StateHasChanged(); return Task.CompletedTask; }, }, ]; base.OnInitialized(); } private void HandleCommandsSubmit(BitAiPromptSubmission submission) { sentPrompts.Add(submission.Text); } ``` \n**Drafts**: ```razor
Sent: @draftSubmission
``` ```csharp private string draftSubmission = "nothing yet"; private void HandleDraftSubmit(BitAiPromptSubmission submission) { draftSubmission = submission.Text; } ``` \n**Templates**: ```razor Enter to send, Shift+Enter for a new line. Answers can be wrong - check what matters.
Sent: @templatesSubmission
``` ```csharp [Inject] private IBitAiModelCatalog modelCatalog { get; set; } = default!; private string templatesValue = string.Empty; private string? templatesModelId = "demo-model-1"; private string templatesSubmission = "nothing yet"; private BitAiModel? FindModel(string? modelId) => modelId is null ? null : modelCatalog.FindModel(modelId); private void SuggestPrompt() { templatesValue = "Draft a friendly reminder for an overdue invoice."; } private void HandleTemplatesSubmit(BitAiPromptSubmission submission) { templatesSubmission = submission.Text; } ``` \n**Cascading parameters**: ```razor
Sent: @cascadeSubmission
``` ```csharp private string cascadeSubmission = "nothing yet"; private readonly BitAiPromptInputParams[] promptInputParams = [ new() { Placeholder = "Ask the team assistant...", Accept = "image/*", MaxAttachments = 2, MaxLength = 200, } ]; private void HandleCascadeSubmit(BitAiPromptSubmission submission) { cascadeSubmission = submission.Text; } ``` \n**Disabled**: ```razor ``` \n**Style & Class**: ```razor
Sent: @styleSubmission
``` ```csharp private const string cssVariablesStyle = "--bit-AiPromptInput-radius:1.5rem;" + "--bit-AiPromptInput-padding:0.5rem 0.75rem;" + "--bit-AiPromptInput-border-color:transparent;" + "--bit-AiPromptInput-background:var(--bit-clr-bg-sec);" + "--bit-AiPromptInput-shadow:0 0.25rem 1rem rgb(0 0 0 / 0.12);"; private string styleSubmission = "nothing yet"; private void HandleStyleSubmit(BitAiPromptSubmission submission) { styleSubmission = submission.Text; } ``` \n**RTL**: ```razor برای ارسال Enter و برای خط جدید Shift+Enter را بزنید.
Sent: @rtlSubmission
``` ```csharp private string rtlSubmission = "nothing yet"; private void HandleRtlSubmit(BitAiPromptSubmission submission) { rtlSubmission = submission.Text; } ```