Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

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 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.

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
}
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.

Shiny.Maui.Controls.SpeechAddins ships two ready-made actions:

  • SpeechToTextTool : ChatInputAction — listens via ISpeechToTextService and backfills the entry; optional AutoSend.
  • TextToSpeechBubbleTool : ChatBubbleAction — reads a message’s Body aloud via ITextToSpeechService.

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)