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
1 change: 0 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"vscode": {
"extensions": [
"GitHub.copilot",
"glenn2223.live-sass",
"GitHub.copilot-chat",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit",
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/bit.full.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,9 @@ jobs:

- name: Build sample configuration 1
run: |
dotnet new bit-bp --name TestProject --database SqlServer --filesStorage AzureBlobStorage --api Integrated --captcha reCaptcha --pipeline Azure --module Admin --offlineDb --appInsights --sentry --signalR --notification --cloudflare --ads --aspire --redis --multitenant --brouter
dotnet build TestProject/TestProject.sln -p:InvariantGlobalization=false -p:Environment=Staging -p:EnforceCodeStyleInBuild=true
rm -r "TestProject"
dotnet new bit-bp --name MyCompany.TestProject --database SqlServer --filesStorage AzureBlobStorage --api Integrated --captcha reCaptcha --pipeline Azure --module Admin --offlineDb --appInsights --sentry --signalR --notification --cloudflare --ads --aspire --redis --multitenant --brouter
dotnet build MyCompany.TestProject/MyCompany.TestProject.sln -p:InvariantGlobalization=false -p:Environment=Staging -p:EnforceCodeStyleInBuild=true
rm -r "MyCompany.TestProject"

- name: Build sample configuration 2
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"vscode": {
"extensions": [
"GitHub.copilot",
"glenn2223.live-sass",
"GitHub.copilot-chat",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit",
Expand Down Expand Up @@ -50,4 +49,4 @@
"remoteEnv": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,6 @@ You can add additional MCP servers to extend Copilot's capabilities further. For

```json
{
"liveSassCompile.settings.watchOnLaunch": true,
"dotnet.defaultSolution": "Boilerplate.Web.slnf",
"dotnet.unitTests.runSettingsPath": "src/Tests/.runsettings",
"chat.tools.autoApprove": true,
Expand Down Expand Up @@ -505,7 +504,6 @@ You can add additional MCP servers to extend Copilot's capabilities further. For
{
"recommendations": [
"GitHub.copilot",
"glenn2223.live-sass",
"GitHub.copilot-chat",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ if (builder.ExecutionContext.IsRunMode)

### Trade-offs

✅ Starting the project with `F5` / `dotnet run` becomes noticeably faster
✅ Starting the project with `F5` / `aspire start` becomes noticeably faster
✅ The automated tests run considerably faster, as they no longer wait for the containers to boot up and become healthy
⚠️ Those containers keep running (and consuming memory) while you're not debugging — stop them from **Docker Desktop** (or with `docker stop`) whenever you need those resources back, the app host will start them again on the next run

Expand Down
2 changes: 1 addition & 1 deletion src/Templates/Boilerplate/Bit.Boilerplate/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,4 @@ custom.aprof
/src/Client/Boilerplate.Client.Maui/Platforms/Android/google-services.json

.env
.playwright-mcp
.playwright-mcp/
53 changes: 35 additions & 18 deletions src/Templates/Boilerplate/Bit.Boilerplate/.mcp.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
{
"mcpServers": {
"MicrosoftLearn": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp"
},
"DeepWiki": {
"type": "http",
"url": "https://mcp.deepwiki.com/mcp"
},
"bitBlazorUI": {
"type": "http",
"url": "https://blazorui.bitplatform.dev/mcp"
},
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
"mcpServers": {
"MicrosoftLearn": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp"
},
"DeepWiki": {
"type": "http",
"url": "https://mcp.deepwiki.com/mcp"
},
"bitBlazorUI": {
"type": "http",
"url": "https://blazorui.bitplatform.dev/mcp"
},
//#if (aspire == true)
"aspire": {
"type": "stdio",
"command": "aspire",
"args": [
"agent",
"mcp"
]
},
//#endif
Comment thread
yasmoradi marked this conversation as resolved.
"playwright": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@playwright/mcp@latest",
"--output-dir",
".playwright-mcp",
"--output-max-size",
"52428800"
]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
"displayName": "Use bit Brouter?",
"type": "parameter",
"datatype": "bool",
"defaultValue": "true",
"defaultValue": "false",
"description": "Replaces the built-in Blazor Router with bit Brouter, bringing advanced routing features - nested routes, route guards, data loaders, keep-alive pages and view transitions - inspired by the routers of Vue.js, React and Angular."
},
"nameToLower": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"recommendations": [
"GitHub.copilot",
"glenn2223.live-sass",
"GitHub.copilot-chat",
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit",
Expand All @@ -12,4 +11,4 @@
"ms-dotnettools.blazorwasm-companion",
"ms-dotnettools.vscode-dotnet-runtime"
]
}
}
21 changes: 19 additions & 2 deletions src/Templates/Boilerplate/Bit.Boilerplate/.vscode/mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,27 @@
"type": "http",
"url": "https://blazorui.bitplatform.dev/mcp"
},
//#if (aspire == true)
"aspire": {
"type": "stdio",
"command": "aspire",
"args": [
"agent",
"mcp"
]
},
//#endif
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
"args": [
"-y",
"@playwright/mcp@latest",
"--output-dir",
"${workspaceFolder}/.playwright-mcp",
"--output-max-size",
"52428800"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"files.encoding": "utf8",
"files.autoGuessEncoding": false,
"liveSassCompile.settings.watchOnLaunch": true,
"dotnet.defaultSolution": "Boilerplate.Web.slnf",
"dotnet.unitTests.runSettingsPath": "src/Tests/.runsettings",
"chat.tools.autoApprove": true,
Expand All @@ -17,7 +16,7 @@
"*.json": "$(capture).*.json, $(capture).*.json",
"AGENTS.md": "CLAUDE.md, GEMINI.md, COPILOT.md"
},
"workbench.editorAssociations": {
"workbench.editorAssociations": {
"*.md": "vscode.markdown.preview.editor"
}
}
37 changes: 2 additions & 35 deletions src/Templates/Boilerplate/Bit.Boilerplate/.vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,10 @@
{
"label": "before-build",
"type": "shell",
"command": "dotnet build src/Client/Boilerplate.Client.Core/Boilerplate.Client.Core.csproj -t:BeforeBuildTasks",
"command": "dotnet build Boilerplate.slnx -t:BeforeBuildTasks",
"runOptions": {
"runOn": "folderOpen"
}
},
{
"label": "build",
"type": "shell",
"command": "dotnet build src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj"
},
{
"label": "generate-resx-files",
"type": "shell",
"command": "dotnet build src/Shared/Boilerplate.Shared.csproj -t:PrepareResources"
},
{
"label": "run",
"type": "shell",
"command": "dotnet",
"args": [
"run"
],
"options": {
"cwd": "${workspaceFolder}/src/Server/Boilerplate.Server.Web"
},
"isBackground": true
},
{
"label": "run-tests",
"type": "shell",
"command": "dotnet",
"args": [
"test"
],
"options": {
"cwd": "${workspaceFolder}/src/Tests"
}
}
]
}
}
15 changes: 8 additions & 7 deletions src/Templates/Boilerplate/Bit.Boilerplate/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ Before implementing any changes, you **MUST** complete the following:
## 4. Critical Command Reference

<!--#if (aspire == true)-->
- **Build the project**: Run `dotnet build` in Boilerplate.Server.AppHost project root directory.
- **Run the project**: Run `dotnet watch` in Boilerplate.Server.AppHost project root directory. If needed, you may use the Playwright MCP tools to interact with the running UI to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
- **Build the project**: Run `dotnet build` in src/Server/Boilerplate.Server.AppHost project directory.
- **Run the project**: Run `aspire start`. If needed, you may use the Playwright MCP tools to interact with the `serverweb` resource running by aspire to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
<!--#else-->
- **Build the project**: Run `dotnet build` in Boilerplate.Server.Web project root directory.
- **Run the project**: Run `dotnet watch` in Boilerplate.Server.Web project root directory. If needed, you may use the Playwright MCP tools to interact with the running UI to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
- **Build the project**: Run `dotnet build` in src/Server/Boilerplate.Server.Web project directory.
- **Run the project**: Run `dotnet watch` in src/Server/Boilerplate.Server.Web project directory. If needed, you may use the Playwright MCP tools to interact with the running UI to validate things (navigate, click, fill forms, take screenshots), and use `browser_evaluate` to run in-page JavaScript to accelerate the process (e.g. quickly locating elements, extracting data, or asserting state).
<!--#endif-->
- **Run tests**: Run `dotnet test` in Boilerplate.Tests project root directory.
- **Add new migrations**: Run `dotnet ef migrations add <MigrationName> --output-dir Infrastructure/Data/Migrations --verbose` in Boilerplate.Server.Api project root directory.
- **Generate Resx C# code**: Run `dotnet build -t:PrepareResources` in Boilerplate.Shared project root directory.
- **Run tests**: Run `dotnet test` in src/Tests/Boilerplate.Tests project directory.
- **Add new migrations**: Run `dotnet ef migrations add <MigrationName> --output-dir Infrastructure/Data/Migrations --verbose` in src/Server/Boilerplate.Server.Api project directory.
- **Generate Resx C# code**: Run `dotnet build -t:PrepareResources` in src/Shared/Boilerplate.Shared project directory.

## 5. Coding Conventions & Best Practices

Expand All @@ -93,6 +93,7 @@ Before implementing any changes, you **MUST** complete the following:
- **Style Bit.BlazorUI Components Correctly**: Use the `::deep` selector in your `.scss` files to style `Bit.BlazorUI` components.
- **Use Theme Colors in C# and Razor**: In C# and Razor files, you **MUST** use `BitColor` enum and `BitCss` class to apply theme colors instead of hardcoded colors. Use `BitColor` for component parameters (e.g., `BitColor.Primary`, `BitColor.TertiaryBackground`). Use `BitCss.Class` for CSS classes (e.g., `@BitCss.Class.Color.Background.Primary`, `@BitCss.Class.Color.Foreground.Secondary`). Use `BitCss.Var` for inline styles with CSS variables (e.g., `border-color:var(@BitCss.Var.Color.Border.Primary)`). This ensures automatic dark/light mode support.
- **Use Theme Colors in SCSS**: In SCSS files, you **MUST** use SCSS variables from `_bit-css-variables.scss` instead of hardcoded colors. Import the file and use variables like `$bit-color-primary`, `$bit-color-foreground-primary`, `$bit-color-background-secondary`, etc. These map to CSS custom properties that automatically adapt to dark/light modes. Available variable categories include: primary, secondary, tertiary, info, success, warning, severe-warning, error, foreground, background, border, and neutral colors.
- **Localize User-Visible Text with String Literals**: When you add or change ANY user-visible text, you **MUST NOT** add, rename, or edit keys in `.resx` files - `.resx` changes are not supported by hot reload (`dotnet watch` / `aspire start`) and force a full restart. Instead, use the `IStringLocalizer` string indexer with the literal English text, e.g. `Localizer["Welcome back {0}!", user.GetDisplayName()]` or `@Localizer["Product saved successfully."]`. `Localizer[nameof(AppStrings.X)]` stays as-is for existing keys whose text you are not changing; do not create a duplicate literal for them. Only when the user **explicitly** asks to apply translations, move all such literals into `resx` files, and switch the call sites back to `Localizer[nameof(AppStrings.X)]`.
- **Use Enhanced Lifecycle Methods**: In components inheriting from `AppComponentBase` or pages inheriting from `AppPageBase`, you **MUST** use `OnInitAsync`, `OnParamsSetAsync`, and `OnAfterFirstRenderAsync`.
- **WrapHandled**: Use `WrapHandled` for event handlers in razor files to prevent unhandled exceptions.
Example 1: `OnClick="WrapHandled(MyMethod)"` instead of `OnClick="MyMethod"`.
Expand Down
12 changes: 12 additions & 0 deletions src/Templates/Boilerplate/Bit.Boilerplate/aspire.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"appHost": {
"path": "src/Server/Boilerplate.Server.AppHost/Boilerplate.Server.AppHost.csproj"
},
"features": {
"deployCommandEnabled": true,
"execCommandEnabled": true,
"defaultWatchEnabled": true,
"aspireSkillsRemoteFetchEnabled": true,
"terminalCommandsEnabled": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ private record Dependency(string Name, string? Url, string? Repo, string License
new("Container Tools (Docker)", "https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker", "https://github.com/microsoft/vscode-docker", "MIT"),
new("Dev Containers", "https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers", null, "Proprietary"),
new("Blazor WASM Companion", "https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.blazorwasm-companion", "https://github.com/dotnet/razor", "MIT"),
new("Live Sass Compiler", "https://marketplace.visualstudio.com/items?itemName=glenn2223.live-sass", "https://github.com/glenn2223/vscode-live-sass-compiler", "MIT"),
new("ResX Manager", "https://marketplace.visualstudio.com/items?itemName=TomEnglert.ResXManager", "https://github.com/dotnet/ResXResourceManager", "MIT"),
new("VS \"ASP.NET & web development\" workload", "https://visualstudio.microsoft.com/vs/features/web", null, "Proprietary"),
new("VS \".NET MAUI development\" workload", "https://dotnet.microsoft.com/apps/maui", null, "Proprietary"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
$(ResolveStaticWebAssetsInputsDependsOn)
</ResolveStaticWebAssetsInputsDependsOn>

<WasmFingerprintAssets>false</WasmFingerprintAssets>
<StaticWebAssetsFingerprintContent>false</StaticWebAssetsFingerprintContent>
<StaticWebAssetFingerprintingEnabled>false</StaticWebAssetFingerprintingEnabled>

<WasmApplicationEnvironmentName>$(Environment)</WasmApplicationEnvironmentName>
<WasmEnableWebcil Condition="'$(Environment)' == 'Development'">false</WasmEnableWebcil>
</PropertyGroup>
Expand Down Expand Up @@ -75,19 +71,19 @@
Blazor WebAssembly diagnostics https://learn.microsoft.com/en-us/aspnet/core/blazor/performance/webassembly-event-pipe-diagnostics

Put the followings inside Boilerplate.Client.Web/wwwroot/Boilerplate.Client.Web.lib.module.js
export function afterWebAssemblyStarted(options, extensions) {
export function afterWebAssemblyStarted(options, extensions) {
globalThis.getDotnetRuntime(0).collectCpuSamples({durationSeconds: 10});
// Or un-comment the following line to collect metrics instead of CPU samples
// globalThis.getDotnetRuntime(0).collectMetrics({durationSeconds: 10});
// globalThis.getDotnetRuntime(0).collectMetrics({durationSeconds: 10});
}

Or run this command in the browser console to collect GC dump:
globalThis.getDotnetRuntime(0).collectGcDump();

Convert nettrace file to json using dotnet-trace convert your-file-name.nettrace -f Chromium
-->
<!--
<PropertyGroup>
<!--
<PropertyGroup>
<EnableDiagnostics>true</EnableDiagnostics>
<MetricsSupport>true</MetricsSupport>
<EventSourceSupport>true</EventSourceSupport>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@
<DefineConstants Condition=" '$(InvariantGlobalization)' == 'true' ">$(DefineConstants);InvariantGlobalization</DefineConstants>
<DefineConstants>$(DefineConstants);$(Environment);$(Configuration)</DefineConstants>
<DefineConstants Condition="$(Configuration.Contains('Debug'))">$(DefineConstants);DEBUG</DefineConstants>

<HotReloadAutoRestart>true</HotReloadAutoRestart>
<CompressionEnabled Condition="'$(Environment)' == 'Development'">false</CompressionEnabled>

<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>

<WasmFingerprintAssets>false</WasmFingerprintAssets>
<StaticWebAssetsFingerprintContent>false</StaticWebAssetsFingerprintContent>
<StaticWebAssetFingerprintingEnabled>false</StaticWebAssetFingerprintingEnabled>
</PropertyGroup>
<!--/+:msbuild-conditional:noEmit -->

Expand Down Expand Up @@ -79,7 +83,7 @@
<Watch Remove="*.scss" />
</ItemGroup>

<!--
<!--
Deletes residual *.razor.css* files (such as .razor.css & .razor.css.map) that are missing a corresponding .razor file in the project before build.
This is necessary in Blazor projects with Scoped CSS to prevent build errors caused by orphaned razor.css files,
which may remain after their source razor files are deleted or renamed. For example, if a developer deletes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class AppCertificateService
/// The self-signed certificate this template ships with. Its private key is public, so anyone could mint a valid
/// token or decrypt the Data Protection key ring - it is a local development convenience and nothing else.
/// </summary>
private const string BoilerplateDefaultCertificateThumbprint = "1D549B7F8B0D52A54DE1C36948055B17C90063A2";
private const string TestCertificateThumbprint = "1D549B7F8B0D52A54DE1C36948055B17C90063A2";

private static X509Certificate2[]? allAppCerts;
private static RsaSecurityKey? privateSecurityKey;
Expand Down Expand Up @@ -45,7 +45,7 @@ public static X509Certificate2[] GetAllAppCertificates(IConfiguration configurat

// Retired certificates are checked too: a retired default would still be trusted for validation, so its
// public private key could still be used to mint tokens this server accepts.
if (AppEnvironment.IsDevelopment() is false && certs.Any(cert => cert.Thumbprint is BoilerplateDefaultCertificateThumbprint))
if (AppEnvironment.IsDevelopment() is false && certs.Any(cert => cert.Thumbprint is TestCertificateThumbprint))
throw new InvalidOperationException("You are using the default self-signed certificate in non-development environment. Generate and use your own certificate using `openssl genrsa` and `openssl req` commands described in AppCertificate.md file.");

return allAppCerts = [.. certs];
Expand Down

This file was deleted.

Loading
Loading