# refactor\(web\): composer key handling lives in useComposerCommandKey · PR \#23060 · pingdotgg/t3code

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

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

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/split2-composer-command-keys

Target branch: refactor/split2-composer-footer-controls

Head commit: c59c78e7b187b19a88634920f0d19d70270b90ff

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

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

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

## Description

---

The editor key handler and prompt-history navigation are about 140 lines in the composer body. The handler covers send shortcuts, the slash/mention menu keys, ArrowUp/ArrowDown history, the Shift+Tab plan toggle, and list continuation on Enter/Tab.

This PR moves:
- `navigatePromptHistory`, `replacePromptFromHistory`, the history messages ref and `onComposerCommandKey` into `useComposerCommandKey`. The composer passes in the helpers it already had and keeps its existing `onComposerCommandKeyStable` wrapper. `promptHistoryPositionRef` stays in the composer, because the prompt-change handler also resets it.
- `extendReplacementRangeForTrailingSpace`, a pure helper used by mention, skill and text insertion, into `composer-logic.ts` next to `replaceTextRange`, with a test.

The existing layout-effect ref for prompt history messages moved with the code. This PR adds no `useStableCallback` and no new layout-effect refs.

| File | before | after |
| --- | --- | --- |
| ChatComposer.tsx | 4,111 | 3,992 |
| useComposerCommandKey.ts | | 264 |

Renders are unchanged. Typing 20 chars is 165 renders, the same as #23058.

Tested:
- `vp test run src/composer-logic.test.ts`, 54 passed, including the new case.
- apps/web typecheck and lint on the touched files.
- React Compiler: 0 bailouts on ChatComposer.tsx, useComposerCommandKey.ts and composer-logic.ts.
- Hands-on in a dev build:
  - Enter sends from a draft.
  - Enter sends in a seeded thread with history (Take Over PR 6315, Claude, Supervised). The thread first compacted its context, which it was already set to do, then the queued message ran and Claude replied "OK".
  - In that thread, ArrowUp recalls the last prompt and ArrowDown clears it again.
  - In the slash menu, ArrowDown moves the highlight and Escape closes the menu.
- Not checked: Shift+Tab. This dev environment shows no plan-mode control, so there was nothing to toggle.

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

