From ee8797c142931343ec3a579d6c1fe679b60b93f9 Mon Sep 17 00:00:00 2001 From: Enciphered Date: Mon, 4 May 2026 19:57:48 +0500 Subject: [PATCH] Documentations added Co-authored-by: Copilot --- docs/01-getting-started.md | 92 +++++++++++ docs/02-creating-a-page.md | 181 ++++++++++++++++++++++ docs/03-creating-a-component.md | 215 ++++++++++++++++++++++++++ docs/04-data-models-and-aot.md | 163 ++++++++++++++++++++ docs/05-form-submission.md | 200 ++++++++++++++++++++++++ docs/06-component-reference.md | 35 +++++ docs/Components/Accordion.md | 175 +++++++++++++++++++++ docs/Components/Alert.md | 177 ++++++++++++++++++++++ docs/Components/Avatar.md | 169 +++++++++++++++++++++ docs/Components/Badge.md | 182 ++++++++++++++++++++++ docs/Components/Breadcrumb.md | 175 +++++++++++++++++++++ docs/Components/Button.md | 193 +++++++++++++++++++++++ docs/Components/Calendar.md | 237 +++++++++++++++++++++++++++++ docs/Components/CalendarRange.md | 228 ++++++++++++++++++++++++++++ docs/Components/Card.md | 184 ++++++++++++++++++++++ docs/Components/Checkbox.md | 207 +++++++++++++++++++++++++ docs/Components/Dialog.md | 252 +++++++++++++++++++++++++++++++ docs/Components/DropdownMenu.md | 198 ++++++++++++++++++++++++ docs/Components/FileInput.md | 212 ++++++++++++++++++++++++++ docs/Components/Input.md | 221 +++++++++++++++++++++++++++ docs/Components/Pagination.md | 172 +++++++++++++++++++++ docs/Components/Progress.md | 176 +++++++++++++++++++++ docs/Components/RadioGroup.md | 211 ++++++++++++++++++++++++++ docs/Components/Select.md | 234 ++++++++++++++++++++++++++++ docs/Components/Separator.md | 148 ++++++++++++++++++ docs/Components/Skeleton.md | 185 +++++++++++++++++++++++ docs/Components/Slider.md | 203 +++++++++++++++++++++++++ docs/Components/Switch.md | 210 ++++++++++++++++++++++++++ docs/Components/Table.md | 200 ++++++++++++++++++++++++ docs/Components/Tabs.md | 200 ++++++++++++++++++++++++ docs/Components/Textarea.md | 229 ++++++++++++++++++++++++++++ docs/Components/TimePicker.md | 236 +++++++++++++++++++++++++++++ docs/Components/Toast.md | 213 ++++++++++++++++++++++++++ docs/Components/ToastViewport.md | 154 +++++++++++++++++++ docs/Components/Tooltip.md | 188 +++++++++++++++++++++++ 35 files changed, 6655 insertions(+) create mode 100644 docs/01-getting-started.md create mode 100644 docs/02-creating-a-page.md create mode 100644 docs/03-creating-a-component.md create mode 100644 docs/04-data-models-and-aot.md create mode 100644 docs/05-form-submission.md create mode 100644 docs/06-component-reference.md create mode 100644 docs/Components/Accordion.md create mode 100644 docs/Components/Alert.md create mode 100644 docs/Components/Avatar.md create mode 100644 docs/Components/Badge.md create mode 100644 docs/Components/Breadcrumb.md create mode 100644 docs/Components/Button.md create mode 100644 docs/Components/Calendar.md create mode 100644 docs/Components/CalendarRange.md create mode 100644 docs/Components/Card.md create mode 100644 docs/Components/Checkbox.md create mode 100644 docs/Components/Dialog.md create mode 100644 docs/Components/DropdownMenu.md create mode 100644 docs/Components/FileInput.md create mode 100644 docs/Components/Input.md create mode 100644 docs/Components/Pagination.md create mode 100644 docs/Components/Progress.md create mode 100644 docs/Components/RadioGroup.md create mode 100644 docs/Components/Select.md create mode 100644 docs/Components/Separator.md create mode 100644 docs/Components/Skeleton.md create mode 100644 docs/Components/Slider.md create mode 100644 docs/Components/Switch.md create mode 100644 docs/Components/Table.md create mode 100644 docs/Components/Tabs.md create mode 100644 docs/Components/Textarea.md create mode 100644 docs/Components/TimePicker.md create mode 100644 docs/Components/Toast.md create mode 100644 docs/Components/ToastViewport.md create mode 100644 docs/Components/Tooltip.md diff --git a/docs/01-getting-started.md b/docs/01-getting-started.md new file mode 100644 index 0000000..48550ed --- /dev/null +++ b/docs/01-getting-started.md @@ -0,0 +1,92 @@ +# Getting Started + +This guide gets the solution running locally and explains what happens during startup. + +## What is in this solution + +- `Htmx.ApiDemo`: ASP.NET Core app (Minimal API + generated HTMX endpoints) +- `Htmx.SourceGenerator`: Roslyn source generator that discovers `.htmx` files and generates endpoint mapping code +- `Htmx.slnx`: solution file at the repository root + +## Prerequisites + +- .NET SDK 10.x (target framework is `net10.0`) +- Node.js + npm (used for Tailwind CSS compilation during build) +- MongoDB running locally on `mongodb://localhost:27017` + +## First-time setup + +From the repository root: + +```bash +cd Htmx.ApiDemo +npm install +``` + +Why this is required: + +- The app build runs Tailwind via `npx @tailwindcss/cli ...` in a custom MSBuild target. +- Without `npm install`, build fails because the Tailwind CLI package is missing. + +## Run the app + +From the repository root: + +```bash +dotnet run --project Htmx.ApiDemo/Htmx.ApiDemo.csproj +``` + +Default local URL: + +- `http://localhost:5120` + +This comes from the launch profile in `Htmx.ApiDemo/Properties/launchSettings.json`. + +## Verify it works + +1. Open `http://localhost:5120` +2. If you are not authenticated, middleware redirects to `/login` +3. Create an account at `/register` +4. Sign in and navigate the app + +## What startup config does + +`Htmx.ApiDemo/Program.cs` configures: + +- MongoDB DI and index initialization (`EnsureIndexesAsync`) +- Cookie authentication + authorization +- Antiforgery middleware +- AOT-friendly JSON resolver chain using `AppJsonSerializerContext` +- Endpoint registration via generated mapping call: + - `app.MapHtmxApiDemoEndpoints();` + +## Build behavior worth knowing + +- Tailwind CSS is compiled before build into `Htmx.ApiDemo/wwwroot/css/output.css` +- `.htmx` files are treated as generator inputs (``) +- AOT is enabled (`true`), so reflection-heavy patterns can break publish/runtime + +## Optional: publish as AOT + +```bash +dotnet publish Htmx.ApiDemo/Htmx.ApiDemo.csproj -c Release +``` + +Use this early to catch AOT issues while developing features. + +## Troubleshooting + +### Build fails on Tailwind command + +- Run `npm install` inside `Htmx.ApiDemo` +- Confirm `node -v` and `npm -v` are available + +### Mongo connection errors + +- Confirm MongoDB is running on `localhost:27017` +- Confirm `ConnectionStrings:DefaultConnection` in `Htmx.ApiDemo/appsettings.json` + +### App keeps redirecting to login + +- This is expected for unauthenticated routes +- Register at `/register` or sign in at `/login` diff --git a/docs/02-creating-a-page.md b/docs/02-creating-a-page.md new file mode 100644 index 0000000..29f1ea4 --- /dev/null +++ b/docs/02-creating-a-page.md @@ -0,0 +1,181 @@ +# Creating a New Page + +This guide explains the full lifecycle of adding a new page to the app: the template file, the code-behind class, the handler, and the sidebar link. + +## How pages work + +Every page is a pair of files: + +| File | Purpose | +|---|---| +| `Templates/MyPage.htmx` | HTML markup with `$$SlotName$$` slots | +| `Templates/MyPage.htmx.cs` | C# class that fills slots + declares the Minimal API handler | + +The Roslyn source generator (`Htmx.SourceGenerator`) reads every `.htmx` file at build time and generates an abstract base class for it. You then write a concrete class in the companion `.htmx.cs` file that inherits from that base. + +## How `$$SlotName$$` becomes code + +Take this simple template: + +```html + +
+

$$Title$$

+

$$Body$$

+
+``` + +The generator splits the file on `$$...$$` patterns and produces: + +```csharp +// auto-generated — do NOT edit +public abstract partial class MyPageBase : IHtmxComponent +{ + protected abstract void RenderTitle(HtmxRenderContext context); + protected abstract void RenderBody(HtmxRenderContext context); + + // static HTML segments stored as ReadOnlySpan for zero-allocation output + private static ReadOnlySpan _part0 => new byte[] { ... }; + private static ReadOnlySpan _part1 => new byte[] { ... }; + private static ReadOnlySpan _part2 => new byte[] { ... }; + + public void Render(HtmxRenderContext context) + { + context.Writer.WriteUtf8(_part0); //

+ RenderTitle(context.Next()); + context.Writer.WriteUtf8(_part1); //

+ RenderBody(context.Next()); + context.Writer.WriteUtf8(_part2); //

+ } +} +``` + +Your job is to write the concrete class that implements each `RenderXxx` method. + +## Step 1 — Create the `.htmx` template + +Create `Htmx.ApiDemo/Templates/MyPage.htmx`: + +```html +
+

$$Heading$$

+

$$Description$$

+
+``` + +Rules: +- Slot names are **PascalCase** and surrounded by `$$` — e.g. `$$MySlot$$` +- A slot can hold plain text, HTML, or another rendered component +- The file must be saved in `Templates/` (or a subfolder) so the `.csproj` `AdditionalFiles` glob picks it up + +## Step 2 — Create the `.htmx.cs` code-behind + +Create `Htmx.ApiDemo/Templates/MyPage.htmx.cs`: + +```csharp +using Immediate.Apis.Shared; +using Immediate.Handlers.Shared; + +namespace Htmx.ApiDemo.Templates; + +// Concrete template — inherits from the generated base +public sealed class MyPage : MyPageBase +{ + private byte[] _headingData = []; + private byte[] _descriptionData = []; + + // Use `init`-only setters to pre-encode strings to UTF-8 bytes once + public required string Heading { init => _headingData = value.ToUtf8Bytes(); } + public required string Description { init => _descriptionData = value.ToUtf8Bytes(); } + + protected override void RenderHeading(HtmxRenderContext context) + => context.Writer.WriteUtf8(_headingData); + + protected override void RenderDescription(HtmxRenderContext context) + => context.Writer.WriteUtf8(_descriptionData); +} + +// Minimal API handler — discovered and registered by the source generator +[Handler] +[MapGet("/my-page")] +public static partial class GetMyPageHandler +{ + public record Query; // add route/query parameters here if needed + + private static ValueTask HandleAsync( + Query query, + IHttpContextAccessor httpContextAccessor, + CancellationToken token) + { + var ctx = httpContextAccessor.HttpContext + ?? throw new InvalidOperationException("HttpContext is not available."); + + var page = new MyPage + { + Heading = "My New Page", + Description = "This is a minimal example page." + }; + + // WriteHtmxPage: full HTML shell for direct browser loads, + // bare fragment for HTMX partial swaps (HX-Request header present) + ctx.WriteHtmxPage(page, title: "My Page", appName: "HtmxApp", pageTitle: "My Page"); + return ValueTask.CompletedTask; + } +} +``` + +## Step 3 — Add a sidebar link (optional but typical) + +Open `Templates/MainLayout.htmx` and add a nav entry inside the `