-
-
Couldn't load subscription status.
- Fork 6
feat: Make example prompts interactive and conditionally visible #302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,9 +15,15 @@ interface ChatPanelProps { | |
| messages: UIState | ||
| input: string | ||
| setInput: (value: string) => void | ||
| onFocus: () => void | ||
| } | ||
|
|
||
| export function ChatPanel({ messages, input, setInput }: ChatPanelProps) { | ||
| export function ChatPanel({ | ||
| messages, | ||
| input, | ||
| setInput, | ||
| onFocus | ||
| }: ChatPanelProps) { | ||
| const [, setMessages] = useUIState<typeof AI>() | ||
| const { submit, clearChat } = useActions() | ||
| // Removed mcp instance as it's no longer passed to submit | ||
|
|
@@ -121,6 +127,7 @@ export function ChatPanel({ messages, input, setInput }: ChatPanelProps) { | |
| onChange={e => { | ||
| setInput(e.target.value) | ||
| }} | ||
| onFocus={onFocus} | ||
| onKeyDown={e => { | ||
|
Comment on lines
+130
to
131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
SuggestionMake the focus handlers optional and add an Example changes:
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion |
||
| if ( | ||
| e.key === 'Enter' && | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,9 +6,12 @@ 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 { nanoid } from 'nanoid' | ||||||||||||||||||||||||||||
| import { UserMessage } from './user-message' | ||||||||||||||||||||||||||||
| import { UIState } from '@/app/actions' | ||||||||||||||||||||||||||||
| import MobileIconsBar from './mobile-icons-bar' | ||||||||||||||||||||||||||||
| import { useProfileToggle, ProfileToggleEnum } from "@/components/profile-toggle-context"; | ||||||||||||||||||||||||||||
| import { useProfileToggle, ProfileToggleEnum } from '@/components/profile-toggle-context' | ||||||||||||||||||||||||||||
| import SettingsView from "@/components/settings/settings-view"; | ||||||||||||||||||||||||||||
| import { MapDataProvider, useMapData } from './map/map-data-context'; // Add this and useMapData | ||||||||||||||||||||||||||||
| import { updateDrawingContext } from '@/lib/actions/chat'; // Import the server action | ||||||||||||||||||||||||||||
|
|
@@ -20,12 +23,14 @@ 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) | ||||||||||||||||||||||||||||
|
Comment on lines
+26
to
29
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||||||||||||||||||||
| const { activeView } = useProfileToggle(); | ||||||||||||||||||||||||||||
| const { activeView } = useProfileToggle() | ||||||||||||||||||||||||||||
| const [input, setInput] = useState('') | ||||||||||||||||||||||||||||
| const [showEmptyScreen, setShowEmptyScreen] = useState(false) | ||||||||||||||||||||||||||||
| const [isInputFocused, setIsInputFocused] = useState(false) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||
| setShowEmptyScreen(messages.length === 0) | ||||||||||||||||||||||||||||
|
|
@@ -83,13 +88,30 @@ 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)} | ||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||
|
Comment on lines
+95
to
+96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You set SuggestionPass an <ChatPanel Implement Reply with "@CharlieHelps yes please" if you'd like me to add a commit with these changes
Comment on lines
+91
to
+96
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| <div className="mobile-chat-messages-area"> | ||||||||||||||||||||||||||||
| {showEmptyScreen ? ( | ||||||||||||||||||||||||||||
| <EmptyScreen | ||||||||||||||||||||||||||||
| submitMessage={message => { | ||||||||||||||||||||||||||||
| setInput(message) | ||||||||||||||||||||||||||||
| isInputFocused={isInputFocused} | ||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||
|
|
@@ -98,20 +120,40 @@ export function Chat({ id }: ChatProps) { | |||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </MapDataProvider> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Desktop layout | ||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
| <MapDataProvider> {/* Add Provider */} | ||||||||||||||||||||||||||||
| <MapDataProvider> | ||||||||||||||||||||||||||||
| {' '} | ||||||||||||||||||||||||||||
| {/* Add Provider */} | ||||||||||||||||||||||||||||
| <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} /> | ||||||||||||||||||||||||||||
| <ChatPanel | ||||||||||||||||||||||||||||
| messages={messages} | ||||||||||||||||||||||||||||
| input={input} | ||||||||||||||||||||||||||||
| setInput={setInput} | ||||||||||||||||||||||||||||
| onFocus={() => setIsInputFocused(true)} | ||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||
| {showEmptyScreen ? ( | ||||||||||||||||||||||||||||
| <EmptyScreen | ||||||||||||||||||||||||||||
| submitMessage={message => { | ||||||||||||||||||||||||||||
| setInput(message) | ||||||||||||||||||||||||||||
| isInputFocused={isInputFocused} | ||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||
|
|
@@ -126,5 +168,5 @@ export function Chat({ id }: ChatProps) { | |||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </MapDataProvider> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,33 +27,36 @@ const exampleMessages = [ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function EmptyScreen({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isInputFocused | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitMessage: (message: string) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitMessage: (message: string) => void | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isInputFocused: boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={`mx-auto w-full transition-all ${className}`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="bg-background p-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="mt-4 flex flex-col items-start space-y-2 mb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {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} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submitMessage(item.message) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Icon size={16} className="mr-2 text-muted-foreground" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {item.heading} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The visibility of example prompts is tied to
Comment on lines
+40
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Prevent accidental form submission and drop unnecessary async.
- <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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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