@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 +6 -0
- package/dist/chat/agent-timeline.d.ts +13 -1
- package/dist/chat/agent-timeline.js +25 -4
- package/dist/chat/chat-message.d.ts +13 -1
- package/dist/chat/chat-message.js +9 -5
- package/dist/chat/message-author.d.ts +24 -0
- package/dist/chat/message-author.js +53 -0
- package/dist/chat/user-message.d.ts +12 -2
- package/dist/chat/user-message.js +52 -16
- package/dist/chat.d.ts +3 -1
- package/dist/chat.js +2 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/types/message.d.ts +19 -0
- package/dist/types.d.ts +2 -2
- package/package.json +2 -2
- package/src/chat/agent-timeline.tsx +33 -3
- package/src/chat/chat-message.tsx +37 -10
- package/src/chat/index.ts +2 -0
- package/src/chat/message-author.stories.tsx +170 -0
- package/src/chat/message-author.test.tsx +189 -0
- package/src/chat/message-author.tsx +52 -0
- package/src/chat/user-message.tsx +63 -8
- package/src/markdown/code-block.test.tsx +2 -1
- package/src/types/message.ts +20 -0
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
|
-
|
|
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",
|
|
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",
|
|
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",
|
|
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:
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
})
|
|
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
|
|
49
|
-
|
|
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(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
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 };
|
package/dist/types/message.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|