Back to server for load speed
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
---
|
||||
sessionId: session-260909-133404-1um1
|
||||
---
|
||||
|
||||
# Requirements
|
||||
|
||||
### Overview & Goals
|
||||
Convert the `Web` project from a standalone Blazor WebAssembly application back to an ASP.NET Core Blazor Server application with interactive server components.
|
||||
|
||||
### Scope
|
||||
#### In Scope
|
||||
- Update `Web/Web.csproj` to use `Microsoft.NET.Sdk.Web` and remove WebAssembly client dependencies.
|
||||
- Convert `Web/Program.cs` from `WebAssemblyHostBuilder` to ASP.NET Core `WebApplication.CreateBuilder` with Razor Components and interactive server render modes.
|
||||
- Relocate configuration files (`appsettings.json`, `appsettings.Development.json`) from `Web/wwwroot/` to the project root `Web/`.
|
||||
- Remove `Web/wwwroot/index.html` and restore `Web/Components/App.razor` as the HTML host root.
|
||||
- Add `Web/Components/Routes.razor`, `Web/Components/Pages/Error.razor`, and `Web/Components/Layout/ReconnectModal.razor` (with corresponding CSS and JS).
|
||||
- Update `Web/Components/_Imports.razor` with server rendering imports.
|
||||
|
||||
#### Out of Scope
|
||||
- Modifying resume content data files (`Overview.cs`, `PersonalInfo.cs`, `Skills.cs`, `WorkExperience.cs`).
|
||||
- Modifying resume UI layouts or styles in `Home.razor`, `MainLayout.razor`, or `app.css`.
|
||||
|
||||
### User Stories
|
||||
- As a visitor, I want to load the resume application rendered quickly from the ASP.NET Core server with interactive server capabilities.
|
||||
- As a developer, I want the project configured as a standard ASP.NET Core Blazor Server app so that it can leverage server-side capabilities and runtime services.
|
||||
|
||||
### Functional Requirements
|
||||
- The application must start using ASP.NET Core `WebApplication` hosting.
|
||||
- Navigation to `/` must render the resume page (`Home.razor`) inside `MainLayout.razor`.
|
||||
- Navigation to unknown routes must display the custom not-found page (`NotFound.razor`) via `UseStatusCodePagesWithReExecute("/not-found")` and the Blazor router.
|
||||
- Disconnected or reconnecting Blazor Server circuits must display the interactive reconnection modal dialog.
|
||||
- Server-side error routing must be handled via `/Error` rendering `Error.razor`.
|
||||
|
||||
### Non-Functional Requirements
|
||||
- Maintain compatibility with .NET 10 (`net10.0`).
|
||||
- Ensure clean build with zero compile errors.
|
||||
|
||||
# Technical Design
|
||||
|
||||
### Current Implementation
|
||||
The `Web` project is currently configured as a standalone Blazor WebAssembly client using `Microsoft.NET.Sdk.BlazorWebAssembly`, with `Program.cs` invoking `WebAssemblyHostBuilder.CreateDefault(args)`, static hosting through `Web/wwwroot/index.html`, and client configuration files in `Web/wwwroot/`.
|
||||
|
||||
### Key Decisions
|
||||
- **Hosting Model**: Use ASP.NET Core Blazor Server with interactive server components (`AddInteractiveServerComponents()` and `.AddInteractiveServerRenderMode()`).
|
||||
- **Host Document**: Use `Web/Components/App.razor` as the HTML root document containing head elements, styles, scripts, reconnect modal, and `<Routes />`.
|
||||
- **Configuration Placement**: Move `appsettings.json` and `appsettings.Development.json` to the `Web/` project root, matching standard ASP.NET Core hosting conventions.
|
||||
|
||||
### Proposed Changes
|
||||
1. **`Web/Web.csproj`**:
|
||||
- Change SDK to `Microsoft.NET.Sdk.Web`.
|
||||
- Remove `<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" ... />` and `<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" ... />`.
|
||||
- Set `<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>`.
|
||||
2. **`Web/Program.cs`**:
|
||||
- Build server host via `var builder = WebApplication.CreateBuilder(args);`.
|
||||
- Register `builder.Services.AddRazorComponents().AddInteractiveServerComponents();`.
|
||||
- Configure HTTP request pipeline: exception handler `/Error`, HSTS, status code re-execute `/not-found`, HTTPS redirection, antiforgery, static assets mapping (`MapStaticAssets()`), and `MapRazorComponents<App>().AddInteractiveServerRenderMode()`.
|
||||
3. **`Web/wwwroot/`**:
|
||||
- Remove `index.html`.
|
||||
- Move `appsettings.json` and `appsettings.Development.json` up to `Web/`.
|
||||
4. **`Web/Components/`**:
|
||||
- `App.razor`: HTML host markup with `<Routes />`, `<ReconnectModal />`, `<script src="@Assets["_framework/blazor.web.js"]"></script>`.
|
||||
- `Routes.razor`: `<Router>` definition referencing `Program.Assembly` and `NotFoundPage="typeof(Pages.NotFound)"`.
|
||||
- `Layout/ReconnectModal.razor` (+ `.razor.css`, `.razor.js`): Circuit reconnection overlay.
|
||||
- `Pages/Error.razor`: Error display component.
|
||||
- `_Imports.razor`: Add `@using static Microsoft.AspNetCore.Components.Web.RenderMode` and remove WebAssembly-only namespaces.
|
||||
|
||||
### File Structure
|
||||
```
|
||||
Web/
|
||||
├── appsettings.json
|
||||
├── appsettings.Development.json
|
||||
├── Program.cs
|
||||
├── Web.csproj
|
||||
├── Properties/
|
||||
│ └── launchSettings.json
|
||||
├── Components/
|
||||
│ ├── _Imports.razor
|
||||
│ ├── App.razor
|
||||
│ ├── Routes.razor
|
||||
│ ├── Layout/
|
||||
│ │ ├── MainLayout.razor
|
||||
│ │ ├── MainLayout.razor.css
|
||||
│ │ ├── ReconnectModal.razor
|
||||
│ │ ├── ReconnectModal.razor.css
|
||||
│ │ └── ReconnectModal.razor.js
|
||||
│ └── Pages/
|
||||
│ ├── Error.razor
|
||||
│ ├── Home.razor
|
||||
│ ├── Home.razor.css
|
||||
│ └── NotFound.razor
|
||||
├── Data/
|
||||
│ ├── Overview.cs
|
||||
│ ├── PersonalInfo.cs
|
||||
│ ├── Skills.cs
|
||||
│ └── WorkExperience.cs
|
||||
└── wwwroot/
|
||||
├── app.css
|
||||
├── favicon.png
|
||||
└── lib/bootstrap/...
|
||||
```
|
||||
|
||||
### Architecture Diagram
|
||||
```mermaid
|
||||
graph TD
|
||||
Browser[Web Browser] -->|HTTP / WebSocket SignalR| ASPNET[ASP.NET Core Server]
|
||||
ASPNET --> Pipeline[Middleware Pipeline]
|
||||
Pipeline --> MapStatic[MapStaticAssets]
|
||||
Pipeline --> RazorComponents[MapRazorComponents: App.razor]
|
||||
RazorComponents --> Routes[Routes.razor / Router]
|
||||
Routes --> MainLayout[MainLayout.razor]
|
||||
MainLayout --> Home[Home.razor]
|
||||
MainLayout --> NotFound[NotFound.razor]
|
||||
Home --> Data[Data Layer: Overview, PersonalInfo, Skills, WorkExperience]
|
||||
```
|
||||
|
||||
# Testing
|
||||
|
||||
### Validation Approach
|
||||
- Verify successful compilation with `dotnet build Web/Web.csproj`.
|
||||
- Validate that no obsolete WebAssembly packages or unused `index.html` remain.
|
||||
- Validate that all server components, router, reconnect modal, error handling, and static asset mappings compile and link properly.
|
||||
|
||||
### Key Scenarios
|
||||
- **Build Verification**: Ensure `dotnet build Web/Web.csproj` completes with 0 errors.
|
||||
- **Root Page Loading**: Ensure root route `/` routes to `Home.razor` within `MainLayout.razor`.
|
||||
- **404 Handling**: Ensure unknown routes re-execute to `/not-found` rendering `NotFound.razor`.
|
||||
- **Error Page**: Ensure `/Error` is routable to `Error.razor`.
|
||||
|
||||
### Edge Cases
|
||||
- Disconnected SignalR circuit: Reconnection modal displays appropriate rejoining/paused/failed statuses.
|
||||
- Static asset path resolution: Verify `_framework/blazor.web.js`, Bootstrap CSS, and `app.css` resolve with `@Assets`.
|
||||
|
||||
# Delivery Steps
|
||||
|
||||
### ✓ Step 1: Convert project SDK, configuration files, and ASP.NET Core server pipeline
|
||||
The Web project is configured as an ASP.NET Core Web application with server hosting services and pipeline middleware.
|
||||
|
||||
- Update `Web/Web.csproj` SDK to `Microsoft.NET.Sdk.Web`, remove `Microsoft.AspNetCore.Components.WebAssembly` package dependencies, and set `BlazorDisableThrowNavigationException`.
|
||||
- Relocate `appsettings.json` and `appsettings.Development.json` from `Web/wwwroot/` to the project root directory `Web/`.
|
||||
- Remove the standalone static `Web/wwwroot/index.html` host file.
|
||||
- Rewrite `Web/Program.cs` to initialize `WebApplication.CreateBuilder(args)`, configure Razor components with interactive server support (`AddRazorComponents().AddInteractiveServerComponents()`), and set up HTTP middleware (antiforgery, static asset mapping, status code re-execution, exception handling, and `MapRazorComponents<App>().AddInteractiveServerRenderMode()`).
|
||||
|
||||
### ✓ Step 2: Restore Blazor Server host, routing, reconnect UX, and imports
|
||||
The application renders HTML from the server with full routing, error handling, and circuit reconnection UI.
|
||||
|
||||
- Update `Web/Components/App.razor` to serve as the HTML root document with `<Routes />`, `<ReconnectModal />`, `<HeadOutlet />`, stylesheet assets, and `_framework/blazor.web.js`.
|
||||
- Add `Web/Components/Routes.razor` defining the `<Router>` with `AppAssembly="typeof(Program).Assembly"` and `NotFoundPage="typeof(Pages.NotFound)"`.
|
||||
- Add `Web/Components/Layout/ReconnectModal.razor` and its scoped CSS / JS assets (`ReconnectModal.razor.css`, `ReconnectModal.razor.js`) to handle Blazor Server circuit reconnection states.
|
||||
- Add `Web/Components/Pages/Error.razor` to handle server-side request errors and show request diagnostic IDs.
|
||||
- Update `Web/Components/_Imports.razor` to import static render modes and server component namespaces.
|
||||
- Verify the solution builds without errors and serves the resume application over Blazor Server.
|
||||
+23
-12
@@ -1,12 +1,23 @@
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(Layout.MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<ResourcePreloader />
|
||||
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
|
||||
<link rel="stylesheet" href="@Assets["app.css"]" />
|
||||
<link rel="stylesheet" href="@Assets["Web.styles.css"]" />
|
||||
<ImportMap />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<ReconnectModal />
|
||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<script type="module" src="@Assets["Components/Layout/ReconnectModal.razor.js"]"></script>
|
||||
|
||||
<dialog id="components-reconnect-modal" data-nosnippet>
|
||||
<div class="components-reconnect-container">
|
||||
<div class="components-rejoining-animation" aria-hidden="true">
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<p class="components-reconnect-first-attempt-visible">
|
||||
Rejoining the server...
|
||||
</p>
|
||||
<p class="components-reconnect-repeated-attempt-visible">
|
||||
Rejoin failed... trying again in <span id="components-seconds-to-next-attempt"></span> seconds.
|
||||
</p>
|
||||
<p class="components-reconnect-failed-visible">
|
||||
Failed to rejoin.<br />Please retry or reload the page.
|
||||
</p>
|
||||
<button id="components-reconnect-button" class="components-reconnect-failed-visible">
|
||||
Retry
|
||||
</button>
|
||||
<p class="components-pause-visible">
|
||||
The session has been paused by the server.
|
||||
</p>
|
||||
<p class="components-resume-failed-visible">
|
||||
Failed to resume the session.<br />Please retry or reload the page.
|
||||
</p>
|
||||
<button id="components-resume-button" class="components-pause-visible components-resume-failed-visible">
|
||||
Resume
|
||||
</button>
|
||||
</div>
|
||||
</dialog>
|
||||
@@ -0,0 +1,157 @@
|
||||
.components-reconnect-first-attempt-visible,
|
||||
.components-reconnect-repeated-attempt-visible,
|
||||
.components-reconnect-failed-visible,
|
||||
.components-pause-visible,
|
||||
.components-resume-failed-visible,
|
||||
.components-rejoining-animation {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
|
||||
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
|
||||
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
|
||||
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
|
||||
#components-reconnect-modal.components-reconnect-retrying,
|
||||
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
|
||||
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
|
||||
#components-reconnect-modal.components-reconnect-failed,
|
||||
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
#components-reconnect-modal {
|
||||
background-color: white;
|
||||
width: 20rem;
|
||||
margin: 20vh auto;
|
||||
padding: 2rem;
|
||||
border: 0;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0;
|
||||
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
|
||||
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
|
||||
&[open]
|
||||
|
||||
{
|
||||
animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#components-reconnect-modal::backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes components-reconnect-modal-slideUp {
|
||||
0% {
|
||||
transform: translateY(30px) scale(0.95);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes components-reconnect-modal-fadeInOpacity {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes components-reconnect-modal-fadeOutOpacity {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.components-reconnect-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#components-reconnect-modal p {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#components-reconnect-modal button {
|
||||
border: 0;
|
||||
background-color: #6b9ed2;
|
||||
color: white;
|
||||
padding: 4px 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#components-reconnect-modal button:hover {
|
||||
background-color: #3b6ea2;
|
||||
}
|
||||
|
||||
#components-reconnect-modal button:active {
|
||||
background-color: #6b9ed2;
|
||||
}
|
||||
|
||||
.components-rejoining-animation {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.components-rejoining-animation div {
|
||||
position: absolute;
|
||||
border: 3px solid #0087ff;
|
||||
opacity: 1;
|
||||
border-radius: 50%;
|
||||
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
||||
}
|
||||
|
||||
.components-rejoining-animation div:nth-child(2) {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
|
||||
@keyframes components-rejoining-animation {
|
||||
0% {
|
||||
top: 40px;
|
||||
left: 40px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
4.9% {
|
||||
top: 40px;
|
||||
left: 40px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
5% {
|
||||
top: 40px;
|
||||
left: 40px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Set up event handlers
|
||||
const reconnectModal = document.getElementById("components-reconnect-modal");
|
||||
reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged);
|
||||
|
||||
const retryButton = document.getElementById("components-reconnect-button");
|
||||
retryButton.addEventListener("click", retry);
|
||||
|
||||
const resumeButton = document.getElementById("components-resume-button");
|
||||
resumeButton.addEventListener("click", resume);
|
||||
|
||||
function handleReconnectStateChanged(event) {
|
||||
if (event.detail.state === "show") {
|
||||
reconnectModal.showModal();
|
||||
} else if (event.detail.state === "hide") {
|
||||
reconnectModal.close();
|
||||
} else if (event.detail.state === "failed") {
|
||||
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
|
||||
} else if (event.detail.state === "rejected") {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
async function retry() {
|
||||
document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
|
||||
|
||||
try {
|
||||
// Reconnect will asynchronously return:
|
||||
// - true to mean success
|
||||
// - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
|
||||
// - exception to mean we didn't reach the server (this can be sync or async)
|
||||
const successful = await Blazor.reconnect();
|
||||
if (!successful) {
|
||||
// We have been able to reach the server, but the circuit is no longer available.
|
||||
// We'll reload the page so the user can continue using the app as quickly as possible.
|
||||
const resumeSuccessful = await Blazor.resumeCircuit();
|
||||
if (!resumeSuccessful) {
|
||||
location.reload();
|
||||
} else {
|
||||
reconnectModal.close();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// We got an exception, server is currently unavailable
|
||||
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
|
||||
}
|
||||
}
|
||||
|
||||
async function resume() {
|
||||
try {
|
||||
const successful = await Blazor.resumeCircuit();
|
||||
if (!successful) {
|
||||
location.reload();
|
||||
}
|
||||
} catch {
|
||||
reconnectModal.classList.replace("components-reconnect-paused", "components-reconnect-resume-failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function retryWhenDocumentBecomesVisible() {
|
||||
if (document.visibilityState === "visible") {
|
||||
await retry();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
@@ -1,10 +1,10 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||
@using Microsoft.JSInterop
|
||||
@using Web
|
||||
@using Web.Components
|
||||
|
||||
@@ -91,7 +91,7 @@ public static class WorkExperience
|
||||
new Point
|
||||
{
|
||||
Description =
|
||||
"Came to the conclusion that I wanted to focus my career on .NET, and left the company to pursue Blazor research and C# opportunities. Unfortunately, Blazor development isn't popular.",
|
||||
"Came to the conclusion that I wanted to focus my career on .NET, and left the company to pursue Blazor research and C# opportunities. Unfortunately, Blazor development isn't popular, and such specialization makes less sense in the AI era.",
|
||||
IsVisible = true
|
||||
}
|
||||
]
|
||||
|
||||
+24
-7
@@ -1,11 +1,28 @@
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Web.Components;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseStatusCodePagesWithReExecute("/not-found");
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapStaticAssets();
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "http://localhost:5202",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
@@ -15,7 +14,6 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "https://localhost:7263;http://localhost:5202",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
|
||||
+2
-6
@@ -1,15 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Web</RootNamespace>
|
||||
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.6" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Jonathan McCaffrey - Resume</title>
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="Web.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<svg class="loading-progress">
|
||||
<circle r="40%" cx="50%" cy="50%" />
|
||||
<circle r="40%" cx="50%" cy="50%" />
|
||||
</svg>
|
||||
<div class="loading-progress-text"></div>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="." class="reload">Reload</a>
|
||||
<span class="dismiss">🗙</span>
|
||||
</div>
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user