Foundation (phase 01, verified): - FastAPI app: /api/health, /api/suggestions, /api/chat (placeholder), static frontend served locally (no CDN) - Postgres 17 + pgvector via db/Containerfile + compose.yaml (podman compose up -d db), Alembic initial migration (documents, chunks with vector(768), query_log) - LLM client targeting https://aipi.reeseapps.com/v1 (turbo/embed); scripts/llm_probe.py verified models + 768-dim embeddings live - Conditional debugpy: imported only when DEBUGPY=1 (attach on demand, :5678); logging config for clean single-line logs - Frontend shell: mobile-first chat + Sources pages, tokens, a11y baselines - Tests: 24 unit+integration (99% coverage on app/), ruff + pyright clean, Playwright smoke E2E (3 tests) against a deterministic mock LLM - Planning: .agent/PLAN.md (architecture + LOCKED decisions), AGENTS.md, 6 user stories, 7 phase files (one story / one phase / one Playwright suite each)
2.8 KiB
2.8 KiB
Story: Suggestion Chips
Phase: 05_story_suggestion_chips.md · E2E: tests/e2e/test_suggestion_chips.py
Narrative
As a user who opens the chat for the first time (or after a deflection), I want a few concrete example questions right in front of me — so I immediately understand what Brain is good at and can start with zero friction.
- Given I land on the chat page
- When the app is healthy
- Then I see 3–4 suggestion chips drawn from
GET /api/suggestions(defaults in settings, tuned to the real Homelab topics), and clicking one fills the composer and submits it.
Acceptance criteria
GET /api/suggestionsreturns the configured list (settings-driven, overridable viaBOR_SUGGESTIONSJSON env).- Chips render in the empty state as
<button class="suggestion-chip">(real buttons, not links/divs) withrole="list"container +role="listitem"items;aria-label="Suggested questions"on the group. - Click behavior: fills
#message-input, focuses it, and submits (one tap → answer). Keyboard: Tab to chip, Enter activates. - After the first user message the empty state (and its chips) is replaced by the conversation; chips re-appear only on deflection (see honest-deflection story).
- If
/api/suggestionsfails, the chat still works (progressive enhancement — no chips, no error spam). - Mobile: chips become a horizontally scrollable single row (no wrapping into the composer's territory).
UI Visualization & Structure
- Chips: pill (
border-radius: 999px),bg --brand-soft,text --brand-ink(≥6:1), 1px--lineborder, min-height 44px, comfortablepadding 0.55rem 1rem; hover deepens bg;:activescales 0.98. - Desktop:
flex-wrap: wrap, centered under the empty-state subcopy, gap 0.5rem. - Mobile (≤640px):
flex-wrap: nowrap; overflow-x: autosingle row,scrollbar-width: thin, chipsflex: 0 0 auto(thumb-friendly, no accidental double-tap on wrapped lines). - Default suggestion copy (tune to real docs in this phase):
- "How is my Kubernetes cluster set up?"
- "What's my backup strategy?"
- "How do I deploy a new service?"
- "What's currently running in the homelab?"
Playwright Mapping Rule
Test Scenario → tests/e2e/test_suggestion_chips.py:
test_onboarding_chips_render— goto/, assert ≥3.suggestion-chipvisible inside#suggestions(role=list) with non-empty text.test_chip_click_submits— click the first chip; assert a user bubble with the chip's exact text appears and the brain reply (mock) follows.test_chips_keyboard_accessible— Tab from the page start reaches the first chip; Enter submits it.test_chips_mobile_row— at 375px viewport, the chip row is horizontally scrollable (scrollWidth > clientWidthor single-line height check) and no chip is cut vertically.