Skip to content

v0.4.0

Latest

Choose a tag to compare

@wushilin wushilin released this 12 Sep 12:39

Server-driven redraw of the visible screen

An app's rarely-rewritten bottom row (Claude Code's status line, say) could still go blank while the tab was in front, and only a resize repaired it. A client-side repaint cannot fix that: the browser's own terminal buffer may already be the thing that is wrong, so redrawing it just redraws the error. The repair now comes from the server.

  • The server keeps its own rendering of each slot's visible screen, fed every byte the browsers get, under the same lock the output is broadcast under, so it tracks the stream exactly. No scrollback is kept there — the ring is the history; this is only the grid.
  • On a fixed period ([terminals] redraw_interval_secs, default 30, 0 disables) every attached browser gets a redraw control frame carrying that screen: every visible row with its attributes and the cursor, as escape sequences the page writes straight into its terminal. It repaints from what the server knows the screen holds, not from its own memory of it.
  • Lightweight and scoped to the visible screen. The sequence clears and rewrites the display in place and never switches buffers or modes; erasing the display never pushes rows into history, and a full bottom row is left in pending-wrap rather than scrolled. The scrollback and the scroll position are untouched — verified with the view scrolled into history and the page's buffer deliberately corrupted: one period later the screen was byte-identical to the original, viewport and history unchanged.
  • Ordered with the stream. The frame is queued by the same task that forwards a slot's output, after everything it already reflects, and rides as a control frame rather than tagged output, so it never moves the client's stream offset. Browsers refuse a frame whose size disagrees with theirs (a resize in flight), one arriving mid-replay, or one from a replaced attachment.
  • Resize. The server model is rebuilt from the ring at the next tick after a resize — the same replay a reconnecting browser performs — rather than truncated in place.

Read-only share viewers get the same frame on their own socket.

Compatibility

The wire protocol gains one server→client control frame, {"type":"redraw","term":N,"cols":C,"rows":R,"screen":"<base64>"} (no term on a viewer socket). An older browser page that ignores unknown control frames keeps working. New config key: redraw_interval_secs under [terminals].

Static binaries

Statically linked musl binaries for x86_64 and aarch64, as before.

VER=0.4.0
ARCH=x86_64          # or aarch64
BASE=https://github.com/wushilin/webshell/releases/download/v$VER
curl -fsSLO $BASE/webshell-$VER-linux-$ARCH-musl
curl -fsSLO $BASE/SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS
chmod +x webshell-$VER-linux-$ARCH-musl