@devalok/shilp-sutra 0.45.0 → 0.45.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"chat.js","names":[],"sources":["../../src/ui/chat/date-separator.tsx","../../src/ui/chat/message.tsx","../../src/ui/chat/message-input.tsx","../../src/ui/chat/message-list.tsx","../../src/ui/chat/system-message.tsx","../../src/ui/chat/typing-indicator.tsx","../../src/ui/chat/unread-separator.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface DateSeparatorProps {\n date: Date | string\n format?: (date: Date) => string\n className?: string\n}\n\nfunction defaultFormat(date: Date): string {\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const target = new Date(date.getFullYear(), date.getMonth(), date.getDate())\n const diffMs = today.getTime() - target.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays === 0) return 'Today'\n if (diffDays === 1) return 'Yesterday'\n\n const month = date.toLocaleString('en-US', { month: 'short' })\n const day = date.getDate()\n\n if (date.getFullYear() !== now.getFullYear()) {\n return `${month} ${day}, ${date.getFullYear()}`\n }\n\n return `${month} ${day}`\n}\n\nfunction DateSeparator({ date, format, className }: DateSeparatorProps) {\n const d = typeof date === 'string' ? new Date(date) : date\n const label = format ? format(d) : defaultFormat(d)\n\n return (\n <div className={cn('flex items-center gap-ds-03 py-ds-03', className)}>\n <div className=\"flex-1 border-t border-surface-border-subtle\" />\n <span className=\"text-ds-xs font-medium text-surface-fg-subtle/50 uppercase tracking-wider\">\n {label}\n </span>\n <div className=\"flex-1 border-t border-surface-border-subtle\" />\n </div>\n )\n}\nDateSeparator.displayName = 'DateSeparator'\n\nexport { DateSeparator }\n","'use client'\n\nimport { IconTrash } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Avatar, AvatarFallback,AvatarImage } from '../avatar'\nimport { Icon } from '../icon'\nimport { IconProvider } from '../icon-context'\nimport type { IconInput } from '../lib/icon-input'\nimport { motionProps,springs } from '../lib/motion'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\nimport { Textarea } from '../textarea'\nimport { Tooltip, TooltipContent,TooltipTrigger } from '../tooltip'\n\n// ── Context ──────────────────────────────────────────────────────────────\n\ninterface MessageContextValue {\n variant: 'flat' | 'bubble'\n placement: 'start' | 'end'\n grouped: boolean\n highlight?: 'mention' | 'internal'\n}\n\nconst MessageContext = React.createContext<MessageContextValue>({\n variant: 'flat',\n placement: 'start',\n grouped: false,\n})\n\nfunction useMessageContext() {\n return React.useContext(MessageContext)\n}\n\n// ── Root ─────────────────────────────────────────────────────────────────\n\nexport interface MessageProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n variant?: 'flat' | 'bubble'\n placement?: 'start' | 'end'\n highlight?: 'mention' | 'internal'\n grouped?: boolean\n deleted?: boolean\n deletedText?: string\n}\n\nconst MessageRoot = React.forwardRef<HTMLDivElement, MessageProps>(\n (\n {\n children,\n variant = 'flat',\n placement = 'start',\n highlight,\n grouped = false,\n deleted = false,\n deletedText = 'This message was deleted',\n className,\n ...props\n },\n ref,\n ) => {\n const ctx = React.useMemo<MessageContextValue>(\n () => ({ variant, placement, grouped, highlight }),\n [variant, placement, grouped, highlight],\n )\n\n if (deleted) {\n return (\n <motion.div\n ref={ref}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'flex items-center gap-ds-02 py-ds-02 text-ds-xs text-surface-fg-subtle/50 italic',\n className,\n )}\n {...motionProps(props)}\n >\n <Icon icon={IconTrash} size=\"xs\" />\n {deletedText}\n </motion.div>\n )\n }\n\n if (variant === 'bubble') {\n return (\n <MessageContext.Provider value={ctx}>\n <motion.div\n ref={ref}\n data-highlight={highlight}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'group/message relative flex',\n placement === 'end' ? 'justify-end' : 'justify-start',\n // mention is carried by the in-content @token, not a row tint/rail\n highlight === 'internal' && 'bg-warning-2/50 rounded-control-inner',\n className,\n )}\n {...motionProps(props)}\n >\n <div\n className={cn(\n 'max-w-[85%] rounded-bubble px-ds-04 py-ds-03',\n placement === 'end'\n ? 'bg-accent-3 text-surface-fg'\n : 'bg-surface-raised text-surface-fg',\n )}\n >\n {children}\n </div>\n </motion.div>\n </MessageContext.Provider>\n )\n }\n\n // flat variant (default)\n return (\n <MessageContext.Provider value={ctx}>\n <motion.div\n ref={ref}\n data-highlight={highlight}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'group/message relative flex gap-ds-04',\n grouped && '-mt-ds-01',\n // mention is carried by the in-content @token, not a row rail/tint\n highlight === 'internal' && 'bg-warning-2/50 rounded-control-inner',\n className,\n )}\n {...motionProps(props)}\n >\n {children}\n </motion.div>\n </MessageContext.Provider>\n )\n },\n)\nMessageRoot.displayName = 'Message'\n\n// ── Avatar ───────────────────────────────────────────────────────────────\n\nexport interface MessageAvatarProps {\n src?: string | null\n fallback?: string\n icon?: IconInput\n size?: 'sm' | 'md'\n children?: React.ReactNode\n}\n\nfunction MessageAvatar({\n src,\n fallback,\n icon,\n size = 'md',\n children,\n}: MessageAvatarProps) {\n const { grouped } = useMessageContext()\n\n const sizeClass = size === 'sm' ? 'w-5' : 'w-6'\n const heightClass = size === 'sm' ? 'h-5' : 'h-6'\n\n if (grouped) {\n return <div className={cn(sizeClass, heightClass, 'shrink-0')} />\n }\n\n if (children) {\n return (\n <div className={cn(sizeClass, 'shrink-0 flex items-start')}>\n {children}\n </div>\n )\n }\n\n if (icon) {\n return (\n <div\n className={cn(\n sizeClass,\n size === 'sm' ? 'h-5' : 'h-6',\n 'shrink-0 flex items-center justify-center rounded-pill bg-surface-raised-hover',\n )}\n >\n <IconProvider size={size === 'sm' ? 'xs' : 'sm'}>{normalizeIcon(icon)}</IconProvider>\n </div>\n )\n }\n\n return (\n <div className={cn(sizeClass, 'shrink-0')}>\n <Avatar size={size === 'sm' ? 'xs' : 'sm'}>\n {src && <AvatarImage src={src} alt={fallback ?? ''} />}\n <AvatarFallback>{fallback ?? ''}</AvatarFallback>\n </Avatar>\n </div>\n )\n}\nMessageAvatar.displayName = 'Message.Avatar'\n\n// ── Content ──────────────────────────────────────────────────────────────\n\nexport interface MessageContentProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nfunction MessageContent({ children, className, ...props }: MessageContentProps) {\n return (\n <div className={cn('min-w-0 flex-1 flex flex-col gap-ds-02', className)} {...props}>\n {children}\n </div>\n )\n}\nMessageContent.displayName = 'Message.Content'\n\n// ── Author ───────────────────────────────────────────────────────────────\n\nexport interface MessageAuthorProps {\n name: string\n badge?: React.ReactNode\n timestamp?: Date\n formattedTimestamp?: string\n timestampFormat?: (date: Date) => string\n}\n\nfunction defaultTimestampFormat(date: Date): string {\n return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })\n}\n\nfunction MessageAuthor({\n name,\n badge,\n timestamp,\n formattedTimestamp,\n timestampFormat = defaultTimestampFormat,\n}: MessageAuthorProps) {\n const { grouped } = useMessageContext()\n\n if (grouped) return null\n\n const timeStr = formattedTimestamp\n ?? (timestamp ? timestampFormat(timestamp) : undefined)\n\n return (\n <div className=\"flex items-baseline gap-ds-02\">\n <span className=\"font-semibold text-[13px] text-surface-fg\">{name}</span>\n {badge}\n {timeStr && (\n <span className=\"text-[11px] text-surface-fg-subtle/50\">{timeStr}</span>\n )}\n </div>\n )\n}\nMessageAuthor.displayName = 'Message.Author'\n\n// ── Body ─────────────────────────────────────────────────────────────────\n\nexport interface MessageBodyProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nfunction MessageBody({ children, className, ...props }: MessageBodyProps) {\n return (\n <div\n className={cn(\n 'text-[13px] leading-relaxed text-surface-fg whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\nMessageBody.displayName = 'Message.Body'\n\n// ── EditableBody ─────────────────────────────────────────────────────────\n\nexport interface MessageEditableBodyProps {\n content: string\n onSave: (newContent: string) => void\n onCancel?: () => void\n canEdit?: boolean\n renderContent?: (content: string) => React.ReactNode\n}\n\nfunction MessageEditableBody({\n content,\n onSave,\n onCancel,\n canEdit = false,\n renderContent,\n}: MessageEditableBodyProps) {\n const [isEditing, setIsEditing] = React.useState(false)\n const [editDraft, setEditDraft] = React.useState(content)\n const textareaRef = React.useRef<HTMLTextAreaElement>(null)\n\n React.useEffect(() => {\n if (isEditing && textareaRef.current) {\n textareaRef.current.focus()\n textareaRef.current.select()\n }\n }, [isEditing])\n\n const handleStartEdit = React.useCallback(() => {\n if (!canEdit) return\n setEditDraft(content)\n setIsEditing(true)\n }, [canEdit, content])\n\n const handleSave = React.useCallback(() => {\n const trimmed = editDraft.trim()\n if (trimmed && trimmed !== content) {\n onSave(trimmed)\n }\n setIsEditing(false)\n }, [editDraft, content, onSave])\n\n const handleCancel = React.useCallback(() => {\n setIsEditing(false)\n setEditDraft(content)\n onCancel?.()\n }, [content, onCancel])\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n handleSave()\n } else if (e.key === 'Escape') {\n e.preventDefault()\n handleCancel()\n }\n },\n [handleSave, handleCancel],\n )\n\n if (isEditing) {\n return (\n <div className=\"text-[13px] leading-relaxed\">\n <Textarea\n ref={textareaRef}\n value={editDraft}\n onChange={(e) => setEditDraft(e.target.value)}\n onKeyDown={handleKeyDown}\n onBlur={handleSave}\n // compact inline-edit overrides on top of the Textarea base\n className=\"resize-none rounded-control-inner bg-surface-raised-hover px-ds-02 py-ds-01 text-[13px] leading-relaxed\"\n rows={2}\n />\n <div className=\"mt-ds-01 text-ds-xs text-surface-fg-subtle/50\">\n Enter to save · Escape to cancel\n </div>\n </div>\n )\n }\n\n return (\n <div\n className={cn(\n 'text-[13px] leading-relaxed text-surface-fg whitespace-pre-wrap',\n canEdit && 'cursor-pointer hover:bg-surface-raised-hover rounded-control-inner transition-colors',\n )}\n onClick={handleStartEdit}\n role={canEdit ? 'button' : undefined}\n tabIndex={canEdit ? 0 : undefined}\n onKeyDown={\n canEdit\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleStartEdit()\n }\n }\n : undefined\n }\n >\n {renderContent ? renderContent(content) : content}\n </div>\n )\n}\nMessageEditableBody.displayName = 'Message.EditableBody'\n\n// ── Reactions ────────────────────────────────────────────────────────────\n\nexport interface MessageReactionsProps {\n reactions: { emoji: string; count: number; reacted: boolean }[]\n onReact: (emoji: string) => void\n}\n\nfunction MessageReactions({ reactions, onReact }: MessageReactionsProps) {\n if (reactions.length === 0) return null\n\n return (\n <div className=\"flex flex-wrap gap-ds-02 mt-ds-03\">\n {reactions.map((r) => (\n <button\n key={r.emoji}\n type=\"button\"\n onClick={() => onReact(r.emoji)}\n aria-label={`${r.emoji} ${r.count} reaction${r.count !== 1 ? 's' : ''}${r.reacted ? ', you reacted' : ''}`}\n className={cn(\n 'inline-flex items-center gap-ds-01 rounded-pill px-ds-02 py-ds-01 text-ds-xs transition-colors',\n r.reacted\n ? 'bg-accent-3 ring-1 ring-accent-6'\n : 'bg-surface-raised-hover hover:bg-surface-raised-active',\n )}\n >\n <span>{r.emoji}</span>\n <span>{r.count}</span>\n </button>\n ))}\n </div>\n )\n}\nMessageReactions.displayName = 'Message.Reactions'\n\n// ── Actions (floating toolbar) ───────────────────────────────────────────\n\nexport interface MessageActionsProps {\n children: React.ReactNode\n delay?: number\n}\n\nfunction MessageActions({ children, delay = 100 }: MessageActionsProps) {\n return (\n <div\n className={cn(\n 'absolute -top-2 right-0 z-10',\n 'flex items-center gap-ds-01 rounded-control bg-surface-raised px-ds-01 py-ds-01 shadow-raised',\n 'opacity-0 group-hover/message:opacity-100 group-focus-within/message:opacity-100 transition-opacity duration-150',\n )}\n style={{ transitionDelay: `${delay}ms` }}\n >\n {children}\n </div>\n )\n}\nMessageActions.displayName = 'Message.Actions'\n\n// ── Action (single icon button) ──────────────────────────────────────────\n\nexport interface MessageActionProps {\n icon: IconInput\n label: string\n onClick: () => void\n variant?: 'default' | 'danger'\n}\n\nfunction MessageAction({\n icon,\n label,\n onClick,\n variant = 'default',\n}: MessageActionProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n className={cn(\n 'p-ds-02 rounded-control-inner transition-colors',\n variant === 'default' &&\n 'text-surface-fg-subtle hover:text-surface-fg hover:bg-surface-raised-hover',\n variant === 'danger' &&\n 'text-surface-fg-subtle hover:text-error-11 hover:bg-surface-raised-hover',\n )}\n >\n <IconProvider size=\"xs\">{normalizeIcon(icon)}</IconProvider>\n </button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n )\n}\nMessageAction.displayName = 'Message.Action'\n\n// ── Compound Export ──────────────────────────────────────────────────────\n\nexport const Message = Object.assign(MessageRoot, {\n Avatar: MessageAvatar,\n Content: MessageContent,\n Author: MessageAuthor,\n Body: MessageBody,\n EditableBody: MessageEditableBody,\n Reactions: MessageReactions,\n Actions: MessageActions,\n Action: MessageAction,\n})\n\nexport {\n type MessageContextValue,\n useMessageContext,\n}\n","'use client'\n\nimport { IconSend, IconSquare } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../button'\nimport { Icon } from '../icon'\nimport { cn } from '../lib/utils'\n\nexport interface MessageInputProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSubmit'> {\n onSubmit: (text: string) => void\n placeholder?: string\n disabled?: boolean\n isStreaming?: boolean\n onCancel?: () => void\n leadingSlot?: React.ReactNode\n trailingSlot?: React.ReactNode\n disclaimer?: string\n sendIcon?: React.ReactNode\n}\n\nconst MessageInput = React.forwardRef<HTMLDivElement, MessageInputProps>(\n (\n {\n onSubmit,\n placeholder = 'Type a message...',\n disabled = false,\n isStreaming = false,\n onCancel,\n leadingSlot,\n trailingSlot,\n disclaimer,\n sendIcon,\n className,\n ...props\n },\n ref,\n ) => {\n const [text, setText] = React.useState('')\n const textareaRef = React.useRef<HTMLTextAreaElement>(null)\n\n const resize = React.useCallback(() => {\n const el = textareaRef.current\n if (!el) return\n el.style.height = 'auto'\n el.style.height = `${Math.min(el.scrollHeight, 160)}px`\n }, [])\n\n const handleChange = React.useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value)\n resize()\n },\n [resize],\n )\n\n const handleSend = React.useCallback(() => {\n const trimmed = text.trim()\n if (!trimmed || isStreaming) return\n onSubmit(trimmed)\n setText('')\n // Reset height after clearing\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto'\n }\n }, [text, isStreaming, onSubmit])\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n if (!isStreaming) {\n handleSend()\n }\n }\n },\n [isStreaming, handleSend],\n )\n\n const isEmpty = text.trim().length === 0\n\n return (\n <div\n ref={ref}\n className={cn(\n 'border-t border-surface-border-subtle px-ds-05 py-ds-04',\n className,\n )}\n {...props}\n >\n <div className=\"flex items-end gap-ds-02 rounded-overlay-lg border border-surface-border bg-surface-base p-ds-03\">\n {leadingSlot}\n <textarea\n ref={textareaRef}\n value={text}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder || 'Type a message'}\n disabled={disabled}\n rows={1}\n className=\"flex-1 resize-none bg-transparent px-ds-02 py-ds-01 text-ds-sm text-surface-fg placeholder:text-surface-fg-subtle/50 focus:outline-hidden disabled:opacity-50\"\n style={{ maxHeight: 160 }}\n />\n {isStreaming ? (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n color=\"error\"\n onClick={onCancel}\n aria-label=\"Stop\"\n >\n <Icon icon={IconSquare} size=\"sm\" />\n </Button>\n ) : (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={handleSend}\n disabled={isEmpty || disabled}\n aria-label=\"Send\"\n >\n {sendIcon ?? <Icon icon={IconSend} size=\"sm\" />}\n </Button>\n )}\n {trailingSlot}\n </div>\n {disclaimer && (\n <p className=\"mt-ds-02 text-center text-ds-xs text-surface-fg-subtle/50\">\n {disclaimer}\n </p>\n )}\n </div>\n )\n },\n)\nMessageInput.displayName = 'MessageInput'\n\nexport { MessageInput }\n","'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../lib/motion'\nimport { cn } from '../lib/utils'\nimport { Spinner } from '../spinner'\n\nexport interface MessageListProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n /** Auto-scroll to bottom when new content arrives (default true) */\n autoScroll?: boolean\n /** Number of new messages to show in the floating pill */\n newMessageCount?: number\n /** Called when user clicks the \"N new\" pill */\n onScrollToBottom?: () => void\n /** Called when user scrolls near the top — triggers load-more */\n onLoadMore?: () => void\n /** Whether more messages are currently being loaded */\n loadingMore?: boolean\n /** Content to render when there are no children */\n emptySlot?: React.ReactNode\n /** Custom scroll-to-bottom button (unused slot for future override) */\n scrollToBottomSlot?: React.ReactNode\n /** Content rendered above the scroll container (e.g. channel name) */\n headerSlot?: React.ReactNode\n}\n\nconst MessageList = React.forwardRef<HTMLDivElement, MessageListProps>(\n (\n {\n children,\n autoScroll = true,\n newMessageCount = 0,\n onScrollToBottom,\n onLoadMore,\n loadingMore = false,\n emptySlot,\n scrollToBottomSlot,\n headerSlot,\n className,\n ...props\n },\n ref,\n ) => {\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const isAtBottomRef = React.useRef(true)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const loadingMoreRef = React.useRef(false)\n const prevScrollHeightRef = React.useRef(0)\n\n const isEmpty = React.Children.count(children) === 0 && !loadingMore\n\n // ── Scroll handler ────────────────────────────────────────────\n const handleScroll = React.useCallback(() => {\n const el = scrollRef.current\n if (!el) return\n\n const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40\n isAtBottomRef.current = atBottom\n setIsAtBottom(atBottom)\n\n // Load more when near top\n if (el.scrollTop < 100 && onLoadMore && !loadingMoreRef.current) {\n loadingMoreRef.current = true\n prevScrollHeightRef.current = el.scrollHeight\n onLoadMore()\n }\n }, [onLoadMore])\n\n // ── Auto-scroll to bottom when children change ────────────────\n React.useLayoutEffect(() => {\n const el = scrollRef.current\n if (!el) return\n\n // Scroll preservation after load-more\n if (loadingMoreRef.current) {\n const newScrollHeight = el.scrollHeight\n el.scrollTop = newScrollHeight - prevScrollHeightRef.current\n loadingMoreRef.current = false\n return\n }\n\n // Auto-scroll if user was at bottom\n if (autoScroll && isAtBottomRef.current) {\n el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })\n }\n }, [children, autoScroll])\n\n // ── Reset loading flag when loadingMore goes false ──────────\n React.useEffect(() => {\n if (!loadingMore) {\n loadingMoreRef.current = false\n }\n }, [loadingMore])\n\n // ── \"N new\" pill visibility ───────────────────────────────────\n const showNewPill = newMessageCount > 0 && !isAtBottom\n\n const handleScrollToBottom = React.useCallback(() => {\n const el = scrollRef.current\n if (el) {\n el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })\n }\n onScrollToBottom?.()\n }, [onScrollToBottom])\n\n return (\n <div\n ref={ref}\n className={cn('relative flex flex-1 flex-col overflow-hidden', className)}\n {...props}\n >\n {headerSlot}\n <div\n ref={scrollRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n className=\"flex-1 overflow-y-auto px-ds-05 py-ds-04\"\n style={{\n scrollbarWidth: 'thin',\n scrollbarColor: 'var(--color-surface-border) transparent',\n }}\n onScroll={handleScroll}\n >\n {loadingMore && (\n <div className=\"flex justify-center py-ds-03\">\n <Spinner size=\"sm\" />\n </div>\n )}\n {isEmpty ? (\n emptySlot\n ) : (\n <div className=\"flex flex-col gap-ds-04\">\n <AnimatePresence initial={false}>{children}</AnimatePresence>\n </div>\n )}\n </div>\n\n {/* \"N new\" floating pill */}\n <AnimatePresence>\n {showNewPill && (\n <motion.button\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n onClick={handleScrollToBottom}\n className=\"absolute bottom-ds-04 left-1/2 z-10 -translate-x-1/2 rounded-pill bg-accent-9 px-ds-04 py-ds-02 text-ds-xs font-medium text-accent-fg shadow-raised transition-colors hover:bg-accent-10\"\n >\n {newMessageCount} new\n </motion.button>\n )}\n </AnimatePresence>\n </div>\n )\n },\n)\nMessageList.displayName = 'MessageList'\n\nexport { MessageList }\n","'use client'\n\nimport { IconAlertCircle } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Icon } from '../icon'\nimport { IconProvider } from '../icon-context'\nimport type { IconInput } from '../lib/icon-input'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\n\nexport interface SystemMessageProps extends React.HTMLAttributes<HTMLDivElement> {\n icon?: IconInput\n timestamp?: string\n variant?: 'event' | 'alert'\n children: React.ReactNode\n}\n\nfunction formatTime(ts: string): string {\n return new Date(ts).toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\nconst SystemMessage = React.forwardRef<HTMLDivElement, SystemMessageProps>(\n ({ icon, timestamp, variant = 'event', children, className, ...props }, ref) => {\n if (variant === 'alert') {\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn('flex justify-center', className)}\n {...props}\n >\n <div className=\"flex items-center gap-ds-03 rounded-surface bg-error-3 px-ds-04 py-ds-03\">\n <IconProvider size=\"sm\">{normalizeIcon(icon) ?? <Icon icon={IconAlertCircle} />}</IconProvider>\n <span className=\"text-ds-sm text-error-11\">{children}</span>\n {timestamp && (\n <span className=\"text-ds-sm text-error-11/60\">\n {formatTime(timestamp)}\n </span>\n )}\n </div>\n </div>\n )\n }\n\n // event variant (default)\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-center gap-ds-02 rounded-control-inner bg-surface-raised-hover/30 px-ds-02 py-ds-01 -mx-ds-02 text-ds-sm text-surface-fg-subtle/60',\n className,\n )}\n {...props}\n >\n <IconProvider size=\"xs\">{normalizeIcon(icon)}</IconProvider>\n <span className=\"flex-1\">{children}</span>\n {timestamp && <span>{formatTime(timestamp)}</span>}\n </div>\n )\n },\n)\nSystemMessage.displayName = 'SystemMessage'\n\nexport { SystemMessage }\n","'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface TypingIndicatorProps {\n users: { name: string; image?: string }[]\n className?: string\n}\n\nfunction formatTypingText(users: { name: string }[]): string {\n if (users.length === 1) return `${users[0].name} is typing...`\n if (users.length === 2) return `${users[0].name} and ${users[1].name} are typing...`\n return 'Several people are typing...'\n}\n\nfunction TypingIndicator({ users, className }: TypingIndicatorProps) {\n return (\n <div className={cn('min-h-[24px] px-ds-05', className)} role=\"status\" aria-live=\"polite\">\n <AnimatePresence>\n {users.length > 0 && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"flex items-center gap-ds-02 text-ds-xs text-surface-fg-subtle\"\n >\n <span className=\"flex items-center gap-ds-01\">\n {[0, 1, 2].map((i) => (\n <motion.span\n key={i}\n className=\"h-1 w-1 rounded-pill bg-surface-fg-subtle\"\n animate={{ y: [0, -3, 0] }}\n transition={{\n duration: 0.5,\n repeat: Infinity,\n delay: i * 0.15,\n }}\n />\n ))}\n </span>\n <span>{formatTypingText(users)}</span>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\nTypingIndicator.displayName = 'TypingIndicator'\n\nexport { TypingIndicator }\n","import * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface UnreadSeparatorProps {\n label?: string\n count?: number\n className?: string\n}\n\nfunction UnreadSeparator({\n label = 'NEW',\n count,\n className,\n}: UnreadSeparatorProps) {\n return (\n <div className={cn('relative flex items-center py-ds-02', className)}>\n <div className=\"flex-1 border-t-2 border-accent-7\" />\n <span className=\"px-ds-03 text-ds-xs font-semibold text-accent-11\">\n {count ? `${count} ${label}` : label}\n </span>\n <div className=\"flex-1 border-t-2 border-accent-7\" />\n </div>\n )\n}\nUnreadSeparator.displayName = 'UnreadSeparator'\n\nexport { UnreadSeparator }\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat.js","names":[],"sources":["../../src/ui/chat/date-separator.tsx","../../src/ui/chat/message.tsx","../../src/ui/chat/message-input.tsx","../../src/ui/chat/message-list.tsx","../../src/ui/chat/system-message.tsx","../../src/ui/chat/typing-indicator.tsx","../../src/ui/chat/unread-separator.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface DateSeparatorProps {\n date: Date | string\n format?: (date: Date) => string\n className?: string\n}\n\nfunction defaultFormat(date: Date): string {\n const now = new Date()\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const target = new Date(date.getFullYear(), date.getMonth(), date.getDate())\n const diffMs = today.getTime() - target.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays === 0) return 'Today'\n if (diffDays === 1) return 'Yesterday'\n\n const month = date.toLocaleString('en-US', { month: 'short' })\n const day = date.getDate()\n\n if (date.getFullYear() !== now.getFullYear()) {\n return `${month} ${day}, ${date.getFullYear()}`\n }\n\n return `${month} ${day}`\n}\n\nfunction DateSeparator({ date, format, className }: DateSeparatorProps) {\n const d = typeof date === 'string' ? new Date(date) : date\n const label = format ? format(d) : defaultFormat(d)\n\n return (\n <div className={cn('flex items-center gap-ds-03 py-ds-03', className)}>\n <div className=\"flex-1 border-t border-surface-border-subtle\" />\n <span className=\"text-ds-xs font-medium text-surface-fg-subtle/50 uppercase tracking-wider\">\n {label}\n </span>\n <div className=\"flex-1 border-t border-surface-border-subtle\" />\n </div>\n )\n}\nDateSeparator.displayName = 'DateSeparator'\n\nexport { DateSeparator }\n","'use client'\n\nimport { IconTrash } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Avatar, AvatarFallback,AvatarImage } from '../avatar'\nimport { Icon } from '../icon'\nimport { IconProvider } from '../icon-context'\nimport type { IconInput } from '../lib/icon-input'\nimport { MENTION_TOKEN_CLASS } from '../lib/mention'\nimport { motionProps,springs } from '../lib/motion'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\nimport { Textarea } from '../textarea'\nimport { Tooltip, TooltipContent,TooltipTrigger } from '../tooltip'\n\n// ── Context ──────────────────────────────────────────────────────────────\n\ninterface MessageContextValue {\n variant: 'flat' | 'bubble'\n placement: 'start' | 'end'\n grouped: boolean\n highlight?: 'mention' | 'internal'\n}\n\nconst MessageContext = React.createContext<MessageContextValue>({\n variant: 'flat',\n placement: 'start',\n grouped: false,\n})\n\nfunction useMessageContext() {\n return React.useContext(MessageContext)\n}\n\n// ── Root ─────────────────────────────────────────────────────────────────\n\nexport interface MessageProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n variant?: 'flat' | 'bubble'\n placement?: 'start' | 'end'\n highlight?: 'mention' | 'internal'\n grouped?: boolean\n deleted?: boolean\n deletedText?: string\n}\n\nconst MessageRoot = React.forwardRef<HTMLDivElement, MessageProps>(\n (\n {\n children,\n variant = 'flat',\n placement = 'start',\n highlight,\n grouped = false,\n deleted = false,\n deletedText = 'This message was deleted',\n className,\n ...props\n },\n ref,\n ) => {\n const ctx = React.useMemo<MessageContextValue>(\n () => ({ variant, placement, grouped, highlight }),\n [variant, placement, grouped, highlight],\n )\n\n if (deleted) {\n return (\n <motion.div\n ref={ref}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'flex items-center gap-ds-02 py-ds-02 text-ds-xs text-surface-fg-subtle/50 italic',\n className,\n )}\n {...motionProps(props)}\n >\n <Icon icon={IconTrash} size=\"xs\" />\n {deletedText}\n </motion.div>\n )\n }\n\n if (variant === 'bubble') {\n return (\n <MessageContext.Provider value={ctx}>\n <motion.div\n ref={ref}\n data-highlight={highlight}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'group/message relative flex',\n placement === 'end' ? 'justify-end' : 'justify-start',\n // mention is carried by the styled in-content @token (see MENTION_TOKEN_CLASS), not a row tint/rail\n MENTION_TOKEN_CLASS,\n highlight === 'internal' && 'bg-warning-2/50 rounded-control-inner',\n className,\n )}\n {...motionProps(props)}\n >\n <div\n className={cn(\n 'max-w-[85%] rounded-bubble px-ds-04 py-ds-03',\n placement === 'end'\n ? 'bg-accent-3 text-surface-fg'\n : 'bg-surface-raised text-surface-fg',\n )}\n >\n {children}\n </div>\n </motion.div>\n </MessageContext.Provider>\n )\n }\n\n // flat variant (default)\n return (\n <MessageContext.Provider value={ctx}>\n <motion.div\n ref={ref}\n data-highlight={highlight}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n className={cn(\n 'group/message relative flex gap-ds-04',\n grouped && '-mt-ds-01',\n // mention is carried by the styled in-content @token (see MENTION_TOKEN_CLASS), not a row rail/tint\n MENTION_TOKEN_CLASS,\n highlight === 'internal' && 'bg-warning-2/50 rounded-control-inner',\n className,\n )}\n {...motionProps(props)}\n >\n {children}\n </motion.div>\n </MessageContext.Provider>\n )\n },\n)\nMessageRoot.displayName = 'Message'\n\n// ── Avatar ───────────────────────────────────────────────────────────────\n\nexport interface MessageAvatarProps {\n src?: string | null\n fallback?: string\n icon?: IconInput\n size?: 'sm' | 'md'\n children?: React.ReactNode\n}\n\nfunction MessageAvatar({\n src,\n fallback,\n icon,\n size = 'md',\n children,\n}: MessageAvatarProps) {\n const { grouped } = useMessageContext()\n\n const sizeClass = size === 'sm' ? 'w-5' : 'w-6'\n const heightClass = size === 'sm' ? 'h-5' : 'h-6'\n\n if (grouped) {\n return <div className={cn(sizeClass, heightClass, 'shrink-0')} />\n }\n\n if (children) {\n return (\n <div className={cn(sizeClass, 'shrink-0 flex items-start')}>\n {children}\n </div>\n )\n }\n\n if (icon) {\n return (\n <div\n className={cn(\n sizeClass,\n size === 'sm' ? 'h-5' : 'h-6',\n 'shrink-0 flex items-center justify-center rounded-pill bg-surface-raised-hover',\n )}\n >\n <IconProvider size={size === 'sm' ? 'xs' : 'sm'}>{normalizeIcon(icon)}</IconProvider>\n </div>\n )\n }\n\n return (\n <div className={cn(sizeClass, 'shrink-0')}>\n <Avatar size={size === 'sm' ? 'xs' : 'sm'}>\n {src && <AvatarImage src={src} alt={fallback ?? ''} />}\n <AvatarFallback>{fallback ?? ''}</AvatarFallback>\n </Avatar>\n </div>\n )\n}\nMessageAvatar.displayName = 'Message.Avatar'\n\n// ── Content ──────────────────────────────────────────────────────────────\n\nexport interface MessageContentProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nfunction MessageContent({ children, className, ...props }: MessageContentProps) {\n return (\n <div className={cn('min-w-0 flex-1 flex flex-col gap-ds-02', className)} {...props}>\n {children}\n </div>\n )\n}\nMessageContent.displayName = 'Message.Content'\n\n// ── Author ───────────────────────────────────────────────────────────────\n\nexport interface MessageAuthorProps {\n name: string\n badge?: React.ReactNode\n timestamp?: Date\n formattedTimestamp?: string\n timestampFormat?: (date: Date) => string\n}\n\nfunction defaultTimestampFormat(date: Date): string {\n return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })\n}\n\nfunction MessageAuthor({\n name,\n badge,\n timestamp,\n formattedTimestamp,\n timestampFormat = defaultTimestampFormat,\n}: MessageAuthorProps) {\n const { grouped } = useMessageContext()\n\n if (grouped) return null\n\n const timeStr = formattedTimestamp\n ?? (timestamp ? timestampFormat(timestamp) : undefined)\n\n return (\n <div className=\"flex items-baseline gap-ds-02\">\n <span className=\"font-semibold text-[13px] text-surface-fg\">{name}</span>\n {badge}\n {timeStr && (\n <span className=\"text-[11px] text-surface-fg-subtle/50\">{timeStr}</span>\n )}\n </div>\n )\n}\nMessageAuthor.displayName = 'Message.Author'\n\n// ── Body ─────────────────────────────────────────────────────────────────\n\nexport interface MessageBodyProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nfunction MessageBody({ children, className, ...props }: MessageBodyProps) {\n return (\n <div\n className={cn(\n 'text-[13px] leading-relaxed text-surface-fg whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\nMessageBody.displayName = 'Message.Body'\n\n// ── EditableBody ─────────────────────────────────────────────────────────\n\nexport interface MessageEditableBodyProps {\n content: string\n onSave: (newContent: string) => void\n onCancel?: () => void\n canEdit?: boolean\n renderContent?: (content: string) => React.ReactNode\n}\n\nfunction MessageEditableBody({\n content,\n onSave,\n onCancel,\n canEdit = false,\n renderContent,\n}: MessageEditableBodyProps) {\n const [isEditing, setIsEditing] = React.useState(false)\n const [editDraft, setEditDraft] = React.useState(content)\n const textareaRef = React.useRef<HTMLTextAreaElement>(null)\n\n React.useEffect(() => {\n if (isEditing && textareaRef.current) {\n textareaRef.current.focus()\n textareaRef.current.select()\n }\n }, [isEditing])\n\n const handleStartEdit = React.useCallback(() => {\n if (!canEdit) return\n setEditDraft(content)\n setIsEditing(true)\n }, [canEdit, content])\n\n const handleSave = React.useCallback(() => {\n const trimmed = editDraft.trim()\n if (trimmed && trimmed !== content) {\n onSave(trimmed)\n }\n setIsEditing(false)\n }, [editDraft, content, onSave])\n\n const handleCancel = React.useCallback(() => {\n setIsEditing(false)\n setEditDraft(content)\n onCancel?.()\n }, [content, onCancel])\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n handleSave()\n } else if (e.key === 'Escape') {\n e.preventDefault()\n handleCancel()\n }\n },\n [handleSave, handleCancel],\n )\n\n if (isEditing) {\n return (\n <div className=\"text-[13px] leading-relaxed\">\n <Textarea\n ref={textareaRef}\n value={editDraft}\n onChange={(e) => setEditDraft(e.target.value)}\n onKeyDown={handleKeyDown}\n onBlur={handleSave}\n // compact inline-edit overrides on top of the Textarea base\n className=\"resize-none rounded-control-inner bg-surface-raised-hover px-ds-02 py-ds-01 text-[13px] leading-relaxed\"\n rows={2}\n />\n <div className=\"mt-ds-01 text-ds-xs text-surface-fg-subtle/50\">\n Enter to save · Escape to cancel\n </div>\n </div>\n )\n }\n\n return (\n <div\n className={cn(\n 'text-[13px] leading-relaxed text-surface-fg whitespace-pre-wrap',\n canEdit && 'cursor-pointer hover:bg-surface-raised-hover rounded-control-inner transition-colors',\n )}\n onClick={handleStartEdit}\n role={canEdit ? 'button' : undefined}\n tabIndex={canEdit ? 0 : undefined}\n onKeyDown={\n canEdit\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleStartEdit()\n }\n }\n : undefined\n }\n >\n {renderContent ? renderContent(content) : content}\n </div>\n )\n}\nMessageEditableBody.displayName = 'Message.EditableBody'\n\n// ── Reactions ────────────────────────────────────────────────────────────\n\nexport interface MessageReactionsProps {\n reactions: { emoji: string; count: number; reacted: boolean }[]\n onReact: (emoji: string) => void\n}\n\nfunction MessageReactions({ reactions, onReact }: MessageReactionsProps) {\n if (reactions.length === 0) return null\n\n return (\n <div className=\"flex flex-wrap gap-ds-02 mt-ds-03\">\n {reactions.map((r) => (\n <button\n key={r.emoji}\n type=\"button\"\n onClick={() => onReact(r.emoji)}\n aria-label={`${r.emoji} ${r.count} reaction${r.count !== 1 ? 's' : ''}${r.reacted ? ', you reacted' : ''}`}\n className={cn(\n 'inline-flex items-center gap-ds-01 rounded-pill px-ds-02 py-ds-01 text-ds-xs transition-colors',\n r.reacted\n ? 'bg-accent-3 ring-1 ring-accent-6'\n : 'bg-surface-raised-hover hover:bg-surface-raised-active',\n )}\n >\n <span>{r.emoji}</span>\n <span>{r.count}</span>\n </button>\n ))}\n </div>\n )\n}\nMessageReactions.displayName = 'Message.Reactions'\n\n// ── Actions (floating toolbar) ───────────────────────────────────────────\n\nexport interface MessageActionsProps {\n children: React.ReactNode\n delay?: number\n}\n\nfunction MessageActions({ children, delay = 100 }: MessageActionsProps) {\n return (\n <div\n className={cn(\n 'absolute -top-2 right-0 z-10',\n 'flex items-center gap-ds-01 rounded-control bg-surface-raised px-ds-01 py-ds-01 shadow-raised',\n 'opacity-0 group-hover/message:opacity-100 group-focus-within/message:opacity-100 transition-opacity duration-150',\n )}\n style={{ transitionDelay: `${delay}ms` }}\n >\n {children}\n </div>\n )\n}\nMessageActions.displayName = 'Message.Actions'\n\n// ── Action (single icon button) ──────────────────────────────────────────\n\nexport interface MessageActionProps {\n icon: IconInput\n label: string\n onClick: () => void\n variant?: 'default' | 'danger'\n}\n\nfunction MessageAction({\n icon,\n label,\n onClick,\n variant = 'default',\n}: MessageActionProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={label}\n className={cn(\n 'p-ds-02 rounded-control-inner transition-colors',\n variant === 'default' &&\n 'text-surface-fg-subtle hover:text-surface-fg hover:bg-surface-raised-hover',\n variant === 'danger' &&\n 'text-surface-fg-subtle hover:text-error-11 hover:bg-surface-raised-hover',\n )}\n >\n <IconProvider size=\"xs\">{normalizeIcon(icon)}</IconProvider>\n </button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n )\n}\nMessageAction.displayName = 'Message.Action'\n\n// ── Compound Export ──────────────────────────────────────────────────────\n\nexport const Message = Object.assign(MessageRoot, {\n Avatar: MessageAvatar,\n Content: MessageContent,\n Author: MessageAuthor,\n Body: MessageBody,\n EditableBody: MessageEditableBody,\n Reactions: MessageReactions,\n Actions: MessageActions,\n Action: MessageAction,\n})\n\nexport {\n type MessageContextValue,\n useMessageContext,\n}\n","'use client'\n\nimport { IconSend, IconSquare } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../button'\nimport { Icon } from '../icon'\nimport { cn } from '../lib/utils'\n\nexport interface MessageInputProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSubmit'> {\n onSubmit: (text: string) => void\n placeholder?: string\n disabled?: boolean\n isStreaming?: boolean\n onCancel?: () => void\n leadingSlot?: React.ReactNode\n trailingSlot?: React.ReactNode\n disclaimer?: string\n sendIcon?: React.ReactNode\n}\n\nconst MessageInput = React.forwardRef<HTMLDivElement, MessageInputProps>(\n (\n {\n onSubmit,\n placeholder = 'Type a message...',\n disabled = false,\n isStreaming = false,\n onCancel,\n leadingSlot,\n trailingSlot,\n disclaimer,\n sendIcon,\n className,\n ...props\n },\n ref,\n ) => {\n const [text, setText] = React.useState('')\n const textareaRef = React.useRef<HTMLTextAreaElement>(null)\n\n const resize = React.useCallback(() => {\n const el = textareaRef.current\n if (!el) return\n el.style.height = 'auto'\n el.style.height = `${Math.min(el.scrollHeight, 160)}px`\n }, [])\n\n const handleChange = React.useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setText(e.target.value)\n resize()\n },\n [resize],\n )\n\n const handleSend = React.useCallback(() => {\n const trimmed = text.trim()\n if (!trimmed || isStreaming) return\n onSubmit(trimmed)\n setText('')\n // Reset height after clearing\n if (textareaRef.current) {\n textareaRef.current.style.height = 'auto'\n }\n }, [text, isStreaming, onSubmit])\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n if (!isStreaming) {\n handleSend()\n }\n }\n },\n [isStreaming, handleSend],\n )\n\n const isEmpty = text.trim().length === 0\n\n return (\n <div\n ref={ref}\n className={cn(\n 'border-t border-surface-border-subtle px-ds-05 py-ds-04',\n className,\n )}\n {...props}\n >\n <div className=\"flex items-end gap-ds-02 rounded-overlay-lg border border-surface-border bg-surface-base p-ds-03\">\n {leadingSlot}\n <textarea\n ref={textareaRef}\n value={text}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n aria-label={placeholder || 'Type a message'}\n disabled={disabled}\n rows={1}\n className=\"flex-1 resize-none bg-transparent px-ds-02 py-ds-01 text-ds-sm text-surface-fg placeholder:text-surface-fg-subtle/50 focus:outline-hidden disabled:opacity-50\"\n style={{ maxHeight: 160 }}\n />\n {isStreaming ? (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n color=\"error\"\n onClick={onCancel}\n aria-label=\"Stop\"\n >\n <Icon icon={IconSquare} size=\"sm\" />\n </Button>\n ) : (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={handleSend}\n disabled={isEmpty || disabled}\n aria-label=\"Send\"\n >\n {sendIcon ?? <Icon icon={IconSend} size=\"sm\" />}\n </Button>\n )}\n {trailingSlot}\n </div>\n {disclaimer && (\n <p className=\"mt-ds-02 text-center text-ds-xs text-surface-fg-subtle/50\">\n {disclaimer}\n </p>\n )}\n </div>\n )\n },\n)\nMessageInput.displayName = 'MessageInput'\n\nexport { MessageInput }\n","'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../lib/motion'\nimport { cn } from '../lib/utils'\nimport { Spinner } from '../spinner'\n\nexport interface MessageListProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n /** Auto-scroll to bottom when new content arrives (default true) */\n autoScroll?: boolean\n /** Number of new messages to show in the floating pill */\n newMessageCount?: number\n /** Called when user clicks the \"N new\" pill */\n onScrollToBottom?: () => void\n /** Called when user scrolls near the top — triggers load-more */\n onLoadMore?: () => void\n /** Whether more messages are currently being loaded */\n loadingMore?: boolean\n /** Content to render when there are no children */\n emptySlot?: React.ReactNode\n /** Custom scroll-to-bottom button (unused slot for future override) */\n scrollToBottomSlot?: React.ReactNode\n /** Content rendered above the scroll container (e.g. channel name) */\n headerSlot?: React.ReactNode\n}\n\nconst MessageList = React.forwardRef<HTMLDivElement, MessageListProps>(\n (\n {\n children,\n autoScroll = true,\n newMessageCount = 0,\n onScrollToBottom,\n onLoadMore,\n loadingMore = false,\n emptySlot,\n scrollToBottomSlot,\n headerSlot,\n className,\n ...props\n },\n ref,\n ) => {\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const isAtBottomRef = React.useRef(true)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const loadingMoreRef = React.useRef(false)\n const prevScrollHeightRef = React.useRef(0)\n\n const isEmpty = React.Children.count(children) === 0 && !loadingMore\n\n // ── Scroll handler ────────────────────────────────────────────\n const handleScroll = React.useCallback(() => {\n const el = scrollRef.current\n if (!el) return\n\n const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40\n isAtBottomRef.current = atBottom\n setIsAtBottom(atBottom)\n\n // Load more when near top\n if (el.scrollTop < 100 && onLoadMore && !loadingMoreRef.current) {\n loadingMoreRef.current = true\n prevScrollHeightRef.current = el.scrollHeight\n onLoadMore()\n }\n }, [onLoadMore])\n\n // ── Auto-scroll to bottom when children change ────────────────\n React.useLayoutEffect(() => {\n const el = scrollRef.current\n if (!el) return\n\n // Scroll preservation after load-more\n if (loadingMoreRef.current) {\n const newScrollHeight = el.scrollHeight\n el.scrollTop = newScrollHeight - prevScrollHeightRef.current\n loadingMoreRef.current = false\n return\n }\n\n // Auto-scroll if user was at bottom\n if (autoScroll && isAtBottomRef.current) {\n el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })\n }\n }, [children, autoScroll])\n\n // ── Reset loading flag when loadingMore goes false ──────────\n React.useEffect(() => {\n if (!loadingMore) {\n loadingMoreRef.current = false\n }\n }, [loadingMore])\n\n // ── \"N new\" pill visibility ───────────────────────────────────\n const showNewPill = newMessageCount > 0 && !isAtBottom\n\n const handleScrollToBottom = React.useCallback(() => {\n const el = scrollRef.current\n if (el) {\n el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })\n }\n onScrollToBottom?.()\n }, [onScrollToBottom])\n\n return (\n <div\n ref={ref}\n className={cn('relative flex flex-1 flex-col overflow-hidden', className)}\n {...props}\n >\n {headerSlot}\n <div\n ref={scrollRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n className=\"flex-1 overflow-y-auto px-ds-05 py-ds-04\"\n style={{\n scrollbarWidth: 'thin',\n scrollbarColor: 'var(--color-surface-border) transparent',\n }}\n onScroll={handleScroll}\n >\n {loadingMore && (\n <div className=\"flex justify-center py-ds-03\">\n <Spinner size=\"sm\" />\n </div>\n )}\n {isEmpty ? (\n emptySlot\n ) : (\n <div className=\"flex flex-col gap-ds-04\">\n <AnimatePresence initial={false}>{children}</AnimatePresence>\n </div>\n )}\n </div>\n\n {/* \"N new\" floating pill */}\n <AnimatePresence>\n {showNewPill && (\n <motion.button\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n onClick={handleScrollToBottom}\n className=\"absolute bottom-ds-04 left-1/2 z-10 -translate-x-1/2 rounded-pill bg-accent-9 px-ds-04 py-ds-02 text-ds-xs font-medium text-accent-fg shadow-raised transition-colors hover:bg-accent-10\"\n >\n {newMessageCount} new\n </motion.button>\n )}\n </AnimatePresence>\n </div>\n )\n },\n)\nMessageList.displayName = 'MessageList'\n\nexport { MessageList }\n","'use client'\n\nimport { IconAlertCircle } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Icon } from '../icon'\nimport { IconProvider } from '../icon-context'\nimport type { IconInput } from '../lib/icon-input'\nimport { normalizeIcon } from '../lib/normalize-icon'\nimport { cn } from '../lib/utils'\n\nexport interface SystemMessageProps extends React.HTMLAttributes<HTMLDivElement> {\n icon?: IconInput\n timestamp?: string\n variant?: 'event' | 'alert'\n children: React.ReactNode\n}\n\nfunction formatTime(ts: string): string {\n return new Date(ts).toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\nconst SystemMessage = React.forwardRef<HTMLDivElement, SystemMessageProps>(\n ({ icon, timestamp, variant = 'event', children, className, ...props }, ref) => {\n if (variant === 'alert') {\n return (\n <div\n ref={ref}\n role=\"alert\"\n className={cn('flex justify-center', className)}\n {...props}\n >\n <div className=\"flex items-center gap-ds-03 rounded-surface bg-error-3 px-ds-04 py-ds-03\">\n <IconProvider size=\"sm\">{normalizeIcon(icon) ?? <Icon icon={IconAlertCircle} />}</IconProvider>\n <span className=\"text-ds-sm text-error-11\">{children}</span>\n {timestamp && (\n <span className=\"text-ds-sm text-error-11/60\">\n {formatTime(timestamp)}\n </span>\n )}\n </div>\n </div>\n )\n }\n\n // event variant (default)\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-center gap-ds-02 rounded-control-inner bg-surface-raised-hover/30 px-ds-02 py-ds-01 -mx-ds-02 text-ds-sm text-surface-fg-subtle/60',\n className,\n )}\n {...props}\n >\n <IconProvider size=\"xs\">{normalizeIcon(icon)}</IconProvider>\n <span className=\"flex-1\">{children}</span>\n {timestamp && <span>{formatTime(timestamp)}</span>}\n </div>\n )\n },\n)\nSystemMessage.displayName = 'SystemMessage'\n\nexport { SystemMessage }\n","'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface TypingIndicatorProps {\n users: { name: string; image?: string }[]\n className?: string\n}\n\nfunction formatTypingText(users: { name: string }[]): string {\n if (users.length === 1) return `${users[0].name} is typing...`\n if (users.length === 2) return `${users[0].name} and ${users[1].name} are typing...`\n return 'Several people are typing...'\n}\n\nfunction TypingIndicator({ users, className }: TypingIndicatorProps) {\n return (\n <div className={cn('min-h-[24px] px-ds-05', className)} role=\"status\" aria-live=\"polite\">\n <AnimatePresence>\n {users.length > 0 && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"flex items-center gap-ds-02 text-ds-xs text-surface-fg-subtle\"\n >\n <span className=\"flex items-center gap-ds-01\">\n {[0, 1, 2].map((i) => (\n <motion.span\n key={i}\n className=\"h-1 w-1 rounded-pill bg-surface-fg-subtle\"\n animate={{ y: [0, -3, 0] }}\n transition={{\n duration: 0.5,\n repeat: Infinity,\n delay: i * 0.15,\n }}\n />\n ))}\n </span>\n <span>{formatTypingText(users)}</span>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\nTypingIndicator.displayName = 'TypingIndicator'\n\nexport { TypingIndicator }\n","import * as React from 'react'\n\nimport { cn } from '../lib/utils'\n\nexport interface UnreadSeparatorProps {\n label?: string\n count?: number\n className?: string\n}\n\nfunction UnreadSeparator({\n label = 'NEW',\n count,\n className,\n}: UnreadSeparatorProps) {\n return (\n <div className={cn('relative flex items-center py-ds-02', className)}>\n <div className=\"flex-1 border-t-2 border-accent-7\" />\n <span className=\"px-ds-03 text-ds-xs font-semibold text-accent-11\">\n {count ? `${count} ${label}` : label}\n </span>\n <div className=\"flex-1 border-t-2 border-accent-7\" />\n </div>\n )\n}\nUnreadSeparator.displayName = 'UnreadSeparator'\n\nexport { UnreadSeparator }\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ //#region src/ui/lib/mention.ts
3
+ var e = "[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11";
4
+ //#endregion
5
+ export { e as t };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention.js","names":[],"sources":["../../src/ui/lib/mention.ts"],"sourcesContent":["/**\n * Tailwind classes that style an in-content `@mention` token (`<span class=\"mention\">`).\n *\n * Shared by the rich-text editor, the chat input, AND the read-only Message\n * display so a mention looks identical whether it's being typed or rendered in a\n * timeline. Keep this the single source — three copies drifted before (see #99,\n * where Message never styled the token and `highlight=\"mention\"` went invisible).\n */\nexport const MENTION_TOKEN_CLASS =\n '[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11'\n"],"mappings":";AAQA,IAAa,IACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"video-preview.d.ts","sourceRoot":"","sources":["../../../src/composed/file-preview/video-preview.tsx"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAiNtG"}
1
+ {"version":3,"file":"video-preview.d.ts","sourceRoot":"","sources":["../../../src/composed/file-preview/video-preview.tsx"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAoNtG"}
@@ -168,8 +168,10 @@ var C = [
168
168
  2
169
169
  ];
170
170
  function w({ url: n, onError: i }) {
171
- let a = f.useRef(null), o = f.useRef(null), [s, h] = f.useState(!1), [x, S] = f.useState(0), [w, T] = f.useState(0), [E, D] = f.useState(0), [O, k] = f.useState(!1), [A, j] = f.useState(!1), [M, N] = f.useState(!0), [P, F] = f.useState(1), I = f.useRef(void 0);
172
- f.useEffect(() => () => clearTimeout(I.current), []), f.useEffect(() => {
171
+ let a = f.useRef(null), o = f.useRef(null), [s, h] = f.useState(!1), [x, S] = f.useState(0), [w, T] = f.useState(0), [E, D] = f.useState(0), [O, k] = f.useState(!1), [A, j] = f.useState(!1), [M, N] = f.useState(!0), [P, F] = f.useState(1), I = f.useRef(P);
172
+ I.current = P;
173
+ let L = f.useRef(void 0);
174
+ f.useEffect(() => () => clearTimeout(L.current), []), f.useEffect(() => {
173
175
  function e(e) {
174
176
  let t = o.current;
175
177
  if (!t || !t.contains(document.activeElement) && document.activeElement !== t) return;
@@ -198,27 +200,27 @@ function w({ url: n, onError: i }) {
198
200
  e.preventDefault(), n.requestFullscreen?.();
199
201
  break;
200
202
  case ">":
201
- e.preventDefault(), L(1);
203
+ e.preventDefault(), R(1);
202
204
  break;
203
205
  case "<":
204
- e.preventDefault(), L(-1);
206
+ e.preventDefault(), R(-1);
205
207
  break;
206
208
  }
207
209
  }
208
210
  return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
209
211
  }, []);
210
- function L(e) {
211
- let t = C.indexOf(P), n = C[Math.max(0, Math.min(C.length - 1, t + e))];
212
+ function R(e) {
213
+ let t = C.indexOf(I.current), n = C[Math.max(0, Math.min(C.length - 1, t + e))];
212
214
  F(n), a.current && (a.current.playbackRate = n);
213
215
  }
214
- function R() {
216
+ function z() {
215
217
  a.current && (s ? a.current.pause() : a.current.play(), h(!s));
216
218
  }
217
- function z(e) {
219
+ function B(e) {
218
220
  !a.current || !w || (a.current.currentTime = e / 100 * w);
219
221
  }
220
- function B() {
221
- N(!0), clearTimeout(I.current), s && (I.current = setTimeout(() => N(!1), 3e3));
222
+ function V() {
223
+ N(!0), clearTimeout(L.current), s && (L.current = setTimeout(() => N(!1), 3e3));
222
224
  }
223
225
  return A ? /* @__PURE__ */ p(d, {
224
226
  message: "Could not load video",
@@ -226,7 +228,7 @@ function w({ url: n, onError: i }) {
226
228
  }) : /* @__PURE__ */ m("div", {
227
229
  ref: o,
228
230
  className: "group relative rounded-control bg-black overflow-hidden",
229
- onMouseMove: B,
231
+ onMouseMove: V,
230
232
  onMouseLeave: () => s && N(!1),
231
233
  tabIndex: -1,
232
234
  children: [
@@ -234,7 +236,7 @@ function w({ url: n, onError: i }) {
234
236
  ref: a,
235
237
  src: n,
236
238
  className: "max-h-[70vh] w-full",
237
- onClick: R,
239
+ onClick: z,
238
240
  muted: O,
239
241
  onTimeUpdate: () => {
240
242
  a.current && (D(a.current.currentTime), a.current.duration && S(a.current.currentTime / a.current.duration * 100));
@@ -258,7 +260,7 @@ function w({ url: n, onError: i }) {
258
260
  scale: 1
259
261
  },
260
262
  transition: e.snappy,
261
- onClick: R,
263
+ onClick: z,
262
264
  className: "absolute inset-0 flex items-center justify-center bg-black/30",
263
265
  "aria-label": "Play video",
264
266
  children: /* @__PURE__ */ p("div", {
@@ -290,14 +292,14 @@ function w({ url: n, onError: i }) {
290
292
  value: x,
291
293
  max: 100,
292
294
  step: .1,
293
- onValueChange: z,
295
+ onValueChange: B,
294
296
  tone: "dark",
295
297
  ariaLabel: "Video progress"
296
298
  }), /* @__PURE__ */ m("div", {
297
299
  className: "flex items-center gap-ds-03",
298
300
  children: [
299
301
  /* @__PURE__ */ p("button", {
300
- onClick: R,
302
+ onClick: z,
301
303
  className: "text-white hover:text-white/80",
302
304
  "aria-label": s ? "Pause" : "Play",
303
305
  title: s ? "Pause" : "Play",
@@ -330,7 +332,7 @@ function w({ url: n, onError: i }) {
330
332
  }),
331
333
  /* @__PURE__ */ p("div", { className: "flex-1" }),
332
334
  /* @__PURE__ */ m("button", {
333
- onClick: () => L(1),
335
+ onClick: () => R(1),
334
336
  className: "text-[11px] font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors",
335
337
  "aria-label": `Playback speed: ${P}x`,
336
338
  children: [P, "x"]
@@ -1 +1 @@
1
- {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-ds-xs font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRate as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-[11px] font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-[11px] font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-ds-xs font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n // Kept current every render so the mount-time keydown handler (empty deps)\n // reads the live rate instead of the stale initial 1x. See #91.\n const playbackRateRef = React.useRef(playbackRate)\n playbackRateRef.current = playbackRate\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRateRef.current as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-[11px] font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-[11px] font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAwB,EAAa,EAAE,QAAK,aAAU,cAAgF;CACpI,IAAM,IAAW,EAAM,OAAyB,KAAK,EAC/C,IAAe,EAAM,OAAuB,KAAK,EACjD,CAAC,GAAS,KAAc,EAAM,SAAS,GAAM,EAC7C,CAAC,GAAU,KAAe,EAAM,SAAS,EAAE,EAC3C,CAAC,GAAU,KAAe,EAAM,SAAS,EAAE,EAC3C,CAAC,GAAa,KAAkB,EAAM,SAAS,EAAE,EACjD,CAAC,GAAQ,KAAa,EAAM,SAAS,EAAE,EACvC,CAAC,GAAO,KAAY,EAAM,SAAS,GAAM,EACzC,CAAC,GAAO,KAAY,EAAM,SAAS,GAAM;AAG/C,GAAM,gBAAgB;EACpB,SAAS,EAAc,GAAkB;GACvC,IAAM,IAAK,EAAa;AACxB,OAAI,CAAC,KAAM,CAAC,EAAG,SAAS,SAAS,cAAc,IAAI,SAAS,kBAAkB,EAAI;GAClF,IAAM,IAAI,EAAS;AACd,SAEL,SAAQ,EAAE,KAAV;IACE,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,GAAW,OACL,IAAM,EAAE,OAAO,GAAO,EAAE,MAAM,EAC3B,CAAC,GACR;AACF;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,EAAE,YAAY,GAAG,EAAE,cAAc,EAAE;AAC5D;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,EAAE;AAC9C;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,GAAS,OACP,EAAE,QAAQ,CAAC,GACJ,CAAC,GACR;AACF;;;AAIN,SADA,SAAS,iBAAiB,WAAW,EAAc,QACtC,SAAS,oBAAoB,WAAW,EAAc;IAElE,EAAE,CAAC;CAEN,SAAS,IAAa;AACf,IAAS,YACV,IAAS,EAAS,QAAQ,OAAO,GAChC,EAAS,QAAQ,MAAM,EAC5B,EAAW,CAAC,EAAQ;;CAGtB,SAAS,EAAW,GAAsB;AACpC,GAAC,EAAS,WAAW,CAAC,MAC1B,EAAS,QAAQ,cAAe,IAAe,MAAO;;CAGxD,SAAS,IAAa;AACpB,MAAI,CAAC,EAAS,QAAS;EACvB,IAAM,IAAO,CAAC;AAEd,EADA,EAAS,EAAK,EACd,EAAS,QAAQ,QAAQ;;AAK3B,QAFI,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;EAAO,CAAA,GAG1E,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;EAAkE,UAAU;YAA9G;GAEE,kBAAC,SAAD;IACE,KAAK;IACL,KAAK;IACL,oBAAoB;AACb,OAAS,YACd,EAAe,EAAS,QAAQ,YAAY,EACxC,EAAS,QAAQ,YAAU,EAAa,EAAS,QAAQ,cAAc,EAAS,QAAQ,WAAY,IAAI;;IAE9G,wBAAwB,EAAY,EAAS,SAAS,YAAY,EAAE;IACpE,eAAe,EAAW,GAAM;IAChC,eAAe;AAAkB,KAAhB,EAAS,GAAK,EAAE,IAAU,uBAAuB;;IAClE,CAAA;GAGF,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,OAAO;KACP,KAAK;KACL,MAAM;KACN,eAAe;KACf,MAAK;KACL,WAAU;KACV,CAAA;IACE,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,SAAS;MACT,cAAY,IAAU,UAAU;MAChC,OAAO,IAAU,UAAU;MAC3B,WAAU;gBAET,IACC,kBAAC,GAAD;OAAM,MAAM;OAAiB,MAAK;OAAO,CAAA,GAEzC,kBAAC,GAAD;OAAM,MAAM;OAAgB,MAAK;OAAK,WAAU;OAAW,CAAA;MAEtD,CAAA;KAGT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,KACC,kBAAC,GAAD;OAAe,IAAG;OAAI,MAAK;OAAS,WAAU;iBAC3C;OACa,CAAA,EAElB,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAW,EAAY;QAAC;QAAI,EAAW,EAAS;QAC/C;SACA;;KAGN,kBAAC,GAAD;MACU;MACD;MACP,iBAAiB,MAAM;AAGrB,OAFA,EAAU,EAAE,EACR,EAAS,YAAS,EAAS,QAAQ,SAAS,IAC5C,IAAI,KAAK,MACX,EAAS,GAAM,EACX,EAAS,YAAS,EAAS,QAAQ,QAAQ;;MAGnD,cAAc;MACd,SAAQ;MACR,CAAA;KACE;;GACF;;;;;AChJV,SAAwB,EAAa,EAAE,QAAK,aAAU,cAA+E;CACnI,IAAM,CAAC,GAAQ,KAAa,EAAM,SAAS,GAAM,EAC3C,CAAC,GAAO,KAAY,EAAM,SAAS,GAAM;AAY/C,QATA,EAAM,gBAAgB;EACpB,IAAM,IAAQ,iBAAiB;AAC7B,GAAK,MAAU,EAAS,GAAK,EAAE,IAAU,kBAAkB;KAC1D,KAAM;AACT,eAAa,aAAa,EAAM;IAC/B,CAAC,GAAQ,EAAQ,CAAC,EAEjB,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;EAAO,CAAA,GAG1E,kBAAC,OAAD;EAAK,WAAU;EAAkD,OAAO,EAAE,aAAa,UAAU;YAAjG,CACG,CAAC,KAAU,kBAAC,GAAD,EAAU,WAAU,oBAAqB,CAAA,EACrD,kBAAC,EAAO,QAAR;GACE,KAAK;GACL,OAAM;GACN,cAAc,EAAU,GAAK;GAC7B,eAAe;AAAkB,IAAhB,EAAS,GAAK,EAAE,IAAU,uBAAuB;;GAClE,SAAS,EAAE,SAAS,GAAG;GACvB,SAAS,EAAE,SAAS,MAAgB;GACpC,YAAY,EAAO;GACnB,WAAU;GACV,iBAAA;GACA,CAAA,CACE;;;;;ACvBV,IAAM,IAAiB;CAAC;CAAK;CAAM;CAAG;CAAM;CAAK;CAAE;AAEnD,SAAwB,EAAa,EAAE,QAAK,cAA6D;CACvG,IAAM,IAAW,EAAM,OAAyB,KAAK,EAC/C,IAAe,EAAM,OAAuB,KAAK,EACjD,CAAC,GAAS,KAAc,EAAM,SAAS,GAAM,EAC7C,CAAC,GAAU,KAAe,EAAM,SAAS,EAAE,EAC3C,CAAC,GAAU,KAAe,EAAM,SAAS,EAAE,EAC3C,CAAC,GAAa,KAAkB,EAAM,SAAS,EAAE,EACjD,CAAC,GAAO,KAAY,EAAM,SAAS,GAAM,EACzC,CAAC,GAAO,KAAY,EAAM,SAAS,GAAM,EACzC,CAAC,GAAc,KAAmB,EAAM,SAAS,GAAK,EACtD,CAAC,GAAc,KAAmB,EAAM,SAAS,EAAE,EAGnD,IAAkB,EAAM,OAAO,EAAa;AAClD,GAAgB,UAAU;CAC1B,IAAM,IAAY,EAAM,OAAkD,KAAA,EAAU;AAOpF,CALA,EAAM,sBACS,aAAa,EAAU,QAAQ,EAC3C,EAAE,CAAC,EAGN,EAAM,gBAAgB;EACpB,SAAS,EAAc,GAAkB;GACvC,IAAM,IAAK,EAAa;AACxB,OAAI,CAAC,KAAM,CAAC,EAAG,SAAS,SAAS,cAAc,IAAI,SAAS,kBAAkB,EAAI;GAClF,IAAM,IAAI,EAAS;AACd,SAEL,SAAQ,EAAE,KAAV;IACE,KAAK;IACL,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,GAAW,OACL,IAAM,EAAE,OAAO,GAAO,EAAE,MAAM,EAC3B,CAAC,GACR;AACF;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;AACvD;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,EAAE;AAC9C;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,GAAG;AAC/C;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,cAAc,KAAK,IAAI,EAAE,UAAU,EAAE,cAAc,GAAG;AACxD;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,GAAS,OACP,EAAE,QAAQ,CAAC,GACJ,CAAC,GACR;AACF;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAE,qBAAqB;AACvB;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAkB,EAAE;AACpB;IACF,KAAK;AAEH,KADA,EAAE,gBAAgB,EAClB,EAAkB,GAAG;AACrB;;;AAIN,SADA,SAAS,iBAAiB,WAAW,EAAc,QACtC,SAAS,oBAAoB,WAAW,EAAc;IAClE,EAAE,CAAC;CAEN,SAAS,EAAkB,GAAmB;EAC5C,IAAM,IAAM,EAAe,QAAQ,EAAgB,QAAyC,EAEtF,IAAO,EADG,KAAK,IAAI,GAAG,KAAK,IAAI,EAAe,SAAS,GAAG,IAAM,EAAU,CAAC;AAGjF,EADA,EAAgB,EAAK,EACjB,EAAS,YAAS,EAAS,QAAQ,eAAe;;CAGxD,SAAS,IAAa;AACf,IAAS,YACV,IAAS,EAAS,QAAQ,OAAO,GAChC,EAAS,QAAQ,MAAM,EAC5B,EAAW,CAAC,EAAQ;;CAGtB,SAAS,EAAW,GAAsB;AACpC,GAAC,EAAS,WAAW,CAAC,MAC1B,EAAS,QAAQ,cAAe,IAAe,MAAO;;CAGxD,SAAS,IAAkB;AAGzB,EAFA,EAAgB,GAAK,EACrB,aAAa,EAAU,QAAQ,EAC3B,MACF,EAAU,UAAU,iBAAiB,EAAgB,GAAM,EAAE,IAAK;;AAMtE,QAFI,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;EAAO,CAAA,GAG1E,kBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,aAAa;EACb,oBAAoB,KAAW,EAAgB,GAAM;EACrD,UAAU;YALZ;GAQE,kBAAC,SAAD;IACE,KAAK;IACL,KAAK;IACL,WAAU;IACV,SAAS;IACF;IACP,oBAAoB;AACb,OAAS,YACd,EAAe,EAAS,QAAQ,YAAY,EACxC,EAAS,QAAQ,YAAU,EAAa,EAAS,QAAQ,cAAc,EAAS,QAAQ,WAAY,IAAI;;IAE9G,wBAAwB,EAAY,EAAS,SAAS,YAAY,EAAE;IACpE,eAAe;AAAqB,KAAnB,EAAW,GAAM,EAAE,EAAgB,GAAK;;IACzD,eAAe;AAAkB,KAAhB,EAAS,GAAK,EAAE,IAAU,uBAAuB;;IAClE,aAAA;IACA,CAAA;GAGD,CAAC,KACA,kBAAC,EAAO,QAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAK;IACnC,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG;IACjC,YAAY,EAAQ;IACpB,SAAS;IACT,WAAU;IACV,cAAW;cAEX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAM,MAAM;MAAgB,MAAK;MAAK,WAAU;MAA2B,CAAA;KACvE,CAAA;IACQ,CAAA;GAIlB,kBAAC,GAAD,EAAA,UACG,KAAgB,IAAW,KAC1B,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,GAAG;KAAI;IAC9B,SAAS;KAAE,SAAS;KAAG,GAAG;KAAG;IAC7B,MAAM;KAAE,SAAS;KAAG,GAAG;KAAI;IAC3B,YAAY,EAAO;IACnB,WAAU;cALZ,CAQE,kBAAC,GAAD;KACE,WAAU;KACV,OAAO;KACP,KAAK;KACL,MAAM;KACN,eAAe;KACf,MAAK;KACL,WAAU;KACV,CAAA,EAGF,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OAAQ,SAAS;OAAY,WAAU;OAAiC,cAAY,IAAU,UAAU;OAAQ,OAAO,IAAU,UAAU;iBACxI,IAAU,kBAAC,GAAD;QAAM,MAAM;QAAiB,MAAK;QAAO,CAAA,GAAG,kBAAC,GAAD;QAAM,MAAM;QAAgB,MAAK;QAAO,CAAA;OACxF,CAAA;MACT,kBAAC,GAAD;OACE,QAAQ;OACD;OACP,iBAAiB,MAAM;AAErB,QADI,EAAS,YAAS,EAAS,QAAQ,SAAS,IAC5C,IAAI,KAAK,MAAS,EAAS,GAAM,EAAM,EAAS,YAAS,EAAS,QAAQ,QAAQ;;OAExF,oBAAoB;AAAoB,QAAlB,EAAS,CAAC,EAAM,EAAM,EAAS,YAAS,EAAS,QAAQ,QAAQ,CAAC;;OACxF,SAAQ;OACR,CAAA;MACF,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAW,EAAY;QAAC;QAAI,EAAW,EAAS;QAC5C;;MACP,kBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;MAC1B,kBAAC,UAAD;OACE,eAAe,EAAkB,EAAE;OACnC,WAAU;OACV,cAAY,mBAAmB,EAAa;iBAH9C,CAKG,GAAa,IACP;;MACT,kBAAC,UAAD;OACE,eAAe,EAAS,SAAS,qBAAqB;OACtD,WAAU;OACV,cAAW;OACX,OAAM;iBAEN,kBAAC,GAAD;QAAM,MAAM;QAAc,MAAK;QAAO,CAAA;OAC/B,CAAA;MACL;OACK;OAEC,CAAA;GACd;;;;;ACvNV,IAAM,IAAmB,EAAM,WAAW,OAAO,+BAAgC,EAC3E,IAAsB,EAAM,WAAW,OAAO,kCAAmC;AA+BvF,SAAS,EAAW,GAAa,GAA6B;AAC5D,KAAI,GAAU;AACZ,MAAI,EAAS,WAAW,SAAS,CAAE,QAAO;AAC1C,MAAI,MAAa,kBAAmB,QAAO;AAC3C,MAAI,EAAS,WAAW,SAAS,CAAE,QAAO;AAC1C,MAAI,EAAS,WAAW,SAAS,CAAE,QAAO;;CAG5C,IAAM,IAAM,EAAI,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,aAAa;AAU7D,QATI;EAAC;EAAO;EAAQ;EAAO;EAAO;EAAQ;EAAO;EAAQ;EAAM,CAAC,SAAS,KAAO,GAAG,GAAS,UACxF,MAAQ,QAAc,QACtB;EAAC;EAAO;EAAQ;EAAO;EAAM,CAAC,SAAS,KAAO,GAAG,GAAS,UAC1D;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO,CAAC,SAAS,KAAO,GAAG,GAAS,UAEjE,EAAI,SAAS,YAAY,IAAI,EAAI,SAAS,WAAW,IAAI,EAAI,SAAS,cAAc,IAAI,EAAI,SAAS,WAAW,IAAI,EAAI,SAAS,YAAY,GACxI,UAGF;;AAGT,SAAS,EAAY,GAAqB;CACxC,IAAM,IAAU,EAAI,MAAM,kDAAkD;AAC5E,KAAI,EAAS,QAAO,iCAAiC,EAAQ;CAE7D,IAAM,IAAa,EAAI,MAAM,oBAAoB;AACjD,KAAI,EAAY,QAAO,kCAAkC,EAAW;AAEpE,KAAI,EAAI,SAAS,YAAY,CAAE,QAAO,oDAAoD,mBAAmB,EAAI;CAEjH,IAAM,IAAY,EAAI,MAAM,4BAA4B;AAGxD,QAFI,IAAkB,8BAA8B,EAAU,OAEvD;;AAOT,SAAS,EAAY,EACnB,QACA,MAAM,GACN,aACA,QACA,iBAAc,GACd,aACA,aACA,YACA,cACA,GAAG,KACgB;CACnB,IAAM,IAAO,KAAY,EAAW,GAAK,EAAS,EAC5C,IAAW,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA;AAErE,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,2BAA2B,EAAU;EAAE,GAAI;YAA9D;IAEI,KAAY,MACZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,GAAD;KAAe,MAAK;KAAS,WAAU;eACpC;KACa,CAAA,EAEjB,KAAY,kBAAC,GAAD;KAAO,SAAQ;KAAS,MAAK;eAAM;KAAiB,CAAA,CAC7D;;GAIP,MAAS,WACR,kBAAC,EAAM,UAAP;IAA0B;cACxB,kBAAC,GAAD;KAAuB;KAAU;KAAc;KAAW,CAAA;IAC3C,CAAA;GAElB,MAAS,SACR,kBAAC,EAAM,UAAP;IAA0B;cACxB,kBAAC,GAAD;KAA0B;KAAkB;KAAsB;KAAW,CAAA;IAC9D,CAAA;GAElB,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAc;IAAW,CAAA;GAChE,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAe;IAAmB;IAAW,CAAA;GACpF,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAK,UAAU,EAAY,EAAI;IAAW;IAAW,CAAA;GAG5F,MAAS,WACR,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,WAAW,kBAAC,GAAD,EAAM,MAAM,GAAgB,CAAA;KAAE,SAAA;eACzE,kBAAC,KAAD;MAAG,MAAM;MAAK,UAAA;MAAS,QAAO;MAAS,KAAI;gBAAsB;MAAY,CAAA;KACtE,CAAA;IACL,CAAA;GAEJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"rich-chat-input.d.ts","sourceRoot":"","sources":["../../src/composed/rich-chat-input.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rich-chat-input.d.ts","sourceRoot":"","sources":["../../src/composed/rich-chat-input.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAO9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAInE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAKrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAA;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAIrD,YAAY,EAAE,WAAW,EAAE,CAAA;AAC3B,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,aAAa,EAAE,CAAA;AACxB,YAAY,EAAE,kBAAkB,EAAE,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,CAAA;AACtB,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAChC,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAA;AAQ/D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9E,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7C;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC5G,QAAQ,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAA;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;IACvD,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,YAAY,GAAG,QAAQ,CAAA;IAC/D,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;IACxB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;IAC3D,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;IAC7C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/C,aAAa,CAAC,EAAE,iBAAiB,EAAE,CAAA;IACnC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,mKAAmK;IACnK,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,IAAI,CAAA;KAAE,CAAA;IAChF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wGAAwG;IACxG,OAAO,CAAC,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,KAAK,CAAC,SAAS,CAAA;IACvD,yHAAyH;IACzH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAA;IACtC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,iGAAiG;IACjG,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,IAAI,KAAK,IAAI,CAAA;IACvE,+EAA+E;IAC/E,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAClD,QAAQ,EAAE,MAAM,IAAI,CAAA;KACrB,CAAC,CAAA;CACH;AAqND,QAAA,MAAM,aAAa,2FAsyBlB,CAAA;AAGD,OAAO,EAAE,aAAa,EAAE,CAAA;AACxB,YAAY,EAAE,eAAe,EAAE,CAAA;AAG/B,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,eAAe,EACf,YAAY,EACZ,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,GAChB,MAAM,gCAAgC,CAAA"}