chore(agent): phase roadmap from TODO.md, 7 phases (127-133)
Converts the TODO list into the phased-execution roadmap (Protocol B append): incremental folder summaries on sync (127), the mobile sync label with the pinned (n/m) count (128), per-chunk embed progress in the sync label (129), chat answer clusters with the bottom-right actions row (130), read(offset/limit) line ranges for huge documents (131), visible chat image-upload feedback (132), and the garbage-PDF text gate with the page-screenshot vision fallback (133 — includes the diagnosed root cause of the hnb guide's pre-phase-126 binary import). Each phase carries its dedicated in-isolation Playwright suite; TODO.md is cleared (items now live in .agents/phases/todo/). LLM-Generated: true
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# Phase 128 — The sync label shows on mobile, count always visible
|
||||
|
||||
**Source:** `TODO.md` L4 — "The `Syncing… name/of/file/here (2/3)` doesn't show up on mobile, it needs to" (the item is truncated in the TODO; ASSUMPTION A1 — read as: the running label must be fully visible on mobile, with the `(n/m)` position never clipped off-screen).
|
||||
**Story:** n/a (TODO-derived phase, Protocol B append).
|
||||
**Context:** `frontend/index.html` L449–451 — the `#sync-btn` / `#sync-label` markup (one span holds the whole label string); `frontend/assets/sources.js` — `fmtSyncLabel(kind, currentFile, done, total)` (~L650) and `fmtSyncPhaseLabel(status)` (~L669) build the running labels as ONE string, the poll loop applies them to `#sync-label` + the button `title` + `#sync-result` (the aria-live announcer — the untruncated text rides both, A4 convention); `frontend/assets/styles.css` — `.sync-label` (~L2067: `inline-block`, `max-width: min(16rem, 40vw)`, ellipsis, `nowrap`) and the mobile media query (~L5323: `.sync-btn { width: 100%; }`, `.sync-label { max-width: none; min-width: 0; }` — with `max-width: none` the inline-block sizes to its FULL content, so the label's tail — where `(n/m)` lives — runs off-screen: the reported bug); `tests/unit/test_frontend_sync_upload.py` — the pinned frontend label tests to update.
|
||||
|
||||
## Objective
|
||||
On a phone, the running sync label is fully visible: the file path ellipsizes to fit, and the `(n/m)` position sits in its own non-shrinking span that is NEVER clipped — in the import phase (`Syncing… <file> (2/3)`) and the summaries phase (`Summarizing folders… <folder> (n/m)`). Desktop renders the same content with its existing visual layout (the cap, the ellipsis, the title/aria-live untruncated text all preserved).
|
||||
|
||||
## Dependencies
|
||||
- `127_sync_incremental_folder_summaries` (todo) — pipeline order only; code dependencies are the completed phase-64/98 label contract.
|
||||
|
||||
## Design (shared by all tasks — the executor reads this, not the chat)
|
||||
- **Markup (task 01):** `#sync-label` gains two child spans — `<span class="sync-label-main"></span><span class="sync-label-count" hidden></span>` (static markup in `index.html`). The JS builders no longer return one string: a single `setSyncLabel(mainText, countText)` helper writes `textContent` into the parts (the createElement/textContent house rule — no HTML strings) and toggles the count span's `hidden` (absent count → hidden, empty). The FULL untruncated label (`mainText + (countText ? " " + countText : "")`) is still set on the button `title` and `#sync-result` exactly as today (A4 — screen readers hear the whole thing).
|
||||
- **Label mapping (task 01):** main/count per state — prelude (null phase, no file): main `"Syncing…"`, no count; import: main `"Syncing… <file>"`, count `"(n/m)"` while `total > 0`; overview: main `"Writing KB overview…"`, no count; summaries: main `"Summarizing folders… <folder>"` (no folder part while `current_summary` is null), count `"(n/m)"`; upload: main `"Importing"`, no count (phase-90 contract — the upload status carries no file or counts); idle/settle labels (`"Sync sources"`, `"Synced HH:MM"`): main only. `fmtSyncPhaseLabel`'s decision tree is preserved 1:1 — only the OUTPUT shape changes (string → parts).
|
||||
- **CSS (task 02):** `.sync-label` becomes a flex row (`display: inline-flex; align-items: baseline; gap: 0.35rem; min-width: 0; max-width: min(16rem, 40vw);`); `.sync-label-main { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }`; `.sync-label-count { flex: 0 0 auto; white-space: nowrap; }`. Mobile media query (the existing block at ~L5323): `.sync-label { max-width: none; flex: 1 1 auto; min-width: 0; }` — the label fills the full-width pill, the main part ellipsizes against the remaining width, the count span stays visible at the right edge. The `#sync-result` mobile clip (visually-hidden) is untouched.
|
||||
|
||||
## Tasks
|
||||
1. `01_label_parts.md` — markup + `setSyncLabel` part builders in `sources.js` (all label states, A4 untruncated text preserved).
|
||||
2. `02_mobile_css.md` — the flex-part CSS + the mobile media query.
|
||||
3. `03_e2e_mobile_label.md` — dedicated Playwright E2E, run in isolation.
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: `tests/unit/test_frontend_sync_upload.py` — the pinned label outputs updated to the part shape (main/count per state, the byte-identical label strings where they must hold: prelude, upload, settle); the `title`/`#sync-result` untruncated text pins stay.
|
||||
- E2E: one dedicated Playwright file, run in isolation, mobile viewport, slow-LLM pattern.
|
||||
- Coverage: **>90%** on new/modified code (frontend: the JS is covered by the pinned unit tests + the E2E; `app/` untouched this phase).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] at a phone viewport (390×844) the running label is fully in the viewport — file path ellipsized, `(n/m)` visible — in BOTH the import and summaries phases
|
||||
- [ ] desktop label visually unchanged (same strings, same cap/ellipsis, same `title`/`#sync-result` untruncated text)
|
||||
- [ ] `uv run pytest tests/e2e/test_sync_label_mobile.py -v --no-cov` green in isolation
|
||||
- [ ] `uv run pytest` (unit + integration) green; no behavior change in completed phases
|
||||
Reference in New Issue
Block a user