From 58dcb3c41ab21d4a1219a58a2ffd33d13761f7e1 Mon Sep 17 00:00:00 2001 From: "dayuan.jiang" Date: Sat, 15 Nov 2025 14:29:18 +0900 Subject: [PATCH] feat: add OpenRouter support and fix input disabling - Add OpenRouter provider support with @openrouter/ai-sdk-provider - Fix input not disabling during 'submitted' state for fast providers - Apply disable logic to all interactive elements (textarea, buttons, handlers) - Clean up env.example by removing model examples and separator blocks - Upgrade zod to v4.1.12 for compatibility with ollama-ai-provider-v2 - Add debug logging for status changes in chat components --- components/chat-input.tsx | 26 +++++++++++------- components/chat-panel.tsx | 8 +++++- env.example | 55 +++------------------------------------ lib/ai-providers.ts | 17 +++++++++--- package-lock.json | 42 ++++++++++++++++++++++++++++++ package.json | 1 + 6 files changed, 83 insertions(+), 66 deletions(-) diff --git a/components/chat-input.tsx b/components/chat-input.tsx index b58a34a..e59dc32 100644 --- a/components/chat-input.tsx +++ b/components/chat-input.tsx @@ -45,6 +45,12 @@ export function ChatInput({ const [isDragging, setIsDragging] = useState(false); const [showClearDialog, setShowClearDialog] = useState(false); + // Debug: Log status changes + const isDisabled = status === "streaming" || status === "submitted"; + useEffect(() => { + console.log('[ChatInput] Status changed to:', status, '| Input disabled:', isDisabled); + }, [status, isDisabled]); + // Auto-resize textarea based on content const adjustTextareaHeight = useCallback(() => { const textarea = textareaRef.current; @@ -63,7 +69,7 @@ export function ChatInput({ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { e.preventDefault(); const form = e.currentTarget.closest("form"); - if (form && input.trim() && status !== "streaming") { + if (form && input.trim() && !isDisabled) { form.requestSubmit(); } } @@ -71,7 +77,7 @@ export function ChatInput({ // Handle clipboard paste const handlePaste = async (e: React.ClipboardEvent) => { - if (status === "streaming") return; + if (isDisabled) return; const items = e.clipboardData.items; const imageItems = Array.from(items).filter((item) => @@ -140,7 +146,7 @@ export function ChatInput({ e.stopPropagation(); setIsDragging(false); - if (status === "streaming") return; + if (isDisabled) return; const droppedFiles = e.dataTransfer.files; @@ -183,7 +189,7 @@ export function ChatInput({ placeholder="Describe what changes you want to make to the diagram or upload(paste) an image to replicate a diagram. (Press Cmd/Ctrl + Enter to send)" - disabled={status === "streaming"} + disabled={isDisabled} aria-label="Chat input" className="min-h-[80px] resize-none transition-all duration-200 px-1 py-0" /> @@ -220,7 +226,7 @@ export function ChatInput({ size="icon" onClick={() => onToggleHistory(true)} disabled={ - status === "streaming" || + isDisabled || diagramHistory.length === 0 } title="Diagram History" @@ -234,7 +240,7 @@ export function ChatInput({ variant="outline" size="icon" onClick={triggerFileInput} - disabled={status === "streaming"} + disabled={isDisabled} title="Upload image" > @@ -247,21 +253,21 @@ export function ChatInput({ onChange={handleFileChange} accept="image/*" multiple - disabled={status === "streaming"} + disabled={isDisabled} />