@tangle-network/ui 11.31.3 → 11.32.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.32.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 02b2d30: One shell header height and one transcript type scale. Brand adds `--shell-header-height` (56px, utility `h-shell-header`) and `--shell-inset-gutter` so every shell header row shares one bottom divider, and `--transcript-font-size` / `--transcript-line-height` with the `tangle-transcript-text` rule. UI's `AgentTimeline`, `UserMessage` and `ChatMessage` set the person's and the agent's message text with the same `TRANSCRIPT_TEXT` treatment (exported), so they differ only by alignment and surface; an agent message no longer renders at a different leading from the person's. `tangle-drift` gates two new metrics: `header_height_literal` (a header row given its own height) and `transcript_type_override` (product CSS restyling transcript text).
8
+
3
9
  ## 11.31.3
4
10
 
5
11
  ### Patch Changes
@@ -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 "./transcript-text.js";
4
5
  import { MessageAuthor } from "./message-author.js";
5
6
  import { UserMessage } from "./user-message.js";
6
7
  import { ThinkingIndicator } from "./thinking-indicator.js";
@@ -62,7 +63,7 @@ function AssistantMessage({ item, author }) {
62
63
  className: "mb-1.5"
63
64
  }),
64
65
  item.content && /* @__PURE__ */ jsx(Markdown, {
65
- className: cn("tangle-prose text-[length:var(--font-size-base)] leading-[1.5]", item.isStreaming && STREAMING_CARET),
66
+ className: cn("tangle-prose", "tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]", item.isStreaming && STREAMING_CARET),
66
67
  children: item.content
67
68
  }),
68
69
  item.isStreaming && !item.content && /* @__PURE__ */ jsx("span", {
@@ -1,8 +1,18 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import { Markdown } from "../markdown/markdown.js";
3
+ import { TRANSCRIPT_TEXT } from "./transcript-text.js";
3
4
  import { MessageAuthor, isViewerMessage } from "./message-author.js";
4
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
6
  //#region src/chat/chat-message.tsx
7
+ /**
8
+ * ChatMessage — one message in the conversation.
9
+ *
10
+ * Supports user messages (plain text) and assistant messages
11
+ * (rich markdown with inline tool call activity). With `author` and
12
+ * `viewerId` it renders a transcript shared by several people: the reader's
13
+ * own messages stay at the end, everyone else's sit at the start under
14
+ * their author.
15
+ */
6
16
  function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel = "You", assistantLabel = "Agent", hideRoleLabel, author, viewerId }) {
7
17
  const isUser = role === "user";
8
18
  const isAssistant = role === "assistant";
@@ -23,10 +33,10 @@ function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, classNa
23
33
  }), /* @__PURE__ */ jsxs("div", {
24
34
  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)]"),
25
35
  children: [isUser ? /* @__PURE__ */ jsx("div", {
26
- className: "whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[var(--line-height-base)]",
36
+ className: cn("whitespace-pre-wrap", TRANSCRIPT_TEXT),
27
37
  children: content
28
38
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [content && /* @__PURE__ */ jsx(Markdown, {
29
- className: "tangle-prose text-[length:var(--font-size-base)] leading-[var(--line-height-base)]",
39
+ className: cn("tangle-prose", "tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]"),
30
40
  children: content
31
41
  }), isStreaming && /* @__PURE__ */ jsx("span", { className: "ml-0.5 inline-block h-4 w-2 animate-pulse rounded-sm bg-[var(--brand-cool)] align-text-bottom" })] }), toolCalls]
32
42
  })]
@@ -0,0 +1,11 @@
1
+ //#region src/chat/transcript-text.d.ts
2
+ /**
3
+ * The one text treatment for every chat message body, the person's and the
4
+ * agent's alike, so the two differ only by alignment and surface.
5
+ * `tangle-transcript-text` (brand globals.css) reads `--transcript-font-size`
6
+ * and `--transcript-line-height`; the utilities repeat the same tokens for a
7
+ * host that has not loaded brand's globals. `length:` types the token: a bare
8
+ * `text-[var(--x)]` compiles to a color.
9
+ */
10
+ export declare const TRANSCRIPT_TEXT = "tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]";
11
+ //#endregion
@@ -0,0 +1,12 @@
1
+ //#region src/chat/transcript-text.ts
2
+ /**
3
+ * The one text treatment for every chat message body, the person's and the
4
+ * agent's alike, so the two differ only by alignment and surface.
5
+ * `tangle-transcript-text` (brand globals.css) reads `--transcript-font-size`
6
+ * and `--transcript-line-height`; the utilities repeat the same tokens for a
7
+ * host that has not loaded brand's globals. `length:` types the token: a bare
8
+ * `text-[var(--x)]` compiles to a color.
9
+ */
10
+ const TRANSCRIPT_TEXT = "tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]";
11
+ //#endregion
12
+ export { TRANSCRIPT_TEXT };
@@ -1,4 +1,5 @@
1
1
  import { SyntheticText, userTextSegments } from "./synthetic-text.js";
2
+ import { TRANSCRIPT_TEXT } from "./transcript-text.js";
2
3
  import { MessageAuthor, isViewerMessage } from "./message-author.js";
3
4
  import { memo } from "react";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -27,7 +28,7 @@ function UserBubble({ text, timestamp, actions }) {
27
28
  className: "pointer-events-none absolute right-full top-1/2 mr-3 -translate-y-1/2 whitespace-nowrap text-[length:var(--font-size-xs)] text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
28
29
  children: formatTime(timestamp)
29
30
  }) : null, /* @__PURE__ */ jsx("div", {
30
- className: "break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]",
31
+ className: `break-words whitespace-pre-wrap ${TRANSCRIPT_TEXT}`,
31
32
  children: text
32
33
  })]
33
34
  }), actions ? /* @__PURE__ */ jsx(MessageActions, { children: actions }) : null]
@@ -53,7 +54,7 @@ function OtherPersonBubble({ text, timestamp, actions, author }) {
53
54
  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
55
  children: formatTime(timestamp)
55
56
  }) : null, /* @__PURE__ */ jsx("div", {
56
- className: "break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]",
57
+ className: `break-words whitespace-pre-wrap ${TRANSCRIPT_TEXT}`,
57
58
  children: text
58
59
  })]
59
60
  }),
package/dist/chat.d.ts CHANGED
@@ -6,4 +6,5 @@ import { ChatMessage, ChatMessageProps, MessageRole } from "./chat/chat-message.
6
6
  import { MessageAuthor, MessageAuthorProps, isViewerMessage } from "./chat/message-author.js";
7
7
  import { ThinkingIndicator, ThinkingIndicatorProps } from "./chat/thinking-indicator.js";
8
8
  import { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem } from "./chat/agent-timeline.js";
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 };
9
+ import { TRANSCRIPT_TEXT } from "./chat/transcript-text.js";
10
+ 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, TRANSCRIPT_TEXT, ThinkingIndicator, type ThinkingIndicatorProps, UserMessage, type UserMessageProps, isViewerMessage };
package/dist/chat.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { TRANSCRIPT_TEXT } from "./chat/transcript-text.js";
1
2
  import { MessageAuthor, isViewerMessage } from "./chat/message-author.js";
2
3
  import { UserMessage } from "./chat/user-message.js";
3
4
  import { MessageList } from "./chat/message-list.js";
@@ -5,4 +6,4 @@ import { ThinkingIndicator } from "./chat/thinking-indicator.js";
5
6
  import { AgentTimeline } from "./chat/agent-timeline.js";
6
7
  import { ChatContainer } from "./chat/chat-container.js";
7
8
  import { ChatMessage } from "./chat/chat-message.js";
8
- export { AgentTimeline, ChatContainer, ChatMessage, MessageAuthor, MessageList, ThinkingIndicator, UserMessage, isViewerMessage };
9
+ export { AgentTimeline, ChatContainer, ChatMessage, MessageAuthor, MessageList, TRANSCRIPT_TEXT, ThinkingIndicator, UserMessage, isViewerMessage };
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ import { ThinkingIndicator, ThinkingIndicatorProps } from "./chat/thinking-indic
18
18
  import { ToolCallStatus, ToolCallType } from "./run/tool-call-step.js";
19
19
  import { FeedSegment, ToolCallData, ToolCallFeed, ToolCallFeedProps, parseToolEvent } from "./run/tool-call-feed.js";
20
20
  import { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem } from "./chat/agent-timeline.js";
21
+ import { TRANSCRIPT_TEXT } from "./chat/transcript-text.js";
21
22
  import "./chat.js";
22
23
  import { ArtifactPane, ArtifactPaneProps } from "./primitives/artifact-pane.js";
23
24
  import { FileNode, FileTree, FileTreeProps, FileTreeVisibilityOptions, filterFileTree } from "./files/file-tree.js";
@@ -129,4 +130,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
129
130
  import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
130
131
  import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
131
132
  import "./redaction.js";
132
- 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, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, 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, Calendar, type CalendarDate, type CalendarProps, 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, DatePicker, type DatePickerProps, 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, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, 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 MarkdownLinkChip, type MarkdownLinkChipResolver, 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, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, 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, type RunPhase, RunPhaseList, 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, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, 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, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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 };
133
+ 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, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, 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, Calendar, type CalendarDate, type CalendarProps, 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, DatePicker, type DatePickerProps, 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, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, 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 MarkdownLinkChip, type MarkdownLinkChipResolver, 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, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, 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, type RunPhase, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, 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, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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
@@ -78,6 +78,7 @@ import { OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES } from "./openui/schema-data
78
78
  import { findUnsupportedOpenUINode, validateOpenUIJsonArtifact, validateOpenUIJsonNode } from "./openui-schema.js";
79
79
  import { OpenUIArtifactRenderer } from "./openui/openui-artifact-renderer.js";
80
80
  import { RunGroup } from "./run/run-group.js";
81
+ import { TRANSCRIPT_TEXT } from "./chat/transcript-text.js";
81
82
  import { MessageAuthor, isViewerMessage } from "./chat/message-author.js";
82
83
  import { UserMessage } from "./chat/user-message.js";
83
84
  import { MessageList } from "./chat/message-list.js";
@@ -113,4 +114,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
113
114
  import { copyText } from "./utils/copy-text.js";
114
115
  import { timeAgo } from "./utils/time-ago.js";
115
116
  import { RedactedDocument } from "./redaction/redacted-document.js";
116
- export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, 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, DatePicker, 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, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, 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, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, 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, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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 };
117
+ export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, 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, DatePicker, 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, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, 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, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.31.3",
3
+ "version": "11.32.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",
@@ -150,7 +150,7 @@
150
150
  "peerDependencies": {
151
151
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
152
152
  "@nanostores/react": ">=0.8.4 <3.0.0",
153
- "@tangle-network/brand": "^1.20.0",
153
+ "@tangle-network/brand": "^1.21.0",
154
154
  "@tiptap/core": ">=3.20.4 <4.0.0",
155
155
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
156
156
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event"
3
3
  import { describe, expect, it, vi } from "vitest"
4
4
  import type { ToolPart } from "../types/parts"
5
5
  import { AgentTimeline, type AgentTimelineItem } from "./agent-timeline"
6
+ import { TRANSCRIPT_TEXT } from "./transcript-text"
6
7
 
7
8
  const editPart: ToolPart = {
8
9
  type: "tool",
@@ -287,3 +288,22 @@ describe("AgentTimeline tool actions", () => {
287
288
  expect(screen.getByText("Output")).toBeInTheDocument()
288
289
  })
289
290
  })
291
+
292
+ describe("transcript type scale", () => {
293
+ it("sets the person's and the agent's message text in one family, size and leading", () => {
294
+ render(
295
+ <AgentTimeline
296
+ items={[
297
+ { id: "u", kind: "message", role: "user", content: "Can Luz take the 3 pm massage?" },
298
+ { id: "a", kind: "message", role: "assistant", content: "Yes, Luz is free at 3 pm." },
299
+ ]}
300
+ />,
301
+ )
302
+ const user = screen.getByText("Can Luz take the 3 pm massage?")
303
+ const agent = screen.getByText("Yes, Luz is free at 3 pm.").closest(".tangle-prose")
304
+ const textClasses = (element: Element | null) =>
305
+ [...(element?.classList ?? [])].filter((name) => /^(tangle-transcript-text|text-|leading-|font-)/.test(name)).sort()
306
+ expect(textClasses(user)).toEqual(TRANSCRIPT_TEXT.split(" ").sort())
307
+ expect(textClasses(agent)).toEqual(textClasses(user))
308
+ })
309
+ })
@@ -1,3 +1,4 @@
1
+ import { TRANSCRIPT_TEXT } from "./transcript-text";
1
2
  import { useState, type KeyboardEvent, type ReactNode } from "react";
2
3
  import {
3
4
  AlertTriangle,
@@ -188,10 +189,9 @@ function AssistantMessage({ item, author }: { item: AgentTimelineMessageItem; au
188
189
  {author && <MessageAuthor author={author} className="mb-1.5" />}
189
190
  {item.content && (
190
191
  <Markdown
191
- // `length:` types the token: a bare `text-[var(--x)]` compiles to a
192
- // color, which left the reading size at the Markdown default.
193
192
  className={cn(
194
- "tangle-prose text-[length:var(--font-size-base)] leading-[1.5]",
193
+ "tangle-prose",
194
+ TRANSCRIPT_TEXT,
195
195
  item.isStreaming && STREAMING_CARET,
196
196
  )}
197
197
  >
@@ -8,6 +8,7 @@
8
8
  * their author.
9
9
  */
10
10
 
11
+ import { TRANSCRIPT_TEXT } from "./transcript-text";
11
12
  import { type ReactNode } from "react";
12
13
  import { cn } from "../lib/utils";
13
14
  import { Markdown } from "../markdown/markdown";
@@ -102,12 +103,12 @@ export function ChatMessage({
102
103
  )}
103
104
  >
104
105
  {isUser ? (
105
- <div className="whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[var(--line-height-base)]">
106
+ <div className={cn("whitespace-pre-wrap", TRANSCRIPT_TEXT)}>
106
107
  {content}
107
108
  </div>
108
109
  ) : (
109
110
  <>
110
- {content && <Markdown className="tangle-prose text-[length:var(--font-size-base)] leading-[var(--line-height-base)]">{content}</Markdown>}
111
+ {content && <Markdown className={cn("tangle-prose", TRANSCRIPT_TEXT)}>{content}</Markdown>}
111
112
  {isStreaming && (
112
113
  <span className="ml-0.5 inline-block h-4 w-2 animate-pulse rounded-sm bg-[var(--brand-cool)] align-text-bottom" />
113
114
  )}
package/src/chat/index.ts CHANGED
@@ -17,3 +17,4 @@ export {
17
17
  type AgentTimelineCustomItem,
18
18
  type AgentTimelineTone,
19
19
  } from "./agent-timeline";
20
+ export { TRANSCRIPT_TEXT } from "./transcript-text";
@@ -22,18 +22,18 @@ const segmentedParts: SessionPart[] = [
22
22
  // existed. A transcript that names no author must keep that exact markup.
23
23
  describe("a transcript without authors", () => {
24
24
  it("renders ChatMessage as before", () => {
25
- expect(html(<ChatMessage role="user" content="Can I break my lease early?" />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-end"><div class="flex items-center gap-2 px-1 flex-row-reverse"><span class="font-medium text-foreground text-xs">You</span></div><div class="min-w-0 max-w-[85%] space-y-1 rounded-[var(--radius-lg)] bg-foreground px-[var(--chat-message-px)] py-[var(--chat-message-py)] text-background"><div class="whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[var(--line-height-base)]">Can I break my lease early?</div></div></div>"`)
26
- expect(html(<ChatMessage role="assistant" content="It depends on the lease terms." />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-start"><div class="flex items-center gap-2 px-1"><span class="font-medium text-foreground text-xs">Agent</span></div><div class="min-w-0 max-w-[85%] space-y-1 text-foreground"><div class="tangle-prose max-w-none tangle-prose text-[length:var(--font-size-base)] leading-[var(--line-height-base)]"><p>It depends on the lease terms.</p></div></div></div>"`)
27
- expect(html(<ChatMessage role="system" content="Session started." />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-start"><div class="flex items-center gap-2 px-1"><span class="font-medium text-foreground text-xs">Agent</span></div><div class="min-w-0 max-w-[85%] space-y-1 rounded-[var(--radius-lg)] border border-border bg-card px-[var(--chat-message-px)] py-[var(--chat-message-py)]"><div class="tangle-prose max-w-none tangle-prose text-[length:var(--font-size-base)] leading-[var(--line-height-base)]"><p>Session started.</p></div></div></div>"`)
25
+ expect(html(<ChatMessage role="user" content="Can I break my lease early?" />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-end"><div class="flex items-center gap-2 px-1 flex-row-reverse"><span class="font-medium text-foreground text-xs">You</span></div><div class="min-w-0 max-w-[85%] space-y-1 rounded-[var(--radius-lg)] bg-foreground px-[var(--chat-message-px)] py-[var(--chat-message-py)] text-background"><div class="whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">Can I break my lease early?</div></div></div>"`)
26
+ expect(html(<ChatMessage role="assistant" content="It depends on the lease terms." />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-start"><div class="flex items-center gap-2 px-1"><span class="font-medium text-foreground text-xs">Agent</span></div><div class="min-w-0 max-w-[85%] space-y-1 text-foreground"><div class="tangle-prose max-w-none tangle-prose tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]"><p>It depends on the lease terms.</p></div></div></div>"`)
27
+ expect(html(<ChatMessage role="system" content="Session started." />)).toMatchInlineSnapshot(`"<div class="flex flex-col gap-1 items-start"><div class="flex items-center gap-2 px-1"><span class="font-medium text-foreground text-xs">Agent</span></div><div class="min-w-0 max-w-[85%] space-y-1 rounded-[var(--radius-lg)] border border-border bg-card px-[var(--chat-message-px)] py-[var(--chat-message-py)]"><div class="tangle-prose max-w-none tangle-prose tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]"><p>Session started.</p></div></div></div>"`)
28
28
  })
29
29
 
30
30
  it("renders UserMessage actions and application notes as before", () => {
31
- expect(html(<UserMessage content="Can I break my lease early?" actions={<button type="button">Copy</button>} />)).toMatchInlineSnapshot(`"<div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">Can I break my lease early?</div></div><div class="flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground"><button type="button">Copy</button></div></div></div>"`)
32
- expect(html(<UserMessage parts={segmentedParts} actions={<button type="button">Copy</button>} />)).toMatchInlineSnapshot(`"<div class="space-y-2"><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">I signed the lease in March.</div></div></div></div><div role="note" aria-label="Application note" data-synthetic-text="" class="min-w-0 rounded-md border border-dashed border-border bg-muted/30 px-3 py-2 text-sm text-muted-foreground"><div class="mb-1 font-medium">Application note</div><div class="break-words whitespace-pre-wrap">Lease uploaded: lease.pdf</div></div><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">Here it is.</div></div></div></div><div class="flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground"><button type="button">Copy</button></div></div>"`)
31
+ expect(html(<UserMessage content="Can I break my lease early?" actions={<button type="button">Copy</button>} />)).toMatchInlineSnapshot(`"<div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">Can I break my lease early?</div></div><div class="flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground"><button type="button">Copy</button></div></div></div>"`)
32
+ expect(html(<UserMessage parts={segmentedParts} actions={<button type="button">Copy</button>} />)).toMatchInlineSnapshot(`"<div class="space-y-2"><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">I signed the lease in March.</div></div></div></div><div role="note" aria-label="Application note" data-synthetic-text="" class="min-w-0 rounded-md border border-dashed border-border bg-muted/30 px-3 py-2 text-sm text-muted-foreground"><div class="mb-1 font-medium">Application note</div><div class="break-words whitespace-pre-wrap">Lease uploaded: lease.pdf</div></div><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">Here it is.</div></div></div></div><div class="flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground"><button type="button">Copy</button></div></div>"`)
33
33
  })
34
34
 
35
35
  it("renders UserMessage as before", () => {
36
- expect(html(<UserMessage content="Can I break my lease early?" />)).toMatchInlineSnapshot(`"<div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">Can I break my lease early?</div></div></div></div>"`)
36
+ expect(html(<UserMessage content="Can I break my lease early?" />)).toMatchInlineSnapshot(`"<div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">Can I break my lease early?</div></div></div></div>"`)
37
37
  })
38
38
 
39
39
  it("renders AgentTimeline messages as before", () => {
@@ -46,7 +46,7 @@ describe("a transcript without authors", () => {
46
46
  ]}
47
47
  />,
48
48
  ),
49
- ).toMatchInlineSnapshot(`"<div class="mx-auto flex w-full max-w-5xl flex-col px-4 py-4"><div data-timeline-step="user" class=""><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">Can I break my lease early?</div></div></div></div></div><div data-timeline-step="prose" class="mt-4"><div><div class="tangle-prose max-w-none tangle-prose text-[length:var(--font-size-base)] leading-[1.5]"><p>It depends on the lease terms.</p></div></div></div></div>"`)
49
+ ).toMatchInlineSnapshot(`"<div class="mx-auto flex w-full max-w-5xl flex-col px-4 py-4"><div data-timeline-step="user" class=""><div class="flex justify-end"><div class="group flex min-w-0 max-w-[78%] flex-col items-end gap-2"><div class="relative w-full min-w-0 rounded-2xl bg-foreground px-4 py-3 text-background"><div class="break-words whitespace-pre-wrap tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]">Can I break my lease early?</div></div></div></div></div><div data-timeline-step="prose" class="mt-4"><div><div class="tangle-prose max-w-none tangle-prose tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]"><p>It depends on the lease terms.</p></div></div></div></div>"`)
50
50
  })
51
51
  })
52
52
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The one text treatment for every chat message body, the person's and the
3
+ * agent's alike, so the two differ only by alignment and surface.
4
+ * `tangle-transcript-text` (brand globals.css) reads `--transcript-font-size`
5
+ * and `--transcript-line-height`; the utilities repeat the same tokens for a
6
+ * host that has not loaded brand's globals. `length:` types the token: a bare
7
+ * `text-[var(--x)]` compiles to a color.
8
+ */
9
+ export const TRANSCRIPT_TEXT =
10
+ "tangle-transcript-text text-[length:var(--transcript-font-size)] leading-[var(--transcript-line-height)]";
@@ -1,3 +1,4 @@
1
+ import { TRANSCRIPT_TEXT } from "./transcript-text";
1
2
  import { memo, type ReactNode } from "react";
2
3
  import type { ChatAuthor, SessionMessage } from "../types/message";
3
4
  import type { SessionPart } from "../types/parts";
@@ -53,7 +54,7 @@ function UserBubble({ text, timestamp, actions }: {
53
54
  {formatTime(timestamp)}
54
55
  </span>
55
56
  ) : null}
56
- <div className="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">
57
+ <div className={`break-words whitespace-pre-wrap ${TRANSCRIPT_TEXT}`}>
57
58
  {text}
58
59
  </div>
59
60
  </div>
@@ -88,7 +89,7 @@ function OtherPersonBubble({ text, timestamp, actions, author }: {
88
89
  {formatTime(timestamp)}
89
90
  </span>
90
91
  ) : null}
91
- <div className="break-words whitespace-pre-wrap text-[length:var(--font-size-base)] leading-[1.5]">
92
+ <div className={`break-words whitespace-pre-wrap ${TRANSCRIPT_TEXT}`}>
92
93
  {text}
93
94
  </div>
94
95
  </div>