type: architecture title: Terminal UI rendering description: How src/ui.rs draws the connecting, QR pairing and main screens with ratatui, including message formatting, wrapping, the input box and the footer. tags: [ui, ratatui, tui, qr-code, rendering] sources:
6bddea306b
resource: repo://src/ui.rs
generated: { by: "claude-code", at: "2026-10-19T18:58:46.219Z" }
verified:src/ui.rs is a pure view: render(frame, &App) reads the
application state and fills a ratatui frame. It never mutates
App; the event loop calls it once per event.
All user-facing strings are Italian constants at the top of the file
(src/ui.rs:21-91).
render (src/ui.rs:97-104) dispatches on app.screen:
| Screen | Function | Content |
|---|---|---|
Connecting |
render_connecting |
"Connessione a WhatsApp in corso..." plus the status line, centered |
Pairing |
render_pairing |
Title, QR code, pairing steps ("WhatsApp > Impostazioni > Dispositivi collegati > Collega un dispositivo"), status |
Main |
render_main |
List pane, messages pane, input pane, footer |
render_centered (src/ui.rs:142-155) centers a block of lines vertically
and horizontally.
The QR payload comes from the WhatsApp client
(WaEvent::Qr). qr_to_text (src/ui.rs:182-191) encodes it with the
qrcode crate using Dense1x2 half-block characters and a quiet zone;
colors are inverted (dark modules drawn as empty cells) so the code scans on a
dark terminal background.
qr_block_lines (src/ui.rs:161-175) replaces the QR with a single line when:
PAIRING_TEXT_ROWS = 5, or
columns): "Terminale troppo piccolo per il QR: ingrandisci la finestra".render_main (src/ui.rs:197-212):
+-----------------+--------------------------------------+
| Chat [F2: ...] | <name> ── AI: ON |
| > Alice ●AI | 10:02 Alice: ciao |
| Bob | 10:03 Tu: ciao! |
| | 10:03 [AI] Tu: ... |
| (30%) +--------------------------------------+
| | Messaggio |
| | > typed text_ |
+-----------------+--------------------------------------+
<status> Tab:focus Enter:apri/invia F2:chat/contatti ...
src/ui.rs:232-274): chats or contacts depending
on list_mode, title "Chat [F2: Contatti]" / "Contatti [F2: Chat]". The
selection is reversed with a "> " marker and clamped by selected_index.
Chats with autoreply enabled show a green ●AI marker.src/ui.rs:280-315): title is
<name> ── AI: ON|OFF or " Nessuna chat aperta ". All messages are
wrapped to the inner width and only the last rows that fit are kept, so the
newest messages are always visible (no scrolling).src/ui.rs:461-491): shows "> " + input; when
the text is wider than the box only the tail is shown (tail_fitting), and
the terminal cursor is placed after it while the input has focus.src/ui.rs:497-506): bold status text followed by the
dimmed key help
Tab:focus Enter:apri/invia F2:chat/contatti Ctrl+A:AI chat Ctrl+C:esci.The focused pane (list or input) has a yellow border (pane_block,
src/ui.rs:218-226).
format_message_line (src/ui.rs:346-362) renders a
stored message as HH:MM <sender>: <text> in
the local time zone; out-of-range timestamps show --:--
(src/ui.rs:368-376).
sender_label (src/ui.rs:383-396) picks:
Tu for own messages, [AI] Tu when the message was generated by the
AI autoreply;@).Colors (message_style, src/ui.rs:334-340): AI messages cyan, own messages
green, incoming messages default.
Widths are measured in terminal cells through ratatui's Line::width, so
wide characters (CJK, emoji) count as two cells (src/ui.rs:410-422).
wrap_text (src/ui.rs:428-455) splits on \n, wraps each paragraph by
characters (not words), drops control characters and always emits at least
one row per paragraph.
Unit tests in src/ui.rs:508-636 cover sender labels and fallbacks, clock
formatting in a fixed time zone, QR text generation and its error/missing
cases, wrapping, keeping the latest visible lines, tail_fitting, the
messages title and selection clamping. See Unit tests.