Skip to content

Conversation

@ZainabTravadi
Copy link

Summary

This PR implements the feature requested in issue #2177 by adding reliable autofocus behavior to the chat message input field. When the page loads or the chat UI mounts, users can immediately begin typing without clicking the input box—improving UX and aligning with expected chat application behavior.

What was happening before

  • The chat textarea (MessageInput.tsx) did not consistently receive focus on initial load.
  • React re-renders triggered by conversation fetches or state updates caused the textarea to lose focus.
  • The existing autoFocus prop was defined but never applied reliably to the actual textarea element.

What this PR changes

  • Applies autofocus to the main chat <textarea> using a delayed focus mechanism inside useEffect to ensure focus persists across initial renders.
  • Supports the existing autoFocus prop (default: true).
  • Adds stable autofocus handling to the custom <Input> component as well (via ref + effect).
  • Ensures users can begin typing immediately after loading the chat page.

Why this approach

React components inside DocsGPT’s chat UI re-render during initialization (loading conversations, state hydration). Immediate focusing inside useEffect can fire too early. Using a delayed focus resolves the race condition and ensures consistent user experience.

Testing

  • Verified that typing works immediately after page load with zero user interaction.
  • Cursor blinks in the message textarea on refresh.
  • Autofocus persists even during UI re-renders.
  • No backend or breaking changes introduced.

Result

A smoother, more intuitive chat experience: users can start typing right away, without needing to click the input field.

Copilot AI review requested due to automatic review settings December 5, 2025 13:05
@vercel
Copy link

vercel bot commented Dec 5, 2025

@ZainabTravadi is attempting to deploy a commit to the Arc53 Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds autofocus functionality to the chat message textarea and custom Input component to improve user experience by allowing immediate typing without clicking. The implementation uses React useEffect hooks with delayed focus mechanisms to handle re-render timing issues.

Key Changes:

  • Replaces simple focus call with an interval-based focus mechanism in MessageInput component
  • Adds useEffect-based autofocus to the Input component while keeping native autoFocus prop
  • Removes the initial handleInput() call from MessageInput's mount effect

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
frontend/src/components/MessageInput.tsx Implements interval-based autofocus with 50ms polling to ensure textarea receives focus after re-renders
frontend/src/components/Input.tsx Adds useEffect hook to programmatically focus input element when isAutoFocused prop is true

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 515 to 527
const interval = setInterval(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();

// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);

return () => clearInterval(interval);
}, [autoFocus]);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The interval created for autofocus is never properly cleaned up. The interval will continue running indefinitely if autoFocus is false or if inputRef.current is null, causing a memory leak. Additionally, if the component unmounts before focus succeeds, the interval will continue attempting to focus a non-existent element.

To fix this, ensure the interval is always cleared in the cleanup function, even if focus never succeeds:

useEffect(() => { if (!autoFocus) return; const interval = setInterval(() => { if (inputRef.current) { inputRef.current.focus(); if (document.activeElement === inputRef.current) { clearInterval(interval); } } }, 50); return () => clearInterval(interval); }, [autoFocus]);
Suggested change
const interval = setInterval(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();
// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);
return () => clearInterval(interval);
}, [autoFocus]);
if (!autoFocus) return;
const interval = setInterval(() => {
if (inputRef.current) {
inputRef.current.focus();
// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);
return () => clearInterval(interval);
}, [autoFocus]);
Copilot uses AI. Check for mistakes.
Comment on lines 515 to 529
const interval = setInterval(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();

// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);

return () => clearInterval(interval);
}, [autoFocus]);


Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The aggressive 50ms interval will attempt to focus the input ~20 times per second until successful. This approach is inefficient and can impact performance, especially on lower-end devices. Consider using a less aggressive interval (e.g., 100-200ms) or a more elegant solution like requestAnimationFrame with a timeout, or simply a single delayed focus with setTimeout.

Suggested change
const interval = setInterval(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();
// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);
return () => clearInterval(interval);
}, [autoFocus]);
let timeout: ReturnType<typeof setTimeout> | null = null;
if (autoFocus) {
timeout = setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, 100);
}
return () => {
if (timeout) clearTimeout(timeout);
};
}, [autoFocus]);
Copilot uses AI. Check for mistakes.
Comment on lines 514 to 527
useEffect(() => {
if (autoFocus) inputRef.current?.focus();
handleInput();
}, []);
const interval = setInterval(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();

// Check if focus succeeded
if (document.activeElement === inputRef.current) {
clearInterval(interval);
}
}
}, 50);

return () => clearInterval(interval);
}, [autoFocus]);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The removal of the handleInput() call from the initial useEffect may cause layout issues on component mount. The handleInput function adjusts the textarea height based on content and window width. Without calling it on mount, the textarea may not have the correct initial height. Consider whether this was an intentional change or an oversight.

Copilot uses AI. Check for mistakes.
id={id}
name={name}
autoFocus={isAutoFocused}
autoFocus={isAutoFocused}
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Trailing whitespace detected after autoFocus={isAutoFocused}.

Suggested change
autoFocus={isAutoFocused}
autoFocus={isAutoFocused}
Copilot uses AI. Check for mistakes.
Comment on lines +43 to +49
// 🔥 Auto-focus logic (the real fix for the issue)
useEffect(() => {
if (isAutoFocused && inputRef.current) {
inputRef.current.focus();
}
}, [isAutoFocused]);

Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The useEffect hook for autofocus is redundant since the native autoFocus prop is already set on line 60. The native autoFocus attribute should be sufficient for most use cases. If there's a specific reason the native behavior doesn't work (e.g., React re-renders), consider removing the native prop to avoid confusion, or add a comment explaining why both are needed. The emoji in the comment is also unprofessional for production code.

Suggested change
// 🔥 Auto-focus logic (the real fix for the issue)
useEffect(() => {
if (isAutoFocused && inputRef.current) {
inputRef.current.focus();
}
}, [isAutoFocused]);
Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1 participant