@epicbagel/saas-kit 1.25.0 → 1.26.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.
@@ -17,24 +17,17 @@ var _chunkPNI354BYcjs = require('../chunk-PNI354BY.cjs');
17
17
  var _chunkM4V7OJNFcjs = require('../chunk-M4V7OJNF.cjs');
18
18
 
19
19
 
20
-
21
- var _chunkB765DSRGcjs = require('../chunk-B765DSRG.cjs');
22
-
23
-
20
+ var _chunkU7XNNLVPcjs = require('../chunk-U7XNNLVP.cjs');
24
21
 
25
22
 
26
23
 
27
-
28
-
29
- var _chunk23IETCLVcjs = require('../chunk-23IETCLV.cjs');
30
- require('../chunk-DMPSKGFM.cjs');
24
+ var _chunkB765DSRGcjs = require('../chunk-B765DSRG.cjs');
31
25
 
32
26
 
33
27
 
34
28
 
35
29
 
36
30
  var _chunkNWDPH2Z7cjs = require('../chunk-NWDPH2Z7.cjs');
37
- require('../chunk-Z2H4LJVG.cjs');
38
31
 
39
32
 
40
33
 
@@ -63,6 +56,19 @@ var _chunk2BHI5ZWJcjs = require('../chunk-2BHI5ZWJ.cjs');
63
56
 
64
57
 
65
58
  var _chunkZAITUDYDcjs = require('../chunk-ZAITUDYD.cjs');
59
+
60
+
61
+ var _chunkZBWL7OUWcjs = require('../chunk-ZBWL7OUW.cjs');
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+ var _chunk23IETCLVcjs = require('../chunk-23IETCLV.cjs');
70
+ require('../chunk-DMPSKGFM.cjs');
71
+ require('../chunk-Z2H4LJVG.cjs');
66
72
  require('../chunk-U2NES7VR.cjs');
67
73
 
68
74
 
@@ -162,6 +168,24 @@ function timelineMarkers(messages, now) {
162
168
  function TimelineMarker({ label }) {
163
169
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkB765DSRGcjs.Marker, { variant: "separator", role: "separator", "aria-label": label, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkB765DSRGcjs.MarkerContent, { children: label }) });
164
170
  }
171
+ function authorLines(messages, markers) {
172
+ let previous = null;
173
+ return messages.map((message, index) => {
174
+ if (message.status === "running") return false;
175
+ const shown = Boolean(message.author) && (!previous || Boolean(markers[index]) || previous.role !== message.role || _optionalChain([previous, 'access', _ => _.author, 'optionalAccess', _2 => _2.id]) !== message.author.id);
176
+ previous = message;
177
+ return shown;
178
+ });
179
+ }
180
+ function ChatAuthorLine({ author, align }) {
181
+ const avatar = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkU7XNNLVPcjs.PersonAvatar, { name: author.name, photoUrl: author.avatarUrl, size: 20, fallbackClassName: "text-[9px]" });
182
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot": "chat-author", className: _chunkKWLSBTB2cjs.cn.call(void 0, "flex items-center gap-2 text-xs", align === "end" ? "self-end" : "self-start"), children: [
183
+ align === "start" ? avatar : null,
184
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "font-medium text-foreground", children: author.name }),
185
+ author.detail ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-muted-foreground", children: author.detail }) : null,
186
+ align === "end" ? avatar : null
187
+ ] });
188
+ }
165
189
  function ChatRunningLine({
166
190
  label,
167
191
  align = "start",
@@ -183,9 +207,13 @@ var ChatThread = _react.forwardRef.call(void 0,
183
207
  onOpenAttachment,
184
208
  attachmentAction = "open",
185
209
  messageActions,
210
+ navigator,
211
+ navigatorLabel,
212
+ navigatorSide,
186
213
  className
187
214
  }, ref) {
188
215
  const markers = timelineMarkers(messages, /* @__PURE__ */ new Date());
216
+ const authorShown = authorLines(messages, markers);
189
217
  const anchorId = useArrivingUserTurnId(messages);
190
218
  const workspace = appearance === "workspace";
191
219
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk23IETCLVcjs.MessageScrollerProvider, { autoScroll: true, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -226,6 +254,7 @@ var ChatThread = _react.forwardRef.call(void 0,
226
254
  children: [
227
255
  marker ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TimelineMarker, { label: marker }) : null,
228
256
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkNWDPH2Z7cjs.Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkNWDPH2Z7cjs.MessageContent, { children: [
257
+ m.author && authorShown[index] ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ChatAuthorLine, { author: m.author, align: isUser ? "end" : "start" }) : null,
229
258
  (m.content || m.text) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
230
259
  _chunkV4YWTRLDcjs.Bubble,
231
260
  {
@@ -285,7 +314,17 @@ var ChatThread = _react.forwardRef.call(void 0,
285
314
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunk23IETCLVcjs.MessageScrollerButton, { variant: "outline", className: "rounded-full shadow-sm", children: [
286
315
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.ArrowDown, { className: "h-4 w-4" }),
287
316
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "sr-only", children: scrollButtonLabel })
288
- ] })
317
+ ] }),
318
+ navigator && navigator.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
319
+ _chunkZBWL7OUWcjs.ChatTurnNavigator,
320
+ {
321
+ items: navigator,
322
+ order: messages.map((m) => m.id),
323
+ label: navigatorLabel,
324
+ side: navigatorSide,
325
+ columnWidth: workspace ? 768 : void 0
326
+ }
327
+ ) : null
289
328
  ]
290
329
  }
291
330
  ) });
@@ -308,7 +347,7 @@ function ChatCopyAction({
308
347
  "data-copied": copied || void 0,
309
348
  onClick: async () => {
310
349
  const ok = await copy(text);
311
- _optionalChain([onCopy, 'optionalCall', _ => _(ok)]);
350
+ _optionalChain([onCopy, 'optionalCall', _3 => _3(ok)]);
312
351
  },
313
352
  children: copied ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Check, {}) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Copy, {})
314
353
  }
@@ -353,7 +392,7 @@ function ChatAttachmentList({
353
392
  {
354
393
  "aria-label": `${verb} ${attachment.name}`,
355
394
  title: `${verb} ${attachment.name}`,
356
- onClick: () => _optionalChain([onOpen, 'optionalCall', _2 => _2(attachment)]),
395
+ onClick: () => _optionalChain([onOpen, 'optionalCall', _4 => _4(attachment)]),
357
396
  className: "rounded-xl focus-visible:ring-2 focus-visible:ring-ring/50"
358
397
  }
359
398
  ),
@@ -533,7 +572,7 @@ function ChatInput({
533
572
  "data-slot": "chat-input-actions",
534
573
  onClick: (event) => {
535
574
  if (!event.currentTarget.contains(event.target)) return;
536
- if (!event.target.closest("button")) _optionalChain([ref, 'access', _3 => _3.current, 'optionalAccess', _4 => _4.focus, 'call', _5 => _5()]);
575
+ if (!event.target.closest("button")) _optionalChain([ref, 'access', _5 => _5.current, 'optionalAccess', _6 => _6.focus, 'call', _7 => _7()]);
537
576
  },
538
577
  className: _chunkKWLSBTB2cjs.cn.call(void 0,
539
578
  compact ? "gap-1 px-2 pb-2 pt-0" : "gap-2",
@@ -552,7 +591,7 @@ function ChatInput({
552
591
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Plus, { className: _chunkKWLSBTB2cjs.cn.call(void 0, spacious && "h-5 w-5") })
553
592
  }
554
593
  ) }),
555
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuContent, { align: "start", side: "top", className: "min-w-52", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZAITUDYDcjs.DropdownMenuItem, { onSelect: () => _optionalChain([fileInputRef, 'access', _6 => _6.current, 'optionalAccess', _7 => _7.click, 'call', _8 => _8()]), children: [
594
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZAITUDYDcjs.DropdownMenuContent, { align: "start", side: "top", className: "min-w-52", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZAITUDYDcjs.DropdownMenuItem, { onSelect: () => _optionalChain([fileInputRef, 'access', _8 => _8.current, 'optionalAccess', _9 => _9.click, 'call', _10 => _10()]), children: [
556
595
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.Paperclip, { className: "h-4 w-4" }),
557
596
  addFilesLabel
558
597
  ] }) })
@@ -790,11 +829,11 @@ function MobileReplySuggestions({
790
829
  className: "max-h-[min(85dvh,40rem)] overflow-hidden pb-[env(safe-area-inset-bottom)]",
791
830
  onOpenAutoFocus: (event) => {
792
831
  event.preventDefault();
793
- _optionalChain([closeRef, 'access', _9 => _9.current, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11({ preventScroll: true })]);
832
+ _optionalChain([closeRef, 'access', _11 => _11.current, 'optionalAccess', _12 => _12.focus, 'call', _13 => _13({ preventScroll: true })]);
794
833
  },
795
834
  onCloseAutoFocus: (event) => {
796
835
  event.preventDefault();
797
- if (_optionalChain([triggerRef, 'access', _12 => _12.current, 'optionalAccess', _13 => _13.isConnected]) && !triggerRef.current.disabled) {
836
+ if (_optionalChain([triggerRef, 'access', _14 => _14.current, 'optionalAccess', _15 => _15.isConnected]) && !triggerRef.current.disabled) {
798
837
  triggerRef.current.focus({ preventScroll: true });
799
838
  }
800
839
  },
@@ -921,6 +960,9 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
921
960
  onOpenAttachment,
922
961
  attachmentAction,
923
962
  messageActions,
963
+ navigator,
964
+ navigatorLabel,
965
+ navigatorSide,
924
966
  className
925
967
  }, ref) {
926
968
  const isMobile = _chunkR35FT53Gcjs.useIsMobile.call(void 0, );
@@ -951,7 +993,7 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
951
993
  }
952
994
  reportSendError(cause);
953
995
  }
954
- window.setTimeout(() => _optionalChain([inputRef, 'access', _14 => _14.current, 'optionalAccess', _15 => _15.focus, 'call', _16 => _16()]), 0);
996
+ window.setTimeout(() => _optionalChain([inputRef, 'access', _16 => _16.current, 'optionalAccess', _17 => _17.focus, 'call', _18 => _18()]), 0);
955
997
  },
956
998
  [attachments, mode, onSend, pending, reportSendError]
957
999
  );
@@ -964,14 +1006,14 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
964
1006
  } catch (cause) {
965
1007
  reportSendError(cause);
966
1008
  }
967
- if (focusInput) window.setTimeout(() => _optionalChain([inputRef, 'access', _17 => _17.current, 'optionalAccess', _18 => _18.focus, 'call', _19 => _19()]), 0);
1009
+ if (focusInput) window.setTimeout(() => _optionalChain([inputRef, 'access', _19 => _19.current, 'optionalAccess', _20 => _20.focus, 'call', _21 => _21()]), 0);
968
1010
  },
969
1011
  [onSend, pending, reportSendError]
970
1012
  );
971
1013
  _react.useImperativeHandle.call(void 0,
972
1014
  ref,
973
1015
  () => ({
974
- focusInput: () => _optionalChain([inputRef, 'access', _20 => _20.current, 'optionalAccess', _21 => _21.focus, 'call', _22 => _22()]),
1016
+ focusInput: () => _optionalChain([inputRef, 'access', _22 => _22.current, 'optionalAccess', _23 => _23.focus, 'call', _24 => _24()]),
975
1017
  clearInput: () => setInput("")
976
1018
  }),
977
1019
  []
@@ -979,7 +1021,7 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
979
1021
  const dropFiles = _react.useCallback.call(void 0,
980
1022
  (files) => {
981
1023
  if (!conversationStarted && mode === "voice") setUserMode("text");
982
- return _optionalChain([onFilesSelected, 'optionalCall', _23 => _23(files)]);
1024
+ return _optionalChain([onFilesSelected, 'optionalCall', _25 => _25(files)]);
983
1025
  },
984
1026
  [conversationStarted, mode, onFilesSelected]
985
1027
  );
@@ -1144,6 +1186,9 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
1144
1186
  onOpenAttachment,
1145
1187
  attachmentAction,
1146
1188
  messageActions,
1189
+ navigator,
1190
+ navigatorLabel,
1191
+ navigatorSide,
1147
1192
  className: _chunkKWLSBTB2cjs.cn.call(void 0,
1148
1193
  "mb-3",
1149
1194
  application && "min-h-0 flex-1 px-4 py-4 md:px-0 md:py-0",
@@ -1187,7 +1232,7 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
1187
1232
  closeLabel: replySuggestionsCloseLabel,
1188
1233
  disabled: pending
1189
1234
  },
1190
- JSON.stringify([_optionalChain([messages, 'access', _24 => _24.at, 'call', _25 => _25(-1), 'optionalAccess', _26 => _26.id]), replySuggestions])
1235
+ JSON.stringify([_optionalChain([messages, 'access', _26 => _26.at, 'call', _27 => _27(-1), 'optionalAccess', _28 => _28.id]), replySuggestions])
1191
1236
  ) : void 0,
1192
1237
  value: input,
1193
1238
  onChange: setInput,
@@ -1226,4 +1271,5 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
1226
1271
 
1227
1272
 
1228
1273
 
1229
- exports.Chat = Chat; exports.ChatCopyAction = ChatCopyAction; exports.ChatInput = ChatInput; exports.ChatThread = ChatThread;
1274
+
1275
+ exports.Chat = Chat; exports.ChatCopyAction = ChatCopyAction; exports.ChatInput = ChatInput; exports.ChatThread = ChatThread; exports.authorLines = authorLines;
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { VoiceSessionControlsProps } from './voice-session-controls.cjs';
5
+ import { ChatNavigatorItem } from './chat-turn-navigator.cjs';
5
6
  import { MarkdownComponents } from './markdown.cjs';
6
7
  import './button.cjs';
7
8
  import 'class-variance-authority/types';
@@ -10,6 +11,17 @@ import 'react-markdown';
10
11
  import './rich-text.cjs';
11
12
 
12
13
  type ChatRole = "user" | "assistant";
14
+ /** A turn's author, as the thread names them above their turns. */
15
+ interface ChatMessageAuthor {
16
+ /** Stable for the person: consecutive turns with the same id are named once. */
17
+ id: string;
18
+ name: string;
19
+ /** Their photo; initials stand in without one. */
20
+ avatarUrl?: string | null;
21
+ /** A quiet note after the name, such as how the turn arrived ("by email"). */
22
+ detail?: string;
23
+ }
24
+
13
25
  type ChatAttachment = {
14
26
  id: string;
15
27
  name: string;
@@ -38,6 +50,12 @@ type ChatMessage = {
38
50
  content?: ReactNode;
39
51
  /** Files that belong to this turn. */
40
52
  attachments?: ReadonlyArray<ChatAttachment>;
53
+ /**
54
+ * Who wrote the turn. The thread names them, with their avatar, above the
55
+ * first of a run of turns by the same author, so a conversation with
56
+ * several people in it says who said what. Turns without one are unnamed.
57
+ */
58
+ author?: ChatMessageAuthor;
41
59
  /**
42
60
  * `"running"`: a reply still being worked on. The thread draws the turn as
43
61
  * its running line, the one `pending` adds at the end, labelled with `text`
@@ -105,8 +123,24 @@ interface ChatThreadProps {
105
123
  * pointer reaches it; return `null` for none.
106
124
  */
107
125
  messageActions?: (message: ChatMessage) => ReactNode;
126
+ /**
127
+ * Places to jump to, drawn as a rail of ticks in the thread's gutter (see
128
+ * `ChatTurnNavigator`): the one in view marked, a preview of each on hover
129
+ * or focus. It shows only where the gutter has room; omit it for none.
130
+ */
131
+ navigator?: readonly ChatNavigatorItem[];
132
+ /** The rail's landmark name. */
133
+ navigatorLabel?: string;
134
+ /** The gutter the rail sits in. */
135
+ navigatorSide?: "start" | "end";
108
136
  className?: string;
109
137
  }
138
+ /**
139
+ * Which turns name their author: the first of each run of turns by the same
140
+ * author on the same side. A time marker, a turn by someone else or a turn
141
+ * with no author between them starts a new run; a running line doesn't.
142
+ */
143
+ declare function authorLines(messages: ReadonlyArray<ChatMessage>, markers: ReadonlyArray<string | null>): boolean[];
110
144
  /**
111
145
  * Scrollable message thread, built on shadcn's headless `MessageScroller`
112
146
  * (streaming-aware auto-scroll, anchored turns) + the `Message`/`Bubble`
@@ -425,6 +459,15 @@ interface ChatProps {
425
459
  * assistant turns, by default), such as `ChatCopyAction`. See `ChatThread`.
426
460
  */
427
461
  messageActions?: (message: ChatMessage) => ReactNode;
462
+ /**
463
+ * Places in the conversation to jump to, as a rail of ticks in the
464
+ * thread's gutter, each previewed on hover or focus. See `ChatThread`.
465
+ */
466
+ navigator?: readonly ChatNavigatorItem[];
467
+ /** The rail's landmark name. Defaults to "Conversation". */
468
+ navigatorLabel?: string;
469
+ /** The gutter the rail sits in. Defaults to `"start"`. */
470
+ navigatorSide?: "start" | "end";
428
471
  className?: string;
429
472
  }
430
473
  interface ChatHandle {
@@ -457,4 +500,4 @@ interface ChatHandle {
457
500
  */
458
501
  declare const Chat: React$1.ForwardRefExoticComponent<ChatProps & React$1.RefAttributes<ChatHandle>>;
459
502
 
460
- export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
503
+ export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatMessageAuthor, ChatNavigatorItem, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps, authorLines };
@@ -2,6 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { VoiceSessionControlsProps } from './voice-session-controls.js';
5
+ import { ChatNavigatorItem } from './chat-turn-navigator.js';
5
6
  import { MarkdownComponents } from './markdown.js';
6
7
  import './button.js';
7
8
  import 'class-variance-authority/types';
@@ -10,6 +11,17 @@ import 'react-markdown';
10
11
  import './rich-text.js';
11
12
 
12
13
  type ChatRole = "user" | "assistant";
14
+ /** A turn's author, as the thread names them above their turns. */
15
+ interface ChatMessageAuthor {
16
+ /** Stable for the person: consecutive turns with the same id are named once. */
17
+ id: string;
18
+ name: string;
19
+ /** Their photo; initials stand in without one. */
20
+ avatarUrl?: string | null;
21
+ /** A quiet note after the name, such as how the turn arrived ("by email"). */
22
+ detail?: string;
23
+ }
24
+
13
25
  type ChatAttachment = {
14
26
  id: string;
15
27
  name: string;
@@ -38,6 +50,12 @@ type ChatMessage = {
38
50
  content?: ReactNode;
39
51
  /** Files that belong to this turn. */
40
52
  attachments?: ReadonlyArray<ChatAttachment>;
53
+ /**
54
+ * Who wrote the turn. The thread names them, with their avatar, above the
55
+ * first of a run of turns by the same author, so a conversation with
56
+ * several people in it says who said what. Turns without one are unnamed.
57
+ */
58
+ author?: ChatMessageAuthor;
41
59
  /**
42
60
  * `"running"`: a reply still being worked on. The thread draws the turn as
43
61
  * its running line, the one `pending` adds at the end, labelled with `text`
@@ -105,8 +123,24 @@ interface ChatThreadProps {
105
123
  * pointer reaches it; return `null` for none.
106
124
  */
107
125
  messageActions?: (message: ChatMessage) => ReactNode;
126
+ /**
127
+ * Places to jump to, drawn as a rail of ticks in the thread's gutter (see
128
+ * `ChatTurnNavigator`): the one in view marked, a preview of each on hover
129
+ * or focus. It shows only where the gutter has room; omit it for none.
130
+ */
131
+ navigator?: readonly ChatNavigatorItem[];
132
+ /** The rail's landmark name. */
133
+ navigatorLabel?: string;
134
+ /** The gutter the rail sits in. */
135
+ navigatorSide?: "start" | "end";
108
136
  className?: string;
109
137
  }
138
+ /**
139
+ * Which turns name their author: the first of each run of turns by the same
140
+ * author on the same side. A time marker, a turn by someone else or a turn
141
+ * with no author between them starts a new run; a running line doesn't.
142
+ */
143
+ declare function authorLines(messages: ReadonlyArray<ChatMessage>, markers: ReadonlyArray<string | null>): boolean[];
110
144
  /**
111
145
  * Scrollable message thread, built on shadcn's headless `MessageScroller`
112
146
  * (streaming-aware auto-scroll, anchored turns) + the `Message`/`Bubble`
@@ -425,6 +459,15 @@ interface ChatProps {
425
459
  * assistant turns, by default), such as `ChatCopyAction`. See `ChatThread`.
426
460
  */
427
461
  messageActions?: (message: ChatMessage) => ReactNode;
462
+ /**
463
+ * Places in the conversation to jump to, as a rail of ticks in the
464
+ * thread's gutter, each previewed on hover or focus. See `ChatThread`.
465
+ */
466
+ navigator?: readonly ChatNavigatorItem[];
467
+ /** The rail's landmark name. Defaults to "Conversation". */
468
+ navigatorLabel?: string;
469
+ /** The gutter the rail sits in. Defaults to `"start"`. */
470
+ navigatorSide?: "start" | "end";
428
471
  className?: string;
429
472
  }
430
473
  interface ChatHandle {
@@ -457,4 +500,4 @@ interface ChatHandle {
457
500
  */
458
501
  declare const Chat: React$1.ForwardRefExoticComponent<ChatProps & React$1.RefAttributes<ChatHandle>>;
459
502
 
460
- export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps };
503
+ export { Chat, type ChatAttachment, type ChatAttachmentAction, ChatCopyAction, type ChatCopyActionProps, type ChatHandle, ChatInput, type ChatInputProps, type ChatMessage, type ChatMessageAuthor, ChatNavigatorItem, type ChatProps, type ChatRole, ChatThread, type ChatThreadProps, authorLines };
@@ -15,26 +15,19 @@ import {
15
15
  Suggestion,
16
16
  Suggestions
17
17
  } from "../chunk-IJG2B3Y2.js";
18
+ import {
19
+ PersonAvatar
20
+ } from "../chunk-NXFZULS3.js";
18
21
  import {
19
22
  Marker,
20
23
  MarkerContent
21
24
  } from "../chunk-IW4ZIXXG.js";
22
- import {
23
- MessageScroller,
24
- MessageScrollerButton,
25
- MessageScrollerContent,
26
- MessageScrollerItem,
27
- MessageScrollerProvider,
28
- MessageScrollerViewport
29
- } from "../chunk-OUZSIA5G.js";
30
- import "../chunk-36LNA4WM.js";
31
25
  import {
32
26
  Message,
33
27
  MessageAction,
34
28
  MessageActions,
35
29
  MessageContent
36
30
  } from "../chunk-H4XH2PPU.js";
37
- import "../chunk-GA5SX7FE.js";
38
31
  import {
39
32
  InputGroup,
40
33
  InputGroupAddon,
@@ -63,6 +56,19 @@ import {
63
56
  DropdownMenuItem,
64
57
  DropdownMenuTrigger
65
58
  } from "../chunk-V4LE2HWP.js";
59
+ import {
60
+ ChatTurnNavigator
61
+ } from "../chunk-HO6DRDGD.js";
62
+ import {
63
+ MessageScroller,
64
+ MessageScrollerButton,
65
+ MessageScrollerContent,
66
+ MessageScrollerItem,
67
+ MessageScrollerProvider,
68
+ MessageScrollerViewport
69
+ } from "../chunk-OUZSIA5G.js";
70
+ import "../chunk-36LNA4WM.js";
71
+ import "../chunk-GA5SX7FE.js";
66
72
  import "../chunk-ZRLUUZHK.js";
67
73
  import {
68
74
  Bubble,
@@ -162,6 +168,24 @@ function timelineMarkers(messages, now) {
162
168
  function TimelineMarker({ label }) {
163
169
  return /* @__PURE__ */ jsx(Marker, { variant: "separator", role: "separator", "aria-label": label, children: /* @__PURE__ */ jsx(MarkerContent, { children: label }) });
164
170
  }
171
+ function authorLines(messages, markers) {
172
+ let previous = null;
173
+ return messages.map((message, index) => {
174
+ if (message.status === "running") return false;
175
+ const shown = Boolean(message.author) && (!previous || Boolean(markers[index]) || previous.role !== message.role || previous.author?.id !== message.author.id);
176
+ previous = message;
177
+ return shown;
178
+ });
179
+ }
180
+ function ChatAuthorLine({ author, align }) {
181
+ const avatar = /* @__PURE__ */ jsx(PersonAvatar, { name: author.name, photoUrl: author.avatarUrl, size: 20, fallbackClassName: "text-[9px]" });
182
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "chat-author", className: cn("flex items-center gap-2 text-xs", align === "end" ? "self-end" : "self-start"), children: [
183
+ align === "start" ? avatar : null,
184
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: author.name }),
185
+ author.detail ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: author.detail }) : null,
186
+ align === "end" ? avatar : null
187
+ ] });
188
+ }
165
189
  function ChatRunningLine({
166
190
  label,
167
191
  align = "start",
@@ -183,9 +207,13 @@ var ChatThread = forwardRef(
183
207
  onOpenAttachment,
184
208
  attachmentAction = "open",
185
209
  messageActions,
210
+ navigator,
211
+ navigatorLabel,
212
+ navigatorSide,
186
213
  className
187
214
  }, ref) {
188
215
  const markers = timelineMarkers(messages, /* @__PURE__ */ new Date());
216
+ const authorShown = authorLines(messages, markers);
189
217
  const anchorId = useArrivingUserTurnId(messages);
190
218
  const workspace = appearance === "workspace";
191
219
  return /* @__PURE__ */ jsx(MessageScrollerProvider, { autoScroll: true, children: /* @__PURE__ */ jsxs(
@@ -226,6 +254,7 @@ var ChatThread = forwardRef(
226
254
  children: [
227
255
  marker ? /* @__PURE__ */ jsx(TimelineMarker, { label: marker }) : null,
228
256
  /* @__PURE__ */ jsx(Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
257
+ m.author && authorShown[index] ? /* @__PURE__ */ jsx(ChatAuthorLine, { author: m.author, align: isUser ? "end" : "start" }) : null,
229
258
  (m.content || m.text) && /* @__PURE__ */ jsx(
230
259
  Bubble,
231
260
  {
@@ -285,7 +314,17 @@ var ChatThread = forwardRef(
285
314
  /* @__PURE__ */ jsxs(MessageScrollerButton, { variant: "outline", className: "rounded-full shadow-sm", children: [
286
315
  /* @__PURE__ */ jsx(ArrowDown, { className: "h-4 w-4" }),
287
316
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: scrollButtonLabel })
288
- ] })
317
+ ] }),
318
+ navigator && navigator.length > 0 ? /* @__PURE__ */ jsx(
319
+ ChatTurnNavigator,
320
+ {
321
+ items: navigator,
322
+ order: messages.map((m) => m.id),
323
+ label: navigatorLabel,
324
+ side: navigatorSide,
325
+ columnWidth: workspace ? 768 : void 0
326
+ }
327
+ ) : null
289
328
  ]
290
329
  }
291
330
  ) });
@@ -921,6 +960,9 @@ var Chat = forwardRef(function Chat2({
921
960
  onOpenAttachment,
922
961
  attachmentAction,
923
962
  messageActions,
963
+ navigator,
964
+ navigatorLabel,
965
+ navigatorSide,
924
966
  className
925
967
  }, ref) {
926
968
  const isMobile = useIsMobile();
@@ -1144,6 +1186,9 @@ var Chat = forwardRef(function Chat2({
1144
1186
  onOpenAttachment,
1145
1187
  attachmentAction,
1146
1188
  messageActions,
1189
+ navigator,
1190
+ navigatorLabel,
1191
+ navigatorSide,
1147
1192
  className: cn(
1148
1193
  "mb-3",
1149
1194
  application && "min-h-0 flex-1 px-4 py-4 md:px-0 md:py-0",
@@ -1225,5 +1270,6 @@ export {
1225
1270
  Chat,
1226
1271
  ChatCopyAction,
1227
1272
  ChatInput,
1228
- ChatThread
1273
+ ChatThread,
1274
+ authorLines
1229
1275
  };
@@ -8,13 +8,13 @@ var _chunkSJB4DPGQcjs = require('../chunk-SJB4DPGQ.cjs');
8
8
  var _chunkVKYRB37Ocjs = require('../chunk-VKYRB37O.cjs');
9
9
 
10
10
 
11
- var _chunkR3DQS6U4cjs = require('../chunk-R3DQS6U4.cjs');
11
+ var _chunkJ467F76Icjs = require('../chunk-J467F76I.cjs');
12
12
 
13
13
 
14
- var _chunk2HV2KKOEcjs = require('../chunk-2HV2KKOE.cjs');
14
+ var _chunkR3DQS6U4cjs = require('../chunk-R3DQS6U4.cjs');
15
15
 
16
16
 
17
- var _chunkJ467F76Icjs = require('../chunk-J467F76I.cjs');
17
+ var _chunk2HV2KKOEcjs = require('../chunk-2HV2KKOE.cjs');
18
18
 
19
19
 
20
20
  var _chunk373W6VADcjs = require('../chunk-373W6VAD.cjs');
@@ -6,15 +6,15 @@ import {
6
6
  import {
7
7
  PanelSection
8
8
  } from "../chunk-KI7SWNIN.js";
9
+ import {
10
+ Stack
11
+ } from "../chunk-WNWBKEWV.js";
9
12
  import {
10
13
  Card
11
14
  } from "../chunk-35YMAIEL.js";
12
15
  import {
13
16
  Textarea
14
17
  } from "../chunk-6FC65AZQ.js";
15
- import {
16
- Stack
17
- } from "../chunk-WNWBKEWV.js";
18
18
  import {
19
19
  Text
20
20
  } from "../chunk-EIY5LLSP.js";
@@ -2,56 +2,10 @@
2
2
 
3
3
 
4
4
 
5
+ var _chunkU7XNNLVPcjs = require('../chunk-U7XNNLVP.cjs');
6
+ require('../chunk-WABCLRBR.cjs');
7
+ require('../chunk-KWLSBTB2.cjs');
5
8
 
6
- var _chunkWABCLRBRcjs = require('../chunk-WABCLRBR.cjs');
7
9
 
8
10
 
9
- var _chunkKWLSBTB2cjs = require('../chunk-KWLSBTB2.cjs');
10
-
11
- // src/primitives/person-avatar.tsx
12
- var _jsxruntime = require('react/jsx-runtime');
13
- function initials(name) {
14
- const words = name.trim().split(/\s+/).filter(Boolean);
15
- const first = words[0];
16
- if (!first) return "?";
17
- const last = words[words.length - 1];
18
- if (words.length === 1 || !last) return first.slice(0, 2).toUpperCase();
19
- return (first.charAt(0) + last.charAt(0)).toUpperCase();
20
- }
21
- function PersonAvatar({
22
- name,
23
- photoUrl,
24
- size,
25
- className,
26
- fallbackClassName,
27
- fallbackSize = "default"
28
- }) {
29
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
30
- _chunkWABCLRBRcjs.Avatar,
31
- {
32
- className: _chunkKWLSBTB2cjs.cn.call(void 0, "shrink-0 overflow-hidden rounded-full bg-muted", className),
33
- style: size != null ? { width: size, height: size } : void 0,
34
- children: [
35
- photoUrl ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
36
- _chunkWABCLRBRcjs.AvatarImage,
37
- {
38
- src: photoUrl,
39
- alt: name,
40
- className: "h-full w-full object-cover"
41
- }
42
- ) : null,
43
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
44
- _chunkWABCLRBRcjs.AvatarFallback,
45
- {
46
- className: _chunkKWLSBTB2cjs.cn.call(void 0, "h-full w-full font-medium", fallbackSize === "default" ? "text-xs" : "text-xl", fallbackClassName),
47
- children: initials(name)
48
- }
49
- )
50
- ]
51
- }
52
- );
53
- }
54
-
55
-
56
-
57
- exports.PersonAvatar = PersonAvatar; exports.initials = initials;
11
+ exports.PersonAvatar = _chunkU7XNNLVPcjs.PersonAvatar; exports.initials = _chunkU7XNNLVPcjs.initials;