Skip to content

[Feature] Split session page controllers and fix composer dock scrolling #351

@Astro-Han

Description

@Astro-Han

Problem

packages/app/src/pages/session.tsx has grown into a 2,145-line session-page controller. It now owns route state, visible-session state, message history, timeline scrolling, composer dock layout, follow-up queueing, revert/restore, review mode, VCS diff loading, artifact files, desktop context sync, keyboard focus, mobile tabs, and right-panel wiring.

This makes local fixes risky because related state is split across distant blocks. The current composer dock scroll bug is the motivating defect: the timeline scroll container padding is written in one place, composer dock height is measured in two places, and only one path performs bottom-scroll compensation. In some mount or height-change paths, the CSS variable updates but the scroll position is not corrected, so the latest message can still be covered by the composer and the user cannot fully scroll past it.

Current evidence

packages/app/src/pages/session.tsx:1546 writes scroll container bottom padding with calc(var(--composer-dock-height, 0px) + 16px).

packages/app/src/pages/session.tsx:1898 observes the composer dock, writes --composer-dock-height, and performs bottom-scroll compensation when the dock height changes.

packages/app/src/pages/session.tsx:2028 also measures the prompt dock and writes the same CSS variable, but it does not perform bottom-scroll compensation. This creates two writers for the same layout state with different side effects.

Goal

Make the session page easier for humans and AI agents to maintain by turning session.tsx into a composition root instead of a mixed controller. The first implementation should fix the composer dock scrolling bug, then extract the directly related controller logic behind small, named hooks and components.

Scope

The first implementation PR may include both the scroll fix and the first controller extraction, but commits must stay review-sized. Behavior fixes, tests, refactors, visual spacing adjustments, and docs updates should be separate commits.

The first PR should focus on composer dock scrolling, scroll-dock ownership extraction, and final-turn spacing. Broader extractions such as history windowing, review state, follow-up state, and revert/restore are follow-up candidates under this issue, not required in the first PR.

The main target is packages/app/src/pages/session.tsx and closely related session-page modules. SessionSidePanel is intentionally out of scope for implementation in this issue. Right-panel product redesign is tracked separately in #154, and terminal-specific right-panel polish is tracked in #65.

Ownership rules

session.tsx should become the route-level composition root. It may read app context and wire controllers together, but the target architecture is that it should not directly own DOM measurement, timer-based loading retries, SDK mutations, VCS refresh state, scroll math, or composer dock layout state.

Shared layout state must have a single owner. In particular, composer dock height, --composer-dock-height, scroll container bottom padding, and bottom-scroll compensation should be owned by one controller.

New hooks and controllers should target roughly 200 lines where practical. This is a soft maintainability target, not a mechanical rule. Business cohesion wins over splitting purely to satisfy a line count.

Hooks should expose small public interfaces. They should not expose internal store setters unless the caller genuinely owns that state.

Acceptance criteria

When the timeline is already at the bottom, composer dock height changes keep the latest turn fully visible above the composer.

When the user has intentionally scrolled upward, composer dock height changes do not force the timeline back to the bottom.

The timeline can scroll fully past the composer dock, with no latest-message text hidden behind the composer.

After the correctness fix, the excessive final-turn gap is reduced in a separate visual commit and verified by screenshot, Browser Use, or Playwright smoke.

session.tsx no longer directly owns composer dock measurement and scroll compensation.

The first extracted controller has a clear name and narrow public API.

Unit tests cover the scroll-dock invariants. A browser or Playwright smoke check verifies the visible composer overlap case.

Non-goals

Do not rewrite the session UI.

Do not change the backend session protocol.

Do not redesign the right panel in this issue. See #154 and #65.

Do not move unrelated SessionSidePanel complexity into this PR.

Do not enforce a hard 200-line rule across all files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priorityenhancementNew feature or requestuiDesign system and user interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions