@epicbagel/saas-kit 1.4.0 → 1.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.
Files changed (130) hide show
  1. package/README.md +57 -0
  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-JZOBIPFC.js → chunk-H4XH2PPU.js} +61 -2
  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-2AU7ZRGX.cjs → chunk-NWDPH2Z7.cjs} +61 -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.js +2 -2
  56. package/dist/primitives/badge.d.cts +1 -1
  57. package/dist/primitives/badge.d.ts +1 -1
  58. package/dist/primitives/board-view.cjs +7 -7
  59. package/dist/primitives/board-view.js +6 -6
  60. package/dist/primitives/bubble.d.cts +1 -1
  61. package/dist/primitives/bubble.d.ts +1 -1
  62. package/dist/primitives/button.cjs +2 -2
  63. package/dist/primitives/button.d.cts +3 -3
  64. package/dist/primitives/button.d.ts +3 -3
  65. package/dist/primitives/button.js +1 -1
  66. package/dist/primitives/calendar.cjs +3 -3
  67. package/dist/primitives/calendar.js +2 -2
  68. package/dist/primitives/chat.cjs +144 -64
  69. package/dist/primitives/chat.d.cts +59 -3
  70. package/dist/primitives/chat.d.ts +59 -3
  71. package/dist/primitives/chat.js +104 -24
  72. package/dist/primitives/code-block.cjs +4 -4
  73. package/dist/primitives/code-block.js +3 -3
  74. package/dist/primitives/confirm-bar.cjs +3 -3
  75. package/dist/primitives/confirm-bar.js +1 -1
  76. package/dist/primitives/connection-status-bar.cjs +4 -4
  77. package/dist/primitives/connection-status-bar.js +1 -1
  78. package/dist/primitives/connection.cjs +13 -13
  79. package/dist/primitives/connection.js +3 -3
  80. package/dist/primitives/copy-button.cjs +3 -3
  81. package/dist/primitives/copy-button.js +2 -2
  82. package/dist/primitives/decision-actions.cjs +2 -2
  83. package/dist/primitives/decision-actions.js +1 -1
  84. package/dist/primitives/delete-confirmation-dialog.cjs +4 -4
  85. package/dist/primitives/delete-confirmation-dialog.js +3 -3
  86. package/dist/primitives/document-preview.cjs +4 -4
  87. package/dist/primitives/document-preview.js +2 -2
  88. package/dist/primitives/editable-list.cjs +6 -6
  89. package/dist/primitives/editable-list.js +1 -1
  90. package/dist/primitives/empty-state.cjs +3 -3
  91. package/dist/primitives/empty-state.js +2 -2
  92. package/dist/primitives/file-dropzone.cjs +3 -3
  93. package/dist/primitives/file-dropzone.js +2 -2
  94. package/dist/primitives/file-preview-internals.cjs +3 -3
  95. package/dist/primitives/file-preview-internals.js +2 -2
  96. package/dist/primitives/file-preview.cjs +15 -15
  97. package/dist/primitives/file-preview.js +4 -4
  98. package/dist/primitives/filter-bar.cjs +4 -4
  99. package/dist/primitives/filter-bar.js +3 -3
  100. package/dist/primitives/input-group.cjs +3 -3
  101. package/dist/primitives/input-group.js +2 -2
  102. package/dist/primitives/load-more-button.cjs +3 -3
  103. package/dist/primitives/load-more-button.js +2 -2
  104. package/dist/primitives/message-scroller.cjs +3 -3
  105. package/dist/primitives/message-scroller.js +2 -2
  106. package/dist/primitives/message.cjs +8 -2
  107. package/dist/primitives/message.d.cts +35 -1
  108. package/dist/primitives/message.d.ts +35 -1
  109. package/dist/primitives/message.js +7 -1
  110. package/dist/primitives/page-header-actions.cjs +3 -3
  111. package/dist/primitives/page-header-actions.js +2 -2
  112. package/dist/primitives/quick-filters.cjs +3 -3
  113. package/dist/primitives/quick-filters.js +2 -2
  114. package/dist/primitives/resource-list.cjs +7 -7
  115. package/dist/primitives/resource-list.js +6 -6
  116. package/dist/primitives/search-input.cjs +101 -0
  117. package/dist/primitives/search-input.d.cts +23 -0
  118. package/dist/primitives/search-input.d.ts +23 -0
  119. package/dist/primitives/search-input.js +101 -0
  120. package/dist/primitives/spinner.cjs +3 -18
  121. package/dist/primitives/spinner.js +3 -18
  122. package/dist/primitives/split-action-button.cjs +3 -3
  123. package/dist/primitives/split-action-button.js +1 -1
  124. package/dist/primitives/suggestion.cjs +3 -3
  125. package/dist/primitives/suggestion.js +2 -2
  126. package/dist/primitives/tag-input.cjs +3 -3
  127. package/dist/primitives/tag-input.js +1 -1
  128. package/dist/primitives/voice-dictation-button.cjs +3 -3
  129. package/dist/primitives/voice-dictation-button.js +2 -2
  130. package/package.json +11 -1
@@ -43,8 +43,20 @@ type ChatMessage = {
43
43
  * with the reply when that lands. The line is not a message: it has no time
44
44
  * marker, files or rich `content`, and the scroller neither anchors nor
45
45
  * tracks it.
46
+ *
47
+ * `"sending"`: a turn still on its way to being stored, such as the user's
48
+ * own turn shown optimistically. It is drawn as any turn, but offers no
49
+ * actions until its stored copy replaces it.
46
50
  */
47
- status?: "running";
51
+ status?: "running" | "sending";
52
+ /**
53
+ * Whether the thread offers this turn the actions `messageActions` returns.
54
+ * By default an assistant turn has them and a user turn doesn't; `false`
55
+ * drops them from an assistant turn that isn't a reply (a progress line),
56
+ * and `true` gives them to a user turn. A running or sending turn never has
57
+ * them, whatever this says.
58
+ */
59
+ actions?: boolean;
48
60
  };
49
61
  interface ChatThreadProps {
50
62
  messages: ChatMessage[];
@@ -80,6 +92,15 @@ interface ChatThreadProps {
80
92
  onOpenAttachment?: (attachment: ChatAttachment) => void;
81
93
  /** What opening a message's file does, for its label and icon. Defaults to `"open"`. */
82
94
  attachmentAction?: ChatAttachmentAction;
95
+ /**
96
+ * The actions under a turn, in a `MessageActions` row: `ChatCopyAction`,
97
+ * then any `MessageAction`s of your own (a thumbs up or down). Called for
98
+ * each turn that offers them (`ChatMessage.actions`: assistant turns, by
99
+ * default), never for the running line, a running or sending turn or the
100
+ * footer. The row always shows, on a phone too, so nothing moves when a
101
+ * pointer reaches it; return `null` for none.
102
+ */
103
+ messageActions?: (message: ChatMessage) => ReactNode;
83
104
  className?: string;
84
105
  }
85
106
  /**
@@ -97,6 +118,24 @@ interface ChatThreadProps {
97
118
  * appears. A turn with `status: "running"` draws the same line in its place.
98
119
  */
99
120
  declare const ChatThread: React$1.ForwardRefExoticComponent<ChatThreadProps & React$1.RefAttributes<HTMLDivElement>>;
121
+ interface ChatCopyActionProps {
122
+ /** The turn whose `text` (its Markdown source) is copied. */
123
+ message: Pick<ChatMessage, "text">;
124
+ /** The tooltip and accessible name. Defaults to "Copy". */
125
+ label?: string;
126
+ /** Both, for a moment after a copy. Defaults to "Copied". */
127
+ copiedLabel?: string;
128
+ /** Called after each copy, with whether the clipboard took the text. */
129
+ onCopy?: (success: boolean) => void;
130
+ /** How long the copied state shows, in ms. Defaults to 2000. */
131
+ resetAfter?: number;
132
+ }
133
+ /**
134
+ * The ready Copy action for `messageActions`: copies the turn's `text`, its
135
+ * Markdown source, and shows a check and "Copied" for a moment. A turn with
136
+ * no `text` (rich `content` only) has nothing to copy, so it draws nothing.
137
+ */
138
+ declare function ChatCopyAction({ message, label, copiedLabel, onCopy, resetAfter, }: ChatCopyActionProps): react_jsx_runtime.JSX.Element | null;
100
139
  interface ChatInputProps {
101
140
  value: string;
102
141
  onChange: (next: string) => void;
@@ -144,6 +183,12 @@ interface ChatInputProps {
144
183
  addFilesLabel?: string;
145
184
  /** Visual density for the complete composer surface. */
146
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;
147
192
  /** Quiet contextual copy between the composer controls. */
148
193
  helperText?: ReactNode;
149
194
  /** Privacy or visibility reassurance rendered below the composer. */
@@ -165,7 +210,7 @@ interface ChatInputProps {
165
210
  * `<Chat>` instead of composing `<ChatInput>` directly if you want
166
211
  * device-aware default modes.
167
212
  */
168
- 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;
169
214
  interface ChatProps {
170
215
  /** Conversation messages. */
171
216
  messages: ChatMessage[];
@@ -260,6 +305,12 @@ interface ChatProps {
260
305
  uploadingLabel?: string;
261
306
  /** Visual treatment for both empty and active text composers. */
262
307
  composerAppearance?: ChatInputProps["appearance"];
308
+ /**
309
+ * On phones (below 768px), fold an active conversation's empty composer
310
+ * into one row until its textarea is focused. Drafts and files stay
311
+ * expanded. Desktop and the new-conversation composer are unchanged.
312
+ */
313
+ mobileCompactComposer?: boolean;
263
314
  /** Quiet contextual copy inside the composer action row. */
264
315
  composerHelperText?: ReactNode;
265
316
  /** Privacy or visibility reassurance below the composer. */
@@ -330,6 +381,11 @@ interface ChatProps {
330
381
  onOpenAttachment?: (attachment: ChatAttachment) => void;
331
382
  /** What opening a message's file does, for its label and icon. Defaults to `"open"`. */
332
383
  attachmentAction?: ChatAttachmentAction;
384
+ /**
385
+ * The actions under each turn that offers them (`ChatMessage.actions`:
386
+ * assistant turns, by default), such as `ChatCopyAction`. See `ChatThread`.
387
+ */
388
+ messageActions?: (message: ChatMessage) => ReactNode;
333
389
  className?: string;
334
390
  }
335
391
  interface ChatHandle {
@@ -361,4 +417,4 @@ interface ChatHandle {
361
417
  */
362
418
  declare const Chat: React$1.ForwardRefExoticComponent<ChatProps & React$1.RefAttributes<ChatHandle>>;
363
419
 
364
- export { Chat, type ChatAttachment, type ChatAttachmentAction, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
420
+ export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
@@ -43,8 +43,20 @@ type ChatMessage = {
43
43
  * with the reply when that lands. The line is not a message: it has no time
44
44
  * marker, files or rich `content`, and the scroller neither anchors nor
45
45
  * tracks it.
46
+ *
47
+ * `"sending"`: a turn still on its way to being stored, such as the user's
48
+ * own turn shown optimistically. It is drawn as any turn, but offers no
49
+ * actions until its stored copy replaces it.
46
50
  */
47
- status?: "running";
51
+ status?: "running" | "sending";
52
+ /**
53
+ * Whether the thread offers this turn the actions `messageActions` returns.
54
+ * By default an assistant turn has them and a user turn doesn't; `false`
55
+ * drops them from an assistant turn that isn't a reply (a progress line),
56
+ * and `true` gives them to a user turn. A running or sending turn never has
57
+ * them, whatever this says.
58
+ */
59
+ actions?: boolean;
48
60
  };
49
61
  interface ChatThreadProps {
50
62
  messages: ChatMessage[];
@@ -80,6 +92,15 @@ interface ChatThreadProps {
80
92
  onOpenAttachment?: (attachment: ChatAttachment) => void;
81
93
  /** What opening a message's file does, for its label and icon. Defaults to `"open"`. */
82
94
  attachmentAction?: ChatAttachmentAction;
95
+ /**
96
+ * The actions under a turn, in a `MessageActions` row: `ChatCopyAction`,
97
+ * then any `MessageAction`s of your own (a thumbs up or down). Called for
98
+ * each turn that offers them (`ChatMessage.actions`: assistant turns, by
99
+ * default), never for the running line, a running or sending turn or the
100
+ * footer. The row always shows, on a phone too, so nothing moves when a
101
+ * pointer reaches it; return `null` for none.
102
+ */
103
+ messageActions?: (message: ChatMessage) => ReactNode;
83
104
  className?: string;
84
105
  }
85
106
  /**
@@ -97,6 +118,24 @@ interface ChatThreadProps {
97
118
  * appears. A turn with `status: "running"` draws the same line in its place.
98
119
  */
99
120
  declare const ChatThread: React$1.ForwardRefExoticComponent<ChatThreadProps & React$1.RefAttributes<HTMLDivElement>>;
121
+ interface ChatCopyActionProps {
122
+ /** The turn whose `text` (its Markdown source) is copied. */
123
+ message: Pick<ChatMessage, "text">;
124
+ /** The tooltip and accessible name. Defaults to "Copy". */
125
+ label?: string;
126
+ /** Both, for a moment after a copy. Defaults to "Copied". */
127
+ copiedLabel?: string;
128
+ /** Called after each copy, with whether the clipboard took the text. */
129
+ onCopy?: (success: boolean) => void;
130
+ /** How long the copied state shows, in ms. Defaults to 2000. */
131
+ resetAfter?: number;
132
+ }
133
+ /**
134
+ * The ready Copy action for `messageActions`: copies the turn's `text`, its
135
+ * Markdown source, and shows a check and "Copied" for a moment. A turn with
136
+ * no `text` (rich `content` only) has nothing to copy, so it draws nothing.
137
+ */
138
+ declare function ChatCopyAction({ message, label, copiedLabel, onCopy, resetAfter, }: ChatCopyActionProps): react_jsx_runtime.JSX.Element | null;
100
139
  interface ChatInputProps {
101
140
  value: string;
102
141
  onChange: (next: string) => void;
@@ -144,6 +183,12 @@ interface ChatInputProps {
144
183
  addFilesLabel?: string;
145
184
  /** Visual density for the complete composer surface. */
146
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;
147
192
  /** Quiet contextual copy between the composer controls. */
148
193
  helperText?: ReactNode;
149
194
  /** Privacy or visibility reassurance rendered below the composer. */
@@ -165,7 +210,7 @@ interface ChatInputProps {
165
210
  * `<Chat>` instead of composing `<ChatInput>` directly if you want
166
211
  * device-aware default modes.
167
212
  */
168
- 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;
169
214
  interface ChatProps {
170
215
  /** Conversation messages. */
171
216
  messages: ChatMessage[];
@@ -260,6 +305,12 @@ interface ChatProps {
260
305
  uploadingLabel?: string;
261
306
  /** Visual treatment for both empty and active text composers. */
262
307
  composerAppearance?: ChatInputProps["appearance"];
308
+ /**
309
+ * On phones (below 768px), fold an active conversation's empty composer
310
+ * into one row until its textarea is focused. Drafts and files stay
311
+ * expanded. Desktop and the new-conversation composer are unchanged.
312
+ */
313
+ mobileCompactComposer?: boolean;
263
314
  /** Quiet contextual copy inside the composer action row. */
264
315
  composerHelperText?: ReactNode;
265
316
  /** Privacy or visibility reassurance below the composer. */
@@ -330,6 +381,11 @@ interface ChatProps {
330
381
  onOpenAttachment?: (attachment: ChatAttachment) => void;
331
382
  /** What opening a message's file does, for its label and icon. Defaults to `"open"`. */
332
383
  attachmentAction?: ChatAttachmentAction;
384
+ /**
385
+ * The actions under each turn that offers them (`ChatMessage.actions`:
386
+ * assistant turns, by default), such as `ChatCopyAction`. See `ChatThread`.
387
+ */
388
+ messageActions?: (message: ChatMessage) => ReactNode;
333
389
  className?: string;
334
390
  }
335
391
  interface ChatHandle {
@@ -361,4 +417,4 @@ interface ChatHandle {
361
417
  */
362
418
  declare const Chat: React$1.ForwardRefExoticComponent<ChatProps & React$1.RefAttributes<ChatHandle>>;
363
419
 
364
- export { Chat, type ChatAttachment, type ChatAttachmentAction, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
420
+ export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
@@ -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,11 +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
+ MessageAction,
26
+ MessageActions,
25
27
  MessageContent
26
- } from "../chunk-JZOBIPFC.js";
28
+ } from "../chunk-H4XH2PPU.js";
27
29
  import {
28
30
  Marker,
29
31
  MarkerContent
@@ -32,12 +34,12 @@ import {
32
34
  InputGroup,
33
35
  InputGroupAddon,
34
36
  InputGroupTextarea
35
- } from "../chunk-MHYAVXOE.js";
37
+ } from "../chunk-QTPPNK6F.js";
36
38
  import "../chunk-6FC65AZQ.js";
37
39
  import {
38
40
  FileDropArea,
39
41
  FileDropzone
40
- } from "../chunk-M7FYGAGO.js";
42
+ } from "../chunk-HBRVOGLA.js";
41
43
  import {
42
44
  LazyMarkdown
43
45
  } from "../chunk-VY5U4VGV.js";
@@ -64,7 +66,7 @@ import {
64
66
  AttachmentMedia,
65
67
  AttachmentTitle,
66
68
  AttachmentTrigger
67
- } from "../chunk-EZCKWTH7.js";
69
+ } from "../chunk-CYDICVH2.js";
68
70
  import "../chunk-ZRLUUZHK.js";
69
71
  import {
70
72
  ActionDock
@@ -72,7 +74,10 @@ import {
72
74
  import "../chunk-QMA3WVB7.js";
73
75
  import {
74
76
  Button
75
- } from "../chunk-HQRWRRZJ.js";
77
+ } from "../chunk-W326GKML.js";
78
+ import {
79
+ useCopyToClipboard
80
+ } from "../chunk-ZWZAAGQF.js";
76
81
  import {
77
82
  useIsMobile
78
83
  } from "../chunk-PIU5SSUV.js";
@@ -98,6 +103,8 @@ import {
98
103
  ArrowRight,
99
104
  ArrowUp,
100
105
  ArrowUpRight,
106
+ Check,
107
+ Copy,
101
108
  Download,
102
109
  FileText,
103
110
  LockKeyhole,
@@ -106,6 +113,10 @@ import {
106
113
  X
107
114
  } from "lucide-react";
108
115
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
116
+ function offersActions(message) {
117
+ if (message.status) return false;
118
+ return message.actions ?? message.role === "assistant";
119
+ }
109
120
  function useArrivingUserTurnId(messages) {
110
121
  const seen = useRef(null);
111
122
  seen.current ??= new Set(messages.map((m) => m.id));
@@ -153,6 +164,7 @@ var ChatThread = forwardRef(
153
164
  markdownComponents,
154
165
  onOpenAttachment,
155
166
  attachmentAction = "open",
167
+ messageActions,
156
168
  className
157
169
  }, ref) {
158
170
  const markers = timelineMarkers(messages, /* @__PURE__ */ new Date());
@@ -186,6 +198,7 @@ var ChatThread = forwardRef(
186
198
  }
187
199
  const isFullWidth = m.width === "full";
188
200
  const marker = markers[index];
201
+ const actions = messageActions && offersActions(m) ? messageActions(m) : null;
189
202
  return /* @__PURE__ */ jsxs(
190
203
  MessageScrollerItem,
191
204
  {
@@ -222,7 +235,21 @@ var ChatThread = forwardRef(
222
235
  uploadingLabel,
223
236
  className: "px-0 pt-0"
224
237
  }
225
- )
238
+ ),
239
+ actions != null && actions !== false ? (
240
+ // Unboxed workspace turns pull the row out by the
241
+ // button's inset, so the first icon lines up with
242
+ // the text above it.
243
+ /* @__PURE__ */ jsx(
244
+ MessageActions,
245
+ {
246
+ className: cn(
247
+ workspace && (isUser ? "-me-2 pointer-coarse:-me-3.5" : "-ms-2 pointer-coarse:-ms-3.5")
248
+ ),
249
+ children: actions
250
+ }
251
+ )
252
+ ) : null
226
253
  ] }) })
227
254
  ]
228
255
  },
@@ -246,6 +273,29 @@ var ChatThread = forwardRef(
246
273
  ) });
247
274
  }
248
275
  );
276
+ function ChatCopyAction({
277
+ message,
278
+ label = "Copy",
279
+ copiedLabel = "Copied",
280
+ onCopy,
281
+ resetAfter = 2e3
282
+ }) {
283
+ const { copied, copy } = useCopyToClipboard({ resetAfter });
284
+ const text = message.text;
285
+ if (!text) return null;
286
+ return /* @__PURE__ */ jsx(
287
+ MessageAction,
288
+ {
289
+ tooltip: copied ? copiedLabel : label,
290
+ "data-copied": copied || void 0,
291
+ onClick: async () => {
292
+ const ok = await copy(text);
293
+ onCopy?.(ok);
294
+ },
295
+ children: copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {})
296
+ }
297
+ );
298
+ }
249
299
  function formatFileSize(size) {
250
300
  if (size === void 0) return "File";
251
301
  if (size < 1024) return `${size} B`;
@@ -335,12 +385,15 @@ function ChatInput({
335
385
  multiple = true,
336
386
  addFilesLabel = "Add files",
337
387
  appearance = "default",
388
+ compactOnBlur = false,
338
389
  helperText,
339
390
  privacyNote
340
391
  }) {
341
392
  const internalRef = useRef(null);
342
393
  const ref = inputRef ?? internalRef;
343
394
  const fileInputRef = useRef(null);
395
+ const [editing, setEditing] = useState(false);
396
+ const focusVersion = useRef(0);
344
397
  const isDisabled = pending || disabled;
345
398
  const attachmentsReady = attachments.length > 0 && attachments.every((item) => item.state !== "uploading");
346
399
  const canSubmit = !isDisabled && (Boolean(value.trim()) || attachmentsReady);
@@ -351,13 +404,27 @@ function ChatInput({
351
404
  };
352
405
  const spacious = appearance === "spacious";
353
406
  const composerSize = COMPOSER_SIZES[size];
407
+ const compact = compactOnBlur && !editing && value.length === 0 && attachments.length === 0;
354
408
  return /* @__PURE__ */ jsxs("div", { "data-slot": "chat-input-container", className: "w-full", children: [
355
409
  /* @__PURE__ */ jsxs(
356
410
  "form",
357
411
  {
358
412
  "data-slot": "chat-input",
359
413
  "data-appearance": appearance,
414
+ "data-compact": compact || void 0,
360
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
+ },
361
428
  onSubmit: (event) => {
362
429
  if (event.target !== event.currentTarget) return;
363
430
  event.preventDefault();
@@ -412,29 +479,31 @@ function ChatInput({
412
479
  submit();
413
480
  },
414
481
  placeholder,
415
- rows: composerSize.rows,
482
+ rows: compact ? 1 : composerSize.rows,
483
+ wrap: compact ? "off" : void 0,
416
484
  disabled: isDisabled,
417
485
  autoFocus,
418
486
  className: cn(
419
- 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,
420
488
  // Grows with the text, then scrolls rather than pushing the thread away.
421
- "max-h-48 leading-relaxed disabled:opacity-70 placeholder:text-sm",
422
- 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"
423
492
  )
424
493
  }
425
494
  ),
426
495
  /* @__PURE__ */ jsxs(
427
496
  InputGroupAddon,
428
497
  {
429
- align: "block-end",
498
+ align: compact ? "inline-end" : "block-end",
430
499
  "data-slot": "chat-input-actions",
431
500
  onClick: (event) => {
432
501
  if (!event.currentTarget.contains(event.target)) return;
433
502
  if (!event.target.closest("button")) ref.current?.focus();
434
503
  },
435
504
  className: cn(
436
- "gap-2",
437
- 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")
438
507
  ),
439
508
  children: [
440
509
  onFilesSelected && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
@@ -442,9 +511,10 @@ function ChatInput({
442
511
  Button,
443
512
  {
444
513
  type: "button",
445
- size: spacious ? "icon-lg" : "icon",
514
+ size: spacious && !compact ? "icon-lg" : "icon",
446
515
  variant: "ghost",
447
516
  "aria-label": "Add to conversation",
517
+ className: compactOnBlur ? "min-h-[44px] min-w-[44px] shrink-0" : void 0,
448
518
  children: /* @__PURE__ */ jsx(Plus, { className: cn(spacious && "h-5 w-5") })
449
519
  }
450
520
  ) }),
@@ -460,24 +530,28 @@ function ChatInput({
460
530
  title: voiceTitle,
461
531
  lang: voiceLang,
462
532
  onCommit: (text) => onSend(text),
463
- 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
+ )
464
538
  }
465
539
  ),
466
540
  helperText ? /* @__PURE__ */ jsx(
467
541
  "span",
468
542
  {
469
543
  "data-slot": "chat-input-helper",
470
- 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"),
471
545
  children: helperText
472
546
  }
473
- ) : /* @__PURE__ */ jsx("div", { className: "flex-1" }),
547
+ ) : /* @__PURE__ */ jsx("div", { className: compact ? "hidden" : "flex-1" }),
474
548
  submitLabel ? /* @__PURE__ */ jsxs(
475
549
  Button,
476
550
  {
477
551
  type: "submit",
478
552
  disabled: !canSubmit,
479
553
  "data-chat-action": "submit",
480
- className: spacious ? "rounded-xl" : void 0,
554
+ className: cn(spacious && "rounded-xl", compactOnBlur && "min-h-[44px] min-w-[44px] shrink-0"),
481
555
  children: [
482
556
  submitLabel,
483
557
  /* @__PURE__ */ jsx(ArrowRight, { className: "ml-2 h-4 w-4" })
@@ -487,11 +561,11 @@ function ChatInput({
487
561
  Button,
488
562
  {
489
563
  type: "submit",
490
- size: spacious ? "icon-lg" : "icon",
564
+ size: spacious && !compact ? "icon-lg" : "icon",
491
565
  disabled: !canSubmit,
492
566
  "aria-label": "Send",
493
567
  "data-chat-action": "submit",
494
- className: spacious ? "rounded-xl" : void 0,
568
+ className: cn(spacious && "rounded-xl", compactOnBlur && "min-h-[44px] min-w-[44px] shrink-0"),
495
569
  children: /* @__PURE__ */ jsx(ArrowUp, { className: cn(spacious && "h-5 w-5") })
496
570
  }
497
571
  )
@@ -631,6 +705,7 @@ var Chat = forwardRef(function Chat2({
631
705
  addFilesLabel,
632
706
  uploadingLabel,
633
707
  composerAppearance = "default",
708
+ mobileCompactComposer = false,
634
709
  composerHelperText,
635
710
  composerPrivacyNote,
636
711
  emptyFileDropzone = false,
@@ -655,6 +730,7 @@ var Chat = forwardRef(function Chat2({
655
730
  markdownComponents,
656
731
  onOpenAttachment,
657
732
  attachmentAction,
733
+ messageActions,
658
734
  className
659
735
  }, ref) {
660
736
  const isMobile = useIsMobile();
@@ -851,6 +927,7 @@ var Chat = forwardRef(function Chat2({
851
927
  markdownComponents,
852
928
  onOpenAttachment,
853
929
  attachmentAction,
930
+ messageActions,
854
931
  className: cn(
855
932
  "mb-3",
856
933
  application && "min-h-0 flex-1 px-4 py-4 md:px-0 md:py-0",
@@ -865,7 +942,8 @@ var Chat = forwardRef(function Chat2({
865
942
  className: cn(
866
943
  "pt-3",
867
944
  application && "px-4 py-3 md:bg-transparent md:px-0 md:pb-0 md:backdrop-blur-none md:supports-backdrop-filter:bg-transparent",
868
- 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"
945
+ 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",
946
+ workspace && mobileCompactComposer && "max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))] max-md:pt-3"
869
947
  ),
870
948
  children: [
871
949
  /* @__PURE__ */ jsx(
@@ -888,6 +966,7 @@ var Chat = forwardRef(function Chat2({
888
966
  addFilesLabel,
889
967
  uploadingLabel,
890
968
  appearance: composerAppearance,
969
+ compactOnBlur: mobileCompactComposer && isMobile,
891
970
  helperText: composerHelperText,
892
971
  privacyNote: composerPrivacyNote,
893
972
  className: workspace ? "mx-auto max-w-3xl rounded-3xl shadow-sm" : void 0
@@ -903,6 +982,7 @@ var Chat = forwardRef(function Chat2({
903
982
  });
904
983
  export {
905
984
  Chat,
985
+ ChatCopyAction,
906
986
  ChatInput,
907
987
  ChatThread
908
988
  };
@@ -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",