Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<Project>
<!-- Centralized ShellUI Native Version - Update this single file to version all components -->
<PropertyGroup>
<ShellUINativeVersion>0.1.0</ShellUINativeVersion>
<ShellUINativeVersionSuffix>alpha.2</ShellUINativeVersionSuffix>
<ShellUINativeVersion>0.2.0</ShellUINativeVersion>
<ShellUINativeVersionSuffix>beta.1</ShellUINativeVersionSuffix>
</PropertyGroup>

<!-- Common properties for all ShellUI Native projects -->
Expand Down
4 changes: 2 additions & 2 deletions docs/COMPONENTS_ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Prioritized list of components to create for ShellUI Native, aligned with [ShellUI Components](https://github.com/shellui/shell-ui) patterns. All components should follow **compositional patterns** using Dependencies (parent + sub-components) instead of monolithic ChildContent.

Last revised: **2026-10-11** (second prerelease, `0.1.0-alpha.2`).
Last revised: **2026-10-15** (`0.2.0-beta.1`).

**Status at a glance:** P0–P7 ✅ done · `0.1.0-alpha.2`: 63 families, 94 CLI targets, each for MAUI and Avalonia · since then: button-group, input-group, menubar, command, navigation-menu, data-table, chat, form, image-viewer, prev-next-nav, charts.
**Status at a glance:** P0–P7 ✅ done · `0.2.0-beta.1`: 81 families, 112 CLI targets, each for MAUI and Avalonia (`0.1.0-alpha.2` had 63 / 94; added since: button-group, input-group, menubar, command, navigation-menu, data-table, chat, form, image-viewer, prev-next-nav and the charts).

---

Expand Down
28 changes: 26 additions & 2 deletions docs/DEVELOPMENT_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ main ← Phase 1 (PRs #1–#5), release 0.1.0-alpha.1 (PR #6), Phase 2 Avaloni

| Branch | Base | Status | Purpose |
|--------|------|--------|---------|
| `main` | — | Phase 1, Phase 2 and PR #14 merged; released as `0.1.0-alpha.2`; PRs #17–#23 merged since (charts in #23) | 114 registry entries (111 CLI targets, 80 families), each with MAUI and Avalonia content; 115 / 112 / 81 with `donut-chart` on `fix/pre-release-issues` |
| `fix/pre-release-issues` | `main` | **Active** | Issues before `0.2.0-alpha.1`: change events report user input only (#24), sheet `MaxPanelWidth` (#26), `ShellLabel` alignment and line break (#27), native components beside ShellUI Blazor (#28), `donut-chart` as its own component (as in ShellUI), did-you-mean, a dependency audit test |
| `main` | — | Released as `0.1.0-alpha.2`; PRs #17–#23 and #31 merged since | 115 registry entries (112 CLI targets, 81 families), each with MAUI and Avalonia content |
| `chore/release-v0.2.0-beta.1` | `main` | **Active** | Version `0.2.0-beta.1`, release notes (change events report user input only, upgrade steps), docs counts, fresh-app checks |

---

Expand Down Expand Up @@ -438,6 +438,30 @@ After merge: tag `v0.1.0-alpha.1` on `main` and push the tag.

---

## Release `0.2.0-beta.1` — `chore/release-v0.2.0-beta.1` (active)

First beta of `ShellUI.Native.CLI`: 18 new components since `0.1.0-alpha.2` (button-group,
input-group, menubar, command, navigation-menu, data-table, chat, form, image-viewer,
prev-next-nav and the charts), the pre-release fixes from PR #31, and change events that report
user input only.

- [x] `Directory.Build.props` → `0.2.0` + `beta.1`; README badge (`prepare-release.ps1`)
- [x] [RELEASE_NOTES.md](./RELEASE_NOTES.md) section: new components, the change-events rule,
CLI and theme additions, upgrade steps (`update shell`, `update button`, `update --all`)
- [x] Counts in the roadmap and PLAN.md: 81 families, 112 CLI targets
- [x] Each new component, plus `sheet` and `label`, alone in a fresh Avalonia and a fresh `dotnet new
maui` app (Windows target), and all 115 entries together in one app per platform: 0 warnings
- [x] An app set up with `0.1.0-alpha.2` (`init`, button, calendar, card, switch) takes the new
components after `update shell button`, or `update --all`, and builds with 0 warnings. This found
that `update` skipped `shell` (init never recorded it); fixed on this branch
- [x] The packed tool, installed from the `.nupkg`, outside the repo: `--version` is
`0.2.0-beta.1`, `list` shows `donut-chart`, and a fresh Avalonia and MAUI app with donut-chart,
data-table, command, button-group and switch build with 0 warnings

After merge: tag `v0.2.0-beta.1` on `main` and push the tag.

---

## Release `0.1.0-alpha.2` — `chore/release-v0.1.0-alpha.2` (**released**; tag `v0.1.0-alpha.2`)

Second NuGet prerelease of `ShellUI.Native.CLI`: the first with Avalonia templates, plus
Expand Down
10 changes: 5 additions & 5 deletions docs/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ ShellUI Native brings the same beautiful, accessible, and customizable component
> target. See [DEVELOPMENT_PLAN.md](./DEVELOPMENT_PLAN.md) for the branch/phase breakdown.

### Phase 1: MAUI (Primary Focus)
- **Target:** .NET MAUI (iOS, Android, Windows, macOS) on .NET 10
- **Components:** 50+ native controls
- **Status:** Released since `0.1.0-alpha.1`; `0.1.0-alpha.2` has 63 component families
(94 CLI targets), P0–P6 complete; remaining work tracked in
- **Target:** .NET MAUI (Android, iOS, Mac Catalyst, Windows) and Avalonia 12 (Windows, macOS, Linux) on .NET 10
- **Components:** 81 families, 112 CLI targets, each for MAUI and Avalonia
- **Status:** Released since `0.1.0-alpha.1`; `0.2.0-beta.1` has 81 component families
(112 CLI targets), P0–P7 complete; remaining work tracked in
[COMPONENTS_ROADMAP.md](./COMPONENTS_ROADMAP.md).

### Phase 2: Avalonia UI
Expand Down Expand Up @@ -147,7 +147,7 @@ dotnet tool install -g ShellUI.Native.CLI --prerelease
```bash
shellui-native init # Initialize project (creates shellui-native.json)
shellui-native add # Add components (copies to Components/UI/)
shellui-native list # List available components (94 targets)
shellui-native list # List available components (112 targets)
shellui-native remove # Remove components
shellui-native update # Update components to latest version
```
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</p>

<p align="center">
<a href="https://www.nuget.org/packages/ShellUI.Native.CLI"><img src="https://img.shields.io/badge/CLI-v0.1.0--alpha.2-004880?style=flat-square&logo=nuget" alt="ShellUI.Native.CLI v0.1.0-alpha.2 on NuGet"></a>
<a href="https://www.nuget.org/packages/ShellUI.Native.CLI"><img src="https://img.shields.io/badge/CLI-v0.2.0--beta.1-004880?style=flat-square&logo=nuget" alt="ShellUI.Native.CLI v0.2.0-beta.1 on NuGet"></a>
<a href="../LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT license"></a>
</p>

Expand Down
78 changes: 78 additions & 0 deletions docs/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,83 @@
# ShellUI Native Release Notes

# ShellUI Native v0.2.0-beta.1 🧪

> The first beta: 18 new components, among them charts, a data table and a command palette, each for .NET MAUI and Avalonia. Change events now report user input only, so read **Upgrading** before you update an existing app. Still a prerelease, so install with `--prerelease`. Report issues via [GitHub Issues](https://github.com/shellui-dev/shellui-native/issues).

## 📦 Install or update

```bash
dotnet tool update -g ShellUI.Native.CLI --prerelease

cd YourApp
shellui-native init --yes
shellui-native add button input card
```

Requires the .NET 10 SDK and a .NET MAUI or Avalonia 12 project. `init` detects which one it is.

## ✨ New components

For MAUI and Avalonia. 81 component families, 112 CLI targets in total.

| Component | What it is |
|---|---|
| `button-group` | Buttons joined into one control. |
| `input-group` | An input with an icon, text or view before or after it. |
| `menubar` | A row of menus that open under their titles. |
| `command` | A searchable list of grouped commands, inline or as a command palette. |
| `navigation-menu` | A row of top-level links that open panels of links. |
| `data-table` | A table filled from a list, with filter, sorting, selection and pages. |
| `chat` | A message list with bubbles, a typing indicator and a message box. |
| `form` | Fields with a label, description and error message. |
| `image-viewer` | A thumbnail that opens the image over the page, with zoom. |
| `prev-next-nav` | Previous and next page links for the foot of a page. |
| `bar-chart`, `line-chart`, `area-chart`, `pie-chart`, `donut-chart`, `radar-chart`, `radial-chart` | Charts drawn by the components themselves, with no charting package: grouped, stacked or horizontal bars, smooth or straight lines, stacked areas, pies and donuts, radar shapes and progress rings. Each brings the shared `chart` core (series, axes, legend, tooltip, grow-in animation). The names are the same as in ShellUI for Blazor. |

## ⚠️ Change events report user input only

`Toggled`, `CheckedChanged`, `ValueChanged`, `SelectedIndexChanged`, `SelectedValueChanged`, `PageChanged`, `DateChanged`, `TimeChanged`, `ItemToggled` and the other value and selection events now fire only when the user changes the value (a click, tap, swipe or key), like Radix's `onValueChange`. Setting the property from code or a binding updates the control without raising them, so filling a form from data or syncing a sidebar with the router no longer looks like user input. To see every change, bind the property or watch it (`PropertyChanged` on MAUI, `GetObservable` on Avalonia).

Methods meant for code are silent too: `MultiSelect.Toggle`, `TagInput.Add` / `Remove`, `DataTable.ClearSelection`, `DateRangePicker.Clear`, `Stepper.Next` / `Previous`, `Carousel.Next` / `Previous` and its auto-play, `Accordion.Toggle`, `Collapsible.Toggle`.

Unchanged, still reporting every change: text fields' `TextChanged`, overlays' `OpenChanged` / `IsOpenChanged`, `ThemeChanged`, `FileUpload.FilesChanged` and `Sidebar.IsCollapsedChanged`.

## 🧰 CLI

- In a project that also uses ShellUI for Blazor (it has `shellui.json`), `init` puts the native components in `Components/Native/` with the namespace `YourApp.Components.Native`, so the two sets don't clash. The namespace now follows the `ComponentsPath` setting; the default `Components/UI` gives the same code as before. See [BLAZOR_HYBRID.md](https://github.com/shellui-dev/shellui-native/blob/main/docs/BLAZOR_HYBRID.md).
- An unknown name gets suggestions: `shellui-native add donut` answers "Did you mean 'donut-chart'?".
- `update shell` and `update --all` include the `shell` utility. `init` used to write `Shell.cs` without recording it, so `update` skipped it; projects set up by an older version are picked up from the file.

## 🎨 Theme

- Five chart colors, `Chart1` to `Chart5`, in both themes, converted from ShellUI's `--chart-1` to `--chart-5`.

## 🛠️ Fixes and additions

- `shell` builds in a MAUI Blazor Hybrid app; the app's `Components.Layout` namespace used to hide MAUI's `Layout` type (CS0118).
- `SheetContent.MaxPanelWidth` sets how wide a left or right sheet gets (384 by default, never more than 75% of the window).
- MAUI `ShellLabel` gains `HorizontalTextAlignment` and `LineBreakMode` (Avalonia's is a `TextBlock` and already has them).

## ⬆️ Upgrading from alpha.2

Installing the new tool doesn't change the components already in your project. `update` rewrites a component's file, so reapply any edits you made to it.

- Before adding a chart, run `shellui-native update shell` (it holds the new chart colors and the Blazor Hybrid fix).
- Before adding `button-group`, run `shellui-native update button`.
- Your existing components keep their old event behaviour until you update them. To move everything at once, run `shellui-native update --all`; check handlers that relied on code-set values raising events.
- Coming from alpha.1, also run `shellui-native update calendar` before adding `date-range-picker`.

## ⚠️ Known limitations

- The components were checked on Windows, for MAUI and Avalonia. CI builds the Avalonia demo on Linux, Windows and macOS, but it hasn't been run on macOS or Linux, or with touch. The new components build for Android, iOS and Mac Catalyst but haven't been run there.
- On Windows, MAUI buttons and sidebar items can't be pressed through UI Automation (Narrator, test tools), [#25](https://github.com/shellui-dev/shellui-native/issues/25).
- `file-upload`: on MAUI, dropping files works on Windows only. The platform file dialog and file drop have not been tested beyond the build on either platform.
- iOS and Mac Catalyst build but have not been run on a device; Escape does not close overlays on Mac Catalyst yet.
- Very large MAUI pages on Windows can hit WinUI's layout-pass limit (`Layout cycle detected`). Split the page or keep sections hidden until needed; see [COMPONENTS.md](https://github.com/shellui-dev/shellui-native/blob/main/docs/COMPONENTS.md#long-pages-on-windows).
- The docs site (native.shellui.dev) is not live yet; see [COMPONENTS.md](https://github.com/shellui-dev/shellui-native/blob/main/docs/COMPONENTS.md) for usage.

---

# ShellUI Native v0.1.0-alpha.2 🧪

> Avalonia support and five new components. Every component now has a .NET MAUI and an Avalonia version, installed by the same commands. Still a prerelease, so install with `--prerelease`. Report issues via [GitHub Issues](https://github.com/shellui-dev/shellui-native/issues).
Expand Down
21 changes: 19 additions & 2 deletions src/ShellUI.Native.CLI/Services/ComponentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ public static void UpdateComponents(string[] components, bool all)

if (all || components.Length == 0)
{
componentsToUpdate = config.InstalledComponents.Select(c => c.Name);
var names = config.InstalledComponents.Select(c => c.Name).ToList();
// Before 0.2.0, init wrote Shell.cs without recording it; update it first, like a new project.
if (!names.Contains("shell") && ComponentRegistry.GetMetadata("shell") is { } shell &&
File.Exists(Path.Combine(Directory.GetCurrentDirectory(), config.ComponentsPath, shell.FilePath)))
names.Insert(0, "shell");
componentsToUpdate = names;
}
else
{
Expand All @@ -161,7 +166,7 @@ public static void UpdateComponents(string[] components, bool all)
continue;
}

var installed = config.InstalledComponents.FirstOrDefault(c => c.Name == componentName);
var installed = FindInstalled(config, componentName, metadata);
if (installed == null)
{
AnsiConsole.MarkupLine($"[yellow]Component '{componentName}' is not installed[/]");
Expand Down Expand Up @@ -204,4 +209,16 @@ public static void UpdateComponents(string[] components, bool all)

AnsiConsole.MarkupLine($"\n[dim]Updated {updatedCount} component(s)[/]");
}

// The config entry for an installed component. A file an older CLI wrote without recording it
// (init's Shell.cs before 0.2.0) counts as installed at an unknown version, so it gets updated.
private static InstalledComponent? FindInstalled(ShellUINativeConfig config, string name, ComponentMetadata metadata)
{
var installed = config.InstalledComponents.FirstOrDefault(c => c.Name == name);
if (installed != null) return installed;
if (!File.Exists(Path.Combine(Directory.GetCurrentDirectory(), config.ComponentsPath, metadata.FilePath))) return null;
installed = new InstalledComponent { Name = name, Version = "unknown", Platform = config.TargetPlatform };
config.InstalledComponents.Add(installed);
return installed;
}
}
18 changes: 14 additions & 4 deletions src/ShellUI.Native.CLI/Services/InitService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ await LogoLoader.RunAsync("Setting up ShellUI Native...", async status =>

// Install Shell utilities
status("Installing Shell utilities...");
await InstallShellUtilityAsync(projectInfo, componentsPath, componentsFolder);
var shellInstalled = await InstallShellUtilityAsync(projectInfo, componentsPath, componentsFolder);

// Create configuration file
status("Creating configuration...");
Expand All @@ -91,6 +91,15 @@ await LogoLoader.RunAsync("Setting up ShellUI Native...", async status =>
}
};

// Recorded like any component, so `update shell` and `update --all` include it.
if (shellInstalled)
config.InstalledComponents.Add(new InstalledComponent
{
Name = "shell",
Version = ComponentRegistry.GetMetadata("shell")!.Version,
Platform = projectInfo.Platform
});

var json = JsonSerializer.Serialize(config, new JsonSerializerOptions
{
WriteIndented = true
Expand Down Expand Up @@ -121,24 +130,25 @@ await LogoLoader.RunAsync("Setting up ShellUI Native...", async status =>
}
}

private static async Task InstallShellUtilityAsync(ProjectInfo projectInfo, string componentsPath, string componentsFolder)
private static async Task<bool> InstallShellUtilityAsync(ProjectInfo projectInfo, string componentsPath, string componentsFolder)
{
if (!ComponentRegistry.SupportsPlatform("shell", projectInfo.Platform))
{
AnsiConsole.MarkupLine(
$"[yellow]Shell utility has no template for {projectInfo.Platform} yet — skipping.[/] " +
$"[dim]Add components once per-platform templates land.[/]");
return;
return false;
}

var content = ComponentRegistry.GetComponentContent("shell", projectInfo.Platform);
if (content == null) return;
if (content == null) return false;

content = TemplateText.Apply(content, projectInfo.RootNamespace, componentsFolder);

var filePath = Path.Combine(componentsPath, "Shell.cs");
await File.WriteAllTextAsync(filePath, content);
AnsiConsole.MarkupLine($"[green]✓ Installed:[/] Shell.cs");
return true;
}

private static async Task CreateThemeResourcesAsync()
Expand Down
4 changes: 2 additions & 2 deletions src/ShellUI.Native.Templates/Templates/PieChartTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ public static class PieChartTemplate
{
Name = "pie-chart",
DisplayName = "Pie Chart",
Description = "Pie and donut charts",
Description = "Slices of a whole, clockwise from twelve o'clock",
Category = ComponentCategory.DataDisplay,
FilePath = "PieChart.cs",
Dependencies = new List<string> { "shell", "chart" },
Tags = new List<string> { "chart", "pie", "donut", "share" }
Tags = new List<string> { "chart", "pie", "share" }
};

public static IReadOnlyDictionary<NativePlatform, string> Contents { get; } = new Dictionary<NativePlatform, string>
Expand Down
Loading