Coverage for src/ai_shell/qr.py: 100%

16 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-09-03 01:05 +0000

1"""Terminal QR rendering shared by the pairing/tunnel integrations. 

2 

3Both the T3 pairing URL and the Expo tunnel URL have to be scannable from a 

4phone before the terminal is handed over to an agent, so the code is drawn 

5with half-block characters (two matrix rows per text row) — the same way t3's 

6own CLI draws its codes, so the two outputs look identical in a terminal. 

7""" 

8 

9from __future__ import annotations 

10 

11 

12def render_qr(data: str, border: int = 2) -> str | None: 

13 """Render *data* as a half-block QR code, or None if segno is missing.""" 

14 try: 

15 import segno 

16 except ImportError: # pragma: no cover - segno is a declared dependency 

17 return None 

18 

19 matrix = [bytearray(row) for row in segno.make(data, error="m").matrix] 

20 size = len(matrix) 

21 

22 def dark(x: int, y: int) -> bool: 

23 return 0 <= x < size and 0 <= y < size and bool(matrix[y][x]) 

24 

25 rows: list[str] = [] 

26 for y in range(-border, size + border, 2): 

27 row = "" 

28 for x in range(-border, size + border): 

29 top, bottom = dark(x, y), dark(x, y + 1) 

30 row += "█" if top and bottom else "▀" if top else "▄" if bottom else " " 

31 rows.append(row) 

32 return "\n".join(rows)