Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env

This file was deleted.

9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,22 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
dev.log

# local env files
.env
.env*.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# Jules's scratchpad
jules-scratch/
12 changes: 11 additions & 1 deletion components/chat-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ interface ChatPanelProps {
messages: UIState
input: string
setInput: (value: string) => void
onFocus?: () => void
onBlur?: () => void
}

export function ChatPanel({ messages, input, setInput }: ChatPanelProps) {
export function ChatPanel({
messages,
input,
setInput,
onFocus,
onBlur
}: ChatPanelProps) {
const [, setMessages] = useUIState<typeof AI>()
const { submit, clearChat } = useActions()
// Removed mcp instance as it's no longer passed to submit
Expand Down Expand Up @@ -118,6 +126,8 @@ export function ChatPanel({ messages, input, setInput }: ChatPanelProps) {
? 'mobile-chat-input input bg-background' // Use mobile input styles
: 'bg-muted pr-20'
)}
onFocus={onFocus}
onBlur={onBlur}
Comment on lines +129 to +130
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.

⚠️ Potential issue | 🟠 Major

Auto-focus defeats the “show on focus” requirement

Forwarding onFocus/onBlur is good, but because the textarea still auto-focuses on mount (Line 66), the parent handler flips isInputFocused to true immediately. The EmptyScreen—and its example prompts—becomes visible before the user interacts, which contradicts the stated goal of hiding prompts until the chat input is manually focused. Please drop or gate the auto-focus so the new visibility logic only triggers after an intentional focus event.

-  useEffect(() => {
-    inputRef.current?.focus(); 
-  }, [])
📝 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
onFocus={onFocus}
onBlur={onBlur}
// … earlier in ChatPanel component …
const ChatPanel = () => {
const inputRef = useRef<HTMLTextAreaElement>(null);
// — Removed the auto-focus effect so that onFocus/onBlur
// are only triggered by user interaction, not on mount.
// useEffect(() => {
// inputRef.current?.focus();
// }, []);
// … other hooks and logic …
return (
<textarea
ref={inputRef}
onFocus={onFocus}
onBlur={onBlur}
// … other props …
/>
);
};
// … rest of file …
🤖 Prompt for AI Agents
In components/chat-panel.tsx around lines 66 and 129-130, the textarea still
auto-focuses on mount (line 66) which immediately fires the forwarded onFocus
and makes the parent treat the input as user-focused; remove or gate that
autoFocus so focus is only set after an intentional user action. Concretely,
stop passing autoFocus unconditionally (either remove the prop or wrap it behind
an explicit prop/state like allowAutoFocus or only set autoFocus after a
confirmed user interaction) so the forwarded onFocus/onBlur reflect actual
manual focus and the EmptyScreen visibility remains hidden until the user
intentionally focuses the input.

onChange={e => {
setInput(e.target.value)
}}
Expand Down
68 changes: 43 additions & 25 deletions components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { ChatPanel } from './chat-panel'
import { ChatMessages } from './chat-messages'
import { EmptyScreen } from './empty-screen'
import { Mapbox } from './map/mapbox-map'
import { useUIState, useAIState } from 'ai/rsc'
import { useUIState, useAIState, useActions } from 'ai/rsc'
import { UserMessage } from './user-message'
import { nanoid } from 'nanoid'
import { UIState } from '@/app/actions'
import MobileIconsBar from './mobile-icons-bar'
import { useProfileToggle, ProfileToggleEnum } from "@/components/profile-toggle-context";
import SettingsView from "@/components/settings/settings-view";
Expand All @@ -20,16 +23,27 @@ type ChatProps = {
export function Chat({ id }: ChatProps) {
const router = useRouter()
const path = usePathname()
const [messages] = useUIState()
const [messages, setMessages] = useUIState()
const [aiState] = useAIState()
const { submit } = useActions()
const [isMobile, setIsMobile] = useState(false)
const { activeView } = useProfileToggle();
const [input, setInput] = useState('')
const [showEmptyScreen, setShowEmptyScreen] = useState(false)

useEffect(() => {
setShowEmptyScreen(messages.length === 0)
}, [messages])
const [isInputFocused, setIsInputFocused] = useState(false)

const submitExampleMessage = 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])
}
Comment on lines +34 to +46
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.

⚠️ Potential issue | 🔴 Critical

submitExampleMessage sends the wrong payload to submit

handleSubmit still builds a FormData payload before calling submit, which matches the server action’s contract. Here we now pass a plain string (submit(message)), so clicking an example prompt will either throw (type mismatch) or reach the server without the expected input field. Align the example flow with the form submission by packaging the message into FormData.

   const submitExampleMessage = async (message: string) => {
     setMessages(currentMessages => [
       ...currentMessages,
       {
         id: nanoid(),
         component: <UserMessage message={message} />
       }
     ])

-    const responseMessage = await submit(message)
+    const formData = new FormData()
+    formData.append('input', message)
+    const responseMessage = await submit(formData)
 
     setMessages(currentMessages => [...currentMessages, responseMessage as any])
   }
📝 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
const submitExampleMessage = async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const responseMessage = await submit(message)
setMessages(currentMessages => [...currentMessages, responseMessage as any])
}
const submitExampleMessage = async (message: string) => {
setMessages(currentMessages => [
...currentMessages,
{
id: nanoid(),
component: <UserMessage message={message} />
}
])
const formData = new FormData()
formData.append('input', message)
const responseMessage = await submit(formData)
setMessages(currentMessages => [...currentMessages, responseMessage as any])
}
🤖 Prompt for AI Agents
components/chat.tsx around lines 33 to 45: submitExampleMessage passes the raw
string to submit but the server action expects a FormData payload like
handleSubmit does; wrap the message in a FormData instance (e.g., const form =
new FormData(); form.append('input', message);) and call submit(form) instead of
submit(message), and ensure any type annotations for submit are satisfied (cast
to the expected type if needed).


useEffect(() => {
// Check if device is mobile
Expand Down Expand Up @@ -83,18 +97,20 @@ export function Chat({ id }: ChatProps) {
<MobileIconsBar />
</div>
<div className="mobile-chat-input-area">
<ChatPanel messages={messages} input={input} setInput={setInput} />
<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
onBlur={() => setIsInputFocused(false)}
/>
Comment on lines +104 to +106
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hiding example prompts on input blur makes them disappear before the user can click an example. In the browser event order, the input blur fires before the example’s click, so your onBlur immediately hides <EmptyScreen>, causing a frustrating flicker and missed clicks.

Suggestion

Delay the blur state update so clicks on the examples still register. Replace the onBlur with a deferred update:

onBlur={() => setTimeout(() => setIsInputFocused(false), 0)}

Apply this change to both mobile and desktop ChatPanel instances.

Reply with "@CharlieHelps yes please" if you'd like me to add a commit updating both places.

</div>
<div className="mobile-chat-messages-area">
{showEmptyScreen ? (
<EmptyScreen
submitMessage={message => {
setInput(message)
}}
/>
) : (
{messages.length > 0 ? (
<ChatMessages messages={messages} />
)}
) : isInputFocused ? (
<EmptyScreen submitMessage={submitExampleMessage} />
) : null}
</div>
</div>
</MapDataProvider>
Expand All @@ -107,16 +123,18 @@ export function Chat({ id }: ChatProps) {
<div className="flex justify-start items-start">
{/* This is the new div for scrolling */}
<div className="w-1/2 flex flex-col space-y-3 md:space-y-4 px-8 sm:px-12 pt-12 md:pt-14 pb-4 h-[calc(100vh-0.5in)] overflow-y-auto">
<ChatPanel messages={messages} input={input} setInput={setInput} />
{showEmptyScreen ? (
<EmptyScreen
submitMessage={message => {
setInput(message)
}}
/>
) : (
<ChatPanel
messages={messages}
input={input}
setInput={setInput}
onFocus={() => setIsInputFocused(true)}
onBlur={() => setIsInputFocused(false)}
/>
{messages.length > 0 ? (
<ChatMessages messages={messages} />
)}
) : isInputFocused ? (
<EmptyScreen submitMessage={submitExampleMessage} />
) : null}
</div>
<div
className="w-1/2 p-4 fixed h-[calc(100vh-0.5in)] top-0 right-0 mt-[0.5in]"
Expand Down
6 changes: 6 additions & 0 deletions dev.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
▲ Next.js 15.3.3 (Turbopack)
- Local: http://localhost:3001
- Network: http://192.168.0.2:3001
- Environments: .env

✓ Starting...
Comment on lines +1 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The dev.log file is being committed even though logs are now ignored. This defeats the goal of preventing logs from entering the repo and can cause noisy diffs or leak environment details. It should be untracked and removed from the repository.

Suggestion

Remove dev.log from version control and rely on .gitignore to prevent future commits. You can do:

  • git rm --cached dev.log (or delete the file in the PR)
  • Commit the change

Reply with "@CharlieHelps yes please" if you'd like me to add a commit removing dev.log from the repo.