feat(ui): improve MessageInput and Settings styling

MessageInput:
- Modern container with hover/focus background transitions
- Circular icon button (ArrowUp/Square)
- IME composition handling to prevent submit during input
- Move hint text outside input container

Settings:
- Fix AgentItem hover overriding active state
- Active item now keeps solid background on hover

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing
2026-01-14 21:23:58 +08:00
parent c0a65f801a
commit 50da07471c
2 changed files with 43 additions and 30 deletions

View File

@@ -2,6 +2,7 @@
* Message input component
*/
import { useState, useRef, useEffect } from 'react'
import { ArrowUp, Square } from 'lucide-react'
import { Button } from '@/components/ui/button'
interface MessageInputProps {
@@ -20,6 +21,7 @@ export function MessageInput({
placeholder = 'Type a message...',
}: MessageInputProps) {
const [value, setValue] = useState('')
const [isComposing, setIsComposing] = useState(false)
const textareaRef = useRef<HTMLTextAreaElement>(null)
// Auto-resize textarea
@@ -45,40 +47,49 @@ export function MessageInput({
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
// Don't submit while IME is composing
if (e.key === 'Enter' && !e.shiftKey && !isComposing) {
e.preventDefault()
handleSubmit()
}
}
const canSubmit = !disabled && value.trim().length > 0
return (
<div className="border-t border-border p-4">
<div className="mx-auto flex max-w-3xl gap-2">
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={disabled ? 'Select or create a session first' : placeholder}
disabled={disabled}
rows={1}
className="flex-1 resize-none rounded-lg border border-border bg-muted px-4 py-2 text-foreground outline-none placeholder:text-muted-foreground focus:border-primary disabled:cursor-not-allowed disabled:opacity-50"
/>
{isProcessing ? (
<Button variant="destructive" onClick={onCancel}>
Cancel
</Button>
) : (
<Button onClick={handleSubmit} disabled={disabled || !value.trim()}>
Send
</Button>
)}
</div>
{/* Hint */}
<div className="mx-auto mt-1 max-w-3xl text-center text-xs text-muted-foreground">
Press Enter to send, Shift+Enter for new line
<div className="p-4">
<div className="mx-auto max-w-3xl">
<div className="bg-secondary/50 hover:bg-secondary focus-within:bg-secondary transition-colors duration-200 rounded-md p-2 border border-border">
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
onCompositionStart={() => setIsComposing(true)}
onCompositionEnd={() => setIsComposing(false)}
placeholder={disabled ? 'Select or create a session first' : placeholder}
disabled={disabled}
rows={1}
className="w-full resize-none bg-transparent px-2 py-1 text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
/>
<div className="flex justify-end pt-1">
<Button
size="icon"
onClick={isProcessing ? onCancel : handleSubmit}
disabled={!canSubmit && !isProcessing}
className="h-8 w-8 rounded-full"
>
{isProcessing ? (
<Square className="h-3.5 w-3.5" fill="currentColor" />
) : (
<ArrowUp className="h-4 w-4" />
)}
</Button>
</div>
</div>
<div className="mt-1 text-center text-xs text-muted-foreground">
Enter to send, Shift+Enter for new line
</div>
</div>
</div>
)

View File

@@ -139,8 +139,10 @@ function AgentItem({ agent, isSelected, onSelect }: AgentItemProps) {
return (
<div
className={cn(
'rounded-md transition-colors duration-150 text-secondary-foreground hover:bg-muted/50 hover:text-foreground',
status === 'selected' && 'bg-muted text-foreground',
'rounded-md transition-colors duration-150 text-secondary-foreground',
status === 'selected'
? 'bg-muted text-foreground'
: 'hover:bg-muted/50 hover:text-foreground',
status === 'setup' && 'opacity-60 hover:opacity-100'
)}
>