feat(widgets): redesign gate widgets — readable hierarchy, prominent select-all, styled controls

Titles + intro read as a clear prompt (comfortable line length), options as
spaced choice rows, Other/Back/Exit as discreet secondary controls under a
divider. Multiselect gets a prominent Select all/Deselect all at the head of the
list with a live count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 18:15:48 +02:00
co-authored by Claude Opus 4.8
parent 56ed8b29bd
commit cec6620c00
4 changed files with 102 additions and 49 deletions
+19 -4
View File
@@ -1,12 +1,27 @@
import { useState } from "react";
import type { WidgetProps } from "./types";
export function FreetextWidget({ descriptor, onRespond }: WidgetProps) {
const [text, setText] = useState("");
return (
<div className="space-y-2">
{descriptor.title && <p className="font-medium">{descriptor.title}</p>}
<textarea className="w-full border rounded p-2" value={text} onChange={(e) => setText(e.target.value)} />
<button className="border rounded px-3 py-1" onClick={() => onRespond({ id: descriptor.id, kind: "freetext", text })}>Send</button>
<div className="space-y-3">
{descriptor.title && (
<p className="text-[0.95rem] font-semibold leading-snug text-foreground">
{descriptor.title}
</p>
)}
<textarea
className="min-h-24 w-full resize-y rounded-lg border border-input bg-card p-3 text-sm outline-none transition-colors focus:border-primary/40 focus:ring-2 focus:ring-ring/20"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type your answer…"
/>
<button
className="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-[oklch(var(--primary-hover))]"
onClick={() => onRespond({ id: descriptor.id, kind: "freetext", text })}
>
Send
</button>
</div>
);
}