Skip to content

Conversation

@ngoiyaeric
Copy link
Collaborator

@ngoiyaeric ngoiyaeric commented Sep 29, 2025

User description

This commit implements the following changes:

  1. The example prompts on the empty chat screen are now hidden by default and only appear when the user focuses on the chat input.
  2. Clicking on an example prompt now submits it as a query to the AI, and the response is displayed in the chat.

These changes were accomplished by:

  • Adding an onFocus handler to the ChatPanel component to detect when the user interacts with the input field.
  • Introducing an isInputFocused state in the Chat component to control the visibility of the example prompts in the EmptyScreen component.
  • Updating the submitMessage function in the Chat component to use the submit action from ai/rsc, which handles the entire chat flow.

PR Type

Enhancement


Description

  • Make example prompts conditionally visible on input focus

  • Enable interactive example prompt submission with AI responses

  • Update chat flow to handle direct message submission

  • Add focus state management for improved UX


Diagram Walkthrough

flowchart LR
  A["User focuses input"] --> B["Show example prompts"]
  B --> C["User clicks example"]
  C --> D["Submit to AI"]
  D --> E["Display response"]
Loading

File Walkthrough

Relevant files
Enhancement
chat-panel.tsx
Add focus event handling to chat input                                     

components/chat-panel.tsx

  • Add onFocus prop to interface and component
  • Wire up onFocus handler to textarea input
  • Update component props destructuring
+8/-1     
chat.tsx
Implement interactive message submission flow                       

components/chat.tsx

  • Add isInputFocused state for controlling prompt visibility
  • Import useActions, nanoid, and UserMessage for message handling
  • Update submitMessage to directly submit and display AI responses
  • Pass onFocus handler and isInputFocused to child components
+55/-14 
empty-screen.tsx
Add conditional visibility for example prompts                     

components/empty-screen.tsx

  • Add isInputFocused prop to control example prompt visibility
  • Conditionally render example messages based on focus state
  • Update component interface and prop destructuring
+23/-20 

Summary by CodeRabbit

  • New Features
    • Chat input focus now triggers contextual updates, enabling smoother interactions across views.
    • Example prompts are shown only when the input is focused, reducing visual clutter.
    • Messages send-and-reply flow improved: user messages appear instantly and AI responses follow seamlessly.
    • Consistent input focus handling across mobile and desktop for a unified experience.

This commit implements the following changes:

1.  The example prompts on the empty chat screen are now hidden by default and only appear when the user focuses on the chat input.
2.  Clicking on an example prompt now submits it as a query to the AI, and the response is displayed in the chat.

These changes were accomplished by:

-   Adding an `onFocus` handler to the `ChatPanel` component to detect when the user interacts with the input field.
-   Introducing an `isInputFocused` state in the `Chat` component to control the visibility of the example prompts in the `EmptyScreen` component.
-   Updating the `submitMessage` function in the `Chat` component to use the `submit` action from `ai/rsc`, which handles the entire chat flow.
@vercel
Copy link

vercel bot commented Sep 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
qcx Ready Ready Preview Comment Sep 29, 2025 1:52pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 29, 2025

Walkthrough

Adds input focus handling: ChatPanel accepts an onFocus prop and forwards it to the Textarea; chat.tsx tracks isInputFocused state and passes it to EmptyScreen, which now conditionally renders example messages based on focus. Also augments chat message flow (nanoid ids, useActions().submit) and minor import/structural changes.

Changes

Cohort / File(s) Summary
Focus props & wiring
components/chat-panel.tsx, components/empty-screen.tsx, components/chat.tsx
Added onFocus: () => void to ChatPanelProps and forwarded to the Textarea; introduced isInputFocused: boolean in EmptyScreen props; chat.tsx tracks focus state and passes onFocus/isInputFocused between components.
Message flow & state
components/chat.tsx
Integrated useActions().submit, appended user messages with nanoid ids and appended AI responses to UI state; added UserMessage usage and adjusted imports/structure for mobile/desktop branches.
Minor typings & imports
components/empty-screen.tsx, components/chat.tsx
Updated prop type annotations and reformatted imports/quotes; conditional rendering in EmptyScreen based on isInputFocused.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ChatPanel
  participant Chat
  participant EmptyScreen
  participant Actions

  Note over ChatPanel,Chat: Focus flow
  User->>ChatPanel: focus input
  ChatPanel-->>Chat: onFocus()
  Chat->>Chat: set isInputFocused = true
  Chat-->>EmptyScreen: isInputFocused=true
  Note right of EmptyScreen: example messages render only when focused

  Note over ChatPanel,Actions: Submit flow
  User->>ChatPanel: submit(message)
  ChatPanel-->>Chat: onSubmit(message)
  Chat->>Chat: append UserMessage (id=nanoid)
  Chat->>Actions: submit(message)
  Actions-->>Chat: AI response
  Chat->>Chat: append AI message
  Chat-->>User: updated message list
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

A twitch of whiskers, tap on keys,
I wake the input with a breeze.
nanoid crumbs mark every chat,
The bot replies — imagine that!
Examples hop when focus calls,
Soft rabbit hops through code-lined halls. 🥕

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly conveys the primary enhancement—making example prompts both interactive and visible only under specific conditions—directly reflecting the pull request’s core objective without extraneous detail.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/interactive-prompts

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39525b1 and ed628eb.

📒 Files selected for processing (1)
  • components/chat.tsx (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
components/chat.tsx (5)
components/chat-panel.tsx (1)
  • ChatPanel (21-184)
components/empty-screen.tsx (1)
  • EmptyScreen (27-62)
app/actions.tsx (1)
  • UIState (268-273)
components/user-message.tsx (1)
  • UserMessage (10-22)
components/map/map-data-context.tsx (1)
  • MapDataProvider (26-34)
🔇 Additional comments (2)
components/chat.tsx (2)

102-156: Build FormData once, reuse it, and drop the unsafe as any.

Both mobile and desktop prompt handlers optimistically append the user bubble, then call submit(message) and push responseMessage as any. That bypasses ai/rsc’s expected FormData contract and masks real type issues, so clicking an example prompt is likely to explode at runtime.

Refactor to share a single handleSubmitExample helper that mirrors ChatPanel’s flow: create FormData, call await submit(formData), and append the typed response—no as any. Add basic error handling so a rejected submit doesn’t strand the UI. Apply the same helper in both branches to kill the duplication. Based on past feedback.

+  const handleSubmitExample = async (message: string) => {
+    setMessages(current => [
+      ...current,
+      { id: nanoid(), component: <UserMessage message={message} /> }
+    ])
+    const formData = new FormData()
+    formData.set('input', message)
+    try {
+      const responseMessage = await submit(formData)
+      setMessages(current => [...current, responseMessage])
+    } catch (err) {
+      console.error('Failed to submit example prompt', err)
+      setMessages(current => current.filter(entry => entry.component !== message))
+    }
+  }
...
-              submitMessage={async (message: string) => {
-                setMessages((currentMessages: UIState) => [
-                  ...currentMessages,
-                  {
-                    id: nanoid(),
-                    component: <UserMessage message={message} />
-                  }
-                ])
-                const responseMessage = await submit(message)
-                setMessages((currentMessages: UIState) => [
-                  ...currentMessages,
-                  responseMessage as any
-                ])
-              }}
+              submitMessage={handleSubmitExample}

91-139: Reset isInputFocused on blur so prompts collapse correctly.

Once focus is gained you never flip isInputFocused back to false, so the “hidden-by-default” prompts stick around after the input loses focus—contradicting the product requirement. Wire onBlur={() => setIsInputFocused(false)} and ensure ChatPanel actually forwards it to the textarea. Applies to both layouts. Based on past feedback.

             <ChatPanel
               messages={messages}
               input={input}
               setInput={setInput}
-              onFocus={() => setIsInputFocused(true)}
+              onFocus={() => setIsInputFocused(true)}
+              onBlur={() => setIsInputFocused(false)}
             />

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

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@qodo-merge-pro
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Prop Handling

The new required onFocus prop is added without a default. Ensure all usages of ChatPanel across the codebase pass onFocus to avoid runtime errors and type mismatches.

export function ChatPanel({
  messages,
  input,
  setInput,
  onFocus
}: ChatPanelProps) {
State Consistency

isInputFocused is only ever set to true on focus and never reset on blur; example prompts may remain visible indefinitely. Consider adding an onBlur or visibility-clearing logic.

  <ChatPanel
    messages={messages}
    input={input}
    setInput={setInput}
    onFocus={() => setIsInputFocused(true)}
  />
</div>
<div className="mobile-chat-messages-area">
  {showEmptyScreen ? (
Message Flow

submitMessage appends the user message and then awaits submit(message) and appends the response, but there is no error handling or loading state; failed submissions may leave UI in inconsistent state.

isInputFocused={isInputFocused}
submitMessage={async (message: string) => {
  setMessages(currentMessages => [
    ...currentMessages,
    {
      id: nanoid(),
      component: <UserMessage message={message} />
    }
  ])
  const responseMessage = await submit(message)
  setMessages(currentMessages => [
    ...currentMessages,
    responseMessage as any
  ])
}}

@qodo-merge-pro
Copy link
Contributor

qodo-merge-pro bot commented Sep 29, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add onBlur handler to ChatPanel

Add an onBlur handler to the ChatPanel component to set isInputFocused to false
when the input loses focus. This will correctly hide the example prompts.

components/chat.tsx [90-95]

 <ChatPanel
   messages={messages}
   input={input}
   setInput={setInput}
   onFocus={() => setIsInputFocused(true)}
+  onBlur={() => setIsInputFocused(false)}
 />
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a UI bug where the focus state is not reset, and provides a clear, correct fix. This improves the user experience by ensuring prompts are only shown when relevant.

Medium
General
Extract duplicated logic into function

Extract the duplicated message submission logic from the mobile and desktop
layouts into a single, reusable function to improve maintainability.

components/chat.tsx [99-115]

 <EmptyScreen
   isInputFocused={isInputFocused}
-  submitMessage={async (message: string) => {
-    setMessages(currentMessages => [
-      ...currentMessages,
-      {
-        id: nanoid(),
-        component: <UserMessage message={message} />
-      }
-    ])
-
-    const responseMessage = await submit(message)
-    setMessages(currentMessages => [
-      ...currentMessages,
-      responseMessage as any
-    ])
-  }}
+  submitMessage={handleSubmitMessage}
 />
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out duplicated logic and proposes a valid refactoring to a reusable function, which improves code maintainability and readability.

Low
  • Update

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

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

  • Example prompts remain visible after the input loses focus; no onBlur is implemented to reset isInputFocused (introduces UX inconsistency).
  • onFocus was added as a required prop to ChatPanel, which risks breaking other call sites; make it optional and add onBlur for symmetry.
  • The example-prompt submission logic is duplicated across mobile/desktop; extract a single handler and add basic error handling.
  • Avoid as any when appending the AI response; type useActions/useUIState to match AI and remove the cast.
Additional notes (1)
  • Style | components/chat.tsx:127-129
    Minor cleanup: remove the stray {' '} and inline comment noise around the provider. These add noise without value.
Summary of changes
  • ChatPanel: added an onFocus prop and wired it to the textarea’s onFocus event.
  • Chat: introduced isInputFocused state, passed onFocus to ChatPanel, gated EmptyScreen example prompts with isInputFocused, and implemented example-prompt submission by appending a UserMessage then awaiting submit(message) from ai/rsc. Added nanoid and UserMessage usage. Duplicated this logic for both mobile and desktop layouts.
  • EmptyScreen: now receives isInputFocused and only renders example prompts when the input is focused. Clicking a prompt calls the provided submitMessage (now used to kick off the chat flow).

Comment on lines +130 to 131
onFocus={onFocus}
onKeyDown={e => {

Choose a reason for hiding this comment

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

onFocus is introduced as a required prop, which creates a silent breaking change for any other usages of ChatPanel that don’t pass it. Also, you only ever set focus state to true and never reset it on blur, so the example prompts will remain visible after the input loses focus. Make the prop optional to preserve backwards-compatibility and also add an onBlur to correctly hide example prompts when the input is no longer focused.

Suggestion

Make the focus handlers optional and add an onBlur prop, then pass it through to the textarea and wire it up in callers.

Example changes:

  • In components/chat-panel.tsx

    interface ChatPanelProps {
    messages: UIState
    input: string
    setInput: (value: string) => void
    onFocus?: () => void
    onBlur?: () => void
    }

    export function ChatPanel({ messages, input, setInput, onFocus, onBlur }: ChatPanelProps) {
    // ...

    <Textarea // ... onFocus={onFocus} onBlur={onBlur} // ... /> }
  • In components/chat.tsx (both usages):

    <ChatPanel
    messages={messages}
    input={input}
    setInput={setInput}
    onFocus={() => setIsInputFocused(true)}
    onBlur={() => setIsInputFocused(false)}
    />

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion

Comment on lines 101 to 113
submitMessage={async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const responseMessage = await submit(message)
setMessages(currentMessages => [
...currentMessages,
responseMessage as any
])

Choose a reason for hiding this comment

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

The example-prompt submission logic is duplicated in both mobile and desktop branches, making the component harder to maintain. There’s also no error handling if submit(message) throws, which could leave the UI in a confusing state. Extract this into a single handleSubmitExample function and reuse it, with basic try/catch for resilience.

Suggestion

Refactor to a single handler and reuse it in both places, adding basic error handling:

const handleSubmitExample = async (message: string) => {
setMessages(curr => [
...curr,
{ id: nanoid(), component: }
])
try {
const responseMessage = await submit(message)
setMessages(curr => [
...curr,
responseMessage
])
} catch (err) {
console.error('Failed to submit example message', err)
// Optionally append a simple error UI message here
}
}

Then pass:

in both mobile and desktop blocks.

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this refactor

Comment on lines 110 to 113
setMessages(currentMessages => [
...currentMessages,
responseMessage as any
])

Choose a reason for hiding this comment

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

Avoid as any when appending the AI response—it undermines type-safety and invites runtime errors. You can usually type submit by providing the AI generic to useActions (and to useUIState) which removes the need for casting and ensures that the returned UI message conforms to your UIState item type.

Suggestion

Type your hooks to align with the AI actions and UI state, and remove the as any:

// near the top of the component
const [messages, setMessages] = useUIState()
const { submit } = useActions()

// later
const responseMessage = await submit(message)
setMessages(curr => [...curr, responseMessage])

Reply with "@CharlieHelps yes please" if you'd like me to add a commit that applies these type fixes

Comment on lines +94 to +95
onFocus={() => setIsInputFocused(true)}
/>

Choose a reason for hiding this comment

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

You set onFocus={() => setIsInputFocused(true)} but never reset it on blur, so once a user focuses the input, example prompts remain visible even after the input loses focus. Add an onBlur to restore isInputFocused to false.

Suggestion

Pass an onBlur handler to ChatPanel to keep isInputFocused accurate:

<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
onBlur={() => setIsInputFocused(false)}
/>

Implement onBlur in ChatPanel as shown in the earlier suggestion.

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with these changes

Comment on lines +40 to +57
{isInputFocused &&
exampleMessages.map(item => {
const Icon = item.icon
return (
<Button
key={item.message} // Use a unique property as the key.
variant="link"
className="h-auto p-0 text-base flex items-center"
name={item.message}
onClick={async () => {
submitMessage(item.message)
}}
>
<Icon size={16} className="mr-2 text-muted-foreground" />
{item.heading}
</Button>
)
})}

Choose a reason for hiding this comment

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

The visibility of example prompts is tied to isInputFocused, which is set to true on input focus but never reset to false on blur. This means the prompts can remain visible when the input is not focused. Once you add the onBlur logic in ChatPanel and wire it in Chat, this will behave as intended.

@charliecreates charliecreates bot removed the request for review from CharlieHelps September 29, 2025 13:48
This commit implements the following changes:

1.  The example prompts on the empty chat screen are now hidden by default and only appear when the user focuses on the chat input.
2.  Clicking on an example prompt now submits it as a query to the AI, and the response is displayed in the chat.

These changes were accomplished by:

-   Adding an `onFocus` handler to the `ChatPanel` component to detect when the user interacts with the input field.
-   Introducing an `isInputFocused` state in the `Chat` component to control the visibility of the example prompts in the `EmptyScreen` component.
-   Updating the `submitMessage` function in the `Chat` component to use the `submit` action from `ai/rsc`, which handles the entire chat flow.
-   Fixing a TypeScript error by explicitly typing the `currentMessages` parameter in the `setMessages` function.
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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
components/chat-panel.tsx (1)

44-57: Clear input after submit to match chat UX and prevent double-send confusion.

After queuing the user message and constructing FormData, clear the controlled input. Optional: disable submit while awaiting.

   const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
     e.preventDefault()
     setMessages(currentMessages => [
@@
     const formData = new FormData(e.currentTarget)
     // Removed mcp argument from submit call
     const responseMessage = await submit(formData)
     setMessages(currentMessages => [...currentMessages, responseMessage as any])
+    setInput('')
   }
components/chat.tsx (2)

68-77: Context order bug: useMapData is called before MapDataProvider.

Hooks consuming context must be inside the provider subtree. Currently, useMapData runs at the top of Chat, outside the provider used later in both mobile/desktop returns. This will read undefined/default context and can throw.

Introduce a child component that syncs the drawing context and place it inside MapDataProvider; remove the top-level useMapData call/effect.

-  // Get mapData to access drawnFeatures
-  const { mapData } = useMapData();
-
-  // useEffect to call the server action when drawnFeatures changes
-  useEffect(() => {
-    if (id && mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
-      console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures);
-      updateDrawingContext(id, mapData.drawnFeatures);
-    }
-  }, [id, mapData.drawnFeatures]);
+  // Move drawing sync inside provider subtree
+  function DrawingContextSync({ chatId }: { chatId?: string }) {
+    const { mapData } = useMapData()
+    useEffect(() => {
+      if (chatId && mapData.drawnFeatures?.length) {
+        console.log('Chat.tsx: drawnFeatures changed, calling updateDrawingContext', mapData.drawnFeatures)
+        updateDrawingContext(chatId, mapData.drawnFeatures)
+      }
+    }, [chatId, mapData.drawnFeatures])
+    return null
+  }

Then render inside each MapDataProvider (see next comments).


127-170: Apply the same onBlur wiring and move drawing sync inside the provider (desktop).

  • Add onBlur to ChatPanel.
  • Mount DrawingContextSync inside MapDataProvider.
-  return (
-    <MapDataProvider>
-      {' '}
-      {/* Add Provider */}
+  return (
+    <MapDataProvider>
+      {/* Add Provider */}
+      <DrawingContextSync chatId={id} />
       <div className="flex justify-start items-start">
@@
           <ChatPanel
             messages={messages}
             input={input}
             setInput={setInput}
-            onFocus={() => setIsInputFocused(true)}
+            onFocus={() => setIsInputFocused(true)}
+            onBlur={() => setIsInputFocused(false)}
           />
@@
             <EmptyScreen
               isInputFocused={isInputFocused}
               submitMessage={async (message: string) => {
                 setMessages(currentMessages => [
                   ...currentMessages,
                   {
                     id: nanoid(),
                     component: <UserMessage message={message} />
                   }
                 ])
-
-                const responseMessage = await submit(message)
+                const fd = new FormData()
+                fd.set('input', message)
+                const responseMessage = await submit(fd)
                 setMessages(currentMessages => [
                   ...currentMessages,
                   responseMessage as any
                 ])
               }}
             />
@@
     </MapDataProvider>
   )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f88d3ce and 39525b1.

📒 Files selected for processing (3)
  • components/chat-panel.tsx (2 hunks)
  • components/chat.tsx (5 hunks)
  • components/empty-screen.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
components/chat-panel.tsx (1)
components/ui/textarea.tsx (2)
  • TextareaProps (5-6)
  • props (9-20)
components/chat.tsx (4)
components/chat-panel.tsx (1)
  • ChatPanel (21-184)
components/empty-screen.tsx (1)
  • EmptyScreen (27-62)
components/user-message.tsx (1)
  • UserMessage (10-22)
components/map/map-data-context.tsx (1)
  • MapDataProvider (26-34)

Comment on lines +18 to 19
onFocus: () => void
}
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

Missing onBlur wiring means prompts never hide after focus leaves.

You added onFocus but not onBlur, so isInputFocused becomes sticky. Expose onBlur in props and forward it to Textarea so EmptyScreen hides when focus is lost.

 interface ChatPanelProps {
   messages: UIState
   input: string
   setInput: (value: string) => void
-  onFocus: () => void
+  onFocus: () => void
+  onBlur?: () => void
 }

 export function ChatPanel({
   messages,
   input,
   setInput,
-  onFocus
+  onFocus,
+  onBlur
 }: ChatPanelProps) {
@@
           <Textarea
             ref={inputRef}
             name="input"
@@
             onChange={e => {
               setInput(e.target.value)
             }}
             onFocus={onFocus}
+            onBlur={onBlur}

Also applies to: 21-26, 112-131

🤖 Prompt for AI Agents
In components/chat-panel.tsx around lines 18-19 (also apply fixes at 21-26 and
112-131): the props type exposes only onFocus so the component never
receives/forwards onBlur, causing isInputFocused to remain true and the
EmptyScreen to stay visible; add onBlur?: () => void to the component props,
accept onBlur in the component parameter list, and pass it through to the
Textarea (or input) element alongside onFocus so focus loss toggles state
correctly and EmptyScreen hides when focus leaves.

Comment on lines +25 to 28
const [messages, setMessages] = useUIState()
const [aiState] = useAIState()
const { submit } = useActions()
const [isMobile, setIsMobile] = useState(false)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Type UI state for consistency.

Optional: mirror ChatPanel’s usage and specify the AI UI state type parameter for useUIState to keep types aligned.

-  const [messages, setMessages] = useUIState()
+  const [messages, setMessages] = useUIState<typeof import('@/app/actions').AI>()
🤖 Prompt for AI Agents
In components/chat.tsx around lines 25 to 28, the UI state hooks are used
without explicit types which can cause inconsistent typings compared to
ChatPanel; update the hook calls to use the same generic type parameters as
ChatPanel (e.g., provide the appropriate Chat UI state type to useUIState and
the matching AI UI state type to useAIState), import those types if needed, and
update any dependent variables (messages, setMessages, aiState) to reflect the
typed generics so the component’s state shapes match ChatPanel’s usage.

Comment on lines +90 to +95
<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Pair onFocus with onBlur to collapse prompts when input loses focus (mobile).

Wire onBlur to reset state.

             <ChatPanel
               messages={messages}
               input={input}
               setInput={setInput}
-              onFocus={() => setIsInputFocused(true)}
+              onFocus={() => setIsInputFocused(true)}
+              onBlur={() => setIsInputFocused(false)}
             />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
/>
<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
onBlur={() => setIsInputFocused(false)}
/>
🤖 Prompt for AI Agents
In components/chat.tsx around lines 90 to 95, the ChatPanel is wired with an
onFocus handler but lacks a paired onBlur to reset focus state; add an onBlur
prop that calls setIsInputFocused(false) (or a small handler that does this) so
that when the input loses focus on mobile the prompt collapse logic runs; ensure
the ChatPanel accepts and forwards the onBlur prop to the actual input element
and that setIsInputFocused is in scope.

Comment on lines 99 to 114
<EmptyScreen
submitMessage={message => {
setInput(message)
isInputFocused={isInputFocused}
submitMessage={async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const responseMessage = await submit(message)
setMessages(currentMessages => [
...currentMessages,
responseMessage 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.

🧹 Nitpick | 🔵 Trivial

Unify submit() usage: pass FormData (keeps parity with ChatPanel and typical ai/rsc actions).

Current code calls submit(message) while ChatPanel uses submit(formData). Unless submit is overloaded, this may break. Prefer FormData to avoid type mismatch.

               submitMessage={async (message: string) => {
                 setMessages(currentMessages => [
                   ...currentMessages,
                   {
                     id: nanoid(),
                     component: <UserMessage message={message} />
                   }
                 ])
-                const responseMessage = await submit(message)
+                const fd = new FormData()
+                fd.set('input', message)
+                const responseMessage = await submit(fd)
                 setMessages(currentMessages => [
                   ...currentMessages,
                   responseMessage as any
                 ])
               }}

If submit(message) is intentionally supported, please confirm and ignore this change.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<EmptyScreen
submitMessage={message => {
setInput(message)
isInputFocused={isInputFocused}
submitMessage={async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const responseMessage = await submit(message)
setMessages(currentMessages => [
...currentMessages,
responseMessage as any
])
}}
<EmptyScreen
isInputFocused={isInputFocused}
submitMessage={async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const fd = new FormData()
fd.set('input', message)
const responseMessage = await submit(fd)
setMessages(currentMessages => [
...currentMessages,
responseMessage as any
])
}}
🤖 Prompt for AI Agents
In components/chat.tsx around lines 99 to 114, the code calls submit(message)
while other callers (e.g., ChatPanel) pass a FormData; to unify behavior change
the submit invocation to build a FormData with the message (e.g., const form =
new FormData(); form.append('message', message)) and call submit(form) instead,
update any type annotations if submit expects FormData, and keep the existing
optimistic UI flow (append the user message before awaiting submit and then
append the response after await).

Comment on lines +40 to +57
{isInputFocused &&
exampleMessages.map(item => {
const Icon = item.icon
return (
<Button
key={item.message} // Use a unique property as the key.
variant="link"
className="h-auto p-0 text-base flex items-center"
name={item.message}
onClick={async () => {
submitMessage(item.message)
}}
>
<Icon size={16} className="mr-2 text-muted-foreground" />
{item.heading}
</Button>
)
})}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Prevent accidental form submission and drop unnecessary async.

  • Buttons default to submit in form contexts; be explicit with type="button".
  • The onClick handler is marked async but doesn’t await; remove async for clarity.
-                <Button
+                <Button
                   key={item.message} // Use a unique property as the key.
                   variant="link"
                   className="h-auto p-0 text-base flex items-center"
                   name={item.message}
-                  onClick={async () => {
+                  type="button"
+                  onClick={() => {
                     submitMessage(item.message)
                   }}
                 >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{isInputFocused &&
exampleMessages.map(item => {
const Icon = item.icon
return (
<Button
key={item.message} // Use a unique property as the key.
variant="link"
className="h-auto p-0 text-base flex items-center"
name={item.message}
onClick={async () => {
submitMessage(item.message)
}}
>
<Icon size={16} className="mr-2 text-muted-foreground" />
{item.heading}
</Button>
)
})}
{isInputFocused &&
exampleMessages.map(item => {
const Icon = item.icon
return (
<Button
key={item.message} // Use a unique property as the key.
variant="link"
className="h-auto p-0 text-base flex items-center"
name={item.message}
type="button"
onClick={() => {
submitMessage(item.message)
}}
>
<Icon size={16} className="mr-2 text-muted-foreground" />
{item.heading}
</Button>
)
})}
🤖 Prompt for AI Agents
In components/empty-screen.tsx around lines 40 to 57, the mapped Button elements
risk submitting the parent form because their type is not specified and the
onClick handlers are unnecessarily marked async; change each Button to include
type="button" to prevent accidental form submission, and remove the async
keyword from the onClick handler (call submitMessage(item.message) directly) to
avoid an unused async function and clarify intent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants