From 12d7b9d263fa0d5f5ad90a727378c6888dbd957d Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi Date: Thu, 22 Oct 2026 16:50:13 +0200 Subject: [PATCH 1/6] fix(docs): black-tile favicon and the bare mark in the header The SVG favicon switched to a white tile in dark browser chrome; it now always uses the black tile, like favicon.png. The header and sidebar logo is the bare dot mark (no tile), dark dots on light and light dots on dark, the way shellui.dev shows its mark. --- docs/ShellIcons.Docs/wwwroot/favicon.svg | 2 +- docs/ShellIcons.Docs/wwwroot/images/logo-dark.svg | 2 +- docs/ShellIcons.Docs/wwwroot/images/logo-light.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ShellIcons.Docs/wwwroot/favicon.svg b/docs/ShellIcons.Docs/wwwroot/favicon.svg index e70bc19..25c455b 100644 --- a/docs/ShellIcons.Docs/wwwroot/favicon.svg +++ b/docs/ShellIcons.Docs/wwwroot/favicon.svg @@ -1 +1 @@ - + diff --git a/docs/ShellIcons.Docs/wwwroot/images/logo-dark.svg b/docs/ShellIcons.Docs/wwwroot/images/logo-dark.svg index 30aed6f..3e546c8 100644 --- a/docs/ShellIcons.Docs/wwwroot/images/logo-dark.svg +++ b/docs/ShellIcons.Docs/wwwroot/images/logo-dark.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/docs/ShellIcons.Docs/wwwroot/images/logo-light.svg b/docs/ShellIcons.Docs/wwwroot/images/logo-light.svg index 25c455b..1b4b767 100644 --- a/docs/ShellIcons.Docs/wwwroot/images/logo-light.svg +++ b/docs/ShellIcons.Docs/wwwroot/images/logo-light.svg @@ -1 +1 @@ - + \ No newline at end of file From e5eed8fde08eadca88266fa3af96060718d2b3ef Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi Date: Thu, 22 Oct 2026 16:50:15 +0200 Subject: [PATCH 2/6] docs: a NuGet readme per package, root README for GitHub Every package packed the root README, which is Blazor-centric. Each project now packs its own README.md (plain Markdown, logo line first, so nuget.org renders it): a new Blazor readme with the Blazor guide, and the MAUI and Avalonia readmes with NuGet install steps. The root README becomes the GitHub overview with a centred header and a quickstart per target. --- Directory.Build.props | 3 +- README.md | 176 ++++++------------------------ src/ShellIcons.Avalonia/README.md | 8 +- src/ShellIcons.Blazor/README.md | 133 ++++++++++++++++++++++ src/ShellIcons.Maui/README.md | 20 ++-- 5 files changed, 187 insertions(+), 153 deletions(-) create mode 100644 src/ShellIcons.Blazor/README.md diff --git a/Directory.Build.props b/Directory.Build.props index 56c283e..6486b88 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -41,7 +41,8 @@ - + + diff --git a/README.md b/README.md index 4153065..77ba7f8 100644 --- a/README.md +++ b/README.md @@ -1,161 +1,51 @@ -![ShellIcons](https://raw.githubusercontent.com/shellui-dev/shell-icons/main/assets/readme-logo.svg) +
+ +ShellIcons logo # ShellIcons -Lucide-derived SVG icons for **Blazor**, **Avalonia**, and **.NET MAUI**. +Lucide-derived icons for **Blazor**, **.NET MAUI** and **Avalonia**. -- Zero JS, zero required CSS -- Same visual system across all three .NET UI frameworks -- Native rendering per target: inline `` in Blazor, native `Path` shapes in MAUI, direct drawing in Avalonia -- Tree-shakeable typed components per icon +[Docs](https://shellicons.shellui.dev) · [NuGet](https://www.nuget.org/packages?q=ShellIcons) · [Changelog](CHANGELOG.md) -Docs: [shellicons.shellui.dev](https://shellicons.shellui.dev) +
-## Status +- One catalog (1,555 Lucide 0.475.0 icons plus drop-in custom icons), the same names on every target +- Native rendering per target: inline `` in Blazor, native `Path` shapes in MAUI, direct drawing in Avalonia, all converted at build time +- Typed components per icon that trim, plus a dispatcher when the icon is a value +- Zero JS and zero required CSS in Blazor +- Docs for AI agents: [`/llms.txt`](https://shellicons.shellui.dev/llms.txt), every page as Markdown, and `AGENTS.md` / `CLAUDE.md` / Copilot / Cursor rule files ([AI Agents](https://shellicons.shellui.dev/docs/ai-agents)) -| Package | Status | -|---|---| -| `ShellIcons.Blazor` | **0.1.0-alpha on [NuGet](https://www.nuget.org/packages/ShellIcons.Blazor)** — 1,555 Lucide 0.475.0 icons | -| `ShellIcons.Maui` | **Preview, unpublished** — builds for Android, iOS, Mac Catalyst and Windows; see [src/ShellIcons.Maui](src/ShellIcons.Maui/README.md) | -| `ShellIcons.Avalonia` | **Preview, unpublished** — Avalonia 12, `net8.0`; see [src/ShellIcons.Avalonia](src/ShellIcons.Avalonia/README.md) | +## Packages -- [x] Source generators emit every icon from the vendored catalog (`LUCIDE_VERSION.txt`) plus drop-in `catalog/custom/` icons -- [x] Blazor: suffixed components (``), `Icon.*` factory, `` dispatcher — safe next to UI kits -- [x] MAUI: typed controls, `IconName` dispatcher, `IconCatalog` metadata with alias lookup -- [x] Avalonia: typed controls, `Kind` dispatcher, inherited `Foreground`, the same `IconCatalog` -- [x] 168 tests (xUnit + bUnit + headless MAUI + headless Avalonia with Skia rendering), CI for both solutions, tag-driven release — see [RELEASING.md](RELEASING.md) -- [x] Docs site at [docs/ShellIcons.Docs](docs/ShellIcons.Docs) (ShellDocs 0.1.19-alpha), with a searchable icon browser -- [x] Docs for AI agents: [`/llms.txt`](https://shellicons.shellui.dev/llms.txt), every page as Markdown, and `AGENTS.md` / `CLAUDE.md` / Copilot / Cursor rule files — see [AI Agents](https://shellicons.shellui.dev/docs/ai-agents) -- [x] Static build for GitHub Pages ([shellicons.shellui.dev](https://shellicons.shellui.dev)) — deploys on push to `main` -- [ ] MAUI: check rendering on Android/iOS devices, then publish -- [ ] Avalonia: run in a real desktop app, then publish +| Package | Status | Readme | +|---|---|---| +| `ShellIcons.Blazor` | [![NuGet](https://img.shields.io/nuget/vpre/ShellIcons.Blazor)](https://www.nuget.org/packages/ShellIcons.Blazor) | [src/ShellIcons.Blazor](src/ShellIcons.Blazor/README.md) | +| `ShellIcons.Maui` | [![NuGet](https://img.shields.io/nuget/vpre/ShellIcons.Maui)](https://www.nuget.org/packages/ShellIcons.Maui) preview | [src/ShellIcons.Maui](src/ShellIcons.Maui/README.md) | +| `ShellIcons.Avalonia` | [![NuGet](https://img.shields.io/nuget/vpre/ShellIcons.Avalonia)](https://www.nuget.org/packages/ShellIcons.Avalonia) preview | [src/ShellIcons.Avalonia](src/ShellIcons.Avalonia/README.md) | -## Blazor quickstart +## Quickstart -Not yet published. During development: +Blazor: -```xml - +```bash +dotnet add package ShellIcons.Blazor ``` -### Pick your API - -Four ways to render an icon — all produce identical SVG. Only the first two are safe to use next to a UI kit. - -| API | Import | Collides with UI kits? | Tree-shakeable? | Use for | -|---|---|---|---|---| -| **``** suffixed component | `@using ShellIcons` | ✅ No | ✅ Yes | **Default for markup.** Reads as an icon, full parameter binding. | -| **`@Icon.ChevronRight()`** factory | `@using ShellIcons` | ✅ No | ✅ Yes | **Icons as values** — `RenderFragment` parameters, nav-item lists, configs. | -| `` dispatcher | `@using ShellIcons` | ✅ No | ❌ Roots all 1,555 | Names only known at runtime (CMS, JSON, markdown). | -| `` flat component | `@using ShellIcons.Icons` | ⚠️ Yes — RZ9985 on Badge, Table, Menu, Router, Card… | ✅ Yes | Icon-only files with no UI kit imported. | - -One `@using ShellIcons` in `_Imports.razor` gives you the first three. - -#### `` — suffixed components (recommended for markup) - -Every icon is also emitted as `{Name}Icon` in the root `ShellIcons` namespace. The suffix keeps names clear of UI-kit components and tells readers "this is an icon" at a glance — the same alias `lucide-react` ships. - ```razor @using ShellIcons - - -``` - -One exception: Lucide's `shell` icon would be `ShellIcon`, which is the dispatcher, so it has no suffixed form — use `@Icon.Shell()`. - -#### `@Icon.ChevronRight()` — factory (icons as values) - -Every icon has a static method on `Icon` returning a `RenderFragment`. Use it wherever an icon is *data* rather than markup: - -```razor -@* A component parameter typed RenderFragment *@ - - -@* A list built in C# *@ -@code { - private readonly (string Label, RenderFragment Icon)[] _items = - [ - ("Inbox", Icon.Inbox()), - ("Settings", Icon.Settings(size: "16")), - ]; -} -``` - -A call with no arguments returns a cached fragment, so `@Icon.Plus()` in a hot render path doesn't allocate. - -#### `` — dispatcher - -Runtime lookup by kebab-case name, for icons whose identity is data-driven. Referencing it roots the full catalog, so prefer the two forms above when the name is known at compile time. - -```razor - - -``` - -#### `` — flat components (⚠️ RZ9985 trap) - -`@using ShellIcons.Icons` puts all 1,555 unprefixed names into your tag lookup. With a UI kit that has `Badge`, `Table`, `Router`, etc., you get RZ9985 collisions. - -**Workarounds that don't work** (verified by real integrators): - -- `@using Icon = ShellIcons.Icons` then `` — Razor's tag matcher ignores namespace aliases; the tag compiles as an unknown HTML element and **renders blank**. -- Type aliases (`@using Badge = MyApp.UI.Badge`) — ignored by the tag matcher too. - -Use the suffixed components instead, or fully qualify: ``. - -### Using icons inside component libraries - -**Buttons and similar containers — just put the icon in the content.** No `Icon` slot needed; the component's CSS sizes child SVGs (shadcn's `[&_svg]:size-4`): - -```razor - -``` - -**Components that place the icon somewhere specific** (input adornments, alerts, nav items) take a `RenderFragment` parameter — pass the factory: - -```razor - + ``` -### Click handlers - -Put handlers on a wrapping ` -``` - -Without `Title` icons are decorative (`aria-hidden="true"`); with it they get `role="img"` + ``: - -```razor -<XIcon Title="Close dialog" /> +```bash +dotnet add package ShellIcons.Maui --prerelease ``` -## MAUI quickstart (preview) - ```xml <ContentPage xmlns:icons="https://shellicons.dev/maui"> <icons:ChevronRight Size="16" /> @@ -163,18 +53,20 @@ Without `Title` icons are decorative (`aria-hidden="true"`); with it they get `r </ContentPage> ``` -Not on NuGet yet — reference `src/ShellIcons.Maui` from source. Docs: [MAUI getting started](docs/ShellIcons.Docs/content/docs/maui/getting-started.md). +Avalonia: -## Avalonia quickstart (preview) +```bash +dotnet add package ShellIcons.Avalonia --prerelease +``` ```xml <Window xmlns:icons="https://shellicons.dev/avalonia"> <icons:ChevronRight Size="16" /> - <icons:Icon Kind="{Binding StatusIcon}" Foreground="Crimson" /> + <icons:Icon Kind="{Binding StatusIcon}" Foreground="{DynamicResource AccentBrush}" /> </Window> ``` -Icons inherit `Foreground`, so they match the surrounding text by default. Not on NuGet yet — reference `src/ShellIcons.Avalonia` from source. Docs: [Avalonia getting started](docs/ShellIcons.Docs/content/docs/avalonia/getting-started.md). +Avalonia's dispatcher uses `Kind` because every Avalonia control already has `Name`. The full guides are in each package's readme and on the [docs site](https://shellicons.shellui.dev). ## Repo layout @@ -204,12 +96,12 @@ shell-icons/ │ ├── ShellIcons.Generator.Tests/ xUnit: SvgParser, Naming, path conversion │ ├── ShellIcons.Blazor.Tests/ xUnit + bUnit: components, factory, dispatcher, collisions │ ├── ShellIcons.Maui.Tests/ headless MAUI: controls, XAML, whole-catalog geometry -│ └── ShellIcons.Avalonia.Tests/ headless Avalonia + Skia: controls, XAML, whole-catalog rendering +│ └── ShellIcons.Avalonia.Tests/ headless Avalonia with Skia: controls, XAML, renders of every icon ├── docs/ │ └── ShellIcons.Docs/ ShellDocs site — dev at http://localhost:5145 └── .github/workflows/ ├── ci.yml ubuntu: ShellIcons.slnx · windows: ShellIcons.Maui.slnx - ├── release.yml tag → NuGet (Trusted Publishing) + GitHub Release + ├── release.yml tag → all three packages on NuGet (Trusted Publishing) + GitHub Release └── docs.yml static build → GitHub Pages ``` @@ -244,7 +136,7 @@ Every page under `content/` is prerendered to plain HTML, so any static host can ## Adding custom icons -Drop an SVG into [catalog/custom/icons/](catalog/custom/icons/) and rebuild — the Phase 2 source generator picks it up automatically and emits a component into `ShellIcons.Blazor`. Same 24×24 stroke contract as Lucide. See [catalog/custom/README.md](catalog/custom/README.md) for the drop-in contract. +Drop an SVG into [catalog/custom/icons/](catalog/custom/icons/) and rebuild — the source generators pick it up and emit it into every package. Same 24×24 stroke contract as Lucide. See [catalog/custom/README.md](catalog/custom/README.md) for the drop-in contract. Custom icons live alongside Lucide-derived icons in the same namespace; consumers can't tell them apart. Use them for: diff --git a/src/ShellIcons.Avalonia/README.md b/src/ShellIcons.Avalonia/README.md index f090000..b92d453 100644 --- a/src/ShellIcons.Avalonia/README.md +++ b/src/ShellIcons.Avalonia/README.md @@ -1,9 +1,15 @@ +![ShellIcons](https://raw.githubusercontent.com/shellui-dev/shell-icons/main/assets/readme-logo.svg) + # ShellIcons.Avalonia (preview) Lucide-derived icons for Avalonia, from the same 1,555-icon catalog as `ShellIcons.Blazor` and `ShellIcons.Maui`. Docs: [shellicons.shellui.dev/docs/avalonia/getting-started](https://shellicons.shellui.dev/docs/avalonia/getting-started). -Not published yet. The release workflow only packs `ShellIcons.Blazor`. +## Install + +```bash +dotnet add package ShellIcons.Avalonia --prerelease +``` Targets `net8.0` and Avalonia 12.1. diff --git a/src/ShellIcons.Blazor/README.md b/src/ShellIcons.Blazor/README.md new file mode 100644 index 0000000..d72b379 --- /dev/null +++ b/src/ShellIcons.Blazor/README.md @@ -0,0 +1,133 @@ +![ShellIcons](https://raw.githubusercontent.com/shellui-dev/shell-icons/main/assets/readme-logo.svg) + +# ShellIcons.Blazor + +1,555 Lucide-derived icons for Blazor as inline `<svg>`: zero JS, zero required CSS, typed components that trim per icon. Docs: [shellicons.shellui.dev](https://shellicons.shellui.dev). + +## Install + +```bash +dotnet add package ShellIcons.Blazor +``` + +Targets `net8.0` and `net9.0` (and `net10.0` via forward compatibility). No CSS import, no JS. + +## Pick your API + +Four ways to render an icon — all produce identical SVG. Only the first two are safe to use next to a UI kit. + +| API | Import | Collides with UI kits? | Tree-shakeable? | Use for | +|---|---|---|---|---| +| **`<ChevronRightIcon />`** suffixed component | `@using ShellIcons` | ✅ No | ✅ Yes | **Default for markup.** Reads as an icon, full parameter binding. | +| **`@Icon.ChevronRight()`** factory | `@using ShellIcons` | ✅ No | ✅ Yes | **Icons as values** — `RenderFragment` parameters, nav-item lists, configs. | +| `<ShellIcon Name="chevron-right" />` dispatcher | `@using ShellIcons` | ✅ No | ❌ Roots all 1,555 | Names only known at runtime (CMS, JSON, markdown). | +| `<ChevronRight />` flat component | `@using ShellIcons.Icons` | ⚠️ Yes — RZ9985 on Badge, Table, Menu, Router, Card… | ✅ Yes | Icon-only files with no UI kit imported. | + +One `@using ShellIcons` in `_Imports.razor` gives you the first three. + +### `<ChevronRightIcon />` — suffixed components (recommended for markup) + +Every icon is also emitted as `{Name}Icon` in the root `ShellIcons` namespace. The suffix keeps names clear of UI-kit components and tells readers "this is an icon" at a glance — the same alias `lucide-react` ships. + +```razor +@using ShellIcons + +<ChevronRightIcon /> +<ZapIcon Size="16" StrokeWidth="1.5" /> +<TriangleAlertIcon Class="text-warning" /> +<XIcon Title="Close dialog" /> +``` + +One exception: Lucide's `shell` icon would be `ShellIcon`, which is the dispatcher, so it has no suffixed form — use `@Icon.Shell()`. + +### `@Icon.ChevronRight()` — factory (icons as values) + +Every icon has a static method on `Icon` returning a `RenderFragment`. Use it wherever an icon is *data* rather than markup: + +```razor +@* A component parameter typed RenderFragment *@ +<NavItem Href="/" Label="Home" Icon="@Icon.House()" /> + +@* A list built in C# *@ +@code { + private readonly (string Label, RenderFragment Icon)[] _items = + [ + ("Inbox", Icon.Inbox()), + ("Settings", Icon.Settings(size: "16")), + ]; +} +``` + +A call with no arguments returns a cached fragment, so `@Icon.Plus()` in a hot render path doesn't allocate. + +### `<ShellIcon Name="…" />` — dispatcher + +Runtime lookup by kebab-case name, for icons whose identity is data-driven. Referencing it roots the full catalog, so prefer the two forms above when the name is known at compile time. + +```razor +<ShellIcon Name="chevron-right" /> +<ShellIcon Name="@page.IconName" Size="20" /> +``` + +### `<ChevronRight />` — flat components (⚠️ RZ9985 trap) + +`@using ShellIcons.Icons` puts all 1,555 unprefixed names into your tag lookup. With a UI kit that has `Badge`, `Table`, `Router`, etc., you get RZ9985 collisions. + +**Workarounds that don't work** (verified by real integrators): + +- `@using Icon = ShellIcons.Icons` then `<Icon.Plus />` — Razor's tag matcher ignores namespace aliases; the tag compiles as an unknown HTML element and **renders blank**. +- Type aliases (`@using Badge = MyApp.UI.Badge`) — ignored by the tag matcher too. + +Use the suffixed components instead, or fully qualify: `<ShellIcons.Icons.ChevronRight />`. + +## Using icons inside component libraries + +**Buttons and similar containers — just put the icon in the content.** No `Icon` slot needed; the component's CSS sizes child SVGs (shadcn's `[&_svg]:size-4`): + +```razor +<Button><ChevronRightIcon /> Next</Button> +``` + +**Components that place the icon somewhere specific** (input adornments, alerts, nav items) take a `RenderFragment` parameter — pass the factory: + +```razor +<Input StartIcon="@Icon.Search()" Placeholder="Search…" /> +``` + +## Click handlers + +Put handlers on a wrapping `<button>` — screen readers expect that. If you must attach one to the icon itself, drop the `@`: + +```razor +<button @onclick="Save" aria-label="Save"><SaveIcon /></button> @* ✅ recommended *@ +<SaveIcon onclick="@(() => Save())" /> @* ✅ works *@ +<SaveIcon @onclick="Save" /> @* ❌ throws — see below *@ +``` + +On a *component*, Razor passes `@onclick="Save"` as a plain string named `@onclick`; the browser then rejects that attribute name and the render batch fails. ShellIcons throws a clear `InvalidOperationException` instead. For the factory, the dictionary key is `"onclick"` (no `@`): + +```razor +@Icon.Bell(additionalAttributes: new Dictionary<string, object> +{ + ["onclick"] = EventCallback.Factory.Create(this, HandleClick), + ["data-testid"] = "notifications", +}) +``` + +## Colors and accessibility + +Colors inherit from CSS `color` (`stroke="currentColor"`): + +```razor +<span style="color: crimson"><TriangleAlertIcon Size="20" /></span> +``` + +Without `Title` icons are decorative (`aria-hidden="true"`); with it they get `role="img"` + `<title>`: + +```razor +<XIcon Title="Close dialog" /> +``` + +## Licensing + +MIT. Lucide-derived icons are ISC; attribution is in `NOTICE.md` inside the package. diff --git a/src/ShellIcons.Maui/README.md b/src/ShellIcons.Maui/README.md index c12ddf0..38a97f6 100644 --- a/src/ShellIcons.Maui/README.md +++ b/src/ShellIcons.Maui/README.md @@ -1,9 +1,17 @@ +![ShellIcons](https://raw.githubusercontent.com/shellui-dev/shell-icons/main/assets/readme-logo.svg) + # ShellIcons.Maui (preview) Lucide-derived icons for .NET MAUI as native `Path` shapes, from the same 1,555-icon catalog as `ShellIcons.Blazor`. Docs: [shellicons.shellui.dev/docs/maui/getting-started](https://shellicons.shellui.dev/docs/maui/getting-started). -Not published yet. The release workflow only packs `ShellIcons.Blazor`. +## Install + +```bash +dotnet add package ShellIcons.Maui --prerelease +``` + +Targets `net10.0-android`, `net10.0-ios`, `net10.0-maccatalyst` and `net10.0-windows10.0.19041.0`. ## Use @@ -74,17 +82,11 @@ caches it, so every control showing the same icon shares one geometry. | No consumer-selected icon subsets yet | The generator runs when this package is built, not in the consumer's build, so it can't see which icons an app wants. Typed controls already trim unused icons. | | Same `IconName` / `Icon` names as ShellUI Native's interim icon component | ShellUI Native can switch to this package without changing its components. | -The conversion contract for custom icons is in [catalog/custom/README.md](../../catalog/custom/README.md); both generators share one naming rule (`Naming.KebabToPascal`). +The conversion contract for custom icons is in [catalog/custom/README.md](https://github.com/shellui-dev/shell-icons/blob/main/catalog/custom/README.md); both generators share one naming rule (`Naming.KebabToPascal`). ## Verified - Builds with zero warnings for `net10.0`, `net10.0-android`, `net10.0-ios`, `net10.0-maccatalyst`, `net10.0-windows10.0.19041.0` (on Windows, with the MAUI workloads). - Tests on the plain `net10.0` build, including a compiled XAML fixture (xmlns, string → `IconName`, `icons:Image` next to MAUI's `Image`). - A check that parses every icon into MAUI geometry and keeps every endpoint on the 24×24 grid. It caught a stray off-canvas shape in Lucide's own `save-off.svg`. -- **Not yet verified:** how it looks on a real device or emulator. The rendering follows the approach ShellUI Native verified on WinUI (`Aspect=None`, scale transform, explicit stroke scaling), but nobody has looked at it on Android or iOS yet. - -## Before publishing - -- Look at it on Android and iOS devices. -- Give the package its own readme — `Directory.Build.props` currently packs the repo's Blazor-centric `README.md` into every package. -- Add `ShellIcons.Maui` to `release.yml` (the job must run on Windows or macOS with the MAUI workload). +- Rendered in a Windows app from the packed package. Not yet looked at on Android or iOS devices. From 8d627fe3a01acbed60029f54a7e71dab7a88e908 Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi <shewatipa@chips.co.zw> Date: Thu, 22 Oct 2026 16:50:16 +0200 Subject: [PATCH 3/6] test(avalonia): check ShellUI Native's 110 icon names ShellUI Native's interim Icon.cs uses 110 names; every one must exist in IconName so Native can switch to this package without renaming icons. --- .../ShellIcons.Avalonia.Tests/CatalogTests.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/ShellIcons.Avalonia.Tests/CatalogTests.cs b/tests/ShellIcons.Avalonia.Tests/CatalogTests.cs index 2a99fa5..b864f3c 100644 --- a/tests/ShellIcons.Avalonia.Tests/CatalogTests.cs +++ b/tests/ShellIcons.Avalonia.Tests/CatalogTests.cs @@ -2,6 +2,17 @@ namespace ShellIcons.Avalonia.Tests; public class CatalogTests { + // ShellUI Native's interim icon set; every one must keep resolving after it switches to this package. + private const string NativeIcons = + "Check X Plus Minus Search Filter Settings SlidersHorizontal Pencil Trash Trash2 Copy Clipboard Save Download Upload " + + "Share Share2 ExternalLink Link RefreshCw RotateCcw Undo2 Redo2 LogIn LogOut Send Paperclip ChevronDown ChevronUp " + + "ChevronLeft ChevronRight ChevronsUpDown ChevronsLeft ChevronsRight ArrowLeft ArrowRight ArrowUp ArrowDown ArrowUpRight " + + "Menu Ellipsis EllipsisVertical GripVertical House LayoutDashboard PanelLeft Info CircleAlert CircleCheck CircleX " + + "CircleHelp TriangleAlert Ban Loader LoaderCircle Bell Circle CirclePlus Dot User Users UserPlus Lock LockOpen Key " + + "Shield Eye EyeOff Mail Inbox Archive MessageCircle Phone Calendar Clock Globe MapPin Image File FileText Folder Tag " + + "Bookmark Heart Star ThumbsUp ShoppingCart CreditCard DollarSign Activity TrendingUp ChartColumn Zap Sparkles Command " + + "Terminal Code Moon Sun Monitor Smartphone Laptop Wifi Camera Mic Play Pause Volume2 Lightbulb"; + [Fact] public void Enum_HasEveryIcon_PlusNone() { @@ -10,6 +21,14 @@ public void Enum_HasEveryIcon_PlusNone() Assert.Equal(0, (int)IconName.None); } + [Fact] + public void Enum_CoversShellUINativeIcons() + { + var names = NativeIcons.Split(' '); + Assert.Equal(110, names.Length); + Assert.All(names, n => Assert.True(Enum.TryParse<IconName>(n, out _), n)); + } + [Theory] [InlineData("chevron-right", IconName.ChevronRight)] [InlineData("ChevronRight", IconName.ChevronRight)] From 15ba2d7d18e043cc7723ebbe78ba834030d55473 Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi <shewatipa@chips.co.zw> Date: Thu, 22 Oct 2026 16:50:18 +0200 Subject: [PATCH 4/6] ci: publish all three packages from a Windows release job ShellIcons.Maui needs the MAUI workload, so the release job runs on windows-latest (bash shell), builds and tests both solutions, packs Blazor, Maui and Avalonia, and checks all three IDs against nuget.org before pushing. CI smoke-packs Maui in its Windows job. RELEASING.md describes the new job. --- .github/workflows/ci.yml | 7 ++++++- .github/workflows/release.yml | 28 ++++++++++++++++++++++------ RELEASING.md | 20 +++++++++++++------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 295c908..b7f3b74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - name: Test run: dotnet test ShellIcons.slnx --configuration Release --no-build --verbosity normal - # The project, not the solution: packing the solution warns for every non-packable project. + # The projects, not the solution: packing the solution warns for every non-packable project. - name: Smoke pack run: | dotnet pack src/ShellIcons.Blazor/ShellIcons.Blazor.csproj --configuration Release --no-build --output nupkgs @@ -69,3 +69,8 @@ jobs: - name: Test run: dotnet test tests/ShellIcons.Maui.Tests --configuration Release --no-build --verbosity normal + + - name: Smoke pack + run: dotnet pack src/ShellIcons.Maui/ShellIcons.Maui.csproj --configuration Release --no-build --output nupkgs + env: + ContinuousIntegrationBuild: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2284884..b81340e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,11 +17,15 @@ on: jobs: release: - runs-on: ubuntu-latest + # Windows: ShellIcons.Maui needs the MAUI workload for its Android, iOS, Mac Catalyst and Windows targets. + runs-on: windows-latest environment: release permissions: contents: write # for creating the GitHub Release id-token: write # required for Trusted Publishing OIDC exchange + defaults: + run: + shell: bash steps: - uses: actions/checkout@v4 with: @@ -39,19 +43,31 @@ jobs: 8.0.x 9.0.x + - name: Install MAUI workload + run: dotnet workload install maui + - name: Restore - run: dotnet restore ShellIcons.slnx + run: | + dotnet restore ShellIcons.slnx + dotnet restore ShellIcons.Maui.slnx - name: Build - run: dotnet build ShellIcons.slnx --configuration Release --no-restore + run: | + dotnet build ShellIcons.slnx --configuration Release --no-restore + dotnet build ShellIcons.Maui.slnx --configuration Release --no-restore env: ContinuousIntegrationBuild: true - name: Test - run: dotnet test ShellIcons.slnx --configuration Release --no-build --verbosity normal + run: | + dotnet test ShellIcons.slnx --configuration Release --no-build --verbosity normal + dotnet test tests/ShellIcons.Maui.Tests --configuration Release --no-build --verbosity normal - name: Pack - run: dotnet pack src/ShellIcons.Blazor/ShellIcons.Blazor.csproj --configuration Release --no-build --output nupkgs + run: | + for project in ShellIcons.Blazor ShellIcons.Maui ShellIcons.Avalonia; do + dotnet pack "src/$project/$project.csproj" --configuration Release --no-build --output nupkgs + done env: ContinuousIntegrationBuild: true @@ -85,7 +101,7 @@ jobs: if: github.event_name == 'push' || inputs.dry_run == false run: | version=$(grep -oE '<Version>[^<]+' Directory.Build.props | head -1 | sed 's/<Version>//') - for pkg in ShellIcons.Blazor; do + for pkg in ShellIcons.Blazor ShellIcons.Maui ShellIcons.Avalonia; do lower=$(echo "$pkg" | tr '[:upper:]' '[:lower:]') existing=$(curl -sf "https://api.nuget.org/v3-flatcontainer/${lower}/index.json" 2>/dev/null | grep -oE '"[^"]*"' | tr -d '"' || echo "") if echo "$existing" | grep -Fxq "$version"; then diff --git a/RELEASING.md b/RELEASING.md index 0215c63..53e6c37 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,10 +1,10 @@ # Releasing ShellIcons -How to ship a new version of `ShellIcons.Blazor` to NuGet.org and create the matching GitHub Release. +How to ship a new version of `ShellIcons.Blazor`, `ShellIcons.Maui` and `ShellIcons.Avalonia` to NuGet.org and create the matching GitHub Release. **Who can release:** anyone who can approve the `release` environment on GitHub. Contributors without that access open a PR; a maintainer does the release. -**What's released:** only `ShellIcons.Blazor`. `ShellIcons.Maui` and `ShellIcons.Avalonia` are in preview — `release.yml` doesn't pack them yet. Publishing MAUI will need a Windows or macOS job with the MAUI workload; Avalonia packs on ubuntu, so it only needs adding to the existing pack step and the "already published" check. The docs site isn't part of a release either: it deploys to [shellicons.shellui.dev](https://shellicons.shellui.dev) on every push to `main` (`docs.yml`). +**What's released:** all three packages, at the one version in `Directory.Build.props`. `release.yml` runs on `windows-latest` and installs the MAUI workload, because `ShellIcons.Maui` builds for Android, iOS, Mac Catalyst and Windows. The docs site isn't part of a release either: it deploys to [shellicons.shellui.dev](https://shellicons.shellui.dev) on every push to `main` (`docs.yml`). Publishing uses **NuGet Trusted Publishing** — GitHub Actions authenticates via OIDC and gets a short-lived (~1 hour) API key per run. No long-lived key lives in the repo. @@ -91,10 +91,13 @@ Merge it to `main`. ```bash dotnet test ShellIcons.slnx --configuration Release +dotnet test tests/ShellIcons.Maui.Tests --configuration Release dotnet pack src/ShellIcons.Blazor/ShellIcons.Blazor.csproj --configuration Release --output nupkgs +dotnet pack src/ShellIcons.Maui/ShellIcons.Maui.csproj --configuration Release --output nupkgs +dotnet pack src/ShellIcons.Avalonia/ShellIcons.Avalonia.csproj --configuration Release --output nupkgs ``` -Should produce `nupkgs/ShellIcons.Blazor.<version>.nupkg` and `.snupkg`. +Should produce a `.nupkg` and `.snupkg` for each package. The MAUI steps need `dotnet workload install maui` (Windows or macOS). ### 4. Tag the release @@ -114,7 +117,7 @@ Pushing the tag triggers `.github/workflows/release.yml`. Because of the `releas ### 6. Verify -- **NuGet.org**: <https://www.nuget.org/packages/ShellIcons.Blazor> — should show the new version within a couple of minutes (indexing) and be installable after ~10 minutes. +- **NuGet.org**: [ShellIcons.Blazor](https://www.nuget.org/packages/ShellIcons.Blazor), [ShellIcons.Maui](https://www.nuget.org/packages/ShellIcons.Maui), [ShellIcons.Avalonia](https://www.nuget.org/packages/ShellIcons.Avalonia) — each should show the new version within a couple of minutes (indexing) and be installable after ~10 minutes. - **GitHub Release**: <https://github.com/shellui-dev/shell-icons/releases> — auto-generated release notes based on merged PRs since the last tag, with `.nupkg` + `.snupkg` attached. ## What the workflow does, step by step @@ -123,13 +126,16 @@ Pushing the tag triggers `.github/workflows/release.yml`. Because of the `releas push tag v<version> │ ▼ - [checkout with full history] + [checkout with full history, windows-latest] │ ▼ - [restore + build (Release) + test] + [install MAUI workload] │ ▼ - [dotnet pack → nupkgs/ShellIcons.Blazor.<v>.nupkg + .snupkg] + [restore + build (Release) + test: ShellIcons.slnx, ShellIcons.Maui.slnx] + │ + ▼ + [dotnet pack → nupkgs/ShellIcons.{Blazor,Maui,Avalonia}.<v>.nupkg + .snupkg] │ ▼ [verify tag == Directory.Build.props version] ← fails if mismatch From 4107047f584d484dc8c8c380e4e7cf7b34b17c3c Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi <shewatipa@chips.co.zw> Date: Thu, 22 Oct 2026 16:50:20 +0200 Subject: [PATCH 5/6] docs(site): install MAUI and Avalonia from NuGet, home page for 0.2.0 The getting-started pages install the packages instead of referencing the projects. The home page covers all three targets and shows 0.2.0-alpha. --- .../Components/Pages/Home.razor | 19 +++++++++++-------- .../content/docs/avalonia/getting-started.md | 10 ++++------ .../content/docs/maui/getting-started.md | 10 ++++------ 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/ShellIcons.Docs/Components/Pages/Home.razor b/docs/ShellIcons.Docs/Components/Pages/Home.razor index b2fb897..cc834e9 100644 --- a/docs/ShellIcons.Docs/Components/Pages/Home.razor +++ b/docs/ShellIcons.Docs/Components/Pages/Home.razor @@ -6,22 +6,22 @@ @using ShellIcons @using ShellIcons.Icons -<PageTitle>ShellIcons — Lucide icons for Blazor</PageTitle> +<PageTitle>ShellIcons — Lucide icons for Blazor, MAUI and Avalonia</PageTitle> <div class="hero"> <div class="hero-badge"> - <span>v0.1.0-alpha</span> + <span>v0.2.0-alpha</span> <span class="dot">·</span> <span>@ShellIcon.Names.Count icons</span> <span class="dot">·</span> - <span>net8, net9, net10</span> + <span>Blazor, MAUI, Avalonia</span> </div> <h1> - <span class="grad">Lucide</span> icons for <span class="grad">Blazor</span>. + <span class="grad">Lucide</span> icons for <span class="grad">.NET</span>. </h1> <p class="hero-tagline"> - 1,555 tree-shakeable, typed SVG icons. Zero JS, zero required CSS. - One <code>dotnet add</code> away. + 1,555 typed, tree-shakeable icons for Blazor, .NET MAUI and Avalonia. + Inline SVG on the web, native shapes on desktop and mobile. One <code>dotnet add</code> away. </p> <div class="hero-cta"> @@ -37,7 +37,10 @@ <pre class="install"><code>dotnet add package ShellIcons.Blazor</code></pre> - <p class="hero-also">Building with .NET MAUI? <a href="/docs/maui/getting-started">ShellIcons.Maui is in preview →</a></p> + <p class="hero-also"> + Building native apps? <a href="/docs/maui/getting-started">ShellIcons.Maui</a> and + <a href="/docs/avalonia/getting-started">ShellIcons.Avalonia</a> are in preview → + </p> </div> <div class="showcase"> @@ -84,7 +87,7 @@ </div> </div> -<DocsFooter Version="0.1.0-alpha" /> +<DocsFooter Version="0.2.0-alpha" /> <style> .hero { diff --git a/docs/ShellIcons.Docs/content/docs/avalonia/getting-started.md b/docs/ShellIcons.Docs/content/docs/avalonia/getting-started.md index ebdbb60..f8f6cfd 100644 --- a/docs/ShellIcons.Docs/content/docs/avalonia/getting-started.md +++ b/docs/ShellIcons.Docs/content/docs/avalonia/getting-started.md @@ -8,16 +8,14 @@ order: 1 The same Lucide catalog as the Blazor and MAUI packages, drawn directly by Avalonia. The SVG is converted at build time, so nothing parses SVG in your app. -<Callout Variant="warning" Title="Preview — not on NuGet yet"> -ShellIcons.Avalonia is covered by tests that render every icon through Avalonia's Skia backend, but it hasn't been run in a real desktop window yet. Reference the project from source until it's published. +<Callout Variant="warning" Title="Preview"> +ShellIcons.Avalonia is covered by tests that render every icon through Avalonia's Skia backend and checked in a desktop app, but the API may still change before 1.0. </Callout> ## Install -Until the package is published, reference the project. No extra workloads are needed. - -```xml -<ProjectReference Include="path/to/shell-icons/src/ShellIcons.Avalonia/ShellIcons.Avalonia.csproj" /> +```bash +dotnet add package ShellIcons.Avalonia --prerelease ``` Targets `net8.0` and Avalonia 12.1. diff --git a/docs/ShellIcons.Docs/content/docs/maui/getting-started.md b/docs/ShellIcons.Docs/content/docs/maui/getting-started.md index b7783fe..19e284c 100644 --- a/docs/ShellIcons.Docs/content/docs/maui/getting-started.md +++ b/docs/ShellIcons.Docs/content/docs/maui/getting-started.md @@ -8,16 +8,14 @@ order: 1 The same Lucide catalog as the Blazor package, drawn as native MAUI `Path` shapes. The SVG is converted at build time, so nothing parses SVG on the device. -<Callout Variant="warning" Title="Preview — not on NuGet yet"> -ShellIcons.Maui builds for Android, iOS, Mac Catalyst and Windows and is covered by tests, but it hasn't been checked visually on Android or iOS devices yet. Reference the project from source until it's published. +<Callout Variant="warning" Title="Preview"> +ShellIcons.Maui builds for Android, iOS, Mac Catalyst and Windows, is covered by tests and has been checked in a Windows app. It hasn't been checked on Android or iOS devices yet, and the API may still change before 1.0. </Callout> ## Install -Until the package is published, reference the project. You need the MAUI workloads installed (`dotnet workload install maui`). - -```xml -<ProjectReference Include="path/to/shell-icons/src/ShellIcons.Maui/ShellIcons.Maui.csproj" /> +```bash +dotnet add package ShellIcons.Maui --prerelease ``` Supported targets: `net10.0-android`, `net10.0-ios`, `net10.0-maccatalyst`, `net10.0-windows10.0.19041.0`. From ff28b24abf2303eb93e091ece5f5c8102bcf9ae2 Mon Sep 17 00:00:00 2001 From: Shewatipa Tseisi <shewatipa@chips.co.zw> Date: Thu, 22 Oct 2026 16:50:22 +0200 Subject: [PATCH 6/6] chore(release): v0.2.0-alpha --- CHANGELOG.md | 15 +++++++++++---- Directory.Build.props | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aba6e08..af6f6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,30 @@ # Changelog -All notable changes to the ShellIcons packages. `ShellIcons.Blazor` is published on NuGet; `ShellIcons.Maui` and `ShellIcons.Avalonia` are in preview and not published yet. +All notable changes to the ShellIcons packages: `ShellIcons.Blazor`, `ShellIcons.Maui` and `ShellIcons.Avalonia` (the last two in preview). Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [SemVer](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.2.0-alpha] — 2026-10-22 + +First release of `ShellIcons.Maui` and `ShellIcons.Avalonia` on NuGet, next to `ShellIcons.Blazor`. All three share one version and carry the ShellIcons mark as their package icon. + ### Added - **Blazor: `{Name}Icon` components** — `<ChevronRightIcon />`, `<ZapIcon />`, … in the root `ShellIcons` namespace. A single `@using ShellIcons` now works next to UI kits whose component names match Lucide icons (`Badge`, `Table`, `Router`, …), which previously failed with RZ9985. This is the recommended form for markup. Lucide's `shell` icon is the one exception, since `ShellIcon` is the dispatcher; use `Icon.Shell()` (build info `SHELLICONS003`). - **Blazor: `Icon.*` factory** — `Icon.ChevronRight()` returns a `RenderFragment`, for icons as values: component parameters and lists built in C#. Unused icons are still trimmed. - **Blazor: clear error for `@onclick` on an icon.** On a component, Razor passes `@onclick="…"` as a plain string, which broke rendering in the browser with a cryptic error. It now throws an `InvalidOperationException` that explains the forms that work. -- **`ShellIcons.Maui` (preview, not yet on NuGet)** — the same catalog as native MAUI `Path` shapes: typed controls (`<icons:ChevronRight />`), an `IconName` dispatcher (`<icons:Icon Name="Search" />`), and `IconCatalog` with tags, categories, alias-aware `TryParse` and `Search`. Builds for Android, iOS, Mac Catalyst and Windows. -- **`ShellIcons.Avalonia` (preview, not yet on NuGet)** — the same catalog for Avalonia 12 (`net8.0`): typed controls (`<icons:ChevronRight />`), a dispatcher (`<icons:Icon Kind="Search" />`) and the same `IconName` / `IconCatalog` API as the MAUI package. Icons inherit `Foreground`, so they take the color of the surrounding text. +- **`ShellIcons.Maui` (preview)** — the same catalog as native MAUI `Path` shapes: typed controls (`<icons:ChevronRight />`), an `IconName` dispatcher (`<icons:Icon Name="Search" />`), and `IconCatalog` with tags, categories, alias-aware `TryParse` and `Search`. Builds for Android, iOS, Mac Catalyst and Windows. +- **`ShellIcons.Avalonia` (preview)** — the same catalog for Avalonia 12 (`net8.0`): typed controls (`<icons:ChevronRight />`), a dispatcher (`<icons:Icon Kind="Search" />`) and the same `IconName` / `IconCatalog` API as the MAUI package. Icons inherit `Foreground`, so they take the color of the surrounding text. - Build diagnostics for custom icons that can't be converted for MAUI or Avalonia: `SHELLICONS002` (unsupported SVG such as `<g>` or `transform`), `SHELLICONS004` (name not usable as an enum member), `SHELLICONS005` (shape outside the viewBox, dropped). -- Docs site at [shellicons.shellui.dev](https://shellicons.shellui.dev) (deployed from `main`), with a searchable icon browser and a MAUI section. +- Docs site at [shellicons.shellui.dev](https://shellicons.shellui.dev) (deployed from `main`), with a searchable icon browser, MAUI and Avalonia sections, and docs for AI agents (`/llms.txt`). ### Changed +- Each package ships its own readme on NuGet, in plain Markdown so nuget.org renders it; the root `README.md` is for GitHub. +- The release workflow runs on Windows with the MAUI workload and publishes all three packages. + - `Icon.*` calls with no arguments reuse a cached fragment instead of allocating on every render. `ShellIcons.Blazor.dll` is about a third smaller than the first factory version, even with the new `{Name}Icon` components. - Components in `ShellIcons.Icons` are no longer `sealed`. - The build no longer references `Microsoft.SourceLink.GitHub`, whose `Microsoft.Build.Tasks.Git 8.0.0` dependency has a known vulnerability (NU1902). Source Link now comes from the .NET SDK; packages still link to the exact commit. diff --git a/Directory.Build.props b/Directory.Build.props index 6486b88..73c0e72 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ <!-- Version. Bump here before tagging a release. See RELEASING.md. --> <PropertyGroup> - <Version>0.1.0-alpha</Version> + <Version>0.2.0-alpha</Version> </PropertyGroup> <PropertyGroup>