Files
brain-of-reese/.agent/phases/complete/52_pinned_composer/01_sticky_composer.md
T

2.7 KiB

Task 01 — Sticky Bottom Composer

Phase: 52_pinned_composer · Source: TODO.md:3 — "The message input text box needs to be pinned to the bottom of the screen so it doesn't "run away" from the user as they try to click "stop"" Story: n/a (TODO-derived)

Objective

The composer stays pinned to the bottom of the viewport at every scroll position — a CSS-only change inside the existing chat column.

Work

  1. frontend/assets/styles.css — in the /* ---------- Composer ---------- */ block (.composer, ~L1133): add position: sticky; and bottom: env(safe-area-inset-bottom); to .composer. The page scrolls at the document level and .chat-shell is the composer's containing column, so the box sticks to the viewport's bottom edge (offset by the mobile safe-area inset) while .messages scrolls behind it; at the document bottom it settles back into its normal flow position above the footer. Keep the existing solid background: var(--surface), border, radius and box-shadow: var(--shadow) — messages must never show through the pinned box.
  2. frontend/index.html — verify NO change needed: the composer is already the LAST child of .chat-shell (the sticky context), and the #message-input / #send-btn / #send-status markup is untouched.
  3. Do NOT touch frontend/assets/app.js — the pin must not add any scroll call site (phase-42 invariant; the one page scroll in the file stays scrollReveal).
  4. tests/unit/test_pinned_composer.py (new, house pin style — see tests/unit/test_frontend_scroll.py): assert styles.css declares position: sticky AND a bottom: offset on .composer (the sticky-bottom pair, matched inside the .composer rule); assert app.js is unchanged in its scroll surface (the phase-42 single-scrollReveal pin still holds — reuse the same assertion approach test_no_reply_autoscroll.py's companion pins use).
  • ASSUMPTION: bottom: env(safe-area-inset-bottom) (not bottom: 0 + extra padding) — the standard notch-aware inset; on desktop env() resolves to 0, so the box sits flush with the viewport bottom.
  • ASSUMPTION: no z-index added — DOM order already stacks the composer above .messages; the sticky header (z 20) and doc modal (z 1000) are unaffected.

Testing & Quality

  • Unit: tests/unit/test_pinned_composer.py (the pins above) — uv run pytest tests/unit/test_pinned_composer.py -v green.
  • Coverage: >90% — no app/ change; the gate stays green.

Completion Criteria

  • .composer in styles.css carries position: sticky + the bottom safe-area offset; the frontend/ diff contains no JS change.
  • uv run pytest green; uv run ruff check . && uv run pyright clean.