Skip to content

fix(web): keep settings inputs focused during IME composition - #10262

Merged
t3dotgg merged 1 commit into
pingdotgg:mainfrom
Lucenx9:fix/web-settings-ime-commit
Sep 6, 2026
Merged

fix(web): keep settings inputs focused during IME composition#10262
t3dotgg merged 1 commit into
pingdotgg:mainfrom
Lucenx9:fix/web-settings-ime-commit

Conversation

@Lucenx9

@Lucenx9 Lucenx9 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What changed

Ignore IME keydowns in useCommitOnBlur using the same isComposing / keyCode === 229 guard already used by thread rename in #6281. Normal Enter and blur still commit changed values.

Why

Pressing Enter to confirm an IME candidate in a settings DraftInput currently blurs the field and saves an unfinished value. Further typing no longer reaches the input. This affects fields such as provider display names.

The shared hook covers web and desktop settings. Mobile uses separate inputs. No provider adapters, contracts, product defaults, or diagnostic settings change.

flowchart LR
    A[Enter in DraftInput] --> B{IME composition?}
    B -->|Yes| C[Keep focus and draft]
    B -->|No| D[Blur]
    D --> E[Commit changed value]
Loading

Verification

Tested the running app in local Chromium with synthetic IME keydown events on Settings → Providers → Display name. This does not claim a native OS IME or Safari test.

  • Before: both isComposing=true and isComposing=false, keyCode=229 blur and save prematurely.
  • After: both retain focus and leave the saved provider label unchanged. Typing can continue.
  • Normal Enter and Tab still save; the committed value survives a reload.
  • Targeted lint, formatting, and hook-only TypeScript checks pass. Fallow's changed-file audit passes with no introduced findings.

UI changes

PNG captures at 2×, immediately after the same synthetic composing Enter.

Before: focus is lost and the partial name has already replaced the provider label.

Before: IME Enter prematurely saves the display name

After: the input keeps focus and the provider label remains unchanged until a normal commit.

After: IME Enter keeps focus and the unsaved draft

Before video, typing team after the composing Enter is lost:

settings-ime-before.webm

After video, typing continues and a normal Enter saves the full name:

settings-ime-after.webm

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for UI changes
  • I included videos for interaction changes

Model: GPT-5. Harness: Codex in T3 Code.

Note

Fix useCommitOnBlur hook to ignore Enter during IME composition

Adds an early return in useCommitOnBlur.ts when the keyboard event is part of IME composition or uses the IME composition key code. This prevents the hook from committing and blurring the input while users are mid-composition with input methods like CJK keyboards. Non-composition Enter handling is unchanged.

Macroscope summarized 8f7adcf.

Summary by CodeRabbit

  • Bug Fixes
    • Improved text input behavior for IME users by preventing premature commits or field blurs when confirming composed text with the Enter key.

Copilot AI lite review requested due to automatic review settings September 6, 2026 02:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-06T02:27:08.406060Z 8f7adcf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Sep 6, 2026
@macroscopeapp

macroscopeapp Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 8f7adcf

Macroscope's review found this PR approvable — The PR adds a single localized IME guard to the shared settings input hook, preserving focus and the draft during composition while leaving normal Enter and blur commits unchanged. Its runtime impact is narrow and readily accounted for, with no product-default or static-analysis changes.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 09db0f39-17dc-400c-85e8-d17ef17897c9

📥 Commits

Reviewing files that changed from the base of the PR and between b438447 and 8f7adcf.

📒 Files selected for processing (1)
  • apps/web/src/hooks/useCommitOnBlur.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The useCommitOnBlur hook now ignores Enter key events that occur during IME composition. This prevents composition confirmation from triggering an early commit or blur.

Changes

IME commit handling

Layer / File(s) Summary
Composition guard
apps/web/src/hooks/useCommitOnBlur.ts
The onKeyDown handler returns when event.nativeEvent.isComposing is true or event.keyCode is 229.

Estimated code review effort: 2 (Simple) | ~5 minutes

Merge Risk: ⚪ Minimal · up to 8f7ad

IME candidate confirmation no longer prematurely commits or blurs settings inputs, while normal Enter and blur commits remain intact. No current merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preserving settings input focus during IME composition.
Description check ✅ Passed The description explains what changed, why it changed, verification results, UI evidence, and checklist completion. It is focused and complete.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@t3dotgg
t3dotgg merged commit 9c96ac2 into pingdotgg:main Sep 6, 2026
22 checks passed
BarretoDiego pushed a commit to BarretoDiego/t3code that referenced this pull request Sep 7, 2026
…tgg#10262)

Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 7, 2026
## What's Changed
* fix(clients): show feedback results in composer banners by @juliusmarminge in pingdotgg/t3code#10398
* fix(server): stop Windows terminal polling from spiking CPU by @UtkarshUsername in pingdotgg/t3code#9476
* fix(web): onboarding installs agents without needing Node or npm by @t3dotgg in pingdotgg/t3code#10402
* fix(server): allow settling threads with unanswered async questions by @t3dotgg in pingdotgg/t3code#10400
* feat(ci): ship stable releases from the latest nightly commit by @t3dotgg in pingdotgg/t3code#10410
* feat(marketing): add a nightly channel to the download page by @t3dotgg in pingdotgg/t3code#10408
* fix(web): keep settings inputs focused during IME composition by @Lucenx9 in pingdotgg/t3code#10262
* fix(server): preserve Codex reset credits during usage updates by @yashranaway in pingdotgg/t3code#10308
* docs: link the repository security reporting policy by @yashranaway in pingdotgg/t3code#10303
* fix(web): only show auto balance errors after failed checks by @maria-rcks in pingdotgg/t3code#10407
* fix(web): improve preview recording frame delivery by @maria-rcks in pingdotgg/t3code#10403
* fix(server): preserve inline provider secrets on redacted saves by @maxwellyoung in pingdotgg/t3code#10054
* fix(web, mobile): replace Apple desktop machine labels by @extoci in pingdotgg/t3code#10396
* fix(web): hide browser when the right panel starts closing by @Neel2107 in pingdotgg/t3code#10385
* fix(web): keep settings section headings description-free by @maria-rcks in pingdotgg/t3code#10415
* fix(usage): read and redeem hub reset credits through CLIProxyAPI by @juliusmarminge in pingdotgg/t3code#10395
* fix(web): deduplicate expanded tool labels and keep errors expandable by @Yash-Singh1 in pingdotgg/t3code#10420
* fix(server): skip git status scans while the index is locked by @Gigioxx in pingdotgg/t3code#9845
* fix(mcp): allow text-only preview snapshots by @juliusmarminge in pingdotgg/t3code#10232
* fix(claude): name the expired login or usage limit instead of a generic API error by @vitalyiegorov in pingdotgg/t3code#10321
* feat(mobile): queue a message while its attachment is still uploading by @juliusmarminge in pingdotgg/t3code#10404
* feat(mobile): show when an existing thread has a message waiting in the outbox by @juliusmarminge in pingdotgg/t3code#10405
* fix(codex): accept misalignment policy errors on thread resume by @realbakari in pingdotgg/t3code#10373
* fix(server): skip disabled settlement lookups by @t3dotgg in pingdotgg/t3code#10424
* fix(server): run OpenCode CLI commands sequentially by @t3dotgg in pingdotgg/t3code#10427
* feat(web): name the drop action while dragging sidebar threads by @SunkenInTime in pingdotgg/t3code#10378
* perf(web): keep the sidebar responsive during bulk thread updates by @t3dotgg in pingdotgg/t3code#10413
* fix(web): onboarding wizard now supports light mode by @t3dotgg in pingdotgg/t3code#10432
* feat(threads): dismiss async questions without replying by @t3dotgg in pingdotgg/t3code#10431
* fix(web): stop collapsing the composer when it loses focus by @t3dotgg in pingdotgg/t3code#10437
* fix(server): keep interrupted threads resumable after restarts by @maria-rcks in pingdotgg/t3code#10421

## New Contributors
* @Neel2107 made their first contribution in pingdotgg/t3code#10385
* @realbakari made their first contribution in pingdotgg/t3code#10373

**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260906.1316...v0.0.39-nightly.20260907.1325

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260907.1325
aorwall added a commit to aorwall/t3code that referenced this pull request Sep 7, 2026
Merges 141 upstream commits (`b438447f6..8b2838e`) into the fork,
following the
`fork-upstream-merge` skill. Landed as a merge commit; conflicts
resolved by the
path-policy verdicts in `docs/fork/inventory.json`.

## Merge shape

563 files landed (`git diff --stat HEAD^1 HEAD`) against 563 in the
upstream
range, so no upstream work was dropped. Fork delta 720 files.

## Conflicts

14 files, resolved by concern. The merge commit message names each. Two
are worth
carrying forward:

- Upstream extracted the project action rows into
`ProjectActionsList.tsx`. The
fork's Edit gate now rides an `editable` prop that defaults to
upstream's
  always-editable behavior, so the gate survives the next extraction.
- Upstream moved the `agent-browser-access` setting onto its new
`/settings/projects` page. `settingsSearch.ts` points that item there
and drops
  a fork delta.

## Two things a clean merge did not show

**Upstream can delete an export the fork still imports.** pingdotgg#10225 removed
`ClientTracingLive` as unused. `apps/web/src/lib/runtime.ts` still
installs it,
and neither file conflicted, so the typecheck failed four ways on a
merge git
called clean. Restored with a `// Fork:` comment naming the consumer.

**A green test step can hide a suite that never finished.** `vp run -r
test`
kills the packages still running when one of them fails. A
`@t3tools/desktop`
failure truncated `apps/web` and `@t3tools/mobile` after each had
reported
hundreds of passing files, and `verify.mjs` counted any package with
labeled
output as tested. Four failing web tests went unreported. The check now
keys on
the closing `Test Files` line and runs every unfinished package alone.

## Unsupported methods

`unsupported-methods.mjs` reports 0 ADD, 0 DROP, 2 KEEP, against 61
dispatched
backend methods and 131 contract methods.

Getting there took a fix. The backend moved its dispatch from
`crates/t3code/src/lib.rs` to `crates/t3code/src/rpc/dispatch.rs`, where
every
arm is a one-line call into a handler below the match. The script read
the old
path and reported zero dispatched methods, then read the new one and
called
`vcs.switchRef` a DROP, because the `unsupported_exit` that refuses it
had moved
out of the arm. It now tries both paths and follows an arm two calls
deep.

Contract changes: `provider.consumeResetCredit` and
`server.getHostResources`
gained `UnsupportedMethodError`; `server.getUsageSummary` lost it, which
closes
the item the previous merge left open.

## Feature classification

**Usable as-is** — client-only, nothing new on the wire. Sidebar drag
across
sections with destination cues and a named drop action (pingdotgg#9731, pingdotgg#9750,
pingdotgg#10378,
pingdotgg#10453, pingdotgg#10464). Composer behavior: a multiline draft survives timeline
scrolling (pingdotgg#10444), the composer stops collapsing on blur (pingdotgg#10437) and
regains
focus when you tab back (pingdotgg#10463). Panel and preview chrome: resize the
floating
preview from any edge (pingdotgg#10467), toolbar controls stay anchored (pingdotgg#10478),
the
stuck resize cursor clears (pingdotgg#10461), the browser hides as the right
panel closes
(pingdotgg#10385), manual panel choices hold during a turn (pingdotgg#10113). Settings and
accessibility polish (pingdotgg#10177, pingdotgg#10262, pingdotgg#10415, pingdotgg#10258, pingdotgg#10124, pingdotgg#10125,
pingdotgg#10127,
pingdotgg#10128, pingdotgg#10175). Performance (pingdotgg#10413, pingdotgg#10190, pingdotgg#10118). Plus the GitHub
mark on
`github.com` links (pingdotgg#10324), the Tux icon for WSL (pingdotgg#8511), a remembered
usage
page selection (pingdotgg#10189), project settings in the legacy sidebar menu
(pingdotgg#10021),
and text-only preview snapshots (pingdotgg#10232).

**Unsupported in Moatless** — resolves to a refusal, or falls through to
its own
empty state. Each is recorded in `docs/fork/gaps.md`:

- Reset credits through the hub and CLIProxyAPI (pingdotgg#10462, pingdotgg#10395,
pingdotgg#10308).
`provider.consumeResetCredit`, new union entry. `UsageLimits.tsx`
catches the
  refusal and shows "Could not use the reset credit."
- Balancing new threads across connected machines (pingdotgg#9895, pingdotgg#10433,
pingdotgg#10407).
`server.getHostResources`, new union entry. Nothing polls until a user
picks
automatic routing, and the composer then reads "Auto balance
unavailable."
- Onboarding: import grouped by repository (pingdotgg#10493), the shared
multi-computer
  wizard (pingdotgg#10465), agent install without Node or npm (pingdotgg#10402). All ride
  `agentSessions.scan` and `.import`, an existing gap.
- Shared project defaults and scoped overrides (pingdotgg#9754). The page reads,
and every
  write goes through `server.updateSettings`, which the backend does not
  dispatch.
- Two new `orchestration.dispatchCommand` types: `thread.active.reorder`
(pingdotgg#9729)
and `thread.user-input.dismiss` (pingdotgg#10431). Both are ordinary controls, a
sidebar
drag and a Dismiss button, and a dispatched command cannot be refused
per type.
That is the standing _A command cannot be refused_ gap, now 26 members
wide.

**Backend behavior to consider reproducing in Moatless** — upstream
server fixes
whose behavior the fork's client assumes:

- Invalid script IDs no longer crash threads (pingdotgg#10019). The fork ships
project
  scripts, so this one is worth reading first.
- Settlement: settle inactive threads without a PR lookup (pingdotgg#10103), skip
disabled
settlement lookups (pingdotgg#10424), settle threads with unanswered async
questions
  (pingdotgg#10400).
- Interrupted threads stay resumable after a restart (pingdotgg#10421).
- Completed requests stay closed across clients (pingdotgg#10123).
- Placeholder branches are followed after a checkout updates (pingdotgg#10441).
- A thread's PR links without an open client (pingdotgg#10101), and checkpoints
are
  captured before a PR status refresh (pingdotgg#10347).
- Adapters declare their own context compaction (pingdotgg#10112).
- Transcripts with oversized tool records import (pingdotgg#10430), and git
status scans
  are skipped while the index is locked (pingdotgg#9845).
- Usage limits pool per provider across accounts and environments
(pingdotgg#10300). The
client renders what `server.getUsageSummary` returns, so this shows
something
  only if the Moatless payload carries per-account limits.

Mobile, marketing, desktop, provider adapters and release tooling are
not this
fork's surface and are not classified.

## Also fixed here, and not upstream's doing

- Three `browser-*` search items still routed to
`/settings/integrations`, which
the fork owns for its Moatless administration page. A non-administrator
who
  searched for them was redirected away from the result.
- `moatless/listSearch.ts` carried no fork-only declaration.
- `pnpm fmt:check` failed on 294 files, 293 of them orval output. The
generator
  now formats what it writes through an `afterAllFilesWrite` hook.
- `@t3tools/moatless-api` exported `./generated`, a barrel that is never
checked
  in.

## Verification

`inventory-check.mjs` clean. `verify.mjs` green on seven checks:
duplicate-adds,
tripwires, resolution-check, unsupported-methods, `fmt:check` (3876
files),
`lint` and `typecheck`.

`test` is red on one package, and it is the machine.
`@t3tools/desktop`'s
`bundled libsecret helper` shells out to `pkg-config` for `libsecret-1`,
which
this sandbox does not have; it fails the same way when retried alone.
Everything
else passes: `apps/web` 369 files, `t3` 291, `@t3tools/mobile` 149,
`t3code-relay` 27, `@t3tools/client-runtime` 71, plus the smaller
packages.

`spec:check` cannot run in a sandbox: it needs a sibling `moatless`
checkout or a
deployment URL and has neither.

Written by Claude Opus 5 in Claude Code.

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

---
Moatless task:
https://moatless.soaplabstest.com/tasks/8d348ecf-f9cb-4e65-b96b-6c1054a8ed33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants