commit 818c0441f80b939949d544dfdf1637bda36df139 Author: Chris Bell Date: Sat May 31 00:24:29 2025 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbad530 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +SessionZeroClient/obj +SessionZeroClient/bin \ No newline at end of file diff --git a/SessionZeroClient.sln b/SessionZeroClient.sln new file mode 100644 index 0000000..6e0098b --- /dev/null +++ b/SessionZeroClient.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SessionZeroClient", "SessionZeroClient\SessionZeroClient.csproj", "{FB52EC9D-387C-4E96-A70B-2A8BD67BFC25}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FB52EC9D-387C-4E96-A70B-2A8BD67BFC25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB52EC9D-387C-4E96-A70B-2A8BD67BFC25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB52EC9D-387C-4E96-A70B-2A8BD67BFC25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB52EC9D-387C-4E96-A70B-2A8BD67BFC25}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/SessionZeroClient/App.razor b/SessionZeroClient/App.razor new file mode 100644 index 0000000..c7730d1 --- /dev/null +++ b/SessionZeroClient/App.razor @@ -0,0 +1,12 @@ + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
\ No newline at end of file diff --git a/SessionZeroClient/Components/Navigation.razor b/SessionZeroClient/Components/Navigation.razor new file mode 100644 index 0000000..b3cf6b4 --- /dev/null +++ b/SessionZeroClient/Components/Navigation.razor @@ -0,0 +1,5 @@ + + +@code { + +} \ No newline at end of file diff --git a/SessionZeroClient/Components/Navigation.razor.cs b/SessionZeroClient/Components/Navigation.razor.cs new file mode 100644 index 0000000..efd6501 --- /dev/null +++ b/SessionZeroClient/Components/Navigation.razor.cs @@ -0,0 +1,7 @@ +using Microsoft.AspNetCore.Components; + +namespace SessionZeroClient.Components; + +public partial class Navigation : ComponentBase +{ +} \ No newline at end of file diff --git a/SessionZeroClient/Components/Navigation.razor.css b/SessionZeroClient/Components/Navigation.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/SessionZeroClient/Layout/MainLayout.razor b/SessionZeroClient/Layout/MainLayout.razor new file mode 100644 index 0000000..53a4e95 --- /dev/null +++ b/SessionZeroClient/Layout/MainLayout.razor @@ -0,0 +1,3 @@ +@inherits LayoutComponentBase + +@Body \ No newline at end of file diff --git a/SessionZeroClient/Pages/Home.razor b/SessionZeroClient/Pages/Home.razor new file mode 100644 index 0000000..5aa3541 --- /dev/null +++ b/SessionZeroClient/Pages/Home.razor @@ -0,0 +1,6 @@ +@page "/" +@using SessionZeroClient.Components + +SessionZero - Home + + \ No newline at end of file diff --git a/SessionZeroClient/Pages/Home.razor.css b/SessionZeroClient/Pages/Home.razor.css new file mode 100644 index 0000000..e69de29 diff --git a/SessionZeroClient/Program.cs b/SessionZeroClient/Program.cs new file mode 100644 index 0000000..4181e18 --- /dev/null +++ b/SessionZeroClient/Program.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using SessionZeroClient; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/SessionZeroClient/Properties/launchSettings.json b/SessionZeroClient/Properties/launchSettings.json new file mode 100644 index 0000000..7c5647f --- /dev/null +++ b/SessionZeroClient/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5023", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7104;http://localhost:5023", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SessionZeroClient/SessionZeroClient.csproj b/SessionZeroClient/SessionZeroClient.csproj new file mode 100644 index 0000000..fccd3b1 --- /dev/null +++ b/SessionZeroClient/SessionZeroClient.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + service-worker-assets.js + + + + + + + + + + + + diff --git a/SessionZeroClient/_Imports.razor b/SessionZeroClient/_Imports.razor new file mode 100644 index 0000000..cdc1fb7 --- /dev/null +++ b/SessionZeroClient/_Imports.razor @@ -0,0 +1,10 @@ +@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 Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using SessionZeroClient +@using SessionZeroClient.Layout \ No newline at end of file diff --git a/SessionZeroClient/wwwroot/css/app.css b/SessionZeroClient/wwwroot/css/app.css new file mode 100644 index 0000000..7bc88b2 --- /dev/null +++ b/SessionZeroClient/wwwroot/css/app.css @@ -0,0 +1,88 @@ +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid red; +} + +.validation-message { + color: red; +} + +#blazor-error-ui { + color-scheme: light only; + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + box-sizing: border-box; + 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; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { + color: var(--bs-secondary-color); + text-align: end; +} + +.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { + text-align: start; +} \ No newline at end of file diff --git a/SessionZeroClient/wwwroot/css/main.css b/SessionZeroClient/wwwroot/css/main.css new file mode 100644 index 0000000..73fb60c --- /dev/null +++ b/SessionZeroClient/wwwroot/css/main.css @@ -0,0 +1,13 @@ +:root { + --background-color: #2c3930; + --text-color: #dcd7c9; + --accent-color-green-1: #8fb49a; + --accent-color-green-2: #6a7b6f; + --accent-color-blue-1: #1d3947; + --accent-color-blue-2: #4c6878; +} + +html { + color: var(--text-color); + background-color: var(--background-color); +} \ No newline at end of file diff --git a/SessionZeroClient/wwwroot/icon-192.png b/SessionZeroClient/wwwroot/icon-192.png new file mode 100644 index 0000000..841a9db Binary files /dev/null and b/SessionZeroClient/wwwroot/icon-192.png differ diff --git a/SessionZeroClient/wwwroot/icon-512.png b/SessionZeroClient/wwwroot/icon-512.png new file mode 100644 index 0000000..f3f5b75 Binary files /dev/null and b/SessionZeroClient/wwwroot/icon-512.png differ diff --git a/SessionZeroClient/wwwroot/index.html b/SessionZeroClient/wwwroot/index.html new file mode 100644 index 0000000..ce6b376 --- /dev/null +++ b/SessionZeroClient/wwwroot/index.html @@ -0,0 +1,36 @@ + + + + + + + SessionZeroClient + + + + + + + + + + + +
+ + + + +
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + + diff --git a/SessionZeroClient/wwwroot/manifest.webmanifest b/SessionZeroClient/wwwroot/manifest.webmanifest new file mode 100644 index 0000000..9ea8a47 --- /dev/null +++ b/SessionZeroClient/wwwroot/manifest.webmanifest @@ -0,0 +1,22 @@ +{ + "name": "SessionZero", + "short_name": "SessionZero", + "id": "./", + "start_url": "./", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#03173d", + "prefer_related_applications": false, + "icons": [ + { + "src": "icon-512.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "icon-192.png", + "type": "image/png", + "sizes": "192x192" + } + ] +} diff --git a/SessionZeroClient/wwwroot/service-worker.js b/SessionZeroClient/wwwroot/service-worker.js new file mode 100644 index 0000000..fe614da --- /dev/null +++ b/SessionZeroClient/wwwroot/service-worker.js @@ -0,0 +1,4 @@ +// In development, always fetch from the network and do not enable offline support. +// This is because caching would make development more difficult (changes would not +// be reflected on the first load after each change). +self.addEventListener('fetch', () => { }); diff --git a/SessionZeroClient/wwwroot/service-worker.published.js b/SessionZeroClient/wwwroot/service-worker.published.js new file mode 100644 index 0000000..1f7f543 --- /dev/null +++ b/SessionZeroClient/wwwroot/service-worker.published.js @@ -0,0 +1,55 @@ +// Caution! Be sure you understand the caveats before publishing an application with +// offline support. See https://aka.ms/blazor-offline-considerations + +self.importScripts('./service-worker-assets.js'); +self.addEventListener('install', event => event.waitUntil(onInstall(event))); +self.addEventListener('activate', event => event.waitUntil(onActivate(event))); +self.addEventListener('fetch', event => event.respondWith(onFetch(event))); + +const cacheNamePrefix = 'offline-cache-'; +const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; +const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; +const offlineAssetsExclude = [ /^service-worker\.js$/ ]; + +// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'. +const base = "/"; +const baseUrl = new URL(base, self.origin); +const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href); + +async function onInstall(event) { + console.info('Service worker: Install'); + + // Fetch and cache all matching items from the assets manifest + const assetsRequests = self.assetsManifest.assets + .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) + .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) + .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); + await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); +} + +async function onActivate(event) { + console.info('Service worker: Activate'); + + // Delete unused caches + const cacheKeys = await caches.keys(); + await Promise.all(cacheKeys + .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) + .map(key => caches.delete(key))); +} + +async function onFetch(event) { + let cachedResponse = null; + if (event.request.method === 'GET') { + // For all navigation requests, try to serve index.html from cache, + // unless that request is for an offline resource. + // If you need some URLs to be server-rendered, edit the following check to exclude those URLs + const shouldServeIndexHtml = event.request.mode === 'navigate' + && !manifestUrlList.some(url => url === event.request.url); + + const request = shouldServeIndexHtml ? 'index.html' : event.request; + const cache = await caches.open(cacheName); + cachedResponse = await cache.match(request); + } + + return cachedResponse || fetch(event.request); +}