Skip to content

refactor(client): extract App.tsx render-prop closures into proper components #626

@srid

Description

@srid

Context

packages/client/src/App.tsx is 666 lines on feat/canvas-only-ux (was 585 at branch base — +81 net across the canvas-only-ux refactor). Project rule says "Keep App.tsx as a thin layout shell" (.claude/rules/solidjs.md), but App.tsx is currently doing both layout wiring and render-prop materialization for per-tile chrome.

What's bloated

Three inline closures account for ~280 of the 666 lines:

  • renderTileTitleActions(id) — ~110 lines. Per-tile chrome (agent indicator, theme pill, split toggle, search button, screenshot button). Reads store, rightPanel, subPanel, activeThemeName, etc.
  • renderCanvasTileBody(id, active) — desktop body wrapper (TerminalContent + SubPanel + search overlay).
  • renderMobileTileBody(id, visible) — mobile equivalent.

These exist as inline closures because CanvasTile / MobileTileView use a render-prop pattern (good for keeping the canvas/tile shells generic), and App.tsx ends up as the "wiring layer" that knows how to materialize per-tile chrome from singleton hooks.

Proposal

Extract each closure into its own component file:

  • packages/client/src/canvas/TileTitleActions.tsx — props { id: TerminalId }, reads useTerminalStore, useRightPanel, useSubPanel directly.
  • packages/client/src/canvas/CanvasTileBody.tsx — props { id: TerminalId; active: () => boolean }.
  • packages/client/src/MobileTileBody.tsx — props { id: TerminalId; visible: () => boolean }.

App.tsx then passes:
```tsx
renderTileTitleActions={(id) => }
renderTileBody={(id, active) => }
```

Expected outcome

  • App.tsx shrinks by ~150–180 lines.
  • Each new component owns its own singleton reads, satisfying the existing no-preference-prop-drilling rule and the spirit of "components own their behavior."
  • No behavior change.

Out of scope

  • The TILE_BUTTON_CLASS constant — fine to leave in App.tsx or move into the extracted component, whichever reads cleaner.
  • selectTerminalFromPill and other small helpers that genuinely are wiring.

Surfaced during /code-police on PR for canvas-only-ux branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorCode refactoring

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions