diff --git a/apps/ppp/messages/en.json b/apps/ppp/messages/en.json index 3b01f4fd..840e140e 100644 --- a/apps/ppp/messages/en.json +++ b/apps/ppp/messages/en.json @@ -1,17 +1,16 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from en!", - "problemsPageTitle": "Problems", - "editorPageTitle": "Editor", - "designPatterns": "Design Patterns", - "tests": "Tests", - "output": "Output", - "settings": "Settings", - "vimMode": "Vim mode", - "run": "Run", - "stop": "Stop", - "forceStop": "Force stop", - "executionTimeout": "Execution timeout (ms)", - "executionTimeoutDescription": "Use zero to disable", - "currently_untranslated_problem": "This problem has not yet been translated for your language" + "problems": "problems", + "editor": "editor", + "design_patterns": "design patterns", + "tests": "tests", + "output": "output", + "settings": "settings", + "vim_mode": "Vim mode", + "run": "run", + "stop": "stop", + "force_stop": "force stop", + "execution_timeout": "execution timeout (ms)", + "execution_timeout_description": "use zero to disable", + "untranslated_content": "this content has not yet been translated into your language." } diff --git a/apps/ppp/messages/ru.json b/apps/ppp/messages/ru.json index 102541af..f726b19a 100644 --- a/apps/ppp/messages/ru.json +++ b/apps/ppp/messages/ru.json @@ -1,15 +1,23 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "hello_world": "Hello, {name} from ru!", - "problemsPageTitle": "Проблемы", - "editorPageTitle": "Редактор", - "designPatterns": "Паттерны проектирования", + "problems": "проблемы", + "editor": "редактор", + "design_patterns": "паттерны проектирования", "tests": "Тесты", "output": "Вывод", "settings": "Настройки", - "vimMode": "Режим Vim", + "vim_mode": "режим Vim", "run": "Запустить", "stop": "Остановить", + "force_stop": "остановить принудительно", + "execution_timeout": "таймаут выполнения (мс)", + "execution_timeout_description": "используйте ноль для отключения", + "untranslated_content": "этот контент пока не переведен на ваш язык", + "hello_world": "Hello, {name} from ru!", + "problemsPageTitle": "Проблемы", + "editorPageTitle": "Редактор", + "designPatterns": "Паттерны проектирования", + "vimMode": "Режим Vim", "forceStop": "Остановить принудительно", "executionTimeout": "Таймаут выполнения (мс)", "executionTimeoutDescription": "Используйте ноль для отключения", diff --git a/apps/ppp/src/hooks.server.ts b/apps/ppp/src/hooks.server.ts index ad77efcf..82efb890 100644 --- a/apps/ppp/src/hooks.server.ts +++ b/apps/ppp/src/hooks.server.ts @@ -1,13 +1,22 @@ import type { Handle } from '@sveltejs/kit'; import { paraglideMiddleware } from '$lib/paraglide/server'; +import { sequence } from '@sveltejs/kit/hooks'; const handleParaglide: Handle = ({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => { event.request = request; - return resolve(event, { transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale) }); }); -export const handle: Handle = handleParaglide; +const headersHandle: Handle = ({ event, resolve }) => { + const { setHeaders } = event; + setHeaders({ + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin' + }); + return resolve(event); +}; + +export const handle: Handle = sequence(handleParaglide, headersHandle); diff --git a/apps/ppp/src/lib/assets/enable-threads.js b/apps/ppp/src/lib/assets/enable-threads.js index 881bd26c..f3cf211c 100644 --- a/apps/ppp/src/lib/assets/enable-threads.js +++ b/apps/ppp/src/lib/assets/enable-threads.js @@ -1,75 +1,146 @@ -// NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads. -// Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that. - -/* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */ -// From here: https://github.com/gzuidhof/coi-serviceworker -if(typeof window === 'undefined') { - self.addEventListener("install", () => self.skipWaiting()); - self.addEventListener("activate", e => e.waitUntil(self.clients.claim())); - - async function handleFetch(request) { - if(request.cache === "only-if-cached" && request.mode !== "same-origin") { - return; - } - - if(request.mode === "no-cors") { // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7 - request = new Request(request.url, { - cache: request.cache, - credentials: "omit", - headers: request.headers, - integrity: request.integrity, - destination: request.destination, - keepalive: request.keepalive, - method: request.method, - mode: request.mode, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - signal: request.signal, - }); - } - - let r = await fetch(request).catch(e => console.error(e)); - - if(r.status === 0) { - return r; - } - - const headers = new Headers(r.headers); - headers.set("Cross-Origin-Embedder-Policy", "credentialless"); // or: require-corp - headers.set("Cross-Origin-Opener-Policy", "same-origin"); - - return new Response(r.body, { status: r.status, statusText: r.statusText, headers }); - } - - self.addEventListener("fetch", function(e) { - e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise) - }); - +/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ +let coepCredentialless = false; +if (typeof window === 'undefined') { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); + + self.addEventListener("message", (ev) => { + if (!ev.data) { + return; + } else if (ev.data.type === "deregister") { + self.registration + .unregister() + .then(() => { + return self.clients.matchAll(); + }) + .then(clients => { + clients.forEach((client) => client.navigate(client.url)); + }); + } else if (ev.data.type === "coepCredentialless") { + coepCredentialless = ev.data.value; + } + }); + + self.addEventListener("fetch", function (event) { + const r = event.request; + if (r.cache === "only-if-cached" && r.mode !== "same-origin") { + return; + } + + const request = (coepCredentialless && r.mode === "no-cors") + ? new Request(r, { + credentials: "omit", + }) + : r; + event.respondWith( + fetch(request) + .then((response) => { + if (response.status === 0) { + return response; + } + + const newHeaders = new Headers(response.headers); + newHeaders.set("Cross-Origin-Embedder-Policy", + coepCredentialless ? "credentialless" : "require-corp" + ); + if (!coepCredentialless) { + newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); + } + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + }) + .catch((e) => console.error(e)) + ); + }); + } else { - (async function() { - if(window.crossOriginIsolated !== false) return; - - let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e)); - if(registration) { - console.log("COOP/COEP Service Worker registered", registration.scope); - - registration.addEventListener("updatefound", () => { - console.log("Reloading page to make use of updated COOP/COEP Service Worker."); - window.location.reload(); - }); - - // If the registration is active, but it's not controlling the page - if(registration.active && !navigator.serviceWorker.controller) { - console.log("Reloading page to make use of COOP/COEP Service Worker."); - window.location.reload(); - } - } - })(); -} - -// Code to deregister: -// let registrations = await navigator.serviceWorker.getRegistrations(); -// for(let registration of registrations) { -// await registration.unregister(); -// } \ No newline at end of file + (() => { + const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); + window.sessionStorage.removeItem("coiReloadedBySelf"); + const coepDegrading = (reloadedBySelf == "coepdegrade"); + + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => !reloadedBySelf, + shouldDeregister: () => false, + coepCredentialless: () => true, + coepDegrade: () => true, + doReload: () => window.location.reload(), + quiet: false, + ...window.coi + }; + + const n = navigator; + const controlling = n.serviceWorker && n.serviceWorker.controller; + + // Record the failure if the page is served by serviceWorker. + if (controlling && !window.crossOriginIsolated) { + window.sessionStorage.setItem("coiCoepHasFailed", "true"); + } + const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); + + if (controlling) { + // Reload only on the first failure. + const reloadToDegrade = coi.coepDegrade() && !( + coepDegrading || window.crossOriginIsolated + ); + n.serviceWorker.controller.postMessage({ + type: "coepCredentialless", + value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) + ? false + : coi.coepCredentialless(), + }); + if (reloadToDegrade) { + !coi.quiet && console.log("Reloading page to degrade COEP."); + window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); + coi.doReload("coepdegrade"); + } + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: "deregister" }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); + return; + } + + // In some environments (e.g. Firefox private mode) this won't be available + if (!n.serviceWorker) { + !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); + return; + } + + n.serviceWorker.register(window.document.currentScript.src).then( + (registration) => { + !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); + + registration.addEventListener("updatefound", () => { + !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); + coi.doReload(); + } + }, + (err) => { + !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); + } + ); + })(); +} \ No newline at end of file diff --git a/apps/ppp/src/lib/components/editor/run-button.svelte b/apps/ppp/src/lib/components/editor/run-button.svelte index 2b245dce..5a3907d0 100644 --- a/apps/ppp/src/lib/components/editor/run-button.svelte +++ b/apps/ppp/src/lib/components/editor/run-button.svelte @@ -12,13 +12,13 @@ const { onClick, status }: Props = $props(); -