/* * Copyright (C) 2026 Mateusz Gruszczyński @linuxiarz.pl * Source-Available Code / Dual-Licensed. * * Free for non-commercial and evaluation use under terms of BSL/GPLv3. * Commercial or production use requires a valid paid license. * See LICENSE file in repository root for details. */ (() => { const order = ["assets", "language", "libraries", "access", "files"]; const resourceParts = new Set(); const locale = String(document.documentElement.lang || "en").toLowerCase().startsWith("pl") ? "pl" : "en"; const strings = { en: { assets: "Preparing the interface", language: "Setting up language", libraries: "Starting the editor", access: "Checking access", files: "Opening the note", complete: "Ready", error: "The note could not be opened", retry: "Reload page", errorDetails: "Check your connection and try again.", progress: "{done} of {total} stages completed", timeout: "Still waiting for the note. Check the connection or reload." }, pl: { assets: "Przygotowanie interfejsu", language: "Ustawianie j\u0119zyka", libraries: "Uruchamianie edytora", access: "Sprawdzanie dost\u0119pu", files: "Otwieranie notatki", complete: "Gotowe", error: "Nie uda\u0142o si\u0119 otworzy\u0107 notatki", retry: "Za\u0142aduj ponownie", errorDetails: "Sprawd\u017a po\u0142\u0105czenie i spr\u00f3buj ponownie.", progress: "Uko\u0144czono {done} z {total} etap\u00f3w", timeout: "Nadal oczekujemy na notatk\u0119. Sprawd\u017a po\u0142\u0105czenie lub od\u015bwie\u017c stron\u0119." } }[locale]; const completed = new Set(); const loadedStyleElements = new WeakSet(); let loadedStyleCount = 0; let readyRequested = false; let dismissed = false; let failure = ""; const element = id => document.getElementById(id); function update() { const overlay = element("app-boot-overlay"); if (!overlay || dismissed) return; const count = completed.size; const progress = Math.round((count / order.length) * 100); const next = order.find(stage => !completed.has(stage)); const heading = element("app-boot-heading"); if (heading) heading.textContent = locale === "pl" ? "Ładowanie…" : "Loading…"; const stageText = element("app-boot-stage"); const stageCount = element("app-boot-count"); const bar = element("app-boot-progress"); const fill = element("app-boot-progress-fill"); const retry = element("app-boot-retry"); if (stageText) stageText.textContent = failure ? strings.error : (next ? strings[next] : strings.complete); if (stageCount) stageCount.textContent = failure || strings.progress.replace("{done}", count).replace("{total}", order.length); if (bar) bar.setAttribute("aria-valuenow", String(progress)); if (fill) fill.style.width = `${progress}%`; if (retry) { retry.hidden = !failure; retry.textContent = strings.retry; } overlay.classList.toggle("has-error", Boolean(failure)); for (const stage of order) { const node = element(`app-boot-step-${stage}`); if (!node) continue; const isDone = completed.has(stage); node.dataset.status = isDone ? "done" : (stage === next && !failure ? "active" : "waiting"); node.querySelector("span")?.replaceChildren(document.createTextNode(strings[stage])); } if (readyRequested && count === order.length && !failure) dismiss(); } function dismiss() { if (dismissed) return; dismissed = true; const overlay = element("app-boot-overlay"); if (!overlay) return; overlay.classList.add("is-ready"); const remove = () => overlay.remove(); overlay.addEventListener("transitionend", remove, { once: true }); setTimeout(remove, 300); } function complete(stage) { if (dismissed || failure) return; if (stage === "css" || stage === "js") { resourceParts.add(stage); if (resourceParts.has("css") && resourceParts.has("js")) completed.add("assets"); } else if (order.includes(stage)) { completed.add(stage); } else { return; } update(); } function fail(reason) { if (dismissed || failure) return; const safeMessage = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : ""; failure = safeMessage && safeMessage.length < 180 ? `${strings.error}: ${safeMessage}` : strings.errorDetails; update(); } document.addEventListener("load", event => { if (!(event.target instanceof HTMLLinkElement) || !event.target.matches("link[data-rustpad-shell-css]")) return; if (loadedStyleElements.has(event.target)) return; loadedStyleElements.add(event.target); loadedStyleCount += 1; const expected = document.querySelectorAll("link[data-rustpad-shell-css]").length; if (expected > 0 && loadedStyleCount >= expected) complete("css"); }, true); document.addEventListener("error", event => { if (event.target instanceof HTMLLinkElement && event.target.matches("link[data-rustpad-shell-css]")) { fail(strings.errorDetails); } }, true); document.addEventListener("DOMContentLoaded", () => { element("app-boot-retry")?.addEventListener("click", () => location.reload()); update(); }, { once: true }); const timeout = setTimeout(() => { if (!dismissed) fail(strings.timeout); }, 45000); window.__RUSTPAD_LOADING__ = Object.freeze({ complete, skip: complete, ready() { readyRequested = true; update(); if (dismissed) clearTimeout(timeout); }, fail, stageDone(stage) { return stage === "css" || stage === "js" ? resourceParts.has(stage) : completed.has(stage); } }); })();