diff --git a/x-blazor-skill/.claude-plugin/marketplace.json b/x-blazor-skill/.claude-plugin/marketplace.json new file mode 100644 index 0000000..ba0e645 --- /dev/null +++ b/x-blazor-skill/.claude-plugin/marketplace.json @@ -0,0 +1,35 @@ +{ + "name": "x-blazor-skills", + "metadata": { + "description": "AntDesign.X.Blazor skill package for building AI chat and agent workspace interfaces in Blazor.", + "descriptionZh": "AntDesign.X.Blazor 智能技能包,用于在 Blazor 中构建 AI 对话和 Agent 工作台界面。", + "version": "1.0.0", + "author": "IoTSharp", + "homepage": "https://github.com/IoTSharp/AntDesignXBlazor", + "repository": { + "type": "git", + "url": "https://github.com/IoTSharp/AntDesignXBlazor.git" + }, + "keywords": [ + "ant-design-x", + "ant-design-x-blazor", + "blazor", + "ai", + "chat", + "agent", + "dotnet" + ] + }, + "plugins": [ + { + "name": "x-blazor-skill", + "description": "AntDesign.X.Blazor component, chat store, request client, streaming, and React-to-Blazor migration guidance.", + "descriptionZh": "AntDesign.X.Blazor 组件、聊天状态、请求客户端、流式响应以及 React 到 Blazor 迁移指南。", + "source": "./", + "strict": false, + "skills": [ + "./skills/x-blazor-skill" + ] + } + ] +} diff --git a/x-blazor-skill/.skill.json b/x-blazor-skill/.skill.json new file mode 100644 index 0000000..fe19740 --- /dev/null +++ b/x-blazor-skill/.skill.json @@ -0,0 +1,32 @@ +{ + "targets": { + "codefuse": { + "enabled": true, + "paths": { + "global": ".codefuse/fuse/skills", + "project": ".codefuse/skills" + } + }, + "claude": { + "enabled": true, + "paths": { + "global": ".claude/skills", + "project": ".claude/skills" + } + }, + "cursor": { + "enabled": true, + "paths": { + "global": ".cursor/skills", + "project": ".cursor/skills" + } + }, + "codex": { + "enabled": true, + "paths": { + "global": ".codex/skills", + "project": ".codex/skills" + } + } + } +} diff --git a/x-blazor-skill/package.json b/x-blazor-skill/package.json new file mode 100644 index 0000000..b2c6a39 --- /dev/null +++ b/x-blazor-skill/package.json @@ -0,0 +1,25 @@ +{ + "name": "x-blazor-skill", + "version": "1.0.0", + "description": "AI agent skill package for building AntDesign.X.Blazor chat and agent workspace interfaces.", + "homepage": "https://github.com/IoTSharp/AntDesignXBlazor", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/IoTSharp/AntDesignXBlazor.git" + }, + "keywords": [ + "ant-design-x", + "ant-design-x-blazor", + "blazor", + "ai", + "chat", + "agent", + "skill" + ], + "files": [ + "skills", + ".skill.json", + ".claude-plugin" + ] +} diff --git a/x-blazor-skill/skills/x-blazor-skill/SKILL.md b/x-blazor-skill/skills/x-blazor-skill/SKILL.md new file mode 100644 index 0000000..5745a7b --- /dev/null +++ b/x-blazor-skill/skills/x-blazor-skill/SKILL.md @@ -0,0 +1,135 @@ +--- +name: x-blazor-skill +description: Build AI chat, agent workspace, and content-generation interfaces with AntDesign.X.Blazor. Use when working in Blazor projects that need Ant Design X concepts such as XProvider, XBubbleList, XSender, XConversations, XPrompts, XMarkdown, XMermaid, XChatStore, XAgentStore, IXRequestClient, streaming replies, attachments, sources, thought chains, or React Ant Design X to Blazor migration guidance. +--- + +# X Blazor Skill + +## Overview + +Use this skill to build Ant Design X style AI experiences in pure Blazor with `AntDesign.X.Blazor` and `AntDesign.Blazor`. + +This library maps Ant Design X concepts to C# records, Razor components, `EventCallback`, `RenderFragment`, and scoped stores. Do not use React hooks, JSX, or `@ant-design/x` APIs in Blazor code. + +## Setup + +Install and register both AntDesign.Blazor and AntDesign.X.Blazor: + +```powershell +dotnet add package AntDesignX.Blazor +``` + +```html + + + +``` + +```csharp +using AntDesign.X; + +builder.Services.AddAntDesign(); +builder.Services.AddAntDesignX(); +``` + +In Razor files, add: + +```razor +@using AntDesign.X +@using AntDesign.X.Components +``` + +For `XMermaid`, include Mermaid separately: + +```html + +``` + +## Decision Guide + +| If you need to... | Start with | +| --- | --- | +| Render chat messages | `XBubbleList` with `IReadOnlyList` | +| Render one-off assistant/user content | `XBubble` | +| Build the chat composer | `XSender` and `XSenderRequest` | +| Keep chat state and stream assistant replies | `XChatStore` | +| Call an agent endpoint directly | `XAgentStore` or `IXRequestClient` | +| Show sessions or history | `XConversations` and `XConversationItem` | +| Show suggested prompts | `XPrompts` and `XPromptItem` | +| Support file input | `XSender` attachments or `XAttachments` | +| Display citations | `XSources` | +| Display tool or reasoning progress | `XThoughtChain` or `XThink` | +| Render Markdown, code, or diagrams | `XMarkdown`, `XCodeHighlighter`, `XMermaid` | +| Apply X theme tokens | `XProvider` with optional `XThemeTokens` | + +## Workflow + +1. Read the host app's existing Blazor patterns before changing UI. +2. Register services and static assets once at the app boundary. +3. Wrap the AI surface with `XProvider`. +4. Use model records from `AntDesign.X` instead of anonymous dictionaries for component data. +5. Use `XBubbleList` for message collections; reserve `XBubble` for single messages or custom templates. +6. Bind `XSender` through `@bind-Value` and handle `OnSubmit(XSenderRequest request)`. +7. For streaming, update `XBubbleItem.Content`, `Streaming`, `Loading`, and `Status` through `XChatStore` or `XAgentStore` rather than ad hoc UI flags. +8. Check reference files only when needed: + - [COMPONENTS.md](reference/COMPONENTS.md) for component choice and key props. + - [PATTERNS.md](reference/PATTERNS.md) for full-page composition examples. + - [API.md](reference/API.md) for model records, service APIs, and React-to-Blazor mappings. + +## Development Rules + +- Prefer `XProvider` around chat/workspace surfaces so theme tokens and locale services are available. +- Prefer `XBubbleList` over manually looping `XBubble` for normal chat logs; it handles role defaults, loading rows, auto-scroll, and optional virtualization. +- Use `Role` values such as `user`, `assistant`, `system`, and `tool`; role config defaults are case-insensitive. +- Use `ContentTemplate`, `HeaderTemplate`, `FooterTemplate`, and item templates for rich Razor content. Do not serialize complex Blazor UI into strings. +- Set `Markdown="true"` only when the content should be parsed as Markdown. +- Treat `Streaming=true` as a transient state and set it to `false` when the final chunk arrives. +- Use `XSender.Loading` plus `OnCancel` for stop behavior while a request is running. +- Keep user secrets and model API keys on the server. Do not embed provider keys in browser-delivered Blazor WebAssembly code. +- Keep custom CSS under the host app's scope; AntDesign.X.Blazor classes use the `antdx-` prefix. +- Prefer examples under `examples/AntDesign.X.Blazor.Demo/Components/Pages/Components` when exact syntax is uncertain. + +## Minimal Example + +```razor +@using AntDesign.X +@using AntDesign.X.Components +@using Microsoft.AspNetCore.Components.Forms + + + + + + + +@code { + private string? draft; + private bool loading; + private readonly List messages = []; + private readonly List attachments = []; + + private Task Submit(XSenderRequest request) + { + messages.Add(new XBubbleItem + { + Role = "user", + Content = request.Text, + Attachments = request.Attachments + }); + + draft = string.Empty; + return Task.CompletedTask; + } + + private Task Cancel() => Task.CompletedTask; + private Task HandleAction(string key) => Task.CompletedTask; + private Task AddFiles(IReadOnlyList files) => Task.CompletedTask; + private Task RemoveAttachment(string id) => Task.CompletedTask; +} +``` diff --git a/x-blazor-skill/skills/x-blazor-skill/agents/openai.yaml b/x-blazor-skill/skills/x-blazor-skill/agents/openai.yaml new file mode 100644 index 0000000..5880448 --- /dev/null +++ b/x-blazor-skill/skills/x-blazor-skill/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "AntDesign.X Blazor" + short_description: "Build Ant Design X chat UIs in Blazor" + default_prompt: "Use $x-blazor-skill to build an AntDesign.X.Blazor chat UI." diff --git a/x-blazor-skill/skills/x-blazor-skill/reference/API.md b/x-blazor-skill/skills/x-blazor-skill/reference/API.md new file mode 100644 index 0000000..7c5da0a --- /dev/null +++ b/x-blazor-skill/skills/x-blazor-skill/reference/API.md @@ -0,0 +1,164 @@ +# AntDesign.X.Blazor API Notes + +Use this reference for service registration, core records, and request/streaming APIs. + +## Namespaces + +```razor +@using AntDesign.X +@using AntDesign.X.Components +``` + +```csharp +using AntDesign.X; +``` + +Component classes live under `AntDesign.X.Components`; models, services, and extension methods live under `AntDesign.X`. + +## Service Registration + +`AddAntDesignX` registers: + +| Service | Lifetime | Purpose | +| --- | --- | --- | +| `XRequestClientOptions` | singleton | Request paths, base address, timeout, headers | +| `IXNotificationService` | scoped | Notification queue | +| `IXRequestClient` | scoped | Low-level request and stream client | +| `XChatStore` | scoped | Chat message state and streaming lifecycle | +| `XAgentStore` | scoped | Agent message/event/tool-call state | +| `IXRendererRegistry` | singleton | Render delegation registry | +| `IXLocaleService` | scoped | Component text locale service | + +```csharp +builder.Services.AddAntDesignX(options => +{ + options.BaseAddress = new Uri("https://example.com"); + options.ChatPath = "/api/chat"; + options.AgentPath = "/api/agent"; + options.Timeout = TimeSpan.FromMinutes(2); + options.DefaultHeaders["x-client"] = "blazor"; +}); +``` + +## Core Records + +| Record | Use | +| --- | --- | +| `XBubbleItem` | Message rows for `XBubbleList`. | +| `XBubbleRoleConfig` | Role-specific placement, avatar, Markdown, shape, and template defaults. | +| `XAttachmentItem` | Files displayed by `XSender`, `XAttachments`, `XFileCard`, or message bubbles. | +| `XActionItem` | Button/action metadata for `XActions`, `XBubble`, and `XSender`. | +| `XConversationItem` | Conversation list item. | +| `XPromptItem` | Prompt card item, including nested children. | +| `XSourceItem` | Citation/source row. | +| `XThoughtItem` | Thought chain node. | +| `XFolderItem` | File/folder tree item. | +| `XNotificationItem` | Notification payload. | +| `XSenderRequest` | Submitted text plus attachments. | +| `XThemeTokens` | Theme custom properties for `XProvider`. | + +Prefer these records over anonymous objects so code remains strongly typed. + +## XBubbleItem Example + +```csharp +var message = new XBubbleItem +{ + Role = "assistant", + Header = "Assistant", + AvatarIcon = "robot", + Content = "Here is **Markdown** content.", + Markdown = true, + Streaming = false, + Status = XMessageStatus.Success, + Actions = + [ + new XActionItem { Key = "copy", Icon = "copy", Tooltip = "Copy" }, + new XActionItem { Key = "retry", Icon = "reload", Tooltip = "Retry" } + ] +}; +``` + +## Request Client + +Use `IXRequestClient` when you need custom control below `XChatStore` or `XAgentStore`. + +```csharp +var handle = await RequestClient.RequestAsync(new XRequestOptions +{ + RequestId = Guid.NewGuid().ToString("N"), + RequestUri = new Uri("/api/chat", UriKind.Relative), + Body = payload, + Stream = true, + StreamMode = XStreamReadMode.Sse, + OnChunk = chunk => + { + // Append chunk content to the active assistant message. + return Task.CompletedTask; + } +}, cancellationToken); + +await handle.Completion; +``` + +Use the stores first unless the app needs a custom protocol or message lifecycle. + +## Chat Store + +`XChatStore` exposes: + +| Member | Use | +| --- | --- | +| `Messages` | Snapshot for `XBubbleList.Items`. | +| `IsLoading` | Bind to `XSender.Loading` and `XBubbleList.Loading`. | +| `LastError` | Render errors or notifications. | +| `ActiveRequestId` | Track the running assistant request. | +| `Changed` | Subscribe and call `StateHasChanged`. | +| `SubmitAsync(XSenderRequest, ct)` | Append user message and stream assistant response. | +| `RetryAsync(ct)` | Re-run the last submission. | +| `AbortAsync()` | Cancel the active request. | +| `ReplaceMessages(...)` | Reset or hydrate conversation state. | + +Always unsubscribe from `Changed` in `Dispose`. + +## Agent Store + +`XAgentStore` follows the same UI subscription pattern as `XChatStore` and is intended for agent workflows. Use it when the UI shows tool calls, events, or agent-specific progress alongside chat messages. + +Typical binding: + +```razor + + + +``` + +## Static Assets + +Include: + +```html + + + +``` + +Add Mermaid only when rendering `XMermaid`: + +```html + +``` + +## File Upload Notes + +Blazor file input uses `IBrowserFile`. Use `BeforeUpload` to filter files before they enter the attachment list. + +```csharp +private ValueTask BeforeUpload(IBrowserFile file) +{ + var accepted = file.Size <= 10 * 1024 * 1024; + return ValueTask.FromResult(accepted); +} +``` + +Do not trust browser-provided file metadata for security decisions on the server. diff --git a/x-blazor-skill/skills/x-blazor-skill/reference/COMPONENTS.md b/x-blazor-skill/skills/x-blazor-skill/reference/COMPONENTS.md new file mode 100644 index 0000000..c7bded6 --- /dev/null +++ b/x-blazor-skill/skills/x-blazor-skill/reference/COMPONENTS.md @@ -0,0 +1,186 @@ +# AntDesign.X.Blazor Components + +Use this reference when selecting components or checking the main data model for a UI surface. + +## Component Groups + +| Stage | Components | Blazor data | +| --- | --- | --- | +| General chat | `XBubble`, `XBubbleList`, `XConversations`, `XNotification` | `XBubbleItem`, `XConversationItem`, `XNotificationItem` | +| Wake / prompt | `XWelcome`, `XPrompts` | `XPromptItem` | +| Express / input | `XSender`, `XAttachments`, `XSuggestion` | `XSenderRequest`, `XAttachmentItem` | +| Thinking | `XThoughtChain`, `XThink` | `XThoughtItem` | +| Feedback / evidence | `XActions`, `XFileCard`, `XSources`, `XFolder` | `XActionItem`, `XSourceItem`, `XFolderItem` | +| Content rendering | `XMarkdown`, `XCodeHighlighter`, `XMermaid` | string content plus optional templates | +| Global | `XProvider` | `XThemeTokens` | + +## XProvider + +Wrap the AI experience in `XProvider` when you need theme, locale, or token support. + +```razor + + @ChildContent + + +@code { + private readonly XThemeTokens tokens = new() + { + PrimaryColor = "#1677ff", + BubbleStartBackground = "#f6f8fa", + BubbleEndBackground = "#e6f4ff" + }; +} +``` + +Use `Theme="light"` or `Theme="dark"`. `Tokens` become CSS custom properties through the `antdx-` styling layer. + +## XBubble and XBubbleList + +Use `XBubbleList` for normal chat history: + +```razor + +``` + +`XBubbleList` applies default role config for: + +| Role | Default behavior | +| --- | --- | +| `assistant` / `ai` | start placement, filled, round, Markdown enabled | +| `user` | end placement, filled, corner, Markdown enabled | +| `system` | start placement, outlined, Markdown enabled | +| `tool` | start placement, borderless, Markdown enabled | +| `divider` | renders as a divider row | + +Use `XBubble` when rendering a single message or a fully custom slot: + +```razor + +``` + +Key properties: + +| Property | Notes | +| --- | --- | +| `Role` | Semantic role used for styling and defaults. | +| `Placement` | `Start` or `End`. User messages normally use `End`. | +| `Variant` | Filled, outlined, borderless, and other enum values from `XBubbleVariant`. | +| `Shape` | Default, round, or corner shape enum. | +| `Content` | Plain text or Markdown string. | +| `ContentTemplate` | Use for rich Razor UI. | +| `Actions` | `IReadOnlyList` shown in the footer. | +| `Attachments` | `IReadOnlyList` rendered compactly below content. | +| `Loading` / `Streaming` / `Status` | Drive loading and stream state. | + +## XSender + +Use `XSender` for the composer. Bind draft text with `@bind-Value`; handle send with `OnSubmit`. + +```razor + +``` + +Key properties: + +| Property | Notes | +| --- | --- | +| `Value` / `ValueChanged` | Text draft binding. | +| `OnSubmit` | Receives `XSenderRequest` with `Text` and `Attachments`. | +| `Loading` | Shows stop behavior and disables submit. | +| `SubmitMode` | Controls Enter-key behavior via `XSenderSubmitMode`. | +| `AttachmentsEnabled` | Enables the built-in file input surface. | +| `BeforeUpload` | `Func>` filter. | +| `Actions` / `OnAction` | Custom composer buttons. | +| `HeaderTemplate`, `PrefixTemplate`, `SuffixTemplate`, `FooterTemplate` | Rich slots. | +| `AllowSpeech` | Enables browser speech input when supported. | + +## Navigation and Prompt Components + +Use `XConversations` for session lists, `XPrompts` for suggested tasks, and `XSuggestion` for inline command hints. + +```razor + + + +``` + +Create stable keys for conversation and prompt items so Blazor can preserve state across renders. + +## Attachments and Files + +Use `XAttachments` when the attachment list is independent from `XSender`; otherwise use the built-in attachment support on `XSender`. + +```razor + +``` + +Represent files with `XAttachmentItem`; include `Name`, `ContentType`, `Size`, `Status`, and optional `Percent`. + +## Thinking, Sources, and Feedback + +Use `XThoughtChain` for multi-step agent/tool progress and `XThink` for one collapsible reasoning block. + +```razor + + + +``` + +Use `XSources` for citations and retrieved documents. Use `XActions` for copy, retry, like/dislike, or custom operations under a message. + +## Content Rendering + +Use `XMarkdown` for Markdown strings, `XCodeHighlighter` for explicit code blocks, and `XMermaid` for diagrams. + +```razor + + + +``` + +If content is part of a bubble, prefer `XBubble Markdown="true"` for simple Markdown and `ContentTemplate` for mixed Markdown/components. + +## Notifications + +Use `IXNotificationService` with `XNotification` host when the page needs queued status messages. + +```razor + +``` + +Inject the service where actions occur: + +```csharp +[Inject] private IXNotificationService Notifications { get; set; } = default!; +``` diff --git a/x-blazor-skill/skills/x-blazor-skill/reference/PATTERNS.md b/x-blazor-skill/skills/x-blazor-skill/reference/PATTERNS.md new file mode 100644 index 0000000..8a2af0a --- /dev/null +++ b/x-blazor-skill/skills/x-blazor-skill/reference/PATTERNS.md @@ -0,0 +1,248 @@ +# AntDesign.X.Blazor Patterns + +Use this reference for full-page composition and streaming flows. + +## App Registration Pattern + +Register AntDesign.Blazor first, then AntDesign.X.Blazor: + +```csharp +using AntDesign.X; + +builder.Services.AddAntDesign(); +builder.Services.AddAntDesignX(options => +{ + options.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); + options.ChatPath = "/api/chat"; +}); +``` + +Include static assets once in the host document: + +```html + + + +``` + +## Manual Local Chat Pattern + +Use this pattern when no server call is needed yet. + +```razor +@using AntDesign.X +@using AntDesign.X.Components +@using Microsoft.AspNetCore.Components.Forms + + + + + + + + + + + +@code { + private string theme = "light"; + private string? draft; + private readonly List messages = []; + private readonly List attachments = []; + + private readonly IReadOnlyList prompts = + [ + new() { Key = "summary", Title = "Summarize", Description = "Summarize the current document" }, + new() { Key = "ideas", Title = "Ideas", Description = "Suggest follow-up questions" } + ]; + + private Task UsePrompt(XPromptItem item) + { + draft = item.Description ?? item.Title; + return Task.CompletedTask; + } + + private Task Submit(XSenderRequest request) + { + messages.Add(new XBubbleItem + { + Role = "user", + Content = request.Text, + Attachments = request.Attachments + }); + + messages.Add(new XBubbleItem + { + Role = "assistant", + Header = "Assistant", + AvatarIcon = "robot", + Content = "Received: " + request.Text, + Markdown = true + }); + + draft = string.Empty; + return Task.CompletedTask; + } + + private Task AddFiles(IReadOnlyList files) + { + attachments.AddRange(files.Select(file => new XAttachmentItem + { + Name = file.Name, + ContentType = file.ContentType, + Size = file.Size + })); + + return Task.CompletedTask; + } + + private Task RemoveAttachment(string id) + { + attachments.RemoveAll(item => item.Id == id); + return Task.CompletedTask; + } +} +``` + +## Store-Based Streaming Pattern + +Use `XChatStore` for normal chat screens. It appends the user message, creates an assistant placeholder, streams chunks into the assistant message, and exposes `Changed`. + +```razor +@implements IDisposable +@inject XChatStore Chat + + + + + + + +@code { + private string? draft; + + protected override void OnInitialized() + { + Chat.Changed += StateHasChanged; + } + + public void Dispose() + { + Chat.Changed -= StateHasChanged; + } + + private async Task Submit(XSenderRequest request) + { + await Chat.SubmitAsync(request); + draft = string.Empty; + } + + private Task HandleAction(string actionKey) + { + return actionKey switch + { + "retry" => Chat.RetryAsync(), + "abort" => Chat.AbortAsync(), + _ => Task.CompletedTask + }; + } +} +``` + +## Agent Store Pattern + +Use `XAgentStore` when the screen is agent-first rather than chat-first. Bind `IsRunning` to sender loading state and subscribe to `Changed` just like `XChatStore`. + +```razor +@implements IDisposable +@inject XAgentStore Agent + + + + + +@code { + private string? prompt; + + protected override void OnInitialized() + { + Agent.Changed += StateHasChanged; + } + + public void Dispose() + { + Agent.Changed -= StateHasChanged; + } + + private Task RunAgent(XSenderRequest request) + { + return Agent.RunAsync(new XAgentRequest + { + Prompt = request.Text, + Attachments = request.Attachments + }); + } + + private static XThoughtItem ToThought(XAgentEventItem item) => new() + { + Key = item.Key, + Title = item.Title, + Description = item.Description, + Content = item.Content, + Status = item.Status + }; +} +``` + +## Server Boundary Pattern + +Keep provider API keys and model credentials on the server. Point the client to an app-owned endpoint through `XRequestClientOptions`. + +```csharp +builder.Services.AddAntDesignX(options => +{ + options.ChatPath = "/api/ai/chat"; + options.AgentPath = "/api/ai/agent"; + options.Timeout = TimeSpan.FromMinutes(5); +}); +``` + +Use server-side middleware or API routes to translate `XChatRequestPayload` or `XAgentRequestPayload` into your provider-specific request. + +## React Ant Design X Migration Pattern + +Map React concepts to Blazor concepts directly: + +| React Ant Design X | AntDesign.X.Blazor | +| --- | --- | +| `XProvider` | `XProvider` Razor component | +| `Bubble.List` | `XBubbleList` | +| `Bubble` | `XBubble` | +| `Sender` | `XSender` | +| `Conversations` | `XConversations` | +| `Prompts` | `XPrompts` | +| `Attachments` | `XAttachments` | +| `ThoughtChain` | `XThoughtChain` | +| `Think` | `XThink` | +| `Sources` | `XSources` | +| `useXChat` | `XChatStore` | +| `useXAgent` | `XAgentStore` | +| `XRequest` | `IXRequestClient` | +| JSX slots | `RenderFragment` parameters | +| event props | `EventCallback` parameters | + +Do not copy JSX examples verbatim. Rebuild them as Razor markup with C# model records.