mirror of
https://git.sr.ht/~ashkeel/strimertul-website
synced 2024-12-21 23:12:19 +00:00
127 lines
3.8 KiB
HTML
127 lines
3.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Online log viewer</title>
|
|
<style>
|
|
@import url("https://rsms.me/inter/inter.css");
|
|
@import url("https://iosevka-webfonts.github.io/iosevka/iosevka.css");
|
|
:root {
|
|
--site-bg: #171717;
|
|
--white: #ededed;
|
|
background-color: var(--site-bg);
|
|
color: var(--white);
|
|
font-family: "Inter", "Gill Sans", "Gill Sans MT", "Segoe UI",
|
|
sans-serif;
|
|
}
|
|
html,
|
|
body {
|
|
margin: 0;
|
|
}
|
|
main {
|
|
display: flex;
|
|
}
|
|
#starting {
|
|
height: 100vh;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
code {
|
|
display: inline-block;
|
|
font-family: "Iosevka", monospace;
|
|
background-color: #222;
|
|
border-radius: 3px;
|
|
padding: 3px 6px;
|
|
}
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main id="app">
|
|
<section id="starting">
|
|
<h1>Online log viewer</h1>
|
|
<p>
|
|
Drag a <code>strimertul.log</code> in this window to view it (or click
|
|
anywhere to open a file dialog).<br />
|
|
The contents will not be uploaded anywhere, this webpage is fully
|
|
local.
|
|
</p>
|
|
</section>
|
|
<table id="logs" class="hidden">
|
|
<thead>
|
|
<tr>
|
|
<th>Level</th>
|
|
<th>Time</th>
|
|
<th>Caller</th>
|
|
<th>Message</th>
|
|
<th>Data</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</main>
|
|
<script>
|
|
// @ts-check
|
|
|
|
const mainEl = document.getElementById("app");
|
|
const startingEl = document.getElementById("starting");
|
|
const tableEl = document.getElementById("logs");
|
|
|
|
// Click handler (open file select)
|
|
startingEl?.addEventListener("click", (ev) => {
|
|
// Create temporary file input and click it
|
|
const fileSelect = document.createElement("input");
|
|
fileSelect.type = "file";
|
|
fileSelect.accept = ".log";
|
|
fileSelect.addEventListener("change", (ev) => {
|
|
if (ev.target instanceof HTMLInputElement && ev.target.files) {
|
|
const file = ev.target.files[0];
|
|
startingEl.innerHTML = "Loading...";
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
reader.addEventListener("load", (ev) => {
|
|
const logs =
|
|
ev.target?.result
|
|
?.toString()
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0)
|
|
.map((line) => {
|
|
return makeLog(JSON.parse(line));
|
|
}) || [];
|
|
tableEl?.querySelector("tbody")?.replaceChildren(...logs);
|
|
tableEl?.classList.remove("hidden");
|
|
startingEl.classList.add("hidden");
|
|
});
|
|
reader.readAsText(file);
|
|
}
|
|
}
|
|
});
|
|
fileSelect.click();
|
|
});
|
|
|
|
function makeLog(log) {
|
|
const tr = document.createElement("tr");
|
|
const { level, ts, caller, msg, ...data } = log;
|
|
tr.appendChild(makeCell(level));
|
|
tr.appendChild(makeCell(new Date(ts * 1000).toISOString()));
|
|
tr.appendChild(makeCell(caller));
|
|
tr.appendChild(makeCell(msg));
|
|
tr.appendChild(makeCell(JSON.stringify(data)));
|
|
return tr;
|
|
}
|
|
|
|
function makeCell(text) {
|
|
const td = document.createElement("td");
|
|
td.appendChild(document.createTextNode(text));
|
|
return td;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|