Frontend uudelleenrakennettu: Astro-komponentit, Wasm pääsäikeessä, ei Workeria

Vanha frontend siirretty temp/. Uusi rakenne:
- StatusBar.astro, Terminal.astro, Editor.astro, Guide.astro
- global.css erillinen
- Wasm pääsäikeessä (ei Worker — yksinkertainen, debugattava)
- Tab-completion, dropdown, projektikortti, Monaco, GUIDE.md
- Ei tokenisointia eikä koodilaboratoriota

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaakko Vanhala
2026-04-09 20:17:39 +03:00
parent e3fdb91ac5
commit a8c4af0975
9617 changed files with 996171 additions and 5349 deletions

View File

@@ -0,0 +1,47 @@
import {
collapseDuplicateTrailingSlashes,
hasFileExtension,
isInternalPath
} from "@astrojs/internal-helpers/path";
import { trailingSlashMismatchTemplate } from "../template/4xx.js";
import { writeHtmlResponse, writeRedirectResponse } from "./response.js";
function evaluateTrailingSlash(pathname, search, trailingSlash) {
if (isInternalPath(pathname)) {
return { action: "next" };
}
const collapsed = collapseDuplicateTrailingSlashes(pathname, true);
if (pathname && collapsed !== pathname) {
return { action: "redirect", status: 301, location: `${collapsed}${search}` };
}
if (trailingSlash === "never" && pathname.endsWith("/") && pathname !== "/" || trailingSlash === "always" && !pathname.endsWith("/") && !hasFileExtension(pathname)) {
return { action: "reject", status: 404, pathname };
}
return { action: "next" };
}
function trailingSlashMiddleware(settings) {
const { trailingSlash } = settings.config;
return function devTrailingSlash(req, res, next) {
const url = new URL(`http://localhost${req.url}`);
let pathname;
try {
pathname = decodeURI(url.pathname);
} catch (e) {
return next(e);
}
const decision = evaluateTrailingSlash(pathname, url.search, trailingSlash);
switch (decision.action) {
case "redirect":
return writeRedirectResponse(res, decision.status, decision.location);
case "reject": {
const html = trailingSlashMismatchTemplate(decision.pathname, trailingSlash);
return writeHtmlResponse(res, decision.status, html);
}
case "next":
return next();
}
};
}
export {
evaluateTrailingSlash,
trailingSlashMiddleware
};