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, """); }