a73x

web/index.html

Ref:   Size: 5.7 KiB   History

<!doctype html>
<!-- `mux web`: the wall of devices (M-web Task 8). Served from memory by
     the hub; everything dynamic lives in mux.js; the replica itself is
     mux_core.wasm — the same ghostty-vt the daemon runs. -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>mux</title>
<style>
  :root { --bg: #0d0f12; --chrome: #1a1d23; --fg: #c8ccd4; --dim: #6b7280; }
  * { box-sizing: border-box; }
  body {
    margin: 0; background: var(--bg); color: var(--fg);
    font: 13px/1.4 ui-monospace, monospace;
  }
  #wall {
    display: grid; gap: 10px; padding: 10px;
    grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
  }
  .tile {
    background: var(--chrome); border: 1px solid #262a33; border-radius: 4px;
    overflow: hidden; cursor: pointer; display: flex; flex-direction: column;
  }
  .tile header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 4px 8px; font-size: 12px; color: var(--dim);
    border-bottom: 1px solid #262a33; user-select: none;
  }
  .tile header .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .copy-request {
    display: none; margin: 0 8px; padding: 0 6px; border: 1px solid #4a5261;
    border-radius: 3px; background: transparent; color: var(--fg);
    font: inherit; font-size: 11px; cursor: pointer;
  }
  .copy-request.on { display: inline-block; }
  .copy-request.error { border-color: #8b3d3d; background: #3d1a1a; color: #e07a7a; }
  .badge { padding: 0 6px; border-radius: 3px; font-size: 11px; }
  .badge.connecting, .badge.reconnecting { background: #4a3b12; color: #e8c35a; }
  .badge.up { background: #16351f; color: #6fce8a; }
  /* .stuck: the tile gave up replaying and is sending nothing further. */
  .badge.gone, .badge.exited, .badge.refused, .badge.stuck { background: #3d1a1a; color: #e07a7a; }
  .badge.scroll { background: #1a2c3d; color: #6ab0e0; }
  /* mux.js sets width/height in CSS pixels and sizes the backing store to
     that times devicePixelRatio, so glyphs rasterize at device resolution
     instead of being a downscaled bitmap. No width:100% here: it would
     stretch the backing store back out and undo exactly that. */
  /* The canvas background is TERMINAL ground, not chrome: it is what
     shows before the first frame lands and along the sub-pixel edge the
     backing-store rounding leaves. mux.js publishes the theme's
     background here, so a light theme does not flash black. */
  .tile canvas { display: block; background: var(--term-bg, #000); }
  /* The zoomed tile: same element, promoted. Keys go here.
     INSET, not inset:0 — the ring of shade left visible around it is the
     only way back out (clicking it unzooms), and a tile covering the
     viewport made unzoom unreachable. Escape is deliberately NOT bound:
     it belongs to the application, and vim needs it. */
  .tile.zoomed {
    position: fixed; inset: 3vh 4vw; z-index: 20; cursor: default;
  }
  .tile.zoomed canvas { margin: 0 auto; cursor: text; touch-action: none; }
  /* Below the zoomed tile, above the wall: the click target for unzoom. */
  #shade {
    display: none; position: fixed; inset: 0; z-index: 10; background: #000a;
  }
  #shade.on { display: block; }
  /* The settings tile: the wall's one standing control, and the only
     element on it the hub did not put there. A tile-shaped panel, not a
     toolbar — the wall stays a wall. Nothing here authors the wall: the
     wall is the hosts file. It costs nothing to make the panel
     unreachable while zoomed, too — the shade covers the wall — so an
     open panel can never be holding the keyboard the terminal owns. */
  .tile.settings-tile {
    min-height: 80px; align-items: center; justify-content: center;
    border-style: dashed;
  }
  .tile.settings-tile .settings { width: 90%; font-size: 11px; }
  .tile.settings-tile .settings summary { color: var(--dim); cursor: pointer; list-style: none; }
  .tile.settings-tile .settings summary::-webkit-details-marker { display: none; }
  .tile.settings-tile .settings summary:hover { color: var(--fg); }
  .tile.settings-tile .settings .row {
    display: flex; align-items: center; gap: 6px; margin-top: 6px;
  }
  .tile.settings-tile .settings .row > span { color: var(--dim); }
  .tile.settings-tile .settings input[type="text"],
  .tile.settings-tile .settings input[type="number"] {
    flex: 1; min-width: 0; padding: 2px 4px; border: 1px solid #4a5261;
    border-radius: 3px; background: transparent; color: var(--fg);
    font: inherit; font-size: 11px; text-align: left;
  }
  .tile.settings-tile .settings input[type="number"] { flex: 0 0 4.5em; }
  .tile.settings-tile .settings button {
    padding: 2px 6px; border: 1px solid #4a5261; border-radius: 3px;
    background: transparent; color: var(--fg); font: inherit;
    font-size: 11px; cursor: pointer;
  }
  .tile.settings-tile .settings .note, .tile.settings-tile .settings .caveat { color: var(--dim); font-size: 11px; }
  .tile.settings-tile .settings .note.bad { color: #e07a7a; }
  .tile header .spawn {
    padding: 0 6px; border: 0; background: transparent; color: var(--dim);
    font: inherit; cursor: pointer;
  }
  /* `+` = another session on this tile's host: an add, so it greens. */
  .tile header .spawn:hover { color: #6fce8a; }
  /* Zoomed = the terminal owns the header; wall management waits. */
  .tile.zoomed header .spawn { display: none; }
  /* IME target: focusable, invisible, never display:none (that kills IME). */
  #ime {
    position: fixed; left: -9999px; top: 0; width: 1px; height: 1px;
    opacity: 0; border: 0; padding: 0;
  }
</style>
</head>
<body>
<div id="shade"></div>
<div id="wall"></div>
<input id="ime" autocomplete="off" autocapitalize="off" spellcheck="false">
<script src="/mux.js"></script>
</body>
</html>