housekeeping updates #7

Merged
nseguin merged 15 commits from dev into main 2024-03-29 14:10:45 -05:00
24 changed files with 367 additions and 334 deletions

View file

@ -14,3 +14,5 @@ jobs:
- uses: actions/checkout@v4
- name: docker build
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>
@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,40 +1,26 @@
@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>
<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">
<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>
</h1>
</p>
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
<h1 class="mt-4 text-4xl md:text-5xl lg:text-6xl font-extrabold text-[2rem] leading-10 tracking-tighter">
Nick Seguin
</h1>
</h1>
<p class="mt-4 text-base leading-7 text-slate-900">
<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>
<div class="flex">
<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
@ -79,6 +65,4 @@
</a>
</div>
</div>
</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">
<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>
@code {
private string Path { get; set; }

View file

@ -1,24 +1,5 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly}">
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] {typeof(Client._Imports).Assembly, typeof(AsyncEventHandler).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>
<RouteView RouteData="@routeData" DefaultLayout="@typeof(SimpleLayout)"/>
</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>

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.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 @@
[
]