Skip to content

fix(init): bootstrap host with render mode, theme script, full theme CSS#15

Merged
Shewart merged 6 commits into
mainfrom
fix/init-bootstrap
Jun 17, 2026
Merged

fix(init): bootstrap host with render mode, theme script, full theme CSS#15
Shewart merged 6 commits into
mainfrom
fix/init-bootstrap

Conversation

@Shewart

@Shewart Shewart commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

shellui init produced a non-functional project: no @rendermode so interactive components were inert, no theme script so light/dark caused FOUC, only @import "tailwindcss"; in input.css so component CSS variables were undefined (everything rendered unstyled black/white), and a stale hsl(var(--x)) color block in the Tailwind v4 npm config. Users had to manually patch four files before any component worked.

This PR makes shellui init produce a working app out of the box and adds tests + CI assertions so the next regression here fails fast.

Changes

Init now patches the host

src/ShellUI.CLI/Services/InitService.cs — new BootstrapHostAsync step between component install and MSBuild targets. Dispatches to one of two patchers based on detected project type:

Blazor Web App / Server / SSR (Components/App.razor):

  • <HeadOutlet /><HeadOutlet @rendermode="InteractiveServer" />
  • <Routes /><Routes @rendermode="InteractiveServer" />
  • Injects a small theme bootstrap <script> into <head> that reads localStorage.theme and sets documentElement.classList.add('dark') before paint — prevents the light flash on dark pages
  • Injects <script src="shellui.js"></script> immediately before _framework/blazor.web.js so window.ShellUI.* (used by ThemeToggle, CopyButton, InputOTP, FileUpload, Command) is defined before Blazor calls into it

Blazor WebAssembly standalone (wwwroot/index.html): same theme bootstrap + shellui.js injection (no @rendermode since WASM uses <Router> not <Routes />).

The "remember to add <script src=\"shellui.js\">" reminder in the success message is gone — it's automatic now.

Render-mode injection is conservative

The regex only matches the bare default form (<HeadOutlet />, <Routes />). If the user (or another tool) already set @rendermode="InteractiveAuto" or any other attribute, the regex doesn't match and the tag is left alone. Idempotent — a second shellui init is a no-op.

shellui-sidebar.js is never injected as a script tag. It's a type=module loaded by SidebarProvider.razor via JSRuntime.InvokeAsync<IJSObjectReference>("import", "./shellui-sidebar.js") — a <script> tag would either double-load it or fail silently because the file uses export function. CI now asserts it's absent.

Default theme ships, not just @import "tailwindcss"

src/ShellUI.Templates/CssTemplates.csInputCss and InputCssNpm now emit the full default theme: :root light vars, .dark dark vars, @theme inline mapping for Tailwind v4, @custom-variant dark (&:is(.dark *));, @layer base defaults, and the Loading-component animation keyframes. Theme values are tweakcn-compatible — users can paste a new :root / .dark block over this to retheme.

Tailwind v4 npm config no longer carries the v3 wrapper

TailwindConfigJsNpm no longer has the colors: { primary: { DEFAULT: 'hsl(var(--primary))' } } block. Tailwind v4 reads the palette via @theme inline in input.css — duplicating it in the config file did nothing useful and was confusing.

Tests

New ShellUI.Tests/InitBootstrapTests.cs — 7 tests covering the rewriter directly:

  • Render mode added to both HeadOutlet and Routes
  • Theme bootstrap injected inside <head> (positionally verified, not just "contains")
  • shellui.js script tag precedes blazor.web.js (ordering matters)
  • Running RewriteAppRazor twice produces identical output (idempotent)
  • Existing @rendermode="InteractiveAuto" is preserved — we don't overwrite
  • WASM index.html gets theme bootstrap + shellui.js ordered correctly
  • WASM rewriter is also idempotent

Plumbing: ShellUI.CLI.csproj gets InternalsVisibleTo("ShellUI.Tests") so the rewriter methods stay internal instead of bloating the public CLI API surface.

CI smoke step extended

.github/workflows/ci.yml — after shellui init, the workflow now greps the produced Components/App.razor and wwwroot/input.css for the expected markers and fails loudly if any are missing:

  • HeadOutlet @rendermode="InteractiveServer"
  • Routes @rendermode="InteractiveServer"
  • ShellUI theme bootstrap
  • <script src="shellui.js"></script>
  • shellui-sidebar.js must not appear in App.razor
  • input.css contains @theme inline, :root, and .dark blocks

Then proceeds with the existing shellui add chart pie-chart dashboard-02 + dotnet build end-to-end.

Verification

  • dotnet test ShellUI.Tests25/25 passing (7 new bootstrap tests + 18 from previous branches)
  • Local manual: scaffolded a fresh dotnet new blazor, ran the CLI from this branch's bin/Release, confirmed App.razor was patched, dotnet build clean, theme toggle flips light/dark and persists across reload, no FOUC on a dark-themed page

Test plan

  • CI green (tests + extended smoke step)
  • Manual: dotnet new blazor → install the prerelease CLI tool → shellui init → diff Components/App.razor against the pre-init version; confirm all four markers landed
  • Manual: shellui init --force on the same project — confirm App.razor is unchanged (idempotency)
  • Manual: pre-set <HeadOutlet @rendermode="InteractiveAuto" /> in App.razor, run shellui init, confirm InteractiveAuto is preserved
  • Manual: dotnet new blazorwasmshellui init → confirm wwwroot/index.html patched, App.razor untouched

Shewart added 6 commits June 17, 2026 18:31
Added assertions in the CI workflow to verify that the `shellui init` command correctly patches the `App.razor` file and writes the full theme to `input.css`. This ensures that the initialization process produces a working host with the expected configurations and theme variables, improving the reliability of the scaffolding process.
Introduced a new test class, InitBootstrapTests, to validate the functionality of the InitService's methods for rewriting Razor files. The tests ensure that the `RewriteAppRazor` and `RewriteWasmIndexHtml` methods correctly inject necessary scripts and theme elements, maintain idempotency, and preserve existing render modes. This addition enhances test coverage for the initialization process in ShellUI.
…ndex.html

Added the BootstrapHostAsync method to the InitService, which handles the patching of Components/App.razor and wwwroot/index.html for Blazor applications. This method ensures the correct injection of theme bootstrap scripts and the shellui.js script tag, enhancing the initialization process. Updated the status messages to reflect these changes and improved the overall user experience during setup.
…o tests

Added a project reference for ShellUI.CLI in the ShellUI.Tests project to facilitate testing. Additionally, made internals of ShellUI.CLI visible to ShellUI.Tests, enabling comprehensive unit testing of internal components and improving test coverage.
Updated the CssTemplates class to replace the InputCss property with FullThemeCss, introducing a new InputCssNpm property. Enhanced TailwindConfigJsNpm with comments for clarity on theme handling and removed unnecessary theme configurations. This refactor improves the overall structure and maintainability of the CSS templates, aligning with Tailwind v4's requirements for theme management.
Updated InitBootstrapTests to include a new test for handling bare Blazor script tags in the RewriteAppRazor method. This ensures compatibility with older templates that do not use the @assets[] wrapper. Additionally, modified the InitService to support both modern and bare script tag formats, improving the robustness of the Razor file rewriting process.
@Shewart Shewart merged commit 9714a8b into main Jun 17, 2026
1 check passed
@Shewart Shewart deleted the fix/init-bootstrap branch June 17, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant