Skip to content

feat: autofocus prop for the embedded chat composer - #95

Merged
camielvs merged 1 commit into
mainfrom
feat/embed-chat-autofocus
Sep 21, 2026
Merged

camielvs merged 1 commit into
mainfrom
feat/embed-chat-autofocus

Conversation

@camielvs

Copy link
Copy Markdown
Contributor

Description

Adds an autoFocus prop to the embedded chat so a host can land the user in the composer on load. The motivating flow is tangle-ui's new-project page (stack leading up to TangleML/tangle-ui#2762): it opens the tangent page, creates a session, and wants the cursor already in the box so the user can type immediately.

The composer's textarea is disabled until the socket joins and the agent roster arrives, so a plain DOM autofocus would land on a disabled field and do nothing. Instead the prop waits for the composer to accept input, then focuses once per mounted session — no refocus on reconnect, on a run finishing, or after the user clicks elsewhere in the host page. Focus uses preventScroll: true so the host page never jumps.

Threaded through all three layers of the contract:

  • <Chat autoFocus> in @tangent/embed-react, assigned as an element property before sessionId so the first render already knows.
  • autoFocus property + auto-focus attribute on <tangent-chat> for raw-element hosts.
  • ComposerShell owns the focus effect; ChatInput / SubagentTabView pass it through, so it also works when agentId targets a sub-agent thread rather than being silently ignored.

No server change was needed for the "start a session, say nothing" half of the flow — newSession("", bundleId) already queues an empty prompt that is never sent.

Screenshots

N/A — no visual change; the only difference is where focus sits on load.

How to test

  1. pnpm dev
  2. In a host app (or a scratch page) pointing at this instance, create a session without an opening prompt and render the chat with the prop:
    const { sessionId } = await newSession("", "tangle-oss", { name: "New project" });
    <Chat sessionId={sessionId} autoFocus />
  3. On load, the cursor lands in the composer as soon as it becomes enabled — typing goes straight into it, and the host page does not scroll.
  4. Click elsewhere in the host page, then let the agent finish a run or drop/restore the socket: focus stays where the user put it.
  5. Without autoFocus, behaviour is unchanged (nothing takes focus).

Notes

  • Prop-contract change, so tangle-ui needs a published @tangent/embed-react to get the typed prop (currently 0.1.1 — happy to bump in this PR or a follow-up). Not strictly required: a host can set element.autoFocus = true / the auto-focus attribute and pick up the behaviour from the runtime bundle alone.
  • Verification is pnpm validate + build:embed + the wrapper's .d.ts. This repo has no embed harness page (only public/bundle-ui-harness, which is for bundle UI), so the focus path has not been exercised in a browser here — it will be on the tangle-ui side.
  • Deliberately no imperative focusComposer() method: refocusing after mount is more annoying than useful. Easy to add later if a host surface needs it.

🤖 Generated with Claude Code

Lets a host land the user in the composer — e.g. a freshly created
project that opens the tangent page and starts a session — without a
click. The textarea is disabled until the socket joins and the roster
arrives, so DOM autofocus would land on a disabled field; this waits for
the composer to accept input and focuses once per mounted session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@camielvs
camielvs merged commit f8435e6 into main Sep 21, 2026
10 checks passed
@camielvs
camielvs deleted the feat/embed-chat-autofocus branch September 21, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant