feat(01-03): implement echo chat UI with MudBlazor

Add ChatGPT-style chat interface using MudBlazor components. User messages
display on the right (purple), bot echoes "success msg!" on the left.
Includes app bar, centered 768px layout, bottom-anchored messages, and
documented inline comments on every Blazor concept introduced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
local
2026-04-03 01:51:53 +01:00
parent 7dd4243f01
commit 711df97ce9
14 changed files with 545 additions and 143 deletions

156
CLAUDE.md
View File

@@ -1,147 +1,43 @@
<!-- GSD:project-start source:PROJECT.md -->
## Project
**Chat Agent WebApp**
A personal AI chat web application built with Blazor WebAssembly and the OpenAI GPT API. Users send messages, receive streaming AI responses rendered as markdown, and manage multiple persistent conversations. The project doubles as an incremental learning journey — each phase introduces one concept with well-documented, explained code, making it suitable as a Blazor tutorial for a developer experienced in C# but new to the framework.
A personal AI chat web application built with Blazor WebAssembly and MudBlazor. Users send messages through a ChatGPT-style interface and receive responses from a backend service. The project is an incremental learning journey — each phase introduces one concept at a time, making it suitable for a C# developer experienced in backend work but new to web application frameworks.
**Core Value:** A working, well-understood AI chat interface every line of code is intentional and explained, so the builder learns Blazor patterns while shipping a real product.
**Core Value:** A working chat interface where every line of code is intentional and explained, so the builder learns Blazor patterns while shipping a real product.
**Current Phase:** Echo — the backend returns "success msg!" for every user message. No external API integration yet.
### Constraints
- **Tech stack**: .NET / C# / Blazor WebAssembly — non-negotiable
- **LLM provider**: OpenAI GPT API
- **Storage**: JSON files on local disk
- **Architecture**: WASM client + backend API (API key stays server-side)
- **Code style**: Every Blazor concept introduced must have inline comments explaining what it does and why
<!-- GSD:project-end -->
- **Tech stack**: C# / Blazor WebAssembly — non-negotiable
- **Hosting model**: Blazor WASM (standalone) with separate ASP.NET Core Web API backend
- **UI library**: MudBlazor
- **Code style**: Simple, well-documented. Every Blazor concept introduced must have inline comments explaining what it does, why it's done that way, and what idiomatic alternatives exist
<!-- GSD:stack-start source:research/STACK.md -->
## Technology Stack
## Recommended Stack
### Core Technologies
| Technology | Version | Purpose | Why Recommended |
|------------|---------|---------|-----------------|
| .NET 9 SDK | 9.x (latest patch) | Runtime, tooling, SDK | LTS-adjacent, stable, .NET 10 is in preview — stay on 9 for a tutorial project targeting a stable foundation |
| Blazor WebAssembly Standalone | .NET 9 | Client SPA running in-browser | Non-negotiable per project constraints; client-side execution with no server round-trip for UI |
| ASP.NET Core Web API | .NET 9 | Backend proxy for OpenAI calls | Required to keep the OpenAI API key server-side; WASM cannot access secrets directly |
| C# 13 | Included with .NET 9 | Application language | Included in .NET 9 SDK; no separate install needed |
### OpenAI Integration
| Library | Version | Purpose | Why Recommended |
|---------|---------|---------|-----------------|
| `OpenAI` (official) | 2.9.1 | OpenAI API client with streaming | The official OpenAI-published .NET library; supports `CompleteChatStreamingAsync()` returning `AsyncCollectionResult<StreamingChatCompletionUpdate>` via `await foreach`; stable release as of 2026-03-02 |
### Markdown Rendering
| Library | Version | Purpose | Why Recommended |
|---------|---------|---------|-----------------|
| `Markdig` | 1.1.1 | Parse markdown text to HTML | The de facto standard markdown processor for .NET; CommonMark-compliant, fast, extensible, targets .NET Standard 2.0 so works in WASM; used by Microsoft and Syncfusion as the underlying engine |
### UI Component Library
| Library | Version | Purpose | Why Recommended |
|---------|---------|---------|-----------------|
| `MudBlazor` | 9.2.0 | Material Design component library | Full .NET 9 support confirmed; pure C# with minimal JavaScript; comprehensive chat-friendly components (MudTextField, MudPaper, MudScrollToBottom, MudList); large community; no per-seat licensing |
### JSON Storage (Server-side)
| Technology | Version | Purpose | Why Recommended |
|------------|---------|---------|-----------------|
| `System.Text.Json` | Built into .NET 9 | Serialize/deserialize conversation history | Built-in, no extra dependency; `JsonSerializerOptions` with `WriteIndented = true` for human-readable files; async file I/O via `File.ReadAllTextAsync` / `File.WriteAllTextAsync` |
## Supporting Libraries
| Library | Version | Purpose | When to Use |
|---------|---------|---------|-------------|
| `Microsoft.Extensions.AI` (abstractions) | 9.x preview | Optional AI abstraction layer | Skip for v1 — adds indirection before the core chat pattern is understood. Relevant for v2 when adding multi-provider support |
| `Blazored.LocalStorage` | latest | Browser local storage | Not needed for this project — persistence is on the server via JSON files, not the browser |
| `System.Net.ServerSentEvents` | Built into .NET 9 | SSE parser for streaming | Used automatically by the `OpenAI` library on the server; no direct usage needed |
## Development Tools
| Tool | Purpose | Notes |
|------|---------|-------|
| Visual Studio 2022 (v17.12+) | IDE with Blazor hot reload | Recommended for tutorial builder; full Blazor debugging, component preview, and hot reload support |
| VS Code + C# Dev Kit | Lighter-weight alternative | Works well; use `dotnet watch` for hot reload |
| `dotnet watch run` | Hot reload during development | Run in both Client and Server project directories simultaneously |
| `dotnet-dev-certs` | HTTPS dev certificate | Required for local HTTPS; run `dotnet dev-certs https --trust` once |
## Installation
# Create solution
# Create Blazor WASM client (standalone)
# Create ASP.NET Core Web API backend
# Install OpenAI SDK in the API project
# Install Markdig in the Client project
## Alternatives Considered
| Recommended | Alternative | When to Use Alternative |
|-------------|-------------|-------------------------|
| `OpenAI` 2.9.1 (official) | `OpenAI-DotNet` 8.8.8 (unofficial) | Never — the official package is now stable and maintained by OpenAI directly |
| `OpenAI` 2.9.1 (official) | `Azure.AI.OpenAI` 2.1.0 | When targeting Azure OpenAI Service specifically (e.g., enterprise, EU data residency, private endpoints) — overkill for this project |
| `Markdig` | `CommonMark.NET` | Only if strict CommonMark compliance matters more than extensions; Markdig is a superset and the ecosystem standard |
| `MudBlazor` | Radzen Blazor | Radzen is fine; choose it if you already know it; MudBlazor has more learning resources |
| `MudBlazor` | Telerik UI for Blazor | Telerik requires a paid license; not appropriate for a personal tool |
| Standalone WASM + separate Web API | Blazor Web App template (unified) | Use the unified Blazor Web App template when you want mixed Server+WASM render modes on a single project; overkill for this project and obscures the WASM-specific patterns the tutorial aims to teach |
| JSON flat files (server-side) | SQLite via EF Core | SQLite is a better choice at scale; JSON is simpler for single-user personal tools and avoids introducing a migration workflow |
## What NOT to Use
| Avoid | Why | Use Instead |
|-------|-----|-------------|
| `OpenAI-DotNet` (unofficial) | Different API surface, not maintained by OpenAI, version numbers create confusion | Official `OpenAI` NuGet package |
| `Microsoft.SemanticKernel` | Adds significant abstraction and dependency weight for a tutorial; streaming works but is complex to explain | Direct `OpenAI` SDK calls; add SK in v2 when orchestration is needed |
| JavaScript `EventSource` API via JSInterop for streaming | Blazor WASM has `SetBrowserResponseStreamingEnabled` which avoids JS interop; adding JSInterop for streaming increases complexity significantly | `HttpCompletionOption.ResponseHeadersRead` + `SetBrowserResponseStreamingEnabled(true)` in the HTTP handler |
| `Newtonsoft.Json` | Unnecessary dependency; `System.Text.Json` is built into .NET 9 and is faster; Newtonsoft was the pre-.NET Core standard | `System.Text.Json` (built-in) |
| `Blazored.LocalStorage` for persistence | Browser storage is limited (~5MB), cleared by users, and not suitable for chat history of any meaningful length; also exposes all data client-side | Server-side JSON file storage via the Web API |
| AOT compilation during learning phase | Dramatically increases build times; not needed until production optimization is a concern; confusing to introduce in a tutorial | Default IL interpretation; add AOT opt-in note in the final phase |
## Stack Patterns by Variant
- Backend streams OpenAI tokens as `text/event-stream` (SSE) or `application/x-ndjson`
- Client uses `SetBrowserResponseStreamingEnabled(true)` on `HttpRequestMessage`
- Client reads with `HttpCompletionOption.ResponseHeadersRead` and iterates the stream
- Trigger `StateHasChanged()` in the component after each token to update the UI
- Define a `ConversationRepository` service on the API that reads/writes from a configurable base path
- Register as `Singleton` (not `Scoped`) since there is only one user and file access must be serialized
- Use `SemaphoreSlim(1,1)` to prevent concurrent write conflicts even in single-user mode
- Use `Markdig.Markdown.ToHtml(text, pipeline)` where `pipeline` is built with `MarkdownPipelineBuilder` enabling extensions (e.g., `UseAutoLinks()`, `UseEmojiAndSmiley()`)
- Render the HTML string using `@((MarkupString)html)` inside a `<div class="markdown-body">` element
- Apply CSS (GitHub Markdown CSS or custom) scoped to `.markdown-body` for code blocks and tables
## Version Compatibility
| Package | Compatible With | Notes |
|---------|-----------------|-------|
| `OpenAI` 2.9.1 | .NET Standard 2.0+ (.NET 9 confirmed) | Published 2026-03-02; requires `System.Net.ServerSentEvents` (built into .NET 9) |
| `Markdig` 1.1.1 | .NET 8.0, .NET Standard 2.0, .NET Framework 4.6.2 | .NET 9 compatible via .NET 8 TFM; published 2026-03-04 |
| `MudBlazor` 9.2.0 | .NET 8.0, .NET 9.0, .NET 10.0 | Published 2026-03-18; version 9.x = full support for .NET 9 |
| .NET 9 SDK | Blazor WASM + Web API in same solution | Both project types target `net9.0`; no cross-framework issues |
## Sources
- https://www.nuget.org/packages/OpenAI — Official OpenAI NuGet package; version 2.9.1 confirmed (2026-03-02)
- https://github.com/openai/openai-dotnet — Official OpenAI .NET SDK; streaming API verified (`CompleteChatStreamingAsync`, `await foreach`)
- https://www.nuget.org/packages/Markdig — Markdig version 1.1.1 confirmed (2026-03-04)
- https://www.nuget.org/packages/MudBlazor — MudBlazor 9.2.0 confirmed; .NET 8/9/10 full support (2026-03-18)
- https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-9.0 — Official Blazor hosting model docs; standalone WASM vs Blazor Web App distinction verified
- https://learn.microsoft.com/en-us/dotnet/core/compatibility/networking/10.0/default-http-streaming — Breaking change: WASM streaming opt-in (.NET 9) vs default (.NET 10)
- https://www.strathweb.com/2024/07/built-in-support-for-server-sent-events-in-net-9/ — SSE native support in .NET 9 via `System.Net.ServerSentEvents`; used internally by OpenAI SDK (MEDIUM confidence, single source)
- https://github.com/openai/openai-dotnet/issues/65 — Confirmed streaming issue in Blazor WASM requires `SetBrowserResponseStreamingEnabled(true)` (MEDIUM confidence, GitHub issue thread)
- https://devblogs.microsoft.com/dotnet/openai-dotnet-library/ — Official .NET Blog announcement of the OpenAI library
- https://dev.to/kazinix/blazor-web-app-webassembly-hosted-in-net8-and-net9-1k6g — Hosted template removal in .NET 8+, manual solution structure (MEDIUM confidence)
<!-- GSD:stack-end -->
| Technology | Version | Purpose |
|------------|---------|---------|
| .NET SDK | 9.0.x | Runtime and tooling |
| Blazor WebAssembly Standalone | .NET 9 | Client SPA running in-browser |
| ASP.NET Core Web API | .NET 9 | Backend proxy (for future external API calls) |
| MudBlazor | latest | Material Design component library |
| System.Text.Json | built-in | JSON serialization |
<!-- GSD:conventions-start source:CONVENTIONS.md -->
## Conventions
Conventions not yet established. Will populate as patterns emerge during development.
<!-- GSD:conventions-end -->
<!-- GSD:architecture-start source:ARCHITECTURE.md -->
## Architecture
Architecture not yet mapped. Follow existing patterns found in the codebase.
<!-- GSD:architecture-end -->
See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed hosting model discussion, API design decisions, and project structure rationale.
<!-- GSD:workflow-start source:GSD defaults -->
## GSD Workflow Enforcement
**Summary:**
- Three-project solution: Client (WASM), Api (backend proxy), Shared (models)
- Components update locally — no API needed for UI rendering
- The Api project exists for future use when external services require server-side secrets
- For the current echo phase, only the Client project is actively used
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
## Conventions
Use these entry points:
- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks
- `/gsd:debug` for investigation and bug fixing
- `/gsd:execute-phase` for planned phase work
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
<!-- GSD:workflow-end -->
<!-- GSD:profile-start -->
## Developer Profile
> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile.
> This section is managed by `generate-claude-profile` -- do not edit manually.
<!-- GSD:profile-end -->
- Inline comments on every new Blazor concept: what it does, why, and idiomatic alternatives
- Emphasize framework idiom and explain choices — written for a C# developer new to web/Blazor
- Keep code simple; avoid abstractions until they are clearly needed
- One concept per phase — do not introduce multiple new patterns at once