---
id: ML-176.2
title: "Presto: add splash screen, search views, and on-screen keyboard"
status: Done
assignee: []
created_date: "2026-05-10 13:35"
updated_date: "2026-05-11 06:47"
labels:
  - presto
milestone: m-0
dependencies:
  - ML-176.1
references:
  - presto/AGENTS.md
  - presto/README.md
  - >-
    doc-17 -
    Implementation-Plan-ML-176.2-—-Presto-splash-screen-search-views-and-on-screen-keyboard
modified_files:
  - presto/records_on_the_day.py
parent_task_id: ML-176
priority: medium
ordinal: 2000
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->

Refactor `presto/records_on_the_day.py` into a unified app with a splash screen, search capability, and the existing calendar. All work is in the single MicroPython file.

## New states

- `STATE_HOME` (new, default on boot/wake)
- `STATE_SEARCH_INPUT` (new — search box + on-screen keyboard)
- `STATE_SEARCH_RESULTS` (new — results list, reuses day-view rendering)
- `STATE_MONTH` / `STATE_DAY` / `STATE_RECORD` (existing, unchanged logic)

## Splash screen (STATE_HOME)

- Two large touch targets: "Search Collection" and "Today's Records"
- "Today's Records" → transitions to existing `STATE_MONTH` / today's records flow
- "Search Collection" → transitions to `STATE_SEARCH_INPUT`
- On boot and display wake: always show `STATE_HOME`

## On-screen keyboard (part of STATE_SEARCH_INPUT)

- 3-row QWERTY layout: qwertyuiop / asdfghjkl / zxcvbnm
- Bottom row: [123 toggle] [space] [⌫ backspace] [OK]
- Numbers keyboard: 1234567890 / symbols row / [ABC toggle] [space] [⌫] [OK]
- Space and ⌫ are shared between both layouts
- Lowercase only, no shift/caps
- OK button disabled (dimmed) when query is empty
- Each key is a rounded rectangle, touch-mapped to append/delete chars
- Visual feedback on key press (brief highlight)
- Query buffer displayed in a text field area above the keyboard, max ~50 chars

## Search triggering

- User types query, presses OK
- OK calls `fetch_search_results(query)` → API
- Transitions to `STATE_SEARCH_RESULTS`

## Search results (STATE_SEARCH_RESULTS)

- Reuses existing `_draw_record_list()`, scroll, and row rendering from day view
- Header shows "Search: <query>" with Back button
- Back returns to `STATE_SEARCH_INPUT` (preserving query so user can refine)
- Tapping a record → `STATE_RECORD` (detail view, unchanged)
- Back from detail → returns to search results
- Empty results: "No records found" full-screen message
- Error: "Could not reach server" with retry-on-tap (same as day view)
- 20 results per page, no infinite scroll

## API client

- New `fetch_search_results(query)` function
- Calls `GET /api/v1/collection?q=<url-encoded-query>&limit=20`
- Parses `{total, records, ...}` response
- Returns `(records_list, error_flag)` tuple (same shape as `fetch_records`)

## Navigation state

- Track previous state to know where "Back" in detail view should go
- Detail Back → search results when entered from search
- Detail Back → day view when entered from day
- Month view and search views both have "Back to Home"

## Constraints

- Must reuse existing record row rendering (`_draw_record_row`), detail view (`draw_record_detail`), thumbnail fetching, WiFi, NTP, display sleep logic
- Scroll performance rules from AGENTS.md still apply
- Memory: no large buffers, keep GC pressure low
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria

<!-- AC:BEGIN -->

- [ ] #1 On boot, splash screen shows with 'Search Collection' and 'Today's Records' buttons
- [ ] #2 Tapping 'Today's Records' opens the existing month calendar (unchanged behavior)
- [ ] #3 Tapping 'Search Collection' opens search input view with on-screen QWERTY keyboard
- [ ] #4 Keyboard: tapping letter keys builds query string shown in input field
- [ ] #5 Keyboard: backspace removes last character
- [ ] #6 Keyboard: 123/ABC toggle switches between QWERTY and numbers keyboard layouts
- [ ] #7 Keyboard: OK button is visually disabled (dimmed) when query is empty
- [ ] #8 Keyboard: OK button triggers API search when query is non-empty
- [ ] #9 Search results display using same record row layout as day view (cover, title, artist, format, year)
- [ ] #10 Search results: Back button returns to search input view with query preserved
- [ ] #11 Search results: tapping a record opens detail view
- [ ] #12 Detail view Back returns to search results when entered from search
- [ ] #13 Detail view Back returns to day view when entered from day
- [ ] #14 Empty results show 'No records found' full-screen message
- [ ] #15 API error shows 'Could not reach server' with retry-on-tap
- [ ] #16 Display sleep/wake always returns to splash screen
- [ ] #17 Scroll in search results works identically to day view scroll
- [ ] #18 Keyboard renders correctly at 720×720: all keys visible and tappable
- [ ] #19 Back-to-Home navigation available from month view and search views
<!-- AC:END -->

## Implementation Notes

<!-- SECTION:NOTES:BEGIN -->

## Implementation complete (~617 new lines, 2450 total)

### State constants + globals

- Renumbered STATE_MONTH=0, STATE_DAY=1, STATE_RECORD=2. Added STATE_HOME=3, STATE_SEARCH_INPUT=4, STATE_SEARCH_RESULTS=5. Removed STATE_STARTUP.
- New globals: `search_query`, `search_results`, `search_results_error`, `search_scroll_offset`, `_search_content_height`, `keyboard_mode`, `previous_state`.

### Splash screen (STATE_HOME)

- `draw_home_screen()` with "Search Collection" and "Today's Records" buttons. Tapping "Today's Records" → month view with today highlighted. Tapping "Search Collection" → search input.

### On-screen keyboard (STATE_SEARCH_INPUT)

- QWERTY layout (3 rows) + numbers/symbols. Bottom row: [123/ABC] [Space] [Backspace] [OK]. OK dimmed when query empty. `_keyboard_hit_test()` maps touch to action. `handle_search_input_touch()` dispatches all actions. Visual feedback via `_flash_key()`.

### Search results (STATE_SEARCH_RESULTS)

- Reuses `_draw_record_row()`. Own scroll state (`search_scroll_offset`, `_search_content_height`). Header: "Search: <query>" with truncation. Empty: "No records found". Error: "Could not reach server" with retry-on-tap.

### API client

- `fetch_search_results(query)` → `GET /api/v1/collection?q=<encoded>&limit=20`. Returns `(records_list, error_flag)`.

### Generic display pipeline

- `prepare_records_for_display(recs=None)` and `preload_record_thumbnails(recs=None)` accept optional list. Height cache tracked per source.

### Navigation tracking

- `previous_state` tracks detail origin (STATE_DAY / STATE_SEARCH_RESULTS). Back → correct origin. `draw_record_detail()` reads from `search_results` or `records`.

### Back-to-Home

- Month header gains "H" Home button. `wake_display()` always sets `state = STATE_HOME`. Search input header Back → Home.

### Boot flow

- Starts at `draw_home_screen()`, no auto-fetch. User taps a button.

### Main loop

- New dispatches for STATE_HOME, STATE_SEARCH_INPUT, STATE_SEARCH_RESULTS (drag scroll mirrors day view).

### Touch helpers

- `touch_to_record_index()` accepts optional `recs` and `offset` params.

### Verification

- Syntax check passes (`python3 py_compile`). No `STATE_STARTUP` references remain.
<!-- SECTION:NOTES:END -->
