import { Marked } from "marked";
import { AssetManager } from "$/client/assets.ts";
import { mod_credits } from "$/client/mods.ts";
import { canvas, draw_rect } from "$/client/renderer/mod.ts";
import { GuiScreen } from "./gui_screen.ts";
// credits come from servers' mods, so markdown only: raw html is shown as text and links can't run code
const markdown = new Marked({
renderer: {
html({ text }) {
return escape_html(text);
},
link({ href, title, tokens }) {
const text = this.parser.parseInline(tokens);
if (!/^(https?:|mailto:)/i.test(href)) {
return text;
}
const title_attribute = title ? ` title="${escape_html(title)}"` : "";
return `${text}`;
},
},
});
// who made what: the engine's assets/ASSETS.md, then each loaded mod's credits file. shown as a page over the
// game since licenses are long, the game underneath keeps drawing
export class CreditsScreen extends GuiScreen {
#overlay: HTMLElement;
constructor(on_back: () => void) {
super();
this.#overlay = document.createElement("div");
this.#overlay.style.cssText = "position:fixed;inset:0;display:flex;justify-content:center;padding:32px 16px;" +
"box-sizing:border-box;color:#e5e7eb;font:15px/1.6 system-ui,sans-serif;";
const panel = document.createElement("div");
panel.style.cssText = "width:100%;max-width:760px;display:flex;flex-direction:column;gap:12px;" +
"background:rgba(17,24,39,0.95);border-radius:8px;padding:20px 24px;box-sizing:border-box;";
const header = document.createElement("div");
header.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;";
const title = document.createElement("div");
title.textContent = "Credits";
title.style.cssText = "font-size:24px;font-weight:600;";
const back = document.createElement("button");
back.textContent = "Back";
back.style.cssText = "font:inherit;padding:6px 20px;cursor:pointer;";
back.addEventListener("click", on_back);
header.append(title, back);
const content = document.createElement("div");
content.style.cssText = "overflow-y:auto;flex:1;min-height:0;padding-right:8px;";
content.innerHTML = this.#sections().map(({ heading, text }) =>
`${escape_html(heading)}
` +
`${markdown.parse(text, { async: false })}`
).join("");
for (const pre of content.querySelectorAll("pre")) {
pre.style.cssText =
"white-space:pre-wrap;background:#0b1220;padding:12px;border-radius:6px;font-size:12px;";
}
for (const link of content.querySelectorAll("a")) {
link.style.color = "#60a5fa";
}
panel.append(header, content);
this.#overlay.append(panel);
document.body.append(this.#overlay);
}
#sections() {
const sections = [{ heading: "bworld", text: AssetManager.instance.get("bworld:assets_text") ?? "" }];
for (const mod of mod_credits) {
sections.push({ heading: `${mod.name} ${mod.version}`, text: mod.text });
}
return sections;
}
on_tick(_delta: number): void {}
on_render(): void {
draw_rect(0, 0, canvas.width, canvas.height, [0, 0, 0, 0.4]);
}
on_close(): void {
this.#overlay.remove();
}
}
function escape_html(text: string) {
return text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
}