# refactor\(web\): the composer send reads its own settings and draft instead of taking them from ChatView · PR \#23095 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23095)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/split3-send-inputs

Target branch: refactor/split3-send-multimodel

Head commit: f6f27d6ba1e15350293c4be0ad3f3ae8996acc61

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23095)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23095/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23095/diffs?format=markdown)

## Description

---

Fourth layer of splitting `useComposerSend`.

The send now reads its own state instead of taking it from ChatView:
- Client settings hydration comes from `useClientSettingsHydrated`.
- `planModeEnabled` comes from `useEnvironmentSettings` with a selector.
- At send time it reads the environment presentation atom (the source of ChatView's `environmentById`/`serverConfig`) for worktree-bootstrap support, attachment-upload support and a stranded thread's machine label.
- It reads the composer draft store for non-prompt content and referenced thread contexts.

Decision logic moves to `composerSend.logic.ts` with tests: referenced thread contexts, the load-balancing toast copy, why a multiple-model send is blocked, and what to say when a send over pending approvals did not go out.

| | before | after |
|---|---|---|
| `useComposerSend.ts` lines | 1,377 | 1,383 |
| `useComposerSend` input fields | 76 | 69 |

Across the stack, `useComposerSend.ts` goes from 1,916 to 1,383 lines and from 82 to 69 input fields. The resume, plan and multiple-model paths now live in their own hooks (167, 402 and 293 lines). The plain single-thread dispatch stays inside `onSend`, because it shares nearly every value its validation computes. Pulling it out would only swap the input object for an equally large argument object.

Render boundaries are unchanged.

Tested in this worktree's dev server (isolated `.t3`, throwaway git repo as the project) with the whole stack applied: plain send from a draft (new thread), send with an image, queued follow-up and Ctrl+Enter steer during a running turn, resume of a stopped run, plan refinement and "Implement in a new thread" (legacy plan mode on), and a two-model start (two threads, two worktrees). All replies arrived and nothing errored. Also ran `apps/web` typecheck, lint on changed files (only the pre-existing `prefer-set-has` warning in `useComposerSend.ts` remains) and the React Compiler coverage test for changed files (0 bailouts). Ran `vp test run src/components/chat/composerSend.logic.test.ts` (14 tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

