Skip to content

[codex] Fix terminal IME composition handling#3331

Open
dandacompany wants to merge 6 commits into
wavetermdev:mainfrom
dandacompany:codex/fix-terminal-ime-composition
Open

[codex] Fix terminal IME composition handling#3331
dandacompany wants to merge 6 commits into
wavetermdev:mainfrom
dandacompany:codex/fix-terminal-ime-composition

Conversation

@dandacompany
Copy link
Copy Markdown

Summary

  • Preserve IME composition state when adapting keyboard events.
  • Avoid Wave global/custom terminal key handling while IME composition is active.
  • Add broader CJK font fallback while keeping Hack as the primary terminal font.
  • Fix the .fixed-font CSS declaration to use a valid font: property.

Why

IME composition key events can flow through Wave keyboard handling and terminal custom key handling. In interactive terminal prompts, this can cause unexpected input while composing text with Korean, Japanese, Chinese, or other composition-based input methods.

The terminal font fallback also previously relied only on Hack/monospace. Keeping Hack first preserves the existing monospace behavior, while adding Noto CJK and common platform CJK fallbacks improves glyph coverage for multilingual terminal output.

Verification

  • npm run build:dev in a clean worktree from this commit
  • Build completed with exit code 0
  • Existing Vite warnings observed: browser externalization for fs/path, circular chunk warning for cytoscape/mermaid
  • Image optimizer reported missing optional sharp package, but the build command still completed successfully

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR propagates IME/composition state into keyboard adapters and makes global, terminal, and TermWrap handlers consume composing events early. It adds a DefaultTermFontFamily constant and default config key term:fontfamily, updates JSON fixed-font CSS to the expanded fallback, and changes terminal WebGL default handling so WebGL is disabled unless explicitly enabled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] Fix terminal IME composition handling' clearly and concisely describes the main change: fixing IME composition handling in the terminal, which is the primary focus of all changeset modifications across keyboard event handling, font configuration, and composition-aware input routing.
Description check ✅ Passed The description comprehensively explains the changeset: it documents IME composition state preservation, avoids terminal key handling during composition, adds CJK font fallbacks, and fixes CSS properties—all of which directly correspond to the actual file changes.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 24, 2026

CLA assistant check
All committers have signed the CLA.

@dandacompany
Copy link
Copy Markdown
Author

@coderabbitai review

@dandacompany dandacompany marked this pull request as ready for review May 24, 2026 10:28
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

✅ Actions performed

Review triggered.

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
frontend/util/keyutil.ts (1)

243-243: ⚡ Quick win

Add isComposing to WaveKeyboardEvent to remove any casts in IME handling

frontend/types/gotypes.d.ts’s WaveKeyboardEvent lacks isComposing, but frontend/util/keyutil.ts sets it via (rtn as any).isComposing (lines ~243 and ~272) and frontend/app/store/keymodel.ts reads it via (waveEvent as any).isComposing (line ~420). Add isComposing?: boolean to the shared WaveKeyboardEvent type so these casts can be removed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/util/keyutil.ts` at line 243, The WaveKeyboardEvent type is missing
isComposing which forces unsafe any casts in keyutil.ts and keymodel.ts; add an
optional boolean property isComposing?: boolean to the WaveKeyboardEvent
declaration in frontend/types/gotypes.d.ts, then update usages in
frontend/util/keyutil.ts (where (rtn as any).isComposing is set around the IME
handling lines) and frontend/app/store/keymodel.ts (where (waveEvent as
any).isComposing is read) to remove the any casts and use the typed property
directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@frontend/util/keyutil.ts`:
- Line 243: The WaveKeyboardEvent type is missing isComposing which forces
unsafe any casts in keyutil.ts and keymodel.ts; add an optional boolean property
isComposing?: boolean to the WaveKeyboardEvent declaration in
frontend/types/gotypes.d.ts, then update usages in frontend/util/keyutil.ts
(where (rtn as any).isComposing is set around the IME handling lines) and
frontend/app/store/keymodel.ts (where (waveEvent as any).isComposing is read) to
remove the any casts and use the typed property directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 013d572c-1605-4183-a9fd-5d35fbd62e80

📥 Commits

Reviewing files that changed from the base of the PR and between 021db67 and 447cba7.

📒 Files selected for processing (7)
  • frontend/app/store/keymodel.ts
  • frontend/app/view/term/ijson.tsx
  • frontend/app/view/term/term-model.ts
  • frontend/app/view/term/term.tsx
  • frontend/app/view/term/termwrap.ts
  • frontend/util/keyutil.ts
  • pkg/wconfig/defaultconfig/settings.json

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/app/view/term/term.tsx`:
- Line 299: The code currently sets termDisableWebGl via
termSettings?.["term:disablewebgl"] ?? true which makes termDisableWebGl true
(WebGL disabled) by default and useWebGl is computed as !termDisableWebGl; this
conflicts with the docs that state the default is false; update the
documentation to reflect the actual default (term:disablewebgl defaults to true)
or change the default in code/config to false to match the docs, and add a short
migration/release note documenting the default behavior change for the setting
(referencing the termDisableWebGl variable and the useWebGl computation) so
users are informed of the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9f41b37e-e7dc-43b7-aae1-e85de3f15e5d

📥 Commits

Reviewing files that changed from the base of the PR and between 447cba7 and d1119b4.

📒 Files selected for processing (2)
  • frontend/app/view/term/term.tsx
  • pkg/wconfig/defaultconfig/settings.json

const termMacOptionIsMeta = globalStore.get(termMacOptionIsMetaAtom) ?? false;
const termCursorStyle = normalizeCursorStyle(globalStore.get(getOverrideConfigAtom(blockId, "term:cursor")));
const termCursorBlink = globalStore.get(getOverrideConfigAtom(blockId, "term:cursorblink")) ?? false;
const termDisableWebGl = termSettings?.["term:disablewebgl"] ?? true;
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for documentation or comments about WebGL default change

# Check for WebGL-related documentation
rg -n -C3 --type=md 'webgl|WebGL' | head -50

# Check for any TODO/FIXME about WebGL defaults
rg -n 'TODO|FIXME' --iglob '*webgl*' --iglob '*term*'

# Look for changelog or migration guide entries
fd -e md -e txt | xargs rg -l 'changelog|CHANGELOG|migration|breaking'

Repository: wavetermdev/waveterm

Length of output: 242


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the toggle logic around the referenced lines
sed -n '250,360p' frontend/app/view/term/term.tsx | nl -ba | sed -n '40,140p'

# 2) Search for the setting key and related strings
rg -n --hidden --no-ignore -S 'term:disablewebgl|disablewebgl|Default terminal rendering to non-WebGL|non-WebGL|useWebGl' frontend/app | head -200

# 3) Search docs/releases/changelog/migration/breaking notes
rg -n -S --hidden --no-ignore '(WebGL|webgl|non-WebGL|nonwebgl|disablewebgl|term:disablewebgl|breaking|migration|changelog|RELEASES\.md|Release note|release notes)' \
  . | head -200

# 4) Specifically inspect RELEASES.md for any WebGL-related entry
if [ -f RELEASES.md ]; then
  rg -n -S 'WebGL|webgl|non-WebGL|disablewebgl|term:disablewebgl|breaking|migration' RELEASES.md | head -200 || true
  # show nearby sections if any match lines
  MATCH_LINES=$(rg -n -S 'WebGL|webgl|non-WebGL|disablewebgl|term:disablewebgl|breaking|migration' RELEASES.md | cut -d: -f1 | head -20 | tr '\n' ' ')
  if [ -n "${MATCH_LINES:-}" ]; then
    for ln in $MATCH_LINES; do
      echo "---- RELEASES.md around line $ln ----"
      sed -n "$((ln-20)),$((ln+20))p" RELEASES.md
    done
  fi
else
  echo "RELEASES.md not found at repo root."
fi

Repository: wavetermdev/waveterm

Length of output: 107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect toggle logic around referenced lines (with line numbers)
sed -n '260,340p' frontend/app/view/term/term.tsx | cat -n | sed -n '1,120p'

# 2) Search for the setting key and related strings (scoped to term code + docs)
rg -n -S --hidden --no-ignore 'term:disablewebgl|disablewebgl|Default terminal rendering to non-WebGL|non-WebGL|useWebGl|useWebGL' frontend/app . | head -200

# 3) Search docs/releases/changelog/migration/breaking notes
rg -n -S --hidden --no-ignore '(WebGL|webgl|non-WebGL|nonwebgl|disablewebgl|term:disablewebgl|breaking|migration|changelog|RELEASES\.md)' . | head -200

# 4) Specifically inspect RELEASES.md
if [ -f RELEASES.md ]; then
  echo "---- RELEASES.md matches ----"
  rg -n -S 'WebGL|webgl|non-WebGL|nonwebgl|disablewebgl|term:disablewebgl|breaking|migration|non-WebGL' RELEASES.md | head -200 || true
else
  echo "RELEASES.md not found at repo root."
fi

Repository: wavetermdev/waveterm

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show termdisablewebgl docs/defaults/schema with tight context (exclude node_modules)
rg -n -S 'term:disablewebgl' pkg/wconfig/defaultconfig/settings.json schema/settings.json docs/docs/config.mdx frontend/types/gotypes.d.ts frontend/app/view/term/termwrap.ts frontend/app/view/term/term.tsx

echo "---- docs/docs/config.mdx ----"
sed -n '40,90p' docs/docs/config.mdx

echo "---- pkg/wconfig/defaultconfig/settings.json ----"
sed -n '1,80p' pkg/wconfig/defaultconfig/settings.json

echo "---- schema/settings.json (term:disablewebgl section) ----"
python3 - <<'PY'
import json
from pathlib import Path

p = Path("schema/settings.json")
data = json.loads(p.read_text())
# brute-force search for key in nested structures
def find_key(obj, key):
    if isinstance(obj, dict):
        for k,v in obj.items():
            if k == key:
                return v
            r = find_key(v, key)
            if r is not None:
                return r
    elif isinstance(obj, list):
        for it in obj:
            r = find_key(it, key)
            if r is not None:
                return r
    return None

v = find_key(data, "term:disablewebgl")
print(v)
PY

echo "---- frontend/app/view/term/termwrap.ts (renderer selection) ----"
sed -n '140,230p' frontend/app/view/term/termwrap.ts

# Search RELEASES.md for WebGL/non-WebGL without blowing up output (exclude node_modules)
if [ -f RELEASES.md ]; then
  rg -n -S 'WebGL|webgl|non-WebGL|nonwebgl|disablewebgl|term:disablewebgl' RELEASES.md | head -200 || true
fi

# Also search docs/releases folder if exists (avoid node_modules)
if [ -d docs ]; then
  rg -n -S '(WebGL|webgl|non-WebGL|nonwebgl|disablewebgl|term:disablewebgl)' docs | head -200 || true
fi

Repository: wavetermdev/waveterm

Length of output: 23101


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for release/note mentions (exclude node_modules)
for f in RELEASES.md docs/RELEASES.md CHANGELOG.md docs/CHANGELOG.md; do
  if [ -f "$f" ]; then
    echo "---- $f ----"
    rg -n -S 'non-WebGL|nonwebgl|WebGL|webgl|term:disablewebgl|disablewebgl|disable WebGL' "$f" || true
  fi
done

# Search docs + repo for the specific phrase the review comment mentioned
rg -n -S --hidden --no-ignore --glob '!**/node_modules/**' \
  'Default terminal rendering to non-WebGL|non-WebGL|term:disablewebgl|disablewebgl|useWebGl' . | head -200

# Check config default description inconsistency
echo "---- docs/docs/config.mdx line with term:disablewebgl ----"
rg -n -S 'term:disablewebgl' docs/docs/config.mdx

Repository: wavetermdev/waveterm

Length of output: 1621


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "DefaultTermFontFamily" frontend/app/view/term/term.tsx

Repository: wavetermdev/waveterm

Length of output: 207


Fix WebGL default inconsistency for term:disablewebgl (and document it)

  • frontend/app/view/term/term.tsx (299): termDisableWebGl = termSettings?.["term:disablewebgl"] ?? true => WebGL is disabled when the setting is unset; (323) useWebGl: !termDisableWebGl => defaults to false.
  • pkg/wconfig/defaultconfig/settings.json (35) also sets "term:disablewebgl": true.
  • docs/docs/config.mdx (63) states term:disablewebgl default is false, which conflicts with the code/config defaults above.
  • No release/changelog/migration note mentioning term:disablewebgl / WebGL (or “non-WebGL”) was found in RELEASES.md / docs release files.

Update the docs (and/or add release/migration notes if this default changed for existing users).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/view/term/term.tsx` at line 299, The code currently sets
termDisableWebGl via termSettings?.["term:disablewebgl"] ?? true which makes
termDisableWebGl true (WebGL disabled) by default and useWebGl is computed as
!termDisableWebGl; this conflicts with the docs that state the default is false;
update the documentation to reflect the actual default (term:disablewebgl
defaults to true) or change the default in code/config to false to match the
docs, and add a short migration/release note documenting the default behavior
change for the setting (referencing the termDisableWebGl variable and the
useWebGl computation) so users are informed of the change.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/app/view/term/termwrap.ts`:
- Around line 521-531: The flushPendingCompositionSpace path
(flushPendingCompositionSpace) and the onData handler in initTerminal are
dropping user keystrokes because they return when this.loaded is false; change
the logic to buffer or queue incoming data instead of discarding it: when
pendingCompositionSpace exists or onData fires and this.loaded is false (and not
disposed), append the bytes to a short-lived buffer (e.g.,
this._startupInputBuffer) and avoid clearing them; once loaded becomes true
(after bootstrap completes) flush that buffer by calling sendTermData for each
queued chunk (or a single concatenated string), and ensure disposed still
prevents queuing/flush. Reference functions/properties:
flushPendingCompositionSpace, initTerminal/onData subscription,
pendingCompositionSpace, loaded, disposed, and sendTermData.
- Around line 541-548: The regex checks in isLikelyCompositionText are
triggering the no-control-regex lint rule; replace the control-character and
non-ASCII regex tests with explicit code-point scans over the input string
(data) instead: iterate through characters (e.g., for...of or index loop using
charCodeAt/codePointAt) to return false if any code point is in the C0/C1
control ranges (0x00–0x1F or 0x7F) or if the string is empty, and return true if
any code point is outside the ASCII range (> 0x7F); keep the same early-return
behavior and preserve the function name isLikelyCompositionText and its input
parameter data.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: da994121-ce8d-4335-b0b3-a7e88d08c23f

📥 Commits

Reviewing files that changed from the base of the PR and between ada924d and 90749e9.

📒 Files selected for processing (1)
  • frontend/app/view/term/termwrap.ts

Comment thread frontend/app/view/term/termwrap.ts Outdated
Comment thread frontend/app/view/term/termwrap.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/app/view/term/termwrap.ts`:
- Around line 552-560: Replace the control-character regex in
isCompositionSuffixData with an explicit code-point check like you did in
isLikelyCompositionText: iterate the string (for...of or Array.from) and return
false if any codePoint <= 0x1F or codePoint === 0x7F, keep the initial empty
check, and retain the final printable-range test (or replace it with an explicit
check that all codePoints are between 0x20 and 0x7E); update the function
isCompositionSuffixData accordingly so there are no control-regex uses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bfe39f3a-8dbd-4316-aa92-833fcb36d927

📥 Commits

Reviewing files that changed from the base of the PR and between 90749e9 and 42d0b9a.

📒 Files selected for processing (1)
  • frontend/app/view/term/termwrap.ts

Comment thread frontend/app/view/term/termwrap.ts
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