136 lines
6.3 KiB
JavaScript
136 lines
6.3 KiB
JavaScript
const params = new URLSearchParams(location.search);
|
|
const sessionId = params.get("session");
|
|
|
|
const pageTitle = document.getElementById("page-title");
|
|
const slideCounter = document.getElementById("slide-counter");
|
|
const previousButton = document.getElementById("previous");
|
|
const nextButton = document.getElementById("next");
|
|
const firstButton = document.getElementById("first");
|
|
const lastButton = document.getElementById("last");
|
|
const videoButton = document.getElementById("video");
|
|
const audienceButton = document.getElementById("audience");
|
|
const exitButton = document.getElementById("exit");
|
|
const currentPreview = document.getElementById("current-preview");
|
|
const nextPreview = document.getElementById("next-preview");
|
|
const currentTime = document.getElementById("current-time");
|
|
const elapsedTime = document.getElementById("elapsed-time");
|
|
const screenStatus = document.getElementById("screen-status");
|
|
const zoomOutButton = document.getElementById("zoom-out");
|
|
const zoomInButton = document.getElementById("zoom-in");
|
|
const zoomValue = document.getElementById("zoom-value");
|
|
const scrollUpButton = document.getElementById("scroll-up");
|
|
const scrollDownButton = document.getElementById("scroll-down");
|
|
const resetViewButton = document.getElementById("reset-view");
|
|
const viewStatus = document.getElementById("view-status");
|
|
|
|
let current = 0;
|
|
let count = 0;
|
|
let audienceHidden = false;
|
|
let audienceZoom = 100;
|
|
let audienceScrollY = 0;
|
|
const startedAt = Date.now();
|
|
|
|
function send(command, value) {
|
|
if (!sessionId) return;
|
|
chrome.runtime.sendMessage({
|
|
type: "QLEARN_PRESENTER_CONTROL_COMMAND",
|
|
sessionId, command, value
|
|
}).catch(() => {});
|
|
}
|
|
|
|
function setPreview(frame, srcdoc, emptyLabel) {
|
|
if (srcdoc) {
|
|
frame.srcdoc = srcdoc;
|
|
frame.style.opacity = "1";
|
|
} else {
|
|
frame.srcdoc = `<!doctype html><html><body style="margin:0;display:grid;place-items:center;height:100vh;background:#111722;color:#9ca8ba;font:600 28px system-ui">${emptyLabel}</body></html>`;
|
|
frame.style.opacity = ".72";
|
|
}
|
|
}
|
|
|
|
function render(state) {
|
|
if (!state) return;
|
|
current = Number(state.current) || 0;
|
|
count = Number(state.count) || 0;
|
|
pageTitle.textContent = state.pageTitle || "Hudra";
|
|
audienceHidden = Boolean(state.audienceHidden);
|
|
audienceZoom = Number(state.audienceZoom) || 100;
|
|
audienceScrollY = Number(state.audienceScrollY) || 0;
|
|
zoomValue.textContent = `${audienceZoom}%`;
|
|
const scrollLabel = Math.abs(audienceScrollY) < 4 ? "top" : `${Math.round(audienceScrollY)} px`;
|
|
viewStatus.textContent = `${audienceZoom === 100 ? "Fit" : `Scaled ${audienceZoom}%`} · ${scrollLabel}`;
|
|
audienceButton.textContent = audienceHidden ? "Restore audience" : "Hide audience";
|
|
screenStatus.textContent = audienceHidden ? "Audience screen hidden" : "Audience screen active";
|
|
slideCounter.textContent = count > 0 ? `Slide ${current + 1} of ${count}` : "Preparing slides…";
|
|
previousButton.disabled = current <= 0 || count <= 0;
|
|
firstButton.disabled = current <= 0 || count <= 0;
|
|
nextButton.disabled = count <= 0 || current >= count - 1;
|
|
lastButton.disabled = count <= 0 || current >= count - 1;
|
|
setPreview(currentPreview, state.currentPreview, "Current slide");
|
|
setPreview(nextPreview, state.nextPreview, current >= count - 1 ? "End of presentation" : "Next slide");
|
|
}
|
|
|
|
function updateTimes() {
|
|
const now = new Date();
|
|
currentTime.textContent = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
const elapsed = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
|
const minutes = String(Math.floor(elapsed / 60)).padStart(2, "0");
|
|
const seconds = String(elapsed % 60).padStart(2, "0");
|
|
elapsedTime.textContent = `${minutes}:${seconds}`;
|
|
}
|
|
setInterval(updateTimes, 1000);
|
|
updateTimes();
|
|
|
|
previousButton.addEventListener("click", () => send("previous"));
|
|
nextButton.addEventListener("click", () => send("next"));
|
|
firstButton.addEventListener("click", () => send("first"));
|
|
lastButton.addEventListener("click", () => send("last"));
|
|
videoButton.addEventListener("click", () => send("toggleVideo"));
|
|
audienceButton.addEventListener("click", () => send("toggleAudienceHidden"));
|
|
exitButton.addEventListener("click", () => send("exit"));
|
|
zoomOutButton.addEventListener("click", () => send("zoomOut"));
|
|
zoomInButton.addEventListener("click", () => send("zoomIn"));
|
|
resetViewButton.addEventListener("click", () => send("resetView"));
|
|
|
|
function addHoldScroll(button, command) {
|
|
let timer = null;
|
|
const stop = () => { if (timer) clearInterval(timer); timer = null; };
|
|
button.addEventListener("pointerdown", (event) => { event.preventDefault(); send(command); timer = setInterval(() => send(command), 140); });
|
|
["pointerup", "pointercancel", "pointerleave"].forEach((name) => button.addEventListener(name, stop));
|
|
}
|
|
addHoldScroll(scrollUpButton, "scrollUp");
|
|
addHoldScroll(scrollDownButton, "scrollDown");
|
|
|
|
currentPreview.addEventListener("wheel", (event) => {
|
|
event.preventDefault();
|
|
if (event.shiftKey) send(event.deltaY < 0 ? "zoomIn" : "zoomOut");
|
|
else send("scrollBy", event.deltaY);
|
|
}, { passive: false });
|
|
currentPreview.addEventListener("dblclick", () => send("resetView"));
|
|
|
|
chrome.runtime.onMessage.addListener((message) => {
|
|
if (message?.type === "QLEARN_PRESENTER_STATE_UPDATE" && message.sessionId === sessionId) render(message);
|
|
});
|
|
|
|
function requestSession(attempt = 0) {
|
|
chrome.runtime.sendMessage({ type: "QLEARN_PRESENTER_GET_SESSION", sessionId })
|
|
.then((state) => {
|
|
if (state) render(state);
|
|
else if (attempt < 20) setTimeout(() => requestSession(attempt + 1), 250);
|
|
})
|
|
.catch(() => {
|
|
if (attempt < 20) setTimeout(() => requestSession(attempt + 1), 250);
|
|
});
|
|
}
|
|
requestSession();
|
|
|
|
window.addEventListener("keydown", (event) => {
|
|
if (event.key.toLowerCase() === "b") { event.preventDefault(); if (!event.repeat) send("toggleAudienceHidden"); return; }
|
|
if (event.key === "Tab") { event.preventDefault(); if (!event.repeat) send("toggleVideo"); return; }
|
|
if (["ArrowRight", "ArrowDown", "PageDown", " ", "Enter"].includes(event.key)) { event.preventDefault(); send("next"); return; }
|
|
if (["ArrowLeft", "ArrowUp", "PageUp", "Backspace"].includes(event.key)) { event.preventDefault(); send("previous"); return; }
|
|
if (event.key === "Home") { event.preventDefault(); send("first"); return; }
|
|
if (event.key === "End") { event.preventDefault(); send("last"); return; }
|
|
if (event.key === "Escape") { event.preventDefault(); send("exit"); }
|
|
});
|