@mastra/playground-ui 48.0.0-alpha.16 → 48.0.0-alpha.18

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,38 @@
1
1
  # @mastra/playground-ui
2
2
 
3
+ ## 48.0.0-alpha.18
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed saved chat transcripts to open at the latest turn after asynchronously loaded history finishes layout. ([#21301](https://github.com/mastra-ai/mastra/pull/21301))
8
+
9
+ ## 48.0.0-alpha.17
10
+
11
+ ### Minor Changes
12
+
13
+ - Added anchored turn positioning to `MessageScroller`. Newly added rows marked with `scrollAnchor` move toward the top of the viewport, while `defaultScrollPosition="last-anchor"` opens saved transcripts at their latest turn. ([#21283](https://github.com/mastra-ai/mastra/pull/21283))
14
+
15
+ Streaming replies can now grow beneath the current prompt without shifting the reading position. Completed replies retain that space until the next anchored turn arrives.
16
+
17
+ ```tsx
18
+ <MessageScrollerProvider defaultScrollPosition="last-anchor">
19
+ <MessageScrollerViewport>
20
+ <MessageScrollerContent>
21
+ <MessageScrollerItem messageId="turn-1" scrollAnchor>
22
+ <p>How do I deploy this workflow?</p>
23
+ </MessageScrollerItem>
24
+ </MessageScrollerContent>
25
+ </MessageScrollerViewport>
26
+ </MessageScrollerProvider>
27
+ ```
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [[`296dc9a`](https://github.com/mastra-ai/mastra/commit/296dc9af29f3616e786c7825ec32e0df92d754c5), [`4a09a9c`](https://github.com/mastra-ai/mastra/commit/4a09a9c0474ef643558fcb5f0edc542b82f1cab0), [`1e83a47`](https://github.com/mastra-ai/mastra/commit/1e83a4734ab61ba5926af6793e3569a78b72ed37), [`ff28284`](https://github.com/mastra-ai/mastra/commit/ff2828416f14daff9d956e6a352fdaa23c950979), [`1670533`](https://github.com/mastra-ai/mastra/commit/1670533986f6bacf567746245348125e3a106448)]:
32
+ - @mastra/core@1.58.0-alpha.16
33
+ - @mastra/client-js@1.39.0-alpha.16
34
+ - @mastra/react@1.4.2-alpha.16
35
+
3
36
  ## 48.0.0-alpha.16
4
37
 
5
38
  ### Minor Changes
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const jsxRuntime = require('react/jsx-runtime');
6
- const messageScroller = require('../message-scroller-BmzjZZNs.cjs');
6
+ const messageScroller = require('../message-scroller-BssU0EKT.cjs');
7
7
  const utils_cn = require('../cn-vA5sWo9W.cjs');
8
8
 
9
9
  function ChatShellRoot({ className, scroller, ...props }) {
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { d as MessageScrollerProvider, e as MessageScrollerViewport, M as MessageScroller, a as MessageScrollerButton, b as MessageScrollerContent } from '../message-scroller-D5O7sUS5.js';
2
+ import { d as MessageScrollerProvider, e as MessageScrollerViewport, M as MessageScroller, a as MessageScrollerButton, b as MessageScrollerContent } from '../message-scroller-DlVS0FW7.js';
3
3
  import { c as cn } from '../cn-BakJzhuv.js';
4
4
 
5
5
  function ChatShellRoot({ className, scroller, ...props }) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
- const messageScroller = require('../message-scroller-BmzjZZNs.cjs');
5
+ const messageScroller = require('../message-scroller-BssU0EKT.cjs');
6
6
  const messageScrollerHooks = require('../message-scroller-hooks-BxAf8k0Y.cjs');
7
7
 
8
8
 
@@ -1,3 +1,3 @@
1
- export { M as MessageScroller, a as MessageScrollerButton, b as MessageScrollerContent, c as MessageScrollerItem, d as MessageScrollerProvider, e as MessageScrollerViewport } from '../message-scroller-D5O7sUS5.js';
1
+ export { M as MessageScroller, a as MessageScrollerButton, b as MessageScrollerContent, c as MessageScrollerItem, d as MessageScrollerProvider, e as MessageScrollerViewport } from '../message-scroller-DlVS0FW7.js';
2
2
  export { u as useMessageScroller, a as useMessageScrollerScrollable, b as useMessageScrollerVisibility, c as useOptionalMessageScroller, d as useOptionalMessageScrollerScrollable, e as useOptionalMessageScrollerVisibility } from '../message-scroller-hooks-D5yxCon2.js';
3
3
  //# sourceMappingURL=MessageScroller.es.js.map
@@ -393,7 +393,7 @@ const getSignalType = (message) => {
393
393
  const metadataType = isRecord(signal) ? signal.type : void 0;
394
394
  return typeof metadataType === "string" ? metadataType : message.type;
395
395
  };
396
- const isDisplayableUserMessage = (message) => {
396
+ const startsUserTurn = (message) => {
397
397
  if (message.role === "user") return true;
398
398
  if (message.role !== "signal") return false;
399
399
  const signalType = getSignalType(message);
@@ -403,7 +403,7 @@ const getNextAssistantReply = (messages, startIndex) => {
403
403
  for (let index = startIndex + 1; index < messages.length; index += 1) {
404
404
  const message = messages[index];
405
405
  if (!message) continue;
406
- if (isDisplayableUserMessage(message)) return void 0;
406
+ if (startsUserTurn(message)) return void 0;
407
407
  if (message.role !== "assistant") continue;
408
408
  const reply = normalizeSummary(getTextFromParts(message), "");
409
409
  if (reply) return reply;
@@ -411,7 +411,7 @@ const getNextAssistantReply = (messages, startIndex) => {
411
411
  return void 0;
412
412
  };
413
413
  const buildThreadRailTurns = (messages) => messages.flatMap((message, index) => {
414
- if (!isDisplayableUserMessage(message)) return [];
414
+ if (!startsUserTurn(message)) return [];
415
415
  const files = getFileLabels(message);
416
416
  const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);
417
417
  return [
@@ -429,4 +429,5 @@ const buildThreadRailTurns = (messages) => messages.flatMap((message, index) =>
429
429
  exports.ThreadRail = ThreadRail;
430
430
  exports.buildThreadRailTurns = buildThreadRailTurns;
431
431
  exports.getClientMessageKey = getClientMessageKey;
432
+ exports.startsUserTurn = startsUserTurn;
432
433
  //# sourceMappingURL=ThreadRail.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadRail.cjs.js","sources":["../../src/ds/components/ThreadRail/thread-rail.tsx","../../src/ds/components/ThreadRail/thread-rail-turns.ts"],"sourcesContent":["import { FileText } from 'lucide-react';\nimport * as React from 'react';\n\nimport type { ThreadRailTurn } from './thread-rail-turns';\n\nimport { useOptionalMessageScroller, useOptionalMessageScrollerVisibility } from '@/ds/components/MessageScroller';\nimport { ScrollArea } from '@/ds/components/ScrollArea';\nimport { useMeasuredAutoHeight } from '@/hooks/use-measured-auto-height';\nimport { cn } from '@/lib/utils';\n\ntype ThreadRailPreviewPhase = 'hidden' | 'entering' | 'visible' | 'exiting';\n\ntype ThreadRailPreviewState = {\n currentTurn: ThreadRailTurn | undefined;\n previousTurn: ThreadRailTurn | undefined;\n index: number | null;\n phase: ThreadRailPreviewPhase;\n top: number;\n transitionKey: number;\n};\n\nconst PREVIEW_TRANSITION_DURATION_MS = 360;\nconst PREVIEW_CLEANUP_DELAY_MS = PREVIEW_TRANSITION_DURATION_MS + 80;\n\nconst DEFAULT_PREVIEW_STATE: ThreadRailPreviewState = {\n currentTurn: undefined,\n previousTurn: undefined,\n index: null,\n phase: 'hidden',\n top: 0,\n transitionKey: 0,\n};\n\nexport interface ThreadRailProps {\n turns: ThreadRailTurn[];\n currentAnchorId?: string;\n visibleMessageIds?: string[];\n label?: string;\n className?: string;\n maxHeight?: string;\n scrollAreaClassName?: string;\n onSelect?: (turn: ThreadRailTurn) => void;\n}\n\nexport function ThreadRail({\n turns,\n currentAnchorId,\n visibleMessageIds,\n label = 'Conversation timeline',\n className,\n maxHeight = 'calc(100dvh - 12rem)',\n scrollAreaClassName,\n onSelect,\n}: ThreadRailProps) {\n const railRef = React.useRef<HTMLElement>(null);\n const previewId = React.useId();\n const [previewState, setPreviewState] = React.useState<ThreadRailPreviewState>(DEFAULT_PREVIEW_STATE);\n const messageScroller = useOptionalMessageScroller();\n const scrollerVisibility = useOptionalMessageScrollerVisibility();\n\n const fallbackMessageId = turns.at(-1)?.messageId;\n const activeMessageId = currentAnchorId ?? scrollerVisibility?.currentAnchorId ?? fallbackMessageId;\n const resolvedVisibleMessageIds =\n visibleMessageIds ?? scrollerVisibility?.visibleMessageIds ?? (activeMessageId ? [activeMessageId] : []);\n const visibleMessageIdSet = new Set(resolvedVisibleMessageIds);\n const previewHoverActive = previewState.phase === 'entering' || previewState.phase === 'visible';\n const hoveredIndex = previewHoverActive ? previewState.index : null;\n const selectTurn = React.useCallback(\n (turn: ThreadRailTurn) => {\n if (onSelect) {\n onSelect(turn);\n return;\n }\n\n messageScroller?.scrollToMessage(turn.messageId, { behavior: 'smooth', align: 'start' });\n },\n [messageScroller, onSelect],\n );\n\n const showPreview = (index: number, element: HTMLElement) => {\n const railTop = railRef.current?.getBoundingClientRect().top ?? 0;\n const itemRect = element.getBoundingClientRect();\n const nextTurn = turns[index];\n if (!nextTurn) return;\n\n setPreviewState(current => {\n const nextTop = itemRect.top + itemRect.height / 2 - railTop;\n if (current.index === index && current.phase !== 'hidden' && current.phase !== 'exiting') {\n return { ...current, top: nextTop };\n }\n\n const previousTurn =\n current.currentTurn && current.currentTurn.key !== nextTurn.key && current.phase !== 'hidden'\n ? current.currentTurn\n : undefined;\n\n return {\n currentTurn: nextTurn,\n previousTurn,\n index,\n phase: 'entering',\n top: nextTop,\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n const hidePreview = () => {\n setPreviewState(current => {\n if (!current.currentTurn || current.phase === 'hidden' || current.phase === 'exiting') {\n return { ...current, index: null };\n }\n\n return {\n ...current,\n index: null,\n previousTurn: undefined,\n phase: 'exiting',\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n React.useEffect(() => {\n if (previewState.phase !== 'entering') return;\n\n const requestFrame = window.requestAnimationFrame ?? window.setTimeout;\n const cancelFrame = window.cancelAnimationFrame ?? window.clearTimeout;\n let settleFrame: number | undefined;\n const startFrame = requestFrame(() => {\n settleFrame = requestFrame(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey && current.phase === 'entering'\n ? { ...current, phase: 'visible' }\n : current,\n );\n });\n });\n\n return () => {\n cancelFrame(startFrame);\n if (settleFrame !== undefined) cancelFrame(settleFrame);\n };\n }, [previewState.phase, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'visible' || !previewState.previousTurn) return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? { ...current, previousTurn: undefined } : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.previousTurn, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'exiting') return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? DEFAULT_PREVIEW_STATE : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.transitionKey]);\n\n if (turns.length === 0) return null;\n\n return (\n <nav\n ref={railRef}\n aria-label={label}\n data-testid=\"thread-rail\"\n className={cn('relative w-8 py-2', className)}\n onMouseLeave={hidePreview}\n onBlur={event => {\n const nextFocus = event.relatedTarget;\n if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) hidePreview();\n }}\n >\n <ScrollArea\n data-testid=\"thread-rail-scroll-area\"\n maxHeight={maxHeight}\n className={cn('w-8', scrollAreaClassName)}\n viewPortClassName=\"pr-3\"\n >\n <div className=\"flex w-4 flex-col items-start gap-2 py-2\">\n {turns.map((turn, index) => {\n const distance = hoveredIndex === null ? null : Math.abs(index - hoveredIndex);\n const active = turn.messageId === activeMessageId;\n const inView = visibleMessageIdSet.has(turn.messageId);\n const previewActive = hoveredIndex === index;\n\n return (\n <ThreadRailItem\n key={turn.key}\n turn={turn}\n index={index}\n distance={distance}\n active={active}\n inView={inView}\n previewId={previewActive ? previewId : undefined}\n onHoverChange={showPreview}\n onSelect={selectTurn}\n />\n );\n })}\n </div>\n </ScrollArea>\n\n {previewState.currentTurn && (\n <ThreadRailPreview\n id={previewId}\n currentTurn={previewState.currentTurn}\n previousTurn={previewState.previousTurn}\n phase={previewState.phase}\n top={previewState.top}\n />\n )}\n </nav>\n );\n}\n\ninterface ThreadRailItemProps {\n turn: ThreadRailTurn;\n index: number;\n distance: number | null;\n active: boolean;\n inView: boolean;\n previewId?: string;\n onHoverChange: (index: number, element: HTMLElement) => void;\n onSelect: (turn: ThreadRailTurn) => void;\n}\n\n/** Width of a rail tick: widest at the hovered item, tapering to neighbours. */\nconst getRailItemWidth = (distance: number | null): string => {\n if (distance === 0) return 'w-4';\n if (distance === 1) return 'w-3';\n return 'w-2';\n};\n\n/** Background tone of a rail tick, prioritising hovered/active, then in-view, then proximity. */\nconst getRailItemTone = ({\n distance,\n active,\n inView,\n}: Pick<ThreadRailItemProps, 'distance' | 'active' | 'inView'>): string => {\n if (distance === 0 || active) return 'bg-neutral6';\n if (inView) return 'bg-neutral5';\n if (distance === 1) return 'bg-neutral4';\n return 'bg-neutral3/60';\n};\n\nfunction ThreadRailItem({\n turn,\n index,\n distance,\n active,\n inView,\n previewId,\n onHoverChange,\n onSelect,\n}: ThreadRailItemProps) {\n const size = getRailItemWidth(distance);\n const tone = getRailItemTone({ distance, active, inView });\n\n return (\n <div\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n className=\"group/thread-rail-item relative flex items-center\"\n >\n <button\n type=\"button\"\n aria-label={`Jump to ${turn.prompt}`}\n aria-current={active ? 'location' : undefined}\n aria-describedby={previewId}\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n onClick={() => onSelect(turn)}\n onMouseEnter={event => onHoverChange(index, event.currentTarget)}\n onFocus={event => onHoverChange(index, event.currentTarget)}\n className={cn(\n 'relative block h-px cursor-pointer rounded-full transition-[width,background-color] duration-normal ease-out',\n \"before:absolute before:inset-x-0 before:-inset-y-1 before:content-['']\",\n 'focus-visible:ring-2 focus-visible:ring-accent1/40 focus-visible:outline-hidden',\n size,\n tone,\n )}\n />\n </div>\n );\n}\n\nfunction ThreadRailPreview({\n id,\n currentTurn,\n previousTurn,\n phase,\n top,\n}: {\n id: string;\n currentTurn: ThreadRailTurn;\n previousTurn: ThreadRailTurn | undefined;\n phase: ThreadRailPreviewPhase;\n top: number;\n}) {\n const {\n ref: previewSizerRef,\n heightStyle: previewHeightStyle,\n measure: measurePreviewHeight,\n } = useMeasuredAutoHeight<HTMLDivElement>();\n const containerVisible = phase === 'visible' || (phase === 'entering' && Boolean(previousTurn));\n // Entering and exiting share the same off-screen resting style.\n const hiddenLayerClassName = 'scale-95 opacity-0 blur-xs';\n const visibleLayerClassName = 'scale-100 opacity-100 blur-none';\n const layerClassName =\n 'absolute inset-x-0 top-0 h-full origin-left transition-[opacity,filter,scale] duration-150 ease-in-out will-change-[opacity,filter,scale] motion-reduce:scale-100 motion-reduce:blur-none motion-reduce:transition-none';\n\n React.useLayoutEffect(() => {\n measurePreviewHeight();\n }, [currentTurn.key, phase, measurePreviewHeight]);\n\n return (\n <div\n id={id}\n data-testid=\"thread-rail-preview\"\n data-visible={containerVisible ? 'true' : undefined}\n className={cn(\n 'pointer-events-none absolute top-0 left-full z-30 ml-3 w-72 overflow-hidden rounded-xl border border-border1 bg-surface3 text-left shadow-dialog transition-[height,translate,opacity] duration-360 ease-out-custom will-change-[height,translate,opacity] motion-reduce:transition-none',\n containerVisible ? 'opacity-100' : 'opacity-0',\n )}\n style={{ ...previewHeightStyle, translate: `0 calc(${top}px - 50%)` }}\n >\n <div ref={previewSizerRef} data-testid=\"thread-rail-preview-sizer\" aria-hidden className=\"invisible\">\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n <div data-testid=\"thread-rail-preview-viewport\" className=\"absolute inset-0\">\n <div className=\"relative h-full\">\n {previousTurn && (\n <div\n key={previousTurn.key}\n data-testid=\"thread-rail-preview-previous\"\n aria-hidden\n className={cn(layerClassName, phase === 'entering' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={previousTurn} className=\"p-3.5\" />\n </div>\n )}\n <div\n key={currentTurn.key}\n data-testid=\"thread-rail-preview-current\"\n className={cn(layerClassName, phase === 'visible' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ThreadRailPreviewContent({\n turn,\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement> & { turn: ThreadRailTurn }) {\n return (\n <div className={className} {...props}>\n <div className=\"text-ui-md leading-ui-md text-neutral6 truncate font-medium\">{turn.prompt}</div>\n {turn.reply && <p className=\"text-ui-sm leading-ui-sm text-neutral4 mt-1.5 line-clamp-3\">{turn.reply}</p>}\n {(turn.files.length > 0 || turn.hiddenFileCount > 0) && (\n <div className=\"border-border1/60 mt-3 flex flex-wrap items-center gap-2 border-t pt-2.5\">\n {turn.files.map(file => (\n <span key={file} className=\"text-ui-sm text-neutral4 inline-flex max-w-44 items-center gap-1.5 truncate\">\n <FileText className=\"size-3.5 shrink-0 opacity-70\" aria-hidden />\n {file}\n </span>\n ))}\n {turn.hiddenFileCount > 0 && (\n <span className=\"text-ui-sm text-neutral4 font-medium\">+{turn.hiddenFileCount}</span>\n )}\n </div>\n )}\n </div>\n );\n}\n","import type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport type { MastraDBMessageMetadata } from '@mastra/react';\n\nconst SUMMARY_MAX_LENGTH = 120;\nconst FILE_PREVIEW_LIMIT = 2;\n// Inlined, not imported: the value export sits behind a barrel this browser bundle must not pull.\nconst CLIENT_MESSAGE_ID_KEY = 'clientMessageId' satisfies keyof MastraDBMessageMetadata;\n\nexport interface ThreadRailTurn {\n key: string;\n messageId: string;\n prompt: string;\n reply?: string;\n files: string[];\n hiddenFileCount: number;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport const getClientMessageKey = (message: MastraDBMessage): string => {\n const metadata = message.content.metadata;\n const clientMessageId = isRecord(metadata) ? metadata[CLIENT_MESSAGE_ID_KEY] : undefined;\n return typeof clientMessageId === 'string' && clientMessageId.length > 0 ? clientMessageId : message.id;\n};\n\nconst normalizeSummary = (value: string, fallback: string): string => {\n const normalized = value.replace(/\\s+/g, ' ').trim();\n if (normalized.length === 0) return fallback;\n if (normalized.length <= SUMMARY_MAX_LENGTH) return normalized;\n return `${normalized.slice(0, SUMMARY_MAX_LENGTH - 3).trimEnd()}...`;\n};\n\nconst getTextFromParts = (message: MastraDBMessage): string =>\n message.content.parts\n .flatMap(part => {\n if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return [];\n return [part.text];\n })\n .join('\\n');\n\nconst getStringField = (part: Record<string, unknown>, keys: string[]): string | undefined => {\n for (const key of keys) {\n const value = part[key];\n if (typeof value === 'string' && value.trim().length > 0) return value;\n }\n return undefined;\n};\n\nconst getPathLabel = (value: string): string | undefined => {\n if (value.startsWith('data:')) return undefined;\n\n try {\n const url = new URL(value);\n const candidate = url.pathname.split('/').filter(Boolean).pop();\n return candidate || url.hostname || undefined;\n } catch {\n const candidate = value.split(/[?#]/)[0]?.split('/').filter(Boolean).pop();\n return candidate && !candidate.startsWith('data:') ? candidate : undefined;\n }\n};\n\nconst getFallbackFileLabel = (part: Record<string, unknown>): string => {\n const mediaType = getStringField(part, ['mimeType', 'mediaType']) ?? '';\n if (part.type === 'image' || mediaType.startsWith('image/')) return 'Image';\n if (mediaType === 'application/pdf') return 'PDF';\n if (mediaType.startsWith('video/')) return 'Video';\n if (mediaType.startsWith('audio/')) return 'Audio';\n return 'File';\n};\n\nconst getFileLabelFromPart = (part: MastraDBMessage['content']['parts'][number]): string | undefined => {\n if (!isRecord(part)) return undefined;\n\n const type = getStringField(part, ['type']);\n if (type !== 'file' && type !== 'image') return undefined;\n\n const explicitLabel = getStringField(part, ['filename', 'name']);\n if (explicitLabel) return explicitLabel;\n\n const source = getStringField(part, ['url', 'image', 'data']);\n const pathLabel = source ? getPathLabel(source) : undefined;\n return pathLabel ?? getFallbackFileLabel(part);\n};\n\nconst getFileLabels = (message: MastraDBMessage): string[] =>\n message.content.parts.map(getFileLabelFromPart).filter((label): label is string => Boolean(label));\n\nconst getSignalType = (message: MastraDBMessage): string | undefined => {\n const signal = message.content.metadata?.signal;\n const metadataType = isRecord(signal) ? signal.type : undefined;\n return typeof metadataType === 'string' ? metadataType : message.type;\n};\n\nconst isDisplayableUserMessage = (message: MastraDBMessage): boolean => {\n if (message.role === 'user') return true;\n if (message.role !== 'signal') return false;\n\n const signalType = getSignalType(message);\n return signalType === 'user' || signalType === 'user-message';\n};\n\nconst getNextAssistantReply = (messages: MastraDBMessage[], startIndex: number): string | undefined => {\n for (let index = startIndex + 1; index < messages.length; index += 1) {\n const message = messages[index];\n if (!message) continue;\n if (isDisplayableUserMessage(message)) return undefined;\n if (message.role !== 'assistant') continue;\n\n const reply = normalizeSummary(getTextFromParts(message), '');\n if (reply) return reply;\n }\n\n return undefined;\n};\n\nexport const buildThreadRailTurns = (messages: MastraDBMessage[]): ThreadRailTurn[] =>\n messages.flatMap((message, index) => {\n if (!isDisplayableUserMessage(message)) return [];\n\n const files = getFileLabels(message);\n const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);\n\n return [\n {\n key: getClientMessageKey(message),\n messageId: message.id,\n prompt: normalizeSummary(getTextFromParts(message), files.length > 0 ? 'Attached file' : 'User message'),\n reply: getNextAssistantReply(messages, index),\n files: visibleFiles,\n hiddenFileCount: files.length - visibleFiles.length,\n },\n ];\n });\n"],"names":["React","useOptionalMessageScroller","useOptionalMessageScrollerVisibility","jsxs","cn","jsx","ScrollArea","useMeasuredAutoHeight","FileText"],"mappings":"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"}
1
+ {"version":3,"file":"ThreadRail.cjs.js","sources":["../../src/ds/components/ThreadRail/thread-rail.tsx","../../src/ds/components/ThreadRail/thread-rail-turns.ts"],"sourcesContent":["import { FileText } from 'lucide-react';\nimport * as React from 'react';\n\nimport type { ThreadRailTurn } from './thread-rail-turns';\n\nimport { useOptionalMessageScroller, useOptionalMessageScrollerVisibility } from '@/ds/components/MessageScroller';\nimport { ScrollArea } from '@/ds/components/ScrollArea';\nimport { useMeasuredAutoHeight } from '@/hooks/use-measured-auto-height';\nimport { cn } from '@/lib/utils';\n\ntype ThreadRailPreviewPhase = 'hidden' | 'entering' | 'visible' | 'exiting';\n\ntype ThreadRailPreviewState = {\n currentTurn: ThreadRailTurn | undefined;\n previousTurn: ThreadRailTurn | undefined;\n index: number | null;\n phase: ThreadRailPreviewPhase;\n top: number;\n transitionKey: number;\n};\n\nconst PREVIEW_TRANSITION_DURATION_MS = 360;\nconst PREVIEW_CLEANUP_DELAY_MS = PREVIEW_TRANSITION_DURATION_MS + 80;\n\nconst DEFAULT_PREVIEW_STATE: ThreadRailPreviewState = {\n currentTurn: undefined,\n previousTurn: undefined,\n index: null,\n phase: 'hidden',\n top: 0,\n transitionKey: 0,\n};\n\nexport interface ThreadRailProps {\n turns: ThreadRailTurn[];\n currentAnchorId?: string;\n visibleMessageIds?: string[];\n label?: string;\n className?: string;\n maxHeight?: string;\n scrollAreaClassName?: string;\n onSelect?: (turn: ThreadRailTurn) => void;\n}\n\nexport function ThreadRail({\n turns,\n currentAnchorId,\n visibleMessageIds,\n label = 'Conversation timeline',\n className,\n maxHeight = 'calc(100dvh - 12rem)',\n scrollAreaClassName,\n onSelect,\n}: ThreadRailProps) {\n const railRef = React.useRef<HTMLElement>(null);\n const previewId = React.useId();\n const [previewState, setPreviewState] = React.useState<ThreadRailPreviewState>(DEFAULT_PREVIEW_STATE);\n const messageScroller = useOptionalMessageScroller();\n const scrollerVisibility = useOptionalMessageScrollerVisibility();\n\n const fallbackMessageId = turns.at(-1)?.messageId;\n const activeMessageId = currentAnchorId ?? scrollerVisibility?.currentAnchorId ?? fallbackMessageId;\n const resolvedVisibleMessageIds =\n visibleMessageIds ?? scrollerVisibility?.visibleMessageIds ?? (activeMessageId ? [activeMessageId] : []);\n const visibleMessageIdSet = new Set(resolvedVisibleMessageIds);\n const previewHoverActive = previewState.phase === 'entering' || previewState.phase === 'visible';\n const hoveredIndex = previewHoverActive ? previewState.index : null;\n const selectTurn = React.useCallback(\n (turn: ThreadRailTurn) => {\n if (onSelect) {\n onSelect(turn);\n return;\n }\n\n messageScroller?.scrollToMessage(turn.messageId, { behavior: 'smooth', align: 'start' });\n },\n [messageScroller, onSelect],\n );\n\n const showPreview = (index: number, element: HTMLElement) => {\n const railTop = railRef.current?.getBoundingClientRect().top ?? 0;\n const itemRect = element.getBoundingClientRect();\n const nextTurn = turns[index];\n if (!nextTurn) return;\n\n setPreviewState(current => {\n const nextTop = itemRect.top + itemRect.height / 2 - railTop;\n if (current.index === index && current.phase !== 'hidden' && current.phase !== 'exiting') {\n return { ...current, top: nextTop };\n }\n\n const previousTurn =\n current.currentTurn && current.currentTurn.key !== nextTurn.key && current.phase !== 'hidden'\n ? current.currentTurn\n : undefined;\n\n return {\n currentTurn: nextTurn,\n previousTurn,\n index,\n phase: 'entering',\n top: nextTop,\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n const hidePreview = () => {\n setPreviewState(current => {\n if (!current.currentTurn || current.phase === 'hidden' || current.phase === 'exiting') {\n return { ...current, index: null };\n }\n\n return {\n ...current,\n index: null,\n previousTurn: undefined,\n phase: 'exiting',\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n React.useEffect(() => {\n if (previewState.phase !== 'entering') return;\n\n const requestFrame = window.requestAnimationFrame ?? window.setTimeout;\n const cancelFrame = window.cancelAnimationFrame ?? window.clearTimeout;\n let settleFrame: number | undefined;\n const startFrame = requestFrame(() => {\n settleFrame = requestFrame(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey && current.phase === 'entering'\n ? { ...current, phase: 'visible' }\n : current,\n );\n });\n });\n\n return () => {\n cancelFrame(startFrame);\n if (settleFrame !== undefined) cancelFrame(settleFrame);\n };\n }, [previewState.phase, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'visible' || !previewState.previousTurn) return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? { ...current, previousTurn: undefined } : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.previousTurn, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'exiting') return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? DEFAULT_PREVIEW_STATE : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.transitionKey]);\n\n if (turns.length === 0) return null;\n\n return (\n <nav\n ref={railRef}\n aria-label={label}\n data-testid=\"thread-rail\"\n className={cn('relative w-8 py-2', className)}\n onMouseLeave={hidePreview}\n onBlur={event => {\n const nextFocus = event.relatedTarget;\n if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) hidePreview();\n }}\n >\n <ScrollArea\n data-testid=\"thread-rail-scroll-area\"\n maxHeight={maxHeight}\n className={cn('w-8', scrollAreaClassName)}\n viewPortClassName=\"pr-3\"\n >\n <div className=\"flex w-4 flex-col items-start gap-2 py-2\">\n {turns.map((turn, index) => {\n const distance = hoveredIndex === null ? null : Math.abs(index - hoveredIndex);\n const active = turn.messageId === activeMessageId;\n const inView = visibleMessageIdSet.has(turn.messageId);\n const previewActive = hoveredIndex === index;\n\n return (\n <ThreadRailItem\n key={turn.key}\n turn={turn}\n index={index}\n distance={distance}\n active={active}\n inView={inView}\n previewId={previewActive ? previewId : undefined}\n onHoverChange={showPreview}\n onSelect={selectTurn}\n />\n );\n })}\n </div>\n </ScrollArea>\n\n {previewState.currentTurn && (\n <ThreadRailPreview\n id={previewId}\n currentTurn={previewState.currentTurn}\n previousTurn={previewState.previousTurn}\n phase={previewState.phase}\n top={previewState.top}\n />\n )}\n </nav>\n );\n}\n\ninterface ThreadRailItemProps {\n turn: ThreadRailTurn;\n index: number;\n distance: number | null;\n active: boolean;\n inView: boolean;\n previewId?: string;\n onHoverChange: (index: number, element: HTMLElement) => void;\n onSelect: (turn: ThreadRailTurn) => void;\n}\n\n/** Width of a rail tick: widest at the hovered item, tapering to neighbours. */\nconst getRailItemWidth = (distance: number | null): string => {\n if (distance === 0) return 'w-4';\n if (distance === 1) return 'w-3';\n return 'w-2';\n};\n\n/** Background tone of a rail tick, prioritising hovered/active, then in-view, then proximity. */\nconst getRailItemTone = ({\n distance,\n active,\n inView,\n}: Pick<ThreadRailItemProps, 'distance' | 'active' | 'inView'>): string => {\n if (distance === 0 || active) return 'bg-neutral6';\n if (inView) return 'bg-neutral5';\n if (distance === 1) return 'bg-neutral4';\n return 'bg-neutral3/60';\n};\n\nfunction ThreadRailItem({\n turn,\n index,\n distance,\n active,\n inView,\n previewId,\n onHoverChange,\n onSelect,\n}: ThreadRailItemProps) {\n const size = getRailItemWidth(distance);\n const tone = getRailItemTone({ distance, active, inView });\n\n return (\n <div\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n className=\"group/thread-rail-item relative flex items-center\"\n >\n <button\n type=\"button\"\n aria-label={`Jump to ${turn.prompt}`}\n aria-current={active ? 'location' : undefined}\n aria-describedby={previewId}\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n onClick={() => onSelect(turn)}\n onMouseEnter={event => onHoverChange(index, event.currentTarget)}\n onFocus={event => onHoverChange(index, event.currentTarget)}\n className={cn(\n 'relative block h-px cursor-pointer rounded-full transition-[width,background-color] duration-normal ease-out',\n \"before:absolute before:inset-x-0 before:-inset-y-1 before:content-['']\",\n 'focus-visible:ring-2 focus-visible:ring-accent1/40 focus-visible:outline-hidden',\n size,\n tone,\n )}\n />\n </div>\n );\n}\n\nfunction ThreadRailPreview({\n id,\n currentTurn,\n previousTurn,\n phase,\n top,\n}: {\n id: string;\n currentTurn: ThreadRailTurn;\n previousTurn: ThreadRailTurn | undefined;\n phase: ThreadRailPreviewPhase;\n top: number;\n}) {\n const {\n ref: previewSizerRef,\n heightStyle: previewHeightStyle,\n measure: measurePreviewHeight,\n } = useMeasuredAutoHeight<HTMLDivElement>();\n const containerVisible = phase === 'visible' || (phase === 'entering' && Boolean(previousTurn));\n // Entering and exiting share the same off-screen resting style.\n const hiddenLayerClassName = 'scale-95 opacity-0 blur-xs';\n const visibleLayerClassName = 'scale-100 opacity-100 blur-none';\n const layerClassName =\n 'absolute inset-x-0 top-0 h-full origin-left transition-[opacity,filter,scale] duration-150 ease-in-out will-change-[opacity,filter,scale] motion-reduce:scale-100 motion-reduce:blur-none motion-reduce:transition-none';\n\n React.useLayoutEffect(() => {\n measurePreviewHeight();\n }, [currentTurn.key, phase, measurePreviewHeight]);\n\n return (\n <div\n id={id}\n data-testid=\"thread-rail-preview\"\n data-visible={containerVisible ? 'true' : undefined}\n className={cn(\n 'pointer-events-none absolute top-0 left-full z-30 ml-3 w-72 overflow-hidden rounded-xl border border-border1 bg-surface3 text-left shadow-dialog transition-[height,translate,opacity] duration-360 ease-out-custom will-change-[height,translate,opacity] motion-reduce:transition-none',\n containerVisible ? 'opacity-100' : 'opacity-0',\n )}\n style={{ ...previewHeightStyle, translate: `0 calc(${top}px - 50%)` }}\n >\n <div ref={previewSizerRef} data-testid=\"thread-rail-preview-sizer\" aria-hidden className=\"invisible\">\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n <div data-testid=\"thread-rail-preview-viewport\" className=\"absolute inset-0\">\n <div className=\"relative h-full\">\n {previousTurn && (\n <div\n key={previousTurn.key}\n data-testid=\"thread-rail-preview-previous\"\n aria-hidden\n className={cn(layerClassName, phase === 'entering' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={previousTurn} className=\"p-3.5\" />\n </div>\n )}\n <div\n key={currentTurn.key}\n data-testid=\"thread-rail-preview-current\"\n className={cn(layerClassName, phase === 'visible' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ThreadRailPreviewContent({\n turn,\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement> & { turn: ThreadRailTurn }) {\n return (\n <div className={className} {...props}>\n <div className=\"text-ui-md leading-ui-md text-neutral6 truncate font-medium\">{turn.prompt}</div>\n {turn.reply && <p className=\"text-ui-sm leading-ui-sm text-neutral4 mt-1.5 line-clamp-3\">{turn.reply}</p>}\n {(turn.files.length > 0 || turn.hiddenFileCount > 0) && (\n <div className=\"border-border1/60 mt-3 flex flex-wrap items-center gap-2 border-t pt-2.5\">\n {turn.files.map(file => (\n <span key={file} className=\"text-ui-sm text-neutral4 inline-flex max-w-44 items-center gap-1.5 truncate\">\n <FileText className=\"size-3.5 shrink-0 opacity-70\" aria-hidden />\n {file}\n </span>\n ))}\n {turn.hiddenFileCount > 0 && (\n <span className=\"text-ui-sm text-neutral4 font-medium\">+{turn.hiddenFileCount}</span>\n )}\n </div>\n )}\n </div>\n );\n}\n","import type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport type { MastraDBMessageMetadata } from '@mastra/react';\n\nconst SUMMARY_MAX_LENGTH = 120;\nconst FILE_PREVIEW_LIMIT = 2;\n// Inlined, not imported: the value export sits behind a barrel this browser bundle must not pull.\nconst CLIENT_MESSAGE_ID_KEY = 'clientMessageId' satisfies keyof MastraDBMessageMetadata;\n\nexport interface ThreadRailTurn {\n key: string;\n messageId: string;\n prompt: string;\n reply?: string;\n files: string[];\n hiddenFileCount: number;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport const getClientMessageKey = (message: MastraDBMessage): string => {\n const metadata = message.content.metadata;\n const clientMessageId = isRecord(metadata) ? metadata[CLIENT_MESSAGE_ID_KEY] : undefined;\n return typeof clientMessageId === 'string' && clientMessageId.length > 0 ? clientMessageId : message.id;\n};\n\nconst normalizeSummary = (value: string, fallback: string): string => {\n const normalized = value.replace(/\\s+/g, ' ').trim();\n if (normalized.length === 0) return fallback;\n if (normalized.length <= SUMMARY_MAX_LENGTH) return normalized;\n return `${normalized.slice(0, SUMMARY_MAX_LENGTH - 3).trimEnd()}...`;\n};\n\nconst getTextFromParts = (message: MastraDBMessage): string =>\n message.content.parts\n .flatMap(part => {\n if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return [];\n return [part.text];\n })\n .join('\\n');\n\nconst getStringField = (part: Record<string, unknown>, keys: string[]): string | undefined => {\n for (const key of keys) {\n const value = part[key];\n if (typeof value === 'string' && value.trim().length > 0) return value;\n }\n return undefined;\n};\n\nconst getPathLabel = (value: string): string | undefined => {\n if (value.startsWith('data:')) return undefined;\n\n try {\n const url = new URL(value);\n const candidate = url.pathname.split('/').filter(Boolean).pop();\n return candidate || url.hostname || undefined;\n } catch {\n const candidate = value.split(/[?#]/)[0]?.split('/').filter(Boolean).pop();\n return candidate && !candidate.startsWith('data:') ? candidate : undefined;\n }\n};\n\nconst getFallbackFileLabel = (part: Record<string, unknown>): string => {\n const mediaType = getStringField(part, ['mimeType', 'mediaType']) ?? '';\n if (part.type === 'image' || mediaType.startsWith('image/')) return 'Image';\n if (mediaType === 'application/pdf') return 'PDF';\n if (mediaType.startsWith('video/')) return 'Video';\n if (mediaType.startsWith('audio/')) return 'Audio';\n return 'File';\n};\n\nconst getFileLabelFromPart = (part: MastraDBMessage['content']['parts'][number]): string | undefined => {\n if (!isRecord(part)) return undefined;\n\n const type = getStringField(part, ['type']);\n if (type !== 'file' && type !== 'image') return undefined;\n\n const explicitLabel = getStringField(part, ['filename', 'name']);\n if (explicitLabel) return explicitLabel;\n\n const source = getStringField(part, ['url', 'image', 'data']);\n const pathLabel = source ? getPathLabel(source) : undefined;\n return pathLabel ?? getFallbackFileLabel(part);\n};\n\nconst getFileLabels = (message: MastraDBMessage): string[] =>\n message.content.parts.map(getFileLabelFromPart).filter((label): label is string => Boolean(label));\n\nconst getSignalType = (message: MastraDBMessage): string | undefined => {\n const signal = message.content.metadata?.signal;\n const metadataType = isRecord(signal) ? signal.type : undefined;\n return typeof metadataType === 'string' ? metadataType : message.type;\n};\n\n/** What opens a turn: a rail stop, and the row the scroller parks at the top. */\nexport const startsUserTurn = (message: MastraDBMessage): boolean => {\n if (message.role === 'user') return true;\n if (message.role !== 'signal') return false;\n\n const signalType = getSignalType(message);\n return signalType === 'user' || signalType === 'user-message';\n};\n\nconst getNextAssistantReply = (messages: MastraDBMessage[], startIndex: number): string | undefined => {\n for (let index = startIndex + 1; index < messages.length; index += 1) {\n const message = messages[index];\n if (!message) continue;\n if (startsUserTurn(message)) return undefined;\n if (message.role !== 'assistant') continue;\n\n const reply = normalizeSummary(getTextFromParts(message), '');\n if (reply) return reply;\n }\n\n return undefined;\n};\n\nexport const buildThreadRailTurns = (messages: MastraDBMessage[]): ThreadRailTurn[] =>\n messages.flatMap((message, index) => {\n if (!startsUserTurn(message)) return [];\n\n const files = getFileLabels(message);\n const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);\n\n return [\n {\n key: getClientMessageKey(message),\n messageId: message.id,\n prompt: normalizeSummary(getTextFromParts(message), files.length > 0 ? 'Attached file' : 'User message'),\n reply: getNextAssistantReply(messages, index),\n files: visibleFiles,\n hiddenFileCount: files.length - visibleFiles.length,\n },\n ];\n });\n"],"names":["React","useOptionalMessageScroller","useOptionalMessageScrollerVisibility","jsxs","cn","jsx","ScrollArea","useMeasuredAutoHeight","FileText"],"mappings":"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"}
@@ -370,7 +370,7 @@ const getSignalType = (message) => {
370
370
  const metadataType = isRecord(signal) ? signal.type : void 0;
371
371
  return typeof metadataType === "string" ? metadataType : message.type;
372
372
  };
373
- const isDisplayableUserMessage = (message) => {
373
+ const startsUserTurn = (message) => {
374
374
  if (message.role === "user") return true;
375
375
  if (message.role !== "signal") return false;
376
376
  const signalType = getSignalType(message);
@@ -380,7 +380,7 @@ const getNextAssistantReply = (messages, startIndex) => {
380
380
  for (let index = startIndex + 1; index < messages.length; index += 1) {
381
381
  const message = messages[index];
382
382
  if (!message) continue;
383
- if (isDisplayableUserMessage(message)) return void 0;
383
+ if (startsUserTurn(message)) return void 0;
384
384
  if (message.role !== "assistant") continue;
385
385
  const reply = normalizeSummary(getTextFromParts(message), "");
386
386
  if (reply) return reply;
@@ -388,7 +388,7 @@ const getNextAssistantReply = (messages, startIndex) => {
388
388
  return void 0;
389
389
  };
390
390
  const buildThreadRailTurns = (messages) => messages.flatMap((message, index) => {
391
- if (!isDisplayableUserMessage(message)) return [];
391
+ if (!startsUserTurn(message)) return [];
392
392
  const files = getFileLabels(message);
393
393
  const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);
394
394
  return [
@@ -403,5 +403,5 @@ const buildThreadRailTurns = (messages) => messages.flatMap((message, index) =>
403
403
  ];
404
404
  });
405
405
 
406
- export { ThreadRail, buildThreadRailTurns, getClientMessageKey };
406
+ export { ThreadRail, buildThreadRailTurns, getClientMessageKey, startsUserTurn };
407
407
  //# sourceMappingURL=ThreadRail.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadRail.es.js","sources":["../../src/ds/components/ThreadRail/thread-rail.tsx","../../src/ds/components/ThreadRail/thread-rail-turns.ts"],"sourcesContent":["import { FileText } from 'lucide-react';\nimport * as React from 'react';\n\nimport type { ThreadRailTurn } from './thread-rail-turns';\n\nimport { useOptionalMessageScroller, useOptionalMessageScrollerVisibility } from '@/ds/components/MessageScroller';\nimport { ScrollArea } from '@/ds/components/ScrollArea';\nimport { useMeasuredAutoHeight } from '@/hooks/use-measured-auto-height';\nimport { cn } from '@/lib/utils';\n\ntype ThreadRailPreviewPhase = 'hidden' | 'entering' | 'visible' | 'exiting';\n\ntype ThreadRailPreviewState = {\n currentTurn: ThreadRailTurn | undefined;\n previousTurn: ThreadRailTurn | undefined;\n index: number | null;\n phase: ThreadRailPreviewPhase;\n top: number;\n transitionKey: number;\n};\n\nconst PREVIEW_TRANSITION_DURATION_MS = 360;\nconst PREVIEW_CLEANUP_DELAY_MS = PREVIEW_TRANSITION_DURATION_MS + 80;\n\nconst DEFAULT_PREVIEW_STATE: ThreadRailPreviewState = {\n currentTurn: undefined,\n previousTurn: undefined,\n index: null,\n phase: 'hidden',\n top: 0,\n transitionKey: 0,\n};\n\nexport interface ThreadRailProps {\n turns: ThreadRailTurn[];\n currentAnchorId?: string;\n visibleMessageIds?: string[];\n label?: string;\n className?: string;\n maxHeight?: string;\n scrollAreaClassName?: string;\n onSelect?: (turn: ThreadRailTurn) => void;\n}\n\nexport function ThreadRail({\n turns,\n currentAnchorId,\n visibleMessageIds,\n label = 'Conversation timeline',\n className,\n maxHeight = 'calc(100dvh - 12rem)',\n scrollAreaClassName,\n onSelect,\n}: ThreadRailProps) {\n const railRef = React.useRef<HTMLElement>(null);\n const previewId = React.useId();\n const [previewState, setPreviewState] = React.useState<ThreadRailPreviewState>(DEFAULT_PREVIEW_STATE);\n const messageScroller = useOptionalMessageScroller();\n const scrollerVisibility = useOptionalMessageScrollerVisibility();\n\n const fallbackMessageId = turns.at(-1)?.messageId;\n const activeMessageId = currentAnchorId ?? scrollerVisibility?.currentAnchorId ?? fallbackMessageId;\n const resolvedVisibleMessageIds =\n visibleMessageIds ?? scrollerVisibility?.visibleMessageIds ?? (activeMessageId ? [activeMessageId] : []);\n const visibleMessageIdSet = new Set(resolvedVisibleMessageIds);\n const previewHoverActive = previewState.phase === 'entering' || previewState.phase === 'visible';\n const hoveredIndex = previewHoverActive ? previewState.index : null;\n const selectTurn = React.useCallback(\n (turn: ThreadRailTurn) => {\n if (onSelect) {\n onSelect(turn);\n return;\n }\n\n messageScroller?.scrollToMessage(turn.messageId, { behavior: 'smooth', align: 'start' });\n },\n [messageScroller, onSelect],\n );\n\n const showPreview = (index: number, element: HTMLElement) => {\n const railTop = railRef.current?.getBoundingClientRect().top ?? 0;\n const itemRect = element.getBoundingClientRect();\n const nextTurn = turns[index];\n if (!nextTurn) return;\n\n setPreviewState(current => {\n const nextTop = itemRect.top + itemRect.height / 2 - railTop;\n if (current.index === index && current.phase !== 'hidden' && current.phase !== 'exiting') {\n return { ...current, top: nextTop };\n }\n\n const previousTurn =\n current.currentTurn && current.currentTurn.key !== nextTurn.key && current.phase !== 'hidden'\n ? current.currentTurn\n : undefined;\n\n return {\n currentTurn: nextTurn,\n previousTurn,\n index,\n phase: 'entering',\n top: nextTop,\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n const hidePreview = () => {\n setPreviewState(current => {\n if (!current.currentTurn || current.phase === 'hidden' || current.phase === 'exiting') {\n return { ...current, index: null };\n }\n\n return {\n ...current,\n index: null,\n previousTurn: undefined,\n phase: 'exiting',\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n React.useEffect(() => {\n if (previewState.phase !== 'entering') return;\n\n const requestFrame = window.requestAnimationFrame ?? window.setTimeout;\n const cancelFrame = window.cancelAnimationFrame ?? window.clearTimeout;\n let settleFrame: number | undefined;\n const startFrame = requestFrame(() => {\n settleFrame = requestFrame(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey && current.phase === 'entering'\n ? { ...current, phase: 'visible' }\n : current,\n );\n });\n });\n\n return () => {\n cancelFrame(startFrame);\n if (settleFrame !== undefined) cancelFrame(settleFrame);\n };\n }, [previewState.phase, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'visible' || !previewState.previousTurn) return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? { ...current, previousTurn: undefined } : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.previousTurn, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'exiting') return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? DEFAULT_PREVIEW_STATE : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.transitionKey]);\n\n if (turns.length === 0) return null;\n\n return (\n <nav\n ref={railRef}\n aria-label={label}\n data-testid=\"thread-rail\"\n className={cn('relative w-8 py-2', className)}\n onMouseLeave={hidePreview}\n onBlur={event => {\n const nextFocus = event.relatedTarget;\n if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) hidePreview();\n }}\n >\n <ScrollArea\n data-testid=\"thread-rail-scroll-area\"\n maxHeight={maxHeight}\n className={cn('w-8', scrollAreaClassName)}\n viewPortClassName=\"pr-3\"\n >\n <div className=\"flex w-4 flex-col items-start gap-2 py-2\">\n {turns.map((turn, index) => {\n const distance = hoveredIndex === null ? null : Math.abs(index - hoveredIndex);\n const active = turn.messageId === activeMessageId;\n const inView = visibleMessageIdSet.has(turn.messageId);\n const previewActive = hoveredIndex === index;\n\n return (\n <ThreadRailItem\n key={turn.key}\n turn={turn}\n index={index}\n distance={distance}\n active={active}\n inView={inView}\n previewId={previewActive ? previewId : undefined}\n onHoverChange={showPreview}\n onSelect={selectTurn}\n />\n );\n })}\n </div>\n </ScrollArea>\n\n {previewState.currentTurn && (\n <ThreadRailPreview\n id={previewId}\n currentTurn={previewState.currentTurn}\n previousTurn={previewState.previousTurn}\n phase={previewState.phase}\n top={previewState.top}\n />\n )}\n </nav>\n );\n}\n\ninterface ThreadRailItemProps {\n turn: ThreadRailTurn;\n index: number;\n distance: number | null;\n active: boolean;\n inView: boolean;\n previewId?: string;\n onHoverChange: (index: number, element: HTMLElement) => void;\n onSelect: (turn: ThreadRailTurn) => void;\n}\n\n/** Width of a rail tick: widest at the hovered item, tapering to neighbours. */\nconst getRailItemWidth = (distance: number | null): string => {\n if (distance === 0) return 'w-4';\n if (distance === 1) return 'w-3';\n return 'w-2';\n};\n\n/** Background tone of a rail tick, prioritising hovered/active, then in-view, then proximity. */\nconst getRailItemTone = ({\n distance,\n active,\n inView,\n}: Pick<ThreadRailItemProps, 'distance' | 'active' | 'inView'>): string => {\n if (distance === 0 || active) return 'bg-neutral6';\n if (inView) return 'bg-neutral5';\n if (distance === 1) return 'bg-neutral4';\n return 'bg-neutral3/60';\n};\n\nfunction ThreadRailItem({\n turn,\n index,\n distance,\n active,\n inView,\n previewId,\n onHoverChange,\n onSelect,\n}: ThreadRailItemProps) {\n const size = getRailItemWidth(distance);\n const tone = getRailItemTone({ distance, active, inView });\n\n return (\n <div\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n className=\"group/thread-rail-item relative flex items-center\"\n >\n <button\n type=\"button\"\n aria-label={`Jump to ${turn.prompt}`}\n aria-current={active ? 'location' : undefined}\n aria-describedby={previewId}\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n onClick={() => onSelect(turn)}\n onMouseEnter={event => onHoverChange(index, event.currentTarget)}\n onFocus={event => onHoverChange(index, event.currentTarget)}\n className={cn(\n 'relative block h-px cursor-pointer rounded-full transition-[width,background-color] duration-normal ease-out',\n \"before:absolute before:inset-x-0 before:-inset-y-1 before:content-['']\",\n 'focus-visible:ring-2 focus-visible:ring-accent1/40 focus-visible:outline-hidden',\n size,\n tone,\n )}\n />\n </div>\n );\n}\n\nfunction ThreadRailPreview({\n id,\n currentTurn,\n previousTurn,\n phase,\n top,\n}: {\n id: string;\n currentTurn: ThreadRailTurn;\n previousTurn: ThreadRailTurn | undefined;\n phase: ThreadRailPreviewPhase;\n top: number;\n}) {\n const {\n ref: previewSizerRef,\n heightStyle: previewHeightStyle,\n measure: measurePreviewHeight,\n } = useMeasuredAutoHeight<HTMLDivElement>();\n const containerVisible = phase === 'visible' || (phase === 'entering' && Boolean(previousTurn));\n // Entering and exiting share the same off-screen resting style.\n const hiddenLayerClassName = 'scale-95 opacity-0 blur-xs';\n const visibleLayerClassName = 'scale-100 opacity-100 blur-none';\n const layerClassName =\n 'absolute inset-x-0 top-0 h-full origin-left transition-[opacity,filter,scale] duration-150 ease-in-out will-change-[opacity,filter,scale] motion-reduce:scale-100 motion-reduce:blur-none motion-reduce:transition-none';\n\n React.useLayoutEffect(() => {\n measurePreviewHeight();\n }, [currentTurn.key, phase, measurePreviewHeight]);\n\n return (\n <div\n id={id}\n data-testid=\"thread-rail-preview\"\n data-visible={containerVisible ? 'true' : undefined}\n className={cn(\n 'pointer-events-none absolute top-0 left-full z-30 ml-3 w-72 overflow-hidden rounded-xl border border-border1 bg-surface3 text-left shadow-dialog transition-[height,translate,opacity] duration-360 ease-out-custom will-change-[height,translate,opacity] motion-reduce:transition-none',\n containerVisible ? 'opacity-100' : 'opacity-0',\n )}\n style={{ ...previewHeightStyle, translate: `0 calc(${top}px - 50%)` }}\n >\n <div ref={previewSizerRef} data-testid=\"thread-rail-preview-sizer\" aria-hidden className=\"invisible\">\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n <div data-testid=\"thread-rail-preview-viewport\" className=\"absolute inset-0\">\n <div className=\"relative h-full\">\n {previousTurn && (\n <div\n key={previousTurn.key}\n data-testid=\"thread-rail-preview-previous\"\n aria-hidden\n className={cn(layerClassName, phase === 'entering' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={previousTurn} className=\"p-3.5\" />\n </div>\n )}\n <div\n key={currentTurn.key}\n data-testid=\"thread-rail-preview-current\"\n className={cn(layerClassName, phase === 'visible' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ThreadRailPreviewContent({\n turn,\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement> & { turn: ThreadRailTurn }) {\n return (\n <div className={className} {...props}>\n <div className=\"text-ui-md leading-ui-md text-neutral6 truncate font-medium\">{turn.prompt}</div>\n {turn.reply && <p className=\"text-ui-sm leading-ui-sm text-neutral4 mt-1.5 line-clamp-3\">{turn.reply}</p>}\n {(turn.files.length > 0 || turn.hiddenFileCount > 0) && (\n <div className=\"border-border1/60 mt-3 flex flex-wrap items-center gap-2 border-t pt-2.5\">\n {turn.files.map(file => (\n <span key={file} className=\"text-ui-sm text-neutral4 inline-flex max-w-44 items-center gap-1.5 truncate\">\n <FileText className=\"size-3.5 shrink-0 opacity-70\" aria-hidden />\n {file}\n </span>\n ))}\n {turn.hiddenFileCount > 0 && (\n <span className=\"text-ui-sm text-neutral4 font-medium\">+{turn.hiddenFileCount}</span>\n )}\n </div>\n )}\n </div>\n );\n}\n","import type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport type { MastraDBMessageMetadata } from '@mastra/react';\n\nconst SUMMARY_MAX_LENGTH = 120;\nconst FILE_PREVIEW_LIMIT = 2;\n// Inlined, not imported: the value export sits behind a barrel this browser bundle must not pull.\nconst CLIENT_MESSAGE_ID_KEY = 'clientMessageId' satisfies keyof MastraDBMessageMetadata;\n\nexport interface ThreadRailTurn {\n key: string;\n messageId: string;\n prompt: string;\n reply?: string;\n files: string[];\n hiddenFileCount: number;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport const getClientMessageKey = (message: MastraDBMessage): string => {\n const metadata = message.content.metadata;\n const clientMessageId = isRecord(metadata) ? metadata[CLIENT_MESSAGE_ID_KEY] : undefined;\n return typeof clientMessageId === 'string' && clientMessageId.length > 0 ? clientMessageId : message.id;\n};\n\nconst normalizeSummary = (value: string, fallback: string): string => {\n const normalized = value.replace(/\\s+/g, ' ').trim();\n if (normalized.length === 0) return fallback;\n if (normalized.length <= SUMMARY_MAX_LENGTH) return normalized;\n return `${normalized.slice(0, SUMMARY_MAX_LENGTH - 3).trimEnd()}...`;\n};\n\nconst getTextFromParts = (message: MastraDBMessage): string =>\n message.content.parts\n .flatMap(part => {\n if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return [];\n return [part.text];\n })\n .join('\\n');\n\nconst getStringField = (part: Record<string, unknown>, keys: string[]): string | undefined => {\n for (const key of keys) {\n const value = part[key];\n if (typeof value === 'string' && value.trim().length > 0) return value;\n }\n return undefined;\n};\n\nconst getPathLabel = (value: string): string | undefined => {\n if (value.startsWith('data:')) return undefined;\n\n try {\n const url = new URL(value);\n const candidate = url.pathname.split('/').filter(Boolean).pop();\n return candidate || url.hostname || undefined;\n } catch {\n const candidate = value.split(/[?#]/)[0]?.split('/').filter(Boolean).pop();\n return candidate && !candidate.startsWith('data:') ? candidate : undefined;\n }\n};\n\nconst getFallbackFileLabel = (part: Record<string, unknown>): string => {\n const mediaType = getStringField(part, ['mimeType', 'mediaType']) ?? '';\n if (part.type === 'image' || mediaType.startsWith('image/')) return 'Image';\n if (mediaType === 'application/pdf') return 'PDF';\n if (mediaType.startsWith('video/')) return 'Video';\n if (mediaType.startsWith('audio/')) return 'Audio';\n return 'File';\n};\n\nconst getFileLabelFromPart = (part: MastraDBMessage['content']['parts'][number]): string | undefined => {\n if (!isRecord(part)) return undefined;\n\n const type = getStringField(part, ['type']);\n if (type !== 'file' && type !== 'image') return undefined;\n\n const explicitLabel = getStringField(part, ['filename', 'name']);\n if (explicitLabel) return explicitLabel;\n\n const source = getStringField(part, ['url', 'image', 'data']);\n const pathLabel = source ? getPathLabel(source) : undefined;\n return pathLabel ?? getFallbackFileLabel(part);\n};\n\nconst getFileLabels = (message: MastraDBMessage): string[] =>\n message.content.parts.map(getFileLabelFromPart).filter((label): label is string => Boolean(label));\n\nconst getSignalType = (message: MastraDBMessage): string | undefined => {\n const signal = message.content.metadata?.signal;\n const metadataType = isRecord(signal) ? signal.type : undefined;\n return typeof metadataType === 'string' ? metadataType : message.type;\n};\n\nconst isDisplayableUserMessage = (message: MastraDBMessage): boolean => {\n if (message.role === 'user') return true;\n if (message.role !== 'signal') return false;\n\n const signalType = getSignalType(message);\n return signalType === 'user' || signalType === 'user-message';\n};\n\nconst getNextAssistantReply = (messages: MastraDBMessage[], startIndex: number): string | undefined => {\n for (let index = startIndex + 1; index < messages.length; index += 1) {\n const message = messages[index];\n if (!message) continue;\n if (isDisplayableUserMessage(message)) return undefined;\n if (message.role !== 'assistant') continue;\n\n const reply = normalizeSummary(getTextFromParts(message), '');\n if (reply) return reply;\n }\n\n return undefined;\n};\n\nexport const buildThreadRailTurns = (messages: MastraDBMessage[]): ThreadRailTurn[] =>\n messages.flatMap((message, index) => {\n if (!isDisplayableUserMessage(message)) return [];\n\n const files = getFileLabels(message);\n const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);\n\n return [\n {\n key: getClientMessageKey(message),\n messageId: message.id,\n prompt: normalizeSummary(getTextFromParts(message), files.length > 0 ? 'Attached file' : 'User message'),\n reply: getNextAssistantReply(messages, index),\n files: visibleFiles,\n hiddenFileCount: files.length - visibleFiles.length,\n },\n ];\n });\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ThreadRail.es.js","sources":["../../src/ds/components/ThreadRail/thread-rail.tsx","../../src/ds/components/ThreadRail/thread-rail-turns.ts"],"sourcesContent":["import { FileText } from 'lucide-react';\nimport * as React from 'react';\n\nimport type { ThreadRailTurn } from './thread-rail-turns';\n\nimport { useOptionalMessageScroller, useOptionalMessageScrollerVisibility } from '@/ds/components/MessageScroller';\nimport { ScrollArea } from '@/ds/components/ScrollArea';\nimport { useMeasuredAutoHeight } from '@/hooks/use-measured-auto-height';\nimport { cn } from '@/lib/utils';\n\ntype ThreadRailPreviewPhase = 'hidden' | 'entering' | 'visible' | 'exiting';\n\ntype ThreadRailPreviewState = {\n currentTurn: ThreadRailTurn | undefined;\n previousTurn: ThreadRailTurn | undefined;\n index: number | null;\n phase: ThreadRailPreviewPhase;\n top: number;\n transitionKey: number;\n};\n\nconst PREVIEW_TRANSITION_DURATION_MS = 360;\nconst PREVIEW_CLEANUP_DELAY_MS = PREVIEW_TRANSITION_DURATION_MS + 80;\n\nconst DEFAULT_PREVIEW_STATE: ThreadRailPreviewState = {\n currentTurn: undefined,\n previousTurn: undefined,\n index: null,\n phase: 'hidden',\n top: 0,\n transitionKey: 0,\n};\n\nexport interface ThreadRailProps {\n turns: ThreadRailTurn[];\n currentAnchorId?: string;\n visibleMessageIds?: string[];\n label?: string;\n className?: string;\n maxHeight?: string;\n scrollAreaClassName?: string;\n onSelect?: (turn: ThreadRailTurn) => void;\n}\n\nexport function ThreadRail({\n turns,\n currentAnchorId,\n visibleMessageIds,\n label = 'Conversation timeline',\n className,\n maxHeight = 'calc(100dvh - 12rem)',\n scrollAreaClassName,\n onSelect,\n}: ThreadRailProps) {\n const railRef = React.useRef<HTMLElement>(null);\n const previewId = React.useId();\n const [previewState, setPreviewState] = React.useState<ThreadRailPreviewState>(DEFAULT_PREVIEW_STATE);\n const messageScroller = useOptionalMessageScroller();\n const scrollerVisibility = useOptionalMessageScrollerVisibility();\n\n const fallbackMessageId = turns.at(-1)?.messageId;\n const activeMessageId = currentAnchorId ?? scrollerVisibility?.currentAnchorId ?? fallbackMessageId;\n const resolvedVisibleMessageIds =\n visibleMessageIds ?? scrollerVisibility?.visibleMessageIds ?? (activeMessageId ? [activeMessageId] : []);\n const visibleMessageIdSet = new Set(resolvedVisibleMessageIds);\n const previewHoverActive = previewState.phase === 'entering' || previewState.phase === 'visible';\n const hoveredIndex = previewHoverActive ? previewState.index : null;\n const selectTurn = React.useCallback(\n (turn: ThreadRailTurn) => {\n if (onSelect) {\n onSelect(turn);\n return;\n }\n\n messageScroller?.scrollToMessage(turn.messageId, { behavior: 'smooth', align: 'start' });\n },\n [messageScroller, onSelect],\n );\n\n const showPreview = (index: number, element: HTMLElement) => {\n const railTop = railRef.current?.getBoundingClientRect().top ?? 0;\n const itemRect = element.getBoundingClientRect();\n const nextTurn = turns[index];\n if (!nextTurn) return;\n\n setPreviewState(current => {\n const nextTop = itemRect.top + itemRect.height / 2 - railTop;\n if (current.index === index && current.phase !== 'hidden' && current.phase !== 'exiting') {\n return { ...current, top: nextTop };\n }\n\n const previousTurn =\n current.currentTurn && current.currentTurn.key !== nextTurn.key && current.phase !== 'hidden'\n ? current.currentTurn\n : undefined;\n\n return {\n currentTurn: nextTurn,\n previousTurn,\n index,\n phase: 'entering',\n top: nextTop,\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n const hidePreview = () => {\n setPreviewState(current => {\n if (!current.currentTurn || current.phase === 'hidden' || current.phase === 'exiting') {\n return { ...current, index: null };\n }\n\n return {\n ...current,\n index: null,\n previousTurn: undefined,\n phase: 'exiting',\n transitionKey: current.transitionKey + 1,\n };\n });\n };\n\n React.useEffect(() => {\n if (previewState.phase !== 'entering') return;\n\n const requestFrame = window.requestAnimationFrame ?? window.setTimeout;\n const cancelFrame = window.cancelAnimationFrame ?? window.clearTimeout;\n let settleFrame: number | undefined;\n const startFrame = requestFrame(() => {\n settleFrame = requestFrame(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey && current.phase === 'entering'\n ? { ...current, phase: 'visible' }\n : current,\n );\n });\n });\n\n return () => {\n cancelFrame(startFrame);\n if (settleFrame !== undefined) cancelFrame(settleFrame);\n };\n }, [previewState.phase, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'visible' || !previewState.previousTurn) return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? { ...current, previousTurn: undefined } : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.previousTurn, previewState.transitionKey]);\n\n React.useEffect(() => {\n if (previewState.phase !== 'exiting') return;\n\n const timeout = window.setTimeout(() => {\n setPreviewState(current =>\n current.transitionKey === previewState.transitionKey ? DEFAULT_PREVIEW_STATE : current,\n );\n }, PREVIEW_CLEANUP_DELAY_MS);\n\n return () => window.clearTimeout(timeout);\n }, [previewState.phase, previewState.transitionKey]);\n\n if (turns.length === 0) return null;\n\n return (\n <nav\n ref={railRef}\n aria-label={label}\n data-testid=\"thread-rail\"\n className={cn('relative w-8 py-2', className)}\n onMouseLeave={hidePreview}\n onBlur={event => {\n const nextFocus = event.relatedTarget;\n if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) hidePreview();\n }}\n >\n <ScrollArea\n data-testid=\"thread-rail-scroll-area\"\n maxHeight={maxHeight}\n className={cn('w-8', scrollAreaClassName)}\n viewPortClassName=\"pr-3\"\n >\n <div className=\"flex w-4 flex-col items-start gap-2 py-2\">\n {turns.map((turn, index) => {\n const distance = hoveredIndex === null ? null : Math.abs(index - hoveredIndex);\n const active = turn.messageId === activeMessageId;\n const inView = visibleMessageIdSet.has(turn.messageId);\n const previewActive = hoveredIndex === index;\n\n return (\n <ThreadRailItem\n key={turn.key}\n turn={turn}\n index={index}\n distance={distance}\n active={active}\n inView={inView}\n previewId={previewActive ? previewId : undefined}\n onHoverChange={showPreview}\n onSelect={selectTurn}\n />\n );\n })}\n </div>\n </ScrollArea>\n\n {previewState.currentTurn && (\n <ThreadRailPreview\n id={previewId}\n currentTurn={previewState.currentTurn}\n previousTurn={previewState.previousTurn}\n phase={previewState.phase}\n top={previewState.top}\n />\n )}\n </nav>\n );\n}\n\ninterface ThreadRailItemProps {\n turn: ThreadRailTurn;\n index: number;\n distance: number | null;\n active: boolean;\n inView: boolean;\n previewId?: string;\n onHoverChange: (index: number, element: HTMLElement) => void;\n onSelect: (turn: ThreadRailTurn) => void;\n}\n\n/** Width of a rail tick: widest at the hovered item, tapering to neighbours. */\nconst getRailItemWidth = (distance: number | null): string => {\n if (distance === 0) return 'w-4';\n if (distance === 1) return 'w-3';\n return 'w-2';\n};\n\n/** Background tone of a rail tick, prioritising hovered/active, then in-view, then proximity. */\nconst getRailItemTone = ({\n distance,\n active,\n inView,\n}: Pick<ThreadRailItemProps, 'distance' | 'active' | 'inView'>): string => {\n if (distance === 0 || active) return 'bg-neutral6';\n if (inView) return 'bg-neutral5';\n if (distance === 1) return 'bg-neutral4';\n return 'bg-neutral3/60';\n};\n\nfunction ThreadRailItem({\n turn,\n index,\n distance,\n active,\n inView,\n previewId,\n onHoverChange,\n onSelect,\n}: ThreadRailItemProps) {\n const size = getRailItemWidth(distance);\n const tone = getRailItemTone({ distance, active, inView });\n\n return (\n <div\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n className=\"group/thread-rail-item relative flex items-center\"\n >\n <button\n type=\"button\"\n aria-label={`Jump to ${turn.prompt}`}\n aria-current={active ? 'location' : undefined}\n aria-describedby={previewId}\n data-in-view={inView ? 'true' : undefined}\n data-active={active ? 'true' : undefined}\n onClick={() => onSelect(turn)}\n onMouseEnter={event => onHoverChange(index, event.currentTarget)}\n onFocus={event => onHoverChange(index, event.currentTarget)}\n className={cn(\n 'relative block h-px cursor-pointer rounded-full transition-[width,background-color] duration-normal ease-out',\n \"before:absolute before:inset-x-0 before:-inset-y-1 before:content-['']\",\n 'focus-visible:ring-2 focus-visible:ring-accent1/40 focus-visible:outline-hidden',\n size,\n tone,\n )}\n />\n </div>\n );\n}\n\nfunction ThreadRailPreview({\n id,\n currentTurn,\n previousTurn,\n phase,\n top,\n}: {\n id: string;\n currentTurn: ThreadRailTurn;\n previousTurn: ThreadRailTurn | undefined;\n phase: ThreadRailPreviewPhase;\n top: number;\n}) {\n const {\n ref: previewSizerRef,\n heightStyle: previewHeightStyle,\n measure: measurePreviewHeight,\n } = useMeasuredAutoHeight<HTMLDivElement>();\n const containerVisible = phase === 'visible' || (phase === 'entering' && Boolean(previousTurn));\n // Entering and exiting share the same off-screen resting style.\n const hiddenLayerClassName = 'scale-95 opacity-0 blur-xs';\n const visibleLayerClassName = 'scale-100 opacity-100 blur-none';\n const layerClassName =\n 'absolute inset-x-0 top-0 h-full origin-left transition-[opacity,filter,scale] duration-150 ease-in-out will-change-[opacity,filter,scale] motion-reduce:scale-100 motion-reduce:blur-none motion-reduce:transition-none';\n\n React.useLayoutEffect(() => {\n measurePreviewHeight();\n }, [currentTurn.key, phase, measurePreviewHeight]);\n\n return (\n <div\n id={id}\n data-testid=\"thread-rail-preview\"\n data-visible={containerVisible ? 'true' : undefined}\n className={cn(\n 'pointer-events-none absolute top-0 left-full z-30 ml-3 w-72 overflow-hidden rounded-xl border border-border1 bg-surface3 text-left shadow-dialog transition-[height,translate,opacity] duration-360 ease-out-custom will-change-[height,translate,opacity] motion-reduce:transition-none',\n containerVisible ? 'opacity-100' : 'opacity-0',\n )}\n style={{ ...previewHeightStyle, translate: `0 calc(${top}px - 50%)` }}\n >\n <div ref={previewSizerRef} data-testid=\"thread-rail-preview-sizer\" aria-hidden className=\"invisible\">\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n <div data-testid=\"thread-rail-preview-viewport\" className=\"absolute inset-0\">\n <div className=\"relative h-full\">\n {previousTurn && (\n <div\n key={previousTurn.key}\n data-testid=\"thread-rail-preview-previous\"\n aria-hidden\n className={cn(layerClassName, phase === 'entering' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={previousTurn} className=\"p-3.5\" />\n </div>\n )}\n <div\n key={currentTurn.key}\n data-testid=\"thread-rail-preview-current\"\n className={cn(layerClassName, phase === 'visible' ? visibleLayerClassName : hiddenLayerClassName)}\n >\n <ThreadRailPreviewContent turn={currentTurn} className=\"p-3.5\" />\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction ThreadRailPreviewContent({\n turn,\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement> & { turn: ThreadRailTurn }) {\n return (\n <div className={className} {...props}>\n <div className=\"text-ui-md leading-ui-md text-neutral6 truncate font-medium\">{turn.prompt}</div>\n {turn.reply && <p className=\"text-ui-sm leading-ui-sm text-neutral4 mt-1.5 line-clamp-3\">{turn.reply}</p>}\n {(turn.files.length > 0 || turn.hiddenFileCount > 0) && (\n <div className=\"border-border1/60 mt-3 flex flex-wrap items-center gap-2 border-t pt-2.5\">\n {turn.files.map(file => (\n <span key={file} className=\"text-ui-sm text-neutral4 inline-flex max-w-44 items-center gap-1.5 truncate\">\n <FileText className=\"size-3.5 shrink-0 opacity-70\" aria-hidden />\n {file}\n </span>\n ))}\n {turn.hiddenFileCount > 0 && (\n <span className=\"text-ui-sm text-neutral4 font-medium\">+{turn.hiddenFileCount}</span>\n )}\n </div>\n )}\n </div>\n );\n}\n","import type { MastraDBMessage } from '@mastra/core/agent/message-list';\nimport type { MastraDBMessageMetadata } from '@mastra/react';\n\nconst SUMMARY_MAX_LENGTH = 120;\nconst FILE_PREVIEW_LIMIT = 2;\n// Inlined, not imported: the value export sits behind a barrel this browser bundle must not pull.\nconst CLIENT_MESSAGE_ID_KEY = 'clientMessageId' satisfies keyof MastraDBMessageMetadata;\n\nexport interface ThreadRailTurn {\n key: string;\n messageId: string;\n prompt: string;\n reply?: string;\n files: string[];\n hiddenFileCount: number;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport const getClientMessageKey = (message: MastraDBMessage): string => {\n const metadata = message.content.metadata;\n const clientMessageId = isRecord(metadata) ? metadata[CLIENT_MESSAGE_ID_KEY] : undefined;\n return typeof clientMessageId === 'string' && clientMessageId.length > 0 ? clientMessageId : message.id;\n};\n\nconst normalizeSummary = (value: string, fallback: string): string => {\n const normalized = value.replace(/\\s+/g, ' ').trim();\n if (normalized.length === 0) return fallback;\n if (normalized.length <= SUMMARY_MAX_LENGTH) return normalized;\n return `${normalized.slice(0, SUMMARY_MAX_LENGTH - 3).trimEnd()}...`;\n};\n\nconst getTextFromParts = (message: MastraDBMessage): string =>\n message.content.parts\n .flatMap(part => {\n if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return [];\n return [part.text];\n })\n .join('\\n');\n\nconst getStringField = (part: Record<string, unknown>, keys: string[]): string | undefined => {\n for (const key of keys) {\n const value = part[key];\n if (typeof value === 'string' && value.trim().length > 0) return value;\n }\n return undefined;\n};\n\nconst getPathLabel = (value: string): string | undefined => {\n if (value.startsWith('data:')) return undefined;\n\n try {\n const url = new URL(value);\n const candidate = url.pathname.split('/').filter(Boolean).pop();\n return candidate || url.hostname || undefined;\n } catch {\n const candidate = value.split(/[?#]/)[0]?.split('/').filter(Boolean).pop();\n return candidate && !candidate.startsWith('data:') ? candidate : undefined;\n }\n};\n\nconst getFallbackFileLabel = (part: Record<string, unknown>): string => {\n const mediaType = getStringField(part, ['mimeType', 'mediaType']) ?? '';\n if (part.type === 'image' || mediaType.startsWith('image/')) return 'Image';\n if (mediaType === 'application/pdf') return 'PDF';\n if (mediaType.startsWith('video/')) return 'Video';\n if (mediaType.startsWith('audio/')) return 'Audio';\n return 'File';\n};\n\nconst getFileLabelFromPart = (part: MastraDBMessage['content']['parts'][number]): string | undefined => {\n if (!isRecord(part)) return undefined;\n\n const type = getStringField(part, ['type']);\n if (type !== 'file' && type !== 'image') return undefined;\n\n const explicitLabel = getStringField(part, ['filename', 'name']);\n if (explicitLabel) return explicitLabel;\n\n const source = getStringField(part, ['url', 'image', 'data']);\n const pathLabel = source ? getPathLabel(source) : undefined;\n return pathLabel ?? getFallbackFileLabel(part);\n};\n\nconst getFileLabels = (message: MastraDBMessage): string[] =>\n message.content.parts.map(getFileLabelFromPart).filter((label): label is string => Boolean(label));\n\nconst getSignalType = (message: MastraDBMessage): string | undefined => {\n const signal = message.content.metadata?.signal;\n const metadataType = isRecord(signal) ? signal.type : undefined;\n return typeof metadataType === 'string' ? metadataType : message.type;\n};\n\n/** What opens a turn: a rail stop, and the row the scroller parks at the top. */\nexport const startsUserTurn = (message: MastraDBMessage): boolean => {\n if (message.role === 'user') return true;\n if (message.role !== 'signal') return false;\n\n const signalType = getSignalType(message);\n return signalType === 'user' || signalType === 'user-message';\n};\n\nconst getNextAssistantReply = (messages: MastraDBMessage[], startIndex: number): string | undefined => {\n for (let index = startIndex + 1; index < messages.length; index += 1) {\n const message = messages[index];\n if (!message) continue;\n if (startsUserTurn(message)) return undefined;\n if (message.role !== 'assistant') continue;\n\n const reply = normalizeSummary(getTextFromParts(message), '');\n if (reply) return reply;\n }\n\n return undefined;\n};\n\nexport const buildThreadRailTurns = (messages: MastraDBMessage[]): ThreadRailTurn[] =>\n messages.flatMap((message, index) => {\n if (!startsUserTurn(message)) return [];\n\n const files = getFileLabels(message);\n const visibleFiles = files.slice(0, FILE_PREVIEW_LIMIT);\n\n return [\n {\n key: getClientMessageKey(message),\n messageId: message.id,\n prompt: normalizeSummary(getTextFromParts(message), files.length > 0 ? 'Attached file' : 'User message'),\n reply: getNextAssistantReply(messages, index),\n files: visibleFiles,\n hiddenFileCount: files.length - visibleFiles.length,\n },\n ];\n });\n"],"names":[],"mappings":"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"}