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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project>
<!-- One version for every ShellUI package -->
<PropertyGroup>
<ShellUIVersion>0.3.2</ShellUIVersion>
<ShellUIVersion>0.3.3</ShellUIVersion>
<ShellUIVersionSuffix></ShellUIVersionSuffix>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

| Channel | Version | Notes |
|---|---|---|
| Latest stable (recommended) | `0.3.2` | Tailwind CSS `4.3.2` |
| Latest stable (recommended) | `0.3.3` | Tailwind CSS `4.3.2` |
| Previous stable | `0.2.1` | Superseded by `0.3.0` |

The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10. The `ShellUI.Components` NuGet package targets .NET 10 only; on .NET 8 or 9, use the CLI.
Expand Down
65 changes: 65 additions & 0 deletions ShellUI.Tests/PackageBehaviorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Bunit;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using ShellUI.Components;
using ShellUI.Components.Models;

namespace ShellUI.Tests;

public class PackageBehaviorTests
{
[Fact]
public void Command_RunsTheItemActionThenRaisesCommandSelected()
{
using var ctx = new BunitContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
var calls = new List<string>();
var item = new CommandItem { Title = "Go home", Action = () => { calls.Add("action"); return Task.CompletedTask; } };

var command = ctx.Render<Command>(p => p
.Add(c => c.IsOpen, true)
.Add(c => c.Commands, new List<CommandItem> { item })
.Add(c => c.CommandSelected, EventCallback.Factory.Create<CommandItem>(this, _ => calls.Add("selected"))));

command.FindAll("button").First(b => b.TextContent.Contains("Go home")).Click();

Assert.Equal(new[] { "action", "selected" }, calls);
}

[Fact]
public void Popover_ClosesOnEscape()
{
using var ctx = new BunitContext();
bool? open = null;

var popover = ctx.Render<Popover>(p => p
.Add(c => c.IsOpen, true)
.Add(c => c.IsOpenChanged, EventCallback.Factory.Create<bool>(this, v => open = v))
.Add(c => c.Trigger, "Open")
.Add(c => c.ChildContent, "Body"));

popover.Find("div.relative").KeyDown(new KeyboardEventArgs { Key = "Escape" });

Assert.False(open);
Assert.DoesNotContain("Body", popover.Markup);
}

[Fact]
public void Dropdown_ClosesOnEscapeButNotOnOtherKeys()
{
using var ctx = new BunitContext();
var changes = new List<bool>();

var dropdown = ctx.Render<Dropdown>(p => p
.Add(c => c.IsOpen, true)
.Add(c => c.IsOpenChanged, EventCallback.Factory.Create<bool>(this, v => changes.Add(v)))
.Add(c => c.Trigger, "Menu")
.Add(c => c.ChildContent, "Item"));

dropdown.Find("div.relative").KeyDown(new KeyboardEventArgs { Key = "a" });
Assert.Empty(changes);

dropdown.Find("div.relative").KeyDown(new KeyboardEventArgs { Key = "Escape" });
Assert.Equal(new[] { false }, changes);
}
}
10 changes: 5 additions & 5 deletions VERSIONING_STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ShellUI uses one centralized version for the source templates, CLI tool, and pac

| Scope | Value |
|---|---|
| Version | `0.3.2` |
| Version | `0.3.3` |
| Target framework | .NET 10 |
| Tailwind version | `4.3.2` |

Expand All @@ -18,15 +18,15 @@ The root `Directory.Build.props` supplies the version:

```xml
<PropertyGroup>
<ShellUIVersion>0.3.2</ShellUIVersion>
<ShellUIVersion>0.3.3</ShellUIVersion>
<ShellUIVersionSuffix></ShellUIVersionSuffix>
</PropertyGroup>
```

`Directory.Build.props` composes the package and assembly metadata:

- `Version` is `0.3.2`; with a suffix such as `rc.1` it becomes `0.3.2-rc.1`.
- `AssemblyVersion` and `FileVersion` use the numeric base `0.3.2`.
- `Version` is `0.3.3`; with a suffix such as `rc.1` it becomes `0.3.3-rc.1`.
- `AssemblyVersion` and `FileVersion` use the numeric base `0.3.3`.
- `InformationalVersion` includes the prerelease suffix.
- Component metadata reads the centralized properties when running from the repository. `VersionHelper` uses assembly metadata as a fallback when its repository search does not find the solution file.

Expand Down Expand Up @@ -57,7 +57,7 @@ The CLI writes the computed version into `shellui.json` for each installed entry
"InstalledComponents": [
{
"Name": "button",
"Version": "0.3.2",
"Version": "0.3.3",
"InstalledAt": "2026-01-01T00:00:00Z",
"IsCustomized": false
}
Expand Down
4 changes: 2 additions & 2 deletions docs/CLI_INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Context | Version |
|---|---|
| Latest stable (recommended) | `0.3.2`, needs the .NET 10 runtime |
| Latest stable (recommended) | `0.3.3`, needs the .NET 10 runtime |
| Previous stable | `0.2.1` |
| Tailwind | `4.3.2` |

Expand Down Expand Up @@ -43,7 +43,7 @@ The manifest is `.config/dotnet-tools.json` and uses the installed package versi
"isRoot": true,
"tools": {
"shellui.cli": {
"version": "0.3.2",
"version": "0.3.3",
"commands": ["shellui"]
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/CLI_SYNTAX.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ShellUI CLI Syntax

This reference covers CLI `0.3.2`: it runs on .NET 10, sets up Blazor projects on .NET 8, 9 and 10, uses Tailwind CSS `4.3.2`, and has and 76 direct component targets. Older versions, such as `0.2.1`, lack some of these commands.
This reference covers CLI `0.3.3`: it runs on .NET 10, sets up Blazor projects on .NET 8, 9 and 10, uses Tailwind CSS `4.3.2`, and has and 76 direct component targets. Older versions, such as `0.2.1`, lack some of these commands.

## Command prefix

Expand Down Expand Up @@ -205,7 +205,7 @@ Representative fields look like this:
"InstalledComponents": [
{
"Name": "button",
"Version": "0.3.2",
"Version": "0.3.3",
"InstalledAt": "2026-01-01T00:00:00Z",
"IsCustomized": false
}
Expand Down
4 changes: 2 additions & 2 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Answers for the current ShellUI source and the currently published packages.

### Which version should I use?

Use `0.3.2`. It uses Tailwind CSS `4.3.2` and has 76 direct component targets:
Use `0.3.3`. It uses Tailwind CSS `4.3.2` and has 76 direct component targets:

```bash
dotnet tool install -g ShellUI.CLI
Expand Down Expand Up @@ -145,7 +145,7 @@ A representative Tailwind and component record is:
"InstalledComponents": [
{
"Name": "button",
"Version": "0.3.2",
"Version": "0.3.3",
"InstalledAt": "2026-01-01T00:00:00Z",
"IsCustomized": false
}
Expand Down
2 changes: 1 addition & 1 deletion docs/QUICKSTART.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ShellUI Quick Start

This quick start uses ShellUI `0.3.2`: Tailwind CSS `4.3.2` and 76 direct component targets. The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10.
This quick start uses ShellUI `0.3.3`: Tailwind CSS `4.3.2` and 76 direct component targets. The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10.

## Prerequisites

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

# ShellUI v0.3.3

> A patch release with fixes for both the `ShellUI.Components` NuGet package and the CLI templates.

## 🐛 Fixes

- NuGet package: linking `shellui-theme.css` directly logged a 404 on every page load, because the file started with `@import "tailwindcss";` and the browser requested `tailwindcss` as a URL. The theme file now holds only the theme; the precompiled `shellui-all.css` is unchanged.
- `Command` ignored `CommandItem.Action`: selecting an item only raised `CommandSelected`. It now runs the item's `Action`, then raises `CommandSelected`, the same as `CommandPalette`.
- `Dropdown` and `Popover` now close on Escape.
- CLI `SidebarProvider` logged a `JSDisconnectedException` twice on every full page reload, typed URL or closed tab. It now ignores the disconnected circuit when it cleans up.

New tests cover `Command` running `Action` and Escape closing `Dropdown` and `Popover`.

## ⬆️ Upgrading

- **NuGet package:** update to `0.3.3`.
- **CLI:** update the tool, then run `shellui update` to rewrite installed components from the new templates. `update` overwrites the files, so commit or back up any components you customized first.

# ShellUI v0.3.2

> A patch release with component fixes for both the `ShellUI.Components` NuGet package and the CLI templates.
Expand Down
6 changes: 4 additions & 2 deletions scripts/rebuild-precompiled-css.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ mv "$INPUT_CSS" "$INPUT_CSS.css"
INPUT_CSS="$INPUT_CSS.css"
COMPONENTS_ROOT="src/ShellUI.Components"

# @source inline, because Tailwind's file extractor misses `[state=…]` classes in plain text.
cat "$COMPONENTS_ROOT/wwwroot/shellui-theme.css" > "$INPUT_CSS"
# The import lives here, not in shellui-theme.css, so the theme can be linked directly without a 404.
printf '@import "tailwindcss";\n\n' > "$INPUT_CSS"
cat "$COMPONENTS_ROOT/wwwroot/shellui-theme.css" >> "$INPUT_CSS"
echo "" >> "$INPUT_CSS"

# @source inline, because Tailwind's file extractor misses `[state=…]` classes in plain text.
# Chunks of ~500 chars to avoid very long strings.
awk '
BEGIN { line=""; }
Expand Down
8 changes: 4 additions & 4 deletions src/ShellUI.CLI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ dotnet watch

## Documentation

- [README](https://github.com/shellui-dev/shellui/blob/v0.3.2/README.md)
- [CLI reference](https://github.com/shellui-dev/shellui/blob/v0.3.2/docs/CLI_SYNTAX.md)
- [Release notes](https://github.com/shellui-dev/shellui/blob/v0.3.2/docs/RELEASE_NOTES.md)
- [README](https://github.com/shellui-dev/shellui/blob/v0.3.3/README.md)
- [CLI reference](https://github.com/shellui-dev/shellui/blob/v0.3.3/docs/CLI_SYNTAX.md)
- [Release notes](https://github.com/shellui-dev/shellui/blob/v0.3.3/docs/RELEASE_NOTES.md)

## License

[MIT](https://github.com/shellui-dev/shellui/blob/v0.3.2/LICENSE.txt)
[MIT](https://github.com/shellui-dev/shellui/blob/v0.3.3/LICENSE.txt)
1 change: 1 addition & 0 deletions src/ShellUI.Components/Components/Command.razor
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@

private async Task SelectCommand(CommandItem command)
{
if (command.Action is not null) await command.Action();
await CommandSelected.InvokeAsync(command);
await CloseCommand();
}
Expand Down
7 changes: 6 additions & 1 deletion src/ShellUI.Components/Components/Dropdown.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@namespace ShellUI.Components

<CascadingValue Value="this" IsFixed="true">
<div class="@Shell.Cn("relative inline-block text-left", ClassName, Class)" @attributes="AdditionalAttributes">
<div class="@Shell.Cn("relative inline-block text-left", ClassName, Class)" @attributes="AdditionalAttributes" @onkeydown="OnKeyDown">
@if (UseCompositional)
{
@ChildContent
Expand Down Expand Up @@ -58,4 +58,9 @@
{
await ToggleAsync();
}

private async Task OnKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Escape" && IsOpen) await CloseAsync();
}
}
7 changes: 6 additions & 1 deletion src/ShellUI.Components/Components/Popover.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@namespace ShellUI.Components

<CascadingValue Value="this" IsFixed="true">
<div class="@Shell.Cn("relative inline-block", ClassName, Class)" @attributes="AdditionalAttributes">
<div class="@Shell.Cn("relative inline-block", ClassName, Class)" @attributes="AdditionalAttributes" @onkeydown="OnKeyDown">
@if (Trigger is null)
{
@ChildContent
Expand Down Expand Up @@ -45,4 +45,9 @@
public async Task CloseAsync() { IsOpen = false; await IsOpenChanged.InvokeAsync(IsOpen); StateHasChanged(); }
private async Task Toggle() => await ToggleAsync();
private async Task Close() => await CloseAsync();

private async Task OnKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Escape" && IsOpen) await CloseAsync();
}
}
8 changes: 4 additions & 4 deletions src/ShellUI.Components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ shellui theme apply https://tweakcn.com/themes/THEME_ID --emit-override wwwroot/

## Documentation

- [README](https://github.com/shellui-dev/shellui/blob/v0.3.2/README.md)
- [Tailwind setup](https://github.com/shellui-dev/shellui/blob/v0.3.2/docs/tailwind-setup.md)
- [Release notes](https://github.com/shellui-dev/shellui/blob/v0.3.2/docs/RELEASE_NOTES.md)
- [README](https://github.com/shellui-dev/shellui/blob/v0.3.3/README.md)
- [Tailwind setup](https://github.com/shellui-dev/shellui/blob/v0.3.3/docs/tailwind-setup.md)
- [Release notes](https://github.com/shellui-dev/shellui/blob/v0.3.3/docs/RELEASE_NOTES.md)

## License

[MIT](https://github.com/shellui-dev/shellui/blob/v0.3.2/LICENSE.txt)
[MIT](https://github.com/shellui-dev/shellui/blob/v0.3.3/LICENSE.txt)
2 changes: 0 additions & 2 deletions src/ShellUI.Components/wwwroot/shellui-theme.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import "tailwindcss";

:root {
--background: oklch(0.9900 0 0);
--foreground: oklch(0 0 0);
Expand Down
2 changes: 1 addition & 1 deletion src/ShellUI.Core/Models/ComponentMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private static string GetCurrentVersion()
if (assemblyVersion != null)
return assemblyVersion.InformationalVersion.Split('+')[0];

return "0.3.2";
return "0.3.3";
}
}

1 change: 1 addition & 0 deletions src/ShellUI.Templates/Templates/CommandTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ private void UpdateFilteredCommands()

private async Task SelectCommand(CommandItem command)
{
if (command.Action is not null) await command.Action();
await CommandSelected.InvokeAsync(command);
await CloseCommand();
}
Expand Down
7 changes: 6 additions & 1 deletion src/ShellUI.Templates/Templates/DropdownTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static class DropdownTemplate
public static string Content => @"@namespace YourProjectNamespace.Components.UI

<CascadingValue Value=""this"" IsFixed=""true"">
<div class=""@Shell.Cn(""relative inline-block text-left"", ClassName, Class)"" @attributes=""AdditionalAttributes"">
<div class=""@Shell.Cn(""relative inline-block text-left"", ClassName, Class)"" @attributes=""AdditionalAttributes"" @onkeydown=""OnKeyDown"">
@if (UseCompositional)
{
@ChildContent
Expand Down Expand Up @@ -75,6 +75,11 @@ private async Task ToggleOpen()
{
await ToggleAsync();
}

private async Task OnKeyDown(KeyboardEventArgs e)
{
if (e.Key == ""Escape"" && IsOpen) await CloseAsync();
}
}
";
}
Expand Down
7 changes: 6 additions & 1 deletion src/ShellUI.Templates/Templates/PopoverTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class PopoverTemplate
public static string Content => @"@namespace YourProjectNamespace.Components.UI

<CascadingValue Value=""this"" IsFixed=""true"">
<div class=""@Shell.Cn(""relative inline-block"", ClassName, Class)"" @attributes=""AdditionalAttributes"">
<div class=""@Shell.Cn(""relative inline-block"", ClassName, Class)"" @attributes=""AdditionalAttributes"" @onkeydown=""OnKeyDown"">
@if (Trigger is null)
{
@ChildContent
Expand Down Expand Up @@ -63,6 +63,11 @@ public class PopoverTemplate
public async Task CloseAsync() { IsOpen = false; await IsOpenChanged.InvokeAsync(IsOpen); StateHasChanged(); }
private async Task Toggle() => await ToggleAsync();
private async Task Close() => await CloseAsync();

private async Task OnKeyDown(KeyboardEventArgs e)
{
if (e.Key == ""Escape"" && IsOpen) await CloseAsync();
}
}
";
}
Expand Down
1 change: 1 addition & 0 deletions src/ShellUI.Templates/Templates/SidebarProviderTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public async ValueTask DisposeAsync()
{
try { await JSRuntime.InvokeVoidAsync(""ShellUI.disposeSidebar"", _handle); }
catch (JSException) { }
catch (JSDisconnectedException) { }
}

_dotnetRef?.Dispose();
Expand Down
2 changes: 1 addition & 1 deletion src/ShellUI.Templates/VersionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static string GetCurrentVersion()
return _cachedVersion;
}

return "0.3.2";
return "0.3.3";
}

private static string? FindSolutionRoot()
Expand Down
Loading