Skip to content

Conversation

@748561321
Copy link

@748561321 748561321 commented Dec 11, 2025

Add basic IME support for Input component
This PR tries to improve Chinese (IME) input in Langflow's Input component. The main issue was that typing Chinese caused the cursor to jump or input to be lost, because React updates the input value on every onChange event without handling composition.
What was changed

  • Added onCompositionStart and onCompositionEnd event handlers.
  • Added a isComposing flag to track if the user is currently composing text.
  • During composition, onChange updates are temporarily ignored, preventing cursor jumps.
    This is a quick, initial fix. Tested mostly in Chrome; may need further testing in other browsers or with different input methods.
    Open to feedback and improvements.
    

Summary by CodeRabbit

  • Improvements
    • Enhanced input component with improved state management and stability.
    • Added support for IME (Input Method Editor) composition to properly handle languages using character composition.
    • Refined placeholder visibility and display behavior for better user experience.
    • Strengthened internal ref handling for more reliable external component integration.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added the community Pull Request from an external contributor label Dec 11, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Enhances the Input component with local controlled-like state management initialized from defaultValue or value, adds IME composition handling with corresponding event handlers to ensure onChange fires correctly after composition ends, implements robust ref handling via mergedRef to preserve external ref semantics, and updates rendering to use currentValue with improved placeholder visibility logic.

Changes

Cohort / File(s) Summary
Input component enhancement
src/frontend/src/components/ui/input.tsx
Adds local currentValue state initialized from defaultValue or value; implements IME composition tracking with onCompositionStart/End handlers; creates mergedRef to merge forwarded and internal refs; updates input to use defaultValue={currentValue} with enhanced placeholder visibility logic; adds onChange, value, and defaultValue properties to InputProps interface; sets Input.displayName.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Ref merging logic: Verify mergedRef correctly handles both forwarded and internal refs without conflicts
  • IME composition handling: Confirm composition state tracking and onChange invocation timing are correct across different input methods
  • State initialization: Ensure currentValue initialization from defaultValue and value handles all edge cases and prop changes correctly
  • Placeholder visibility logic: Validate placeholder display behavior ties correctly to currentValue in all scenarios

Suggested labels

bug

Suggested reviewers

  • deon-sanchez
  • Cristhianzl
  • mfortman11

Pre-merge checks and finishing touches

❌ Failed checks (1 error, 2 warnings)
Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error PR adds IME composition support and controlled/uncontrolled component logic to Input component but includes no test files to verify this critical functionality. Create input.test.tsx with tests covering IME composition handling, controlled vs uncontrolled behavior, defaultValue initialization, onChange invocation, and ref forwarding.
Test Quality And Coverage ⚠️ Warning PR adds IME composition event handling to Input component but includes no new test cases for the modified functionality. Add unit tests covering IME composition events, controlled/uncontrolled behavior, and edge cases like rapid composition events and concurrent prop changes.
Test File Naming And Structure ⚠️ Warning Pull request adds IME support to Input component without corresponding test coverage for the new functionality. Create test file at src/frontend/src/components/ui/tests/input.test.tsx with comprehensive tests for IME composition, ref forwarding, onChange timing, and rendering behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Excessive Mock Usage Warning ✅ Passed No test files found in repository to assess mock usage patterns in relation to the input component changes.
Title check ✅ Passed The title 'fix(input): handle Chinese IME composition correctly' directly and accurately describes the main change—adding IME composition handling to the Input component to fix issues with Chinese input.

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.

Copy link
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

🧹 Nitpick comments (1)
src/frontend/src/components/ui/input.tsx (1)

35-41: Ref merging works but types and inputRef usage can be tightened

The merged ref logic is functionally fine, but a couple of small improvements would make it more idiomatic and typesafe:

  • mergedRef should accept HTMLInputElement | null, because React calls ref callbacks with null on unmount.
  • inputRef currently isn’t used outside the merge; if you don’t need it yet, you can drop it to reduce noise. If you plan to use it, prefer typing it as HTMLInputElement | null.

For example:

-    const inputRef = React.useRef<HTMLInputElement>(null);
+    const inputRef = React.useRef<HTMLInputElement | null>(null);
@@
-    const mergedRef = (node: HTMLInputElement) => {
-      inputRef.current = node;
+    const mergedRef = (node: HTMLInputElement | null) => {
+      inputRef.current = node;
       if (typeof ref === "function") ref(node);
       else if (ref) (ref as any).current = node;
     };

If inputRef isn’t needed yet, you can also simplify <input ref={mergedRef}> to use a small mergeRefs helper or inline callback when you introduce a second consumer.

Also applies to: 88-88

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63141c1 and d7249ae.

📒 Files selected for processing (1)
  • src/frontend/src/components/ui/input.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{ts,tsx}: Use React 18 with TypeScript for frontend development
Use Zustand for state management

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/**/*.{tsx,jsx,css,scss}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

Use Tailwind CSS for styling

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/components/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/components/**/*.{tsx,jsx}: Use React Flow for flow graph visualization with Node, Edge, Controls, and Background components
Use the cn() utility function for combining Tailwind CSS classes in components
Use TypeScript interfaces for defining component props in React components

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{tsx,jsx}: Implement dark mode support using the useDarkMode hook and dark store
Use Lucide React for icon components in the application

Files:

  • src/frontend/src/components/ui/input.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-24T19:46:57.920Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/icons.mdc:0-0
Timestamp: 2025-11-24T19:46:57.920Z
Learning: Applies to src/frontend/src/icons/*/index.tsx : Create an `index.tsx` for each icon that exports the icon using `forwardRef` and properly types it as `React.ForwardRefExoticComponent<React.PropsWithChildren<{}> & React.RefAttributes<SVGSVGElement>>`.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-16T11:14:04.200Z
Learnt from: dolfim-ibm
Repo: langflow-ai/langflow PR: 8394
File: src/frontend/src/icons/Docling/index.tsx:4-6
Timestamp: 2025-06-16T11:14:04.200Z
Learning: The Langflow codebase consistently uses `React.PropsWithChildren<{}>` as the prop type for all icon components using forwardRef, rather than `React.SVGProps<SVGSVGElement>`. This is an established pattern across hundreds of icon files in src/frontend/src/icons/.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-23T12:46:52.420Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/icons.mdc:0-0
Timestamp: 2025-06-23T12:46:52.420Z
Learning: Export custom icon components in React using React.forwardRef to ensure proper ref forwarding and compatibility with parent components.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-23T12:46:42.048Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-06-23T12:46:42.048Z
Learning: Custom React Flow node types should be implemented as memoized components, using Handle components for connection points and supporting optional icons and labels.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
🧬 Code graph analysis (1)
src/frontend/src/components/ui/input.tsx (2)
src/frontend/src/utils/utils.ts (1)
  • cn (38-40)
src/frontend/tailwind.config.mjs (1)
  • className (476-476)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 16/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 17/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 15/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 14/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 12/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 9/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 1/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 13/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 11/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 3/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 10/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 8/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 7/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 2/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 5/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 6/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 4/17
  • GitHub Check: Test Docker Images / Test docker images
  • GitHub Check: Test Starter Templates

Comment on lines 24 to 29
onChange,
value,
defaultValue,
...props
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -name "input.tsx" -path "*/src/frontend/src/components/ui/*" 2>/dev/null | head -5

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

git ls-files | grep -E "src/frontend/src/components/ui/input\.tsx"

Repository: langflow-ai/langflow

Length of output: 104


🏁 Script executed:

cat -n src/frontend/src/components/ui/input.tsx | head -120

Repository: langflow-ai/langflow

Length of output: 4062


🏁 Script executed:

rg -A 3 -B 1 "<Input\s+" src/frontend/src --type tsx --type jsx | head -100

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

rg 'value\s*=' src/frontend/src --type tsx --type jsx -A 1 | grep -A 1 '<Input' | head -50

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

rg "import.*Input.*from.*input" src/frontend/src | head -20

Repository: langflow-ai/langflow

Length of output: 2625


🏁 Script executed:

rg "<Input" src/frontend/src --type ts --type jsx | head -30

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

find src/frontend/src -name "*.tsx" -o -name "*.jsx" | xargs grep -l "<Input" | head -10

Repository: langflow-ai/langflow

Length of output: 1663


🏁 Script executed:

grep -A 5 "<Input" src/frontend/src/modals/secretKeyModal/components/form-key-render.tsx | grep -E "(value|onChange)" | head -10

Repository: langflow-ai/langflow

Length of output: 181


🏁 Script executed:

grep -B 2 -A 5 "<Input" src/frontend/src/pages/MainPage/components/header/index.tsx | head -30

Repository: langflow-ai/langflow

Length of output: 405


🏁 Script executed:

grep -B 2 -A 5 "<Input" src/frontend/src/pages/MainPage/components/inputSearchComponent/index.tsx | head -30

Repository: langflow-ai/langflow

Length of output: 351


🏁 Script executed:

cat -n src/frontend/src/modals/secretKeyModal/components/form-key-render.tsx | head -80

Repository: langflow-ai/langflow

Length of output: 1435


🏁 Script executed:

rg "onCompositionStart|onCompositionEnd" src/frontend/src --no-heading | head -10

Repository: langflow-ai/langflow

Length of output: 249


Controlled input behavior is broken by value/defaultValue handling

The destructuring of value and defaultValue (lines 24-27) removes them from ...props, and the <input> uses defaultValue={currentValue} instead of value={currentValue}. This makes the input effectively uncontrolled, breaking existing controlled usages:

  • In src/frontend/src/modals/secretKeyModal/components/form-key-render.tsx (and likely other places), the component is used as <Input value={apiKeyName} onChange={...} />, a controlled pattern.
  • When apiKeyName changes after mount, the input will not update because the value prop is ignored after initialization.
  • Parent-driven resets or form state changes won't be reflected.

Fix: Use value={currentValue} instead of defaultValue={currentValue} and add a useEffect to sync value when not composing:

-          defaultValue={currentValue}
+          value={currentValue}

Also add after line 43:

+    React.useEffect(() => {
+      if (!isComposing && value !== undefined) {
+        setCurrentValue(String(value));
+      }
+    }, [isComposing, value]);

This preserves uncontrolled usage via defaultValue (initialization only) and controlled usage via value (synced after mount), while IME behavior remains gated by isComposing.

Missing handler chaining for composition events (lines 89-90)

onCompositionStart and onCompositionEnd override any user-supplied handlers without calling them. Chain them:

-    const handleCompositionStart = () => setIsComposing(true);
+    const handleCompositionStart = (e: React.CompositionEvent<HTMLInputElement>) => {
+      setIsComposing(true);
+      props.onCompositionStart?.(e);
+    };

-    const handleCompositionEnd = (e: React.CompositionEvent<HTMLInputElement>) => {
+    const handleCompositionEnd = (e: React.CompositionEvent<HTMLInputElement>) => {
       setIsComposing(false);
       const newValue = e.currentTarget.value;
       setCurrentValue(newValue);
+      props.onCompositionEnd?.(e);
       onChange?.(e as any);
     };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/frontend/src/components/ui/input.tsx lines 24-27, 43, and 89-90: the
component currently destructures value/defaultValue out of props and renders
<input defaultValue={currentValue}>, which breaks controlled inputs; change the
input to use value={currentValue} so controlled props update, add a useEffect
after line 43 that syncs internal currentValue from the value prop when not
composing (so defaultValue still works for uncontrolled initialization), and
update onCompositionStart/onCompositionEnd handlers (lines 89-90) to call any
user-supplied handlers in props (chain them) before/after toggling composing
state to avoid swallowing consumers' handlers.

Comment on lines 43 to 75
const [isComposing, setIsComposing] = React.useState(false);

const handleCompositionStart = () => setIsComposing(true);

const handleCompositionEnd = (
e: React.CompositionEvent<HTMLInputElement>,
) => {
setIsComposing(false);
const newValue = e.currentTarget.value;
setCurrentValue(newValue);
onChange?.(e as any);
};

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l src/frontend/src/components/ui/input.tsx

Repository: langflow-ai/langflow

Length of output: 108


🏁 Script executed:

cat -n src/frontend/src/components/ui/input.tsx

Repository: langflow-ai/langflow

Length of output: 4087


IME composition handlers double‑call onChange and swallow user onComposition* props

Two confirmed issues in the composition logic:

  1. handleCompositionEnd (line 53) calls onChange?.(e as any) with a CompositionEvent, but React then fires a final onChange (ChangeEvent) when composition commits. Since isComposing is set to false at line 50, handleChange will execute and call onChange again at line 60. This results in onChange being invoked twice per committed IME input.

  2. The InputProps interface does not include onCompositionStart or onCompositionEnd. The handlers at lines 45 and 47–54 are hardcoded and do not forward to any user-supplied composition callbacks from props, effectively swallowing them if a parent component attempts to pass them.

Fix by:

  • Removing the onChange call from handleCompositionEnd (line 53).
  • Adding onCompositionStart and onCompositionEnd to the props destructuring and forwarding them in the composition handlers.
  • Guarding handleChange with both isComposing state and the native e.nativeEvent.isComposing flag for cross-browser safety.

This ensures onChange fires exactly once per committed value and preserves any consumer-supplied composition handlers.

Also applies to: 56–62, 89–91

🤖 Prompt for AI Agents
In src/frontend/src/components/ui/input.tsx around lines 43 to 55 (also apply
same changes to ranges 56–62 and 89–91): the composition handlers currently call
onChange with a CompositionEvent and do not forward user-supplied
onCompositionStart/onCompositionEnd props, causing double onChange and swallowed
callbacks; remove the onChange call from handleCompositionEnd, add
onCompositionStart and onCompositionEnd to the component props destructuring,
forward those props from the internal handlers (call user callbacks if
provided), and guard the main handleChange so it returns early when isComposing
is true OR e.nativeEvent?.isComposing is true to avoid duplicate onChange firing
across browsers.

Copy link
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

🧹 Nitpick comments (1)
src/frontend/src/components/ui/input.tsx (1)

35-41: inputRef is unused; consider removing or wiring it where needed

inputRef is set in mergedRef (Lines 35–41) but never read anywhere in the component. This adds indirection without benefit and slightly complicates the ref logic.

Either:

  • Remove inputRef and just forward ref:
- const inputRef = React.useRef<HTMLInputElement>(null);
-
- const mergedRef = (node: HTMLInputElement) => {
-   inputRef.current = node;
-   if (typeof ref === "function") ref(node);
-   else if (ref) (ref as any).current = node;
- };
+ const mergedRef = (node: HTMLInputElement | null) => {
+   if (typeof ref === "function") ref(node);
+   else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = node;
+ };

or:

  • Actually use inputRef for some behavior (e.g., selection management) so the extra ref has a clear purpose.

Given this is a small UI component, I’d lean toward removing unused state to keep it simple.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63141c1 and d7249ae.

📒 Files selected for processing (1)
  • src/frontend/src/components/ui/input.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{ts,tsx}: Use React 18 with TypeScript for frontend development
Use Zustand for state management

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/**/*.{tsx,jsx,css,scss}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

Use Tailwind CSS for styling

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/components/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/components/**/*.{tsx,jsx}: Use React Flow for flow graph visualization with Node, Edge, Controls, and Background components
Use the cn() utility function for combining Tailwind CSS classes in components
Use TypeScript interfaces for defining component props in React components

Files:

  • src/frontend/src/components/ui/input.tsx
src/frontend/src/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{tsx,jsx}: Implement dark mode support using the useDarkMode hook and dark store
Use Lucide React for icon components in the application

Files:

  • src/frontend/src/components/ui/input.tsx
🧠 Learnings (5)
📚 Learning: 2025-11-24T19:46:45.790Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-11-24T19:46:45.790Z
Learning: Applies to src/frontend/src/components/**/*.{tsx,jsx} : Use TypeScript interfaces for defining component props in React components

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-11-24T19:46:57.920Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/icons.mdc:0-0
Timestamp: 2025-11-24T19:46:57.920Z
Learning: Applies to src/frontend/src/icons/*/index.tsx : Create an `index.tsx` for each icon that exports the icon using `forwardRef` and properly types it as `React.ForwardRefExoticComponent<React.PropsWithChildren<{}> & React.RefAttributes<SVGSVGElement>>`.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-16T11:14:04.200Z
Learnt from: dolfim-ibm
Repo: langflow-ai/langflow PR: 8394
File: src/frontend/src/icons/Docling/index.tsx:4-6
Timestamp: 2025-06-16T11:14:04.200Z
Learning: The Langflow codebase consistently uses `React.PropsWithChildren<{}>` as the prop type for all icon components using forwardRef, rather than `React.SVGProps<SVGSVGElement>`. This is an established pattern across hundreds of icon files in src/frontend/src/icons/.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-23T12:46:52.420Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/icons.mdc:0-0
Timestamp: 2025-06-23T12:46:52.420Z
Learning: Export custom icon components in React using React.forwardRef to ensure proper ref forwarding and compatibility with parent components.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
📚 Learning: 2025-06-23T12:46:42.048Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-06-23T12:46:42.048Z
Learning: Custom React Flow node types should be implemented as memoized components, using Handle components for connection points and supporting optional icons and labels.

Applied to files:

  • src/frontend/src/components/ui/input.tsx
🧬 Code graph analysis (1)
src/frontend/src/components/ui/input.tsx (2)
src/frontend/src/utils/utils.ts (1)
  • cn (38-40)
src/frontend/tailwind.config.mjs (1)
  • className (476-476)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 16/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 12/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 1/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 13/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 11/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 3/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 10/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 8/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 7/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 2/17
  • GitHub Check: Run Frontend Tests / Playwright Tests - Shard 5/17
  • GitHub Check: Test Docker Images / Test docker images
🔇 Additional comments (3)
src/frontend/src/components/ui/input.tsx (3)

95-100: Placeholder visibility tied to currentValue is fine but depends on correct value syncing

The updated placeholder logic (Lines 95–100) using currentValue ? "hidden" : "" is consistent with the “floating placeholder” pattern and the Tailwind classes look aligned with existing styles.

However, its correctness depends on currentValue accurately reflecting the actual input value (see earlier comment on syncing from value / using value={currentValue}). Once that is addressed, this part looks good.


118-119: Adding Input.displayName is a good improvement for debugging

Input.displayName = "Input"; (Lines 118–119) helps with React DevTools readability and is aligned with common forwardRef patterns. No issues here.


5-12: This comment is incorrect—InputProps does not redeclare value, defaultValue, or onChange

Lines 24–27 are function parameter destructuring, not interface property redeclaration. InputProps extends React.InputHTMLAttributes<HTMLInputElement> without redefining these properties. The component's handling of onChange, value, and defaultValue (extracting them and managing internal state with composition event handling) is intentional custom behavior, not a problematic override.

Likely an incorrect or invalid review comment.

Comment on lines 24 to 29
onChange,
value,
defaultValue,
...props
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Controlled vs uncontrolled behavior and value syncing look inconsistent and may break existing usages

Right now the component:

  • Initializes currentValue from defaultValue ?? value (Line 31–33),
  • Uses defaultValue={currentValue} on the <input> (Line 81),
  • Updates currentValue only from handleChange / handleCompositionEnd (Lines 56–62),
  • Never re-syncs currentValue when the value prop changes after mount.

As a result:

  • Existing controlled usages like <Input value={state} onChange={...} /> will no longer behave as controlled: updates to value from the parent are ignored after the first render.
  • The visible text in the input can get out of sync with currentValue and the external value prop, while the placeholder visibility (Lines 95–100) is driven by currentValue, not the actual DOM value.
  • Consumers passing defaultValue also can’t programmatically clear/reset via props.

To keep IME fixes while preserving expected semantics, consider:

  • Using value={currentValue} instead of defaultValue={currentValue}, and
  • Adding an effect that syncs currentValue from the external value prop when it’s defined and not currently composing, e.g.:
- const [currentValue, setCurrentValue] = React.useState(() =>
-   String(defaultValue ?? value ?? ""),
- );
+ const [currentValue, setCurrentValue] = React.useState(() =>
+   String(defaultValue ?? value ?? ""),
+ );
+
+ React.useEffect(() => {
+   if (value !== undefined) {
+     setCurrentValue(String(value));
+   }
+ }, [value]);

and then:

-  <input
-    ...
-    defaultValue={currentValue}
+  <input
+    ...
+    value={currentValue}

You can still gate parent onChange calls during composition (see next comment) without sacrificing controlled behavior.

Also applies to: 31-33, 56-62, 78-82, 95-100

🤖 Prompt for AI Agents
In src/frontend/src/components/ui/input.tsx around lines 24–27, 31–33, 56–62,
78–82 and 95–100: the component currently initializes internal currentValue from
defaultValue ?? value, writes that to defaultValue on the <input>, and only
updates currentValue from internal handlers, so external controlled value
updates are ignored after mount. Change to use value={currentValue} on the input
(not defaultValue), initialize currentValue from value ?? defaultValue, and add
a useEffect that re-syncs currentValue whenever the external value prop changes
(but only when not composing) so controlled usages remain in sync; continue to
gate calling parent onChange during composition but still update internal state
on composition-aware changes.

Comment on lines 43 to 72
const [isComposing, setIsComposing] = React.useState(false);

const handleCompositionStart = () => setIsComposing(true);

const handleCompositionEnd = (
e: React.CompositionEvent<HTMLInputElement>,
) => {
setIsComposing(false);
const newValue = e.currentTarget.value;
setCurrentValue(newValue);
onChange?.(e as any);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/frontend/src/components/ui/input.tsx | head -100

Repository: langflow-ai/langflow

Length of output: 3510


🌐 Web query:

React 18 compositionend onChange event sequence IME input handling best practices

💡 Result:

Short best-practices for handling IME (composition) + onChange in React 18

  1. Understand event sequencing: IME uses compositionstart/compositionupdate/compositionend; browsers differ in whether input/onChange fires during composition — handle composition events instead of assuming onChange means “final” text. [1][2]

  2. Track composition state and ignore intermediate onChange updates:

    • Set a boolean isComposing on compositionstart -> true, compositionend -> false.
    • In onChange, if isComposing === true, skip heavy work (validation, filtering, API calls, state that would reset the input caret) and only store transient value if needed.
    • On compositionend, apply final update/validation using event.target.value. This avoids premature resets/extra renders. [1][3]
  3. Keep inputs consistently controlled (never flip value between undefined/null and string):

    • Initialize value with '' and never set undefined/null; switching controlled↔uncontrolled causes weird behaviors with composition. [4]
  4. Edge cases & platform differences:

    • Some IMEs (mobile keyboards, certain OS IMEs) emit input/onInput before/without composition events; combine checks (composition flag + final input/compositionend) for robust handling.
    • For numeric/filtered inputs, validate on compositionend or debounce validation to avoid discarding composing text. [1][5]
  5. Implementation patterns (concise code sketch)

    • useRef for isComposing; attach onCompositionStart/Update/End and onChange.
    • In onChange: if (isComposingRef.current) { /* optionally cache */ return; } else setState(...)
    • In onCompositionEnd: setState(e.target.value) and run validation/side-effects. [1][3]
  6. Alternatives when composition problems persist:

    • Use uncontrolled input (defaultValue) and read value on blur/submit.
    • Use libraries/components built to handle composition (or small wrappers that buffer changes until compositionend). [6]

References

  • React discussion / issues on composition & controlled inputs (recommendation to use composition events). [1]
  • MDN docs for compositionstart/compositionend/compositionupdate. [2]
  • Recent React issue reporting IME-controlled-input resets in React 18 (example of platform/implementation differences). [3]
  • Controlled vs uncontrolled best-practices (always initialize to ''). [4]
  • Article/notes about cross-IME differences and strategies (debounce, validate on compositionend). [5]
  • Example npm packages / wrappers that buffer on compositionend. [6]

If you want, I can paste a minimal React 18 code example implementing the isComposing pattern (with TypeScript or plain JS).


🏁 Script executed:

rg "onCompositionStart|onCompositionEnd" src/frontend/src/components --type tsx --type ts -A 5 -B 5

Repository: langflow-ai/langflow

Length of output: 91


🌐 Web query:

React 18 double onChange firing IME composition event browser behavior canonical pattern

💡 Result:

Short answer
When typing with an IME the browser emits composition events plus input events (and different browsers/OSes emit them in different orders). React’s onChange is a wrapper around the native input/onInput behavior, so you can see “double” updates (one during composition/update and one when composition finishes). The canonical solution is to track composition state with onCompositionStart/onCompositionEnd and ignore onChange while composing, then apply the final value at compositionend. (developer.mozilla.org)

Minimal example (canonical pattern)

  • maintain an isComposing flag (ref/state)
  • ignore onChange updates while composing; handle final update in onCompositionEnd

Example:
const isComposing = useRef(false);
const [value, setValue] = useState('');

<input
value={value}
onCompositionStart={() => (isComposing.current = true)}
onCompositionEnd={(e) => {
isComposing.current = false;
setValue(e.currentTarget.value); // apply final composed text
}}
onChange={(e) => {
if (!isComposing.current) setValue(e.currentTarget.value);
}}
/>

Notes and gotchas

  • Browser behavior varies (some emit input/keyup after compositionend, some don’t) — testing across Chrome/Safari/Firefox/iOS is needed. (stum.de)
  • Alternatives: use onBeforeInput/onInput carefully, or use uncontrolled inputs and read value on blur/submit if re-renders are the issue. (reactspectrum.blob.core.windows.net)

Sources

  1. MDN: compositionstart / compositionend / CompositionEvent (composition event docs). [MDN]. (developer.mozilla.org)
  2. React events (composition events listed; onChange maps to input behavior). [React docs]. (gaearon.github.io)
  3. Practical notes on cross-browser IME differences (examples & quirks). (stum.de)

If you want, I can paste a ready-to-drop hook (useComposition-aware onChange) you can copy into your app.

Citations:


🏁 Script executed:

rg "onCompositionStart|onCompositionEnd" src/frontend/src/components -A 5 -B 5 -t ts

Repository: langflow-ai/langflow

Length of output: 885


Remove manual onChange call from handleCompositionEnd to prevent double-firing

The composition event handler fires onChange manually (line 53), but React will emit a natural onChange event after compositionend. This results in two onChange invocations for the final committed text, which can trigger duplicate validation, API calls, or other side-effects.

Per React 18 canonical patterns for IME handling: update state in onCompositionEnd but let the browser's natural onChange propagate through handleChange (which now correctly checks !isComposing). Remove the manual onChange?.(e as any) call at line 53.

Additionally, onCompositionStart and onCompositionEnd are not destructured from props (line 24–27), so if a caller passes these handlers via props, they will override your IME logic. Consider destructuring and merging them:

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({
    className,
    inputClassName,
    icon = "",
    endIcon = "",
    endIconClassName = "",
    type,
    placeholder,
    onChange,
+   onCompositionStart: userOnCompositionStart,
+   onCompositionEnd: userOnCompositionEnd,
    value,
    defaultValue,
    ...props
  }, ref) => {
    ...
-   const handleCompositionStart = () => setIsComposing(true);
+   const handleCompositionStart: React.CompositionEventHandler<HTMLInputElement> = (e) => {
+     setIsComposing(true);
+     userOnCompositionStart?.(e);
+   };
    
    const handleCompositionEnd = (
      e: React.CompositionEvent<HTMLInputElement>,
    ) => {
      setIsComposing(false);
      const newValue = e.currentTarget.value;
      setCurrentValue(newValue);
-     onChange?.(e as any);
+     userOnCompositionEnd?.(e);
    };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/frontend/src/components/ui/input.tsx around lines 43 to 54 (and note
props area around lines 24–27), remove the manual call to onChange in
handleCompositionEnd so the browser/React natural onChange after compositionend
is the only change event; keep setting isComposing to false and updating local
state there. Also destructure onCompositionStart and onCompositionEnd from props
and, in your internal handlers, invoke the caller-provided handlers (if any)
after performing your isComposing state updates so external handlers can run
without overriding the IME logic.

@748561321 748561321 force-pushed the fix/chinese-ime-input branch from d7249ae to 2db2176 Compare December 15, 2025 03:23
@748561321 748561321 changed the title Fix/chinese ime input fix(input): handle Chinese IME composition correctly Dec 15, 2025
@github-actions github-actions bot added the bug Something isn't working label Dec 15, 2025
@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 15, 2025
@748561321
Copy link
Author

This PR only touches the Input component (IME / composition handling) and does not modify canvas interactions, flow logic, selectors, or code paths covered by Playwright component tests.

The failing job appears to be:
Frontend Tests / Playwright Tests – Shard 8/17 (--grep="@components"),
which is timing out before any assertion failure.

I retriggered CI with an empty commit, and the same timeout still occurred.
Based on this, it might be related to test sharding or CI environment instability rather than a regression introduced here, though I could be mistaken.

If it makes sense from your side, could you please try rerunning this job?
Happy to investigate further if you think the change could be related.

@github-actions github-actions bot added bug Something isn't working and removed bug Something isn't working labels Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working community Pull Request from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant