# Composer The chat composer: the prompt language, the editor that renders it, and everything between typing and sending. `ChatInput.tsx` (one directory up) is the orchestrator. It holds the composer's own state and wires these modules together; it should not grow logic that belongs to one of them. ## Layers | Directory | Owns | |---|---| | `language/` | What the text *means*: `@` references, `/` and `#` tokens, markdown, and which picker a caret asks for | | `editor/` | The CodeMirror view that renders the language and owns the caret | | `state/` | Composer state with a lifecycle: drafts, mobile shell, history, popup placement, draft targeting | | `submit/` | Turning what the user has into what gets sent | | `attachments/` | Files: paths, drop payloads | | `ui/` | Presentation | | `text.ts` | How inserted text meets the text already there | ## The prompt language `language/` is the single source of truth for composer syntax. Everything that needs to know what a token means — highlighting, send-time resolution, and the autocomplete triggers — goes through it. **This is the invariant that matters most in this module.** Before it existed, the `@` rule was written four times with divergent cleanup and the `/` rule three times with different valid character sets, so a token could be painted as a reference and then not resolve as one. Adding a construct meant finding every copy. - `mentions.ts` — `@` references. The `start..end` span is the reference itself and is what gets highlighted; in `see @a/b.ts,` the comma is sentence punctuation, not part of the file being referenced. Mentions are plain editable text: deleting a character edits the token and reopens the mention picker, the same way `/skill` tokens behave — not an atomic delete. - `prefixTokens.ts` — `/command`, `/skill`, `#snippet`. Scanning is deliberately generous; **membership in the command, skill or snippet registry is the authority**, not the pattern. An unknown `/token` stays plain prose. - `triggers.ts` — which picker a caret position asks for. Exactly one can be active, with precedence `command > skill > snippet > mention`. - `tokenize.ts` — one pass producing every highlight range. Adding a construct to the language means adding it here, once. ## The editor `editor/` wraps CodeMirror. The document is a plain string: `getValue()` is exactly what gets sent, so nothing downstream serializes a rich document model back into a prompt. The composer previously painted a transparent `