ux(setup): rewrite captive-portal + AP/setup-screen copy

Beta tester called the previous setup wording "Chinglish."
Tighter, plainer language across all three surfaces:

- Captive portal (PORTAL_HTML): "Connect your frame", explicit
  "Home WiFi name / Home WiFi password", clearer footer.
- AP screen step list: 5-line plain-English checklist; no more
  Safari-specific reference.
- Setup screen: fixed step 2 wrapping mid-domain
  ("pictureframe / .edholm.me"), tightened steps 1 and 3.

Regenerated bg.bin to match the new gen_screens.py output.
NEEDS-FLASH: in-field beta unit still has prior copy.
This commit is contained in:
2026-05-09 15:17:15 -04:00
parent f1d867c659
commit c1bed8c218
8 changed files with 33 additions and 24 deletions
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+20 -15
View File
@@ -222,17 +222,19 @@ def gen_ap(accent=YL, header="SETUP MODE — STEP 1 OF 2", qr_label="SCAN TO C
# Steps — frames ship with battery unplugged to preserve shelf life # Steps — frames ship with battery unplugged to preserve shelf life
# (idle setup-screen polling is non-trivial draw on e-ink), so the # (idle setup-screen polling is non-trivial draw on e-ink), so the
# very first prompt is "Plug in power". Step 2 is unlock-first (iOS # very first prompt is "Plug in the frame". Step 2 is unlock-phone
# won't fire the captive UI from a locked-phone scan). Two-QR flow # because iOS won't fire the captive UI from a locked-phone scan.
# because iOS in recent versions doesn't auto-open the captive # Two-QR flow because iOS in recent versions doesn't auto-open the
# portal even after CNA detects it; scanning the second QR opens # captive portal even after CNA detects it; scanning the second QR
# Safari which forces the portal to render. # opens a browser which forces the portal to render.
# NEW WORDING 2026-05-09 — beta tester called the prior copy
# "Chinglish." Tighter, plainer, no Safari-specific reference.
steps = [ steps = [
("Plug in power", ""), ("Plug in the frame", ""),
("Unlock your phone first", ""), ("Unlock your phone", ""),
("Scan QR 1", "joins PictureFrame WiFi"), ("Scan QR 1", "joins your phone to PictureFrame"),
("Scan QR 2", "page opens in Safari"), ("Scan QR 2", "opens the setup page"),
("Enter your WiFi password", "and tap Connect"), ("Type your home WiFi password", "and tap Connect"),
] ]
sy = BODY_Y + 95 sy = BODY_Y + 95
step_pitch = 32 step_pitch = 32
@@ -361,13 +363,16 @@ def gen_setup():
bb = draw.textbbox((0,0), "ready.", font=F_HEAD) bb = draw.textbbox((0,0), "ready.", font=F_HEAD)
draw.rectangle([28, BODY_Y+82, 28+bb[2]+2, BODY_Y+85], fill=GR) draw.rectangle([28, BODY_Y+82, 28+bb[2]+2, BODY_Y+85], fill=GR)
draw.text((28, BODY_Y+96), "Scan to name this frame and", font=F_STEP, fill=(80,80,75)) draw.text((28, BODY_Y+96), "Scan the QR to link this frame", font=F_STEP, fill=(80,80,75))
draw.text((28, BODY_Y+110), "link it to your account.", font=F_STEP, fill=(80,80,75)) draw.text((28, BODY_Y+110), "to your account.", font=F_STEP, fill=(80,80,75))
# NEW WORDING 2026-05-09 — beta-tester feedback. Step 2 used to break
# mid-domain ("pictureframe / .edholm.me") which read as broken text;
# also tightened step 1 and 3.
steps = [ steps = [
("Scan the QR with your phone", "camera or QR app"), ("Scan the QR with your phone's", "camera"),
("Sign in at pictureframe", ".edholm.me"), ("Sign in or create an account", ""),
("Name the frame, choose", "orientation — done."), ("Name your frame and pick", "orientation — done."),
] ]
sy = BODY_Y + 136 sy = BODY_Y + 136
for i, (l1, l2) in enumerate(steps): for i, (l1, l2) in enumerate(steps):
+12 -8
View File
@@ -54,6 +54,8 @@ static void show_setup_qr(const String& mac) {
} }
// ── Captive portal HTML ─────────────────────────────────────────────────────── // ── Captive portal HTML ───────────────────────────────────────────────────────
// NEW WORDING 2026-05-09 — beta-tester feedback called the previous copy
// "Chinglish." Needs flash test before going to a recipient.
static const char PORTAL_HTML[] PROGMEM = R"html( static const char PORTAL_HTML[] PROGMEM = R"html(
<!DOCTYPE html><html lang="en"> <!DOCTYPE html><html lang="en">
@@ -64,26 +66,28 @@ static const char PORTAL_HTML[] PROGMEM = R"html(
min-height:100vh;margin:0;background:#fdf6ee;color:#3a2e22} min-height:100vh;margin:0;background:#fdf6ee;color:#3a2e22}
.card{width:100%;max-width:340px;margin:1rem;padding:1.5rem;background:#fff9f2; .card{width:100%;max-width:340px;margin:1rem;padding:1.5rem;background:#fff9f2;
border:1px solid #e8d9c4;border-radius:16px} border:1px solid #e8d9c4;border-radius:16px}
h1{font-size:1.25rem;margin:0 0 1.25rem} h1{font-size:1.25rem;margin:0 0 .5rem}
.sub{font-size:.875rem;color:#8a7060;margin:0 0 1.25rem;line-height:1.4}
label{display:block;font-size:.8125rem;font-weight:600;color:#8a7060;margin-bottom:.3rem} label{display:block;font-size:.8125rem;font-weight:600;color:#8a7060;margin-bottom:.3rem}
input{width:100%;min-height:44px;padding:0 .875rem;border:1px solid #e8d9c4; input{width:100%;min-height:44px;padding:0 .875rem;border:1px solid #e8d9c4;
border-radius:10px;background:#fff;font-size:1rem;color:#3a2e22;box-sizing:border-box;margin-bottom:1rem} border-radius:10px;background:#fff;font-size:1rem;color:#3a2e22;box-sizing:border-box;margin-bottom:1rem}
button{width:100%;min-height:44px;background:#c97c3a;color:#fff;border:none; button{width:100%;min-height:44px;background:#c97c3a;color:#fff;border:none;
border-radius:9999px;font-size:1rem;font-weight:700;cursor:pointer} border-radius:9999px;font-size:1rem;font-weight:700;cursor:pointer}
p{font-size:.875rem;color:#8a7060;margin-top:1rem;text-align:center} p.foot{font-size:.875rem;color:#8a7060;margin-top:1rem;text-align:center;line-height:1.4}
</style> </style>
</head> </head>
<body> <body>
<div class="card"> <div class="card">
<h1>Connect to WiFi</h1> <h1>Connect your frame</h1>
<p class="sub">Type the name and password of your home WiFi so the frame can join it.</p>
<form method="POST" action="/connect"> <form method="POST" action="/connect">
<label for="s">WiFi network name</label> <label for="s">Home WiFi name</label>
<input id="s" name="ssid" type="text" autocomplete="off" placeholder="e.g. HomeNetwork"> <input id="s" name="ssid" type="text" autocomplete="off" placeholder="e.g. HomeNetwork">
<label for="p">Password</label> <label for="p">Home WiFi password</label>
<input id="p" name="pass" type="password" autocomplete="off"> <input id="p" name="pass" type="password" autocomplete="off">
<button type="submit">Connect</button> <button type="submit">Connect</button>
</form> </form>
<p>pictureFrame will join your network and display a setup QR code.</p> <p class="foot">Once it connects, the frame's screen will update with the next step.</p>
</div> </div>
</body></html> </body></html>
)html"; )html";
@@ -93,9 +97,9 @@ static const char CONNECTING_HTML[] PROGMEM = R"html(
<title>Connecting</title> <title>Connecting</title>
<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center; <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;
min-height:100vh;margin:0;background:#fdf6ee;color:#3a2e22;text-align:center} min-height:100vh;margin:0;background:#fdf6ee;color:#3a2e22;text-align:center}
.card{padding:2rem;max-width:300px}</style></head> .card{padding:2rem;max-width:320px}p{line-height:1.5}</style></head>
<body><div class="card"><h2>Connecting…</h2> <body><div class="card"><h2>Connecting…</h2>
<p>The frame is joining your network.<br>Watch the display for a setup QR code.</p></div></body></html> <p>The frame is joining your WiFi. Look at the frame's screen a new QR code will appear there for the next step.</p></div></body></html>
)html"; )html";
// ── Web server handlers ─────────────────────────────────────────────────────── // ── Web server handlers ───────────────────────────────────────────────────────