@nucleux/structured-input 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,10 +14,42 @@ pnpm add @nucleux/structured-input @nucleux/tokens
14
14
 
15
15
  ## Usage
16
16
 
17
+ Lightweight slots and hints that compose clearer prompts. Four composable pieces:
18
+
17
19
  ```tsx
18
- import { StructuredInput } from "@nucleux/structured-input";
20
+ import {
21
+ StructuredInput, // a clarifying question with single/multi choice, "something else" and skip
22
+ StructuredInputSlots, // named-slots: 2–4 labelled fields that fill a prompt template
23
+ StructuredInputTags, // tag-input: @person, p:project, /file become chips
24
+ StructuredInputWizard, // wizard-step: Step 2 of 3 · Audience, Back / Next / Generate
25
+ } from "@nucleux/structured-input";
26
+
27
+ <StructuredInputSlots
28
+ slots={[
29
+ { name: "audience", label: "Audience", options: [{ value: "execs", label: "Execs" }],
30
+ suggested: "from the meeting invite" },
31
+ { name: "length", label: "Length", placeholder: "3 bullets" },
32
+ { name: "topic", label: "Topic", placeholder: "Yesterday's standup", wide: true },
33
+ ]}
34
+ freeform
35
+ onSubmit={(values, freeform) => send(fill(template, values), freeform)}
36
+ />
37
+
38
+ <StructuredInputTags
39
+ triggers={[{ trigger: "@", label: "Person", suggestions: ["sam", "priya"] }, { trigger: "p:", label: "Project" }]}
40
+ onSubmit={(parts, text) => send(text)}
41
+ />
42
+
43
+ <StructuredInputWizard steps={["Intent", "Audience", "Tone"]} description="Who is the brief for?" onSubmit={generate}>
44
+ {/* the current step's fields */}
45
+ </StructuredInputWizard>
19
46
  ```
20
47
 
48
+ Rules the components follow: plain labels; a freeform override; **no field is ever required before
49
+ sending**; agent-suggested values are marked “Suggested” (with why) and become “Edited by you” once
50
+ changed — never changed back. The single-choice question follows the radio-group keyboard pattern
51
+ (arrow keys move and select).
52
+
21
53
  Requires the theme once in your app: `import "@nucleux/tokens/styles.css";`
22
54
  and the Tailwind preset from `@nucleux/tokens/preset`.
23
55
 
package/dist/index.cjs CHANGED
@@ -4,6 +4,12 @@
4
4
  var React = require('react');
5
5
  var utils = require('@nucleux/utils');
6
6
  var lucideReact = require('lucide-react');
7
+ var badge = require('@nucleux/badge');
8
+ var button = require('@nucleux/button');
9
+ var field = require('@nucleux/field');
10
+ var input = require('@nucleux/input');
11
+ var select = require('@nucleux/select');
12
+ var textarea = require('@nucleux/textarea');
7
13
  var jsxRuntime = require('react/jsx-runtime');
8
14
 
9
15
  function _interopNamespace(e) {
@@ -46,6 +52,8 @@ var StructuredInput = React__namespace.forwardRef(
46
52
  }, ref) => {
47
53
  const [internal, setInternal] = React__namespace.useState(defaultValue);
48
54
  const [other, setOther] = React__namespace.useState("");
55
+ const questionId = React__namespace.useId();
56
+ const groupRef = React__namespace.useRef(null);
49
57
  const selected = value !== void 0 ? value : internal;
50
58
  const setSelected = (next) => {
51
59
  if (value === void 0) setInternal(next);
@@ -65,21 +73,35 @@ var StructuredInput = React__namespace.forwardRef(
65
73
  className: utils.cn("flex flex-col gap-4 rounded-xl border border-border bg-background p-5", className),
66
74
  ...props,
67
75
  children: [
68
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base font-medium text-foreground", children: question }),
76
+ /* @__PURE__ */ jsxRuntime.jsx("p", { id: questionId, className: "text-base font-medium text-foreground", children: question }),
69
77
  /* @__PURE__ */ jsxRuntime.jsx(
70
78
  "div",
71
79
  {
80
+ ref: groupRef,
72
81
  role: multiple ? "group" : "radiogroup",
73
- "aria-label": typeof question === "string" ? question : "Options",
82
+ "aria-labelledby": questionId,
74
83
  className: "flex flex-col gap-2",
75
- children: options.map((option) => {
84
+ onKeyDown: (e) => {
85
+ if (multiple || !["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft"].includes(e.key)) return;
86
+ const radios = Array.from(groupRef.current?.querySelectorAll('[role="radio"]') ?? []);
87
+ const i = radios.indexOf(document.activeElement);
88
+ if (i < 0) return;
89
+ e.preventDefault();
90
+ const step = e.key === "ArrowDown" || e.key === "ArrowRight" ? 1 : -1;
91
+ const next = (i + step + radios.length) % radios.length;
92
+ radios[next].focus();
93
+ setSelected([options[next].value]);
94
+ },
95
+ children: options.map((option, index) => {
76
96
  const active = selected.includes(option.value);
97
+ const tabbable = multiple || index === Math.max(0, options.findIndex((o) => selected.includes(o.value)));
77
98
  return /* @__PURE__ */ jsxRuntime.jsxs(
78
99
  "button",
79
100
  {
80
101
  type: "button",
81
102
  role: multiple ? "checkbox" : "radio",
82
103
  "aria-checked": active,
104
+ tabIndex: tabbable ? 0 : -1,
83
105
  onClick: () => toggle(option.value),
84
106
  className: utils.cn(
85
107
  "flex items-center gap-3 rounded-lg border px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
@@ -143,7 +165,332 @@ var StructuredInput = React__namespace.forwardRef(
143
165
  }
144
166
  );
145
167
  StructuredInput.displayName = "StructuredInput";
168
+ var StructuredInputSlots = React__namespace.forwardRef(
169
+ ({
170
+ slots,
171
+ values: valuesProp,
172
+ defaultValues,
173
+ onValuesChange,
174
+ freeform = false,
175
+ onSubmit,
176
+ submitLabel = "Generate",
177
+ label = "Structured prompt builder",
178
+ className,
179
+ ...props
180
+ }, ref) => {
181
+ const [internal, setInternal] = React__namespace.useState(defaultValues ?? {});
182
+ const values = valuesProp ?? internal;
183
+ const [edited, setEdited] = React__namespace.useState({});
184
+ const [extra, setExtra] = React__namespace.useState("");
185
+ const baseId = React__namespace.useId();
186
+ const setValue = (name, value) => {
187
+ const next = { ...values, [name]: value };
188
+ if (valuesProp === void 0) setInternal(next);
189
+ setEdited((e) => ({ ...e, [name]: true }));
190
+ onValuesChange?.(next);
191
+ };
192
+ const hasAny = Object.values(values).some((v) => v && v.trim()) || extra.trim().length > 0;
193
+ const freeformLabel = typeof freeform === "string" ? freeform : "Anything else?";
194
+ return /* @__PURE__ */ jsxRuntime.jsxs(
195
+ "form",
196
+ {
197
+ ref,
198
+ "aria-label": label,
199
+ className: utils.cn("grid grid-cols-1 gap-3 sm:grid-cols-2", className),
200
+ onSubmit: (e) => {
201
+ e.preventDefault();
202
+ if (hasAny) onSubmit?.(values, extra);
203
+ },
204
+ ...props,
205
+ children: [
206
+ slots.map((slot) => {
207
+ const id = `${baseId}-${slot.name}`;
208
+ const helperId = `${id}-helper`;
209
+ const mark = slot.suggested ? edited[slot.name] ? { text: "Edited by you", variant: "secondary" } : { text: "Suggested", variant: "info" } : null;
210
+ const describedBy = slot.helper || mark && !edited[slot.name] ? helperId : void 0;
211
+ return /* @__PURE__ */ jsxRuntime.jsxs(field.Field, { className: utils.cn("gap-1.5", slot.wide && "sm:col-span-2"), children: [
212
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
213
+ /* @__PURE__ */ jsxRuntime.jsx(field.FieldLabel, { id: `${id}-label`, htmlFor: id, className: "text-xs font-semibold text-muted-foreground", children: slot.label }),
214
+ mark && /* @__PURE__ */ jsxRuntime.jsx(badge.Badge, { variant: mark.variant, children: mark.text })
215
+ ] }),
216
+ slot.options ? /* @__PURE__ */ jsxRuntime.jsxs(select.Select, { value: values[slot.name] || void 0, onValueChange: (v) => setValue(slot.name, v), children: [
217
+ /* @__PURE__ */ jsxRuntime.jsx(select.SelectTrigger, { id, "aria-labelledby": `${id}-label ${id}`, "aria-describedby": describedBy, children: /* @__PURE__ */ jsxRuntime.jsx(select.SelectValue, { placeholder: slot.placeholder ?? "Choose\u2026" }) }),
218
+ /* @__PURE__ */ jsxRuntime.jsx(select.SelectContent, { children: slot.options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(select.SelectItem, { value: o.value, children: o.label }, o.value)) })
219
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
220
+ input.Input,
221
+ {
222
+ id,
223
+ value: values[slot.name] ?? "",
224
+ placeholder: slot.placeholder,
225
+ "aria-describedby": describedBy,
226
+ onChange: (e) => setValue(slot.name, e.target.value)
227
+ }
228
+ ),
229
+ describedBy && /* @__PURE__ */ jsxRuntime.jsxs(field.FieldDescription, { id: helperId, className: "text-xs", children: [
230
+ mark && !edited[slot.name] ? `Suggested ${slot.suggested}. ` : "",
231
+ slot.helper
232
+ ] })
233
+ ] }, slot.name);
234
+ }),
235
+ freeform && /* @__PURE__ */ jsxRuntime.jsxs(field.Field, { className: "gap-1.5 sm:col-span-2", children: [
236
+ /* @__PURE__ */ jsxRuntime.jsx(field.FieldLabel, { htmlFor: `${baseId}-freeform`, className: "text-xs font-semibold text-muted-foreground", children: freeformLabel }),
237
+ /* @__PURE__ */ jsxRuntime.jsx(
238
+ textarea.Textarea,
239
+ {
240
+ id: `${baseId}-freeform`,
241
+ rows: 2,
242
+ value: extra,
243
+ placeholder: "Say it in your own words \u2014 this overrides the fields above",
244
+ onChange: (e) => setExtra(e.target.value)
245
+ }
246
+ )
247
+ ] }),
248
+ onSubmit && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end sm:col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "submit", variant: "cta", size: "sm", disabled: !hasAny, children: submitLabel }) })
249
+ ]
250
+ }
251
+ );
252
+ }
253
+ );
254
+ StructuredInputSlots.displayName = "StructuredInputSlots";
255
+ var DEFAULT_TRIGGERS = [
256
+ { trigger: "@", label: "Person" },
257
+ { trigger: "p:", label: "Project" },
258
+ { trigger: "/", label: "File" }
259
+ ];
260
+ var TAG_TONES = [
261
+ { variant: "secondary", className: "bg-brand-muted text-brand" },
262
+ { variant: "info" },
263
+ { variant: "success" },
264
+ { variant: "outline" }
265
+ ];
266
+ function structuredPartsToText(parts) {
267
+ return parts.map((p) => p.type === "text" ? p.text : `${p.trigger}${p.value}`).join(" ").replace(/\s+/g, " ").trim();
268
+ }
269
+ var StructuredInputTags = React__namespace.forwardRef(
270
+ ({
271
+ triggers = DEFAULT_TRIGGERS,
272
+ value: valueProp,
273
+ defaultValue,
274
+ onValueChange,
275
+ onSubmit,
276
+ placeholder = "Type @ for people, p: for projects, / for files",
277
+ submitLabel = "Send",
278
+ className,
279
+ ...props
280
+ }, ref) => {
281
+ const [internal, setInternal] = React__namespace.useState(defaultValue ?? []);
282
+ const parts = valueProp ?? internal;
283
+ const [draft, setDraft] = React__namespace.useState("");
284
+ const inputRef = React__namespace.useRef(null);
285
+ const hintId = React__namespace.useId();
286
+ const commit = (next) => {
287
+ if (valueProp === void 0) setInternal(next);
288
+ onValueChange?.(next);
289
+ };
290
+ const ordered = [...triggers].sort((a, b) => b.trigger.length - a.trigger.length);
291
+ const matchTag = (word) => {
292
+ const t = ordered.find((tr) => word.startsWith(tr.trigger) && word.length > tr.trigger.length);
293
+ return t ? { trigger: t.trigger, value: word.slice(t.trigger.length) } : null;
294
+ };
295
+ const lastWord = draft.split(" ").pop() ?? "";
296
+ const activeTrigger = ordered.find((tr) => lastWord.startsWith(tr.trigger));
297
+ const query = activeTrigger ? lastWord.slice(activeTrigger.trigger.length).toLowerCase() : "";
298
+ const suggestions = activeTrigger?.suggestions?.filter((s) => s.toLowerCase().startsWith(query)).slice(0, 5) ?? [];
299
+ const flush = (source = draft, forceTag) => {
300
+ const words = source.split(" ");
301
+ const last = words.pop() ?? "";
302
+ const tag = forceTag ?? matchTag(last);
303
+ const textBefore = (tag ? words.join(" ") : source).trim();
304
+ const next = [...parts];
305
+ if (textBefore) next.push({ type: "text", text: textBefore });
306
+ if (tag) next.push({ type: "tag", ...tag });
307
+ if (next.length !== parts.length) commit(next);
308
+ setDraft("");
309
+ return next;
310
+ };
311
+ const toneFor = (trigger) => {
312
+ const i = triggers.findIndex((t) => t.trigger === trigger);
313
+ return TAG_TONES[(i < 0 ? 0 : i) % TAG_TONES.length];
314
+ };
315
+ const labelFor = (trigger) => triggers.find((t) => t.trigger === trigger)?.label ?? "Tag";
316
+ const submit = () => {
317
+ const next = flush();
318
+ const text = structuredPartsToText(next);
319
+ if (text) onSubmit?.(next, text);
320
+ };
321
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: utils.cn("flex flex-col gap-2", className), ...props, children: [
322
+ /* @__PURE__ */ jsxRuntime.jsxs(
323
+ "div",
324
+ {
325
+ className: "flex flex-wrap items-center gap-1.5 rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground focus-within:ring-2 focus-within:ring-ring",
326
+ onClick: () => inputRef.current?.focus(),
327
+ children: [
328
+ parts.map(
329
+ (part, i) => part.type === "text" ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: part.text }, i) : /* @__PURE__ */ jsxRuntime.jsxs(
330
+ badge.Badge,
331
+ {
332
+ variant: toneFor(part.trigger).variant,
333
+ className: utils.cn("gap-0.5 rounded-md pr-0.5 font-semibold", toneFor(part.trigger).className),
334
+ children: [
335
+ part.trigger,
336
+ part.value,
337
+ /* @__PURE__ */ jsxRuntime.jsx(
338
+ "button",
339
+ {
340
+ type: "button",
341
+ "aria-label": `Remove ${labelFor(part.trigger).toLowerCase()} ${part.trigger}${part.value}`,
342
+ onClick: (e) => {
343
+ e.stopPropagation();
344
+ commit(parts.filter((_, j) => j !== i));
345
+ inputRef.current?.focus();
346
+ },
347
+ className: "inline-flex size-4 items-center justify-center rounded-sm opacity-70 hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
348
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": "true" })
349
+ }
350
+ )
351
+ ]
352
+ },
353
+ i
354
+ )
355
+ ),
356
+ /* @__PURE__ */ jsxRuntime.jsx(
357
+ input.Input,
358
+ {
359
+ ref: inputRef,
360
+ value: draft,
361
+ "aria-label": placeholder,
362
+ "aria-describedby": hintId,
363
+ placeholder: parts.length ? void 0 : placeholder,
364
+ onChange: (e) => {
365
+ const v = e.target.value;
366
+ if (v.endsWith(" ") && matchTag(v.trimEnd().split(" ").pop() ?? "")) flush(v.trimEnd());
367
+ else setDraft(v);
368
+ },
369
+ onKeyDown: (e) => {
370
+ if (e.key === " " && matchTag(lastWord)) {
371
+ e.preventDefault();
372
+ flush();
373
+ } else if (e.key === "Enter") {
374
+ e.preventDefault();
375
+ submit();
376
+ } else if (e.key === "Backspace" && draft === "" && parts.length) {
377
+ e.preventDefault();
378
+ const last = parts[parts.length - 1];
379
+ commit(parts.slice(0, -1));
380
+ if (last.type === "text") setDraft(last.text);
381
+ }
382
+ },
383
+ className: "h-7 min-w-[8rem] flex-1 border-0 px-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
384
+ }
385
+ )
386
+ ]
387
+ }
388
+ ),
389
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { id: hintId, className: "sr-only", children: [
390
+ triggers.map((t) => `${t.trigger} for ${t.label.toLowerCase()}`).join(", "),
391
+ ". Space turns a tag into a chip; Backspace removes the last one; Enter sends."
392
+ ] }),
393
+ activeTrigger && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", "aria-label": `${activeTrigger.label} suggestions`, className: "flex flex-wrap gap-1", children: suggestions.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
394
+ button.Button,
395
+ {
396
+ variant: "secondary",
397
+ size: "sm",
398
+ className: "h-7 px-2",
399
+ onClick: () => {
400
+ flush(draft, { trigger: activeTrigger.trigger, value: s });
401
+ inputRef.current?.focus();
402
+ },
403
+ children: [
404
+ activeTrigger.trigger,
405
+ s
406
+ ]
407
+ },
408
+ s
409
+ )) }),
410
+ onSubmit && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
411
+ button.Button,
412
+ {
413
+ variant: "cta",
414
+ size: "sm",
415
+ onClick: submit,
416
+ disabled: parts.length === 0 && draft.trim() === "",
417
+ children: submitLabel
418
+ }
419
+ ) })
420
+ ] });
421
+ }
422
+ );
423
+ StructuredInputTags.displayName = "StructuredInputTags";
424
+ var StructuredInputWizard = React__namespace.forwardRef(
425
+ ({
426
+ steps,
427
+ step: stepProp,
428
+ defaultStep = 0,
429
+ onStepChange,
430
+ description,
431
+ onSubmit,
432
+ submitLabel = "Generate",
433
+ allowEarlySubmit = true,
434
+ className,
435
+ children,
436
+ ...props
437
+ }, ref) => {
438
+ const [internal, setInternal] = React__namespace.useState(defaultStep);
439
+ const step = Math.min(Math.max(stepProp ?? internal, 0), Math.max(steps.length - 1, 0));
440
+ const headingRef = React__namespace.useRef(null);
441
+ const headingId = React__namespace.useId();
442
+ const moved = React__namespace.useRef(false);
443
+ React__namespace.useEffect(() => {
444
+ if (moved.current) headingRef.current?.focus();
445
+ }, [step]);
446
+ const go = (next) => {
447
+ moved.current = true;
448
+ if (stepProp === void 0) setInternal(next);
449
+ onStepChange?.(next);
450
+ };
451
+ const last = step === steps.length - 1;
452
+ return /* @__PURE__ */ jsxRuntime.jsxs(
453
+ "div",
454
+ {
455
+ ref,
456
+ role: "group",
457
+ "aria-labelledby": headingId,
458
+ className: utils.cn("flex flex-col gap-3 rounded-xl border border-border bg-background p-4", className),
459
+ ...props,
460
+ children: [
461
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1", "aria-hidden": "true", children: steps.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("h-1 flex-1 rounded-full", i <= step ? "bg-brand" : "bg-muted") }, i)) }),
462
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
463
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { ref: headingRef, id: headingId, tabIndex: -1, className: "text-sm font-semibold text-foreground outline-none", children: [
464
+ "Step ",
465
+ step + 1,
466
+ " of ",
467
+ steps.length,
468
+ " \xB7 ",
469
+ steps[step]
470
+ ] }),
471
+ description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: description })
472
+ ] }),
473
+ children,
474
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
475
+ step > 0 && /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "ghost", size: "sm", onClick: () => go(step - 1), children: "Back" }),
476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
477
+ !last && allowEarlySubmit && onSubmit && /* @__PURE__ */ jsxRuntime.jsxs(button.Button, { variant: "link", size: "sm", onClick: onSubmit, children: [
478
+ submitLabel,
479
+ " now"
480
+ ] }),
481
+ last ? onSubmit && /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "cta", size: "sm", onClick: onSubmit, children: submitLabel }) : /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "cta", size: "sm", onClick: () => go(step + 1), children: "Next" })
482
+ ] })
483
+ ]
484
+ }
485
+ );
486
+ }
487
+ );
488
+ StructuredInputWizard.displayName = "StructuredInputWizard";
146
489
 
147
490
  exports.StructuredInput = StructuredInput;
491
+ exports.StructuredInputSlots = StructuredInputSlots;
492
+ exports.StructuredInputTags = StructuredInputTags;
493
+ exports.StructuredInputWizard = StructuredInputWizard;
494
+ exports.structuredPartsToText = structuredPartsToText;
148
495
  //# sourceMappingURL=index.cjs.map
149
496
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":["React","jsxs","cn","jsx","Check"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@nucleux/utils\";\nimport { Check } from \"lucide-react\";\n\nexport interface StructuredOption {\n /** Stable value. */\n value: string;\n /** Display label. */\n label: React.ReactNode;\n}\n\nexport interface StructuredInputProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"onSubmit\"> {\n /** The clarifying question the agent is asking. */\n question: React.ReactNode;\n /** The selectable answers. */\n options: StructuredOption[];\n /** Allow selecting more than one option. */\n multiple?: boolean;\n /** Selected values (controlled). */\n value?: string[];\n /** Initial selection (uncontrolled). */\n defaultValue?: string[];\n /** Called when the selection changes. */\n onValueChange?: (values: string[]) => void;\n /** Show a free-text \"something else\" escape. */\n allowOther?: boolean;\n /** Label for the other escape. Defaults to `\"Something else…\"`. */\n otherPlaceholder?: string;\n /** Called when the answer is submitted. */\n onSubmit?: (values: string[], other?: string) => void;\n /** Called when the user skips the question. Renders the skip control when set. */\n onSkip?: () => void;\n submitLabel?: React.ReactNode;\n skipLabel?: React.ReactNode;\n}\n\n/**\n * An agent-generated clarifying question rendered as a structured choice —\n * single or multi-select options, a free-text escape, and a skip.\n * Source: Figma \"Expressive Input / Structured Input\".\n */\nexport const StructuredInput = React.forwardRef<HTMLDivElement, StructuredInputProps>(\n (\n {\n question,\n options,\n multiple = false,\n value,\n defaultValue = [],\n onValueChange,\n allowOther = false,\n otherPlaceholder = \"Something else…\",\n onSubmit,\n onSkip,\n submitLabel = \"Continue\",\n skipLabel = \"Skip\",\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<string[]>(defaultValue);\n const [other, setOther] = React.useState(\"\");\n const selected = value !== undefined ? value : internal;\n\n const setSelected = (next: string[]) => {\n if (value === undefined) setInternal(next);\n onValueChange?.(next);\n };\n const toggle = (val: string) => {\n if (multiple) {\n setSelected(selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]);\n } else {\n setSelected([val]);\n }\n };\n\n return (\n <div\n ref={ref}\n className={cn(\"flex flex-col gap-4 rounded-xl border border-border bg-background p-5\", className)}\n {...props}\n >\n <p className=\"text-base font-medium text-foreground\">{question}</p>\n <div\n role={multiple ? \"group\" : \"radiogroup\"}\n aria-label={typeof question === \"string\" ? question : \"Options\"}\n className=\"flex flex-col gap-2\"\n >\n {options.map((option) => {\n const active = selected.includes(option.value);\n return (\n <button\n key={option.value}\n type=\"button\"\n role={multiple ? \"checkbox\" : \"radio\"}\n aria-checked={active}\n onClick={() => toggle(option.value)}\n className={cn(\n \"flex items-center gap-3 rounded-lg border px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n active\n ? \"border-brand bg-brand-muted/50 text-foreground\"\n : \"border-border bg-background text-foreground hover:bg-accent\",\n )}\n >\n <span\n className={cn(\n \"inline-flex size-5 shrink-0 items-center justify-center border transition-colors [&_svg]:size-3.5\",\n multiple ? \"rounded-md\" : \"rounded-full\",\n active ? \"border-brand bg-brand text-brand-foreground\" : \"border-border\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n </span>\n <span className=\"flex-1\">{option.label}</span>\n </button>\n );\n })}\n </div>\n {allowOther && (\n <input\n type=\"text\"\n value={other}\n onChange={(e) => setOther(e.target.value)}\n placeholder={otherPlaceholder}\n aria-label={otherPlaceholder}\n className=\"rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n )}\n <div className=\"flex items-center justify-end gap-2\">\n {onSkip && (\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"inline-flex items-center rounded-lg px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {skipLabel}\n </button>\n )}\n {onSubmit && (\n <button\n type=\"button\"\n onClick={() => onSubmit(selected, allowOther ? other : undefined)}\n disabled={selected.length === 0 && !(allowOther && other.trim())}\n className=\"inline-flex items-center rounded-lg bg-info px-3 py-2 text-sm font-medium text-info-foreground transition-colors hover:bg-info/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\"\n >\n {submitLabel}\n </button>\n )}\n </div>\n </div>\n );\n },\n);\nStructuredInput.displayName = \"StructuredInput\";\n"]}
1
+ {"version":3,"sources":["../src/index.tsx"],"names":["React","jsxs","cn","jsx","Check","Field","FieldLabel","Badge","Select","SelectTrigger","SelectValue","SelectContent","SelectItem","Input","FieldDescription","Textarea","Button","X"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@nucleux/utils\";\nimport { Check, X } from \"lucide-react\";\nimport { Badge, type BadgeVariant } from \"@nucleux/badge\";\nimport { Button } from \"@nucleux/button\";\nimport { Field, FieldDescription, FieldLabel } from \"@nucleux/field\";\nimport { Input } from \"@nucleux/input\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@nucleux/select\";\nimport { Textarea } from \"@nucleux/textarea\";\n\nexport interface StructuredOption {\n /** Stable value. */\n value: string;\n /** Display label. */\n label: React.ReactNode;\n}\n\nexport interface StructuredInputProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\" | \"onSubmit\"> {\n /** The clarifying question the agent is asking. */\n question: React.ReactNode;\n /** The selectable answers. */\n options: StructuredOption[];\n /** Allow selecting more than one option. */\n multiple?: boolean;\n /** Selected values (controlled). */\n value?: string[];\n /** Initial selection (uncontrolled). */\n defaultValue?: string[];\n /** Called when the selection changes. */\n onValueChange?: (values: string[]) => void;\n /** Show a free-text \"something else\" escape. */\n allowOther?: boolean;\n /** Label for the other escape. Defaults to `\"Something else…\"`. */\n otherPlaceholder?: string;\n /** Called when the answer is submitted. */\n onSubmit?: (values: string[], other?: string) => void;\n /** Called when the user skips the question. Renders the skip control when set. */\n onSkip?: () => void;\n /** Label for the submit button. */\n submitLabel?: React.ReactNode;\n /** Label for the skip button. */\n skipLabel?: React.ReactNode;\n}\n\n/**\n * An agent-generated clarifying question rendered as a structured choice —\n * single or multi-select options, a free-text escape, and a skip. The other\n * structured-input variants are composable siblings: {@link StructuredInputSlots}\n * (named slots), {@link StructuredInputTags} (tagged input) and\n * {@link StructuredInputWizard} (wizard step).\n * Source: Figma \"Expressive Input / Structured Input\".\n */\nexport const StructuredInput = React.forwardRef<HTMLDivElement, StructuredInputProps>(\n (\n {\n question,\n options,\n multiple = false,\n value,\n defaultValue = [],\n onValueChange,\n allowOther = false,\n otherPlaceholder = \"Something else…\",\n onSubmit,\n onSkip,\n submitLabel = \"Continue\",\n skipLabel = \"Skip\",\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<string[]>(defaultValue);\n const [other, setOther] = React.useState(\"\");\n const questionId = React.useId();\n const groupRef = React.useRef<HTMLDivElement>(null);\n const selected = value !== undefined ? value : internal;\n\n const setSelected = (next: string[]) => {\n if (value === undefined) setInternal(next);\n onValueChange?.(next);\n };\n const toggle = (val: string) => {\n if (multiple) {\n setSelected(selected.includes(val) ? selected.filter((v) => v !== val) : [...selected, val]);\n } else {\n setSelected([val]);\n }\n };\n\n return (\n <div\n ref={ref}\n className={cn(\"flex flex-col gap-4 rounded-xl border border-border bg-background p-5\", className)}\n {...props}\n >\n <p id={questionId} className=\"text-base font-medium text-foreground\">\n {question}\n </p>\n <div\n ref={groupRef}\n role={multiple ? \"group\" : \"radiogroup\"}\n aria-labelledby={questionId}\n className=\"flex flex-col gap-2\"\n onKeyDown={(e) => {\n // Radio-group keyboard pattern: arrows move and select.\n if (multiple || ![\"ArrowDown\", \"ArrowUp\", \"ArrowRight\", \"ArrowLeft\"].includes(e.key)) return;\n const radios = Array.from(groupRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]') ?? []);\n const i = radios.indexOf(document.activeElement as HTMLButtonElement);\n if (i < 0) return;\n e.preventDefault();\n const step = e.key === \"ArrowDown\" || e.key === \"ArrowRight\" ? 1 : -1;\n const next = (i + step + radios.length) % radios.length;\n radios[next].focus();\n setSelected([options[next].value]);\n }}\n >\n {options.map((option, index) => {\n const active = selected.includes(option.value);\n const tabbable = multiple || index === Math.max(0, options.findIndex((o) => selected.includes(o.value)));\n return (\n <button\n key={option.value}\n type=\"button\"\n role={multiple ? \"checkbox\" : \"radio\"}\n aria-checked={active}\n tabIndex={tabbable ? 0 : -1}\n onClick={() => toggle(option.value)}\n className={cn(\n \"flex items-center gap-3 rounded-lg border px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n active\n ? \"border-brand bg-brand-muted/50 text-foreground\"\n : \"border-border bg-background text-foreground hover:bg-accent\",\n )}\n >\n <span\n className={cn(\n \"inline-flex size-5 shrink-0 items-center justify-center border transition-colors [&_svg]:size-3.5\",\n multiple ? \"rounded-md\" : \"rounded-full\",\n active ? \"border-brand bg-brand text-brand-foreground\" : \"border-border\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n </span>\n <span className=\"flex-1\">{option.label}</span>\n </button>\n );\n })}\n </div>\n {allowOther && (\n <input\n type=\"text\"\n value={other}\n onChange={(e) => setOther(e.target.value)}\n placeholder={otherPlaceholder}\n aria-label={otherPlaceholder}\n className=\"rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n )}\n <div className=\"flex items-center justify-end gap-2\">\n {onSkip && (\n <button\n type=\"button\"\n onClick={onSkip}\n className=\"inline-flex items-center rounded-lg px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {skipLabel}\n </button>\n )}\n {onSubmit && (\n <button\n type=\"button\"\n onClick={() => onSubmit(selected, allowOther ? other : undefined)}\n disabled={selected.length === 0 && !(allowOther && other.trim())}\n className=\"inline-flex items-center rounded-lg bg-info px-3 py-2 text-sm font-medium text-info-foreground transition-colors hover:bg-info/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\"\n >\n {submitLabel}\n </button>\n )}\n </div>\n </div>\n );\n },\n);\nStructuredInput.displayName = \"StructuredInput\";\n\n/* -------------------------------------------------------------------------- */\n/* Named slots */\n/* -------------------------------------------------------------------------- */\n\nexport interface StructuredInputSlot {\n /** Key in the values object. */\n name: string;\n /** Plain-language label, e.g. `\"Audience\"`. */\n label: string;\n /** Placeholder / example value. */\n placeholder?: string;\n /** Choices — renders a select instead of a text field. */\n options?: StructuredOption[];\n /** Helper text under the field. */\n helper?: string;\n /**\n * Marks the initial value as the agent's suggestion, with the reason, e.g.\n * `\"from the meeting invite\"`. Once the person changes it, it reads “Edited by you”.\n */\n suggested?: string;\n /** Span the full row. */\n wide?: boolean;\n}\n\nexport type StructuredInputValues = Record<string, string>;\n\nexport interface StructuredInputSlotsProps\n extends Omit<React.HTMLAttributes<HTMLFormElement>, \"onSubmit\" | \"defaultValue\"> {\n /** The 2–4 slots the prompt template needs. */\n slots: StructuredInputSlot[];\n /** Values (controlled). */\n values?: StructuredInputValues;\n /** Initial values (uncontrolled). */\n defaultValues?: StructuredInputValues;\n /** Called on every change. */\n onValuesChange?: (values: StructuredInputValues) => void;\n /** Show a freeform override field. `true` uses the label `\"Anything else?\"`; a string sets the label. */\n freeform?: boolean | string;\n /** Called on submit. Never requires every slot — any value (or the freeform text) is enough. */\n onSubmit?: (values: StructuredInputValues, freeform: string) => void;\n /** Label of the submit button. Defaults to `\"Generate\"`. */\n submitLabel?: React.ReactNode;\n /** Accessible name of the form. Defaults to `\"Structured prompt builder\"`. */\n label?: string;\n}\n\n/**\n * Named slots: a short set of plain-language fields (2–4) that fill a prompt\n * template — text fields or selects, an optional freeform override, and a\n * submit that never requires every field. Agent-suggested values are marked\n * “Suggested” with their reason and become “Edited by you” once changed; the\n * component never changes a value the person set.\n */\nexport const StructuredInputSlots = React.forwardRef<HTMLFormElement, StructuredInputSlotsProps>(\n (\n {\n slots,\n values: valuesProp,\n defaultValues,\n onValuesChange,\n freeform = false,\n onSubmit,\n submitLabel = \"Generate\",\n label = \"Structured prompt builder\",\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<StructuredInputValues>(defaultValues ?? {});\n const values = valuesProp ?? internal;\n const [edited, setEdited] = React.useState<Record<string, boolean>>({});\n const [extra, setExtra] = React.useState(\"\");\n const baseId = React.useId();\n\n const setValue = (name: string, value: string) => {\n const next = { ...values, [name]: value };\n if (valuesProp === undefined) setInternal(next);\n setEdited((e) => ({ ...e, [name]: true }));\n onValuesChange?.(next);\n };\n\n const hasAny = Object.values(values).some((v) => v && v.trim()) || extra.trim().length > 0;\n const freeformLabel = typeof freeform === \"string\" ? freeform : \"Anything else?\";\n\n return (\n <form\n ref={ref}\n aria-label={label}\n className={cn(\"grid grid-cols-1 gap-3 sm:grid-cols-2\", className)}\n onSubmit={(e) => {\n e.preventDefault();\n if (hasAny) onSubmit?.(values, extra);\n }}\n {...props}\n >\n {slots.map((slot) => {\n const id = `${baseId}-${slot.name}`;\n const helperId = `${id}-helper`;\n const mark = slot.suggested\n ? edited[slot.name]\n ? { text: \"Edited by you\", variant: \"secondary\" as const }\n : { text: \"Suggested\", variant: \"info\" as const }\n : null;\n const describedBy = slot.helper || (mark && !edited[slot.name]) ? helperId : undefined;\n return (\n <Field key={slot.name} className={cn(\"gap-1.5\", slot.wide && \"sm:col-span-2\")}>\n <div className=\"flex items-center gap-2\">\n <FieldLabel id={`${id}-label`} htmlFor={id} className=\"text-xs font-semibold text-muted-foreground\">\n {slot.label}\n </FieldLabel>\n {mark && <Badge variant={mark.variant}>{mark.text}</Badge>}\n </div>\n {slot.options ? (\n <Select value={values[slot.name] || undefined} onValueChange={(v) => setValue(slot.name, v)}>\n <SelectTrigger id={id} aria-labelledby={`${id}-label ${id}`} aria-describedby={describedBy}>\n <SelectValue placeholder={slot.placeholder ?? \"Choose…\"} />\n </SelectTrigger>\n <SelectContent>\n {slot.options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : (\n <Input\n id={id}\n value={values[slot.name] ?? \"\"}\n placeholder={slot.placeholder}\n aria-describedby={describedBy}\n onChange={(e) => setValue(slot.name, e.target.value)}\n />\n )}\n {describedBy && (\n <FieldDescription id={helperId} className=\"text-xs\">\n {mark && !edited[slot.name] ? `Suggested ${slot.suggested}. ` : \"\"}\n {slot.helper}\n </FieldDescription>\n )}\n </Field>\n );\n })}\n {freeform && (\n <Field className=\"gap-1.5 sm:col-span-2\">\n <FieldLabel htmlFor={`${baseId}-freeform`} className=\"text-xs font-semibold text-muted-foreground\">\n {freeformLabel}\n </FieldLabel>\n <Textarea\n id={`${baseId}-freeform`}\n rows={2}\n value={extra}\n placeholder=\"Say it in your own words — this overrides the fields above\"\n onChange={(e) => setExtra(e.target.value)}\n />\n </Field>\n )}\n {onSubmit && (\n <div className=\"flex justify-end sm:col-span-2\">\n <Button type=\"submit\" variant=\"cta\" size=\"sm\" disabled={!hasAny}>\n {submitLabel}\n </Button>\n </div>\n )}\n </form>\n );\n },\n);\nStructuredInputSlots.displayName = \"StructuredInputSlots\";\n\n/* -------------------------------------------------------------------------- */\n/* Tagged input */\n/* -------------------------------------------------------------------------- */\n\nexport interface StructuredInputTagTrigger {\n /** The prefix that starts a tag, e.g. `\"@\"`, `\"p:\"`, `\"/\"`. */\n trigger: string;\n /** What the tag refers to, e.g. `\"Person\"`. */\n label: string;\n /** Values offered while typing after the trigger. */\n suggestions?: string[];\n}\n\nexport type StructuredInputPart =\n | { type: \"text\"; text: string }\n | { type: \"tag\"; trigger: string; value: string };\n\nexport interface StructuredInputTagsProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onSubmit\" | \"defaultValue\" | \"onChange\"> {\n /** Recognised tag prefixes. Defaults to `@` person, `p:` project, `/` file. */\n triggers?: StructuredInputTagTrigger[];\n /** Committed parts (controlled). */\n value?: StructuredInputPart[];\n /** Initial parts (uncontrolled). */\n defaultValue?: StructuredInputPart[];\n /** Called when parts are added or removed. */\n onValueChange?: (parts: StructuredInputPart[]) => void;\n /** Called on Enter or the send button with the parts and their plain-text form. */\n onSubmit?: (parts: StructuredInputPart[], text: string) => void;\n /** Accessible name and placeholder of the text field. */\n placeholder?: string;\n /** Label of the send button. Defaults to `\"Send\"`. */\n submitLabel?: React.ReactNode;\n}\n\nconst DEFAULT_TRIGGERS: StructuredInputTagTrigger[] = [\n { trigger: \"@\", label: \"Person\" },\n { trigger: \"p:\", label: \"Project\" },\n { trigger: \"/\", label: \"File\" },\n];\n\nconst TAG_TONES: { variant: BadgeVariant; className?: string }[] = [\n { variant: \"secondary\", className: \"bg-brand-muted text-brand\" },\n { variant: \"info\" },\n { variant: \"success\" },\n { variant: \"outline\" },\n];\n\n/** Serialise parts into the plain prompt text, e.g. `\"Update @sam on p:Apollo\"`. */\nexport function structuredPartsToText(parts: StructuredInputPart[]): string {\n return parts\n .map((p) => (p.type === \"text\" ? p.text : `${p.trigger}${p.value}`))\n .join(\" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * Tagged input: a prompt field where typed prefixes (`@` person, `p:` project,\n * `/` file — configurable) become removable tag chips, with optional\n * type-ahead suggestions shown as buttons. Backspace on an empty field removes\n * the last tag; Enter sends. Tags are optional — plain text always works.\n */\nexport const StructuredInputTags = React.forwardRef<HTMLDivElement, StructuredInputTagsProps>(\n (\n {\n triggers = DEFAULT_TRIGGERS,\n value: valueProp,\n defaultValue,\n onValueChange,\n onSubmit,\n placeholder = \"Type @ for people, p: for projects, / for files\",\n submitLabel = \"Send\",\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<StructuredInputPart[]>(defaultValue ?? []);\n const parts = valueProp ?? internal;\n const [draft, setDraft] = React.useState(\"\");\n const inputRef = React.useRef<HTMLInputElement>(null);\n const hintId = React.useId();\n\n const commit = (next: StructuredInputPart[]) => {\n if (valueProp === undefined) setInternal(next);\n onValueChange?.(next);\n };\n\n // Longest trigger first so \"p:\" wins over \"p\".\n const ordered = [...triggers].sort((a, b) => b.trigger.length - a.trigger.length);\n const matchTag = (word: string) => {\n const t = ordered.find((tr) => word.startsWith(tr.trigger) && word.length > tr.trigger.length);\n return t ? { trigger: t.trigger, value: word.slice(t.trigger.length) } : null;\n };\n\n const lastWord = draft.split(\" \").pop() ?? \"\";\n const activeTrigger = ordered.find((tr) => lastWord.startsWith(tr.trigger));\n const query = activeTrigger ? lastWord.slice(activeTrigger.trigger.length).toLowerCase() : \"\";\n const suggestions =\n activeTrigger?.suggestions?.filter((s) => s.toLowerCase().startsWith(query)).slice(0, 5) ?? [];\n\n /** Split the draft into text + a trailing tag (if any) and commit them. */\n const flush = (source = draft, forceTag?: { trigger: string; value: string }): StructuredInputPart[] => {\n const words = source.split(\" \");\n const last = words.pop() ?? \"\";\n const tag = forceTag ?? matchTag(last);\n const textBefore = (tag ? words.join(\" \") : source).trim();\n const next = [...parts];\n if (textBefore) next.push({ type: \"text\", text: textBefore });\n if (tag) next.push({ type: \"tag\", ...tag });\n if (next.length !== parts.length) commit(next);\n setDraft(\"\");\n return next;\n };\n\n const toneFor = (trigger: string) => {\n const i = triggers.findIndex((t) => t.trigger === trigger);\n return TAG_TONES[(i < 0 ? 0 : i) % TAG_TONES.length];\n };\n const labelFor = (trigger: string) => triggers.find((t) => t.trigger === trigger)?.label ?? \"Tag\";\n\n const submit = () => {\n const next = flush();\n const text = structuredPartsToText(next);\n if (text) onSubmit?.(next, text);\n };\n\n return (\n <div ref={ref} className={cn(\"flex flex-col gap-2\", className)} {...props}>\n <div\n className=\"flex flex-wrap items-center gap-1.5 rounded-lg border border-input bg-background px-3 py-2 text-sm text-foreground focus-within:ring-2 focus-within:ring-ring\"\n onClick={() => inputRef.current?.focus()}\n >\n {parts.map((part, i) =>\n part.type === \"text\" ? (\n <span key={i}>{part.text}</span>\n ) : (\n <Badge\n key={i}\n variant={toneFor(part.trigger).variant}\n className={cn(\"gap-0.5 rounded-md pr-0.5 font-semibold\", toneFor(part.trigger).className)}\n >\n {part.trigger}\n {part.value}\n <button\n type=\"button\"\n aria-label={`Remove ${labelFor(part.trigger).toLowerCase()} ${part.trigger}${part.value}`}\n onClick={(e) => {\n e.stopPropagation();\n commit(parts.filter((_, j) => j !== i));\n inputRef.current?.focus();\n }}\n className=\"inline-flex size-4 items-center justify-center rounded-sm opacity-70 hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3\"\n >\n <X aria-hidden=\"true\" />\n </button>\n </Badge>\n ),\n )}\n <Input\n ref={inputRef}\n value={draft}\n aria-label={placeholder}\n aria-describedby={hintId}\n placeholder={parts.length ? undefined : placeholder}\n onChange={(e) => {\n const v = e.target.value;\n // A space after a tag word (incl. mobile keyboards that skip keydown) makes it a chip.\n if (v.endsWith(\" \") && matchTag(v.trimEnd().split(\" \").pop() ?? \"\")) flush(v.trimEnd());\n else setDraft(v);\n }}\n onKeyDown={(e) => {\n if (e.key === \" \" && matchTag(lastWord)) {\n e.preventDefault();\n flush();\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n submit();\n } else if (e.key === \"Backspace\" && draft === \"\" && parts.length) {\n e.preventDefault();\n const last = parts[parts.length - 1];\n commit(parts.slice(0, -1));\n if (last.type === \"text\") setDraft(last.text);\n }\n }}\n className=\"h-7 min-w-[8rem] flex-1 border-0 px-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0\"\n />\n </div>\n <p id={hintId} className=\"sr-only\">\n {triggers.map((t) => `${t.trigger} for ${t.label.toLowerCase()}`).join(\", \")}. Space turns a tag into a\n chip; Backspace removes the last one; Enter sends.\n </p>\n {activeTrigger && suggestions.length > 0 && (\n <div role=\"group\" aria-label={`${activeTrigger.label} suggestions`} className=\"flex flex-wrap gap-1\">\n {suggestions.map((s) => (\n <Button\n key={s}\n variant=\"secondary\"\n size=\"sm\"\n className=\"h-7 px-2\"\n onClick={() => {\n flush(draft, { trigger: activeTrigger.trigger, value: s });\n inputRef.current?.focus();\n }}\n >\n {activeTrigger.trigger}\n {s}\n </Button>\n ))}\n </div>\n )}\n {onSubmit && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"cta\"\n size=\"sm\"\n onClick={submit}\n disabled={parts.length === 0 && draft.trim() === \"\"}\n >\n {submitLabel}\n </Button>\n </div>\n )}\n </div>\n );\n },\n);\nStructuredInputTags.displayName = \"StructuredInputTags\";\n\n/* -------------------------------------------------------------------------- */\n/* Wizard step */\n/* -------------------------------------------------------------------------- */\n\nexport interface StructuredInputWizardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onSubmit\" | \"title\"> {\n /** Step titles, e.g. `[\"Intent\", \"Audience\", \"Tone\"]`. */\n steps: string[];\n /** Current step, 0-based (controlled). */\n step?: number;\n /** Initial step (uncontrolled). */\n defaultStep?: number;\n /** Called when Back / Next move. */\n onStepChange?: (step: number) => void;\n /** The current step's question, e.g. `\"Who is the brief for?\"`. */\n description?: React.ReactNode;\n /** Called from the last step (or from “Generate now” when `allowEarlySubmit`). */\n onSubmit?: () => void;\n /** Label of the final button. Defaults to `\"Generate\"`. */\n submitLabel?: React.ReactNode;\n /** Offer “Generate now” before the last step — fields are never all required. Defaults to `true`. */\n allowEarlySubmit?: boolean;\n /** The current step's fields. */\n children?: React.ReactNode;\n}\n\n/**\n * Wizard step: a multi-step frame for high-stakes prompts (legal, HR, medical) —\n * a progress bar, “Step 2 of 3 · Audience”, the step's question and fields\n * (children), and Back / Next / Generate. Focus moves to the step heading when\n * the step changes; nothing forces every step to be filled before generating.\n */\nexport const StructuredInputWizard = React.forwardRef<HTMLDivElement, StructuredInputWizardProps>(\n (\n {\n steps,\n step: stepProp,\n defaultStep = 0,\n onStepChange,\n description,\n onSubmit,\n submitLabel = \"Generate\",\n allowEarlySubmit = true,\n className,\n children,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState(defaultStep);\n const step = Math.min(Math.max(stepProp ?? internal, 0), Math.max(steps.length - 1, 0));\n const headingRef = React.useRef<HTMLParagraphElement>(null);\n const headingId = React.useId();\n const moved = React.useRef(false);\n\n React.useEffect(() => {\n if (moved.current) headingRef.current?.focus();\n }, [step]);\n\n const go = (next: number) => {\n moved.current = true;\n if (stepProp === undefined) setInternal(next);\n onStepChange?.(next);\n };\n\n const last = step === steps.length - 1;\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-labelledby={headingId}\n className={cn(\"flex flex-col gap-3 rounded-xl border border-border bg-background p-4\", className)}\n {...props}\n >\n <div className=\"flex gap-1\" aria-hidden=\"true\">\n {steps.map((_, i) => (\n <span key={i} className={cn(\"h-1 flex-1 rounded-full\", i <= step ? \"bg-brand\" : \"bg-muted\")} />\n ))}\n </div>\n <div className=\"flex flex-col gap-1\">\n <p ref={headingRef} id={headingId} tabIndex={-1} className=\"text-sm font-semibold text-foreground outline-none\">\n Step {step + 1} of {steps.length} · {steps[step]}\n </p>\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {children}\n <div className=\"flex items-center gap-2\">\n {step > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => go(step - 1)}>\n Back\n </Button>\n )}\n <span className=\"flex-1\" />\n {!last && allowEarlySubmit && onSubmit && (\n <Button variant=\"link\" size=\"sm\" onClick={onSubmit}>\n {submitLabel} now\n </Button>\n )}\n {last ? (\n onSubmit && (\n <Button variant=\"cta\" size=\"sm\" onClick={onSubmit}>\n {submitLabel}\n </Button>\n )\n ) : (\n <Button variant=\"cta\" size=\"sm\" onClick={() => go(step + 1)}>\n Next\n </Button>\n )}\n </div>\n </div>\n );\n },\n);\nStructuredInputWizard.displayName = \"StructuredInputWizard\";\n"]}