ui changes
This commit is contained in:
@@ -10,8 +10,12 @@ import { cn } from '@/lib/utils';
|
||||
* deliberate newline. That combination is what makes typing a real question feel
|
||||
* natural instead of cramped.
|
||||
*
|
||||
* `placeholders` rotate while the field is empty and unfocused, which teaches
|
||||
* the range of what can be asked without spending vertical space on a legend.
|
||||
* `placeholders` is a list, but only the first is shown. It used to rotate every
|
||||
* few seconds; that was defensible when the panel opened on demand and wrong once
|
||||
* Owliver became permanent furniture, because a permanently visible field that
|
||||
* rewrites itself forever is movement in the corner of the eye with nothing to
|
||||
* gain — the suggestion chips already teach the range of what can be asked, and
|
||||
* they do it without a timer or a rerender every four seconds.
|
||||
*/
|
||||
export function PromptInput({
|
||||
value,
|
||||
@@ -26,7 +30,6 @@ export function PromptInput({
|
||||
}) {
|
||||
const textareaRef = React.useRef(null);
|
||||
const [focused, setFocused] = React.useState(false);
|
||||
const [placeholderIndex, setPlaceholderIndex] = React.useState(0);
|
||||
|
||||
/* Grow to fit the content, up to a cap, then scroll internally. */
|
||||
React.useEffect(() => {
|
||||
@@ -36,30 +39,6 @@ export function PromptInput({
|
||||
el.style.height = `${Math.min(el.scrollHeight, 148)}px`;
|
||||
}, [value]);
|
||||
|
||||
/* Rotate the examples only while the field is idle — changing the placeholder
|
||||
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(() => {
|
||||
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]);
|
||||
@@ -100,7 +79,7 @@ export function PromptInput({
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
disabled={busy}
|
||||
aria-label="Ask Krow AI"
|
||||
aria-label="Ask Owliver"
|
||||
aria-busy={busy || undefined}
|
||||
className={cn(
|
||||
'block w-full resize-none bg-transparent leading-relaxed text-ink-1',
|
||||
@@ -110,19 +89,19 @@ export function PromptInput({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Animated placeholder. Hidden from assistive tech — the textarea's
|
||||
aria-label is the accessible name. */}
|
||||
{/* Rendered as an element rather than the native `placeholder` so it can
|
||||
be truncated and swapped for the busy state. 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',
|
||||
'pointer-events-none absolute inset-0 truncate text-ink-4',
|
||||
'py-0.5 text-body-sm',
|
||||
isCenter ? 'text-center' : 'text-left',
|
||||
fading ? 'opacity-0' : 'opacity-100'
|
||||
isCenter ? 'text-center' : 'text-left'
|
||||
)}
|
||||
>
|
||||
{busy ? 'Thinking…' : placeholders[placeholderIndex]}
|
||||
{busy ? 'Thinking…' : placeholders[0]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user