update chatbox
This commit is contained in:
@@ -20,8 +20,6 @@ export function PromptInput({
|
||||
onStop,
|
||||
busy = false,
|
||||
placeholders = ['Ask anything…'],
|
||||
/** Larger treatment for the landing state, where the input is the focus. */
|
||||
size = 'default',
|
||||
autoFocus = false,
|
||||
className,
|
||||
}) {
|
||||
@@ -38,16 +36,29 @@ export function PromptInput({
|
||||
}, [value]);
|
||||
|
||||
/* Rotate the examples only while the field is idle — changing the placeholder
|
||||
under someone who is mid-thought is distracting. */
|
||||
under someone who is mid-thought is distracting. The crossfade is done with
|
||||
a real element rather than the native placeholder, which cannot animate. */
|
||||
const [fading, setFading] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (value || focused || placeholders.length < 2) return undefined;
|
||||
const timer = setInterval(
|
||||
() => setPlaceholderIndex((i) => (i + 1) % placeholders.length),
|
||||
3800
|
||||
);
|
||||
const timer = setInterval(() => {
|
||||
setFading(true);
|
||||
// Swap the text at the midpoint of the fade so neither string is visible
|
||||
// while it changes.
|
||||
setTimeout(() => {
|
||||
setPlaceholderIndex((i) => (i + 1) % placeholders.length);
|
||||
setFading(false);
|
||||
}, 220);
|
||||
}, 4000);
|
||||
return () => clearInterval(timer);
|
||||
}, [value, focused, placeholders.length]);
|
||||
|
||||
/* A shorter list on a context change must not leave the index out of range. */
|
||||
React.useEffect(() => {
|
||||
setPlaceholderIndex(0);
|
||||
}, [placeholders]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (autoFocus) textareaRef.current?.focus();
|
||||
}, [autoFocus]);
|
||||
@@ -64,36 +75,52 @@ export function PromptInput({
|
||||
}
|
||||
};
|
||||
|
||||
const large = size === 'large';
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => { e.preventDefault(); submit(); }}
|
||||
className={cn(
|
||||
'relative flex items-end gap-2 rounded-2xl border bg-surface transition-all duration-base ease-out',
|
||||
large ? 'px-4 py-3' : 'px-3 py-2.5',
|
||||
'px-3 py-2',
|
||||
focused
|
||||
? 'border-krow-blue/50 shadow-[0_8px_28px_-8px_rgba(10,57,223,0.28)] ring-2 ring-krow-blue/15'
|
||||
: 'border-border shadow-md hover:border-krow-blue/30',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
rows={1}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
placeholder={placeholders[placeholderIndex]}
|
||||
aria-label="Ask Krow AI"
|
||||
className={cn(
|
||||
'flex-1 min-w-0 resize-none bg-transparent leading-relaxed text-ink-1',
|
||||
'placeholder:text-ink-4 outline-none no-scrollbar',
|
||||
large ? 'text-body py-1' : 'text-body-sm py-0.5'
|
||||
<div className="relative min-w-0 flex-1">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
rows={1}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
disabled={busy}
|
||||
aria-label="Ask Krow AI"
|
||||
aria-busy={busy || undefined}
|
||||
className={cn(
|
||||
'block w-full resize-none bg-transparent leading-relaxed text-ink-1',
|
||||
'no-scrollbar outline-none disabled:cursor-wait disabled:text-ink-3',
|
||||
'py-0.5 text-body-sm'
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Animated placeholder. Hidden from assistive tech — the textarea's
|
||||
aria-label is the accessible name. */}
|
||||
{!value && (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-0 truncate text-ink-4 transition-opacity duration-base',
|
||||
'py-0.5 text-body-sm',
|
||||
fading ? 'opacity-0' : 'opacity-100'
|
||||
)}
|
||||
>
|
||||
{busy ? 'Thinking…' : placeholders[placeholderIndex]}
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{busy ? (
|
||||
<button
|
||||
@@ -103,10 +130,10 @@ export function PromptInput({
|
||||
className={cn(
|
||||
'grid shrink-0 place-items-center rounded-full border border-border bg-surface text-ink-2',
|
||||
'transition-colors duration-base hover:bg-surface-sunken focus-ring',
|
||||
large ? 'w-9 h-9' : 'w-8 h-8'
|
||||
'h-8 w-8'
|
||||
)}
|
||||
>
|
||||
<Square className={large ? 'w-3.5 h-3.5' : 'w-3 h-3'} aria-hidden="true" />
|
||||
<Square className="h-3 w-3" aria-hidden="true" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
@@ -117,10 +144,10 @@ export function PromptInput({
|
||||
'grid shrink-0 place-items-center rounded-full bg-krow-blue text-white',
|
||||
'transition-all duration-base ease-out hover:bg-krow-blue-dark',
|
||||
'disabled:bg-surface-sunken disabled:text-ink-4 focus-ring',
|
||||
large ? 'w-9 h-9' : 'w-8 h-8'
|
||||
'h-8 w-8'
|
||||
)}
|
||||
>
|
||||
<ArrowUp className={large ? 'w-4 h-4' : 'w-3.5 h-3.5'} aria-hidden="true" />
|
||||
<ArrowUp className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user