ChatView | Custom Actions
The old ChatEntryTool / ChatBubbleTool FAB tool tree is gone. ChatView now derives its built-in actions from permissions and ownership, and exposes two lightweight hooks for genuinely app-specific verbs.
Built-in Actions (Permission-Driven)
Section titled “Built-in Actions (Permission-Driven)”Built-in bubble and input actions are derived automatically from Info.Permissions + ownership — there is no per-tool wiring:
| Action | Appears when |
|---|---|
| React | CanReactToMessages (picker filtered to PermittedEmojis) |
| Edit | CanEditMessages and the message is the current user’s own |
| Delete | CanDeleteMessages and the message is the current user’s own |
| Copy | always available on text bubbles (client-side) |
| Attach image | CanSendImages (input bar) |
See Permissions for the full map. You don’t register these — set the right permissions on your ChatSessionInfo and they appear.
Custom Hooks (MAUI only)
Section titled “Custom Hooks (MAUI only)”For app-specific verbs, add lightweight actions to two collections:
| Property | Type | Description |
|---|---|---|
InputActions | IList<ChatInputAction> | Custom input-bar actions (surfaced via the overflow button) |
CustomBubbleActions | IList<ChatBubbleAction> | Custom bubble actions appended to the built-in permission-driven set |
public class ChatInputAction : BindableObject{ public string? Text { get; set; } public ImageSource? Icon { get; set; } public Func<ChatView, Task>? Handler { get; set; } public event EventHandler<ChatView>? Clicked; public virtual Task InvokeAsync(ChatView chatView); // overridable}
public class ChatBubbleAction : BindableObject{ public string? Text { get; set; } public ImageSource? Icon { get; set; } public Func<ChatMessage, Task>? Handler { get; set; } public event EventHandler<ChatMessage>? Clicked; public virtual Task InvokeAsync(ChatMessage message); // overridable}Defining them in a ViewModel
Section titled “Defining them in a ViewModel”public partial class ChatViewModel : ObservableObject{ public ChatViewModel(IChatSessionProvider provider) { this.Provider = provider;
this.InputActions = [ new ChatInputAction { Text = "Insert Greeting", Handler = view => { var existing = view.EntryText?.Trim(); view.EntryText = string.IsNullOrEmpty(existing) ? "Hello 👋" : $"{existing} Hello 👋"; return Task.CompletedTask; } } ];
this.CustomBubbleActions = [ new ChatBubbleAction { Text = "Translate", Handler = async msg => await Shell.Current.DisplayAlert("Translate", $"Translating: {msg.Body}", "OK") } ]; }
public IChatSessionProvider Provider { get; } public string SessionId => "demo"; public IList<ChatInputAction> InputActions { get; } public IList<ChatBubbleAction> CustomBubbleActions { get; }}<shiny:ChatView Provider="{Binding Provider}" SessionId="{Binding SessionId}" InputActions="{Binding InputActions}" CustomBubbleActions="{Binding CustomBubbleActions}" />A ChatInputAction receives the ChatView (handy with EntryText / SubmitEntry()); a ChatBubbleAction receives the ChatMessage it was invoked on. Override InvokeAsync to build a reusable, stateful action instead of using the Handler delegate.
Speech Add-ins
Section titled “Speech Add-ins”Shiny.Maui.Controls.SpeechAddins ships two ready-made actions:
SpeechToTextTool : ChatInputAction— listens viaISpeechToTextServiceand backfills the entry; optionalAutoSend.TextToSpeechBubbleTool : ChatBubbleAction— reads a message’sBodyaloud viaITextToSpeechService.
Add the package and register Shiny Speech, then add them like any other action. They are registered under the same http://shiny.net/maui/controls XAML namespace:
<shiny:ChatView Provider="{Binding Provider}" SessionId="{Binding SessionId}"> <shiny:ChatView.InputActions> <shiny:SpeechToTextTool AutoSend="False" SilenceTimeout="00:00:03" /> </shiny:ChatView.InputActions> <shiny:ChatView.CustomBubbleActions> <shiny:TextToSpeechBubbleTool /> </shiny:ChatView.CustomBubbleActions></shiny:ChatView>Or in code:
using Shiny.Maui.Controls.SpeechAddins.Chat;
this.InputActions = [ new SpeechToTextTool { AutoSend = false, SilenceTimeout = TimeSpan.FromSeconds(3) } ];this.CustomBubbleActions = [ new TextToSpeechBubbleTool() ];SpeechToTextTool | Default | |
|---|---|---|
AutoSend | false | submit the entry after recognition completes |
SilenceTimeout | 2s | silence before recognition is considered done |
Culture | null | recognition culture (e.g. "en-US") |
PreferOnDevice | false | prefer on-device recognition |
TextToSpeechBubbleTool | Default |
|---|---|
SpeechRate / Pitch / Volume | 1.0 |
Culture / VoiceName | null (system default) |
Next Steps
Section titled “Next Steps”- Permissions — what drives the built-in action set
- Message Templates — custom bubble content vs custom verbs
- Markdown & Input Bar —
EntryText/SubmitEntry()