@tangle-network/ui 11.24.0 → 11.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cf29d7e: Chat transcripts can carry more than one person beside the agent, such as a client and an attorney. `ChatMessage`, `UserMessage` and `AgentTimeline` message items take an optional `author` (`ChatAuthor`: `id`, `name`, optional `role` and `avatarUrl`), and `ChatMessage`, `UserMessage` and `AgentTimeline` take the reader's `viewerId`. The reader's own messages keep the end-aligned inverse bubble. Anyone else's sit at the start on a card under a `MessageAuthor` line: avatar (image, or initials on a tone keyed to the author's id), name, and role tag. An agent's author line opens its turn in `AgentTimeline` and is not repeated after each tool row. Without `author`, every component renders exactly the markup it did before. `MessageAuthor`, `isViewerMessage` and `ChatAuthor` are exported from `./chat`, and `ChatAuthor` from `./types`.
8
+
3
9
  ## 11.24.0
4
10
 
5
11
  ### Minor Changes
@@ -1,3 +1,4 @@
1
+ import { ChatAuthor } from "../types/message.js";
1
2
  import { ToolPart } from "../types/parts.js";
2
3
  import { MessageRole } from "./chat-message.js";
3
4
  import { ToolCallData } from "../run/tool-call-feed.js";
@@ -15,6 +16,12 @@ export interface AgentTimelineMessageItem {
15
16
  * timestamp of its own — the turn's user message dates the exchange. */
16
17
  timestamp?: Date;
17
18
  after?: ReactNode;
19
+ /**
20
+ * Who wrote the message, when more than one person shares the transcript.
21
+ * A person's message is compared with the timeline's `viewerId`; an agent's
22
+ * author line opens its turn and is not repeated after each tool row.
23
+ */
24
+ author?: ChatAuthor;
18
25
  }
19
26
  export interface AgentTimelineToolItem {
20
27
  id: string;
@@ -72,6 +79,11 @@ export interface AgentTimelineProps {
72
79
  * except user messages) behind a "Show N more steps" toggle. Omit to always
73
80
  * show every row. */
74
81
  collapseAfter?: number;
82
+ /**
83
+ * The reader's participant id. A user message whose `author` has another
84
+ * id is someone else's: it sits at the start, on a card, under its author.
85
+ */
86
+ viewerId?: string;
75
87
  }
76
88
  /**
77
89
  * AgentTimeline — unified mixed-content transcript for agent-backed sandbox
@@ -79,5 +91,5 @@ export interface AgentTimelineProps {
79
91
  * as one plain column: prose at the reading size, tool calls as quiet
80
92
  * one-liners between the paragraphs, no connector chrome.
81
93
  */
82
- export declare function AgentTimeline({ items, isThinking, emptyState, className, renderToolActions, collapseAfter }: AgentTimelineProps): import("react").JSX.Element | null;
94
+ export declare function AgentTimeline({ items, isThinking, emptyState, className, renderToolActions, collapseAfter, viewerId }: AgentTimelineProps): import("react").JSX.Element | null;
83
95
  //#endregion
@@ -1,6 +1,7 @@
1
1
  import { focusRing } from "../lib/focus.js";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { Markdown } from "../markdown/markdown.js";
4
+ import { MessageAuthor } from "./message-author.js";
4
5
  import { UserMessage } from "./user-message.js";
5
6
  import { ThinkingIndicator } from "./thinking-indicator.js";
6
7
  import { ToolCallGroup, ToolCallStep } from "../run/tool-call-step.js";
@@ -54,8 +55,12 @@ function spacingBefore(prev, next) {
54
55
  }
55
56
  const ROW_BLEED = "-mx-3";
56
57
  const STREAMING_CARET = "[&>:last-child:not(ul,ol)]:after:ml-0.5 [&>:last-child:not(ul,ol)]:after:inline-block [&>:last-child:not(ul,ol)]:after:h-[1em] [&>:last-child:not(ul,ol)]:after:w-0.5 [&>:last-child:not(ul,ol)]:after:animate-pulse [&>:last-child:not(ul,ol)]:after:rounded-full [&>:last-child:not(ul,ol)]:after:bg-muted-foreground [&>:last-child:not(ul,ol)]:after:align-text-bottom [&>:last-child:not(ul,ol)]:after:content-[''] [&>:is(ul,ol):last-child>li:last-child]:after:ml-0.5 [&>:is(ul,ol):last-child>li:last-child]:after:inline-block [&>:is(ul,ol):last-child>li:last-child]:after:h-[1em] [&>:is(ul,ol):last-child>li:last-child]:after:w-0.5 [&>:is(ul,ol):last-child>li:last-child]:after:animate-pulse [&>:is(ul,ol):last-child>li:last-child]:after:rounded-full [&>:is(ul,ol):last-child>li:last-child]:after:bg-muted-foreground [&>:is(ul,ol):last-child>li:last-child]:after:align-text-bottom [&>:is(ul,ol):last-child>li:last-child]:after:content-['']";
57
- function AssistantMessage({ item }) {
58
+ function AssistantMessage({ item, author }) {
58
59
  return /* @__PURE__ */ jsxs("div", { children: [
60
+ author && /* @__PURE__ */ jsx(MessageAuthor, {
61
+ author,
62
+ className: "mb-1.5"
63
+ }),
59
64
  item.content && /* @__PURE__ */ jsx(Markdown, {
60
65
  className: cn("tangle-prose text-[length:var(--font-size-base)] leading-[1.5]", item.isStreaming && STREAMING_CARET),
61
66
  children: item.content
@@ -151,7 +156,7 @@ function ArtifactCard({ item }) {
151
156
  * as one plain column: prose at the reading size, tool calls as quiet
152
157
  * one-liners between the paragraphs, no connector chrome.
153
158
  */
154
- function AgentTimeline({ items, isThinking, emptyState, className, renderToolActions, collapseAfter }) {
159
+ function AgentTimeline({ items, isThinking, emptyState, className, renderToolActions, collapseAfter, viewerId }) {
155
160
  const [expanded, setExpanded] = useState(false);
156
161
  if (items.length === 0 && !isThinking) return emptyState ? /* @__PURE__ */ jsx("div", {
157
162
  className: cn("flex h-full items-center justify-center p-4", className),
@@ -180,11 +185,27 @@ function AgentTimeline({ items, isThinking, emptyState, className, renderToolAct
180
185
  renderList = visible;
181
186
  hiddenCount = stepItems.length - limit;
182
187
  }
188
+ const headedAgentMessages = /* @__PURE__ */ new Set();
189
+ let speakingAgent;
190
+ for (const item of renderList) {
191
+ if (item.kind !== "message") continue;
192
+ if (item.role === "user") {
193
+ speakingAgent = void 0;
194
+ continue;
195
+ }
196
+ if (item.author && item.author.id !== speakingAgent) headedAgentMessages.add(item.id);
197
+ speakingAgent = item.author?.id;
198
+ }
183
199
  const renderItem = (item) => {
184
200
  if (item.kind === "message") return item.role === "user" ? /* @__PURE__ */ jsx(UserMessage, {
185
201
  content: item.content,
186
- timestamp: item.timestamp
187
- }) : /* @__PURE__ */ jsx(AssistantMessage, { item });
202
+ timestamp: item.timestamp,
203
+ author: item.author,
204
+ viewerId
205
+ }) : /* @__PURE__ */ jsx(AssistantMessage, {
206
+ item,
207
+ author: headedAgentMessages.has(item.id) ? item.author : void 0
208
+ });
188
209
  if (item.kind === "tool") return /* @__PURE__ */ jsx(ToolCallStep, {
189
210
  type: item.call.type,
190
211
  label: item.call.label,
@@ -1,3 +1,4 @@
1
+ import { ChatAuthor } from "../types/message.js";
1
2
  import { ReactNode } from "react";
2
3
  //#region src/chat/chat-message.d.ts
3
4
  export type MessageRole = "user" | "assistant" | "system";
@@ -17,6 +18,17 @@ export interface ChatMessageProps {
17
18
  assistantLabel?: string;
18
19
  /** Hide the role label row entirely */
19
20
  hideRoleLabel?: boolean;
21
+ /**
22
+ * Who wrote the message, when more than one person shares the transcript.
23
+ * It replaces the role label with the author's avatar, name and role,
24
+ * except on the reader's own messages, which keep `userLabel`.
25
+ */
26
+ author?: ChatAuthor;
27
+ /**
28
+ * The reader's participant id. A user message whose `author` has another
29
+ * id is someone else's: it sits at the start, on a card, under its author.
30
+ */
31
+ viewerId?: string;
20
32
  }
21
- export declare function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel, assistantLabel, hideRoleLabel }: ChatMessageProps): import("react").JSX.Element;
33
+ export declare function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel, assistantLabel, hideRoleLabel, author, viewerId }: ChatMessageProps): import("react").JSX.Element;
22
34
  //#endregion
@@ -1,15 +1,19 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import { Markdown } from "../markdown/markdown.js";
3
+ import { MessageAuthor, isViewerMessage } from "./message-author.js";
3
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region src/chat/chat-message.tsx
5
- function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel = "You", assistantLabel = "Agent", hideRoleLabel }) {
6
+ function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel = "You", assistantLabel = "Agent", hideRoleLabel, author, viewerId }) {
6
7
  const isUser = role === "user";
7
8
  const isAssistant = role === "assistant";
9
+ const fromViewer = isUser && isViewerMessage(author, viewerId);
10
+ const fromOtherPerson = isUser && !fromViewer;
11
+ const shownAuthor = fromViewer ? void 0 : author;
8
12
  return /* @__PURE__ */ jsxs("div", {
9
- className: cn("flex flex-col gap-1", isUser ? "items-end" : "items-start", className),
13
+ className: cn("flex flex-col gap-1", fromViewer ? "items-end" : "items-start", className),
10
14
  children: [!hideRoleLabel && /* @__PURE__ */ jsxs("div", {
11
- className: cn("flex items-center gap-2 px-1", isUser && "flex-row-reverse"),
12
- children: [/* @__PURE__ */ jsx("span", {
15
+ className: cn("flex items-center gap-2 px-1", fromViewer && "flex-row-reverse"),
16
+ children: [shownAuthor ? /* @__PURE__ */ jsx(MessageAuthor, { author: shownAuthor }) : /* @__PURE__ */ jsx("span", {
13
17
  className: "font-medium text-foreground text-xs",
14
18
  children: isUser ? userLabel : assistantLabel
15
19
  }), timestamp && /* @__PURE__ */ jsx("span", {
@@ -17,7 +21,7 @@ function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, classNa
17
21
  children: formatTime(timestamp)
18
22
  })]
19
23
  }), /* @__PURE__ */ jsxs("div", {
20
- className: cn("min-w-0 max-w-[85%] space-y-1", isUser ? "rounded-[var(--radius-lg)] bg-foreground px-[var(--chat-message-px)] py-[var(--chat-message-py)] text-background" : isAssistant ? "text-foreground" : "rounded-[var(--radius-lg)] border border-border bg-card px-[var(--chat-message-px)] py-[var(--chat-message-py)]"),
24
+ className: cn("min-w-0 max-w-[85%] space-y-1", fromViewer ? "rounded-[var(--radius-lg)] bg-foreground px-[var(--chat-message-px)] py-[var(--chat-message-py)] text-background" : fromOtherPerson ? "rounded-[var(--radius-lg)] border border-border bg-card px-[var(--chat-message-px)] py-[var(--chat-message-py)] text-foreground" : isAssistant ? "text-foreground" : "rounded-[var(--radius-lg)] border border-border bg-card px-[var(--chat-message-px)] py-[var(--chat-message-py)]"),
21
25
  children: [isUser ? /* @__PURE__ */ jsx("div", {
22
26
  className: "whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[var(--line-height-base)]",
23
27
  children: content
@@ -0,0 +1,24 @@
1
+ import { ChatAuthor } from "../types/message.js";
2
+ import { HTMLAttributes } from "react";
3
+ //#region src/chat/message-author.d.ts
4
+ /**
5
+ * Whether the reader wrote a human message. A message without an author is
6
+ * the reader's, which keeps a two-party transcript as it was. A message with
7
+ * an author is the reader's only when that author is `viewerId`; with no
8
+ * `viewerId`, every authored message belongs to someone else.
9
+ */
10
+ export declare function isViewerMessage(author: ChatAuthor | undefined, viewerId: string | undefined): boolean;
11
+ export interface MessageAuthorProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
12
+ author: ChatAuthor;
13
+ }
14
+ /**
15
+ * The line above a message in a transcript with several participants: the
16
+ * author's avatar, name and, when given, their role as a tag.
17
+ *
18
+ * The avatar's tone comes from the author's id, so a participant keeps one
19
+ * colour for the whole thread and in every product. Tones can repeat between
20
+ * participants, so the name is always shown beside it. The avatar repeats the
21
+ * visible name and is hidden from assistive technology.
22
+ */
23
+ export declare function MessageAuthor({ author, className, ...props }: MessageAuthorProps): import("react").JSX.Element;
24
+ //#endregion
@@ -0,0 +1,53 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { toneFor } from "../primitives/tone.js";
3
+ import { Tag } from "../primitives/tag.js";
4
+ import { IconTile } from "../primitives/icon-tile.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/chat/message-author.tsx
7
+ /**
8
+ * Whether the reader wrote a human message. A message without an author is
9
+ * the reader's, which keeps a two-party transcript as it was. A message with
10
+ * an author is the reader's only when that author is `viewerId`; with no
11
+ * `viewerId`, every authored message belongs to someone else.
12
+ */
13
+ function isViewerMessage(author, viewerId) {
14
+ return author === void 0 || author.id === viewerId;
15
+ }
16
+ /**
17
+ * The line above a message in a transcript with several participants: the
18
+ * author's avatar, name and, when given, their role as a tag.
19
+ *
20
+ * The avatar's tone comes from the author's id, so a participant keeps one
21
+ * colour for the whole thread and in every product. Tones can repeat between
22
+ * participants, so the name is always shown beside it. The avatar repeats the
23
+ * visible name and is hidden from assistive technology.
24
+ */
25
+ function MessageAuthor({ author, className, ...props }) {
26
+ return /* @__PURE__ */ jsxs("div", {
27
+ "data-message-author": "",
28
+ className: cn("flex min-w-0 max-w-full items-center gap-2", className),
29
+ ...props,
30
+ children: [
31
+ /* @__PURE__ */ jsx(IconTile, {
32
+ size: "xs",
33
+ src: author.avatarUrl,
34
+ name: author.name,
35
+ tone: toneFor(author.id),
36
+ className: "rounded-full"
37
+ }),
38
+ /* @__PURE__ */ jsx("span", {
39
+ className: "min-w-0 truncate font-medium text-foreground text-sm",
40
+ title: author.name,
41
+ children: author.name
42
+ }),
43
+ author.role ? /* @__PURE__ */ jsx(Tag, {
44
+ size: "sm",
45
+ emphasis: "outline",
46
+ className: "max-w-[50%]",
47
+ children: author.role
48
+ }) : null
49
+ ]
50
+ });
51
+ }
52
+ //#endregion
53
+ export { MessageAuthor, isViewerMessage };
@@ -1,4 +1,4 @@
1
- import { SessionMessage } from "../types/message.js";
1
+ import { ChatAuthor, SessionMessage } from "../types/message.js";
2
2
  import { SessionPart } from "../types/parts.js";
3
3
  import { ReactNode } from "react";
4
4
  //#region src/chat/user-message.d.ts
@@ -10,10 +10,20 @@ export interface UserMessageProps {
10
10
  content?: string;
11
11
  timestamp?: Date;
12
12
  actions?: ReactNode;
13
+ /**
14
+ * Who wrote the message, when more than one person shares the transcript.
15
+ * Omit it in a two-party chat: the message renders as the reader's.
16
+ */
17
+ author?: ChatAuthor;
18
+ /**
19
+ * The reader's participant id. A message whose `author` has another id is
20
+ * someone else's: it sits at the start, on a card, under its author.
21
+ */
22
+ viewerId?: string;
13
23
  }
14
24
  /**
15
25
  * User speech retains the inverse-theme bubble and direct-content contract.
16
26
  * Application notes interrupt the bubble in source order, outside its attribution.
17
27
  */
18
- export declare const UserMessage: import("react").MemoExoticComponent<({ message: _message, parts, content, timestamp, actions }: UserMessageProps) => import("react").JSX.Element | null>;
28
+ export declare const UserMessage: import("react").MemoExoticComponent<({ message: _message, parts, content, timestamp, actions, author, viewerId }: UserMessageProps) => import("react").JSX.Element | null>;
19
29
  //#endregion
@@ -1,4 +1,5 @@
1
1
  import { SyntheticText, userTextSegments } from "./synthetic-text.js";
2
+ import { MessageAuthor, isViewerMessage } from "./message-author.js";
2
3
  import { memo } from "react";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region src/chat/user-message.tsx
@@ -8,9 +9,9 @@ function formatTime(date) {
8
9
  minute: "2-digit"
9
10
  });
10
11
  }
11
- function MessageActions({ children }) {
12
+ function MessageActions({ children, align = "end" }) {
12
13
  return /* @__PURE__ */ jsx("div", {
13
- className: "flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground",
14
+ className: `flex flex-wrap items-center ${align === "end" ? "justify-end" : "justify-start"} gap-1.5 text-xs text-muted-foreground`,
14
15
  children
15
16
  });
16
17
  }
@@ -34,28 +35,63 @@ function UserBubble({ text, timestamp, actions }) {
34
35
  });
35
36
  }
36
37
  /**
38
+ * Another person's speech: start-aligned under its author, on a card rather
39
+ * than the reader's inverse fill, so "mine" and "theirs" differ before the
40
+ * names are read. The timestamp is revealed on the bubble's far side.
41
+ */
42
+ function OtherPersonBubble({ text, timestamp, actions, author }) {
43
+ return /* @__PURE__ */ jsx("div", {
44
+ className: "flex justify-start",
45
+ children: /* @__PURE__ */ jsxs("div", {
46
+ className: "group flex min-w-0 max-w-[78%] flex-col items-start gap-1.5",
47
+ children: [
48
+ author ? /* @__PURE__ */ jsx(MessageAuthor, { author }) : null,
49
+ /* @__PURE__ */ jsxs("div", {
50
+ className: "relative min-w-0 max-w-full rounded-2xl border border-border bg-card px-4 py-3 text-foreground",
51
+ children: [timestamp ? /* @__PURE__ */ jsx("span", {
52
+ "data-user-message-time": "",
53
+ className: "pointer-events-none absolute left-full top-1/2 ml-3 -translate-y-1/2 whitespace-nowrap text-[length:var(--font-size-xs)] text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
54
+ children: formatTime(timestamp)
55
+ }) : null, /* @__PURE__ */ jsx("div", {
56
+ className: "break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]",
57
+ children: text
58
+ })]
59
+ }),
60
+ actions ? /* @__PURE__ */ jsx(MessageActions, {
61
+ align: "start",
62
+ children: actions
63
+ }) : null
64
+ ]
65
+ })
66
+ });
67
+ }
68
+ /**
37
69
  * User speech retains the inverse-theme bubble and direct-content contract.
38
70
  * Application notes interrupt the bubble in source order, outside its attribution.
39
71
  */
40
- const UserMessage = memo(({ message: _message, parts, content, timestamp, actions }) => {
41
- if (content != null) return content.trim() ? /* @__PURE__ */ jsx(UserBubble, {
42
- text: content,
72
+ const UserMessage = memo(({ message: _message, parts, content, timestamp, actions, author, viewerId }) => {
73
+ const other = isViewerMessage(author, viewerId) ? void 0 : author;
74
+ const bubble = (text, bubbleActions, headed = true, key) => other ? /* @__PURE__ */ jsx(OtherPersonBubble, {
75
+ text,
76
+ timestamp,
77
+ actions: bubbleActions,
78
+ author: headed ? other : void 0
79
+ }, key) : /* @__PURE__ */ jsx(UserBubble, {
80
+ text,
43
81
  timestamp,
44
- actions
45
- }) : null;
82
+ actions: bubbleActions
83
+ }, key);
84
+ if (content != null) return content.trim() ? bubble(content, actions) : null;
46
85
  const segments = userTextSegments(parts ?? []);
47
86
  if (segments.length === 0) return null;
48
- if (segments.length === 1 && !segments[0].synthetic) return /* @__PURE__ */ jsx(UserBubble, {
49
- text: segments[0].text,
50
- timestamp,
51
- actions
52
- });
87
+ if (segments.length === 1 && !segments[0].synthetic) return bubble(segments[0].text, actions);
88
+ const firstSpoken = segments.find((segment) => !segment.synthetic);
53
89
  return /* @__PURE__ */ jsxs("div", {
54
90
  className: "space-y-2",
55
- children: [segments.map((segment) => segment.synthetic ? /* @__PURE__ */ jsx(SyntheticText, { text: segment.text }, segment.index) : /* @__PURE__ */ jsx(UserBubble, {
56
- text: segment.text,
57
- timestamp
58
- }, segment.index)), actions ? /* @__PURE__ */ jsx(MessageActions, { children: actions }) : null]
91
+ children: [segments.map((segment) => segment.synthetic ? /* @__PURE__ */ jsx(SyntheticText, { text: segment.text }, segment.index) : bubble(segment.text, void 0, segment === firstSpoken, segment.index)), actions ? /* @__PURE__ */ jsx(MessageActions, {
92
+ align: other ? "start" : "end",
93
+ children: actions
94
+ }) : null]
59
95
  });
60
96
  });
61
97
  UserMessage.displayName = "UserMessage";
package/dist/chat.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { ChatAuthor } from "./types/message.js";
1
2
  import { ChatContainer, ChatContainerProps } from "./chat/chat-container.js";
2
3
  import { MessageList, MessageListProps } from "./chat/message-list.js";
3
4
  import { UserMessage, UserMessageProps } from "./chat/user-message.js";
4
5
  import { ChatMessage, ChatMessageProps, MessageRole } from "./chat/chat-message.js";
6
+ import { MessageAuthor, MessageAuthorProps, isViewerMessage } from "./chat/message-author.js";
5
7
  import { ThinkingIndicator, ThinkingIndicatorProps } from "./chat/thinking-indicator.js";
6
8
  import { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem } from "./chat/agent-timeline.js";
7
- export { AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, MessageList, type MessageListProps, type MessageRole, ThinkingIndicator, type ThinkingIndicatorProps, UserMessage, type UserMessageProps };
9
+ export { AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, type ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, MessageAuthor, type MessageAuthorProps, MessageList, type MessageListProps, type MessageRole, ThinkingIndicator, type ThinkingIndicatorProps, UserMessage, type UserMessageProps, isViewerMessage };
package/dist/chat.js CHANGED
@@ -1,7 +1,8 @@
1
+ import { MessageAuthor, isViewerMessage } from "./chat/message-author.js";
1
2
  import { UserMessage } from "./chat/user-message.js";
2
3
  import { MessageList } from "./chat/message-list.js";
3
4
  import { ThinkingIndicator } from "./chat/thinking-indicator.js";
4
5
  import { AgentTimeline } from "./chat/agent-timeline.js";
5
6
  import { ChatContainer } from "./chat/chat-container.js";
6
7
  import { ChatMessage } from "./chat/chat-message.js";
7
- export { AgentTimeline, ChatContainer, ChatMessage, MessageList, ThinkingIndicator, UserMessage };
8
+ export { AgentTimeline, ChatContainer, ChatMessage, MessageAuthor, MessageList, ThinkingIndicator, UserMessage, isViewerMessage };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Button, ButtonProps, buttonVariants } from "./primitives/button.js";
2
2
  import { AuthHeader, AuthHeaderProps, GitHubLoginButton, GitHubLoginButtonProps, SessionUser, UserMenu, UserMenuProps } from "./auth/auth.js";
3
3
  import { LoginLayout, LoginLayoutProps } from "./auth/login-layout.js";
4
4
  import "./auth.js";
5
- import { SessionMessage } from "./types/message.js";
5
+ import { ChatAuthor, SessionMessage } from "./types/message.js";
6
6
  import { ReasoningPart, SessionPart, TextPart, ToolPart, ToolState, ToolStatus, ToolTime } from "./types/parts.js";
7
7
  import { AgentBranding } from "./types/branding.js";
8
8
  import { CustomToolRenderer, DisplayVariant, ToolDisplayMetadata } from "./types/tool-display.js";
@@ -13,6 +13,7 @@ import { ChatContainer, ChatContainerProps } from "./chat/chat-container.js";
13
13
  import { MessageList, MessageListProps } from "./chat/message-list.js";
14
14
  import { UserMessage, UserMessageProps } from "./chat/user-message.js";
15
15
  import { ChatMessage, ChatMessageProps, MessageRole } from "./chat/chat-message.js";
16
+ import { MessageAuthor, MessageAuthorProps, isViewerMessage } from "./chat/message-author.js";
16
17
  import { ThinkingIndicator, ThinkingIndicatorProps } from "./chat/thinking-indicator.js";
17
18
  import { ToolCallStatus, ToolCallType } from "./run/tool-call-step.js";
18
19
  import { FeedSegment, ToolCallData, ToolCallFeed, ToolCallFeedProps, parseToolEvent } from "./run/tool-call-feed.js";
@@ -114,4 +115,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
114
115
  import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
115
116
  import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
116
117
  import "./redaction.js";
117
- export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, AppendUserMessageOptions, ApplySdkEventOptions, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CustomToolRenderer, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, type FileFormat, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
118
+ export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, AppendUserMessageOptions, ApplySdkEventOptions, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CustomToolRenderer, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, type FileFormat, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownProps, MessageAuthor, type MessageAuthorProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
package/dist/index.js CHANGED
@@ -71,6 +71,7 @@ import { OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES } from "./openui/schema-data
71
71
  import { findUnsupportedOpenUINode, validateOpenUIJsonArtifact, validateOpenUIJsonNode } from "./openui-schema.js";
72
72
  import { OpenUIArtifactRenderer } from "./openui/openui-artifact-renderer.js";
73
73
  import { RunGroup } from "./run/run-group.js";
74
+ import { MessageAuthor, isViewerMessage } from "./chat/message-author.js";
74
75
  import { UserMessage } from "./chat/user-message.js";
75
76
  import { MessageList } from "./chat/message-list.js";
76
77
  import { ThinkingIndicator } from "./chat/thinking-indicator.js";
@@ -98,4 +99,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
98
99
  import { copyText } from "./utils/copy-text.js";
99
100
  import { timeAgo } from "./utils/time-ago.js";
100
101
  import { RedactedDocument } from "./redaction/redacted-document.js";
101
- export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunRowShell, RunRowStatusDot, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
102
+ export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunRowShell, RunRowStatusDot, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
@@ -12,4 +12,23 @@ export interface SessionMessage {
12
12
  /** Monotonically increasing insertion index for stable ordering. */
13
13
  _insertionIndex?: number;
14
14
  }
15
+ /**
16
+ * Who wrote a chat message, for a transcript with more than one person beside
17
+ * the agent: a client, an attorney and the agent in one thread.
18
+ */
19
+ export interface ChatAuthor {
20
+ /**
21
+ * Stable participant id. A transcript's `viewerId` is compared with it to
22
+ * tell the reader's own messages apart, and it keys the avatar's tone.
23
+ */
24
+ id: string;
25
+ name: string;
26
+ /**
27
+ * The participant's part in the conversation, shown as a tag beside the
28
+ * name, such as "Attorney". Unrelated to the message's `role`.
29
+ */
30
+ role?: string;
31
+ /** Falls back to initials when absent or when the image fails to load. */
32
+ avatarUrl?: string;
33
+ }
15
34
  //#endregion
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { SessionMessage } from "./types/message.js";
1
+ import { ChatAuthor, SessionMessage } from "./types/message.js";
2
2
  import { ReasoningPart, SessionPart, TextPart, ToolPart, ToolState, ToolStatus, ToolTime } from "./types/parts.js";
3
3
  import { AgentBranding } from "./types/branding.js";
4
4
  import { CustomToolRenderer, DisplayVariant, ToolDisplayMetadata } from "./types/tool-display.js";
5
5
  import { FinalTextPart, GroupedMessage, MessageRun, MessageUser, Run, RunStats, ToolCategory } from "./types/run.js";
6
- export { AgentBranding, CustomToolRenderer, DisplayVariant, FinalTextPart, GroupedMessage, MessageRun, MessageUser, ReasoningPart, Run, RunStats, SessionMessage, SessionPart, TextPart, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime };
6
+ export { AgentBranding, ChatAuthor, CustomToolRenderer, DisplayVariant, FinalTextPart, GroupedMessage, MessageRun, MessageUser, ReasoningPart, Run, RunStats, SessionMessage, SessionPart, TextPart, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.24.0",
3
+ "version": "11.25.0",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -149,7 +149,7 @@
149
149
  "peerDependencies": {
150
150
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
151
151
  "@nanostores/react": ">=0.8.4 <3.0.0",
152
- "@tangle-network/brand": "^1.15.6",
152
+ "@tangle-network/brand": "^1.17.0",
153
153
  "@tiptap/core": ">=3.20.4 <4.0.0",
154
154
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
155
155
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -9,11 +9,13 @@ import {
9
9
  import { focusRing } from "../lib/focus";
10
10
  import { cn } from "../lib/utils";
11
11
  import { type MessageRole } from "./chat-message";
12
+ import { MessageAuthor } from "./message-author";
12
13
  import { UserMessage } from "./user-message";
13
14
  import { Markdown } from "../markdown/markdown";
14
15
  import { ThinkingIndicator } from "./thinking-indicator";
15
16
  import { type ToolCallData } from "../run/tool-call-feed";
16
17
  import { ToolCallGroup, ToolCallStep } from "../run/tool-call-step";
18
+ import type { ChatAuthor } from "../types/message";
17
19
  import type { ToolPart } from "../types/parts";
18
20
 
19
21
  export type AgentTimelineTone = "default" | "info" | "success" | "warning" | "error";
@@ -29,6 +31,12 @@ export interface AgentTimelineMessageItem {
29
31
  * timestamp of its own — the turn's user message dates the exchange. */
30
32
  timestamp?: Date;
31
33
  after?: ReactNode;
34
+ /**
35
+ * Who wrote the message, when more than one person shares the transcript.
36
+ * A person's message is compared with the timeline's `viewerId`; an agent's
37
+ * author line opens its turn and is not repeated after each tool row.
38
+ */
39
+ author?: ChatAuthor;
32
40
  }
33
41
 
34
42
  export interface AgentTimelineToolItem {
@@ -99,6 +107,11 @@ export interface AgentTimelineProps {
99
107
  * except user messages) behind a "Show N more steps" toggle. Omit to always
100
108
  * show every row. */
101
109
  collapseAfter?: number;
110
+ /**
111
+ * The reader's participant id. A user message whose `author` has another
112
+ * id is someone else's: it sits at the start, on a card, under its author.
113
+ */
114
+ viewerId?: string;
102
115
  }
103
116
 
104
117
  const TONE_STYLES: Record<AgentTimelineTone, { card: string; text: string; supportingText: string; icon: typeof Info }> = {
@@ -169,9 +182,10 @@ const STREAMING_CARET =
169
182
  "[&>:last-child:not(ul,ol)]:after:ml-0.5 [&>:last-child:not(ul,ol)]:after:inline-block [&>:last-child:not(ul,ol)]:after:h-[1em] [&>:last-child:not(ul,ol)]:after:w-0.5 [&>:last-child:not(ul,ol)]:after:animate-pulse [&>:last-child:not(ul,ol)]:after:rounded-full [&>:last-child:not(ul,ol)]:after:bg-muted-foreground [&>:last-child:not(ul,ol)]:after:align-text-bottom [&>:last-child:not(ul,ol)]:after:content-[''] " +
170
183
  "[&>:is(ul,ol):last-child>li:last-child]:after:ml-0.5 [&>:is(ul,ol):last-child>li:last-child]:after:inline-block [&>:is(ul,ol):last-child>li:last-child]:after:h-[1em] [&>:is(ul,ol):last-child>li:last-child]:after:w-0.5 [&>:is(ul,ol):last-child>li:last-child]:after:animate-pulse [&>:is(ul,ol):last-child>li:last-child]:after:rounded-full [&>:is(ul,ol):last-child>li:last-child]:after:bg-muted-foreground [&>:is(ul,ol):last-child>li:last-child]:after:align-text-bottom [&>:is(ul,ol):last-child>li:last-child]:after:content-['']";
171
184
 
172
- function AssistantMessage({ item }: { item: AgentTimelineMessageItem }) {
185
+ function AssistantMessage({ item, author }: { item: AgentTimelineMessageItem; author?: ChatAuthor }) {
173
186
  return (
174
187
  <div>
188
+ {author && <MessageAuthor author={author} className="mb-1.5" />}
175
189
  {item.content && (
176
190
  <Markdown
177
191
  // `length:` types the token: a bare `text-[var(--x)]` compiles to a
@@ -281,6 +295,7 @@ export function AgentTimeline({
281
295
  className,
282
296
  renderToolActions,
283
297
  collapseAfter,
298
+ viewerId,
284
299
  }: AgentTimelineProps) {
285
300
  const [expanded, setExpanded] = useState(false);
286
301
 
@@ -322,12 +337,27 @@ export function AgentTimeline({
322
337
  hiddenCount = stepItems.length - limit;
323
338
  }
324
339
 
340
+ // An agent's turn is often several prose items split by tool rows. Its
341
+ // author line goes on the first of them, and again only when a person
342
+ // speaks or a different agent takes over.
343
+ const headedAgentMessages = new Set<string>();
344
+ let speakingAgent: string | undefined;
345
+ for (const item of renderList) {
346
+ if (item.kind !== "message") continue;
347
+ if (item.role === "user") {
348
+ speakingAgent = undefined;
349
+ continue;
350
+ }
351
+ if (item.author && item.author.id !== speakingAgent) headedAgentMessages.add(item.id);
352
+ speakingAgent = item.author?.id;
353
+ }
354
+
325
355
  const renderItem = (item: AgentTimelineItem): ReactNode => {
326
356
  if (item.kind === "message") {
327
357
  return item.role === "user" ? (
328
- <UserMessage content={item.content} timestamp={item.timestamp} />
358
+ <UserMessage content={item.content} timestamp={item.timestamp} author={item.author} viewerId={viewerId} />
329
359
  ) : (
330
- <AssistantMessage item={item} />
360
+ <AssistantMessage item={item} author={headedAgentMessages.has(item.id) ? item.author : undefined} />
331
361
  );
332
362
  }
333
363