Files
cli/assets/term/index.html
zeekayandHanzo Dev c585b5b614 term: fork the client — our terminal, our page (1.9.37)
ttyd's page is a fine terminal and the wrong surface for this product. It cannot
be themed to match the console that frames it, it has no touch affordances, and —
being somebody else's document inside a cross-origin frame — it swallows every
keystroke the workspace would like to hear. Three problems, one cause: we did not
own the page. Now we do. ttyd itself is untouched; only `--index` changes.

WHAT THE PAGE IS. xterm.js and the fit addon, vendored and INLINED, plus ~180
lines of client. Inlined because ttyd serves exactly one document and no assets,
so `src="./xterm.js"` would ask it for a file it does not have and render
nothing — and because a terminal must not need a CDN to be up. Tests assert both:
no relative asset, no remote fetch, and every placeholder substituted.

THEMED. True black, because the terminal is the deepest surface in the product;
monochrome foreground with the app's status hues; a bar cursor, 10k lines of
scrollback, and a scrollbar that matches the console rather than the OS.

THE KEY ROW. A soft keyboard has no Esc, no Ctrl, no Tab and no arrows, so on a
phone a terminal without one is a terminal you can read and not use. Fourteen
keys, docked above the software keyboard via visualViewport, with a STICKY ctrl —
tap it, then tap a letter — which is the only way to reach ^C on a touch device
and how Blink and Termius both do it. Rendered only where `pointer: coarse`.

THE CHORD BRIDGE. The workspace's shortcuts cannot exist unless this page
forwards them, so it forwards `⌃⌥` and NOTHING else: Ctrl belongs to the shell,
Alt is readline's Meta, ⌘ is the browser's, and a workspace that takes one of
those breaks the terminal it contains. AltGr reports as ctrl+alt on European
layouts and is excluded explicitly, or AltGr+E (€) would read as a chord.

PROVEN IN A REAL BROWSER, not asserted. Chromium at 1200x800 and as an iPhone 14:
43 rows and 33 rows, black ground, zero console errors, the key row present on
touch and absent on desktop — and a round trip, typing a command and reading its
output back out of the terminal. Both pages ran DIFFERENT shells from one server
(`?arg=desktop`, `?arg=phone`), which is the one-link-many-shells claim
demonstrated rather than described.

Best-effort by design: a page we cannot write is a reason to serve ttyd's own,
never a reason to have no terminal.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-03 22:42:47 -07:00

58 lines
2.2 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no">
<meta name="color-scheme" content="dark">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>Hanzo</title>
<style>
/*__XTERM_CSS__*/
:root { color-scheme: dark }
html, body {
margin: 0; height: 100%; background: #000; overflow: hidden;
font-family: ui-sans-serif, -apple-system, "SF Pro Text", system-ui, sans-serif;
}
/* The terminal owns the page, minus whatever the key row takes. */
#term { position: absolute; inset: 0; padding: 6px 8px; }
#term.with-keys { bottom: 44px; }
.xterm-viewport::-webkit-scrollbar { width: 8px }
.xterm-viewport::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 4px }
.xterm-viewport { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.16) transparent }
/* The key row. Docked, scrollable, and 44px so a thumb can hit it. */
#keys {
position: fixed; left: 0; right: 0; bottom: 0; height: 44px;
display: flex; gap: 4px; align-items: center;
padding: 0 6px calc(env(safe-area-inset-bottom) * 0.5);
background: #111; border-top: 1px solid rgba(255,255,255,.08);
overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
#keys::-webkit-scrollbar { display: none }
#keys button {
flex: 0 0 auto; min-width: 40px; height: 32px; padding: 0 10px;
border: 1px solid rgba(255,255,255,.10); border-radius: 8px;
background: #1a1a1a; color: #e6e6e6;
font: 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
-webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
#keys button:active { background: #2a2a2a }
#keys button[data-armed] { background: #e6e6e6; color: #000 }
</style>
</head>
<body>
<div id="term"></div>
<div id="keys" hidden></div>
<script>/*__XTERM_JS__*/</script>
<script>/*__FIT_JS__*/</script>
<script>
// The key row steals 44px from the terminal, so the container has to know.
if (window.matchMedia('(pointer: coarse)').matches) {
document.getElementById('term').classList.add('with-keys');
}
</script>
<script>/*__CLIENT_JS__*/</script>
</body>
</html>