fix(app): make composer input IME-safe and paste-aware (#3343)

* fix(app): preserve native composer input semantics

Native text was round-tripped through application state on every keystroke, which could overwrite an input method's in-flight edits. Keep native text ownership inside the input and reserve application-driven replacement for explicit draft mutations.\n\nRoute native clipboard images through the existing persisted attachment pipeline so keyboard and system paste actions behave like other image sources.

* fix(nix): refresh dependency hash

The native composer dependency changes the fixed npm dependency closure used by desktop builds.

* fix(app): keep composer locked across image pastes

Count concurrent native image persistence operations so submit remains disabled until every pasted image is attached.

* fix(app): replace composer text through the DOM on web

Web TextInput refs are DOM elements, so application-driven draft replacement must update their value and selection without native setNativeProps.

* fix(app): preserve web IME composition

Carry the Chinese IME fix from #2990 into the web composer input boundary.

Co-authored-by: Jimmy Lee <jimersylee+github@gmail.com>

---------

Co-authored-by: Jimmy Lee <jimersylee+github@gmail.com>
This commit is contained in:
Mohamed Boudra
2026-08-14 11:52:24 +02:00
committed by GitHub
co-authored by Jimmy Lee
parent 770b87a15c
commit 30195d3bd1
23 changed files with 757 additions and 71 deletions
+15 -5
View File
@@ -1228,7 +1228,9 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
// when only toast state changes (which does not affect any draft field).
const {
text,
setText,
editText,
replaceText,
textReplacementKey,
attachments,
setAttachments,
clear,
@@ -1239,7 +1241,9 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
const agentInputDraft = useMemo(
(): AgentInputDraft => ({
text,
setText,
editText,
replaceText,
textReplacementKey,
attachments,
setAttachments,
clear,
@@ -1249,7 +1253,9 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
}),
[
text,
setText,
editText,
replaceText,
textReplacementKey,
attachments,
setAttachments,
clear,
@@ -1296,7 +1302,10 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
const contentContainer = <View style={styles.contentContainer}>{streamContent}</View>;
return (
<RewindComposerRestoreProvider text={agentInputDraft.text} setText={agentInputDraft.setText}>
<RewindComposerRestoreProvider
text={agentInputDraft.text}
setText={agentInputDraft.replaceText}
>
<View style={styles.root}>
<FileDropZone style={styles.container} disabled={isArchivingCurrentAgent}>
{contentContainer}
@@ -1629,7 +1638,8 @@ function ActiveAgentComposer({
externalKeyboardShift
isPaneFocused={isPaneFocused}
value={agentInputDraft.text}
onChangeText={agentInputDraft.setText}
onChangeText={agentInputDraft.editText}
textReplacementKey={agentInputDraft.textReplacementKey}
attachments={agentInputDraft.attachments}
attachmentScopeKeys={attachmentScopeKeys}
onOpenWorkspaceAttachment={handleOpenWorkspaceAttachment}