mirror of
https://github.com/hanzoai/cli.git
synced 2026-10-30 15:00:27 +00:00
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]>
58 lines
2.2 KiB
HTML
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>
|