loading indicator, growing message box
This commit is contained in:
@@ -272,6 +272,14 @@ export default function App() {
|
||||
const searchRunCounterRef = useRef(0);
|
||||
const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document === "undefined") return;
|
||||
const textarea = document.getElementById("composer-input") as HTMLTextAreaElement | null;
|
||||
if (!textarea) return;
|
||||
textarea.style.height = "0px";
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}, [composer]);
|
||||
|
||||
const sidebarItems = useMemo(() => buildSidebarItems(chats, searches), [chats, searches]);
|
||||
|
||||
const resetWorkspaceState = () => {
|
||||
@@ -916,9 +924,15 @@ export default function App() {
|
||||
<footer className="border-t p-3 md:p-4">
|
||||
<div className="mx-auto max-w-3xl rounded-xl border bg-background p-2 shadow-sm">
|
||||
<Textarea
|
||||
rows={3}
|
||||
id="composer-input"
|
||||
rows={1}
|
||||
value={composer}
|
||||
onInput={(event) => setComposer(event.currentTarget.value)}
|
||||
onInput={(event) => {
|
||||
const textarea = event.currentTarget;
|
||||
textarea.style.height = "0px";
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
setComposer(textarea.value);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
@@ -926,11 +940,11 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
placeholder={isSearchMode ? "Search the web" : "Message Sybil"}
|
||||
className="resize-none border-0 shadow-none focus-visible:ring-0"
|
||||
className="max-h-40 min-h-0 resize-none overflow-y-auto border-0 shadow-none focus-visible:ring-0"
|
||||
disabled={isSending}
|
||||
/>
|
||||
<div className="flex items-center justify-between px-2 pb-1">
|
||||
{error ? <p className="text-xs text-red-600">{error}</p> : <span className="text-xs text-muted-foreground">{isSearchMode ? "Enter to search" : "Enter to send"}</span>}
|
||||
<div className={cn("flex items-center px-2 pb-1", error ? "justify-between" : "justify-end")}>
|
||||
{error ? <p className="text-xs text-red-600">{error}</p> : null}
|
||||
<Button onClick={() => void handleSend()} size="icon" disabled={isSending || !composer.trim()}>
|
||||
{isSearchMode ? <Search className="h-4 w-4" /> : <SendHorizontal className="h-4 w-4" />}
|
||||
</Button>
|
||||
|
||||
@@ -9,6 +9,8 @@ type Props = {
|
||||
};
|
||||
|
||||
export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) {
|
||||
const hasPendingAssistant = messages.some((message) => message.id.startsWith("temp-assistant-"));
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && messages.length === 0 ? <p className="text-sm text-muted-foreground">Loading messages...</p> : null}
|
||||
@@ -26,9 +28,9 @@ export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) {
|
||||
>
|
||||
{isPendingAssistant ? (
|
||||
<span className="inline-flex items-center gap-1" aria-label="Assistant is typing" role="status">
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-white [animation-delay:0ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-white [animation-delay:140ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-white [animation-delay:280ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:0ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:140ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:280ms]" />
|
||||
</span>
|
||||
) : (
|
||||
<MarkdownContent
|
||||
@@ -40,6 +42,17 @@ export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{isSending && !hasPendingAssistant ? (
|
||||
<div className="flex justify-start">
|
||||
<div className="max-w-[85%] text-base leading-7 text-fuchsia-100">
|
||||
<span className="inline-flex items-center gap-1" aria-label="Assistant is typing" role="status">
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:0ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:140ms]" />
|
||||
<span className="inline-block h-1.5 w-1.5 animate-bounce rounded-full bg-muted-foreground [animation-delay:280ms]" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user