6468febb
fix(web): re-review nits — the zoom gate learns about ws, both engines drain
a73x 2026-08-13 14:31
Commit message
src/wasm_core.zig
| Old | New | ||
|---|---|---|---|
| @@ -191,7 +191,8 @@ export fn mux_apply_frame(msg_type: u32, len: u32) i32 { | |||
| 191 | // (DSR and friends), but the daemon is authoritative and answers the | 191 | // (DSR and friends), but the daemon is authoritative and answers the |
| 192 | // application already — forwarding these would double every reply, so | 192 | // application already — forwarding these would double every reply, so |
| 193 | // nothing in the browser may ever read them. Unread, they are an | 193 | // nothing in the browser may ever read them. Unread, they are an |
| 194 | // accumulation with no bound; this is the one place that closes it. | 194 | // accumulation with no bound; each engine drains where it feeds — |
| 195 | // here for the live replica, in mux_scroll_feed for the scratch. | ||
| 195 | // (engine.zig pins the replies themselves, where they matter.) | 196 | // (engine.zig pins the replies themselves, where they matter.) |
| 196 | c.rep.eng.clearPtyOutput(); | 197 | c.rep.eng.clearPtyOutput(); |
| 197 | if (applied == .resync) return 1; | 198 | if (applied == .resync) return 1; |
| @@ -490,6 +491,9 @@ export fn mux_scroll_feed(len: u32) i32 { | |||
| 490 | const se = c.scroll_eng.?; | 491 | const se = c.scroll_eng.?; |
| 491 | se.reset(); | 492 | se.reset(); |
| 492 | se.feed(input_buf[0..len]); | 493 | se.feed(input_buf[0..len]); |
| 494 | // Same drain rule as mux_apply_frame: reset() is a fullReset of the | ||
| 495 | // terminal, not of Engine.pty_out, and history rows can answer too. | ||
| 496 | se.clearPtyOutput(); | ||
| 493 | return 0; | 497 | return 0; |
| 494 | } | 498 | } |
| 495 | 499 | ||
web/mux.js
| Old | New | ||
|---|---|---|---|
| @@ -215,6 +215,7 @@ class Tile { | |||
| 215 | } | 215 | } |
| 216 | this.gotState = false; | 216 | this.gotState = false; |
| 217 | this.scrollPages = 0; | 217 | this.scrollPages = 0; |
| 218 | this.renderBadge(); // scrollPages changed; the mask must lift with it | ||
| 218 | this.drawScale = 0; // force the backing store to be re-sized | 219 | this.drawScale = 0; // force the backing store to be re-sized |
| 219 | this.replayFailed(`re-initialized the core after ${why}`, 'replay failed'); | 220 | this.replayFailed(`re-initialized the core after ${why}`, 'replay failed'); |
| 220 | } | 221 | } |
| @@ -563,7 +564,12 @@ class Tile { | |||
| 563 | // 'up': painting that green while sendAttach stays gated on replayDead | 564 | // 'up': painting that green while sendAttach stays gated on replayDead |
| 564 | // is exactly a silent tile that reads healthy. Terminal states do | 565 | // is exactly a silent tile that reads healthy. Terminal states do |
| 565 | // replace each other — the newest fact about a dead session is still | 566 | // replace each other — the newest fact about a dead session is still |
| 566 | // the true one. | 567 | // the true one. The arguable exception, considered and accepted: a |
| 568 | // browser-leg 'gone' (the page can no longer observe the session at | ||
| 569 | // all) is also withheld while terminal. The word is then stale but the | ||
| 570 | // color is not — index.html renders all four in the same red — and | ||
| 571 | // splitting "link news that supersedes" from "link news that doesn't" | ||
| 572 | // would reintroduce the peer-writers problem this field deleted. | ||
| 567 | setStatus(cls, text) { | 573 | setStatus(cls, text) { |
| 568 | if (TERMINAL.has(this.status) && !TERMINAL.has(cls)) return; | 574 | if (TERMINAL.has(this.status) && !TERMINAL.has(cls)) return; |
| 569 | this.status = cls; | 575 | this.status = cls; |
| @@ -596,7 +602,9 @@ function zoom(tile) { | |||
| 596 | // that lands in that window has no core to size, resize or key against. | 602 | // that lands in that window has no core to size, resize or key against. |
| 597 | // Gated HERE and not at each call because zoom is what makes the tile | 603 | // Gated HERE and not at each call because zoom is what makes the tile |
| 598 | // reachable at all — no zoom, no keys, no wheel, no paste (spec). | 604 | // reachable at all — no zoom, no keys, no wheel, no paste (spec). |
| 599 | if (!tile.core) return; | 605 | // `core` alone is not enough: a tile whose mux_init failed has a truthy |
| 606 | // core and no socket — connect() was never reached. | ||
| 607 | if (!tile.core || !tile.ws) return; | ||
| 600 | if (zoomedTile) unzoom(); | 608 | if (zoomedTile) unzoom(); |
| 601 | zoomedTile = tile; | 609 | zoomedTile = tile; |
| 602 | tile.zoomed = true; | 610 | tile.zoomed = true; |