/** * @license * Copyright 2025 Qwen Team * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { EditPencilIcon, AutoEditIcon, PlanModeIcon, CodeBracketsIcon, ThinkingIcon, SlashCommandIcon, LinkIcon, ArrowUpIcon, StopIcon, } from './icons/index.js'; import { CompletionMenu } from './ui/CompletionMenu.js'; import type { CompletionItem } from '../types/CompletionTypes.js'; type EditMode = 'ask' | 'auto' | 'plan'; interface InputFormProps { inputText: string; // Note: RefObject carries nullability in its `current` property, so the // generic should be `HTMLDivElement` (not `HTMLDivElement | null`). inputFieldRef: React.RefObject; isStreaming: boolean; isWaitingForResponse: boolean; isComposing: boolean; editMode: EditMode; thinkingEnabled: boolean; activeFileName: string | null; activeSelection: { startLine: number; endLine: number } | null; onInputChange: (text: string) => void; onCompositionStart: () => void; onCompositionEnd: () => void; onKeyDown: (e: React.KeyboardEvent) => void; onSubmit: (e: React.FormEvent) => void; onCancel: () => void; onToggleEditMode: () => void; onToggleThinking: () => void; onFocusActiveEditor: () => void; onShowCommandMenu: () => void; onAttachContext: () => void; completionIsOpen: boolean; completionItems?: CompletionItem[]; onCompletionSelect?: (item: CompletionItem) => void; onCompletionClose?: () => void; } // Get edit mode display info const getEditModeInfo = (editMode: EditMode) => { switch (editMode) { case 'ask': return { text: 'Ask before edits', title: 'Qwen will ask before each edit. Click to switch modes.', icon: , }; case 'auto': return { text: 'Edit automatically', title: 'Qwen will edit files automatically. Click to switch modes.', icon: , }; case 'plan': return { text: 'Plan mode', title: 'Qwen will plan before executing. Click to switch modes.', icon: , }; default: return { text: 'Unknown mode', title: 'Unknown edit mode', icon: null, }; } }; export const InputForm: React.FC = ({ inputText, inputFieldRef, isStreaming, isComposing, editMode, thinkingEnabled, activeFileName, activeSelection, onInputChange, onCompositionStart, onCompositionEnd, onKeyDown, onSubmit, onCancel, onToggleEditMode, onToggleThinking, onFocusActiveEditor, onShowCommandMenu, onAttachContext, completionIsOpen, // Claude-style completion dropdown (optional) completionItems, onCompletionSelect, onCompletionClose, }) => { const editModeInfo = getEditModeInfo(editMode); const handleKeyDown = (e: React.KeyboardEvent) => { // If composing (Chinese IME input), don't process Enter key if (e.key === 'Enter' && !e.shiftKey && !isComposing) { // If CompletionMenu is open, let it handle Enter key if (completionIsOpen) { return; } e.preventDefault(); onSubmit(e); } onKeyDown(e); }; return (
{/* Inner background layer */}
{/* Banner area */}
{/* Input wrapper (Claude-style anchor container) */}
{/* Claude-style anchored dropdown */} {completionIsOpen && completionItems && completionItems.length > 0 && onCompletionSelect && onCompletionClose && ( // Render dropdown above the input, matching Claude Code )}
{ const target = e.target as HTMLDivElement; onInputChange(target.textContent || ''); }} onCompositionStart={onCompositionStart} onCompositionEnd={onCompositionEnd} onKeyDown={handleKeyDown} suppressContentEditableWarning />
{/* Actions row (compact, Claude-style) */}
{/* Edit mode button */} {/* Active file indicator */} {activeFileName && ( )} {/* Spacer */}
{/* Thinking button */} {/* Command button */} {/* Attach button */} {/* Send/Stop button */} {isStreaming || isWaitingForResponse ? ( ) : ( )}
); };