summaryrefslogtreecommitdiff
path: root/wwwroot
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2026-03-17 03:04:36 +1100
committerJake Mannens <jake@asger.xyz>2026-03-25 01:57:41 +1100
commitc751709b1b4fe6f16fd84647e8e071455e7b78d6 (patch)
tree47734a083d888660606e6cf6cf158c93e69a9807 /wwwroot
v0.1av0.1a
Diffstat (limited to 'wwwroot')
-rw-r--r--wwwroot/css/site.css28
-rw-r--r--wwwroot/favicon.icobin0 -> 3262 bytes
-rw-r--r--wwwroot/icon-192.pngbin0 -> 31523 bytes
-rw-r--r--wwwroot/icon-512.pngbin0 -> 136487 bytes
-rw-r--r--wwwroot/images/loginbg.webpbin0 -> 2247672 bytes
-rw-r--r--wwwroot/js/dialog.js78
-rw-r--r--wwwroot/js/keyboard.js51
-rw-r--r--wwwroot/loginbg.webmbin0 -> 390877 bytes
-rw-r--r--wwwroot/manifest.webmanifest6
-rw-r--r--wwwroot/styles/data-table.css21
-rw-r--r--wwwroot/styles/global.css169
11 files changed, 353 insertions, 0 deletions
diff --git a/wwwroot/css/site.css b/wwwroot/css/site.css
new file mode 100644
index 0000000..21f9a94
--- /dev/null
+++ b/wwwroot/css/site.css
@@ -0,0 +1,28 @@
+#blazor-error-ui {
+ background: #555;
+ 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: 3.5rem;
+ top: 0.5rem;
+}
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ color: white;
+ padding: 1rem 1rem 1rem 3.7rem;
+}
+
+.blazor-error-boundary::after {
+ content: "An error has occurred."
+}
diff --git a/wwwroot/favicon.ico b/wwwroot/favicon.ico
new file mode 100644
index 0000000..a1be4cc
--- /dev/null
+++ b/wwwroot/favicon.ico
Binary files differ
diff --git a/wwwroot/icon-192.png b/wwwroot/icon-192.png
new file mode 100644
index 0000000..28ce06d
--- /dev/null
+++ b/wwwroot/icon-192.png
Binary files differ
diff --git a/wwwroot/icon-512.png b/wwwroot/icon-512.png
new file mode 100644
index 0000000..8c28696
--- /dev/null
+++ b/wwwroot/icon-512.png
Binary files differ
diff --git a/wwwroot/images/loginbg.webp b/wwwroot/images/loginbg.webp
new file mode 100644
index 0000000..759e666
--- /dev/null
+++ b/wwwroot/images/loginbg.webp
Binary files differ
diff --git a/wwwroot/js/dialog.js b/wwwroot/js/dialog.js
new file mode 100644
index 0000000..418962f
--- /dev/null
+++ b/wwwroot/js/dialog.js
@@ -0,0 +1,78 @@
+function dialogMouseDown(e) {
+ bumpDialog(e.currentTarget);
+}
+
+function dialogTitleMouseDown(e) {
+ e = e || window.event;
+ e.preventDefault();
+ var element = e.currentTarget.parentElement;
+ var ds = element.dataset;
+ ds.lastX = e.clientX;
+ ds.lastY = e.clientY;
+
+ window.dragDialog = element;
+ document.onmouseup = dragMouseUp;
+ document.onmousemove = dragMouseMove;
+}
+
+function dragMouseUp() {
+ window.dragDialog = null;
+ document.onmouseup = null;
+ document.onmousemove = null;
+}
+
+function dragMouseMove(e) {
+ e = e || window.event;
+ e.preventDefault();
+ var element = window.dragDialog;
+ var ds = element.dataset;
+ deltaX = ds.lastX - e.clientX;
+ deltaY = ds.lastY - e.clientY;
+ ds.lastX = e.clientX;
+ ds.lastY = e.clientY;
+ element.style.left = (element.offsetLeft - deltaX) + 'px';
+ element.style.top = (element.offsetTop - deltaY) + 'px';
+}
+
+function setDialogVisibility(element, visible) {
+ if(visible) {
+ element.style.left = null;
+ element.style.top = null;
+ element.style.opacity = 1;
+ element.style.visibility = 'visible';
+ bumpDialog(element);
+
+ var input = element.querySelector('input[type="text"]');
+ if(input) {
+ setTimeout(() => input.focus(), 100);
+ }
+ } else {
+ element.style.opacity = 0;
+ element.style.visibility = 'hidden';
+ }
+}
+
+function bumpDialog(element) {
+ var dialogs = Array
+ .from(document.querySelectorAll('div.dialog'))
+ .map(e => ({ zIndex: parseInt(e.style.zIndex), element: e }))
+ .sort((a, b) => a.zIndex - b.zIndex)
+ .map(d => d.element)
+ .filter(e => e != element);
+
+ dialogs.push(element);
+
+ var z = 900;
+ for(var d of dialogs)
+ d.style.zIndex = z++;
+}
+
+function dialogAddObjectReference(element, dialogObject) {
+ if(!window.dialogObjects)
+ window.dialogObjects = []
+
+ window.dialogObjects.push({
+ element: element,
+ dialogObject: dialogObject
+ });
+}
diff --git a/wwwroot/js/keyboard.js b/wwwroot/js/keyboard.js
new file mode 100644
index 0000000..72ed3cb
--- /dev/null
+++ b/wwwroot/js/keyboard.js
@@ -0,0 +1,51 @@
+async function keyDownHandler(e) {
+ function isDialogChild(e) {
+ while(e = e.parentElement)
+ if(e.tagName == 'DIV' && e.classList.contains('dialog'))
+ return true;
+ return false;
+ }
+
+ var tag = document.activeElement.tagName;
+ if((tag == 'INPUT' || (tag == 'TEXTAREA' && e.ctrlKey)) && e.key == 'Enter') {
+ var element = document.activeElement;
+ while(element = element.parentElement) {
+ if(element.tagName == 'FORM') {
+ element.submit();
+ e.preventDefault();
+ return;
+ }
+ }
+ }
+
+ if((tag == 'INPUT' || tag == 'TEXTAREA') && e.key != 'Escape')
+ return;
+
+ var element = Array.from(document.querySelectorAll('div.dialog'))
+ .filter(e => e.style.visibility == 'visible')
+ .map(e => ({ element: e, zIndex: parseInt(e.style.zIndex) }))
+ .sort((a, b) => b.zIndex - a.zIndex)
+ .map(e => e.element)[0];
+
+ if(element) {
+ await window.dialogObjects
+ .find(d => d.element == element)
+ .dialogObject
+ .invokeMethodAsync('KeyHandler', e.key);
+ e.preventDefault();
+ return;
+ }
+
+ var button = Array.from(document.getElementsByTagName('button'))
+ .filter(b => typeof(b.dataset.keyboardShortcut) == 'string')
+ .filter(b => !isDialogChild(b))
+ .find(b => b.dataset.keyboardShortcut == e.key);
+
+ if(!e.ctrlKey && button) {
+ button.click();
+ e.preventDefault();
+ return;
+ }
+}
+
+window.onload = () => document.onkeydown = keyDownHandler; \ No newline at end of file
diff --git a/wwwroot/loginbg.webm b/wwwroot/loginbg.webm
new file mode 100644
index 0000000..139ed0d
--- /dev/null
+++ b/wwwroot/loginbg.webm
Binary files differ
diff --git a/wwwroot/manifest.webmanifest b/wwwroot/manifest.webmanifest
new file mode 100644
index 0000000..f150f98
--- /dev/null
+++ b/wwwroot/manifest.webmanifest
@@ -0,0 +1,6 @@
+{
+ "icons": [
+ { "src": "/icon-192.png", "type": "images/png", "sizes": "192x192" },
+ { "src": "/icon-512.png", "type": "images/png", "sizes": "512x512" }
+ ]
+} \ No newline at end of file
diff --git a/wwwroot/styles/data-table.css b/wwwroot/styles/data-table.css
new file mode 100644
index 0000000..994d625
--- /dev/null
+++ b/wwwroot/styles/data-table.css
@@ -0,0 +1,21 @@
+table.data-table {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+table.data-table > tr > th {
+ border-bottom: 1px solid white;
+ padding: 4px;
+}
+
+table.data-table > tr > td {
+ padding: 4px;
+}
+
+table.data-table > tr:nth-child(2n) {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+table.data-table > tr > td:not(:last-child) {
+ border-right: 1px solid white;
+}
diff --git a/wwwroot/styles/global.css b/wwwroot/styles/global.css
new file mode 100644
index 0000000..ebcda47
--- /dev/null
+++ b/wwwroot/styles/global.css
@@ -0,0 +1,169 @@
+@import url('data-table.css');
+
+:root {
+ --col-accent-pri: #0aa;
+ --col-accent-pri-hl: #0cc;
+ --col-error-pri: #ffaa00;
+ --col-checksum-verified-pri: #8dff76;
+ --col-bg: #222;
+ --col-dialog-bg: #333;
+ --col-navbar-bg: var(--col-accent-pri);
+ --col-button-pri: var(--col-accent-pri);
+ --col-button-pri-hl: var(--col-accent-pri-hl);
+ --col-button-disabled: #777;
+ --col-button-disabled-bg: #444;
+ --col-button-sec: #555;
+ --col-button-sec-hl: #777;
+ --col-button-sec-disabled: #555;
+ --col-button-sec-disabled-bg: #000;
+ --col-button-warning: #ff4848;
+ --col-button-warning-hl: #ff9999;
+ --col-scrollbar: #666666;
+ --col-scrollbar-hover: #aaaaaa;
+ --col-switch-bg: var(--col-bg);
+ --col-switch-fg: #fff;
+ --col-switch-bg-hl: var(--col-accent-pri);
+
+ --size-default-gap: 30px;
+}
+
+::selection {
+ background: var(--col-accent-pri);
+}
+
+body {
+ background: var(--col-bg);
+ color: white;
+ display: flex;
+ flex-direction: column;
+ font-family: 'Trebuchet MS', 'Lucida Sans Unicode';
+ margin: 0;
+ overflow: hidden;
+}
+
+a {
+ color: var(--col-accent-pri);
+ text-decoration: none;
+}
+
+a:hover {
+ filter: brightness(1.5);
+}
+
+a::selection {
+ background: var(--col-accent-pri);
+ color: #fff;
+}
+
+a.nondecorated {
+ color: #fff;
+}
+
+a.nondecorated:hover {
+ color: #999;
+}
+
+code {
+ background: #222;
+ border-radius: 10px;
+ box-sizing: border-box;
+ font-family: 'Lucida Console';
+ font-size: 8pt;
+ overflow-y: auto;
+ padding: 20px;
+ white-space: pre-line;
+}
+
+button, input[type=submit] {
+ background: var(--col-button-pri);
+ border-radius: 10px;
+ border: none;
+ box-sizing: border-box;
+ color: white;
+ cursor: pointer;
+ height: 30px;
+ margin: 10px 5px 0 5px;
+ padding: 0 9px 0 9px;
+ user-select: none;
+}
+
+button:disabled {
+ color: var(--col-button-disabled) !important;
+ background: var(--col-button-disabled-bg) !important;
+}
+
+button.warning {
+ background: var(--col-button-warning);
+}
+
+button.warning:hover {
+ background: var(--col-button-warning-hl);
+}
+
+button.warning:active {
+ color: var(--col-button-warning);
+ background: white;
+}
+
+button.secondary {
+ background: var(--col-button-sec);
+}
+
+button.secondary:hover {
+ background: var(--col-button-sec-hl);
+}
+
+button.secondary:active {
+ background: white;
+ color: var(--col-button-sec);
+}
+
+button.secondary:disabled {
+ color: var(--col-button-sec-disabled) !important;
+ background: var(--col-button-sec-disabled-bg) !important;
+}
+
+button:hover, input[type=submit]:hover {
+ background: var(--col-button-pri-hl);
+}
+
+button:active, input[type=submit]:active {
+ background: white;
+ color: var(--col-button-pri);
+}
+
+input, textarea {
+ background: rgba(0, 0, 0, 0);
+ border-radius: 5px;
+ border: 1px solid #aaa;
+ box-sizing: border-box;
+ color: white;
+ margin-bottom: 10px;
+}
+
+input {
+ height: 25px !important;
+}
+
+/* necessary for use inside flex containers */
+hr {
+ width: 100%;
+}
+
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--col-scrollbar);
+ border-radius: 10px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--col-scrollbar-hover);
+}
+
+::-webkit-scrollbar-corner {
+ opacity: 0;
+}