Skip to content

Add native desktop terminal client (crates/desktop)#83

Open
pcarrier wants to merge 7 commits intomainfrom
indent-2026-04-01-desktop-client
Open

Add native desktop terminal client (crates/desktop)#83
pcarrier wants to merge 7 commits intomainfrom
indent-2026-04-01-desktop-client

Conversation

@pcarrier
Copy link
Copy Markdown
Contributor

@pcarrier pcarrier commented Apr 1, 2026

Summary

  • New crates/desktop crate (blit-desktop) — a GPU-accelerated native terminal client using winit 0.30 + wgpu 25 + cosmic-text
  • Multi-connection architecture: simultaneously connect to Unix sockets, TCP, SSH, and WebRTC shares from a single window
  • Persistent remote configuration at ~/.config/blit/remotes.conf (INI-style format)
  • BSP tiled layouts with DSL grammar (line(a 2, col(b, c))) and 7 presets
  • Dynamic status bar showing terminal count, focused session name, dimensions, and connection status
  • Reads ~/.config/blit/blit.conf for font family/size, palette, and custom layouts (same format as web app)
  • Mouse support: click, drag, scroll, motion — forwarded to terminal when mouse mode is active, scrollback navigation otherwise
  • Cmd/Ctrl+K switcher overlay with search filtering, keyboard navigation, session switching, and new terminal creation

Motivation

Provide a lightweight, GPU-rendered native desktop client for blit that covers the full feature set of the web version. The desktop client avoids browser overhead and enables direct socket/SSH connections without a relay server.

Testing

Manual compilation and launch tested. No automated tests yet — the desktop client is a new standalone binary.

Open in Indent
Tag @indent to continue the conversation here.

@indent
Copy link
Copy Markdown
Contributor

indent bot commented Apr 1, 2026

PR Summary

This PR adds a GPU-accelerated native desktop terminal client (blit-desktop) using winit for windowing, wgpu for rendering, and cosmic-text for text shaping. It connects to blit servers via Unix, TCP, SSH, or WebRTC transports with auto-reconnect, supports BSP tiled layouts, 31 color palettes, mouse tracking, scrollback, and a keyboard-driven session switcher overlay.

  • New crates/desktop crate with full GPU rendering pipeline (rect + glyph shaders, dynamic glyph atlas)
  • Connection management with async tokio tasks, EventLoopProxy-based wakeup, and reconnection via ReconnectNeeded events
  • Transport abstraction supporting Unix sockets, TCP, SSH (via ControlMaster), and WebRTC share connections
  • Terminal state rendering converting blit_remote::TerminalState cells to GPU vertices with cursor rendering
  • BSP layout system with DSL parser and 7 tiled layout presets
  • Overlay system with functional Cmd+K session switcher (search, arrow navigation, actions) and help screen
  • Mouse event handling (click, drag, scroll, motion) forwarding to server based on terminal mouse mode
  • User configuration via ~/.config/blit/remotes.conf and ~/.config/blit/blit.conf (font, palette, layout settings)
  • Status bar with session info, title, dimensions, and connection indicator

Issues

All clear! No issues remaining. 🎉

9 issues already resolved
  • If the server sends a cell with content_len > 4 (the 3-bit field allows 0-7), GlyphKey.bytes is only [u8; 4] and copy_from_slice will panic. Valid UTF-8 caps at 4 bytes so this isn't triggered today, but there's no guard against malformed data. (fixed by commit 5a05d15)
  • Terminal freezes after initial render: process_server_events() only runs inside RedrawRequested, but with ControlFlow::Wait and no wakeup mechanism for the mpsc channel, no redraws are requested after the first frame. Server output queues up but is never displayed until the user interacts with the window. (fixed by commit e217f87)
  • Every FrameUpdate sends Command::Resize { pty_id, rows: 0, cols: 0 } to the server, which is a resize to zero dimensions. This fires on every frame, flooding the server with invalid resize commands. (fixed by commit 5a05d15)
  • No tokio runtime is initialized — tokio::spawn() in ConnectionManager::connect() will panic with "must be called from the context of a Tokio runtime" as soon as connect_remotes() fires during resumed(). The default local remote has autoconnect = true, so this crashes on every startup. (fixed by commit 5a05d15)
  • assign_sessions in bsp.rs uses format!("{:p}", *c as *const K) for deduplication, which compares pointer addresses instead of values. The same session appearing in both lru and live will have different addresses and can be assigned to multiple panes. (fixed by commit 5a05d15)
  • Status bar panics on multi-byte UTF-8 titles: &name[..max_chars - 1] byte-slices a &str using a character count as byte index. If the slice point falls within a multi-byte character (CJK, emoji), the client panics. (fixed by commit e217f87)
  • RedrawRequested unconditionally calls w.request_redraw(), creating an infinite redraw loop despite ControlFlow::Wait. This burns 100% CPU/GPU continuously. Should only request redraw when needs_redraw is true or the blink timer fires. (fixed by commit 5a05d15)
  • Reconnection is broken: after disconnect, connection_task creates a new mpsc channel but immediately drops the sender (new_tx), so cmd_rx.recv() in the writer task returns None instantly. The manager's ConnectionHandle.cmd_tx still references the old dead sender, so no commands can reach reconnected sessions. (fixed by commit 5a05d15)
  • Overlays remain non-functional after the fix commit: overlay text glyphs are still never rendered (empty &[] passed to renderer), and keyboard input is still silently consumed without being dispatched to overlay handlers. Overlays can only be opened (showing an empty panel) and closed. (fixed by commit 5ac12e1)

CI Checks

All CI checks passed on commit 5ac12e1.

@pcarrier pcarrier added the indent label Apr 1, 2026 — with indent
pcarrier and others added 4 commits April 1, 2026 15:21
GPU-accelerated terminal client using winit + wgpu with cosmic-text for text rendering.
Supports multiple simultaneous connections (Unix, TCP, SSH, WebRTC shares) with persistent
config at ~/.config/blit/remotes.conf. Includes BSP tiled layouts, 31 color palettes,
keyboard-driven overlays (switcher, help, palette, font), and the full blit wire protocol.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
- Add tokio runtime init in main.rs for async spawning
- Fix reconnection: non-looping connection task with ReconnectNeeded event
- Remove erroneous resize on every FrameUpdate
- Fix infinite redraw loop: only render when needs_redraw
- Fix BSP dedup to use value-based HashSet instead of pointer formatting
- Fix content_len guard to <= 4
- Add dynamic status bar (height from atlas.cell_height)
- Clean up unused imports and suppress dead_code on future-use items

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
Crate-level allows for dead_code, too_many_arguments, unnecessary_cast,
and manual_range_patterns since the desktop client has many features
scaffolded but not yet fully wired up.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
Use EventLoopProxy to wake the event loop when server events arrive,
fixing the freeze where ControlFlow::Wait never re-rendered after the
first frame. Fix status bar title truncation to use char boundaries
instead of byte slicing.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
@indent indent bot force-pushed the indent-2026-04-01-desktop-client branch from e217f87 to fb2374b Compare April 1, 2026 15:23
pcarrier and others added 3 commits April 1, 2026 15:28
Glyph placement offsets (physical.x + image.placement.left) can be
negative, causing overflow when cast to u32. Compute in i32 first
and skip glyphs with negative coordinates.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
Parse ~/.config/blit/blit.conf (key=value format matching the web app)
to read blit.fontFamily, blit.fontSize, blit.palette, and blit.layouts.
Apply font family/size to the glyph atlas and palette to the renderer
on startup instead of hardcoding "monospace"/14/default.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
Mouse: handle click, drag, scroll, and motion events. Forward to
server when terminal has mouse mode enabled; fall back to scrollback
navigation for scroll when mouse mode is off.

Overlay: render session list text with search filtering, highlight
selected item, handle arrow keys/enter/backspace/typing for navigation.
Selecting a session focuses it; Enter on "New terminal" creates one.

Generated with [Indent](https://indent.com)
Co-Authored-By: Indent <noreply@indent.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant