Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 13 additions & 14 deletions apps/ppp/messages/en.json
Original file line number Diff line number Diff line change
@@ -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."
}
18 changes: 13 additions & 5 deletions apps/ppp/messages/ru.json
Original file line number Diff line number Diff line change
@@ -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": "Используйте ноль для отключения",
Expand Down
13 changes: 11 additions & 2 deletions apps/ppp/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -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);
219 changes: 145 additions & 74 deletions apps/ppp/src/lib/assets/enable-threads.js
Original file line number Diff line number Diff line change
@@ -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();
// }
(() => {
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);
}
);
})();
}
4 changes: 2 additions & 2 deletions apps/ppp/src/lib/components/editor/run-button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
const { onClick, status }: Props = $props();
</script>

<button class="btn btn-sm btn-primary" onclick={onClick}>
<button class="btn btn-sm btn-primary capitalize" onclick={onClick}>
{#if status === 'running'}
<span class="loading loading-spinner"></span>
{m.stop()}
{:else if status === 'stopping'}
<span class="loading loading-spinner"></span>
{m.forceStop()}
{m.force_stop()}
{:else}
<LucidePlay class="w-6" />
{m.run()}
Expand Down
6 changes: 3 additions & 3 deletions apps/ppp/src/lib/problem/content.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { getLocale } from '$lib/paraglide/runtime';
import { m } from '$lib/paraglide/messages';

import type { Problem } from './model';

const { problem, content: dirtyContent }: { problem: Problem; content: Record<string, string> } =
Expand All @@ -22,13 +22,13 @@
return dirtyContent[key];
}
}
return m.currently_untranslated_problem();
return m.untranslated_content();
});
</script>

<svelte:head>
<title>{problem.titles[lang]}</title>
</svelte:head>

<h1>{problem.titles[lang]}</h1>
<h1 class="capitalize">{problem.titles[lang]}</h1>
{@html marked.parse(content)}
12 changes: 7 additions & 5 deletions apps/ppp/src/lib/problem/editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,10 @@
<Logo />
<div class="breadcrumbs">
<ul>
<li>
<a href="..">{PROBLEM_CATEGORY_TO_LABEL[problemCategory]()}</a>
<li class="min-w-0">
<a class="truncate capitalize block" href="..">
{PROBLEM_CATEGORY_TO_LABEL[problemCategory]()}
</a>
</li>
</ul>
</div>
Expand Down Expand Up @@ -337,10 +339,10 @@
</TabContent>
<TabContent tab={EditorPanelTab.Settings}>
<div class="flex flex-col gap-4 overflow-auto p-4">
<CheckBox title={m.vimMode()} bind:value={vimState} />
<CheckBox title={m.vim_mode()} bind:value={vimState} />
<Number
title={m.executionTimeout()}
alt={m.executionTimeoutDescription()}
title={m.execution_timeout()}
alt={m.execution_timeout_description()}
bind:value={executionTimeout}
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions apps/ppp/src/lib/problem/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export enum ProblemCategory {
export const PROBLEM_CATEGORIES = Object.values(ProblemCategory);

export const PROBLEM_CATEGORY_TO_LABEL: Record<ProblemCategory, () => string> = {
[ProblemCategory.DesignPatterns]: m.designPatterns
[ProblemCategory.DesignPatterns]: m.design_patterns
};

export interface Runtime<I, O> {
Expand All @@ -23,5 +23,5 @@ export interface Runtime<I, O> {
}

export const EDITOR_MIN_WIDTH = 5;
export const DESCRIPTION_PANEL_MIN_WIDTH = 600;
export const DESCRIPTION_PANEL_MIN_WIDTH = 500;
export const DESCRIPTION_PANEL_FLIP_POINT = DESCRIPTION_PANEL_MIN_WIDTH / 2;
Loading