Files
kandev/docs/plans/threads-layouts/task-02-render-grid.md
T

189 lines
8.7 KiB
Markdown

---
id: "02-render-grid"
title: "Render the conversation grid"
status: done
wave: 2
depends_on:
- "01-persist-presentation"
plan: "plan.md"
requirements:
- REQ-UI-THREADS-DECK-004
acceptance_criteria:
- AC-UI-THREADS-DECK-004.1
- AC-UI-THREADS-DECK-004.2
- AC-UI-THREADS-DECK-004.3
- AC-UI-THREADS-DECK-004.4
- AC-UI-THREADS-DECK-004.5
- AC-UI-THREADS-DECK-004.6
- AC-UI-THREADS-DECK-004.7
- AC-UI-THREADS-DECK-004.8
system_design:
- ../../specs/ui/system-design/threads-conversation-deck.md
---
# Task 02: Render the conversation grid
## Summary
Render the saved layout using stable task tiles, with two live desktop/tablet
rows and the existing one-chat phone composition. Preserve selection, draft
identity, scrolling, and viewport-owned session delivery.
## In scope
- Pass effective layout from the page and add the proposed pure
`thread-layout.ts` helper plus board content-size measurement.
- Preserve direct task children and keys; add Grid tracks, odd/single-task
handling, height fallback, lower-row navigation, and layout-aware recovery.
- Refresh activation on layout changes, reject stale observer callbacks, and
prove both visible rows without increasing offscreen membership/streams.
- Localize the fallback explanation and preserve existing task actions.
## Out of scope
Composer disclosure and the Display editor/shortcut, owned by Tasks 03 and 04.
Do not change platform traffic contracts, task ordering, or admission limits.
## Acceptance
1. Columns/Grid match UI-01/UI-02, including odd/single/empty cases and the
300px row-height fallback; saved preferences survive responsive changes.
2. Switching layouts or removing a lower-row task retains the deterministic
reader/session/draft and existing task navigation behavior.
3. Both visible rows load selected conversations; 30-shell tests stay bounded,
offscreen chats release, and phone transitions retain one active detail.
## ASCII UI preview
UI-02: Grid at a sufficient desktop height, excerpt from
[full previews](plan.md#ui-02-grid-auto-hide-enabled-no-active-draft).
```text
+--------------+--------------+--------------+
| A [Open] | C [Open] | E [Open] |
| transcript | transcript | transcript |
+--------------+--------------+--------------+
| B [Open] | D [Open] | F [Open] |
| transcript | transcript | transcript |
+--------------+--------------+--------------+
```
Task 02 retains normal composers; Task 03 supplies the collapsed footer shown
in the combined preview. The task order is A, B, C, D, E, F.
UI-05 phone excerpt:
```text
[Threads / View] [2/6] [Menu]
[Task B v] [Open]
[one live conversation ]
[normal composer ]
```
The phone uses swipe/picker navigation. These structures cover all assigned
layout criteria. The board scrolls horizontally; each transcript owns its
vertical content scroll. Grid tracks must not stretch when content grows.
## Verification
From the repository root, after Task 01 bootstrap. Add behavior-specific RED
unit/browser assertions before implementation. New files below are planned.
```bash
(cd apps/web && pnpm exec vitest run components/threads/thread-layout.test.ts components/threads/threads-board.test.tsx components/threads/thread-column-activation.test.tsx components/threads/use-thread-selection-recovery.test.tsx components/threads/use-mobile-thread-position.test.tsx lib/threads/thread-selection-fallback.test.ts app/threads/threads-page-client.test.tsx)
(cd apps/web && pnpm run typecheck)
(cd apps/web && pnpm exec eslint components/threads/threads-board.tsx components/threads/thread-column.tsx components/threads/thread-layout.ts components/threads/use-thread-column-activation.ts components/threads/use-thread-selection-recovery.ts app/threads/threads-page-client.tsx --max-warnings 0)
(cd apps/web && pnpm run i18n:check)
(cd apps/web && pnpm run i18n:ratchet)
(cd apps/web && pnpm e2e:run --project chromium tests/task/threads-layouts.spec.ts tests/task/threads-view.spec.ts tests/task/threads-task-actions.spec.ts -- --retries=0)
(cd apps/web && pnpm e2e:run --project mobile-chrome tests/task/mobile-threads-view.spec.ts tests/task/mobile-threads-swipe.spec.ts -- --retries=0)
git diff --check
```
Use the managed runner's builds and memory limits. Confirm discovery and
capture rendered UI-01/UI-02 at wide desktop, short height, 767/768px, and a
coarse-pointer tablet. API-seeded preferences are fixture setup until Task 04
exposes controls; assertions must read and interact with the rendered board.
## Files likely touched
- `apps/web/app/threads/threads-page-client.tsx`
- `apps/web/components/threads/threads-board.tsx`, `thread-column.tsx`,
`use-thread-column-activation.ts`, and `use-thread-selection-recovery.ts`.
- New `apps/web/components/threads/thread-layout.ts` and its test.
- New `use-thread-selection-recovery.test.tsx` beside the hook; existing
board/activation/page tests named above.
- New `apps/web/e2e/tests/task/threads-layouts.spec.ts`; reused presentation
setup may live in new `threads-presentation-helpers.ts`.
- `apps/web/src/locales/*/threads.json` for the height fallback.
- Existing desktop/task-action/mobile specs only when fixture expectations
need the explicit default layout; retain their scenarios.
- `apps/web/components/threads/AGENTS.md` for layout and reflow ownership.
## Dependencies
Task 01. Layout consumes its normalized effective presentation.
## Risks
Pair wrappers or index-based React keys remount editors. An observer can
deliver old-layout geometry after resize. Both rows share horizontal offsets,
so recovery must retain task identity instead of inferring a unique task from
an x-coordinate. Full editors need bounded row sizing.
## Parallelism
sequential
## Inputs
- [Layout requirements](../../specs/ui/requirements/threads-conversation-deck.md#req-ui-threads-deck-004-conversation-layouts)
- [Layout design](../../specs/ui/system-design/threads-conversation-deck.md#layout-composition)
- Existing board, viewport activation, stable order, primary-session E2E,
thread task actions, and mobile swipe test patterns.
## Results
PR #3626 follow-up: observer rebuilds measure current visibility, height
fallback text no longer changes board allocation, wheel interaction records
the reader anchor, and deep-link scrolling honors reduced motion. Final
commands and results are in the [review remediation record](plan.md#pr-review-remediation-2026-09-12).
The original implementation evidence below remains historical.
Done, 2026-09-11.
- Behavioral RED: layout helper returned Columns for Grid, stale observer
delivery changed active details, and browser geometry remained single-row.
The real-page test additionally caught the native boot field omission
recorded in Task 01, resize-generated scroll/live snapshots replacing the
reader before responsive commit, and layout/width reflow interrupting
deep-link scroll.
- GREEN: the seven required Vitest files pass 62 tests. Task shells retain
identity, both rows activate, 30-shell preload/stream windows stay bounded,
lower-row horizontal anchors survive reflow, and user wheel input retires
the initial deep-link mark. Typecheck and the exact ESLint command pass.
- `pnpm run i18n:check`, `pnpm run i18n:ratchet`, and `git diff --check` pass.
Traditional Chinese and pseudo catalogs were generated; unrelated generator
changes were removed. No new dependencies or non-test runtime instances were
added. Updated the scoped engineering guide's grid and reflow ownership.
- First rebuilt desktop run: 19 passed, one deep-link failure. Corrected the
reflow handoff. Final desktop command below passes all 20 cases (3.7 minutes)
against the final same-membership snapshot guard, with retries disabled.
The guard's 62 unit cases, typecheck, lint, and localization ratchet pass too.
- Final mobile command: 10 tests passed with retries disabled (1.6 minutes),
after `pnpm run build:e2e`. Both phone-to-tablet selection and an immediate
resize after a deep link are covered. Inspected phone and coarse-tablet
screenshots: one phone chat, two tablet rows, retained lower-row selection,
and 44px Open task touch targets. This is emulator evidence, not a physical
keyboard/device check.
Final browser commands ran sequentially from `apps/web` against the final
production change, with one managed worker and no retries. The backend and
plugin fixtures were built by the earlier managed run; the final change was
frontend-only.
```bash
pnpm run build:e2e
pnpm e2e:run --no-build --project chromium tests/task/threads-layouts.spec.ts tests/task/threads-view.spec.ts tests/task/threads-task-actions.spec.ts -- --retries=0
pnpm e2e:run --no-build --project mobile-chrome tests/task/mobile-threads-view.spec.ts tests/task/mobile-threads-swipe.spec.ts -- --retries=0
```