feat(ui): redesign lighthouse chat composer input

This commit is contained in:
alejandrobailo
2026-06-26 14:11:53 +02:00
parent ccc3c0a4af
commit 6f3928d09e
2 changed files with 54 additions and 20 deletions
@@ -1,10 +1,17 @@
"use client";
import { ArrowRight, Square } from "lucide-react";
import { CornerDownLeft, Settings } from "lucide-react";
import Link from "next/link";
import { type FormEvent } from "react";
import { Button } from "@/components/shadcn/button/button";
import { Spinner } from "@/components/shadcn/spinner/spinner";
import { Textarea } from "@/components/shadcn/textarea/textarea";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/shadcn/tooltip";
interface ChatComposerPanelProps {
feedback: string | null;
@@ -69,6 +76,8 @@ interface ChatComposerProps {
isStreaming: boolean;
selectedConfigurationConnected: boolean;
onInputChange: (value: string) => void;
// Kept on the contract but unused for now: the backend can't cancel a run yet,
// so the stop control is replaced by a non-interactive spinner.
onStop: () => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
onSubmitText: (text: string) => Promise<void>;
@@ -80,13 +89,12 @@ function ChatComposer({
isStreaming,
selectedConfigurationConnected,
onInputChange,
onStop,
onSubmit,
onSubmitText,
}: ChatComposerProps) {
return (
<form
className="border-border-neutral-secondary bg-bg-neutral-secondary flex min-h-[150px] w-full flex-col rounded-[8px] border shadow-xs"
className="border-border-neutral-secondary bg-bg-neutral-tertiary has-[textarea:focus]:border-border-input-primary-press flex min-h-[150px] w-full flex-col overflow-hidden rounded-[8px] border shadow-xs transition-all"
onSubmit={onSubmit}
>
<Textarea
@@ -99,9 +107,9 @@ function ChatComposer({
? "Ask a question"
: "Connect a provider first"
}
variant="ghost"
variant="soft"
textareaSize="lg"
className="min-h-[104px] flex-1 rounded-b-none border-0 hover:bg-transparent focus:bg-transparent focus:ring-0"
className="min-h-[104px] flex-1"
onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
@@ -109,26 +117,51 @@ function ChatComposer({
}
}}
/>
<div className="flex items-center justify-end px-3 pb-3">
<div className="flex items-center justify-between px-3 pb-3">
<Button type="button" variant="outline" size="icon-sm" asChild>
<Link href="/lighthouse/settings" aria-label="Lighthouse AI settings">
<Settings className="size-4" />
</Link>
</Button>
{isStreaming ? (
<Button
type="button"
variant="outline"
size="icon-sm"
onClick={onStop}
<div
className="flex size-8 items-center justify-center"
role="status"
aria-label="Generating response"
>
<Square className="size-4" />
</Button>
<Spinner className="size-4" />
</div>
) : (
<Button
type="submit"
size="icon-sm"
disabled={!canSend || !input.trim()}
>
<ArrowRight className="size-4" />
</Button>
<ChatSendButton canSend={canSend} hasText={input.trim().length > 0} />
)}
</div>
</form>
);
}
function ChatSendButton({
canSend,
hasText,
}: {
canSend: boolean;
hasText: boolean;
}) {
const sendButton = (
<Button type="submit" size="icon-sm" disabled={!canSend || !hasText}>
<CornerDownLeft className="size-4" />
</Button>
);
if (canSend && !hasText) {
return (
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<span className="inline-flex cursor-not-allowed">{sendButton}</span>
</TooltipTrigger>
<TooltipContent side="top">Type something</TooltipContent>
</Tooltip>
);
}
return sendButton;
}
@@ -14,6 +14,7 @@ const textareaVariants = cva(
"border-border-input-primary bg-bg-input-primary dark:bg-input/30 hover:bg-bg-neutral-tertiary dark:hover:bg-input/50 focus:border-border-input-primary-press focus:ring-1 focus:ring-inset focus:ring-border-input-primary-press placeholder:text-text-neutral-tertiary",
ghost:
"border-transparent bg-transparent hover:bg-bg-neutral-tertiary focus:bg-bg-neutral-tertiary placeholder:text-text-neutral-tertiary",
soft: "border-transparent bg-bg-neutral-tertiary placeholder:text-text-neutral-tertiary",
},
textareaSize: {
default: "min-h-16 px-4 py-3",