Merge pull request 'housekeeping updates' (#7) from dev into main
deploy / deploy (push) Successful in 3m10s Details

Reviewed-on: #7
This commit is contained in:
Nick Seguin 2024-03-29 14:10:44 -05:00
commit ab666e4efe
24 changed files with 367 additions and 334 deletions

View File

@ -13,4 +13,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: docker build
run: docker build -t nseguin42/website:${{ github.sha }} .
run: docker build -t nseguin42/website:${{ github.sha }} .
- name: cleanup old images
run: docker image prune -f --filter "until=24h"

View File

@ -1,19 +1,30 @@
<!DOCTYPE html>
@using Microsoft.AspNetCore.Mvc.ViewFeatures
@using JetBrains.Annotations
@inject IFileVersionProvider FileVersionProvider
<!DOCTYPE html>
<html lang="en">
<!--suppress HtmlRequiredTitleElement -->
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="/"/>
<link rel="stylesheet" href="css/styles.css" asp-append-version="true"/>
<link rel="stylesheet" href="app.css"/>
<link rel="stylesheet" href="NSeguin.Dev.Web.styles.css"/>
<base href="@PathBase"/>
<link rel="stylesheet" href="@AddVersionToPath("css/styles.css")"/>
<link rel="stylesheet" href="@AddVersionToPath("app.css")"/>
<link rel="icon" type="image/png" href="favicon.ico"/>
<HeadOutlet/>
</head>
<body>
<Routes/>
@* ReSharper disable once Html.PathError *@
<script src="_framework/blazor.web.js"></script>
<script src="js/app.js"></script>
<script src="@AddVersionToPath("js/app.js")"></script>
</body>
</html>
</html>
@code {
private const string PathBase = "/";
private string AddVersionToPath([PathReference] string path) => FileVersionProvider.AddFileVersionToPath(PathBase, path);
}

View File

@ -1,15 +1,26 @@
@inherits LayoutComponentBase
<div class="page">
<main>
<article class="content px-4">
@Body
</article>
</main>
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
<svg
viewBox="0 0 1080 957"
fill="none"
aria-hidden="true"
class="absolute inset-0 h-full w-full"
preserveAspectRatio="xMinYMid slice">
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
</svg>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<main>
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
<div class="mx-auto max-w-xl lg:mx-0">
<article class="px-4">
<ErrorBoundary>
@Body
</ErrorBoundary>
</article>
</div>
</div>
</main>
<BlazorErrorUI/>

View File

@ -1,92 +0,0 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@ -0,0 +1,65 @@
@page "/attributions"
@using System.Text.Json
<PageTitle>Attributions</PageTitle>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
Attributions
</h1>
<p class="mt-4 text-base text-slate-900">
The source code for this website is available on
<a href="https://www.git.nseguin.dev/nseguin/website" class="text-blue-600">Forgejo (git.nseguin.dev).</a>
</p>
<p class="mt-4 text-base text-slate-900">
It is licensed under the <a href="@LicenseUrl" class="text-blue-600">MIT License</a>.
</p>
<ul class="list-disc mt-4 text-base leading-7 text-slate-900">
@if (AttributionsList != null)
{
@foreach (var attribution in AttributionsList)
{
<li class="mt-4" key="@attribution.Name">
<h2 class="text-lg font-bold">@attribution.Name</h2>
@if (!string.IsNullOrWhiteSpace(attribution.Url?.ToString()))
{
<p class="mt-2">URL: <a href="@attribution.Url.ToString()" class="text-blue-600">@attribution.Url</a></p>
}
@if (attribution.Authors?.Any() ?? false)
{
<p class="mt-2">Author(s): @string.Join(", ", attribution.Authors)</p>
}
@if (!string.IsNullOrWhiteSpace(attribution.License))
{
<p class="mt-2">License: <a href="@attribution.LicenseUrl?.ToString()" class="text-blue-600">@attribution.License</a></p>
}
</li>
}
}
</ul>
@code {
public record Attribution(string Name, Uri Url, string? License, Uri? LicenseUrl, string[]? Authors);
private string? AttributionsJson { get; set; }
private List<Attribution>? AttributionsList { get; set; }
private string? LicenseUrl => "static/LICENSE.txt";
[Inject]
private IWebHostEnvironment HostEnvironment { get; set; }
protected override async Task OnInitializedAsync()
{
var attributionsFile = HostEnvironment.WebRootFileProvider.GetFileInfo("static/attributions.json");
using var reader = new StreamReader(attributionsFile.CreateReadStream());
AttributionsJson = await reader.ReadToEndAsync();
AttributionsList = JsonSerializer.Deserialize<List<Attribution>>(AttributionsJson);
await base.OnInitializedAsync();
}
}

View File

@ -1,42 +0,0 @@
@page "/authentication/{action}"
@using System.Security.Claims
@code{
private const string Login = "login";
private const string Logout = "logout";
public ClaimsPrincipal? User { get; private set; }
[CascadingParameter]
public required Task<AuthenticationState> AuthenticationState { get; set; }
[Parameter]
public string? Action { get; set; }
[Inject]
public required NavigationManager NavigationManager { get; set; }
private void SignOut()
{
NavigationManager.NavigateTo("MicrosoftIdentity/Account/SignOut", true);
}
private void SignIn()
{
NavigationManager.NavigateTo("MicrosoftIdentity/Account/SignIn", true);
}
protected override async Task OnInitializedAsync()
{
var authenticationState = await AuthenticationState;
User = authenticationState.User;
if (Action == Logout)
{
SignOut();
}
else if (Action == Login)
{
SignIn();
}
}
}

View File

@ -1,40 +1,49 @@
@page "/Error"
@using System.Diagnostics
@using Microsoft.AspNetCore.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Error</h1>
<p class="text-slate text-xl mt-4">
An error occurred while processing your request.
</p>
@if (ShowRequestId)
{
<p>
<p class="text-slate text-md mt-4 mb-4">
<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>
@if (ShowException)
{
<p class="text-slate mt-4 mb-4">
<h2 class="text-2xl font-bold">Exception</h2>
<pre class="text-md">
<code>@(Exception?.ToString())</code>
</pre>
</p>
}
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
[Inject]
private IHostEnvironment HostEnvironment { get; set; } = default!;
private Exception? Exception { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private bool ShowException => Exception is not null && HostEnvironment.IsDevelopment();
protected override void OnInitialized()
{
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
Exception = HttpContext?.Features.Get<IExceptionHandlerFeature>()?.Error ?? new Exception("An error occurred while processing your request.");
}
}

View File

@ -1,84 +1,68 @@
@page "/"
@using NSeguin.Dev.Web.Components.Shared.HeroIcons
@layout SimpleLayout
@rendermode InteractiveServer
<PageTitle>Home</PageTitle>
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
<svg
viewBox="0 0 1080 957"
fill="none"
aria-hidden="true"
class="absolute inset-0 h-full w-full"
preserveAspectRatio="xMinYMid slice">
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
</svg>
</div>
<p class="mt-10 flex items-center text-sm font-semibold leading-6 text-brand">
<a href="https://git.nseguin.dev/nseguin/website">nseguin/website</a>
<small class="ml-3 rounded-full bg-brand/5 px-2 text-[0.8125rem] font-medium leading-6">
</small>
</p>
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
<div class="mx-auto max-w-xl lg:mx-0">
<h1 class="mt-10 flex items-center text-sm font-semibold leading-6 text-brand">
<a href="https://git.nseguin.dev/nseguin/website">nseguin/website</a>
<small class="ml-3 rounded-full bg-brand/5 px-2 text-[0.8125rem] font-medium leading-6">
</small>
</h1>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
Nick Seguin
</h1>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
Nick Seguin
</h1>
<p class="mt-4 text-base leading-7 text-slate-900">
I'm a software developer based in Iowa.
I'm currently working on a few projects, including this website.
Thanks for visiting!
</p>
<p class="mt-4 text-base leading-7 text-slate-900">
I'm a software developer based in Iowa.
I'm currently working on a few projects, including this website.
Thanks for visiting!
</p>
<div class="flex">
<div class="w-full sm:w-auto">
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
<a
href="/resume"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col">
<div class="w-6 h-6">
<HeroIcon Kind="HeroIcon.DocumentText"/>
</div>
Resumé
</span>
</a>
<a
href="https://github.com/nseguin42"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col">
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 0C5.37 0 0 5.506 0 12.303c0 5.445 3.435 10.043 8.205 11.674.6.107.825-.262.825-.585 0-.292-.015-1.261-.015-2.291C6 21.67 5.22 20.346 4.98 19.654c-.135-.354-.72-1.446-1.23-1.738-.42-.23-1.02-.8-.015-.815.945-.015 1.62.892 1.845 1.261 1.08 1.86 2.805 1.338 3.495 1.015.105-.8.42-1.338.765-1.645-2.67-.308-5.46-1.37-5.46-6.075 0-1.338.465-2.446 1.23-3.307-.12-.308-.54-1.569.12-3.26 0 0 1.005-.323 3.3 1.26.96-.276 1.98-.415 3-.415s2.04.139 3 .416c2.295-1.6 3.3-1.261 3.3-1.261.66 1.691.24 2.952.12 3.26.765.861 1.23 1.953 1.23 3.307 0 4.721-2.805 5.767-5.475 6.075.435.384.81 1.122.81 2.276 0 1.645-.015 2.968-.015 3.383 0 .323.225.707.825.585a12.047 12.047 0 0 0 5.919-4.489A12.536 12.536 0 0 0 24 12.304C24 5.505 18.63 0 12 0Z"
fill="#18181B"/>
</svg>
GitHub
</span>
</a>
<a
href="mailto:mail@nseguin.dev"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col text-black">
<div class="w-6 h-6">
<HeroIcon Kind="HeroIcon.Envelope"/>
</div>
Contact
</span>
</a>
</div>
</div>
<div class="flex">
<div class="w-full sm:w-auto">
<div class="mt-10 grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
<a
href="/resume"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col">
<div class="w-6 h-6">
<HeroIcon Kind="HeroIcon.DocumentText"/>
</div>
Resumé
</span>
</a>
<a
href="https://github.com/nseguin42"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col">
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-6 w-6">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 0C5.37 0 0 5.506 0 12.303c0 5.445 3.435 10.043 8.205 11.674.6.107.825-.262.825-.585 0-.292-.015-1.261-.015-2.291C6 21.67 5.22 20.346 4.98 19.654c-.135-.354-.72-1.446-1.23-1.738-.42-.23-1.02-.8-.015-.815.945-.015 1.62.892 1.845 1.261 1.08 1.86 2.805 1.338 3.495 1.015.105-.8.42-1.338.765-1.645-2.67-.308-5.46-1.37-5.46-6.075 0-1.338.465-2.446 1.23-3.307-.12-.308-.54-1.569.12-3.26 0 0 1.005-.323 3.3 1.26.96-.276 1.98-.415 3-.415s2.04.139 3 .416c2.295-1.6 3.3-1.261 3.3-1.261.66 1.691.24 2.952.12 3.26.765.861 1.23 1.953 1.23 3.307 0 4.721-2.805 5.767-5.475 6.075.435.384.81 1.122.81 2.276 0 1.645-.015 2.968-.015 3.383 0 .323.225.707.825.585a12.047 12.047 0 0 0 5.919-4.489A12.536 12.536 0 0 0 24 12.304C24 5.505 18.63 0 12 0Z"
fill="#18181B"/>
</svg>
GitHub
</span>
</a>
<a
href="mailto:mail@nseguin.dev"
class="group relative rounded-2xl px-6 py-4 text-sm font-semibold leading-6 text-nord-1 sm:py-6">
<span class="absolute inset-0 rounded-2xl bg-slate-50 transition group-hover:bg-slate-100 sm:group-hover:scale-105">
</span>
<span class="relative flex items-center gap-4 sm:flex-col text-black">
<div class="w-6 h-6">
<HeroIcon Kind="HeroIcon.Envelope"/>
</div>
Contact
</span>
</a>
</div>
</div>
</div>

View File

@ -1,34 +1,17 @@
@page "/NotFound"
@using Microsoft.AspNetCore.WebUtilities
@layout SimpleLayout
@inject NavigationManager NavigationManager
<PageTitle>Not Found</PageTitle>
<div class="fixed inset-y-0 right-0 left-[40rem] hidden lg:block xl:left-[50rem] z-0">
<svg
viewBox="0 0 1080 957"
fill="none"
aria-hidden="true"
class="absolute inset-0 h-full w-full"
preserveAspectRatio="xMinYMid slice">
<path fill="#2e3440" d="M0 0h1080v957H0z"/>
</svg>
</div>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Not Found</h1>
<p class="text-slate text-xl mt-4">
No page was found at <code>@Path</code>.
</p>
<div class="px-4 py-10 sm:py-28 sm:px-6 lg:px-8 xl:py-32 xl:px-28">
<div class="mx-auto max-w-xl lg:mx-0">
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-semibold text-[2rem] leading-10 tracking-tighter">Not Found</h1>
<p class="text-slate text-xl mt-4">
No page was found at <code>@Path</code>.<br/>
</p>
<p class="text-blue-600 text-xl mt-4">
<a href="/" aria-label="Home" class="underline">Go to home page</a>
</p>
</div>
</div>
<p class="text-blue-600 text-xl mt-4">
<a href="/" aria-label="Home" class="underline">Go to home page</a>
</p>
@code {
private string Path { get; set; }

View File

@ -1,24 +1,5 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly}">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(SimpleLayout)">
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true)
{
<RedirectToLogin/>
}
else
{
<p role="alert">You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(SimpleLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly, typeof(AsyncEventHandler).Assembly}">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(SimpleLayout)"/>
</Found>
</Router>

View File

@ -1,14 +1,21 @@
@inject IJSRuntime JSRuntime
@code {
[Parameter]
public Func<ElementReference> GetControl { get; set; }
public Func<ElementReference>? GetControl { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (GetControl is null)
{
throw new ArgumentNullException(nameof(GetControl));
}
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("App.scrollIntoView", GetControl(), true);
}
}
}

View File

@ -0,0 +1,11 @@
@inject IHostEnvironment HostEnvironment
<div id="blazor-error-ui"
class="bg-yellow-400 bottom-0 shadow-md fixed left-0 py-2 px-4 w-full z-50 hidden"
role="alertdialog"
tabindex="-1"
data-nosnippet>
An unhandled error has occurred.
<a href="#" class="reload underline">Reload</a>
<button class="dismiss" type="button">🗙</button>
</div>

View File

@ -1,20 +0,0 @@
@inject NavigationManager Navigation
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
<button class="nav-link btn btn-link" @onclick="BeginLogout">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code{
private void BeginLogout(MouseEventArgs args)
{
Navigation.NavigateTo("authentication/logout");
}
}

View File

@ -1,10 +0,0 @@
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}

View File

@ -38,6 +38,12 @@
<AdditionalFiles Include="pnpm-lock.yaml" CopyToOutputDirectory="Always"/>
<Content Remove="libman.json"/>
<AdditionalFiles Include="libman.json" CopyToOutputDirectory="Always"/>
<Content Update="wwwroot\static\attributions.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\static\LICENSE.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=components_005Cmodels/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,6 +1,8 @@
using Azure.Identity;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileProviders;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
@ -30,6 +32,20 @@ builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddProblemDetails(
options =>
{
options.CustomizeProblemDetails = ctx =>
{
ctx.ProblemDetails.Extensions.TryAdd("traceId", ctx.HttpContext.TraceIdentifier);
if (ctx.HttpContext.RequestServices.GetRequiredService<IHostEnvironment>()
.IsDevelopment())
{
ctx.ProblemDetails.Extensions.TryAdd("exception", ctx.Exception?.ToString());
}
};
});
if (builder.Environment.IsStaging() || builder.Environment.IsProduction())
{
builder.Configuration.AddAzureAppConfiguration(
@ -97,14 +113,21 @@ app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddAdditionalAssemblies(typeof(Counter).Assembly)
.AddAdditionalAssemblies(typeof(AsyncEventHandler).Assembly)
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode();
app.MapControllers();
app.UseExceptionHandler();
app.MapFallback(
ctx =>
{
ctx.Response.Redirect($"/NotFound?path={ctx.Request.Path}");
ctx.Response.StatusCode = StatusCodes.Status404NotFound;
var headers = ctx.Request.GetTypedHeaders();
if (headers.Accept.Any(h => h.MatchesMediaType("text/html")))
{
ctx.Response.Redirect($"/NotFound?path={ctx.Request.Path}");
}
return Task.CompletedTask;
});

View File

@ -2,7 +2,6 @@
"name": "nseguin.dev.web",
"type": "module",
"devDependencies": {
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
@ -12,7 +11,11 @@
"css:watch": "pnpm tailwindcss -i ./wwwroot/css/site.css -o ./wwwroot/css/styles.css -c ./tailwind.config.ts --watch"
},
"dependencies": {
"tailwind-nord": "^1.3.0",
"tailwindcss": "^3.4.1"
"tailwindcss": "^3.4.1",
"@tailwindcss/typography": "^0.5.10",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/container-queries": "^0.1.1",
"tailwind-nord": "^1.3.0"
}
}

View File

@ -5,6 +5,18 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@tailwindcss/aspect-ratio':
specifier: ^0.4.2
version: 0.4.2(tailwindcss@3.4.1)
'@tailwindcss/container-queries':
specifier: ^0.1.1
version: 0.1.1(tailwindcss@3.4.1)
'@tailwindcss/forms':
specifier: ^0.5.7
version: 0.5.7(tailwindcss@3.4.1)
'@tailwindcss/typography':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.4.1)
tailwind-nord:
specifier: ^1.3.0
version: 1.3.0
@ -15,11 +27,6 @@ dependencies:
specifier: ^5.0.0
version: 5.3.3
devDependencies:
bun-types:
specifier: latest
version: 1.0.15
packages:
/@alloc/quick-lru@5.2.0:
@ -78,6 +85,43 @@ packages:
fastq: 1.15.0
dev: false
/@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.4.1):
resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==}
peerDependencies:
tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
dependencies:
tailwindcss: 3.4.1
dev: false
/@tailwindcss/container-queries@0.1.1(tailwindcss@3.4.1):
resolution: {integrity: sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==}
peerDependencies:
tailwindcss: '>=3.2.0'
dependencies:
tailwindcss: 3.4.1
dev: false
/@tailwindcss/forms@0.5.7(tailwindcss@3.4.1):
resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==}
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.1
dev: false
/@tailwindcss/typography@0.5.10(tailwindcss@3.4.1):
resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.1
dev: false
/any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
dev: false
@ -117,10 +161,6 @@ packages:
fill-range: 7.0.1
dev: false
/bun-types@1.0.15:
resolution: {integrity: sha512-XkEvWLV1JIhcVIpf2Lu6FXnZUxRUkQVJmgY+VT7os6Tk5X1nkXx11q4Rtu6txsqpDJZfUeZXblnnD59K+6wsVA==}
dev: true
/camelcase-css@2.0.1:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
@ -296,6 +336,18 @@ packages:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: false
/lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
dev: false
/lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
dev: false
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: false
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -309,6 +361,11 @@ packages:
picomatch: 2.3.1
dev: false
/mini-svg-data-uri@1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
dev: false
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@ -427,6 +484,14 @@ packages:
postcss-selector-parser: 6.0.13
dev: false
/postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
dev: false
/postcss-selector-parser@6.0.13:
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
engines: {node: '>=4'}

View File

@ -145,7 +145,11 @@ export default {
}
},
plugins: [
require('tailwind-nord')
require('tailwind-nord'),
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/container-queries')
]
}
} satisfies Config

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,15 @@
var App = App || {};
const App = App || {};
App.setFocus = function (element) {
element.focus();
}
App.scrollIntoView = function (element, args) {
if (element === undefined) {
console.error('Element is undefined');
} else if (element === null) {
console.error('Element is null');
}
element.scrollIntoView(args);
}

View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2023 Nicholas Seguin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,2 @@
[
]