diff --git a/blazor/smart-ai-solutions/ai-samples/kanban/sentiment-analysis.md b/blazor/smart-ai-solutions/ai-samples/kanban/sentiment-analysis.md index d66c1982d1..e35814a715 100644 --- a/blazor/smart-ai-solutions/ai-samples/kanban/sentiment-analysis.md +++ b/blazor/smart-ai-solutions/ai-samples/kanban/sentiment-analysis.md @@ -228,9 +228,21 @@ await builder.Build().RunAsync(); {% endhighlight %} {% endtabs %} -## Razor Component +## AI-powered Sentiment Analysis in Kanban -This section demonstrates how to implement sentiment analysis in the Syncfusion Blazor Kanban component using AI. The AI Assistant evaluates the emotional tone of each task description and displays a corresponding emoji (😊, 😐, 😞) to help teams quickly assess the mood or urgency of tasks. This can be especially useful in agile workflows where emotional context can influence task priority and team communication. +The AI-powered sentiment analysis feature in Syncfusion Blazor Kanban evaluates customer feedback for delivered tasks and displays emojis (😊, 😐, 😞) to indicate positive, neutral, or negative sentiment. This helps teams quickly assess satisfaction levels and prioritize follow-up actions, improving customer experience in service workflows. + +### UI Structure and User Interaction (`Home.razor`) + +The Razor page includes a Kanban board for managing pizza orders across columns like Menu, Order, Ready to Serve, and Delivered. A progress button triggers sentiment analysis, while custom card templates display task details such as title, description, and delivery date. After analysis, an emoji appears on delivered tasks to represent sentiment. A dialog template supports editing fields like category, size, and feedback, with conditional rendering based on task type. + +### AI Sentiment Analysis Logic (`Home.razor.cs`) + +The `GetScore` method serializes Kanban data to JSON and sends it to the AI service with a prompt requesting a `SentimentScore` (1–5) based on feedback. The AI response is cleaned and deserialized into the data model, updating each task’s score and assigning emojis: 😒 for 1–2, 😐 for 3, and πŸ˜€ for 4–5. State flags like `ShowScore` control rendering, while progress events (`Begin`, `End`) update button text during processing. + +### Data Binding and Error Handling + +The `SfKanban` binds to the `Pizza` list using `Category` as the `KeyField`, with dynamic columns from `columnData`. Card templates conditionally render sentiment emojis for delivered tasks. If AI analysis fails, the spinner state prevents premature interactions, and `StateHasChanged()` refreshes the UI after completion. (`Home.razor`) @@ -520,10 +532,6 @@ If the AI service fails to return a valid response, the Kanban will display an e - **Network Issues**: Check connectivity to the AI service endpoint, especially for self-hosted Ollama instances. - **Large Prompts**: Processing large text inputs may cause timeouts. Consider reducing the prompt size or optimizing the request for efficiency. -## Performance Considerations - -When handling large text content, ensure the Ollama server has sufficient resources (CPU/GPU) to process requests efficiently. For long-form content or batch operations, consider splitting the input into smaller segments to avoid performance bottlenecks. Test the application with your specific use case to determine optimal performance. - ## Sample Code A complete working example is available in the [Syncfusion Blazor AI Samples GitHub repository](https://github.com/syncfusion/smart-ai-samples). diff --git a/blazor/smart-ai-solutions/ai-samples/kanban/smart-task-suggestion.md b/blazor/smart-ai-solutions/ai-samples/kanban/smart-task-suggestion.md index a8034ddae5..baedc607b9 100644 --- a/blazor/smart-ai-solutions/ai-samples/kanban/smart-task-suggestion.md +++ b/blazor/smart-ai-solutions/ai-samples/kanban/smart-task-suggestion.md @@ -228,9 +228,27 @@ await builder.Build().RunAsync(); {% endhighlight %} {% endtabs %} -## Razor Component +## AI-powered Kanban Smart Card Generation -This section demonstrates how to implement the Syncfusion Blazor Kanban component with AI-powered task generation. The AI Assistant analyzes the provided project description and automatically suggests relevant tasks, which are then displayed in the Kanban board. +The AI-powered Kanban feature integrates Syncfusion Blazor Kanban with an AI service to automatically create structured task cards from user input. It combines a simple UI for project details with backend logic that generates tasks and updates the Kanban board dynamically. + +### UI Structure and User Interaction (`Home.razor`) + +The Razor page includes an input form for project details and a Kanban board for visualizing tasks. Users enter a project description and task count, then click Generate Tasks to start AI processing. A modal dialog ensures clean input for new projects, while conditional rendering switches between input and Kanban views. Cards display essential details like title, description, and story points with drag-and-drop support. + +### AI Task Generation Logic (`Home.razor.cs`) + +The `GenerateProjectTasks` method builds a prompt for the AI service, requesting tasks in strict JSON format with fields like Id, Title, Status, and Due Date. The response is cleaned and deserialized into `SmartSuggestionDataModel` objects, which are added to the Kanban data source. Progress button events provide feedback during generation, and default values like `Status="Open"` and `Color="#000000"` are applied to new tasks. + +### Data Binding and Kanban Configuration + +The `SfKanban` component binds to the `smartSuggestion` list, using `Status` as the `KeyField` to organize cards into columns (To Do, In Progress, Review, Done). +Card templates are customized via `KanbanCardSettings` to show essential task details with visual clarity. +A fallback `SfGrid` view allows tabular task management with inline editing, while a toggle button switches between Grid and Kanban layouts. + +### Error Handling and User Feedback + +If AI generation fails, a toast notification displays an error message without disrupting the workflow. Input validation ensures both project details and task count are provided before submission. State flags manage UI transitions and prevent issues during asynchronous operations. (`Home.razor`) @@ -573,10 +591,6 @@ If the AI service fails to return a valid response, the Kanban will display an e - **Network Issues**: Check connectivity to the AI service endpoint, especially for self-hosted Ollama instances. - **Large Prompts**: Processing large text inputs may cause timeouts. Consider reducing the prompt size or optimizing the request for efficiency. -## Performance Considerations - -When handling large text content, ensure the Ollama server has sufficient resources (CPU/GPU) to process requests efficiently. For long-form content or batch operations, consider splitting the input into smaller segments to avoid performance bottlenecks. Test the application with your specific use case to determine optimal performance. - ## Sample Code A complete working example is available in the [Syncfusion Blazor AI Samples GitHub repository](https://github.com/syncfusion/smart-ai-samples). diff --git a/blazor/smart-ai-solutions/ai-samples/rich-text-editor/writting-assistance.md b/blazor/smart-ai-solutions/ai-samples/rich-text-editor/writting-assistance.md index 3074d17360..4a5f3adbf3 100644 --- a/blazor/smart-ai-solutions/ai-samples/rich-text-editor/writting-assistance.md +++ b/blazor/smart-ai-solutions/ai-samples/rich-text-editor/writting-assistance.md @@ -228,432 +228,376 @@ await builder.Build().RunAsync(); {% endhighlight %} {% endtabs %} -## Razor Component +## AI-powered Rich Text Editor in Blazor -This section implements the Syncfusion Blazor Rich Text Editor with AI-powered content assistance features such as rephrase, correct grammar, summarize, elaborate, and translate. +This guide explains how to integrate a full-featured AI writing assistant into the Blazor Rich Text Editor using Azure OpenAI (via Semantic Kernel). The implementation supports **Rephrase**, **Correct Grammar**, **Summarize**, **Elaborate**, and **Translate** with live preview, dynamic tone/language controls, skeleton loading, and safe, undoable content replacement. -(`Home.razor`) +### How the Custom Toolbar is Rendered -```csharp -@inject AzureAIService semanticKernelAI -@inject IJSRuntime JSRuntime -@using AISamples.Components.Service -@using Syncfusion.Blazor.RichTextEditor -@using Syncfusion.Blazor.SplitButtons -@using Syncfusion.Blazor.Buttons -@using Syncfusion.Blazor.Popups -@using Syncfusion.Blazor.Notifications -@using Syncfusion.Blazor.DropDowns - -
- - - - - - - - - - - - - -
-
- - -
AI Assistant
- -
-
-
- - +The toolbar uses [RichTextEditorCustomToolbarItems](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.RichTextEditor.RichTextEditorCustomToolbarItems.html) to inject a custom item named AI. + +Inside the template, an `SfDropDownButton` is rendered with: + +- **Icon**: AI icon (e-icons e-ai-chat) +- **Dropdown Items**: Rephrase, Grammar, Summarize, Elaborate, Translate +- **Event Binding**: ItemSelected="AIQuerySelectedMenu" triggers the AI workflow when an option is selected. + +An additional `SfButton` for **Rephrase** is provided for quick access. + +{% tabs %} +{% highlight razor %} + + + + + + + + + + + + +{% endhighlight %} +{% endtabs %} + +### AI Assistant Dialog – Layout and Dynamic Controls + +The AI Assistant dialog provides a responsive, user-friendly interface for interacting with AI-generated content. It is designed as a modal dialog that overlays the editor and ensures proper z-index stacking for seamless integration. + +#### Dialog Structure + +The dialog is divided into two main rows: + +**Row 1:** Action Selection and Dynamic Controls + +- **Action Dropdown:** Allows users to switch between AI actions (Rephrase, Grammar, Summarize, Elaborate, Translate). +- **Dynamic Controls:** + - **Tone Chips** (Standard, Fluent, Professional) appear when the selected action is Rephrase. + - **Language Dropdown** (15+ languages) appears when the selected action is Translate. + +**Row 2:** Original Text vs AI Result + +- **Left Panel:** Displays the original selected text in a read-only Rich Text Editor. +- **Right Panel:** Shows AI-generated content in another Rich Text Editor. +- **Loading State:** While AI processes the request, a progressive skeleton loader animates from 100% to 10% width for visual feedback. +- **Fallback State:** If no result is returned, a β€œNo results found” message with a warning icon is displayed. + +**Footer Actions** + +- **Regenerate:** Requests a new AI output for the same prompt. +- **Copy:** Copies the AI-generated content to the clipboard. +- **Replace:** Inserts the AI-generated content back into the original editor with undo support. + +{% tabs %} +{% highlight %} + + + + +
+ AI Assistant +
+ + + +
+
+
+ + + + + + +
+
+ +
+ @if (this.enableRephraseChips) + { + + + + + + + + + + } + else if (this.enableLanguageList) + { +
+ Target Language +
+
+ + - +
-
-
- @if (this.enableRephraseChips) - { - - - - - - - - - } - else if (this.enableLanguageList) - { -
- Target Language -
-
- - - - -
- } -
+ }
-
-
-
- - - -
+
+ +
+
+
+ + + +
- @if (!isContentGenerating) +
+ + @if (!isContentGenerating) + { + @if (noResultsFound) { - @if (noResultsFound) - { -
-
-
- -
No results found
-
-
-
- } - else { -
-
- - - + +
+
+
+ +
No results found
- } - } else { +
+ } + else {
-
-
-
-
-
-
- -
+ + + +
} -
- - -
-
+ } else { +
- Regenerate +
+ +
+
+
+
+
+ +
-
- @if (!string.IsNullOrEmpty(sentiment)) { - - } - Copy - Replace + } +
+ + + +
+
+
+ + Regenerate
- - - - -
- - - +
+ @if (!string.IsNullOrEmpty(sentiment)) { + + } + + Copy + Replace +
+
+ + + + + +{% endhighlight %} +{% endtabs %} + +### Opening the AI Dialog and Preserving Selection + +The `DialogueOpen()` method opens the AI dialog when an action is selected and prepares the editor for processing. It first uses `GetSelectedHtmlAsync()` to capture the highlighted content with its formatting. If no text is selected, a toast message alerts the user to select content before proceeding. + +When valid text is found, the method makes the dialog visible, stores the selected HTML for AI processing, and calls `SaveSelectionAsync()` to preserve the cursor position so the AI output can replace only the selected text later. It then refreshes the dialog’s editor and updates AI suggestions based on the chosen action. + + +```csharp +private async Task Rephrase() +{ + // Directly open dialog for Rephrase action + await DialogueOpen("Rephrase"); +} +private async Task AIQuerySelectedMenu(MenuEventArgs args) +{ + // Open dialog based on selected dropdown item + await DialogueOpen(args.Item.Text); +} +private async Task DialogueOpen(string selectedQuery) +{ + // Get the selected text from the RichTextEditor + var selectionText = await rteObj.GetSelectedHtmlAsync(); + + if (!string.IsNullOrEmpty(selectionText)) + { + // Make the dialog visible + dialogVisible = true; + + // Identify the selected query and map it to its ID + + dropVal = QueryList.FirstOrDefault(q => q.Text.Equals(selectedQuery, StringComparison.OrdinalIgnoreCase))?.ID; + + // Store the selected text for AI processing + promptQuery = selectionText; + + // Save the current selection so AI output can be inserted later + await this.rteObj.SaveSelectionAsync(); + + // Refresh the left-side RichTextEditor in the dialog to show original text + await this.leftRteChildObj.RefreshUIAsync(); + + // Update AI suggestions based on the selected query + await UpdateAISuggestionsData(selectedQuery); + } + else + { + // Show a toast notification if no text is selected + await this.ToastObj.ShowAsync(new ToastModel { ContentTemplate = @GetTemplate(true), ShowCloseButton = true, Timeout = 0 }); + } +} ``` -`Home.razor.cs` +### Building and Sending the AI + +The AI prompt is built in two parts: **user intent** and **system instruction**. + +1. **User Intent (subQuery)** - Defines what the AI should do, such as summarizing, rephrasing with a specific tone, or translating into a target language. This makes the prompt context-aware and action-specific. + +2. **System Instruction (HTML preservation)** - Enforces formatting rules, for example: β€œRetain existing HTML structure. Modify content only.” This ensures that the AI preserves elements like bold text, lists, and links while updating the content. + +Both parts are combined and sent to `semanticKernelAI.GetCompletionAsync()` via Semantic Kernel, resulting in an AI-generated output that is accurate and safe to insert back into the editor. ```csharp -using Markdig; -using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; -using Syncfusion.Blazor.DropDowns; -using Syncfusion.Blazor.Inputs; -using Syncfusion.Blazor.Notifications; -using Syncfusion.Blazor.RichTextEditor; -using Syncfusion.Blazor.SplitButtons; - -namespace AISamples.Components.Pages + +private async Task UpdateAISuggestionsData(string selectedQuery) { - public partial class Home + enableRephraseChips = false; + enableLanguageList = false; + isSentimentCheck = false; + switch (selectedQuery) { - SfToast ToastObj; - private string ToastTarget { get; set; } = "#scroll-restricted"; - SfRichTextEditor rteObj; - SfRichTextEditor leftRteChildObj; - SfRichTextEditor rightRteChildObj; - private string Value { get; set; } = "
Integrate AI with the Editor

Integrate the AI assistant into the rich text editor by capturing the content from the editor, sending it to the AI service, and displaying the results or suggestions back in the editor.

Summarize

This function condenses the selected content into a brief summary, capturing the main points succinctly.

Elaborate

This function expands the selected content, adding additional details and context.

Rephrase

This function rewrites the selected content to convey the same meaning using different words or structures. It also enables rephrase options and disables language selection.

Correct Grammar

This function reviews and corrects the grammar of the selected content, ensuring it adheres to standard grammatical rules.

Translate

This function translates the selected content into the specified language, enabling language selection and disabling rephrase options.

"; - private bool dialogVisible { get; set; } - private bool enabelAIAssitantButton { get; set; } = false; - private bool enabelRegenerateContentButton { get; set; } = false; - private bool enabelContentButton { get; set; } = true; - private string promptQuery = string.Empty; - private string subQuery = string.Empty; - private string[] chipValue = new[] { "Standard" }; - private string translatelanguage = "EN"; - private string dropVal { get; set; } = "Rephrase"; - private bool enableRephraseChips { get; set; } = true; - private bool enableLanguageList { get; set; } = false; - private bool noResultsFound { get; set; } = false; - public bool isContentGenerating { get; set; } = true; - private string AIResult { get; set; } = string.Empty; - private bool isSentimentCheck { get; set; } = false; - private MarkdownPipeline pipeline { get; set; } = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); - private string sentiment = ""; - private string apiResultData = ""; - private string ButtonClass = "e-tbar-btn"; - private void UpdateStatus(Syncfusion.Blazor.RichTextEditor.ChangeEventArgs args) - { - Value = args.Value; - enabelAIAssitantButton = string.IsNullOrWhiteSpace(Value); - } - private void OnActionCompleteHandler(Syncfusion.Blazor.RichTextEditor.ActionCompleteEventArgs args) - { - if (args.RequestType == "SourceCode") - { - this.ButtonClass = "e-tbar-btn e-overlay"; - } - if (args.RequestType == "Preview") - { - this.ButtonClass = "e-tbar-btn"; - } - } - private void UpdateTextAreaStatus(InputEventArgs args) - { - Value = args.Value; - enabelRegenerateContentButton = string.IsNullOrWhiteSpace(Value); - } - private async Task AIQuerySelectedMenu(MenuEventArgs args) - { - await DialogueOpen(args.Item.Text); - } - private async Task Rephrase() - { - await DialogueOpen("Rephrase"); - } - private async Task DialogueOpen(string selectedQuery) - { - var selectionText = await rteObj.GetSelectedHtmlAsync(); - if (!string.IsNullOrEmpty(selectionText)) - { - dialogVisible = true; - dropVal = QueryList.FirstOrDefault(q => q.Text.Equals(selectedQuery, StringComparison.OrdinalIgnoreCase))?.ID; - promptQuery = selectionText; - await this.rteObj.SaveSelectionAsync(); - await this.leftRteChildObj.RefreshUIAsync(); - await UpdateAISuggestionsData(selectedQuery); - } - else - { - await this.ToastObj.ShowAsync(new ToastModel { ContentTemplate = @GetTemplate(true), ShowCloseButton = true, Timeout = 0 }); - } - } - private async Task SelectedChipsChanged(Syncfusion.Blazor.Buttons.SelectionChangedEventArgs args) - { - if (chipValue.Length == 0 && args != null && args.RemovedItems.Length > 0) - { - chipValue = new [] { args.RemovedItems[0] }; - } - await UpdateAISuggestionsData("Rephrase"); - } - private async Task AITranslateDropdownList(ChangeEventArgs args) - { - await UpdateAISuggestionsData("Translate"); - } - private async Task AIQuerySelectedDropdownList(ChangeEventArgs args) - { - if (!string.IsNullOrEmpty(dropVal)) - { - chipValue = new[] { "Standard" }; - translatelanguage = "EN"; - var selectedQuery = QueryList.FirstOrDefault(q => q.ID.Equals(dropVal, StringComparison.OrdinalIgnoreCase))?.Text; - await UpdateAISuggestionsData(selectedQuery); - } - } - private async Task UpdateAISuggestionsData(string selectedQuery) - { - enableRephraseChips = false; - enableLanguageList = false; - isSentimentCheck = false; - switch (selectedQuery) - { - case "Summarize": - subQuery = "Briefly summarize the following text in a short and concise manner."; - break; - case "Elaborate": - subQuery = "Elaborate/Expand on the following text, providing more detail and context."; - break; - case "Rephrase": - enableRephraseChips = true; - enableLanguageList = false; - subQuery = $"Rephrase the following text in a {chipValue[0]} [tone/style], ensuring clarity and maintaining the original meaning."; - break; - case "Correct Grammar": - subQuery = "Correct any grammatical errors in the following text, ensuring it is clear and well-structured."; - break; - case "Translate": - enableLanguageList = true; - enableRephraseChips = false; - subQuery = $"Translate the following text into {translatelanguage}, preserving the original meaning and tone."; - break; - } - UpdateAISuggestionsData(); - } - private async Task RegenerateContent() - { - UpdateAISuggestionsData(); - } - private async Task ReplaceContent() - { - ExecuteCommandOption executeCommandOption = new ExecuteCommandOption(); - executeCommandOption.Undo = true; - await this.rteObj.RestoreSelectionAsync(); - await this.rteObj.ExecuteCommandAsync(CommandName.InsertHTML, this.apiResultData, executeCommandOption); - await CloseDialog(); - } - private async Task CopyContent() - { - await JSRuntime.InvokeVoidAsync("copyToClipboard", Markdig.Markdown.ToPlainText(AIResult, pipeline)); - } - private async Task CloseDialog() - { - dialogVisible = false; - promptQuery = string.Empty; - AIResult = string.Empty; - chipValue = new[] { "Standard" }; - dropVal = "Query1"; + case "Summarize": + subQuery = "Briefly summarize the following text in a short and concise manner."; + break; + case "Elaborate": + subQuery = "Elaborate/Expand on the following text, providing more detail and context."; + break; + case "Rephrase": enableRephraseChips = true; enableLanguageList = false; - sentiment = ""; - apiResultData = ""; - } - private async Task UpdateAISuggestionsData() + subQuery = $"Rephrase the following text in a {chipValue[0]} [tone/style], ensuring clarity and maintaining the original meaning."; + break; + case "Correct Grammar": + subQuery = "Correct any grammatical errors in the following text, ensuring it is clear and well-structured."; + break; + case "Translate": + enableLanguageList = true; + enableRephraseChips = false; + subQuery = $"Translate the following text into {translatelanguage}, preserving the original meaning and tone."; + break; + } + UpdateAISuggestionsData(); +} + +private async Task UpdateAISuggestionsData() +{ + try + { + if (!string.IsNullOrEmpty(promptQuery)) { - try + enabelRegenerateContentButton = isContentGenerating = enabelContentButton = true; + string systemPrompt = subQuery.Contains("emoji followed by the sentiment in the format") ? "You are a helpful assistant. Please respond in string format." : "NOTE:Please retain the existing HTML structure and modify the content only. Ensure that the response adheres to the specified formatting."; + apiResultData = await semanticKernelAI.GetCompletionAsync(promptQuery, false, false, (subQuery + systemPrompt)); + if (apiResultData != null) { - if (!string.IsNullOrEmpty(promptQuery)) - { - enabelRegenerateContentButton = isContentGenerating = enabelContentButton = true; - string systemPrompt = subQuery.Contains("emoji followed by the sentiment in the format") ? "You are a helpful assistant. Please respond in string format." : "NOTE:Please retain the existing HTML structure and modify the content only. Ensure that the response adheres to the specified formatting."; - apiResultData = await semanticKernelAI.GetCompletionAsync(promptQuery, false, false, (subQuery + systemPrompt)); - if (apiResultData != null) - { - isContentGenerating = false; - sentiment = isSentimentCheck ? apiResultData.Replace("\"", "").Replace("'", "") : ""; - AIResult = isSentimentCheck ? promptQuery : apiResultData; - noResultsFound = string.IsNullOrEmpty(AIResult) || string.IsNullOrEmpty(promptQuery); - enabelRegenerateContentButton = enabelContentButton = noResultsFound; - await InvokeAsync(StateHasChanged); - } - else - { - isContentGenerating = false; - await InvokeAsync(StateHasChanged); - } - } - } - catch - { - await this.ToastObj.ShowAsync(new ToastModel { ContentTemplate = @GetTemplate(), ShowCloseButton = true, Timeout = 0 }); - } + isContentGenerating = false; + sentiment = isSentimentCheck ? apiResultData.Replace("\"", "").Replace("'", "") : ""; + AIResult = isSentimentCheck ? promptQuery : apiResultData; + noResultsFound = string.IsNullOrEmpty(AIResult) || string.IsNullOrEmpty(promptQuery); + enabelRegenerateContentButton = enabelContentButton = noResultsFound; + await InvokeAsync(StateHasChanged); } - private RenderFragment GetTemplate(bool hasTextSelection = false) => builder => - { - builder.OpenElement(0, "div"); - builder.AddContent(1, hasTextSelection ? "Please select the content to perform the AI operation." : "An error occurred during the AI process, Please try again."); - builder.CloseElement(); - }; - public class SubQuery - { - public string ID { get; set; } - public string Text { get; set; } - } - public class Languages + else { - public string ID { get; set; } - public string Text { get; set; } + isContentGenerating = false; + await InvokeAsync(StateHasChanged); } - public List QueryList = new List - { - new SubQuery { ID = "Rephrase", Text = "Rephrase" }, - new SubQuery { ID = "Grammar", Text = "Correct Grammar" }, - new SubQuery { ID = "Summarize", Text = "Summarize" }, - new SubQuery { ID = "Elaborate", Text = "Elaborate" }, - new SubQuery { ID = "Translate", Text = "Translate" } - }; - public List LanguageList = new List - { - new Languages { ID = "EN", Text = "English" }, - new Languages { ID = "ZH", Text = "Chinese (Simplified)" }, - new Languages { ID = "ZHT", Text = "Chinese (Traditional)" }, - new Languages { ID = "ES", Text = "Spanish" }, - new Languages { ID = "HI", Text = "Hindi" }, - new Languages { ID = "AR", Text = "Arabic" }, - new Languages { ID = "BN", Text = "Bengali" }, - new Languages { ID = "PT", Text = "Portuguese" }, - new Languages { ID = "RU", Text = "Russian" }, - new Languages { ID = "JA", Text = "Japanese" }, - new Languages { ID = "DE", Text = "German" }, - new Languages { ID = "KO", Text = "Korean" }, - new Languages { ID = "FR", Text = "French" }, - new Languages { ID = "IT", Text = "Italian" }, - new Languages { ID = "TR", Text = "Turkish" } - }; - private List Tools = new List() - { - new ToolbarItemModel() { Name = "AIAssistant", TooltipText = "AI Assistant" }, - new ToolbarItemModel() { Name = "Rephrase", TooltipText = "Rephrase" }, - new ToolbarItemModel() { Command = ToolbarCommand.Bold }, - new ToolbarItemModel() { Command = ToolbarCommand.Italic }, - new ToolbarItemModel() { Command = ToolbarCommand.Underline }, - new ToolbarItemModel() { Command = ToolbarCommand.Separator }, - new ToolbarItemModel() { Command = ToolbarCommand.FontName }, - new ToolbarItemModel() { Command = ToolbarCommand.FontSize }, - new ToolbarItemModel() { Command = ToolbarCommand.FontColor }, - new ToolbarItemModel() { Command = ToolbarCommand.Separator }, - new ToolbarItemModel() { Command = ToolbarCommand.BackgroundColor }, - new ToolbarItemModel() { Command = ToolbarCommand.Formats }, - new ToolbarItemModel() { Command = ToolbarCommand.Alignments }, - new ToolbarItemModel() { Command = ToolbarCommand.Separator }, - new ToolbarItemModel() { Command = ToolbarCommand.NumberFormatList }, - new ToolbarItemModel() { Command = ToolbarCommand.BulletFormatList }, - new ToolbarItemModel() { Command = ToolbarCommand.CreateLink }, - new ToolbarItemModel() { Command = ToolbarCommand.Image }, - new ToolbarItemModel() { Command = ToolbarCommand.Separator }, - new ToolbarItemModel() { Command = ToolbarCommand.CreateTable }, - new ToolbarItemModel() { Command = ToolbarCommand.SourceCode }, - new ToolbarItemModel() { Command = ToolbarCommand.Undo }, - new ToolbarItemModel() { Command = ToolbarCommand.Redo }, - }; + } + } + catch + { + await this.ToastObj.ShowAsync(new ToastModel { ContentTemplate = @GetTemplate(), ShowCloseButton = true, Timeout = 0 }); } } + +``` + +### Replacing AI Result Back into the Editor (with Undo Support) + +Once the AI-generated content is ready, it needs to be inserted exactly where the user originally selected text, without disturbing the rest of the document. This is achieved using the [ExecuteCommandAsync](https://help.syncfusion.com/cr/blazor/Syncfusion.Blazor.RichTextEditor.SfRichTextEditor.html#Syncfusion_Blazor_RichTextEditor_SfRichTextEditor_ExecuteCommandAsync_Syncfusion_Blazor_RichTextEditor_CommandName_System_String_Syncfusion_Blazor_RichTextEditor_ExecuteCommandOption_) method with the `InsertHTML` command. The `Undo` option is enabled to allow users to revert changes if needed. + +Additionally, users can copy the AI-generated content to the clipboard for use elsewhere. This is handled via a `JavaScript interop` function that copies plain text extracted from the AI result. + +{% tabs %} +{% highlight %} + + + Copy + Replace + + +{% endhighlight %} +{% endtabs %} + +```csharp + +private async Task ReplaceContent() +{ + ExecuteCommandOption executeCommandOption = new ExecuteCommandOption(); + executeCommandOption.Undo = true; + await this.rteObj.RestoreSelectionAsync(); // Return to original selection + await this.rteObj.ExecuteCommandAsync(CommandName.InsertHTML, this.apiResultData, executeCommandOption); + await CloseDialog(); +} +private async Task CopyContent() +{ + await JSRuntime.InvokeVoidAsync("copyToClipboard", Markdig.Markdown.ToPlainText(AIResult, pipeline)); +} + +``` + -``` +## Workflow Summary + +Select text β†’ Choose AI action β†’ Dialog opens β†’ AI processes β†’ Updated content displayed β†’ User copies or replaces content. ## Error Handling and Troubleshooting @@ -696,10 +642,6 @@ If the AI service fails to return a valid response, the Rich Text Editor will di - **Network Issues**: Check connectivity to the AI service endpoint, especially for self-hosted Ollama instances. - **Large Prompts**: Processing large text inputs may cause timeouts. Consider reducing the prompt size or optimizing the request for efficiency. -## Performance Considerations - -When handling large text content, ensure the Ollama server has sufficient resources (CPU/GPU) to process requests efficiently. For long-form content or batch operations, consider splitting the input into smaller segments to avoid performance bottlenecks. Test the application with your specific use case to determine optimal performance. - ## Sample Code A complete working example is available in the [Syncfusion Blazor AI Samples GitHub repository](https://github.com/syncfusion/smart-ai-samples).