Skip to content

Keep the chat composer to one focus indicator on web - #89

Merged
jerelvelarde merged 2 commits into
mainfrom
jerel/composer-focus-ring
Sep 29, 2026
Merged

jerelvelarde merged 2 commits into
mainfrom
jerel/composer-focus-ring

Conversation

@jerelvelarde

Copy link
Copy Markdown
Collaborator

Keep the chat composer to one focus indicator

The problem

When you click into the chat composer on web and start typing, Chrome draws its default focus ring around the textarea. The ring is a dark blue rectangle with square corners, sitting inside a composer that is rounded and already shows focus by tinting its own border light blue. So you see two focus indicators at once, and the square one clashes with the pill every time you type a message.

The approach

The composer keeps its own focus cue (the focused state that tints the pill's border and deepens its shadow), and the textarea inside it stops drawing the browser's ring. This is only for web; nothing changes on iOS or Android.

The obvious fix, outlineWidth: 0, does not work. Chrome paints the :focus-visible ring as outline-style: auto, which ignores the outline width, so the ring stays. The recording's first take checks this: the textarea's computed outline goes from auto 1px to none only once the style is outline-style: none. React Native's TextStyle type accepts only solid | dotted | dashed for outlineStyle, but react-native-web passes none through to the DOM. The value therefore lives in one web-only constant (noFocusRing) with a comment saying why it is cast, so the cast stays in one place.

The cost is small but real: keyboard users now see focus through the pill's border tint, which is softer than the browser's ring. Focus is still visible, but the contrast is lower.

What is not covered

  • The mail search field in screens.tsx sits inside a styled container the same way and probably shows the same ring. It is left alone to keep this change to the surface that was reported.
  • Standalone bordered inputs (Field, the website address input) keep the browser ring on purpose, because there it is the only focus indicator.

Verification

  • pnpm --dir apps/mobile typecheck passes.
  • biome check apps/mobile/src/chat.tsx is clean.
  • The mobile tests pass: 13/13 (apps/mobile/test/*.test.ts).
  • Recorded against this branch running on Expo web (sample workspace, Chromium). The "before" take is the same run with chat.tsx reverted to main. The computed textarea outline goes from auto 1px to none 1px.

jerelvelarde and others added 2 commits September 29, 2026 03:58
The composer pill already signals focus by tinting its border, so Chrome's
focus ring on the textarea drew a second, square box inside the rounded pill.
Chrome paints outline-style auto at any width, so the fix sets outline-style
none on web only.
@jerelvelarde
jerelvelarde merged commit 6151b4e into main Sep 29, 2026
7 checks passed
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Sep 30, 2026
Drop the browser focus ring inside the chat composer

The composer pill already signals focus by tinting its border, so Chrome's
focus ring on the textarea drew a second, square box inside the rounded pill.
Chrome paints outline-style auto at any width, so the fix sets outline-style
none on web only.
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Sep 30, 2026
上游 5 个提交:JEV(CopilotKit#42)、无 scheme worker 地址(CopilotKit#92)、web 输入框焦点(CopilotKit#89)、
worker 测试健壮性(CopilotKit#90)、render 蓝图(CopilotKit#86)。

冲突解决(4 文件 9 块)与本地适配:
- conversation.ts:以上游 JEV 结构(runInternal/choices 分支/noteEvidence)为基座,
  换回我们的中性工具层(forAgUi + chatTools),并把 noteEvidence 改成经 ChatToolContext 回调;
  prompt 用我们的 chatInstructions() + JEV 段 + computerInstructions;恢复 maxSteps=10 与
  sample 分支的 threadId 注入
- agent.ts:保留我们的 mastra 引擎分支,两处 ConversationAgent 注入 sharedJevAdapter()
- chat.tsx:我们的卡片(搜索/浏览器操作/审批)与 JEV 卡片并存,去重 delegate_task 渲染器
- config.ts:去掉 cherry-pick 造成的 browserWorkerUrl 重复定义
- mastra-engine.ts:JEV 工具与指令补到 mastra 路径(上游只挂自带引擎,我们线上跑 mastra)
- jev/tools.ts:抽出 presentChoicesSpec 中性规格,两条引擎共用同一份校验逻辑

验证:pnpm test 321/321 通过(含上游 JEV 全部测试)
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Oct 14, 2026
Drop the browser focus ring inside the chat composer

The composer pill already signals focus by tinting its border, so Chrome's
focus ring on the textarea drew a second, square box inside the rounded pill.
Chrome paints outline-style auto at any width, so the fix sets outline-style
none on web only.
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Oct 14, 2026
Drop the browser focus ring inside the chat composer

The composer pill already signals focus by tinting its border, so Chrome's
focus ring on the textarea drew a second, square box inside the rounded pill.
Chrome paints outline-style auto at any width, so the fix sets outline-style
none on web only.
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Oct 14, 2026
上游 5 个提交:JEV(CopilotKit#42)、无 scheme worker 地址(CopilotKit#92)、web 输入框焦点(CopilotKit#89)、
worker 测试健壮性(CopilotKit#90)、render 蓝图(CopilotKit#86)。

冲突解决(4 文件 9 块)与本地适配:
- conversation.ts:以上游 JEV 结构(runInternal/choices 分支/noteEvidence)为基座,
  换回我们的中性工具层(forAgUi + chatTools),并把 noteEvidence 改成经 ChatToolContext 回调;
  prompt 用我们的 chatInstructions() + JEV 段 + computerInstructions;恢复 maxSteps=10 与
  sample 分支的 threadId 注入
- agent.ts:保留我们的 mastra 引擎分支,两处 ConversationAgent 注入 sharedJevAdapter()
- chat.tsx:我们的卡片(搜索/浏览器操作/审批)与 JEV 卡片并存,去重 delegate_task 渲染器
- config.ts:去掉 cherry-pick 造成的 browserWorkerUrl 重复定义
- mastra-engine.ts:JEV 工具与指令补到 mastra 路径(上游只挂自带引擎,我们线上跑 mastra)
- jev/tools.ts:抽出 presentChoicesSpec 中性规格,两条引擎共用同一份校验逻辑

验证:pnpm test 321/321 通过(含上游 JEV 全部测试)
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Oct 14, 2026
Drop the browser focus ring inside the chat composer

The composer pill already signals focus by tinting its border, so Chrome's
focus ring on the textarea drew a second, square box inside the rounded pill.
Chrome paints outline-style auto at any width, so the fix sets outline-style
none on web only.
sunshaoan0808 pushed a commit to sunshaoan0808/openmuse that referenced this pull request Oct 14, 2026
上游 5 个提交:JEV(CopilotKit#42)、无 scheme worker 地址(CopilotKit#92)、web 输入框焦点(CopilotKit#89)、
worker 测试健壮性(CopilotKit#90)、render 蓝图(CopilotKit#86)。

冲突解决(4 文件 9 块)与本地适配:
- conversation.ts:以上游 JEV 结构(runInternal/choices 分支/noteEvidence)为基座,
  换回我们的中性工具层(forAgUi + chatTools),并把 noteEvidence 改成经 ChatToolContext 回调;
  prompt 用我们的 chatInstructions() + JEV 段 + computerInstructions;恢复 maxSteps=10 与
  sample 分支的 threadId 注入
- agent.ts:保留我们的 mastra 引擎分支,两处 ConversationAgent 注入 sharedJevAdapter()
- chat.tsx:我们的卡片(搜索/浏览器操作/审批)与 JEV 卡片并存,去重 delegate_task 渲染器
- config.ts:去掉 cherry-pick 造成的 browserWorkerUrl 重复定义
- mastra-engine.ts:JEV 工具与指令补到 mastra 路径(上游只挂自带引擎,我们线上跑 mastra)
- jev/tools.ts:抽出 presentChoicesSpec 中性规格,两条引擎共用同一份校验逻辑

验证:pnpm test 321/321 通过(含上游 JEV 全部测试)
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.

2 participants