'use client'
import * as React from 'react'
import { createPortal } from 'react-dom'
import { Command } from 'cmdk'
import { Loader2, Send, Square } from 'lucide-react'
import { cn } from '@open-mercato/shared/lib/utils'
import { Button } from '@open-mercato/ui/primitives/button'
import { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'
import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
import { useCommandPaletteContext } from './CommandPaletteProvider'
import { CommandInput } from './CommandInput'
import { CommandHeader } from './CommandHeader'
import { CommandFooter } from './CommandFooter'
import { ToolChatPage } from './ToolChatPage'
import { DebugPanel } from './DebugPanel'
// Idle state - shown when palette is open but no query submitted
function IdleState() {
return (
Ask me anything or describe what you want to do.
Examples:
- "Search for customers in New York"
- "Create a new product"
- "Show me recent orders"
)
}
// Routing indicator - shown while fast model analyzes intent
function RoutingIndicator() {
return (
Analyzing request...
)
}
export function CommandPalette() {
const {
state,
isThinking,
agentStatus,
isSessionAuthorized,
messages,
pendingToolCalls,
selectedTool,
close,
setInputValue,
handleSubmit,
reset,
sendAgenticMessage,
stopExecution,
approveToolCall,
rejectToolCall,
debugEvents,
showDebug,
setShowDebug,
clearDebugEvents,
pendingQuestion,
answerQuestion,
} = useCommandPaletteContext()
const {
isOpen,
phase,
inputValue,
isLoading,
isStreaming,
connectionStatus,
} = state
const [localInput, setLocalInput] = React.useState('')
const [chatInput, setChatInput] = React.useState('')
const chatInputRef = React.useRef(null)
// Reset local input when phase changes to idle
React.useEffect(() => {
if (phase === 'idle') {
setLocalInput('')
setChatInput('')
}
}, [phase])
// Focus chat input when entering chatting phase
React.useEffect(() => {
if (phase === 'chatting' || phase === 'confirming' || phase === 'executing') {
// Small delay to ensure DOM is ready
setTimeout(() => chatInputRef.current?.focus(), 50)
}
}, [phase])
const handleOpenChange = (open: boolean) => {
if (!open) {
close()
}
}
const handleInputSubmit = async () => {
const query = localInput.trim()
if (!query) return
setLocalInput('')
await handleSubmit(query)
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && phase === 'idle' && localInput.trim()) {
e.preventDefault()
handleInputSubmit()
}
}
const handleChatSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!chatInput.trim() || isStreaming) return
const content = chatInput
setChatInput('')
await sendAgenticMessage(content)
}
const handleChatKeyDown = (e: React.KeyboardEvent) => {
// Prevent escape from bubbling to close the palette
if (e.key === 'Escape') {
e.stopPropagation()
}
// Submit on Enter (not Shift+Enter for multiline)
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
if (chatInput.trim() && !isStreaming) {
const content = chatInput
setChatInput('')
sendAgenticMessage(content)
}
}
}
return (
<>
{/* Custom blur overlay when debug mode is on (since modal=false removes it) */}
{isOpen && showDebug && (
)}
{/* Debug panel - rendered via portal outside the dialog DOM tree */}
{isOpen && showDebug && typeof document !== 'undefined' && createPortal(
setShowDebug(false)}
/>
,
document.body
)}
>
)
}