WYSIWYG editing Image gallery upload Content templates
AI Toolkit

In-editor AI for the <richtextbox> tag helper: Ask AI in the toolbar, a docked AI Chat panel, an inline suggestion preview, and a persistent AI Review drawer backed by MapRichTextBoxUploads()'s server-side endpoints. The built-in demo resolver runs here so no API key is required. See also: Structured content demo for JSON / Markdown round-trip.

See the BYOK pattern →
What this demo is showing

enable-ai-toolkit turns on the AI plugin for this editor only. The tag helper assigns a persistence key, configures the shared review endpoints, and injects a server-backed demo resolver so the AI workflow works in ASP.NET Core without adding custom page bootstrapping. If you register your own IRichTextBoxAiResolver, the same toolbar, chat, and review UI will call your server logic instead. You can also swap IRichTextBoxAiSuggestionLedgerStore and IRichTextBoxAiReviewLogStore if you want shared review state to live in SQL, Redis, or another backend.

Tag helper

<richtextbox
    id="AiToolkitEditor"
    name="AiToolkitEditor"
    toolbar="default"
    enable-ai-toolkit="true"
    ai-toolkit-persistence-key="demo-ai-toolkit"
    ai-toolkit-review-sync-interval="10000"
    height="440px" />

Register your own resolver

The editor is provider-agnostic. Register an implementation of IRichTextBoxAiResolver in DI and the existing tag-helper, dialog, chat, and review UI will call your server logic.

builder.Services.AddRichTextBox();
builder.Services.AddSingleton<IRichTextBoxAiResolver, MyAiResolver>();
public sealed class MyAiResolver : IRichTextBoxAiResolver
{
    public ValueTask<RichTextBoxAiResponse> ResolveAsync(RichTextBoxAiRequest request, CancellationToken cancellationToken = default)
    {
        return ValueTask.FromResult(
            RichTextBoxAiResponseBuilder.FromOperations(
                request.HasSelection ? "Selection suggestion" : "Document suggestion",
                RichTextBoxAiResponseBuilder.PreviewSuggestion(
                    "Provider-backed rewrite goes here.",
                    "Generated by a custom ASP.NET Core AI resolver.")));
    }
}

Wire your own backend (client side)

When you need fully custom request shaping, swap the client-side resolver instead. Every editor exposes aiToolkit.setResolver() - the toolbar, dialog, and panels call your function and handle the UI for you.

<script>
window.addEventListener("DOMContentLoaded", function () {
    var editor = window.richTextBoxEditors && window.richTextBoxEditors.AiToolkitEditor;
    if (!editor || !editor.aiToolkit) return;

    editor.aiToolkit.setResolver(async function (request) {
        // POST to YOUR backend - the key stays server-side
        var reply = await fetch("/my-ai", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                mode: request.mode, text: request.source, language: request.language
            })
        }).then(function (r) { return r.json(); });

        return {
            result: reply.text,
            reason: reply.explanation,
            operations: [{ type: "preview-suggestion", text: reply.text }]
        };
    });
});
</script>

Persist suggestions across sessions

Swap the default in-memory stores to share pending / accepted / rejected review state across requests and users.

builder.Services.AddSingleton<IRichTextBoxAiSuggestionLedgerStore, MySuggestionLedgerStore>();
builder.Services.AddSingleton<IRichTextBoxAiReviewLogStore, MyReviewLogStore>();
Looking for a BYOK admin form?

See the AI Provider Settings (BYOK) demo for a worked bring-your-own-key pattern - tenant admin form, client-side resolver swap via setResolver(), and a server-side /api/ai handler sketch that reads keys from a secrets store.