Revert the share card QR experiments — back to half-blocks
Braille (one dot per module) would not scan, and the quadrant mosaic reads worse on screen than the plain half-block form. The original qr_text render scans and stays. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a00fbe5639
commit
bf992979ef
4 files changed
+3
-110
No files matched your search
@@ -815,13 +815,13 @@ async def show_contact_card(
|
||||
node_type: The MeshCore advert type byte (1 companion, 2 repeater, 3 room,
|
||||
4 sensor).
|
||||
"""
|
||||
from .qr import qr_quad
|
||||
from .qr import qr_text
|
||||
|
||||
url = contact_share_url(name, public_key, node_type)
|
||||
body = Group(
|
||||
Text("Scan to add this node as a contact:", style="muted"),
|
||||
Text(""),
|
||||
qr_quad(url),
|
||||
qr_text(url),
|
||||
Text(""),
|
||||
Text(url, style="accent"),
|
||||
)
|
||||
|
||||
@@ -5,13 +5,6 @@ Each character cell stacks two vertical modules — ``▀`` (upper only), ``▄`
|
||||
one and comfortably fits an interactive window. Modules are always drawn black on a white
|
||||
background (with the mandatory quiet-zone border), so a phone camera reads the code no
|
||||
matter what colour theme the terminal itself is using.
|
||||
|
||||
:func:`qr_quad` is the dense variant: quadrant mosaic glyphs (``▘▚▛█``…) pack a 2×2
|
||||
block of modules into every cell — half the columns of the half-block form at the same
|
||||
row count. Ink stays solid (every dark module is a filled quarter-cell), so it scans
|
||||
exactly as well as the half-block render, just drawn smaller. (A braille one-dot-per-
|
||||
module form was tried first: it drew even smaller but the dots' white surrounds starve
|
||||
the scanner of ink, and phones would not read it.)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -34,28 +27,6 @@ _GLYPH = {
|
||||
#: contrast holds regardless of the surrounding terminal palette.
|
||||
_STYLE = "black on white"
|
||||
|
||||
#: Quadrant glyph for each (top-left, top-right, bottom-left, bottom-right) dark-module
|
||||
#: quartet — the sixteen 2×2 mosaics Unicode provides (space and the half/full blocks
|
||||
#: fill the patterns the Block Elements quadrant range skips).
|
||||
_QUAD = {
|
||||
(False, False, False, False): " ",
|
||||
(True, False, False, False): "▘",
|
||||
(False, True, False, False): "▝",
|
||||
(True, True, False, False): "▀",
|
||||
(False, False, True, False): "▖",
|
||||
(True, False, True, False): "▌",
|
||||
(False, True, True, False): "▞",
|
||||
(True, True, True, False): "▛",
|
||||
(False, False, False, True): "▗",
|
||||
(True, False, False, True): "▚",
|
||||
(False, True, False, True): "▐",
|
||||
(True, True, False, True): "▜",
|
||||
(False, False, True, True): "▄",
|
||||
(True, False, True, True): "▙",
|
||||
(False, True, True, True): "▟",
|
||||
(True, True, True, True): "█",
|
||||
}
|
||||
|
||||
|
||||
def qr_text(data: str, *, error: str = "m") -> Text:
|
||||
"""Render ``data`` as a QR code built from half-block characters.
|
||||
@@ -83,39 +54,3 @@ def qr_text(data: str, *, error: str = "m") -> Text:
|
||||
text.append(line, style=_STYLE)
|
||||
text.append("\n")
|
||||
return text
|
||||
|
||||
|
||||
def qr_quad(data: str, *, error: str = "m") -> Text:
|
||||
"""Render ``data`` as a QR code built from quadrant mosaics, 2×2 modules per cell.
|
||||
|
||||
Half the width of :func:`qr_text` at the same row count, and every dark module is
|
||||
still a solid quarter-cell of ink — the property a scanner actually needs — so the
|
||||
smaller draw reads as well as the half-block form.
|
||||
|
||||
Args:
|
||||
data: The string to encode.
|
||||
error: QR error-correction level — ``l``/``m``/``q``/``h`` (default ``m``).
|
||||
|
||||
Returns:
|
||||
A Rich :class:`~rich.text.Text` (no-wrap) whose lines draw the code.
|
||||
"""
|
||||
import segno
|
||||
|
||||
qr = segno.make(data, error=error)
|
||||
rows = [[bool(v) for v in row] for row in qr.matrix_iter(border=_BORDER)]
|
||||
# Pad to whole cells with light modules: out to an even row and column count.
|
||||
if len(rows) % 2:
|
||||
rows.append([False] * len(rows[0]))
|
||||
if len(rows[0]) % 2:
|
||||
for row in rows:
|
||||
row.append(False)
|
||||
|
||||
text = Text(no_wrap=True)
|
||||
for top, bottom in zip(rows[0::2], rows[1::2], strict=True):
|
||||
line = "".join(
|
||||
_QUAD[(top[x], top[x + 1], bottom[x], bottom[x + 1])]
|
||||
for x in range(0, len(top), 2)
|
||||
)
|
||||
text.append(line, style=_STYLE)
|
||||
text.append("\n")
|
||||
return text
|
||||
@@ -91,8 +91,7 @@ async def test_show_contact_card_pops_the_qr_over_the_link() -> None:
|
||||
console.print(renderable)
|
||||
out = console.export_text()
|
||||
assert f"meshcore://contact/add?name=Hub&public_key={'ab' * 32}&type=2" in out
|
||||
# The QR actually drew its modules — quadrant mosaics, 2×2 modules per glyph.
|
||||
assert "█" in out
|
||||
assert "█" in out # the QR actually drew its modules
|
||||
|
||||
|
||||
# -- coordinate parsing ----------------------------------------------------------
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
"""The terminal QR renderers: half-block and quadrant-mosaic forms of the same matrix."""
|
||||
|
||||
import segno
|
||||
|
||||
from meshterm.ui.qr import _BORDER, _QUAD, qr_quad, qr_text
|
||||
|
||||
URL = "meshcore://contact/add?name=Test&public_key=" + "ab" * 32 + "&type=1"
|
||||
|
||||
|
||||
def _matrix(data: str) -> list[list[bool]]:
|
||||
qr = segno.make(data, error="m")
|
||||
return [[bool(v) for v in row] for row in qr.matrix_iter(border=_BORDER)]
|
||||
|
||||
|
||||
def test_qr_text_matches_matrix() -> None:
|
||||
rows = _matrix(URL)
|
||||
lines = qr_text(URL).plain.rstrip("\n").split("\n")
|
||||
glyph_bits = {"█": (True, True), "▀": (True, False), "▄": (False, True), " ": (False, False)}
|
||||
for y, row in enumerate(rows):
|
||||
for x, dark in enumerate(row):
|
||||
top, bottom = glyph_bits[lines[y // 2][x]]
|
||||
assert (top if y % 2 == 0 else bottom) == dark
|
||||
|
||||
|
||||
def test_qr_quad_matches_matrix() -> None:
|
||||
"""Every module maps to exactly its quarter of a mosaic glyph, raster order."""
|
||||
rows = _matrix(URL)
|
||||
lines = qr_quad(URL).plain.rstrip("\n").split("\n")
|
||||
quarters = {glyph: quartet for quartet, glyph in _QUAD.items()}
|
||||
for y, row in enumerate(rows):
|
||||
for x, dark in enumerate(row):
|
||||
quartet = quarters[lines[y // 2][x // 2]]
|
||||
assert quartet[(y % 2) * 2 + (x % 2)] == dark
|
||||
|
||||
|
||||
def test_qr_quad_geometry() -> None:
|
||||
"""Whole-cell padding: equal-length lines at half the matrix width and height."""
|
||||
rows = _matrix(URL)
|
||||
lines = qr_quad(URL).plain.rstrip("\n").split("\n")
|
||||
assert len(lines) == -(-len(rows) // 2)
|
||||
assert all(len(line) == -(-len(rows[0]) // 2) for line in lines)
|
||||
Reference in new issue
Block a user