Files
PLG-MuDiCS/display/pkg/main.templ
T

311 lines
7.6 KiB
Plaintext

package pkg
import "strings"
templ basicTemplate() {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>PLG MuDiCS Display</title>
<style>
:root {
--background-color: black;
--foreground-color: oklch(92.3% 0.003 48.717);
--font-size: 5rem;
}
body {
display: flex;
justify-content: center;
/* centers horizontally */
align-items: center;
/* centers vertically */
width: 100vw;
/* Viewport width */
height: 100vh;
/* Viewport height */
margin: 0;
padding: 0;
overflow: hidden;
background-color: var(--background-color);
color: var(--foreground-color);
font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
cursor: none;
}
video,
img {
width: 100vw;
/* Viewport width */
height: 100vh;
/* Viewport height */
object-fit: contain;
}
p,
li {
font-size: var(--font-size);
line-height: 1.2;
text-wrap: balance;
max-width: 90vw;
word-break: break-word;
margin: 0 0 calc(var(--font-size) * 0.4) 0;
}
ul,
ol {
padding-left: calc(var(--font-size)*1.5);
}
</style>
</head>
<bod>
{ children... }
</bod>
</html>
}
templ videoTemplate(path string) {
@basicTemplate() {
<video autoplay>
<source src={ "file://" + path } type="video/mp4"/>
</video>
}
}
templ htmlTemplate(html string) {
@basicTemplate() {
@templ.Raw(html)
}
}
templ imageTemplate(path string) {
@basicTemplate() {
<img src={ "file://" + path }/>
}
}
templ deviceInfoTemplate(ip string, mac string, showQR bool) {
@basicTemplate() {
<div style="width: 100vw; height: 100vh; display: flex; flex-direction: row; justify-content: space-between;">
<div
style="display: flex; flex-direction: column; gap: 1rem; justify-content: end; padding: 2rem; font-size: 4rem;"
>
{ ip }
<span style="text-transform: uppercase;">{ mac }</span>
</div>
if showQR {
<div style="display: flex; justify-content: end; align-items: end; padding: 2rem;">
<div style="padding: 1rem; background-color: var(--foreground-color); border-radius: 1rem;">
<img
style="height: 30vh; width: auto; image-rendering: pixelated; image-rendering: crisp-edges;"
src={ "/qr?data=http://" + ip + ":8080" }
alt="QR-Code"
/>
</div>
</div>
}
</div>
<style>
:root {
--splash-bg: transparent !important;
--splash-fade-out-state: paused !important;
--background-color: oklch(21.6% 0.006 56.043);
}
</style>
}
}
templ pdfTemplate(path string, serverOrigin string) {
@basicTemplate() {
<base href={ serverOrigin + "/" }/>
<div
id="pdf"
data-src={ "api/file/" + strings.Split(path, "/.local/share/plg-mudics/display")[1] }
data-lib="static/pdfjs/pdf.min.mjs"
data-worker="static/pdfjs/pdf.worker.min.mjs"
></div>
<style>
html,body,#pdf {
margin:0;
width:100%;
height:100%;
overflow:hidden;
background:#000;
}
#pdf {
display:grid;
place-items:center;
}
#pdf canvas {
grid-area:1/1;
display:block;
}
</style>
<script type="module">
const host=document.querySelector("#pdf");
const resolve=x=>new URL(x,document.baseURI).href;
const P=await import(resolve(host.dataset.lib));
P.GlobalWorkerOptions.workerSrc=resolve(host.dataset.worker);
const pdf=await P.getDocument({
url:resolve(host.dataset.src),
useWasm:false
}).promise;
const cache=new Map();
let currentPage=1;
let wantedPage=1;
let requestNumber=0;
function renderPage(number) {
if(cache.has(number)) return cache.get(number);
const promise=(async()=>{
const page=await pdf.getPage(number);
const original=page.getViewport({scale:1});
const scale=Math.min(
innerWidth/original.width,
innerHeight/original.height
);
// Use 1 on old hardware. Raise to 1.5 for sharper output.
const pixelRatio=1;
const viewport=page.getViewport({
scale:scale*pixelRatio
});
const canvas=document.createElement("canvas");
canvas.width=Math.ceil(viewport.width);
canvas.height=Math.ceil(viewport.height);
canvas.style.width=`${viewport.width/pixelRatio}px`;
canvas.style.height=`${viewport.height/pixelRatio}px`;
const task=page.render({
canvasContext:canvas.getContext("2d"),
viewport
});
await task.promise;
return canvas;
})().catch(error=>{
cache.delete(number);
throw error;
});
cache.set(number,promise);
return promise;
}
async function show(number) {
wantedPage=Math.max(1,Math.min(pdf.numPages,number));
const request=++requestNumber;
const canvas=await renderPage(wantedPage);
// Ignore an obsolete result after multiple fast key presses.
if(request!==requestNumber) return;
// The new canvas is already completely rendered.
host.replaceChildren(canvas);
currentPage=wantedPage;
pruneCache();
preloadNeighbours();
}
function preloadNeighbours() {
const page=currentPage;
requestIdleCallback(async()=>{
if(page+1<=pdf.numPages) {
await renderPage(page+1);
}
if(page-1>=1) {
await renderPage(page-1);
}
},{timeout:250});
}
function pruneCache() {
const keep=new Set([
currentPage-1,
currentPage,
currentPage+1
]);
for(const [number,promise] of cache) {
if(keep.has(number)) continue;
cache.delete(number);
promise.then(canvas=>{
if(!canvas.isConnected) {
canvas.width=0;
canvas.height=0;
}
}).catch(()=>{});
}
}
addEventListener("keydown",event=>{
if(event.key==="ArrowRight") {
event.preventDefault();
show(wantedPage+1);
} else if(event.key==="ArrowLeft") {
event.preventDefault();
show(wantedPage-1);
}
});
addEventListener("resize",()=>{
cache.clear();
show(currentPage);
});
window.pdfViewer={
page:()=>currentPage,
count:()=>pdf.numPages,
show
};
await show(1);
</script>
}
}
templ startScreenTemplate(splashScreenHtml string, ip string, mac string, qrPath string) {
@basicTemplate() {
<div style="width: 100vw; height: 100vh; display: flex; flex-direction: row; justify-content: space-between;">
<div
style="display: flex; flex-direction: column; gap: 1rem; justify-content: end; padding: 2rem; font-size: 4rem;"
>
{ ip }
<span style="text-transform: uppercase;">{ mac }</span>
</div>
if qrPath != "" {
<div style="display: flex; justify-content: end; align-items: end; padding: 2rem;">
<div style="padding: 1rem; background-color: var(--foreground-color); border-radius: 1rem;">
<img
style="height: 30vh; width: auto; image-rendering: pixelated; image-rendering: crisp-edges;"
src={ "file://" + qrPath }
alt="QR-Code"
/>
</div>
</div>
}
</div>
@templ.Raw(splashScreenHtml)
<style>
:root {
--splash-bg: transparent !important;
--splash-fade-out-state: paused !important;
--background-color: oklch(21.6% 0.006 56.043);
}
</style>
}
}