Spaces:
Running on Zero
Running on Zero
File size: 4,607 Bytes
9fca766 34b513d 9fca766 6f42620 9fca766 6f42620 9fca766 34b513d 9fca766 34b513d 6f42620 9fca766 a0de8fb 9fca766 6f42620 9fca766 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SCRYPT — session</title>
<link rel="stylesheet" href="/static/app.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.min.css" />
</head>
<body>
<main class="room">
<section class="term-frame">
<div class="term-bar">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
<span class="title">warden@scryptos — tty1</span>
<a href="/">◄ leave</a>
</div>
<div id="terminal"></div>
<div class="term-status" id="status">connecting to the machine…</div>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.18.0/lib/addon-webgl.min.js"></script>
<script>
// Osaka Jade, mapped onto an xterm 16-colour palette so the game's Rich
// styles land on exactly the colours the terminal build uses.
const JADE = {
background: "#111c18", foreground: "#c1c497", cursor: "#d7c995",
cursorAccent: "#111c18", selectionBackground: "#2c4a3a",
black: "#23372b", red: "#ff5345", green: "#549e6a", yellow: "#459451",
blue: "#509475", magenta: "#d2689c", cyan: "#2dd5b7", white: "#f6f5dd",
brightBlack: "#53685b", brightRed: "#db9f9c", brightGreen: "#9eebb3",
brightYellow: "#e5c736", brightBlue: "#acd4cf", brightMagenta: "#75bbb3",
brightCyan: "#8cd3cb", brightWhite: "#f6f5dd",
};
// The board needs this much terminal real estate; below it, cards clip.
const MIN_ROWS = 44;
const MIN_COLS = 110;
const MAX_FONT = 15;
const MIN_FONT = 10;
const term = new Terminal({
theme: JADE,
fontFamily: "'IBM Plex Mono', ui-monospace, monospace",
fontSize: MAX_FONT,
lineHeight: 1.05,
cursorBlink: true,
cursorStyle: "block",
allowProposedApi: true,
scrollback: 2000,
// Card frames, scales, and shading are box-drawing/block glyphs; let
// xterm draw those itself (cell-perfect) instead of trusting the webfont,
// whose substituted glyphs warp the card art.
customGlyphs: true,
});
const fit = new FitAddon.FitAddon();
term.loadAddon(fit);
term.open(document.getElementById("terminal"));
// customGlyphs needs the WebGL renderer; without it xterm falls back to the
// DOM renderer, which hands the art back to the font. Fall back gracefully
// on machines without WebGL.
try {
const webgl = new WebglAddon.WebglAddon();
webgl.onContextLoss(() => webgl.dispose());
term.loadAddon(webgl);
} catch (e) { /* DOM renderer: playable, just softer card frames */ }
// Fit the whole board, not just the window: start at the comfy font size and
// step down until the grid clears MIN_ROWS x MIN_COLS (or we hit the floor).
function fitGame() {
let size = MAX_FONT;
term.options.fontSize = size;
fit.fit();
while ((term.rows < MIN_ROWS || term.cols < MIN_COLS) && size > MIN_FONT) {
size -= 1;
term.options.fontSize = size;
fit.fit();
}
}
fitGame();
const status = document.getElementById("status");
function setStatus(html, dead = false) {
status.innerHTML = html;
status.classList.toggle("dead", dead);
}
const proto = location.protocol === "https:" ? "wss" : "ws";
const ws = new WebSocket(`${proto}://${location.host}/pty`);
ws.binaryType = "arraybuffer";
function sendResize() {
if (ws.readyState === WebSocket.OPEN) {
// Reserve one row: the board docks its instruction prompt to the bottom
// row, and FitAddon tends to report one more row than is actually
// painted — without this margin that prompt renders just off-screen.
const rows = Math.max(1, term.rows - 1);
ws.send(JSON.stringify({ resize: [term.cols, rows] }));
}
}
ws.onopen = () => {
setStatus("the Warden is <b>watching</b> · type to play");
sendResize();
term.focus();
};
ws.onmessage = (ev) => {
const bytes = typeof ev.data === "string" ? ev.data : new Uint8Array(ev.data);
term.write(bytes);
};
ws.onclose = () => setStatus("the machine closed the session. <b>refresh to wake it again.</b>", true);
ws.onerror = () => setStatus("connection severed.", true);
term.onData((data) => {
if (ws.readyState === WebSocket.OPEN) ws.send(new TextEncoder().encode(data));
});
window.addEventListener("resize", () => { fitGame(); sendResize(); });
const ro = new ResizeObserver(() => { fitGame(); sendResize(); });
ro.observe(document.getElementById("terminal"));
</script>
</body>
</html>
|