Skip to content

fix(vscode): handle multiline bidi prompt input - #12006

Merged
marius-kilocode merged 4 commits into
Kilo-Org:mainfrom
mjnaderi:fix/prompt-input-bidi-multiline
Jul 7, 2026
Merged

fix(vscode): handle multiline bidi prompt input#12006
marius-kilocode merged 4 commits into
Kilo-Org:mainfrom
mjnaderi:fix/prompt-input-bidi-multiline

Conversation

@mjnaderi

@mjnaderi mjnaderi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Issue

No linked issue; follow-up to #11918.

Context

Fix follow-up bugs in bidirectional prompt input support for multiline text.

The prompt textarea resolves direction per hard-newline paragraph, but the highlight overlay was still behaving like a normal div, so mixed multiline prompts could render differently between the textarea and overlay. File mention arrow navigation also assumed one resolved direction for the whole textarea, which breaks when different lines or paragraphs have different base directions.

Implementation

The prompt highlight overlay now uses unicode-bidi: plaintext so it matches textarea plaintext bidi behavior for each hard-newline paragraph.

File mention arrow navigation no longer derives a single textarea direction. Instead, it lets the VS Code WebView textarea perform its native bidi-aware caret move, then reads the updated collapsed selection and snaps to the mention boundary only if the caret landed inside a mention. This keeps mention skipping aligned with the actual caret movement for the current line/run.

Screenshots / Video

before after
before.mp4
after.mp4

How to Test

Manual/local verification

  • Manually built and tested the VS Code extension with different multiline mixed-direction prompt text.
  • Ran bun test tests/unit/use-file-mention.test.ts tests/unit/prompt-input-bidirectional.test.ts.
  • Ran bun eslint webview-ui/src/hooks/useFileMention.ts tests/unit/use-file-mention.test.ts tests/unit/prompt-input-bidirectional.test.ts.
  • Ran bun run check-types:webview.

Reviewer test steps

  1. Open the Kilo VS Code extension webview.
  2. Type multiline prompt text where one line starts with right-to-left text and another starts with left-to-right text.
  3. Confirm highlighted mentions / ghost overlay text stay aligned with the textarea across lines.
  4. Add a file mention to a right-to-left line and to a left-to-right line.
  5. Move the caret across each mention with left/right arrow keys.
  6. Confirm the caret skips the mention in the expected native visual direction for that line.

Blocked checks and substitute verification

  • None.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

if (pos === next) return

textarea.setSelectionRange(pos, pos)
}, 0)

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.

SUGGESTION: Possible stale-closure race under fast key repeat

cursor and val are captured in the handleArrowKey closure at keydown time, but the callback reads the live textarea.selectionStart (next) when it fires. If ArrowLeft/ArrowRight auto-repeats fast enough that two keydowns queue their setTimeout(0) callbacks before the first one runs, the first callback's next will reflect the caret position after both native moves (not just its own), while its cursor closure only reflects the position before its own move. That mismatch can make next > cursor (and thus the forward/backward branch, and the mention range lookup) resolve against a caret delta larger than one character, potentially snapping to the wrong edge or skipping a mention overlap check it should have made.

This is unlikely to trigger during normal single key-presses (repeat intervals are usually well above the setTimeout(0) delay), but it's worth a comment or a guard (e.g. bail if more than one arrow key is pending) if key-repeat/robustness matters here.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • packages/kilo-vscode/webview-ui/src/hooks/useFileMention.ts - the previously flagged stale-closure race (fast ArrowLeft/ArrowRight key-repeat) is fixed: pending arrow snaps are tracked in pendingArrowSnap and resolved synchronously via resolvePendingArrowSnap at the start of every handleArrowKey call, so a second keydown always resolves the prior snap using the intermediate caret position instead of letting two setTimeout(0) callbacks race against stale closures. Cleanup on unmount now also clears the pending timer.
  • packages/kilo-vscode/tests/unit/use-file-mention.test.ts - new test ("resolves a pending arrow snap before the next native arrow move") exercises two rapid ArrowRight keydowns and confirms the mention-boundary snap resolves against the correct intermediate position, and that the follow-up snap still behaves correctly afterward
Previous Review Summaries (2 snapshots, latest commit e717323)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit e717323)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • packages/kilo-vscode/webview-ui/src/hooks/useFileMention.ts - the previously flagged stale-closure race (fast ArrowLeft/ArrowRight key-repeat) is fixed: pending arrow snaps are tracked in pendingArrowSnap and resolved synchronously via resolvePendingArrowSnap at the start of every handleArrowKey call, so a second keydown always resolves the prior snap using the intermediate caret position instead of letting two setTimeout(0) callbacks race against stale closures. Cleanup on unmount now also clears the pending timer.
  • packages/kilo-vscode/tests/unit/use-file-mention.test.ts - new test ("resolves a pending arrow snap before the next native arrow move") exercises two rapid ArrowRight keydowns and confirms the mention-boundary snap resolves against the correct intermediate position, and that the follow-up snap still behaves correctly afterward

Previous review (commit 86cdccd)

Status: 1 Issue Found | Recommendation: Merge (minor suggestion, non-blocking)

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/hooks/useFileMention.ts 301 Possible stale-closure race in the caret-snap setTimeout under fast key repeat
Files Reviewed (5 files)
  • .changeset/fix-prompt-bidi-multiline.md - no issues, correctly describes user-facing fix
  • packages/kilo-vscode/tests/unit/prompt-input-bidirectional.test.ts - no issues, verifies overlay CSS bidi behavior
  • packages/kilo-vscode/tests/unit/use-file-mention.test.ts - no issues, tests updated to match native-caret-move + async snap behavior
  • packages/kilo-vscode/webview-ui/src/hooks/useFileMention.ts - 1 suggestion (async race edge case)
  • packages/kilo-vscode/webview-ui/src/styles/prompt-input.css - no issues, unicode-bidi: plaintext correctly matches textarea per-paragraph bidi resolution

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5-20260630 · Input: 32 · Output: 6.8K · Cached: 722.8K

Review guidance: REVIEW.md from base branch main

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Thanks @mjnaderi
would you be so kind to also make this work in the agent manager prompt modal? Then we have it all aligned.

image

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Let's maybe do that separate to the PR's you contributed to the prompt input.

Revert the stylistic `const fresh` introduced by Kilo-Org#12009 to the inline
ternary at both send sites so session.tsx drops back under the 3000-line
max-lines lint cap (3001 -> 2999). The draft-agent seeding contract is
preserved: a draftID is minted when there is no session, and the pending
agent is seeded before promptAgent(scope) resolves.

Update the two prompt-send-contract assertions to the compacted shape.
@mjnaderi

mjnaderi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@marius-kilocode Sure, happy to. Thanks for checking this and for the kind note.
I’ll send a separate PR to add the same bidirectional prompt input behavior to the Agent Manager “New Worktree” modal so the two prompt surfaces stay aligned.

@marius-kilocode marius-kilocode left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The bidi fix is sound: the overlay now uses unicode-bidi: plaintext to match the textarea per-paragraph resolution, and the file-mention arrow handling lets the native bidi-aware caret move run before snapping to mention boundaries via pendingArrowSnap/resolvePendingArrowSnap. The earlier stale-closure race from the bare setTimeout(0) is resolved by resolving pending snaps synchronously at the start of each handleArrowKey and clearing the timer on cleanup.

Pushed a follow-up commit reverting #12009's stylistic const fresh back to the inline ternary so session.tsx drops back under the 3000-line max-lines cap (3001 to 2999); the draft-agent seeding contract is preserved and the contract tests were updated to the compacted shape. The session.tsx regression was on main from #12009, not from this PR, but folding the compaction here keeps the PR's own CI green.

@marius-kilocode
marius-kilocode merged commit 5c41d65 into Kilo-Org:main Jul 7, 2026
21 checks passed
@mjnaderi

mjnaderi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@marius-kilocode I created #12015 for agent manager prompt modal.

t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
* fix(vscode): handle multiline bidi prompt input

* fix(vscode): resolve pending prompt arrow snaps

* fix(vscode): compact session draft-agent seeding under max-lines

Revert the stylistic `const fresh` introduced by Kilo-Org#12009 to the inline
ternary at both send sites so session.tsx drops back under the 3000-line
max-lines lint cap (3001 -> 2999). The draft-agent seeding contract is
preserved: a draftID is minted when there is no session, and the pending
agent is seeded before promptAgent(scope) resolves.

Update the two prompt-send-contract assertions to the compacted shape.

---------

Co-authored-by: Marius <marius@kilocode.ai>
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