diff --git a/ui/desktop/src/components/ChatView.tsx b/ui/desktop/src/components/ChatView.tsx
index f3ebd353..52676856 100644
--- a/ui/desktop/src/components/ChatView.tsx
+++ b/ui/desktop/src/components/ChatView.tsx
@@ -193,12 +193,7 @@ export default function ChatView({ setView }: { setView: (view: View) => void })
{isLoading && }
-
+
diff --git a/ui/desktop/src/components/Input.tsx b/ui/desktop/src/components/Input.tsx
index f90b778e..b330d163 100644
--- a/ui/desktop/src/components/Input.tsx
+++ b/ui/desktop/src/components/Input.tsx
@@ -5,27 +5,21 @@ import { Attach, Send } from './icons';
interface InputProps {
handleSubmit: (e: React.FormEvent) => void;
- disabled?: boolean;
isLoading?: boolean;
onStop?: () => void;
}
-export default function Input({
- handleSubmit,
- disabled = false,
- isLoading = false,
- onStop,
-}: InputProps) {
+export default function Input({ handleSubmit, isLoading = false, onStop }: InputProps) {
const [value, setValue] = useState('');
// State to track if the IME is composing (i.e., in the middle of Japanese IME input)
const [isComposing, setIsComposing] = useState(false);
const textAreaRef = useRef(null);
useEffect(() => {
- if (textAreaRef.current && !disabled) {
+ if (textAreaRef.current) {
textAreaRef.current.focus();
}
- }, [disabled, value]);
+ }, [value]);
const useAutosizeTextArea = (textAreaRef: HTMLTextAreaElement | null, value: string) => {
useEffect(() => {
@@ -57,10 +51,19 @@ export default function Input({
};
const handleKeyDown = (evt: React.KeyboardEvent) => {
- // Only trigger submit on Enter if not composing (IME input in progress) and shift is not pressed
- if (evt.key === 'Enter' && !evt.shiftKey && !isComposing) {
+ if (evt.key === 'Enter') {
+ // should not trigger submit on Enter if it's composing (IME input in progress) or shift is pressed
+ if (evt.shiftKey || isComposing) {
+ // Allow line break for Shift+Enter or during IME composition
+ return;
+ }
+
+ // Prevent default Enter behavior when loading or when not loading but has content
+ // So it won't trigger a new line
evt.preventDefault();
- if (value.trim()) {
+
+ // Only submit if not loading and has content
+ if (!isLoading && value.trim()) {
handleSubmit(new CustomEvent('submit', { detail: { value } }));
setValue('');
}
@@ -69,7 +72,7 @@ export default function Input({
const onFormSubmit = (e: React.FormEvent) => {
e.preventDefault();
- if (value.trim()) {
+ if (value.trim() && !isLoading) {
handleSubmit(new CustomEvent('submit', { detail: { value } }));
setValue('');
}
@@ -97,7 +100,6 @@ export default function Input({
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
onKeyDown={handleKeyDown}
- disabled={disabled}
ref={textAreaRef}
rows={1}
style={{
@@ -105,19 +107,14 @@ export default function Input({
maxHeight: `${maxHeight}px`,
overflowY: 'auto',
}}
- className={`w-full outline-none border-none focus:ring-0 bg-transparent p-0 text-base resize-none text-textStandard ${
- disabled ? 'cursor-not-allowed opacity-50' : ''
- }`}
+ className="w-full outline-none border-none focus:ring-0 bg-transparent p-0 text-base resize-none text-textStandard"
/>
@@ -126,7 +123,11 @@ export default function Input({
type="button"
size="icon"
variant="ghost"
- onClick={onStop}
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ onStop();
+ }}
className="absolute right-2 top-1/2 -translate-y-1/2 [&_svg]:size-5 text-textSubtle hover:text-textStandard"
>
@@ -136,9 +137,9 @@ export default function Input({
type="submit"
size="icon"
variant="ghost"
- disabled={disabled || !value.trim()}
+ disabled={!value.trim()}
className={`absolute right-2 top-1/2 -translate-y-1/2 text-textSubtle hover:text-textStandard ${
- disabled || !value.trim() ? 'text-textSubtle cursor-not-allowed' : ''
+ !value.trim() ? 'text-textSubtle cursor-not-allowed' : ''
}`}
>