UX/UI Review Tracker
Source: review of backend/ and webapp/ on 2026-09-26,
focused on chat-bot UX and Mini App UX/UI.
Scope: backend/*.py, webapp/index.html, webapp/app.js,
webapp/style.css (vendor/ excluded).
Status legend: Open / In progress / Done / Won't do.
Check the box when an item is done: - [x].
Suggested build order
- UX-001, UX-002 (non-admin search, shared cache)
- UX-003, UX-004 (picker Back, replace confirm)
- UX-011, UX-012, UX-014 (non-host lock, waiting list, ended overlay)
- UX-005, UX-006, UX-013, UX-018, UX-019 (help/menu, polish, a11y)
- The rest (low/chore).
Chat bot
-
UX-001 (High, Open) Search is a dead end for non-admins
- Files:
backend/bot.pywatch_cmd (~L544), screen_search_prompt (~L246). - Problem: card Search tells anyone to send
/watch <name>, but that command requires admin. Non-admins follow instructions they cannot use. - Fix options: (a) allow search for all, gate only pick/play; (b) tailor the prompt for non-admins ("an admin searches").
- Done when: a non-admin tapping Search never lands on unusable instructions.
- Files:
-
UX-002 (High, Open) Per-room search cache shared across users
- Files:
backend/bot.py_picks(~L90), run_search, screen_*. - Problem: cache keyed by room only. Two admins searching at once overwrite each other's results/streams; taps index into whoever searched last. OOB is caught, wrong-list taps are not.
- Fix options: key cache per user, or embed a query token in callbacks and reject stale taps with "search again".
- Done when: two admins searching in one room cannot pick from each other's lists.
- Files:
-
UX-003 (High, Open) Back from file picker jumps to home
- Files:
backend/bot.pyscreen_streams footer (~L378), screen_title Back (~L288). - Problem: Back from streams goes to home, dropping title/episode context. Title Back resets results to page 0.
- Fix: carry the return screen (title/episode/results+page) in the callback payload.
- Done when: Back from streams returns to the title/episode screen; Back from title returns to the same results page.
- Files:
-
UX-004 (Medium, Open) No confirm when replacing a playing film
- Files:
backend/bot.pyplay_cmd (~L572),pickverb (~L914). - Problem:
/playand picker taps start immediately. One mistap interrupts everyone. Stop already has a confirm screen; replace does not. - Fix: when
room.state.nowis set, show a confirm step ("Stop X and play Y?"). - Done when: replacing a live film always asks first; starting from idle does not.
- Files:
-
UX-005 (Medium, Open) /help and command menu under-describe the app
- Files:
backend/bot.pyCOMMANDS (~L49), HELP (~L64). - Problem:
/deauthmissing from the menu; HELP omits auth grants, the host model, subtitles, in-player chat, and the external-player path. - Fix: add
deauthto COMMANDS; extend HELP by a few lines covering host, subtitles, chat, unplayable files. - Done when: menu lists deauth; /help mentions host, subtitles, chat, external player.
- Files:
-
UX-006 (Low, Open) Pause/resume confirm state they did not change
- Files:
backend/bot.pytransport_cmd (~L663). - Problem:
/pausewhile paused still says "Paused for everybody"; same for resume while playing. - Fix: reply "Already paused." / "Already playing." when idle.
- Done when: idempotent taps report current state, not a change.
- Files:
-
UX-007 (Low, Open) /auth needs a reply; no grant list
- Files:
backend/bot.pyauth_cmd (~L675). - Problem: reply-to-message is the only path; no username/id form, no way to see who is authed.
- Fix options: accept a username/id argument; add a list
(e.g.
/authsor include in card). Keep admin-only. - Done when: an admin can grant without a reply target and can see current grants.
- Files:
-
UX-008 (Low, Open) Search failure leaves a plain message, no retry path
- Files:
backend/bot.pyrun_search (~L560). - Problem: "Nothing found" edits the status message with no buttons or example to copy.
- Fix: include an example query line; keep it a plain message (no card needed).
- Done when: the failure message shows an example to retry with.
- Files:
-
UX-009 (Low, Open) Pager "2/5" button is a silent noop
- Files:
backend/bot.pypager (~L171),noophandler. - Problem: fine for sighted users, opaque for screen readers.
- Fix: give it a stable label or ignore; lowest priority.
- Done when: decided and documented, or label added.
- Files:
-
UX-010 (Low, Open) Startup log omits the usual "why doesn't X work" facts
- Files:
backend/main.pylifespan (~L44). - Problem: no one-line summary of ALLOWED_CHATS count or LINKS_ENABLED / DOWNLOADS_ENABLED flags.
- Fix: print counts/flags at startup (never secrets).
- Done when: boot log shows allowed-chat count and link/upload flags.
- Files:
Web app
-
UX-011 (High, Open) Non-host controls look usable but refuse
- Files:
webapp/app.jsroomWants (~L717);webapp/style.cssfullscreen info hide (~L407). - Problem: play/pause/seek appear enabled, then toast "Only X controls playback" and snap back. The host explanation is in the info block, hidden in landscape and fullscreen.
- Fix: visible locked/disabled control state for non-hosts plus a small on-stage "Hosted by X" badge.
- Done when: a non-host can tell before tapping that controls are the host's, in portrait, landscape, and fullscreen.
- Files:
-
UX-012 (Medium, Open) Countdown shows no waiting-for list
- Files:
webapp/app.jsrenderPresence (~L235);backend/state.pyready set (~L72), snapshot. - Problem: server tracks/transports
ready, the app never renders it. The 8s pause reads as a stall. - Fix: show "Waiting for N…" or ready dots during countdown.
- Done when: countdown names who/what the room waits for.
- Files:
-
UX-013 (Medium, Open) Every +/-30s nudge freezes the picture up to 4s
- Files:
backend/state.pyseek/nudge (~L146). - Problem: nudges re-arm the shared countdown, and the overlay covers the picture. Repeated skips feel broken.
- Fix options: shorter re-buffer window for small nudges than for full seeks; keep overlay but briefer.
- Done when: repeated +/-30s stays usable; full seeks still get a proper countdown.
- Files:
-
UX-014 (Medium, Open) Ended state has no overlay
- Files:
webapp/app.jsvideo:ended (~L326), applyState. - Problem: end-of-film only flips the sync dot; room keeps the film loaded, viewers sit on a frozen last frame.
- Fix: show "Finished — /watch for the next one" overlay.
- Done when: a finished film shows a next-step overlay, not a still frame.
- Files:
-
UX-015 (Low, Open) Chat head button changes meaning by layout
- Files:
webapp/app.jschat head handler (~L853). - Problem: same button hides (below picture) vs grows/shrinks (over picture). Icons mitigate; first-timers still mis-tap.
- Fix: none required; do not add a third behavior. Close as documented if accepted.
- Done when: accepted as-is, or behavior unified.
- Files:
-
UX-016 (Low, Open) Sync dot meaning is undiscoverable
- Files:
webapp/style.csssync-dot (~L236);webapp/app.jssetSync. - Problem: green/amber dot with no label or tooltip.
- Fix: add a
titleattr (and match legend in README if kept). - Done when: hovering/long-pressing the dot explains it.
- Files:
-
UX-017 (Chore, Open) Dead
_tgSdkMissingflag- Files:
webapp/index.html(~L8);webapp/app.js(never read). - Problem: set on CDN error, never consumed; vendored SDK is the real path.
- Fix: remove the flag or wire it to a fallback branch.
- Done when: flag removed or actually read.
- Files:
Visual and accessibility
-
UX-018 (Medium, Open) Chat input removes focus outline with no replacement
- Files:
webapp/style.csschat input (~L366); no:focus-visiblerules elsewhere. - Problem: keyboard users lose their place; other controls rely on inconsistent default outlines.
- Fix: visible focus ring for the input and player/chat
controls via
:focus-visible. - Done when: every interactive element shows focus by keyboard.
- Files:
-
UX-019 (Medium, Open) Toast and countdown are not announced to screen readers
- Files:
webapp/index.htmltoast/countdown markup;webapp/app.jstoast/renderCountdown. - Problem: connection state and shared start are visual-only.
- Fix:
aria-liveon toast (polite) and countdown (or a one-line status alternative). - Done when: a screen-reader user hears reconnect, refusal, and start events.
- Files:
-
UX-020 (Low, Open)
user-scalable=noblocks pinch zoom- Files:
webapp/index.htmlviewport (~L5). - Problem: real a11y tradeoff; 16px input already mitigates iOS auto-zoom.
- Fix: keep or relax, but leave a comment recording the decision.
- Done when: decision recorded in markup or this tracker.
- Files: