@epicbagel/saas-kit 1.5.0 → 1.7.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.
Files changed (138) hide show
  1. package/README.md +33 -1
  2. package/dist/{chunk-GFG2R5BZ.js → chunk-2V7AVB2U.js} +1 -1
  3. package/dist/{chunk-LI5EOSQG.js → chunk-43UMPZWD.js} +1 -1
  4. package/dist/{chunk-XABXUP6L.cjs → chunk-5VMKDVB6.cjs} +2 -2
  5. package/dist/{chunk-QNZYQLNP.js → chunk-6S6GD43V.js} +1 -1
  6. package/dist/{chunk-WQJKUKBL.cjs → chunk-6YXKFVZU.cjs} +4 -4
  7. package/dist/{chunk-RIWLAMME.cjs → chunk-7LDJAXRX.cjs} +2 -2
  8. package/dist/{chunk-FNDBM67K.cjs → chunk-7YCKII7G.cjs} +2 -2
  9. package/dist/{chunk-2TKLS73N.js → chunk-C3VZMTVB.js} +1 -1
  10. package/dist/{chunk-G54TQV2R.cjs → chunk-CLYAASFN.cjs} +2 -2
  11. package/dist/{chunk-SGW4FWBX.cjs → chunk-CRTH5GJ5.cjs} +13 -13
  12. package/dist/{chunk-EZCKWTH7.js → chunk-CYDICVH2.js} +1 -1
  13. package/dist/{chunk-J3GQXM37.js → chunk-EEHIMPD7.js} +1 -1
  14. package/dist/{chunk-4NN7EA3W.js → chunk-ET4THLFX.js} +1 -1
  15. package/dist/{chunk-PNWNU6JW.cjs → chunk-EUFV25KL.cjs} +4 -4
  16. package/dist/{chunk-HHV4G77I.js → chunk-F3B4M6DU.js} +1 -1
  17. package/dist/{chunk-32Q4XOTU.cjs → chunk-FYRQFEU4.cjs} +2 -2
  18. package/dist/{chunk-VLXPLKFR.cjs → chunk-GTVSRRE3.cjs} +2 -2
  19. package/dist/{chunk-RRRD5EUI.js → chunk-H2CCBRVG.js} +1 -1
  20. package/dist/{chunk-FGISSYXG.js → chunk-H4XH2PPU.js} +1 -1
  21. package/dist/{chunk-M7FYGAGO.js → chunk-HBRVOGLA.js} +1 -1
  22. package/dist/{chunk-NW3OWKBN.js → chunk-HREEZUKL.js} +1 -1
  23. package/dist/{chunk-THWIJG5C.js → chunk-IJG2B3Y2.js} +1 -1
  24. package/dist/{chunk-L4DH72UF.cjs → chunk-JDEXZGIY.cjs} +2 -1
  25. package/dist/{chunk-HRLP4BHJ.js → chunk-JN6JOSZG.js} +4 -4
  26. package/dist/{chunk-NE3ZVJ6G.cjs → chunk-KM5FHCXU.cjs} +8 -8
  27. package/dist/{chunk-MLHNYRFX.js → chunk-KNKV25WS.js} +2 -2
  28. package/dist/{chunk-23UMHZ4S.cjs → chunk-LIUYHYFN.cjs} +3 -3
  29. package/dist/{chunk-EOUO3ORO.cjs → chunk-LKPM6DQP.cjs} +3 -3
  30. package/dist/{chunk-SUB4BAYE.cjs → chunk-M4V7OJNF.cjs} +2 -2
  31. package/dist/{chunk-VKIGJNJR.cjs → chunk-MU4F3DSW.cjs} +2 -2
  32. package/dist/{chunk-LAN3K2JI.cjs → chunk-NWDPH2Z7.cjs} +2 -2
  33. package/dist/{chunk-MHYAVXOE.js → chunk-QTPPNK6F.js} +1 -1
  34. package/dist/{chunk-IH3COANJ.cjs → chunk-QTWJQQDH.cjs} +12 -12
  35. package/dist/chunk-QZBPZLAB.js +24 -0
  36. package/dist/{chunk-HAC32ALJ.js → chunk-TI57GAQP.js} +1 -1
  37. package/dist/{chunk-GRY77463.cjs → chunk-TP74SBRH.cjs} +3 -3
  38. package/dist/chunk-VMXKT67E.cjs +24 -0
  39. package/dist/{chunk-R5L4CM6V.cjs → chunk-VOTCYG3F.cjs} +4 -4
  40. package/dist/{chunk-HQRWRRZJ.js → chunk-W326GKML.js} +2 -1
  41. package/dist/{chunk-HYSWE75T.js → chunk-WL73DFE3.js} +1 -1
  42. package/dist/{chunk-KADJ56AH.cjs → chunk-Z3JJSNR5.cjs} +2 -2
  43. package/dist/{chunk-FLPYZNWZ.js → chunk-ZLU32SRP.js} +1 -1
  44. package/dist/primitives/activity-feed.cjs +3 -3
  45. package/dist/primitives/activity-feed.js +2 -2
  46. package/dist/primitives/activity-log.cjs +3 -3
  47. package/dist/primitives/activity-log.js +2 -2
  48. package/dist/primitives/alert-dialog.cjs +3 -3
  49. package/dist/primitives/alert-dialog.js +2 -2
  50. package/dist/primitives/alert.d.cts +1 -1
  51. package/dist/primitives/alert.d.ts +1 -1
  52. package/dist/primitives/archive-confirmation-dialog.cjs +4 -4
  53. package/dist/primitives/archive-confirmation-dialog.js +3 -3
  54. package/dist/primitives/attachment.cjs +3 -3
  55. package/dist/primitives/attachment.d.cts +1 -1
  56. package/dist/primitives/attachment.d.ts +1 -1
  57. package/dist/primitives/attachment.js +2 -2
  58. package/dist/primitives/badge.d.cts +1 -1
  59. package/dist/primitives/badge.d.ts +1 -1
  60. package/dist/primitives/board-view.cjs +7 -7
  61. package/dist/primitives/board-view.js +6 -6
  62. package/dist/primitives/bubble.d.cts +1 -1
  63. package/dist/primitives/bubble.d.ts +1 -1
  64. package/dist/primitives/button.cjs +2 -2
  65. package/dist/primitives/button.d.cts +3 -3
  66. package/dist/primitives/button.d.ts +3 -3
  67. package/dist/primitives/button.js +1 -1
  68. package/dist/primitives/calendar.cjs +3 -3
  69. package/dist/primitives/calendar.js +2 -2
  70. package/dist/primitives/chat.cjs +155 -80
  71. package/dist/primitives/chat.d.cts +32 -3
  72. package/dist/primitives/chat.d.ts +32 -3
  73. package/dist/primitives/chat.js +120 -45
  74. package/dist/primitives/code-block.cjs +4 -4
  75. package/dist/primitives/code-block.js +3 -3
  76. package/dist/primitives/confirm-bar.cjs +3 -3
  77. package/dist/primitives/confirm-bar.js +1 -1
  78. package/dist/primitives/connection-status-bar.cjs +4 -4
  79. package/dist/primitives/connection-status-bar.js +1 -1
  80. package/dist/primitives/connection.cjs +13 -13
  81. package/dist/primitives/connection.js +3 -3
  82. package/dist/primitives/copy-button.cjs +3 -3
  83. package/dist/primitives/copy-button.js +2 -2
  84. package/dist/primitives/decision-actions.cjs +2 -2
  85. package/dist/primitives/decision-actions.js +1 -1
  86. package/dist/primitives/delete-confirmation-dialog.cjs +4 -4
  87. package/dist/primitives/delete-confirmation-dialog.js +3 -3
  88. package/dist/primitives/document-preview.cjs +4 -4
  89. package/dist/primitives/document-preview.js +2 -2
  90. package/dist/primitives/editable-list.cjs +6 -6
  91. package/dist/primitives/editable-list.js +1 -1
  92. package/dist/primitives/empty-state.cjs +3 -3
  93. package/dist/primitives/empty-state.js +2 -2
  94. package/dist/primitives/file-dropzone.cjs +3 -3
  95. package/dist/primitives/file-dropzone.js +2 -2
  96. package/dist/primitives/file-preview-internals.cjs +3 -3
  97. package/dist/primitives/file-preview-internals.js +2 -2
  98. package/dist/primitives/file-preview.cjs +15 -15
  99. package/dist/primitives/file-preview.js +4 -4
  100. package/dist/primitives/filter-bar.cjs +4 -4
  101. package/dist/primitives/filter-bar.js +3 -3
  102. package/dist/primitives/input-group.cjs +3 -3
  103. package/dist/primitives/input-group.js +2 -2
  104. package/dist/primitives/item.d.cts +2 -2
  105. package/dist/primitives/item.d.ts +2 -2
  106. package/dist/primitives/load-more-button.cjs +3 -3
  107. package/dist/primitives/load-more-button.js +2 -2
  108. package/dist/primitives/message-scroller.cjs +3 -3
  109. package/dist/primitives/message-scroller.js +2 -2
  110. package/dist/primitives/message.cjs +3 -3
  111. package/dist/primitives/message.js +2 -2
  112. package/dist/primitives/page-header-actions.cjs +3 -3
  113. package/dist/primitives/page-header-actions.js +2 -2
  114. package/dist/primitives/quick-filters.cjs +3 -3
  115. package/dist/primitives/quick-filters.js +2 -2
  116. package/dist/primitives/resource-list.cjs +7 -7
  117. package/dist/primitives/resource-list.js +6 -6
  118. package/dist/primitives/search-input.cjs +101 -0
  119. package/dist/primitives/search-input.d.cts +23 -0
  120. package/dist/primitives/search-input.d.ts +23 -0
  121. package/dist/primitives/search-input.js +101 -0
  122. package/dist/primitives/section-header.d.cts +1 -1
  123. package/dist/primitives/section-header.d.ts +1 -1
  124. package/dist/primitives/spinner.cjs +3 -18
  125. package/dist/primitives/spinner.js +3 -18
  126. package/dist/primitives/split-action-button.cjs +3 -3
  127. package/dist/primitives/split-action-button.js +1 -1
  128. package/dist/primitives/suggestion.cjs +3 -3
  129. package/dist/primitives/suggestion.js +2 -2
  130. package/dist/primitives/tag-input.cjs +3 -3
  131. package/dist/primitives/tag-input.js +1 -1
  132. package/dist/primitives/toggle-group.d.cts +2 -2
  133. package/dist/primitives/toggle-group.d.ts +2 -2
  134. package/dist/primitives/toggle.d.cts +2 -2
  135. package/dist/primitives/toggle.d.ts +2 -2
  136. package/dist/primitives/voice-dictation-button.cjs +3 -3
  137. package/dist/primitives/voice-dictation-button.js +2 -2
  138. package/package.json +11 -1
@@ -183,6 +183,12 @@ interface ChatInputProps {
183
183
  addFilesLabel?: string;
184
184
  /** Visual density for the complete composer surface. */
185
185
  appearance?: "default" | "spacious";
186
+ /**
187
+ * Fold an empty, unfocused composer into one row. Focusing its textarea
188
+ * expands it; drafts and staged files always keep it expanded. Chat enables
189
+ * this only for an active mobile conversation with `mobileCompactComposer`.
190
+ */
191
+ compactOnBlur?: boolean;
186
192
  /** Quiet contextual copy between the composer controls. */
187
193
  helperText?: ReactNode;
188
194
  /** Privacy or visibility reassurance rendered below the composer. */
@@ -204,7 +210,7 @@ interface ChatInputProps {
204
210
  * `<Chat>` instead of composing `<ChatInput>` directly if you want
205
211
  * device-aware default modes.
206
212
  */
207
- declare function ChatInput({ value, onChange, onSend, pending, disabled, placeholder, size, uploadingLabel, voice, voiceTitle, voiceLang, submitLabel, autoFocus, className, inputRef, attachments, onFilesSelected, onRemoveAttachment, accept, multiple, addFilesLabel, appearance, helperText, privacyNote, }: ChatInputProps): react_jsx_runtime.JSX.Element;
213
+ declare function ChatInput({ value, onChange, onSend, pending, disabled, placeholder, size, uploadingLabel, voice, voiceTitle, voiceLang, submitLabel, autoFocus, className, inputRef, attachments, onFilesSelected, onRemoveAttachment, accept, multiple, addFilesLabel, appearance, compactOnBlur, helperText, privacyNote, }: ChatInputProps): react_jsx_runtime.JSX.Element;
208
214
  interface ChatProps {
209
215
  /** Conversation messages. */
210
216
  messages: ChatMessage[];
@@ -243,7 +249,10 @@ interface ChatProps {
243
249
  emptyTitle?: ReactNode;
244
250
  /** Subheadline shown under the headline. */
245
251
  emptySubtitle?: ReactNode;
246
- /** Optional list of suggested starter prompts. Click → sends. */
252
+ /**
253
+ * Optional list of suggested starter prompts, under the empty-state
254
+ * composer. Picking one sends it as `replySuggestions` do.
255
+ */
247
256
  examplePrompts?: ReadonlyArray<string>;
248
257
  /** Label above the example prompts. Defaults to "Try". */
249
258
  examplePromptsLabel?: string;
@@ -297,8 +306,27 @@ interface ChatProps {
297
306
  addFilesLabel?: string;
298
307
  /** Shown on a file, staged or on a message, while it is still uploading. Defaults to "Uploading…". */
299
308
  uploadingLabel?: string;
309
+ /**
310
+ * Replies the conversation offers next, such as the choices the latest
311
+ * assistant turn asked for. Once the conversation has started they show
312
+ * at the composer, directly above the input and at its width, as the same
313
+ * suggestion chips as `examplePrompts`, wrapping onto as many lines as
314
+ * they need. Picking one sends only its words: the draft and any staged
315
+ * files stay in the composer, and a failed send is reported
316
+ * (`onSendError`) without touching them. Disabled while `pending`. Pass
317
+ * none (the default) to show none; drop them once they no longer apply.
318
+ */
319
+ replySuggestions?: ReadonlyArray<string>;
320
+ /** The reply suggestions' accessible name. Defaults to "Suggested replies". */
321
+ replySuggestionsLabel?: string;
300
322
  /** Visual treatment for both empty and active text composers. */
301
323
  composerAppearance?: ChatInputProps["appearance"];
324
+ /**
325
+ * On phones (below 768px), fold an active conversation's empty composer
326
+ * into one row until its textarea is focused. Drafts and files stay
327
+ * expanded. Desktop and the new-conversation composer are unchanged.
328
+ */
329
+ mobileCompactComposer?: boolean;
302
330
  /** Quiet contextual copy inside the composer action row. */
303
331
  composerHelperText?: ReactNode;
304
332
  /** Privacy or visibility reassurance below the composer. */
@@ -394,7 +422,8 @@ interface ChatHandle {
394
422
  * - Optional example prompts render as suggestions below the input.
395
423
  *
396
424
  * **Active state** (messages exist) — always the compact text input;
397
- * the mic stays as an accessory icon.
425
+ * the mic stays as an accessory icon. Optional reply suggestions render as
426
+ * the same suggestions, directly above the input.
398
427
  *
399
428
  * **Files** — with `onFilesSelected`, the composer's menu picks files and a
400
429
  * file dragged anywhere on the conversation can be dropped (`fileDrop`);
@@ -183,6 +183,12 @@ interface ChatInputProps {
183
183
  addFilesLabel?: string;
184
184
  /** Visual density for the complete composer surface. */
185
185
  appearance?: "default" | "spacious";
186
+ /**
187
+ * Fold an empty, unfocused composer into one row. Focusing its textarea
188
+ * expands it; drafts and staged files always keep it expanded. Chat enables
189
+ * this only for an active mobile conversation with `mobileCompactComposer`.
190
+ */
191
+ compactOnBlur?: boolean;
186
192
  /** Quiet contextual copy between the composer controls. */
187
193
  helperText?: ReactNode;
188
194
  /** Privacy or visibility reassurance rendered below the composer. */
@@ -204,7 +210,7 @@ interface ChatInputProps {
204
210
  * `<Chat>` instead of composing `<ChatInput>` directly if you want
205
211
  * device-aware default modes.
206
212
  */
207
- declare function ChatInput({ value, onChange, onSend, pending, disabled, placeholder, size, uploadingLabel, voice, voiceTitle, voiceLang, submitLabel, autoFocus, className, inputRef, attachments, onFilesSelected, onRemoveAttachment, accept, multiple, addFilesLabel, appearance, helperText, privacyNote, }: ChatInputProps): react_jsx_runtime.JSX.Element;
213
+ declare function ChatInput({ value, onChange, onSend, pending, disabled, placeholder, size, uploadingLabel, voice, voiceTitle, voiceLang, submitLabel, autoFocus, className, inputRef, attachments, onFilesSelected, onRemoveAttachment, accept, multiple, addFilesLabel, appearance, compactOnBlur, helperText, privacyNote, }: ChatInputProps): react_jsx_runtime.JSX.Element;
208
214
  interface ChatProps {
209
215
  /** Conversation messages. */
210
216
  messages: ChatMessage[];
@@ -243,7 +249,10 @@ interface ChatProps {
243
249
  emptyTitle?: ReactNode;
244
250
  /** Subheadline shown under the headline. */
245
251
  emptySubtitle?: ReactNode;
246
- /** Optional list of suggested starter prompts. Click → sends. */
252
+ /**
253
+ * Optional list of suggested starter prompts, under the empty-state
254
+ * composer. Picking one sends it as `replySuggestions` do.
255
+ */
247
256
  examplePrompts?: ReadonlyArray<string>;
248
257
  /** Label above the example prompts. Defaults to "Try". */
249
258
  examplePromptsLabel?: string;
@@ -297,8 +306,27 @@ interface ChatProps {
297
306
  addFilesLabel?: string;
298
307
  /** Shown on a file, staged or on a message, while it is still uploading. Defaults to "Uploading…". */
299
308
  uploadingLabel?: string;
309
+ /**
310
+ * Replies the conversation offers next, such as the choices the latest
311
+ * assistant turn asked for. Once the conversation has started they show
312
+ * at the composer, directly above the input and at its width, as the same
313
+ * suggestion chips as `examplePrompts`, wrapping onto as many lines as
314
+ * they need. Picking one sends only its words: the draft and any staged
315
+ * files stay in the composer, and a failed send is reported
316
+ * (`onSendError`) without touching them. Disabled while `pending`. Pass
317
+ * none (the default) to show none; drop them once they no longer apply.
318
+ */
319
+ replySuggestions?: ReadonlyArray<string>;
320
+ /** The reply suggestions' accessible name. Defaults to "Suggested replies". */
321
+ replySuggestionsLabel?: string;
300
322
  /** Visual treatment for both empty and active text composers. */
301
323
  composerAppearance?: ChatInputProps["appearance"];
324
+ /**
325
+ * On phones (below 768px), fold an active conversation's empty composer
326
+ * into one row until its textarea is focused. Drafts and files stay
327
+ * expanded. Desktop and the new-conversation composer are unchanged.
328
+ */
329
+ mobileCompactComposer?: boolean;
302
330
  /** Quiet contextual copy inside the composer action row. */
303
331
  composerHelperText?: ReactNode;
304
332
  /** Privacy or visibility reassurance below the composer. */
@@ -394,7 +422,8 @@ interface ChatHandle {
394
422
  * - Optional example prompts render as suggestions below the input.
395
423
  *
396
424
  * **Active state** (messages exist) — always the compact text input;
397
- * the mic stays as an accessory icon.
425
+ * the mic stays as an accessory icon. Optional reply suggestions render as
426
+ * the same suggestions, directly above the input.
398
427
  *
399
428
  * **Files** — with `onFilesSelected`, the composer's menu picks files and a
400
429
  * file dragged anywhere on the conversation can be dropped (`fileDrop`);
@@ -7,11 +7,11 @@ import {
7
7
  } from "../chunk-Z45YKVAM.js";
8
8
  import {
9
9
  VoiceDictationButton
10
- } from "../chunk-HAC32ALJ.js";
10
+ } from "../chunk-TI57GAQP.js";
11
11
  import {
12
12
  Suggestion,
13
13
  Suggestions
14
- } from "../chunk-THWIJG5C.js";
14
+ } from "../chunk-IJG2B3Y2.js";
15
15
  import {
16
16
  MessageScroller,
17
17
  MessageScrollerButton,
@@ -19,13 +19,13 @@ import {
19
19
  MessageScrollerItem,
20
20
  MessageScrollerProvider,
21
21
  MessageScrollerViewport
22
- } from "../chunk-GFG2R5BZ.js";
22
+ } from "../chunk-2V7AVB2U.js";
23
23
  import {
24
24
  Message,
25
25
  MessageAction,
26
26
  MessageActions,
27
27
  MessageContent
28
- } from "../chunk-FGISSYXG.js";
28
+ } from "../chunk-H4XH2PPU.js";
29
29
  import {
30
30
  Marker,
31
31
  MarkerContent
@@ -34,12 +34,12 @@ import {
34
34
  InputGroup,
35
35
  InputGroupAddon,
36
36
  InputGroupTextarea
37
- } from "../chunk-MHYAVXOE.js";
37
+ } from "../chunk-QTPPNK6F.js";
38
38
  import "../chunk-6FC65AZQ.js";
39
39
  import {
40
40
  FileDropArea,
41
41
  FileDropzone
42
- } from "../chunk-M7FYGAGO.js";
42
+ } from "../chunk-HBRVOGLA.js";
43
43
  import {
44
44
  LazyMarkdown
45
45
  } from "../chunk-VY5U4VGV.js";
@@ -66,7 +66,7 @@ import {
66
66
  AttachmentMedia,
67
67
  AttachmentTitle,
68
68
  AttachmentTrigger
69
- } from "../chunk-EZCKWTH7.js";
69
+ } from "../chunk-CYDICVH2.js";
70
70
  import "../chunk-ZRLUUZHK.js";
71
71
  import {
72
72
  ActionDock
@@ -74,7 +74,7 @@ import {
74
74
  import "../chunk-QMA3WVB7.js";
75
75
  import {
76
76
  Button
77
- } from "../chunk-HQRWRRZJ.js";
77
+ } from "../chunk-W326GKML.js";
78
78
  import {
79
79
  useCopyToClipboard
80
80
  } from "../chunk-ZWZAAGQF.js";
@@ -385,12 +385,15 @@ function ChatInput({
385
385
  multiple = true,
386
386
  addFilesLabel = "Add files",
387
387
  appearance = "default",
388
+ compactOnBlur = false,
388
389
  helperText,
389
390
  privacyNote
390
391
  }) {
391
392
  const internalRef = useRef(null);
392
393
  const ref = inputRef ?? internalRef;
393
394
  const fileInputRef = useRef(null);
395
+ const [editing, setEditing] = useState(false);
396
+ const focusVersion = useRef(0);
394
397
  const isDisabled = pending || disabled;
395
398
  const attachmentsReady = attachments.length > 0 && attachments.every((item) => item.state !== "uploading");
396
399
  const canSubmit = !isDisabled && (Boolean(value.trim()) || attachmentsReady);
@@ -401,13 +404,27 @@ function ChatInput({
401
404
  };
402
405
  const spacious = appearance === "spacious";
403
406
  const composerSize = COMPOSER_SIZES[size];
407
+ const compact = compactOnBlur && !editing && value.length === 0 && attachments.length === 0;
404
408
  return /* @__PURE__ */ jsxs("div", { "data-slot": "chat-input-container", className: "w-full", children: [
405
409
  /* @__PURE__ */ jsxs(
406
410
  "form",
407
411
  {
408
412
  "data-slot": "chat-input",
409
413
  "data-appearance": appearance,
414
+ "data-compact": compact || void 0,
410
415
  className: "w-full",
416
+ onFocusCapture: (event) => {
417
+ focusVersion.current += 1;
418
+ if (event.nativeEvent.target === ref.current) setEditing(true);
419
+ },
420
+ onBlurCapture: (event) => {
421
+ if (event.currentTarget.contains(event.relatedTarget)) return;
422
+ const form = event.currentTarget;
423
+ const blurredAt = ++focusVersion.current;
424
+ queueMicrotask(() => {
425
+ if (form.isConnected && focusVersion.current === blurredAt) setEditing(false);
426
+ });
427
+ },
411
428
  onSubmit: (event) => {
412
429
  if (event.target !== event.currentTarget) return;
413
430
  event.preventDefault();
@@ -462,29 +479,31 @@ function ChatInput({
462
479
  submit();
463
480
  },
464
481
  placeholder,
465
- rows: composerSize.rows,
482
+ rows: compact ? 1 : composerSize.rows,
483
+ wrap: compact ? "off" : void 0,
466
484
  disabled: isDisabled,
467
485
  autoFocus,
468
486
  className: cn(
469
- composerSize.minHeight,
487
+ compact ? "h-[44px] min-h-[44px] max-h-[44px] min-w-0 field-sizing-fixed overflow-hidden whitespace-nowrap text-ellipsis" : composerSize.minHeight,
470
488
  // Grows with the text, then scrolls rather than pushing the thread away.
471
- "max-h-48 leading-relaxed disabled:opacity-70 placeholder:text-sm",
472
- spacious ? "px-6 pb-2 pt-6" : "px-4 py-3.5"
489
+ "leading-relaxed disabled:opacity-70 placeholder:text-sm",
490
+ !compact && "max-h-48",
491
+ compact ? "px-4 py-2" : spacious ? "px-6 pb-2 pt-6" : "px-4 py-3.5"
473
492
  )
474
493
  }
475
494
  ),
476
495
  /* @__PURE__ */ jsxs(
477
496
  InputGroupAddon,
478
497
  {
479
- align: "block-end",
498
+ align: compact ? "inline-end" : "block-end",
480
499
  "data-slot": "chat-input-actions",
481
500
  onClick: (event) => {
482
501
  if (!event.currentTarget.contains(event.target)) return;
483
502
  if (!event.target.closest("button")) ref.current?.focus();
484
503
  },
485
504
  className: cn(
486
- "gap-2",
487
- spacious ? "px-5 pb-5 pt-1" : "border-t border-border px-3 py-2.5"
505
+ compact ? "shrink-0 gap-0 p-1" : "gap-2",
506
+ !compact && (spacious ? "px-5 pb-5 pt-1" : "border-t border-border px-3 py-2.5")
488
507
  ),
489
508
  children: [
490
509
  onFilesSelected && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
@@ -492,9 +511,10 @@ function ChatInput({
492
511
  Button,
493
512
  {
494
513
  type: "button",
495
- size: spacious ? "icon-lg" : "icon",
514
+ size: spacious && !compact ? "icon-lg" : "icon",
496
515
  variant: "ghost",
497
516
  "aria-label": "Add to conversation",
517
+ className: compactOnBlur ? "min-h-[44px] min-w-[44px] shrink-0" : void 0,
498
518
  children: /* @__PURE__ */ jsx(Plus, { className: cn(spacious && "h-5 w-5") })
499
519
  }
500
520
  ) }),
@@ -510,24 +530,28 @@ function ChatInput({
510
530
  title: voiceTitle,
511
531
  lang: voiceLang,
512
532
  onCommit: (text) => onSend(text),
513
- className: spacious ? "h-12 w-12 border-transparent bg-transparent shadow-none hover:bg-muted" : void 0
533
+ className: cn(
534
+ spacious && "border-transparent bg-transparent shadow-none hover:bg-muted",
535
+ spacious && !compact && "h-12 w-12",
536
+ compactOnBlur && "min-h-[44px] min-w-[44px] shrink-0"
537
+ )
514
538
  }
515
539
  ),
516
540
  helperText ? /* @__PURE__ */ jsx(
517
541
  "span",
518
542
  {
519
543
  "data-slot": "chat-input-helper",
520
- className: "min-w-0 flex-1 truncate px-1 text-center text-xs font-normal text-muted-foreground sm:px-2 sm:text-sm",
544
+ className: cn("min-w-0 flex-1 truncate px-1 text-center text-xs font-normal text-muted-foreground sm:px-2 sm:text-sm", compact && "hidden"),
521
545
  children: helperText
522
546
  }
523
- ) : /* @__PURE__ */ jsx("div", { className: "flex-1" }),
547
+ ) : /* @__PURE__ */ jsx("div", { className: compact ? "hidden" : "flex-1" }),
524
548
  submitLabel ? /* @__PURE__ */ jsxs(
525
549
  Button,
526
550
  {
527
551
  type: "submit",
528
552
  disabled: !canSubmit,
529
553
  "data-chat-action": "submit",
530
- className: spacious ? "rounded-xl" : void 0,
554
+ className: cn(spacious && "rounded-xl", compactOnBlur && "min-h-[44px] min-w-[44px] shrink-0"),
531
555
  children: [
532
556
  submitLabel,
533
557
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 h-4 w-4" })
@@ -537,11 +561,11 @@ function ChatInput({
537
561
  Button,
538
562
  {
539
563
  type: "submit",
540
- size: spacious ? "icon-lg" : "icon",
564
+ size: spacious && !compact ? "icon-lg" : "icon",
541
565
  disabled: !canSubmit,
542
566
  "aria-label": "Send",
543
567
  "data-chat-action": "submit",
544
- className: spacious ? "rounded-xl" : void 0,
568
+ className: cn(spacious && "rounded-xl", compactOnBlur && "min-h-[44px] min-w-[44px] shrink-0"),
545
569
  children: /* @__PURE__ */ jsx(ArrowUp, { className: cn(spacious && "h-5 w-5") })
546
570
  }
547
571
  )
@@ -605,27 +629,40 @@ var EXAMPLE_PROMPT_LAYOUTS = {
605
629
  row: "row",
606
630
  cards: "grid"
607
631
  };
608
- function ExamplePrompts({
609
- prompts,
632
+ function ChatSuggestions({
633
+ slot,
634
+ items,
610
635
  onSelect,
611
- label = "Try",
636
+ label,
637
+ showLabel = true,
612
638
  variant = "chips",
639
+ disabled = false,
613
640
  className
614
641
  }) {
615
642
  const labelId = useId();
616
- if (prompts.length === 0) return null;
643
+ if (items.length === 0) return null;
617
644
  const cards = variant === "cards";
618
- return /* @__PURE__ */ jsxs("div", { "data-slot": "chat-examples", "data-variant": variant, className: cn(cards ? "mt-4" : "mt-3", className), children: [
619
- /* @__PURE__ */ jsx("p", { id: labelId, "data-slot": "chat-examples-label", className: "mb-2 text-left text-xs text-muted-foreground", children: label }),
620
- /* @__PURE__ */ jsx(Suggestions, { role: "group", "aria-labelledby": labelId, layout: EXAMPLE_PROMPT_LAYOUTS[variant], children: prompts.map((prompt) => /* @__PURE__ */ jsx(
621
- Suggestion,
645
+ return /* @__PURE__ */ jsxs("div", { "data-slot": slot, "data-variant": variant, className, children: [
646
+ showLabel && /* @__PURE__ */ jsx("p", { id: labelId, "data-slot": `${slot}-label`, className: "mb-2 text-left text-xs text-muted-foreground", children: label }),
647
+ /* @__PURE__ */ jsx(
648
+ Suggestions,
622
649
  {
623
- suggestion: prompt,
624
- onClick: onSelect,
625
- className: cards ? "h-auto min-h-12 px-3 py-2.5 text-xs" : void 0
626
- },
627
- prompt
628
- )) })
650
+ role: "group",
651
+ "aria-labelledby": showLabel ? labelId : void 0,
652
+ "aria-label": showLabel ? void 0 : label,
653
+ layout: EXAMPLE_PROMPT_LAYOUTS[variant],
654
+ children: items.map((item) => /* @__PURE__ */ jsx(
655
+ Suggestion,
656
+ {
657
+ suggestion: item,
658
+ onClick: onSelect,
659
+ disabled,
660
+ className: cards ? "h-auto min-h-12 px-3 py-2.5 text-xs" : void 0
661
+ },
662
+ item
663
+ ))
664
+ }
665
+ )
629
666
  ] });
630
667
  }
631
668
  function ExamplePromptsLoading({ label, className }) {
@@ -669,6 +706,8 @@ var Chat = forwardRef(function Chat2({
669
706
  examplePromptsVariant = "chips",
670
707
  examplePromptsLoading = false,
671
708
  examplePromptsLoadingLabel = "Loading suggestions\u2026",
709
+ replySuggestions = [],
710
+ replySuggestionsLabel = "Suggested replies",
672
711
  compactEmptyState = false,
673
712
  defaultMode,
674
713
  placeholder = "Type your message\u2026",
@@ -681,6 +720,7 @@ var Chat = forwardRef(function Chat2({
681
720
  addFilesLabel,
682
721
  uploadingLabel,
683
722
  composerAppearance = "default",
723
+ mobileCompactComposer = false,
684
724
  composerHelperText,
685
725
  composerPrivacyNote,
686
726
  emptyFileDropzone = false,
@@ -714,6 +754,14 @@ var Chat = forwardRef(function Chat2({
714
754
  const conversationStarted = messages.length > 0;
715
755
  const [userMode, setUserMode] = useState(null);
716
756
  const mode = userMode ?? defaultMode ?? (isMobile ? "voice" : "text");
757
+ const reportSendError = useCallback(
758
+ (cause) => {
759
+ if (onSendError) onSendError(cause);
760
+ else if (typeof globalThis.reportError === "function") globalThis.reportError(cause);
761
+ else console.error(cause);
762
+ },
763
+ [onSendError]
764
+ );
717
765
  const send = useCallback(
718
766
  async (text, sentAttachments = attachments) => {
719
767
  const trimmed = text.trim();
@@ -726,13 +774,24 @@ var Chat = forwardRef(function Chat2({
726
774
  setInput((current) => current ? current : trimmed);
727
775
  if (mode === "voice") setUserMode("text");
728
776
  }
729
- if (onSendError) onSendError(cause);
730
- else if (typeof globalThis.reportError === "function") globalThis.reportError(cause);
731
- else console.error(cause);
777
+ reportSendError(cause);
732
778
  }
733
779
  window.setTimeout(() => inputRef.current?.focus(), 0);
734
780
  },
735
- [attachments, mode, onSend, onSendError, pending]
781
+ [attachments, mode, onSend, pending, reportSendError]
782
+ );
783
+ const sendSuggestion = useCallback(
784
+ async (text) => {
785
+ const trimmed = text.trim();
786
+ if (!trimmed || pending) return;
787
+ try {
788
+ await onSend(trimmed, []);
789
+ } catch (cause) {
790
+ reportSendError(cause);
791
+ }
792
+ window.setTimeout(() => inputRef.current?.focus(), 0);
793
+ },
794
+ [onSend, pending, reportSendError]
736
795
  );
737
796
  useImperativeHandle(
738
797
  ref,
@@ -873,12 +932,14 @@ var Chat = forwardRef(function Chat2({
873
932
  error && /* @__PURE__ */ jsx(ChatError, { children: error }),
874
933
  !pending && examplePromptsLoading && /* @__PURE__ */ jsx(ExamplePromptsLoading, { label: examplePromptsLoadingLabel }),
875
934
  !pending && !examplePromptsLoading && examplePrompts.length > 0 && /* @__PURE__ */ jsx(
876
- ExamplePrompts,
935
+ ChatSuggestions,
877
936
  {
878
- prompts: examplePrompts,
879
- onSelect: (p) => void send(p),
937
+ slot: "chat-examples",
938
+ items: examplePrompts,
939
+ onSelect: (prompt) => void sendSuggestion(prompt),
880
940
  label: examplePromptsLabel,
881
- variant: examplePromptsVariant
941
+ variant: examplePromptsVariant,
942
+ className: examplePromptsVariant === "cards" ? "mt-4" : "mt-3"
882
943
  }
883
944
  ),
884
945
  emptyFooter && /* @__PURE__ */ jsx("div", { "data-slot": "chat-empty-footer", className: "mt-5", children: emptyFooter })
@@ -917,9 +978,22 @@ var Chat = forwardRef(function Chat2({
917
978
  className: cn(
918
979
  "pt-3",
919
980
  application && "px-4 py-3 md:bg-transparent md:px-0 md:pb-0 md:backdrop-blur-none md:supports-backdrop-filter:bg-transparent",
920
- workspace && "border-0 bg-gradient-to-t from-background via-background to-transparent px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-8 backdrop-blur-none supports-[backdrop-filter]:bg-gradient-to-t"
981
+ workspace && "border-0 bg-gradient-to-t from-background via-background to-transparent px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-8 backdrop-blur-none supports-[backdrop-filter]:bg-gradient-to-t",
982
+ workspace && mobileCompactComposer && "max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))] max-md:pt-3"
921
983
  ),
922
984
  children: [
985
+ /* @__PURE__ */ jsx(
986
+ ChatSuggestions,
987
+ {
988
+ slot: "chat-replies",
989
+ items: replySuggestions,
990
+ onSelect: (reply) => void sendSuggestion(reply),
991
+ label: replySuggestionsLabel,
992
+ showLabel: false,
993
+ disabled: pending,
994
+ className: cn("mb-3", workspace && "mx-auto max-w-3xl")
995
+ }
996
+ ),
923
997
  /* @__PURE__ */ jsx(
924
998
  ChatInput,
925
999
  {
@@ -940,6 +1014,7 @@ var Chat = forwardRef(function Chat2({
940
1014
  addFilesLabel,
941
1015
  uploadingLabel,
942
1016
  appearance: composerAppearance,
1017
+ compactOnBlur: mobileCompactComposer && isMobile,
943
1018
  helperText: composerHelperText,
944
1019
  privacyNote: composerPrivacyNote,
945
1020
  className: workspace ? "mx-auto max-w-3xl rounded-3xl shadow-sm" : void 0
@@ -1,11 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
- var _chunkVLXPLKFRcjs = require('../chunk-VLXPLKFR.cjs');
5
- require('../chunk-VKIGJNJR.cjs');
6
- require('../chunk-L4DH72UF.cjs');
4
+ var _chunkGTVSRRE3cjs = require('../chunk-GTVSRRE3.cjs');
5
+ require('../chunk-MU4F3DSW.cjs');
6
+ require('../chunk-JDEXZGIY.cjs');
7
7
  require('../chunk-4LXN6GLZ.cjs');
8
8
  require('../chunk-KWLSBTB2.cjs');
9
9
 
10
10
 
11
- exports.CodeBlock = _chunkVLXPLKFRcjs.CodeBlock;
11
+ exports.CodeBlock = _chunkGTVSRRE3cjs.CodeBlock;
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import {
3
3
  CodeBlock
4
- } from "../chunk-HHV4G77I.js";
5
- import "../chunk-NW3OWKBN.js";
6
- import "../chunk-HQRWRRZJ.js";
4
+ } from "../chunk-F3B4M6DU.js";
5
+ import "../chunk-HREEZUKL.js";
6
+ import "../chunk-W326GKML.js";
7
7
  import "../chunk-ZWZAAGQF.js";
8
8
  import "../chunk-G6SRSWXB.js";
9
9
  export {
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkL4DH72UFcjs = require('../chunk-L4DH72UF.cjs');
5
+ var _chunkJDEXZGIYcjs = require('../chunk-JDEXZGIY.cjs');
6
6
  require('../chunk-KWLSBTB2.cjs');
7
7
 
8
8
  // src/primitives/confirm-bar.tsx
@@ -88,8 +88,8 @@ function ConfirmBar({
88
88
  remaining,
89
89
  "s"
90
90
  ] }),
91
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkL4DH72UFcjs.Button, { size: "sm", variant: "ghost", onClick: onCancel, className: "shrink-0", children: cancelLabel }),
92
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkL4DH72UFcjs.Button, { size: "sm", variant: confirmVariant, onClick: onConfirm, className: "shrink-0", children: confirmLabel })
91
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { size: "sm", variant: "ghost", onClick: onCancel, className: "shrink-0", children: cancelLabel }),
92
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { size: "sm", variant: confirmVariant, onClick: onConfirm, className: "shrink-0", children: confirmLabel })
93
93
  ] })
94
94
  }
95
95
  ),
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  Button
5
- } from "../chunk-HQRWRRZJ.js";
5
+ } from "../chunk-W326GKML.js";
6
6
  import "../chunk-G6SRSWXB.js";
7
7
 
8
8
  // src/primitives/confirm-bar.tsx
@@ -4,7 +4,7 @@
4
4
  var _chunkI6GUOEBOcjs = require('../chunk-I6GUOEBO.cjs');
5
5
 
6
6
 
7
- var _chunkL4DH72UFcjs = require('../chunk-L4DH72UF.cjs');
7
+ var _chunkJDEXZGIYcjs = require('../chunk-JDEXZGIY.cjs');
8
8
 
9
9
 
10
10
  var _chunkKWLSBTB2cjs = require('../chunk-KWLSBTB2.cjs');
@@ -54,7 +54,7 @@ function StatusAction({
54
54
  const variant = state === "disconnected" ? "default" : action.iconOnly ? "quiet" : "outline";
55
55
  const size = action.iconOnly ? "icon-sm" : "sm";
56
56
  if (action.href) {
57
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkL4DH72UFcjs.Button, { asChild: true, variant, size, className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
57
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkJDEXZGIYcjs.Button, { asChild: true, variant, size, className, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
58
58
  "a",
59
59
  {
60
60
  href: action.href,
@@ -75,7 +75,7 @@ function StatusAction({
75
75
  ) });
76
76
  }
77
77
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
78
- _chunkL4DH72UFcjs.Button,
78
+ _chunkJDEXZGIYcjs.Button,
79
79
  {
80
80
  type: "button",
81
81
  variant,
@@ -213,7 +213,7 @@ function ConnectionStatusBar({
213
213
  _nullishCoalesce(visibleAction.ariaLabel, () => ( (typeof visibleAction.label === "string" ? visibleAction.label : index)))
214
214
  )),
215
215
  children ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CollapsiblePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
216
- _chunkL4DH72UFcjs.Button,
216
+ _chunkJDEXZGIYcjs.Button,
217
217
  {
218
218
  type: "button",
219
219
  variant: "quiet",
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-D4ZL2OEQ.js";
5
5
  import {
6
6
  Button
7
- } from "../chunk-HQRWRRZJ.js";
7
+ } from "../chunk-W326GKML.js";
8
8
  import {
9
9
  cn
10
10
  } from "../chunk-G6SRSWXB.js";