@tribepad/themis 1.9.3 → 1.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/elements/AlertDialog/index.js +1 -1
- package/dist/elements/AlertDialog/index.js.map +1 -1
- package/dist/elements/AlertDialog/index.mjs +1 -1
- package/dist/elements/AlertDialog/index.mjs.map +1 -1
- package/dist/elements/Breadcrumbs/index.js +1 -1
- package/dist/elements/Breadcrumbs/index.js.map +1 -1
- package/dist/elements/Breadcrumbs/index.mjs +1 -1
- package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
- package/dist/elements/Button/index.js +1 -1
- package/dist/elements/Button/index.js.map +1 -1
- package/dist/elements/Button/index.mjs +1 -1
- package/dist/elements/Button/index.mjs.map +1 -1
- package/dist/elements/Card/index.js +1 -1
- package/dist/elements/Card/index.js.map +1 -1
- package/dist/elements/Card/index.mjs +1 -1
- package/dist/elements/Card/index.mjs.map +1 -1
- package/dist/elements/Carousel/Carousel.d.ts +8 -1
- package/dist/elements/Carousel/Carousel.d.ts.map +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts +1 -1
- package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -1
- package/dist/elements/Carousel/index.js +1 -1
- package/dist/elements/Carousel/index.js.map +1 -1
- package/dist/elements/Carousel/index.mjs +1 -1
- package/dist/elements/Carousel/index.mjs.map +1 -1
- package/dist/elements/ChatInterface/ChatLiveRegions.d.ts +2 -2
- package/dist/elements/ChatInterface/ChatLiveRegions.d.ts.map +1 -1
- package/dist/elements/ChatInterface/ChatMessageBubble.d.ts +1 -1
- package/dist/elements/ChatInterface/ChatMessageBubble.d.ts.map +1 -1
- package/dist/elements/ChatInterface/ChatThinkingDisplay.d.ts +2 -2
- package/dist/elements/ChatInterface/ChatThinkingDisplay.d.ts.map +1 -1
- package/dist/elements/ChatInterface/ChatTypingIndicator.d.ts +2 -2
- package/dist/elements/ChatInterface/ChatTypingIndicator.d.ts.map +1 -1
- package/dist/elements/ChatInterface/index.js +1 -1
- package/dist/elements/ChatInterface/index.js.map +1 -1
- package/dist/elements/ChatInterface/index.mjs +1 -1
- package/dist/elements/ChatInterface/index.mjs.map +1 -1
- package/dist/elements/FeatureIcon/FeatureIcon.d.ts +3 -0
- package/dist/elements/FeatureIcon/FeatureIcon.d.ts.map +1 -0
- package/dist/elements/FeatureIcon/FeatureIcon.styles.d.ts +17 -0
- package/dist/elements/FeatureIcon/FeatureIcon.styles.d.ts.map +1 -0
- package/dist/elements/FeatureIcon/FeatureIcon.types.d.ts +137 -0
- package/dist/elements/FeatureIcon/FeatureIcon.types.d.ts.map +1 -0
- package/dist/elements/FeatureIcon/index.d.ts +25 -0
- package/dist/elements/FeatureIcon/index.d.ts.map +1 -0
- package/dist/elements/FeatureIcon/index.js +3 -0
- package/dist/elements/FeatureIcon/index.js.map +1 -0
- package/dist/elements/FeatureIcon/index.mjs +3 -0
- package/dist/elements/FeatureIcon/index.mjs.map +1 -0
- package/dist/elements/FileField/index.js +1 -1
- package/dist/elements/FileField/index.js.map +1 -1
- package/dist/elements/FileField/index.mjs +1 -1
- package/dist/elements/FileField/index.mjs.map +1 -1
- package/dist/elements/Filters/index.js +1 -1
- package/dist/elements/Filters/index.js.map +1 -1
- package/dist/elements/Filters/index.mjs +1 -1
- package/dist/elements/Filters/index.mjs.map +1 -1
- package/dist/elements/Modal/Modal.styles.d.ts +2 -2
- package/dist/elements/Modal/Modal.types.d.ts +2 -2
- package/dist/elements/NumberField/index.js +1 -1
- package/dist/elements/NumberField/index.js.map +1 -1
- package/dist/elements/NumberField/index.mjs +1 -1
- package/dist/elements/NumberField/index.mjs.map +1 -1
- package/dist/elements/PageTitle/index.js +1 -1
- package/dist/elements/PageTitle/index.js.map +1 -1
- package/dist/elements/PageTitle/index.mjs +1 -1
- package/dist/elements/PageTitle/index.mjs.map +1 -1
- package/dist/elements/Pagination/index.js +1 -1
- package/dist/elements/Pagination/index.js.map +1 -1
- package/dist/elements/Pagination/index.mjs +1 -1
- package/dist/elements/Pagination/index.mjs.map +1 -1
- package/dist/elements/Panel/Panel.d.ts +1 -1
- package/dist/elements/Panel/Panel.types.d.ts +2 -2
- package/dist/elements/PasswordField/index.js +1 -1
- package/dist/elements/PasswordField/index.js.map +1 -1
- package/dist/elements/PasswordField/index.mjs +1 -1
- package/dist/elements/PasswordField/index.mjs.map +1 -1
- package/dist/elements/RatingScale/RatingScale.d.ts.map +1 -1
- package/dist/elements/RatingScale/index.js +1 -1
- package/dist/elements/RatingScale/index.js.map +1 -1
- package/dist/elements/RatingScale/index.mjs +1 -1
- package/dist/elements/RatingScale/index.mjs.map +1 -1
- package/dist/elements/Resizable/index.js +1 -1
- package/dist/elements/Resizable/index.js.map +1 -1
- package/dist/elements/Resizable/index.mjs +1 -1
- package/dist/elements/Resizable/index.mjs.map +1 -1
- package/dist/elements/SearchField/index.js +1 -1
- package/dist/elements/SearchField/index.js.map +1 -1
- package/dist/elements/SearchField/index.mjs +1 -1
- package/dist/elements/SearchField/index.mjs.map +1 -1
- package/dist/elements/Select/Select.d.ts +3 -3
- package/dist/elements/Select/Select.d.ts.map +1 -1
- package/dist/elements/Sidebar/Sidebar.styles.d.ts +1 -1
- package/dist/elements/Sidebar/Sidebar.styles.d.ts.map +1 -1
- package/dist/elements/Sidebar/Sidebar.types.d.ts +2 -2
- package/dist/elements/Sidebar/SidebarRail.d.ts.map +1 -1
- package/dist/elements/Sidebar/index.js +1 -1
- package/dist/elements/Sidebar/index.js.map +1 -1
- package/dist/elements/Sidebar/index.mjs +1 -1
- package/dist/elements/Sidebar/index.mjs.map +1 -1
- package/dist/elements/Table/index.js +1 -1
- package/dist/elements/Table/index.js.map +1 -1
- package/dist/elements/Table/index.mjs +1 -1
- package/dist/elements/Table/index.mjs.map +1 -1
- package/dist/elements/TextField/index.js +1 -1
- package/dist/elements/TextField/index.js.map +1 -1
- package/dist/elements/TextField/index.mjs +1 -1
- package/dist/elements/TextField/index.mjs.map +1 -1
- package/dist/elements/Timeline/index.js +1 -1
- package/dist/elements/Timeline/index.js.map +1 -1
- package/dist/elements/Timeline/index.mjs +1 -1
- package/dist/elements/Timeline/index.mjs.map +1 -1
- package/dist/elements/Toast/index.js +1 -1
- package/dist/elements/Toast/index.js.map +1 -1
- package/dist/elements/Toast/index.mjs +1 -1
- package/dist/elements/Toast/index.mjs.map +1 -1
- package/dist/elements/Wizard/index.js +1 -1
- package/dist/elements/Wizard/index.js.map +1 -1
- package/dist/elements/Wizard/index.mjs +1 -1
- package/dist/elements/Wizard/index.mjs.map +1 -1
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/index.d.ts.map +1 -1
- package/dist/elements/index.js +1 -1
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs +1 -1
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/styles/animations.css +15 -0
- package/package.json +16 -14
- package/src/elements/FeatureIcon/FeatureIcon.stories.tsx +164 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/hooks/useReducedMotion.ts","../../../src/elements/Button/Button.styles.ts","../../../src/styles/interaction-states.ts","../../../src/elements/ButtonGroup/ButtonGroupContext.tsx","../../../src/elements/ButtonGroup/ButtonGroup.variants.ts","../../../src/elements/Button/Button.tsx","../../../src/elements/ChatInterface/ChatInterface.styles.ts","../../../src/elements/ChatInterface/ChatLiveRegions.tsx","../../../src/elements/ChatInterface/ChatTypingIndicator.tsx","../../../src/elements/ChatInterface/ChatThinkingDisplay.tsx","../../../src/elements/ChatInterface/ChatMessageBubble.tsx","../../../src/elements/ChatInterface/ChatMessageList.tsx","../../../src/elements/ChatInterface/ChatInterface.hooks.ts","../../../src/elements/ChatInterface/DefaultChatInput.tsx","../../../src/elements/ChatInterface/ChatInputArea.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/ChatInterface/ChatInterface.types.ts","../../../src/elements/ChatInterface/ChatInterface.tsx"],"names":["cn","inputs","twMerge","clsx","QUERY","getInitialState","useReducedMotion","prefersReducedMotion","setPrefersReducedMotion","useState","useEffect","mql","handler","event","buttonOuterVariants","cva","buttonVisualVariants","PRESSED_STYLES","HIGH_CONTRAST_HOVER","HIGH_CONTRAST_PRESSED","ButtonGroupContext","createContext","useButtonGroupContext","useContext","ButtonGroupItemContext","useButtonGroupItemContext","buttonGroupItemVariants","Button","memo","forwardRef","className","buttonVisualClassName","variant","size","visualSize","fullWidth","loading","loadingText","shortcut","children","isDisabled","paywall","paywallRedirect","paywallDescription","onPress","props","ref","paywallDescriptionId","useId","groupContext","itemContext","effectiveVariant","effectiveSize","effectiveIsDisabled","isInVerticalGroup","effectiveFullWidth","positionClassName","effectiveVisualSize","jsx","AriaButton","e","renderProps","jsxs","Fragment","Loader2","Zap","chatRootVariants","chatHeaderVariants","chatLogVariants","chatBubbleRowVariants","chatBubbleVariants","chatAvatarSlotVariants","chatInputAreaVariants","chatInputWrapperVariants","chatInputTextareaVariants","chatTypingIndicatorVariants","chatThinkingPillVariants","chatToolCallRowVariants","chatJumpToLatestVariants","chatStatusRegionVariants","chatErrorActionsVariants","ChatLiveRegions","current","testId","ChatTypingIndicator","ChatThinkingDisplay","parts","mode","disclosureLabel","rows","part","idx","Icon","Check","AlertCircle","statusLabel","shouldSkipRender","prev","next","a","b","i","pa","pb","ChatMessageBubble","message","isStreamingHere","isOffscreen","assistantAvatar","userAvatar","renderAssistantContent","renderUserContent","thinkingDisclosureLabel","contentRef","onRetry","onEscalate","renderSystem","renderUser","renderAssistant","avatar","render","content","args","isError","isThinking","suppressStreamingContent","renderedContent","CACHE_LIMIT","ChatMessageList","messages","streamingMessageId","logLabel","emptyState","registerStreamingContentRef","bottomSentinelRef","onscreenIds","useMemo","ids","m","cacheRef","useRef","lastRendererRef","cachedRenderAssistant","cache","hit","node","oldest","contentRefCacheRef","getContentRef","useCallback","id","cb","STRIP_PATTERN","normaliseAnnouncementText","input","out","truncateAnnouncement","text","maxChars","suffix","requested","clamped","detectFieldSizingSupport","useAutoResizeTextarea","textareaRef","value","maxLines","supportsFieldSizing","pendingRafRef","resize","computed","lineHeight","maxHeight","useLayoutEffect","useTranscriptScroll","scrollContainerRef","sentinelRef","isAtBottom","setIsAtBottom","root","target","obs","entries","last","scrollToBottom","opts","smooth","STILL_WORKING_INTERVALS_MS","useStillWorkingTimer","isStreaming","streamingContentLength","enabled","onAnnounce","lastTokenAtRef","announcementCountRef","lastLengthRef","timerRef","messageRef","onAnnounceRef","clearTimer","arm","targetDelay","elapsed","remaining","sinceToken","idxNow","delayNow","DEFAULT_HOLD_MS","DEFAULT_MAX_QUEUE","useAnnouncementQueue","minIntervalMs","dedupWindowMs","holdMs","maxQueueSize","setCurrent","queueRef","lastFiredAtRef","lastTextRef","lastTextAtRef","flushTimerRef","mountedRef","flush","now","sinceLast","enqueue","DefaultChatInput","placeholder","maxLength","ariaLabel","onSubmit","onChange","onFocus","onBlur","sendShortcut","setValueState","valueRef","isDisabledRef","onSubmitRef","onChangeRef","handleSubmit","v","useImperativeHandle","el","len","markdown","handleKeyDown","isComposing","ChatInputArea","status","consumerDisabled","inputComponent","maxInputLength","inputActions","inputAriaLabel","onSendMessage","onStopGeneration","registerInputHandle","inputIsDisabled","inputHandleRef","setInputHandle","h","handleSubmitFromInput","toSend","handleSendClick","handleStopClick","InputComp","StopCircle","Send","BaseComponentPropsSchema","z","ChatMessageStatusSchema","ChatTopLevelStatusSchema","ThinkingPartSchema","ChatUserMessageSchema","ChatAssistantMessageSchema","ChatSystemMessageSchema","ChatMessageSchema","ChatInterfacePropsSchema","ChatInterface","header","progressAnnouncements","completionAnnouncementMaxChars","hasWarnedLongConversationRef","streamingMessage","found","count","streamingContentRefs","announcement","didMountRef","lastMessagesRef","scrollRafRef","prevInProgressRef","errorFiredThisTickRef","pendingRafIdsRef","nextInProgress","prevMsg","normalised","preview","cssEscape","prevStatusRef","ArrowDown"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\nfunction getInitialState(): boolean {\n if (typeof window === 'undefined') return false;\n return window.matchMedia(QUERY).matches;\n}\n\n/**\n * Detects whether the user prefers reduced motion.\n *\n * - SSR-safe (returns `false` on the server)\n * - Reacts to changes in the OS preference\n * - Cleans up the listener on unmount\n */\nexport function useReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(getInitialState);\n\n useEffect(() => {\n const mql = window.matchMedia(QUERY);\n setPrefersReducedMotion(mql.matches);\n\n const handler = (event: MediaQueryListEvent): void => {\n setPrefersReducedMotion(event.matches);\n };\n\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }, []);\n\n return prefersReducedMotion;\n}\n","import { cva } from 'class-variance-authority';\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * Always transparent, centers the visual button inside\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n *\n * In vertical ButtonGroups, uses items-stretch so the visual layer (Layer 2)\n * can fill the full touch target height, eliminating gaps between buttons.\n */\nexport const buttonOuterVariants = cva(\n \"inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n inVerticalGroup: {\n true: \"items-stretch\",\n false: \"items-center\",\n },\n },\n defaultVariants: {\n fullWidth: false,\n inVerticalGroup: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual button appearance (adjustable size)\n * Provides the visual appearance with configurable size\n * Can be smaller than touch target for use cases like carousel dots\n * NOTE: NO focus-visible styles here - focus ring is on Layer 1\n */\nexport const buttonVisualVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-normal ring-offset-background motion-safe:transition-transform motion-safe:duration-200 motion-safe:[transition-timing-function:cubic-bezier(0.34,1.56,0.64,1)] motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] hover:-translate-y-1\",\n destructive:\n \"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] hover:bg-[var(--destructive-background-hover,var(--destructive-background))] motion-safe:hover:btn-warn-shake\",\n outline:\n \"border border-[var(--input-border)] bg-[var(--page-background)] hover:bg-[var(--input-border)]\",\n secondary:\n \"bg-[var(--secondary)] text-[var(--secondary-foreground)] hover:bg-[var(--secondary-hover,var(--secondary))]\",\n ghost:\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]\",\n link: \"text-[var(--text-link)] underline underline-offset-4 hover:bg-[var(--accent)] hover:text-[var(--text-link-hover)]\",\n },\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n visualSize: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3 text-xs\",\n lg: \"h-11 rounded-md px-6 text-base\",\n icon: \"h-10 w-10\",\n dot: \"h-5 w-5 rounded-full p-0 min-h-0 min-w-0\",\n },\n paywall: {\n true: \"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n visualSize: \"default\",\n paywall: false,\n },\n }\n);\n\n/**\n * @deprecated Use buttonVisualVariants instead. This alias is kept for backward compatibility.\n */\nexport const buttonVariants = buttonVisualVariants;\n","/**\n * Global Interaction State Styles\n *\n * Consistent interaction patterns across all Themis components.\n * These styles ensure WCAG 2.2 AAA compliance and predictable UX.\n *\n * @see spec.md FR-010 (Visible focus ring for keyboard navigation)\n * @see spec.md FR-031 (Pressed state feedback)\n * @see spec.md FR-012 (High contrast mode support)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\n/**\n * Focus state styles (FR-010)\n * Visible focus ring for keyboard navigation - WCAG 2.2 Level AAA\n *\n * Components can override by extending these styles:\n * @example\n * cn(FOCUS_STYLES, \"ring-4\") // Increases ring width to 4px\n */\nexport const FOCUS_STYLES = \"data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--themis-ring)] data-[focus-visible]:ring-offset-2\";\n\n/**\n * Pressed/Active state styles (FR-031)\n * Visual feedback for press interactions\n *\n * Components can override the scale amount:\n * @example\n * cn(PRESSED_STYLES_BASE, \"data-[pressed]:scale-[0.95]\") // More pronounced scale\n */\nexport const PRESSED_STYLES = \"data-[pressed]:scale-[0.97]\";\n\n/**\n * Base pressed styles without scale (for components that need different feedback)\n */\nexport const PRESSED_STYLES_BASE = \"data-[pressed]:transition-transform data-[pressed]:duration-100\";\n\n/**\n * Hover state styles\n * Elevation change on hover for better affordance\n *\n * Components can override shadow depth:\n * @example\n * cn(HOVER_STYLES_BASE, \"data-[hovered]:shadow-lg\") // Larger shadow\n */\nexport const HOVER_STYLES = \"\";\n\n/**\n * Base hover styles without shadow (for components that use different hover effects)\n */\nexport const HOVER_STYLES_BASE = \"data-[hovered]:transition-shadow data-[hovered]:duration-200\";\n\n/**\n * High contrast mode focus (FR-012)\n * Enhanced outlines for users requiring high contrast\n *\n * Uses 'hc:' prefix for prefers-contrast: more media query\n */\nexport const HIGH_CONTRAST_FOCUS = \"hc:data-[focus-visible]:outline hc:data-[focus-visible]:outline-4 hc:data-[focus-visible]:outline-offset-2 hc:data-[focus-visible]:outline-foreground\";\n\n/**\n * High contrast mode hover (FR-012)\n * Enhanced outlines for hover in high contrast mode\n */\nexport const HIGH_CONTRAST_HOVER = \"hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground\";\n\n/**\n * High contrast mode pressed state\n * Enhanced outlines for pressed state in high contrast mode\n */\nexport const HIGH_CONTRAST_PRESSED = \"hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground\";\n\n/**\n * Combined high contrast styles\n * Use this for components that need all high contrast interaction states\n */\nexport const HIGH_CONTRAST_INTERACTIONS = `${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Disabled state styles\n * Consistent disabled appearance across all components\n */\nexport const DISABLED_STYLES = \"disabled:pointer-events-none disabled:opacity-50\";\n\n/**\n * Default interaction bundle\n * Most common combination for interactive components\n *\n * Includes: focus ring, pressed scale, hover shadow, high contrast enhancements\n *\n * @example\n * <button className={cn(DEFAULT_INTERACTIONS, \"bg-primary\")}>Click</button>\n */\nexport const DEFAULT_INTERACTIONS = `${FOCUS_STYLES} ${PRESSED_STYLES} ${HOVER_STYLES} ${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Subtle interaction bundle\n * For components that need less pronounced feedback\n *\n * Includes: focus ring and high contrast, but no hover shadow or pressed scale\n */\nexport const SUBTLE_INTERACTIONS = `${FOCUS_STYLES} ${HIGH_CONTRAST_FOCUS}`;\n\n/**\n * Non-interactive element styles\n * For elements that should indicate they are not interactive\n */\nexport const NON_INTERACTIVE = \"cursor-default select-none\";\n","\"use client\";\n\nimport { createContext, useContext } from 'react';\nimport type {\n ButtonGroupContextValue,\n ButtonGroupItemContextValue,\n} from './ButtonGroup.types';\n\n/**\n * ButtonGroup Context System (Two-Level)\n *\n * Provides a two-level context pattern for ButtonGroup:\n *\n * 1. ButtonGroupContext (group-level):\n * - Provides: orientation, variant, size, isDisabled\n * - Consumed by: Button (for prop inheritance), Separator (for orientation)\n *\n * 2. ButtonGroupItemContext (item-level):\n * - Provides: position ('first' | 'middle' | 'last' | 'only')\n * - Consumed by: Button (for border-radius styling)\n *\n * Both contexts return null when not in a provider, allowing Button\n * to work standalone without any group context.\n *\n * @see plan.md for architecture details\n * @see ButtonGroup.tsx for Provider implementation\n */\n\n// =============================================================================\n// Group-Level Context\n// =============================================================================\n\n/**\n * Context for group-level props (orientation, variant, size, isDisabled)\n * Default value is null to indicate \"not in a group\"\n */\nconst ButtonGroupContext = createContext<ButtonGroupContextValue | null>(null);\n\nButtonGroupContext.displayName = 'ButtonGroupContext';\n\n/**\n * Hook to access group-level context\n * @returns ButtonGroupContextValue if inside a ButtonGroup, null otherwise\n */\nexport function useButtonGroupContext(): ButtonGroupContextValue | null {\n return useContext(ButtonGroupContext);\n}\n\n// =============================================================================\n// Item-Level Context\n// =============================================================================\n\n/**\n * Context for per-button position information\n * Default value is null to indicate \"not wrapped with position context\"\n */\nconst ButtonGroupItemContext =\n createContext<ButtonGroupItemContextValue | null>(null);\n\nButtonGroupItemContext.displayName = 'ButtonGroupItemContext';\n\n/**\n * Hook to access item-level context (position)\n * @returns ButtonGroupItemContextValue if wrapped with position context, null otherwise\n */\nexport function useButtonGroupItemContext(): ButtonGroupItemContextValue | null {\n return useContext(ButtonGroupItemContext);\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ButtonGroupContext, ButtonGroupItemContext };\n","import { cva } from 'class-variance-authority';\n\n/**\n * ButtonGroup CVA Variants\n *\n * Defines Class Variance Authority (CVA) variants for:\n * - ButtonGroup container (orientation-based layout)\n * - ButtonGroupItem (position-based border-radius)\n * - ButtonGroupSeparator (orientation-based styling)\n *\n * @see plan.md Phase 1: Design & Contracts - CVA Variants\n * @see constitution.md Principle V (Component Quality Standards)\n */\n\n// =============================================================================\n// Container Variants\n// =============================================================================\n\n/**\n * ButtonGroup container variants\n * Controls the layout direction based on orientation\n * Uses gap-0 to ensure buttons are connected (share borders)\n */\nexport const buttonGroupVariants = cva('inline-flex items-center gap-0', {\n variants: {\n orientation: {\n horizontal: 'flex-row',\n vertical: 'flex-col w-full',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n\n// =============================================================================\n// Item Position Variants\n// =============================================================================\n\n/**\n * ButtonGroupItem position variants\n * Applied to Button's visual layer (Layer 2) for position-aware border-radius\n *\n * Compound variants handle both orientation and position combinations:\n * - Horizontal: left/right borders and radii\n * - Vertical: top/bottom borders and radii\n */\nexport const buttonGroupItemVariants = cva('', {\n variants: {\n orientation: {\n // min-w-[44px] ensures visual layer fills touch target width (for icon buttons)\n horizontal: 'min-w-[44px]',\n // flex (overrides inline-flex) + min-h-[44px] makes visual layer fill touch target,\n // eliminating gaps between stacked buttons in vertical orientation\n vertical: 'flex min-h-[44px]',\n },\n position: {\n first: '',\n middle: '',\n last: '',\n only: '', // Single button - no modifications needed\n },\n },\n compoundVariants: [\n // ==========================================================================\n // Horizontal Orientation\n // ==========================================================================\n {\n orientation: 'horizontal',\n position: 'first',\n className: 'rounded-r-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'middle',\n className: 'rounded-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'last',\n className: 'rounded-l-none',\n },\n // ==========================================================================\n // Vertical Orientation\n // Note: w-full is handled by Button's effectiveFullWidth for both layers\n // ==========================================================================\n {\n orientation: 'vertical',\n position: 'first',\n className: 'rounded-b-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'middle',\n className: 'rounded-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'last',\n className: 'rounded-t-none',\n },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n position: 'only',\n },\n});\n\n// =============================================================================\n// Separator Variants\n// =============================================================================\n\n/**\n * ButtonGroupSeparator variants\n * Orientation-aware visual divider between button groups\n */\nexport const buttonGroupSeparatorVariants = cva('bg-[var(--border)]', {\n variants: {\n orientation: {\n horizontal: 'w-px h-6 mx-1',\n vertical: 'h-px w-full my-1',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n","\"use client\";\n\n/**\n * Button Component - 3-Layer Architecture\n * Accessible button with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant, transparent)\n * - Layer 2: Visual Button (configurable size, colors, borders)\n * - Layer 3: Content & Effects (text, icons, ripple, loading spinner)\n *\n * @see 3layer-plan.md for architecture details\n * @see spec.md FR-029 to FR-037 (Button Component Requirements)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 7:1 contrast ratio)\n * @see spec.md FR-014 (44x44px minimum touch targets)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useId } from 'react';\nimport {\n Button as AriaButton,\n type ButtonProps as AriaButtonProps,\n} from 'react-aria-components';\nimport { Loader2, Zap } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { ButtonProps } from './Button.types';\nimport { buttonOuterVariants, buttonVisualVariants, buttonVariants } from './Button.styles';\nimport { PRESSED_STYLES, HOVER_STYLES, HIGH_CONTRAST_HOVER, HIGH_CONTRAST_PRESSED } from '../../styles/interaction-states';\nimport {\n useButtonGroupContext,\n useButtonGroupItemContext,\n} from '../ButtonGroup/ButtonGroupContext';\nimport { buttonGroupItemVariants } from '../ButtonGroup/ButtonGroup.variants';\n\n/**\n * Button Component - 3-Layer Architecture\n * Fully accessible button with React Aria and themed styling\n *\n * Layer 1: Touch Target (AriaButton) - 44x44px WCAG AAA compliant\n * Layer 2: Visual Button (span) - configurable appearance\n * Layer 3: Content (children) - text, icons, effects\n */\nconst Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n buttonVisualClassName,\n variant,\n size,\n visualSize,\n fullWidth,\n loading = false,\n loadingText = \"Loading...\",\n shortcut,\n children,\n isDisabled,\n paywall = false,\n paywallRedirect,\n paywallDescription,\n onPress,\n ...props\n },\n ref\n ) => {\n const paywallDescriptionId = useId();\n\n // ==========================================================================\n // ButtonGroup Context Integration\n // ==========================================================================\n\n // Consume group-level context (variant, size, isDisabled, orientation)\n const groupContext = useButtonGroupContext();\n\n // Consume item-level context (position for border-radius styling)\n const itemContext = useButtonGroupItemContext();\n\n // Merge context values with props (props take precedence)\n const effectiveVariant = variant ?? groupContext?.variant ?? 'default';\n const effectiveSize = size ?? groupContext?.size;\n const effectiveIsDisabled = isDisabled ?? groupContext?.isDisabled ?? false;\n\n // In vertical groups, buttons should be full width automatically\n const isInVerticalGroup = groupContext?.orientation === 'vertical';\n const effectiveFullWidth = fullWidth || isInVerticalGroup;\n\n // Position styling for ButtonGroup (only applied when in a group)\n const positionClassName = itemContext\n ? buttonGroupItemVariants({\n orientation: groupContext?.orientation ?? 'horizontal',\n position: itemContext.position,\n })\n : '';\n\n // Default visualSize to size for backward compatibility\n const effectiveVisualSize = visualSize ?? effectiveSize ?? 'default';\n\n // AAA Accessibility: Warn in dev/test if icon/dot variant lacks accessible name\n if (process.env.NODE_ENV !== 'production') {\n if (\n (effectiveVisualSize === 'dot' || effectiveVisualSize === 'icon') &&\n !props['aria-label'] &&\n !children\n ) {\n console.warn(\n '[Button] visualSize=\"dot\" or \"icon\" requires aria-label when no visible text is provided (WCAG 1.1.1)'\n );\n }\n }\n\n /**\n * Handle button press - intercepts action when paywalled\n * If paywalled with redirect URL, opens in new tab\n * Otherwise, calls the normal onPress handler\n */\n const handlePress = (e: Parameters<NonNullable<AriaButtonProps['onPress']>>[0]): void => {\n if (paywall) {\n if (paywallRedirect) {\n window.open(paywallRedirect, '_blank', 'noopener,noreferrer');\n }\n // Don't call onPress when paywalled\n return;\n }\n onPress?.(e);\n };\n\n // Only set isDisabled when we have a reason to disable\n // Otherwise, let slot system control disabled state (e.g., in NumberField)\n const computedIsDisabled = effectiveIsDisabled || loading || undefined;\n\n return (\n <AriaButton\n ref={ref}\n isDisabled={computedIsDisabled}\n aria-disabled={paywall ? true : undefined}\n aria-describedby={paywall ? paywallDescriptionId : undefined}\n onPress={handlePress}\n className={cn(buttonOuterVariants({ fullWidth: effectiveFullWidth, inVerticalGroup: isInVerticalGroup }), className)}\n {...props}\n >\n {(renderProps) => (\n /* Layer 2: Visual Button */\n <span\n className={cn(\n buttonVisualVariants({\n variant: effectiveVariant,\n visualSize: effectiveVisualSize,\n paywall,\n fullWidth: effectiveFullWidth,\n }),\n // Position styling from ButtonGroup context (border-radius adjustments)\n positionClassName,\n buttonVisualClassName,\n // Layer 2 interaction styles (no focus - focus ring is on Layer 1)\n PRESSED_STYLES,\n HOVER_STYLES,\n HIGH_CONTRAST_HOVER,\n HIGH_CONTRAST_PRESSED\n )}\n data-pressed={renderProps.isPressed || undefined}\n >\n {/* Layer 3: Content & Effects */}\n\n {/* FR-033: Loading spinner with screen reader announcement */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {loading && (\n <>\n <Loader2 className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />\n <span className=\"sr-only\" aria-live=\"polite\">\n {loadingText}\n </span>\n </>\n )}\n\n {/* Hide children during loading */}\n {!loading && children}\n\n {/* Paywall: Lightning bolt icon */}\n {paywall && (\n <Zap\n data-testid=\"zap-icon\"\n aria-hidden=\"true\"\n className=\"ml-1\"\n />\n )}\n\n {/* Paywall: Screen reader description */}\n {paywall && (\n <span id={paywallDescriptionId} className=\"sr-only\">\n Premium feature: {paywallDescription || \"Upgrade required to access this feature\"}\n </span>\n )}\n\n {/* FR-034: Keyboard shortcut display on focus */}\n {renderProps.isFocusVisible && shortcut && (\n <kbd className=\"ml-auto hidden text-xs opacity-60 lg:inline\">\n {shortcut}\n </kbd>\n )}\n\n </span>\n )}\n </AriaButton>\n );\n }\n));\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonOuterVariants, buttonVisualVariants };\nexport type { ButtonProps } from './Button.types';\n","import { cva } from 'class-variance-authority';\n\n/**\n * Root container — fills the parent.\n */\nexport const chatRootVariants = cva(\n 'flex flex-col h-full w-full bg-[var(--content-background)] text-[var(--content-foreground)]'\n);\n\nexport const chatHeaderVariants = cva(\n 'flex-shrink-0 border-b border-[var(--border)] px-4 py-2'\n);\n\n/**\n * Log region — visual + navigable. NO `aria-live`. Scroll container.\n * `overflow-anchor: none` prevents browser scroll-anchoring fighting auto-scroll while the streaming bubble grows.\n */\nexport const chatLogVariants = cva(\n 'flex-1 min-h-0 overflow-y-auto overflow-x-hidden flex flex-col gap-3 px-4 py-3 outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 [--tw-ring-offset-color:var(--content-background)] [overflow-anchor:none]'\n);\n\n/**\n * Message bubble — wrapper for a single message row.\n * `content-visibility: auto` skips layout for offscreen bubbles. Skip on streaming bubble.\n */\nexport const chatBubbleRowVariants = cva('flex w-full gap-2', {\n variants: {\n align: {\n user: 'justify-end',\n assistant: 'justify-start',\n system: 'justify-center',\n },\n offscreen: {\n true: '[content-visibility:auto] [contain-intrinsic-size:auto_80px]',\n false: '',\n },\n },\n defaultVariants: { align: 'assistant', offscreen: true },\n});\n\n/**\n * The visible bubble itself.\n */\nexport const chatBubbleVariants = cva(\n 'rounded-[var(--radius-md)] px-3 py-2 max-w-[85%] break-words',\n {\n variants: {\n role: {\n user: 'bg-[var(--primary-action)] text-[var(--primary-action-foreground)]',\n assistant:\n 'bg-[var(--muted)] text-[var(--content-foreground)] border border-[var(--border)]',\n system: 'bg-transparent text-[var(--muted-foreground)] text-sm italic',\n },\n isError: {\n true: 'border border-[var(--destructive-background)] bg-[var(--destructive-background)]/10',\n false: '',\n },\n },\n defaultVariants: { role: 'assistant', isError: false },\n }\n);\n\n/**\n * Avatar slot beside a bubble.\n */\nexport const chatAvatarSlotVariants = cva(\n 'flex-shrink-0 self-end',\n {\n variants: {\n role: {\n user: 'order-2',\n assistant: 'order-1',\n system: 'hidden',\n },\n },\n defaultVariants: { role: 'assistant' },\n }\n);\n\n/**\n * Input area at the bottom of the chat.\n */\nexport const chatInputAreaVariants = cva(\n 'flex-shrink-0 flex items-end gap-2 border-t border-[var(--border)] bg-[var(--content-background)] p-3'\n);\n\n/**\n * The default input wrapper (Layer 1 of the input).\n *\n * `min-h-[44px]` matches the Button outer touch-target height so an empty single-line input\n * visually aligns with the Send/Stop button. With `field-sizing: content` on the textarea,\n * the wrapper grows naturally as the user types multi-line; `items-end` on the parent keeps\n * the bottoms aligned (the wrapper's top rises while the button stays anchored).\n */\nexport const chatInputWrapperVariants = cva(\n 'flex-1 min-w-0 min-h-[44px] flex items-center rounded-[var(--radius-lg)] border border-[var(--input-border)] bg-[var(--input-background)] focus-within:border-[var(--ring)] focus-within:ring-2 focus-within:ring-[var(--ring)] focus-within:ring-offset-1 [--tw-ring-offset-color:var(--content-background)] motion-safe:transition-colors',\n {\n variants: {\n isDisabled: {\n true: 'opacity-60 cursor-not-allowed',\n false: '',\n },\n },\n defaultVariants: { isDisabled: false },\n }\n);\n\n/**\n * The textarea inside the wrapper. JS path for browsers that don't support `field-sizing: content`.\n */\nexport const chatInputTextareaVariants = cva(\n 'block w-full resize-none border-0 bg-transparent px-3 py-2 text-sm text-[var(--content-foreground)] placeholder:text-[var(--muted-foreground)] outline-none focus:outline-none disabled:cursor-not-allowed [field-sizing:content]'\n);\n\n/**\n * Typing indicator (three dots) shown during active token streaming.\n */\nexport const chatTypingIndicatorVariants = cva(\n 'inline-flex items-center gap-1 px-2 py-1',\n {\n variants: {\n animate: {\n true: '[&>span]:motion-safe:animate-pulse',\n false: '',\n },\n },\n defaultVariants: { animate: true },\n }\n);\n\n/**\n * Thinking pill shown while assistant message has status === 'thinking'.\n */\nexport const chatThinkingPillVariants = cva(\n 'inline-flex items-center gap-1 rounded-full bg-[var(--muted)] px-2 py-0.5 text-xs text-[var(--muted-foreground)]',\n {\n variants: {\n animate: {\n true: 'motion-safe:animate-pulse',\n false: '',\n },\n },\n defaultVariants: { animate: true },\n }\n);\n\n/**\n * Tool-call row inside the thinking display.\n */\nexport const chatToolCallRowVariants = cva(\n 'flex items-center gap-2 rounded px-2 py-1 text-xs',\n {\n variants: {\n status: {\n running: 'bg-[var(--muted)] text-[var(--muted-foreground)]',\n done: 'bg-[var(--success-background)]/20 text-[var(--content-foreground)]',\n error: 'bg-[var(--destructive-background)]/20 text-[var(--content-foreground)]',\n },\n },\n defaultVariants: { status: 'running' },\n }\n);\n\n/**\n * Jump-to-latest floating button. 44x44 layer-1 + visual layer.\n */\nexport const chatJumpToLatestVariants = cva(\n 'absolute bottom-20 right-4 z-10 motion-safe:transition-transform motion-safe:duration-150',\n {\n variants: {\n visible: {\n true: 'translate-y-0 opacity-100',\n false: 'translate-y-2 opacity-0 pointer-events-none',\n },\n },\n defaultVariants: { visible: false },\n }\n);\n\n/**\n * sr-only utility for the status region. Stays out of visual flow but accessible.\n */\nexport const chatStatusRegionVariants = cva(\n 'sr-only'\n);\n\n/**\n * Action row below an errored assistant bubble (Try again / Submit a support request).\n */\nexport const chatErrorActionsVariants = cva(\n 'mt-2 flex flex-wrap items-center gap-2'\n);\n","'use client';\n\nimport { memo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { chatStatusRegionVariants } from './ChatInterface.styles';\n\n/**\n * Single sr-only `role=\"status\"` region — the canonical AT announcement channel.\n * The log region (separate component) has NO `aria-live`; all announcements flow through here.\n *\n * @see ChatInterface.hooks.ts → useAnnouncementQueue for the FIFO + dedup + rate-limit logic.\n */\nconst ChatLiveRegions = memo(function ChatLiveRegions({\n current,\n className,\n testId,\n}: {\n current: string;\n className?: string;\n testId?: string;\n}) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(chatStatusRegionVariants(), className)}\n data-testid={testId}\n >\n {current}\n </div>\n );\n});\n\nexport { ChatLiveRegions };\n","'use client';\n\nimport { memo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { chatTypingIndicatorVariants } from './ChatInterface.styles';\n\n/**\n * Three-dot typing indicator. Visible only during active token streaming\n * (NOT during thinking — that uses the static \"Thinking…\" pill).\n *\n * Animation gated by `useReducedMotion`. Compositor-thread (opacity).\n */\nconst ChatTypingIndicator = memo(function ChatTypingIndicator({\n className,\n}: {\n className?: string;\n}) {\n const prefersReducedMotion = useReducedMotion();\n return (\n <span\n className={cn(chatTypingIndicatorVariants({ animate: !prefersReducedMotion }), className)}\n aria-hidden=\"true\"\n >\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" />\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" style={{ animationDelay: '0.15s' }} />\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" style={{ animationDelay: '0.3s' }} />\n </span>\n );\n});\n\nexport { ChatTypingIndicator };\n","'use client';\n\nimport { memo } from 'react';\nimport { Loader2, Check, AlertCircle } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport {\n chatThinkingPillVariants,\n chatToolCallRowVariants,\n} from './ChatInterface.styles';\nimport type { ThinkingPart } from './ChatInterface.types';\n\n/**\n * Renders thinking parts visually.\n *\n * Two render modes:\n * - `mode=\"active\"` (status === 'thinking'): rows visible at the top of the bubble, \"Thinking…\" pill shown.\n * - `mode=\"collapsed\"` (status === 'streaming' | 'complete'): wrapped in a `<details>` disclosure\n * labelled by `disclosureLabel`. Closed by default.\n *\n * Tool transitions (running → done → error) are SILENT to AT — no `aria-live` here. The\n * in-bubble \"Still working\" timer in the parent covers slow-response signalling.\n */\nconst ChatThinkingDisplay = memo(function ChatThinkingDisplay({\n parts,\n mode,\n disclosureLabel,\n className,\n}: {\n parts: ThinkingPart[];\n mode: 'active' | 'collapsed';\n disclosureLabel: string;\n className?: string;\n}) {\n const prefersReducedMotion = useReducedMotion();\n if (parts.length === 0) return null;\n\n const rows = (\n <div className=\"flex flex-col gap-1\">\n {parts.map((part, idx) => {\n if (part.kind === 'text') {\n return (\n <p key={idx} className=\"text-xs text-[var(--muted-foreground)]\">\n {part.text}\n </p>\n );\n }\n // Tool call: announce status via text label, not just colour/icon.\n const Icon =\n part.status === 'running'\n ? Loader2\n : part.status === 'done'\n ? Check\n : AlertCircle;\n const statusLabel =\n part.status === 'running'\n ? 'running'\n : part.status === 'done'\n ? 'completed'\n : 'failed';\n return (\n <div key={idx} className={chatToolCallRowVariants({ status: part.status })}>\n <Icon\n className={cn(\n 'h-3 w-3',\n part.status === 'running' && !prefersReducedMotion && 'motion-safe:animate-spin'\n )}\n aria-hidden=\"true\"\n />\n {/* Visually-hidden prefix gives AT users the kind of element they're hearing without\n cluttering the visible row. Visible children remain in document order for both. */}\n <span className=\"sr-only\">{part.name} tool, </span>\n <span className=\"font-medium\">{part.name}</span>\n <span aria-hidden=\"true\" className=\"text-[var(--muted-foreground)]\">·</span>\n <span>{statusLabel}</span>\n {part.summary ? (\n <span className=\"ml-1 text-[var(--muted-foreground)]\">— {part.summary}</span>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n\n if (mode === 'active') {\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <span className={chatThinkingPillVariants({ animate: !prefersReducedMotion })}>\n Thinking…\n </span>\n {rows}\n </div>\n );\n }\n\n // Collapsed disclosure — native <details>/<summary> is keyboard-accessible without ARIA.\n // summary gets min-h 44 to satisfy WCAG 2.5.5 target size.\n return (\n <details className={cn('mt-2 text-xs', className)}>\n <summary className=\"cursor-pointer inline-flex items-center min-h-[44px] py-1 text-[var(--muted-foreground)] hover:text-[var(--content-foreground)]\">\n {disclosureLabel}\n </summary>\n <div className=\"mt-2\">{rows}</div>\n </details>\n );\n});\n\nexport { ChatThinkingDisplay };\n","'use client';\n\nimport { memo, type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { Button } from '../Button/Button';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\nimport { ChatThinkingDisplay } from './ChatThinkingDisplay';\nimport {\n chatBubbleRowVariants,\n chatBubbleVariants,\n chatAvatarSlotVariants,\n chatErrorActionsVariants,\n} from './ChatInterface.styles';\nimport type {\n ChatAssistantMessage,\n ChatAssistantRenderer,\n ChatMessage,\n ChatSystemMessage,\n ChatUserMessage,\n ChatUserRenderer,\n} from './ChatInterface.types';\n\n/**\n * Single message bubble. Memoized with a custom comparator covering all visual-relevant fields.\n *\n * IMMUTABILITY: consumers MUST pass a new message object when content/parts/status/errorMessage/createdAt changes.\n * In-place mutation will not re-render. (Documented on the public `messages` prop.)\n */\n\ninterface BubbleProps {\n message: ChatMessage;\n isStreamingHere: boolean;\n isOffscreen: boolean;\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n thinkingDisclosureLabel: string;\n contentRef?: React.RefCallback<HTMLDivElement>;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n}\n\nfunction shouldSkipRender(prev: BubbleProps, next: BubbleProps): boolean {\n if (prev.isStreamingHere !== next.isStreamingHere) return false;\n if (prev.isOffscreen !== next.isOffscreen) return false;\n if (prev.thinkingDisclosureLabel !== next.thinkingDisclosureLabel) return false;\n if (prev.assistantAvatar !== next.assistantAvatar) return false;\n if (prev.userAvatar !== next.userAvatar) return false;\n if (prev.renderAssistantContent !== next.renderAssistantContent) return false;\n if (prev.renderUserContent !== next.renderUserContent) return false;\n if (prev.onRetry !== next.onRetry) return false;\n if (prev.onEscalate !== next.onEscalate) return false;\n if (prev.contentRef !== next.contentRef) return false;\n\n const a = prev.message;\n const b = next.message;\n if (a === b) return true;\n if (a.id !== b.id) return false;\n if (a.role !== b.role) return false;\n if (a.status !== b.status) return false;\n if (a.createdAt !== b.createdAt) return false;\n // role-specific\n if (a.role === 'assistant' && b.role === 'assistant') {\n if (a.content !== b.content) return false;\n if (a.errorMessage !== b.errorMessage) return false;\n // Shallow-equal parts: same length AND same kind/status/text/name/summary fields.\n if ((a.parts?.length ?? 0) !== (b.parts?.length ?? 0)) return false;\n if (a.parts && b.parts) {\n for (let i = 0; i < a.parts.length; i++) {\n const pa = a.parts[i];\n const pb = b.parts[i];\n if (!pa || !pb) return false;\n if (pa.kind !== pb.kind) return false;\n if (pa.kind === 'text' && pb.kind === 'text') {\n if (pa.text !== pb.text) return false;\n } else if (pa.kind === 'tool_call' && pb.kind === 'tool_call') {\n if (pa.name !== pb.name) return false;\n if (pa.status !== pb.status) return false;\n if (pa.summary !== pb.summary) return false;\n }\n }\n }\n return true;\n }\n if (a.role === 'user' && b.role === 'user') {\n return a.content === b.content;\n }\n if (a.role === 'system' && b.role === 'system') {\n return a.content === b.content && a.announce === b.announce;\n }\n return false;\n}\n\nconst ChatMessageBubble = memo(function ChatMessageBubble(props: BubbleProps): ReactNode {\n // Reduced-motion is read inside the memo'd component. Hooks always run on render, and\n // useReducedMotion returns a stable value backed by a media-query subscription — so the\n // bubble re-renders if the OS preference flips, even though prefersReducedMotion isn't a prop.\n const prefersReducedMotion = useReducedMotion();\n const {\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n userAvatar,\n renderAssistantContent,\n renderUserContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n } = props;\n\n if (message.role === 'system') {\n return renderSystem(message);\n }\n if (message.role === 'user') {\n return renderUser(message, userAvatar, renderUserContent, isOffscreen);\n }\n return renderAssistant({\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n renderAssistantContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n prefersReducedMotion,\n });\n}, shouldSkipRender);\n\nChatMessageBubble.displayName = 'ChatMessageBubble';\n\nfunction renderSystem(message: ChatSystemMessage): ReactNode {\n return (\n <div className={chatBubbleRowVariants({ align: 'system', offscreen: false })}>\n <div className={chatBubbleVariants({ role: 'system' })} data-message-id={message.id}>\n {message.content}\n </div>\n </div>\n );\n}\n\nfunction renderUser(\n message: ChatUserMessage,\n avatar: ReactNode | undefined,\n render: ChatUserRenderer | undefined,\n isOffscreen: boolean\n): ReactNode {\n const content = render ? (\n render(message)\n ) : (\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n );\n return (\n <div className={chatBubbleRowVariants({ align: 'user', offscreen: isOffscreen })}>\n {avatar ? <div className={chatAvatarSlotVariants({ role: 'user' })}>{avatar}</div> : null}\n <div\n className={cn(\n chatBubbleVariants({ role: 'user', isError: message.status === 'error' }),\n 'order-1'\n )}\n data-message-id={message.id}\n >\n {content}\n </div>\n </div>\n );\n}\n\nfunction renderAssistant(args: {\n message: ChatAssistantMessage;\n isStreamingHere: boolean;\n isOffscreen: boolean;\n assistantAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n thinkingDisclosureLabel: string;\n contentRef?: React.RefCallback<HTMLDivElement>;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n prefersReducedMotion: boolean;\n}): ReactNode {\n const {\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n renderAssistantContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n prefersReducedMotion,\n } = args;\n\n const isError = message.status === 'error';\n const isThinking = message.status === 'thinking';\n // Under reduced-motion, partial streaming content (which shifts layout token-by-token) is\n // suppressed. The static \"Generating response…\" placeholder + non-animated typing dots signal\n // activity without the layout churn. Final content snaps in when status flips to 'complete'.\n // Thinking is unaffected — it already renders static parts + a (gated) pill.\n const suppressStreamingContent =\n isStreamingHere && !isThinking && prefersReducedMotion && message.status === 'streaming';\n\n // Plain-text fallback: render markdown source verbatim.\n const renderedContent = renderAssistantContent ? (\n renderAssistantContent(message)\n ) : (\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n );\n\n // Skip content-visibility on the streaming bubble (its height changes constantly).\n const skipOffscreen = isStreamingHere || isThinking;\n\n return (\n <div className={chatBubbleRowVariants({ align: 'assistant', offscreen: isOffscreen && !skipOffscreen })}>\n {assistantAvatar ? (\n <div className={chatAvatarSlotVariants({ role: 'assistant' })}>{assistantAvatar}</div>\n ) : null}\n <div\n className={cn(chatBubbleVariants({ role: 'assistant', isError }), 'flex flex-col gap-1')}\n data-message-id={message.id}\n data-status={message.status}\n >\n {/* Thinking parts — active mode while thinking, collapsed disclosure once response begins */}\n {message.parts && message.parts.length > 0 && (\n <ChatThinkingDisplay\n parts={message.parts}\n mode={isThinking ? 'active' : 'collapsed'}\n disclosureLabel={thinkingDisclosureLabel}\n />\n )}\n\n {/* Main content. Hidden mid-stream under reduced-motion; the contentRef is still attached\n so the completion announcement can read innerText once the message snaps to complete. */}\n {!isThinking && !suppressStreamingContent && (\n <div ref={contentRef} data-content>\n {renderedContent}\n </div>\n )}\n\n {/* Reduced-motion placeholder while content is suppressed */}\n {suppressStreamingContent && (\n <div className=\"text-sm text-[var(--muted-foreground)]\" data-streaming-placeholder>\n Generating response…\n </div>\n )}\n\n {/* Typing dots ONLY during active token streaming (not during thinking) */}\n {isStreamingHere && !isThinking && <ChatTypingIndicator />}\n\n {/* Error: show errorMessage + retry/escalate actions */}\n {isError && (\n <>\n {message.errorMessage ? (\n <p className=\"text-xs text-[var(--content-foreground)]\">{message.errorMessage}</p>\n ) : null}\n <div className={chatErrorActionsVariants()}>\n {onRetry ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onPress={() => onRetry(message.id)}\n data-action=\"retry\"\n >\n Try again\n </Button>\n ) : null}\n {onEscalate ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onPress={onEscalate}\n data-action=\"escalate\"\n >\n Submit a support request\n </Button>\n ) : null}\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\nexport { ChatMessageBubble };\n","'use client';\n\nimport { forwardRef, useCallback, useMemo, useRef, type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\nimport { chatLogVariants } from './ChatInterface.styles';\nimport { ChatMessageBubble } from './ChatMessageBubble';\nimport type {\n ChatAssistantRenderer,\n ChatMessage,\n ChatUserRenderer,\n} from './ChatInterface.types';\n\n\n/**\n * The transcript region. `role=\"log\"` + `aria-label` + `tabIndex={0}` for keyboard scroll.\n * NO `aria-live` — all AT announcements flow through ChatLiveRegions.\n *\n * Maintains a markdown-render cache keyed on `(message.id, content)` to skip re-running\n * `renderAssistantContent` for completed messages whose content hasn't changed.\n * Bounded LRU (max 200 entries) using Map insertion order: delete + set on touch moves to tail.\n */\ninterface ChatMessageListProps {\n messages: ChatMessage[];\n streamingMessageId: string | null;\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n thinkingDisclosureLabel: string;\n logLabel: string;\n /** Rendered inside the log when messages is empty — keeps the landmark stable across empty→populated. */\n emptyState?: ReactNode;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n /** Receives the latest assistant content node when its message id flips to streaming/complete. */\n registerStreamingContentRef?: (id: string, node: HTMLDivElement | null) => void;\n bottomSentinelRef: React.RefObject<HTMLDivElement | null>;\n className?: string;\n testId?: string;\n}\n\nconst CACHE_LIMIT = 200;\n\nconst ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(\n function ChatMessageList(\n {\n messages,\n streamingMessageId,\n assistantAvatar,\n userAvatar,\n renderAssistantContent,\n renderUserContent,\n thinkingDisclosureLabel,\n logLabel,\n emptyState,\n onRetry,\n onEscalate,\n registerStreamingContentRef,\n bottomSentinelRef,\n className,\n testId,\n },\n ref\n ) {\n // Mark roughly the last few messages as \"onscreen-most-likely\" — they skip content-visibility.\n // Crude heuristic: only the last 5 messages are likely visible at any moment.\n const onscreenIds = useMemo(() => {\n const ids = new Set<string>();\n for (let i = Math.max(0, messages.length - 5); i < messages.length; i++) {\n const m = messages[i];\n if (m) ids.add(m.id);\n }\n return ids;\n }, [messages]);\n\n // LRU cache for assistant content nodes (used by the assistant render-prop path).\n // Lives on a ref; pruned on every render that looks up a new key.\n const cacheRef = useRef<Map<string, { content: string; node: ReactNode }>>(new Map());\n\n // The wrapper passes a memoised renderer that consults the cache. If the upstream\n // renderer reference changes we invalidate the cache wholesale.\n const lastRendererRef = useRef<ChatAssistantRenderer | undefined>(undefined);\n if (lastRendererRef.current !== renderAssistantContent) {\n cacheRef.current.clear();\n lastRendererRef.current = renderAssistantContent;\n }\n\n const cachedRenderAssistant: ChatAssistantRenderer | undefined = useMemo(() => {\n if (!renderAssistantContent) return undefined;\n return (message): ReactNode => {\n const cache = cacheRef.current;\n const hit = cache.get(message.id);\n if (hit && hit.content === message.content) {\n // Move to tail (LRU touch).\n cache.delete(message.id);\n cache.set(message.id, hit);\n return hit.node;\n }\n const node = renderAssistantContent(message);\n cache.set(message.id, { content: message.content, node });\n // Evict oldest if over cap.\n if (cache.size > CACHE_LIMIT) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n return node;\n };\n }, [renderAssistantContent]);\n\n // Stable per-id ref callbacks. Without this we'd allocate a fresh arrow function for every\n // assistant bubble on every parent render, busting the bubble's memo via prop identity change.\n // The Map is keyed by id; entries are GC'd alongside `streamingContentRefs` in the parent\n // when messages are removed (the ref callback fires with `null` on unmount, deleting itself).\n const contentRefCacheRef = useRef(new Map<string, React.RefCallback<HTMLDivElement>>());\n const getContentRef = useCallback(\n (id: string): React.RefCallback<HTMLDivElement> | undefined => {\n if (!registerStreamingContentRef) return undefined;\n const cache = contentRefCacheRef.current;\n let cb = cache.get(id);\n if (!cb) {\n cb = (node: HTMLDivElement | null): void => {\n registerStreamingContentRef(id, node);\n if (node === null) cache.delete(id);\n };\n cache.set(id, cb);\n }\n return cb;\n },\n [registerStreamingContentRef]\n );\n\n return (\n <div\n ref={ref}\n role=\"log\"\n aria-label={logLabel}\n // log region must be keyboard-scrollable per WCAG 2.1.1; arrow keys then scroll inside.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n className={cn(chatLogVariants(), className)}\n data-testid={testId}\n >\n {messages.length === 0 && emptyState ? (\n <div className=\"flex h-full items-center justify-center\">{emptyState}</div>\n ) : null}\n {messages.map((message) => {\n const isStreamingHere = streamingMessageId === message.id;\n const isOffscreen = !onscreenIds.has(message.id);\n const contentRef =\n message.role === 'assistant' ? getContentRef(message.id) : undefined;\n return (\n <ChatMessageBubble\n key={message.id}\n message={message}\n isStreamingHere={isStreamingHere}\n isOffscreen={isOffscreen}\n assistantAvatar={assistantAvatar}\n userAvatar={userAvatar}\n renderAssistantContent={cachedRenderAssistant}\n renderUserContent={renderUserContent}\n thinkingDisclosureLabel={thinkingDisclosureLabel}\n contentRef={contentRef}\n onRetry={onRetry}\n onEscalate={onEscalate}\n />\n );\n })}\n {/* Bottom sentinel for IntersectionObserver-based stuck-to-bottom detection */}\n <div ref={bottomSentinelRef} aria-hidden=\"true\" style={{ height: 1, flexShrink: 0 }} />\n </div>\n );\n }\n);\n\nexport { ChatMessageList };\n","'use client';\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\n\n/**\n * Strip Unicode that breaks AT announcements:\n * - C0 controls (U+0000–U+001F), DEL (U+007F), C1 controls (U+0080–U+009F)\n * - Zero-width (U+200B–U+200F)\n * - Bidi overrides (U+202A–U+202E)\n * - Isolates (U+2066–U+2069)\n * - BOM/ZWNBSP (U+FEFF)\n *\n * Then NFC-normalises and collapses whitespace.\n *\n * Built via RegExp constructor with Unicode escapes to avoid no-irregular-whitespace lint.\n */\nconst STRIP_PATTERN = new RegExp(\n '[' +\n '\\\\u0000-\\\\u001F' + // C0 controls\n '\\\\u007F' + // DEL\n '\\\\u0080-\\\\u009F' + // C1 controls\n '\\\\u200B-\\\\u200F' + // Zero-width\n '\\\\u202A-\\\\u202E' + // Bidi overrides\n '\\\\u2066-\\\\u2069' + // Isolates\n '\\\\uFEFF' + // BOM/ZWNBSP\n ']',\n 'g'\n);\n\nexport function normaliseAnnouncementText(input: string): string {\n if (!input) return '';\n let out = input.replace(STRIP_PATTERN, '');\n // NFC normalise so combining marks/composites are stable.\n if (typeof out.normalize === 'function') {\n out = out.normalize('NFC');\n }\n // Collapse runs of whitespace (including newlines from innerText) to single spaces.\n out = out.replace(/\\s+/g, ' ').trim();\n return out;\n}\n\n/**\n * Truncate to `maxChars`, suffixing with the supplied `suffix` if truncated.\n * `maxChars` is clamped to [50, 1000] before truncation.\n */\nexport function truncateAnnouncement(\n text: string,\n maxChars: number | undefined,\n suffix = '…full response in transcript'\n): string {\n const requested = typeof maxChars === 'number' ? maxChars : 200;\n const clamped = Math.min(Math.max(requested, 50), 1000);\n if (process.env.NODE_ENV !== 'production' && requested !== clamped) {\n console.warn(\n `[ChatInterface] completionAnnouncementMaxChars must be in [50, 1000]; got ${requested}, clamped to ${clamped}.`\n );\n }\n if (text.length <= clamped) return text;\n return `${text.slice(0, clamped).trimEnd()}${suffix ? ` ${suffix}` : ''}`;\n}\n\n/**\n * Detect whether `field-sizing: content` is supported (Chrome 123+/Safari 18.4+/Firefox 137+).\n * Computed once via `useState` initialiser. SSR-safe (returns false server-side).\n */\nfunction detectFieldSizingSupport(): boolean {\n if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function') return false;\n try {\n return CSS.supports('field-sizing', 'content');\n } catch {\n return false;\n }\n}\n\n/**\n * Auto-resize a textarea based on its scrollHeight, capped at `--chat-input-max-lines × line-height`.\n * On browsers supporting `field-sizing: content`, the JS path is a no-op (CSS handles it).\n * On older browsers, the JS measurement is rAF-batched to collapse N keystrokes per frame into one reflow.\n */\nexport function useAutoResizeTextarea(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n value: string,\n maxLines = 6\n): void {\n const [supportsFieldSizing] = useState(detectFieldSizingSupport);\n const pendingRafRef = useRef<number | null>(null);\n\n const resize = useCallback(() => {\n if (supportsFieldSizing) return;\n const el = textareaRef.current;\n if (!el) return;\n if (pendingRafRef.current !== null) return;\n pendingRafRef.current = requestAnimationFrame(() => {\n pendingRafRef.current = null;\n const node = textareaRef.current;\n if (!node) return;\n const computed = window.getComputedStyle(node);\n const lineHeight = parseFloat(computed.lineHeight);\n const maxHeight = Number.isFinite(lineHeight) ? lineHeight * maxLines : 0;\n node.style.height = 'auto';\n const next = maxHeight > 0 ? Math.min(node.scrollHeight, maxHeight) : node.scrollHeight;\n node.style.height = `${next}px`;\n node.style.overflowY = node.scrollHeight > next ? 'auto' : 'hidden';\n });\n }, [textareaRef, supportsFieldSizing, maxLines]);\n\n // Re-measure when value changes.\n useLayoutEffect(() => {\n resize();\n }, [value, resize]);\n\n // Cleanup any pending rAF on unmount.\n useEffect(() => {\n return (): void => {\n if (pendingRafRef.current !== null) {\n cancelAnimationFrame(pendingRafRef.current);\n pendingRafRef.current = null;\n }\n };\n }, []);\n}\n\n/**\n * Track whether the user is \"stuck to bottom\" using an IntersectionObserver on a sentinel element\n * at the bottom of the log. No scroll listener — browser-native.\n *\n * `rootMargin` is wide (256px) to tolerate scrollHeight churn from `content-visibility: auto`\n * placeholders being measured as bubbles scroll into view.\n *\n * Returns:\n * - `isAtBottom` — true if the bottom sentinel is intersecting the root.\n * - `scrollToBottom(opts?)` — imperative scroll (respects reduced-motion).\n */\nexport function useTranscriptScroll(\n scrollContainerRef: React.RefObject<HTMLDivElement | null>,\n sentinelRef: React.RefObject<HTMLDivElement | null>,\n prefersReducedMotion: boolean\n): {\n isAtBottom: boolean;\n scrollToBottom: (opts?: { smooth?: boolean }) => void;\n} {\n const [isAtBottom, setIsAtBottom] = useState(true);\n\n useEffect(() => {\n const root = scrollContainerRef.current;\n const target = sentinelRef.current;\n if (!root || !target) return;\n if (typeof IntersectionObserver === 'undefined') return;\n\n const obs = new IntersectionObserver(\n (entries) => {\n const last = entries[entries.length - 1];\n if (last) setIsAtBottom(last.isIntersecting);\n },\n { root, rootMargin: '0px 0px 256px 0px', threshold: 0 }\n );\n obs.observe(target);\n return (): void => obs.disconnect();\n }, [scrollContainerRef, sentinelRef]);\n\n const scrollToBottom = useCallback(\n (opts?: { smooth?: boolean }) => {\n const target = sentinelRef.current;\n if (!target) return;\n const smooth = opts?.smooth && !prefersReducedMotion;\n target.scrollIntoView({ behavior: smooth ? 'smooth' : 'auto', block: 'end' });\n },\n [sentinelRef, prefersReducedMotion]\n );\n\n return { isAtBottom, scrollToBottom };\n}\n\n/**\n * Bounded \"still working\" announcement timer.\n *\n * Starts when `isStreaming` becomes true. Pings at 3s, 15s, 60s elapsed silence.\n * - Gated on `document.visibilityState === 'visible'`.\n * - Capped at 3 announcements per stream.\n * - Resets on `streamingContentLength` increase (token arrival is the signal of progress).\n * - Suppressed when `enabled` is false.\n *\n * Implementation: a single long-lived timer. Tokens update `lastTokenAtRef` via render-time read;\n * when the timer fires, it checks elapsed silence — if not enough, it re-arms with the remaining\n * delta. This avoids clear+rearm thrash at high token rates (60Hz × 2 syscalls/tick = 120 timer\n * ops/sec under naive implementations).\n */\nconst STILL_WORKING_INTERVALS_MS = [3000, 15000, 60000];\n\nexport function useStillWorkingTimer(args: {\n isStreaming: boolean;\n streamingContentLength: number;\n enabled: boolean;\n message: string;\n onAnnounce: (text: string) => void;\n}): void {\n const { isStreaming, streamingContentLength, enabled, message, onAnnounce } = args;\n const lastTokenAtRef = useRef<number>(0);\n const announcementCountRef = useRef<number>(0);\n const lastLengthRef = useRef<number>(streamingContentLength);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const messageRef = useRef(message);\n const onAnnounceRef = useRef(onAnnounce);\n\n // Update token timestamp on length increase (cheap render-time bookkeeping).\n if (streamingContentLength > lastLengthRef.current) {\n lastLengthRef.current = streamingContentLength;\n lastTokenAtRef.current = Date.now();\n }\n\n const clearTimer = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n // Keep latest message/onAnnounce in refs so the long-lived timer always uses current values.\n useEffect(() => {\n messageRef.current = message;\n onAnnounceRef.current = onAnnounce;\n });\n\n // Single timer effect: starts on stream start, self-rearms until cap, tears down on stop.\n useEffect(() => {\n if (!isStreaming || !enabled) {\n clearTimer();\n announcementCountRef.current = 0;\n lastLengthRef.current = 0;\n lastTokenAtRef.current = Date.now();\n return clearTimer;\n }\n\n lastTokenAtRef.current = Date.now();\n\n const arm = (): void => {\n const idx = announcementCountRef.current;\n if (idx >= STILL_WORKING_INTERVALS_MS.length) return;\n const targetDelay = STILL_WORKING_INTERVALS_MS[idx];\n if (targetDelay === undefined) return;\n const elapsed = Date.now() - lastTokenAtRef.current;\n const remaining = Math.max(targetDelay - elapsed, 0);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n const sinceToken = Date.now() - lastTokenAtRef.current;\n const idxNow = announcementCountRef.current;\n const delayNow = STILL_WORKING_INTERVALS_MS[idxNow];\n if (delayNow === undefined) return;\n if (sinceToken < delayNow) {\n // Token arrived during the wait — reset with the remaining delta.\n arm();\n return;\n }\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {\n // Skip this firing but advance the count so we don't loop.\n announcementCountRef.current = idxNow + 1;\n arm();\n return;\n }\n announcementCountRef.current = idxNow + 1;\n onAnnounceRef.current(messageRef.current);\n arm();\n }, remaining);\n };\n\n arm();\n return clearTimer;\n // We only care about start/stop transitions — token-driven rearming is handled inside `arm`\n // via the `lastTokenAtRef` read, NOT by re-running this effect.\n }, [isStreaming, enabled, clearTimer]);\n}\n\n/**\n * Status region announcement queue.\n *\n * Provides:\n * - `current` — the text currently in the live region.\n * - `enqueue(text)` — push a ping. Identical text within `dedupWindowMs` is dropped.\n *\n * Each enqueued text holds for `holdMs` (default 200ms — older AT like NVDA/Orca poll live regions\n * every ~80–150ms; sub-150ms holds risk skipped announcements). Sequential distinct pings are\n * spaced at least `minIntervalMs` apart. Queue is capped at `maxQueueSize` (default 32) to prevent\n * unbounded memory growth on misbehaving callers.\n */\nconst DEFAULT_HOLD_MS = 200;\nconst DEFAULT_MAX_QUEUE = 32;\n\nexport function useAnnouncementQueue(opts?: {\n minIntervalMs?: number;\n dedupWindowMs?: number;\n holdMs?: number;\n maxQueueSize?: number;\n}): { current: string; enqueue: (text: string) => void } {\n const minIntervalMs = opts?.minIntervalMs ?? 500;\n const dedupWindowMs = opts?.dedupWindowMs ?? 1000;\n const holdMs = opts?.holdMs ?? DEFAULT_HOLD_MS;\n const maxQueueSize = opts?.maxQueueSize ?? DEFAULT_MAX_QUEUE;\n const [current, setCurrent] = useState('');\n const queueRef = useRef<string[]>([]);\n const lastFiredAtRef = useRef<number>(0);\n const lastTextRef = useRef<string>('');\n const lastTextAtRef = useRef<number>(0);\n const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Guards async timer callbacks from running after unmount.\n const mountedRef = useRef(true);\n\n const flush = useCallback(() => {\n if (flushTimerRef.current !== null) return;\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n const next = queueRef.current.shift();\n if (next === undefined) {\n setCurrent('');\n return;\n }\n const now = Date.now();\n const sinceLast = now - lastFiredAtRef.current;\n if (sinceLast < minIntervalMs) {\n // Re-queue at the front and try again after the gap.\n queueRef.current.unshift(next);\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n flush();\n }, minIntervalMs - sinceLast);\n return;\n }\n lastFiredAtRef.current = now;\n lastTextRef.current = next;\n lastTextAtRef.current = now;\n setCurrent(next);\n // Clear after the hold so AT polling has time to catch the change.\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n setCurrent('');\n if (queueRef.current.length > 0) flush();\n }, holdMs);\n }, 0);\n }, [minIntervalMs, holdMs]);\n\n const enqueue = useCallback(\n (text: string) => {\n if (!text) return;\n const now = Date.now();\n // Dedup: drop if identical to the last fired text within the window.\n if (text === lastTextRef.current && now - lastTextAtRef.current < dedupWindowMs) {\n return;\n }\n // Cap: drop oldest queued ping if the queue is full. The current pending fire is unaffected.\n if (queueRef.current.length >= maxQueueSize) {\n queueRef.current.shift();\n }\n queueRef.current.push(text);\n flush();\n },\n [dedupWindowMs, flush, maxQueueSize]\n );\n\n // Cleanup\n useEffect(() => {\n return (): void => {\n mountedRef.current = false;\n if (flushTimerRef.current !== null) {\n clearTimeout(flushTimerRef.current);\n flushTimerRef.current = null;\n }\n };\n }, []);\n\n return { current, enqueue };\n}\n","'use client';\n\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n} from 'react';\nimport { cn } from '../../utils/cn';\nimport { useAutoResizeTextarea } from './ChatInterface.hooks';\nimport {\n chatInputTextareaVariants,\n chatInputWrapperVariants,\n} from './ChatInterface.styles';\nimport type {\n ChatInputComponentHandle,\n ChatInputComponentProps,\n} from './ChatInterface.types';\n\n/**\n * Default ChatInterface input.\n *\n * - Multi-line textarea (Enter sends, Shift+Enter newlines).\n * - `field-sizing: content` primary path; rAF-batched JS fallback for older browsers.\n * - IME-safe Enter handling: checks `event.isComposing || event.keyCode === 229`.\n * - `autoComplete=\"off\"` for privacy (no draft persistence in browser autofill).\n * - Implements the ChatInputComponent contract: focus / clear / submit / setValue.\n *\n * Accepts `sendShortcut` ('enter' | 'mod-enter') as a prop (passed by ChatInputArea).\n */\n\ninterface DefaultChatInputOwnProps extends ChatInputComponentProps {\n sendShortcut?: 'enter' | 'mod-enter';\n}\n\nconst DefaultChatInput = forwardRef<ChatInputComponentHandle, DefaultChatInputOwnProps>(\n function DefaultChatInput(\n {\n isDisabled,\n placeholder,\n maxLength,\n 'aria-label': ariaLabel,\n onSubmit,\n onChange,\n onFocus,\n onBlur,\n sendShortcut = 'enter',\n },\n ref\n ) {\n const [value, setValueState] = useState('');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useAutoResizeTextarea(textareaRef, value);\n\n // Refs that track the latest value/disabled/onSubmit/onChange so the imperative handle's\n // closures see the current values without re-creating the handle.\n //\n // Writes happen in useLayoutEffect — NOT during render — to be safe under React 19 concurrent\n // rendering, where renders may be discarded before commit. useLayoutEffect runs synchronously\n // after commit, before any imperative-handle method could fire (which always originates from\n // a user event after paint).\n const valueRef = useRef(value);\n const isDisabledRef = useRef(isDisabled);\n const onSubmitRef = useRef(onSubmit);\n const onChangeRef = useRef(onChange);\n useLayoutEffect(() => {\n valueRef.current = value;\n isDisabledRef.current = isDisabled;\n onSubmitRef.current = onSubmit;\n onChangeRef.current = onChange;\n });\n\n const handleSubmit = useCallback((): void => {\n if (isDisabledRef.current) return;\n const v = valueRef.current;\n if (v.trim().length === 0) return;\n onSubmitRef.current(v);\n }, []);\n\n useImperativeHandle(\n ref,\n () => ({\n focus: (): void => {\n textareaRef.current?.focus();\n // Place cursor at end if there's existing text.\n const el = textareaRef.current;\n if (el) {\n const len = el.value.length;\n el.setSelectionRange(len, len);\n }\n },\n clear: (): void => {\n setValueState('');\n onChangeRef.current?.('');\n },\n submit: (): void => {\n handleSubmit();\n },\n setValue: (markdown: string): void => {\n setValueState(markdown);\n onChangeRef.current?.(markdown);\n // Cursor at end on next paint.\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (!el) return;\n const len = el.value.length;\n el.setSelectionRange(len, len);\n });\n },\n }),\n [handleSubmit]\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>): void => {\n // IME composition guard: use event.isComposing OR keyCode === 229 (Chromium ordering bug\n // means a state-flag tracking compositionstart/end is unreliable).\n const isComposing =\n (e.nativeEvent as KeyboardEvent['nativeEvent'] & { isComposing?: boolean }).isComposing ||\n e.keyCode === 229;\n if (e.key !== 'Enter') return;\n if (isComposing) return;\n if (e.shiftKey) return; // Shift+Enter = newline\n if (sendShortcut === 'mod-enter') {\n if (!(e.metaKey || e.ctrlKey)) return;\n }\n e.preventDefault();\n handleSubmit();\n },\n [handleSubmit, sendShortcut]\n );\n\n return (\n <div className={chatInputWrapperVariants({ isDisabled })}>\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e): void => {\n setValueState(e.target.value);\n onChange?.(e.target.value);\n }}\n onKeyDown={handleKeyDown}\n onFocus={onFocus}\n onBlur={onBlur}\n disabled={isDisabled}\n placeholder={placeholder}\n maxLength={maxLength}\n autoComplete=\"off\"\n aria-label={ariaLabel ?? 'Message'}\n rows={1}\n className={cn(chatInputTextareaVariants())}\n />\n </div>\n );\n }\n);\n\nexport { DefaultChatInput };\n","'use client';\n\nimport { useCallback, useRef, type ReactNode } from 'react';\nimport { Send, StopCircle } from 'lucide-react';\nimport { Button } from '../Button/Button';\nimport { cn } from '../../utils/cn';\nimport { chatInputAreaVariants } from './ChatInterface.styles';\nimport { DefaultChatInput } from './DefaultChatInput';\nimport type {\n ChatInputComponent,\n ChatInputComponentHandle,\n ChatTopLevelStatus,\n} from './ChatInterface.types';\n\n/**\n * Wraps the (default or custom) input + Send/Stop button + inputActions slot.\n *\n * The Send button is ALWAYS enabled when the input area isn't disabled — it does not gate on\n * input emptiness. The contract guarantees `submit()` is no-op on empty input, so harmless\n * clicks on an empty input produce no message.\n *\n * `isDisabled` truth table (forwarded to the input):\n * - consumer.isDisabled === true → true\n * - status === 'streaming' → true\n * - status === 'error' → true\n * - otherwise → false\n *\n * Stop button is shown only when status === 'streaming'.\n */\ninterface ChatInputAreaProps {\n status: ChatTopLevelStatus;\n consumerDisabled: boolean;\n inputComponent?: ChatInputComponent;\n placeholder?: string;\n maxInputLength?: number;\n sendShortcut: 'enter' | 'mod-enter';\n inputActions?: ReactNode;\n inputAriaLabel: string;\n /** Forwarded to consumer's `onSendMessage`. ChatInterface enforces maxInputLength backstop here. */\n onSendMessage: (text: string) => void;\n onStopGeneration?: () => void;\n /** Receives the input handle so the parent can implement `chatRef.current.focusInput()`. */\n registerInputHandle: (handle: ChatInputComponentHandle | null) => void;\n /** Tells ChatInterface that an `error` → `idle` transition just happened so it can move focus. */\n className?: string;\n}\n\nexport function ChatInputArea({\n status,\n consumerDisabled,\n inputComponent,\n placeholder,\n maxInputLength,\n sendShortcut,\n inputActions,\n inputAriaLabel,\n onSendMessage,\n onStopGeneration,\n registerInputHandle,\n className,\n}: ChatInputAreaProps): React.ReactElement {\n // The forwarded isDisabled per the truth table.\n const inputIsDisabled = consumerDisabled || status === 'streaming' || status === 'error';\n\n const inputHandleRef = useRef<ChatInputComponentHandle | null>(null);\n\n const setInputHandle = useCallback(\n (h: ChatInputComponentHandle | null): void => {\n inputHandleRef.current = h;\n registerInputHandle(h);\n },\n [registerInputHandle]\n );\n\n // Wired onSubmit: ChatInterface enforces maxInputLength as defence-in-depth and\n // calls consumer's onSendMessage + clears the input.\n const handleSubmitFromInput = useCallback(\n (markdown: string): void => {\n let toSend = markdown;\n if (typeof maxInputLength === 'number' && toSend.length > maxInputLength) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ChatInterface] Input exceeded maxInputLength (${maxInputLength}); truncating before send.`\n );\n }\n toSend = toSend.slice(0, maxInputLength);\n }\n onSendMessage(toSend);\n inputHandleRef.current?.clear();\n },\n [maxInputLength, onSendMessage]\n );\n\n const handleSendClick = useCallback((): void => {\n inputHandleRef.current?.submit();\n }, []);\n\n const handleStopClick = useCallback((): void => {\n onStopGeneration?.();\n }, [onStopGeneration]);\n\n const isStreaming = status === 'streaming';\n\n // Render the chosen input.\n const InputComp = inputComponent;\n\n return (\n <div className={cn(chatInputAreaVariants(), className)}>\n <div className=\"flex-1 min-w-0\">\n {InputComp ? (\n <InputComp\n ref={setInputHandle}\n isDisabled={inputIsDisabled}\n placeholder={placeholder}\n maxLength={maxInputLength}\n aria-label={inputAriaLabel}\n onSubmit={handleSubmitFromInput}\n />\n ) : (\n <DefaultChatInput\n ref={setInputHandle}\n isDisabled={inputIsDisabled}\n placeholder={placeholder}\n maxLength={maxInputLength}\n aria-label={inputAriaLabel}\n onSubmit={handleSubmitFromInput}\n sendShortcut={sendShortcut}\n />\n )}\n </div>\n\n {inputActions ? <div className=\"flex-shrink-0\">{inputActions}</div> : null}\n\n {isStreaming ? (\n <Button\n variant=\"destructive\"\n visualSize=\"icon\"\n aria-label=\"Stop generating\"\n onPress={handleStopClick}\n data-action=\"stop\"\n >\n <StopCircle aria-hidden=\"true\" />\n </Button>\n ) : (\n <Button\n variant=\"default\"\n visualSize=\"icon\"\n aria-label=\"Send message\"\n isDisabled={consumerDisabled}\n onPress={handleSendClick}\n data-action=\"send\"\n >\n <Send aria-hidden=\"true\" />\n </Button>\n )}\n </div>\n );\n}\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type {\n ForwardRefExoticComponent,\n ReactNode,\n RefAttributes,\n} from 'react';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * ChatInterface — public types and Zod schemas.\n *\n * Schemas use `.strict()` so unknown keys throw rather than strip silently.\n * Production callers should NEVER call `.parse()` directly — internal validation\n * is gated on `process.env.NODE_ENV !== 'production'` so tsup tree-shakes it out.\n */\n\n// ============================================================================\n// Roles & statuses\n// ============================================================================\n\nexport const ChatRoleSchema = z.enum(['user', 'assistant', 'system']);\nexport type ChatRole = z.infer<typeof ChatRoleSchema>;\n\nexport const ChatMessageStatusSchema = z.enum([\n 'complete',\n 'streaming',\n 'thinking',\n 'error',\n]);\nexport type ChatMessageStatus = z.infer<typeof ChatMessageStatusSchema>;\n\nexport const ChatTopLevelStatusSchema = z.enum(['idle', 'streaming', 'error']);\nexport type ChatTopLevelStatus = z.infer<typeof ChatTopLevelStatusSchema>;\n\n// ============================================================================\n// Thinking / tool-call parts\n// ============================================================================\n\nexport const ThinkingPartSchema = z\n .discriminatedUnion('kind', [\n z\n .object({\n kind: z.literal('text'),\n text: z.string(),\n })\n .strict(),\n z\n .object({\n kind: z.literal('tool_call'),\n name: z.string(),\n status: z.enum(['running', 'done', 'error']),\n summary: z.string().optional(),\n })\n .strict(),\n ]);\n\nexport type ThinkingPart = z.infer<typeof ThinkingPartSchema>;\n\n// ============================================================================\n// Message variants (discriminated union over `role`)\n// ============================================================================\n\nconst ChatUserMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('user'),\n /** User messages never stream or think. */\n status: z.enum(['complete', 'error']),\n content: z.string(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport type ChatUserMessage = z.infer<typeof ChatUserMessageSchema>;\n\nconst ChatAssistantMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('assistant'),\n status: ChatMessageStatusSchema,\n /** Markdown source text. ChatInterface treats it as opaque — consumer renders. */\n content: z.string(),\n /** Visible thinking / tool-call parts. */\n parts: z.array(ThinkingPartSchema).optional(),\n /** Free-form error detail when status === 'error'. Never interpolated into AT announcements. */\n errorMessage: z.string().optional(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport type ChatAssistantMessage = z.infer<typeof ChatAssistantMessageSchema>;\n\n/**\n * System messages bypass `renderAssistantContent` / `renderUserContent`.\n * `content` is `ReactNode` (not validated by Zod beyond presence) so consumers\n * can drop in formatted blocks (\"Conversation started\", session expiry notes).\n */\nexport interface ChatSystemMessage {\n id: string;\n role: 'system';\n status: 'complete';\n content: ReactNode;\n /** Default false — system messages are visual-only. Set true to announce via the status region. */\n announce?: boolean;\n createdAt?: number;\n}\n\nconst ChatSystemMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('system'),\n status: z.literal('complete'),\n // ReactNode isn't validatable by Zod — accept anything; runtime check only verifies role+status.\n content: z.unknown(),\n announce: z.boolean().optional(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport const ChatMessageSchema = z.union([\n ChatUserMessageSchema,\n ChatAssistantMessageSchema,\n ChatSystemMessageSchema,\n]);\n\nexport type ChatMessage =\n | ChatUserMessage\n | ChatAssistantMessage\n | ChatSystemMessage;\n\n// ============================================================================\n// Pluggable input contract\n// ============================================================================\n\n/**\n * Props ChatInterface passes to the input component (default OR custom).\n *\n * Trust boundary: the input emits `markdown: string` via `onSubmit`. ChatInterface\n * treats it as opaque and forwards to `props.onSendMessage`. Sanitisation is the\n * consumer's responsibility (in `renderAssistantContent` / `renderUserContent`).\n */\nexport interface ChatInputComponentProps {\n /** True while top-level status is streaming/error or consumer-disabled. Custom inputs MUST honour. */\n isDisabled: boolean;\n placeholder?: string;\n /** Best-effort cap. Default input enforces strictly; ChatInterface enforces a defence-in-depth backstop. */\n maxLength?: number;\n 'aria-label'?: string;\n /**\n * Called when the user confirms (Enter, Cmd+Enter, RTE submit, etc.).\n * The string MUST be markdown. Must NOT be called with empty/whitespace.\n */\n onSubmit: (markdown: string) => void;\n /**\n * Optional. Fires on value changes for char-count UX or analytics.\n * Custom inputs MAY debounce or throttle. ChatInterface does NOT use this to drive Send button state —\n * Send is always enabled when ChatInterface isn't disabled.\n */\n onChange?: (markdown: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n}\n\n/**\n * Imperative methods the input must expose via `forwardRef` + `useImperativeHandle`.\n */\nexport interface ChatInputComponentHandle {\n /** Move focus to the input. ChatInterface delegates `chatRef.current.focusInput()` here. */\n focus: () => void;\n /** Reset the input's value. Called after a successful send. */\n clear: () => void;\n /**\n * Trigger a submit. The input extracts its current value and invokes its own `onSubmit`.\n * No-op when empty/whitespace — never calls `onSubmit` with empty string.\n * This empty-guard is the contract: Send button is always enabled when ChatInterface isn't disabled,\n * and harmless clicks on an empty input produce no message.\n */\n submit: () => void;\n /**\n * Programmatically set the input's value (suggested-question prefill, edit-prior-message).\n * Does NOT change focus. Cursor lands at end. Consumers wanting focus call `focus()` separately.\n * `setValue` followed by `submit` is a documented feature; consumers wanting human-only origination\n * should track origination state themselves.\n */\n setValue: (markdown: string) => void;\n}\n\nexport type ChatInputComponent = ForwardRefExoticComponent<\n ChatInputComponentProps & RefAttributes<ChatInputComponentHandle>\n>;\n\n// ============================================================================\n// Imperative handle on the root component\n// ============================================================================\n\nexport interface ChatInterfaceHandle {\n focusInput: () => void;\n scrollToBottom: (opts?: { smooth?: boolean }) => void;\n}\n\n// ============================================================================\n// Root props\n// ============================================================================\n\n/**\n * Zod schema for runtime validation. Production callers should not invoke `.parse()` —\n * internal validation is dev-only.\n */\nexport const ChatInterfacePropsSchema = BaseComponentPropsSchema.extend({\n // Required\n status: ChatTopLevelStatusSchema,\n\n // Inputs that aren't validatable by Zod (functions, ReactNode, components) accept `unknown`/`any`.\n messages: z.array(ChatMessageSchema),\n onSendMessage: z.custom<(text: string) => void>(),\n onStopGeneration: z.custom<() => void>().optional(),\n onRetry: z.custom<(messageId: string) => void>().optional(),\n onEscalate: z.custom<() => void>().optional(),\n\n renderAssistantContent: z.any().optional(),\n renderUserContent: z.any().optional(),\n\n inputComponent: z.any().optional(),\n\n assistantAvatar: z.any().optional(),\n userAvatar: z.any().optional(),\n\n header: z.any().optional(),\n emptyState: z.any().optional(),\n inputActions: z.any().optional(),\n\n placeholder: z.string().optional(),\n isDisabled: z.boolean().optional(),\n maxInputLength: z.number().int().positive().optional(),\n sendShortcut: z.enum(['enter', 'mod-enter']).optional(),\n\n progressAnnouncements: z.boolean().optional(),\n completionAnnouncementMaxChars: z.number().int().positive().optional(),\n\n logLabel: z.string().optional(),\n thinkingDisclosureLabel: z.string().optional(),\n}).strict();\n\n/**\n * Render-prop renderers. Consumers parse markdown and produce a ReactNode.\n * MUST be pure render functions — no side effects. MUST NOT include `aria-live`,\n * `aria-busy`, or `role=\"log\"` (ChatInterface owns the live-region semantics).\n */\nexport type ChatAssistantRenderer = (message: ChatAssistantMessage) => ReactNode;\nexport type ChatUserRenderer = (message: ChatUserMessage) => ReactNode;\n\nexport interface ChatInterfaceProps {\n // ---- Required state ----\n /**\n * The full conversation transcript. ChatInterface is fully controlled — the consumer\n * owns this array and updates it via setState/reducer/etc.\n *\n * IMMUTABILITY: Pass a new message object when its `content`, `parts`, `status`,\n * `errorMessage`, or `createdAt` changes. In-place mutation will not re-render.\n */\n messages: ChatMessage[];\n /** Top-level status drives the input area (Send vs Stop, disabled state). */\n status: ChatTopLevelStatus;\n /** Required when status can be 'streaming'. */\n onSendMessage: (text: string) => void;\n\n // ---- Optional callbacks ----\n onStopGeneration?: () => void;\n onRetry?: (messageId: string) => void;\n onEscalate?: () => void;\n\n // ---- Content rendering (markdown is consumer-rendered) ----\n /** @see ChatAssistantRenderer for purity rules. */\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n\n // ---- Pluggable input ----\n /** Custom input component honouring the ChatInputComponent contract. Defaults to the built-in Themis-styled multi-line input. */\n inputComponent?: ChatInputComponent;\n\n // ---- Avatars ----\n /** Memoize inline JSX (e.g. `useMemo(() => <Avatar ... />, [])`) so it doesn't bust the bubble memo. */\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n\n // ---- Slots ----\n header?: ReactNode;\n emptyState?: ReactNode;\n inputActions?: ReactNode;\n\n // ---- Input config ----\n placeholder?: string;\n isDisabled?: boolean;\n maxInputLength?: number;\n /** Default 'enter'. 'mod-enter' = Cmd/Ctrl+Enter. Only honoured by the default input. */\n sendShortcut?: 'enter' | 'mod-enter';\n\n // ---- Live region behaviour ----\n /**\n * Default true. When false, suppresses the bounded \"still working\" pings (3s/15s/60s slow-response safety net).\n * Note: disabling removes the only AT signal of a hung stream.\n */\n progressAnnouncements?: boolean;\n /** Default 200. Clamped to [50, 1000] at runtime; out-of-range values dev-warn and clamp. */\n completionAnnouncementMaxChars?: number;\n\n // ---- Labelling / overrides ----\n 'aria-label'?: string;\n /** Default \"Conversation transcript\". */\n logLabel?: string;\n /** Default \"Thoughts\". */\n thinkingDisclosureLabel?: string;\n\n // ---- Standard ----\n className?: string;\n 'data-testid'?: string;\n}\n","'use client';\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { Button } from '../Button/Button';\nimport { ArrowDown } from 'lucide-react';\nimport { ChatLiveRegions } from './ChatLiveRegions';\nimport { ChatMessageList } from './ChatMessageList';\nimport { ChatInputArea } from './ChatInputArea';\nimport {\n chatHeaderVariants,\n chatJumpToLatestVariants,\n chatRootVariants,\n} from './ChatInterface.styles';\nimport {\n normaliseAnnouncementText,\n truncateAnnouncement,\n useAnnouncementQueue,\n useStillWorkingTimer,\n useTranscriptScroll,\n} from './ChatInterface.hooks';\nimport { ChatInterfacePropsSchema } from './ChatInterface.types';\nimport type {\n ChatAssistantMessage,\n ChatInputComponentHandle,\n ChatInterfaceHandle,\n ChatInterfaceProps,\n ChatMessage,\n} from './ChatInterface.types';\n\n/**\n * ChatInterface — WCAG 2.2 AAA-compliant chatbot UI.\n *\n * Stateless from the library's perspective: consumer owns the messages array,\n * status, and websocket. ChatInterface renders + emits events.\n *\n * Live-region architecture:\n * - `<div role=\"log\">` — visual + navigable, no `aria-live`. AT users locate via landmarks/rotor.\n * - `<div role=\"status\" aria-live=\"polite\">` — single sr-only AT announcement channel.\n * - Tokens mutate the streaming bubble's text content in place (silent).\n * - On streaming → complete, the bubble's rendered DOM `innerText` is normalised, truncated,\n * and pinged into the status region as `\"Assistant replied: <preview>\"`.\n *\n * IMMUTABILITY: Pass a new message object when its content/parts/status/errorMessage/createdAt\n * changes. In-place mutation will not re-render (memoised bubbles).\n *\n * Markdown rendering is the consumer's responsibility via `renderAssistantContent` /\n * `renderUserContent`. ChatInterface treats content as opaque string. See README for\n * `react-markdown` + `rehype-sanitize` integration guidance.\n */\nconst ChatInterface = forwardRef<ChatInterfaceHandle, ChatInterfaceProps>(\n function ChatInterface(props, ref) {\n const {\n messages,\n status,\n onSendMessage,\n onStopGeneration,\n onRetry,\n onEscalate,\n renderAssistantContent,\n renderUserContent,\n inputComponent,\n assistantAvatar,\n userAvatar,\n header,\n emptyState,\n inputActions,\n placeholder = 'Type a message…',\n isDisabled = false,\n maxInputLength,\n sendShortcut = 'enter',\n progressAnnouncements = true,\n completionAnnouncementMaxChars,\n 'aria-label': ariaLabel = 'Chat',\n logLabel = 'Conversation transcript',\n thinkingDisclosureLabel = 'Thoughts',\n className,\n 'data-testid': testId,\n } = props;\n\n // Dev-only schema validation. Production-stripped by tsup via the NODE_ENV guard.\n if (process.env.NODE_ENV !== 'production') {\n try {\n ChatInterfacePropsSchema.parse(props);\n } catch (e) {\n console.warn('[ChatInterface] props failed validation:', e);\n }\n if (inputComponent && !renderUserContent) {\n console.warn(\n '[ChatInterface] `inputComponent` is set but `renderUserContent` is not. ' +\n 'User messages will render markdown source verbatim — provide `renderUserContent` for parsed output.'\n );\n }\n }\n\n const prefersReducedMotion = useReducedMotion();\n\n // Dev-only: warn once per instance when the conversation exceeds the documented v1 ceiling.\n // `content-visibility: auto` keeps things smooth up to ~1000-2000 messages on a mid-range\n // device; beyond that, consumers should paginate (full virtualisation is a v2 candidate).\n const hasWarnedLongConversationRef = useRef(false);\n if (\n process.env.NODE_ENV !== 'production' &&\n messages.length > 1000 &&\n !hasWarnedLongConversationRef.current\n ) {\n hasWarnedLongConversationRef.current = true;\n console.warn(\n `[ChatInterface] messages.length is ${messages.length} (>1000). Render performance may degrade — ` +\n 'consider paginating in the consumer state. Full virtualisation is a v2 candidate.'\n );\n }\n\n // ---- Derived state ----\n const streamingMessage = useMemo<ChatAssistantMessage | null>(() => {\n let found: ChatAssistantMessage | null = null;\n let count = 0;\n for (const m of messages) {\n if (m.role === 'assistant' && (m.status === 'streaming' || m.status === 'thinking')) {\n if (!found) found = m;\n count += 1;\n }\n }\n if (process.env.NODE_ENV !== 'production' && count > 1) {\n console.warn(\n `[ChatInterface] Found ${count} messages with status='streaming' or 'thinking'; expected at most 1. Using the first.`\n );\n }\n return found;\n }, [messages]);\n\n const streamingMessageId = streamingMessage?.id ?? null;\n\n // ---- Refs ----\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const bottomSentinelRef = useRef<HTMLDivElement | null>(null);\n const inputHandleRef = useRef<ChatInputComponentHandle | null>(null);\n\n // Track the streaming bubble's rendered content node so we can read innerText on completion.\n const streamingContentRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());\n const registerStreamingContentRef = useCallback(\n (id: string, node: HTMLDivElement | null) => {\n if (node) {\n streamingContentRefs.current.set(id, node);\n } else {\n streamingContentRefs.current.delete(id);\n }\n },\n []\n );\n\n // ---- Status region announcement queue ----\n const { current: announcement, enqueue } = useAnnouncementQueue();\n\n // ---- Auto-scroll: stuck-to-bottom via IntersectionObserver sentinel ----\n const { isAtBottom, scrollToBottom } = useTranscriptScroll(\n scrollContainerRef,\n bottomSentinelRef,\n prefersReducedMotion\n );\n\n // Initial mount: scroll to bottom non-animated.\n const didMountRef = useRef(false);\n useEffect(() => {\n if (didMountRef.current) return;\n didMountRef.current = true;\n scrollToBottom({ smooth: false });\n }, [scrollToBottom]);\n\n // On new messages while sticky, scroll to bottom (non-animated to avoid jitter during streaming).\n const lastMessagesRef = useRef<ChatMessage[]>(messages);\n useEffect(() => {\n const prev = lastMessagesRef.current;\n lastMessagesRef.current = messages;\n // Only when new messages are appended or content grows; keep cheap.\n if (messages.length > prev.length && isAtBottom) {\n scrollToBottom({ smooth: false });\n }\n }, [messages, isAtBottom, scrollToBottom]);\n\n // Streaming-bubble growth: keep bottom in view while sticky.\n // `overflow-anchor: none` prevents the browser fighting us; without an active scroll call the\n // streaming text would scroll off-viewport. rAF-throttled so token rates of 100Hz coalesce to\n // ~display-refresh rate (60Hz) — `scrollIntoView` forces layout, so duplicate calls per frame\n // cost more than the user can perceive.\n const streamingContentLength =\n streamingMessage && streamingMessage.status === 'streaming'\n ? streamingMessage.content.length\n : 0;\n const scrollRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!streamingMessage) return;\n if (!isAtBottom) return;\n if (scrollRafRef.current !== null) return;\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n scrollToBottom({ smooth: false });\n });\n return (): void => {\n if (scrollRafRef.current !== null) {\n cancelAnimationFrame(scrollRafRef.current);\n scrollRafRef.current = null;\n }\n };\n }, [streamingContentLength, streamingMessage, isAtBottom, scrollToBottom]);\n\n // ---- Detect streaming → complete transitions to fire completion announcement ----\n //\n // O(1) tracking: we only need to know the previously-streaming/thinking assistant message.\n // Avoids the O(N) Map rebuild + GC sweep on every messages reference change. At 500 msgs\n // × 60Hz that drops 30k Map ops/sec to ~0.\n //\n // `errorFiredThisTickRef` signals to the top-level-status effect that a per-message error\n // fired, so we don't double-announce \"Response failed\" + \"Generation stopped\" for the same\n // logical event.\n const prevInProgressRef = useRef<{ id: string; status: 'streaming' | 'thinking' } | null>(null);\n const errorFiredThisTickRef = useRef(false);\n const pendingRafIdsRef = useRef<Set<number>>(new Set());\n useEffect(() => {\n errorFiredThisTickRef.current = false;\n const prev = prevInProgressRef.current;\n\n // Find current streaming/thinking message (reuses the upstream useMemo result).\n const nextInProgress = streamingMessage\n ? { id: streamingMessage.id, status: streamingMessage.status as 'streaming' | 'thinking' }\n : null;\n\n // Detect transition: previous in-progress message is no longer in-progress.\n if (prev && (!nextInProgress || nextInProgress.id !== prev.id)) {\n // The previously-streaming message either: completed, errored, was removed, or another\n // message took over as the streaming one. We need to look it up in the current messages.\n const prevMsg = messages.find((m) => m.id === prev.id);\n if (prevMsg && prevMsg.role === 'assistant') {\n if (prevMsg.status === 'complete') {\n // Build preview from rendered DOM innerText. Both lookup and read happen INSIDE rAF\n // so we capture the post-commit DOM state.\n const id = requestAnimationFrame(() => {\n pendingRafIdsRef.current.delete(id);\n const node = streamingContentRefs.current.get(prevMsg.id);\n const text = (node?.innerText ?? prevMsg.content ?? '').toString();\n const normalised = normaliseAnnouncementText(text);\n const preview = truncateAnnouncement(normalised, completionAnnouncementMaxChars);\n if (preview) {\n enqueue(`Assistant replied: ${preview}`);\n }\n });\n pendingRafIdsRef.current.add(id);\n } else if (prevMsg.status === 'error') {\n errorFiredThisTickRef.current = true;\n enqueue('Response failed');\n // Move focus from the (now vanished) Stop button to Try again.\n const id = requestAnimationFrame(() => {\n pendingRafIdsRef.current.delete(id);\n const bubble = scrollContainerRef.current?.querySelector(\n `[data-message-id=\"${cssEscape(prevMsg.id)}\"]`\n );\n // Library invariant: consumer renderer output MUST NOT include `data-action=\"retry\"`.\n const btn = bubble?.querySelector<HTMLButtonElement>('[data-action=\"retry\"]');\n btn?.focus();\n });\n pendingRafIdsRef.current.add(id);\n }\n }\n }\n\n prevInProgressRef.current = nextInProgress;\n }, [streamingMessage, messages, completionAnnouncementMaxChars, enqueue]);\n\n // Cancel any outstanding rAF on unmount.\n useEffect(() => {\n const ids = pendingRafIdsRef.current;\n return (): void => {\n for (const id of ids) cancelAnimationFrame(id);\n ids.clear();\n };\n }, []);\n\n // ---- Detect \"Generation stopped\" via top-level status transition ----\n // Suppressed if a per-message error transition fired in the same tick — they originate from\n // the same logical event (\"user pressed Stop on a failing stream\") and we don't want both pings.\n const prevStatusRef = useRef(status);\n useEffect(() => {\n const prev = prevStatusRef.current;\n prevStatusRef.current = status;\n if (prev === 'streaming' && status === 'idle' && !errorFiredThisTickRef.current) {\n enqueue('Generation stopped');\n }\n }, [status, enqueue]);\n\n // ---- \"Still working\" timer (bounded; resets on token arrival) ----\n // (streamingContentLength derived above for auto-scroll; reused here.)\n useStillWorkingTimer({\n isStreaming: status === 'streaming',\n streamingContentLength,\n enabled: progressAnnouncements,\n message: 'Assistant is still working',\n onAnnounce: enqueue,\n });\n\n // ---- Imperative handle ----\n useImperativeHandle(\n ref,\n () => ({\n focusInput: (): void => inputHandleRef.current?.focus(),\n scrollToBottom: (opts): void => scrollToBottom(opts),\n }),\n // refs are stable; scrollToBottom is memoised by useTranscriptScroll\n [scrollToBottom]\n );\n\n // ---- Render ----\n return (\n <section\n className={cn(chatRootVariants(), className, 'relative')}\n aria-label={ariaLabel}\n data-testid={testId}\n data-themis-chatinterface\n >\n {header ? <div className={chatHeaderVariants()}>{header}</div> : null}\n\n {/* Log region is ALWAYS rendered so the landmark stays consistent across empty→populated.\n The empty state lives inside the log, not in a separate region. */}\n <ChatMessageList\n ref={scrollContainerRef}\n messages={messages}\n streamingMessageId={streamingMessageId}\n assistantAvatar={assistantAvatar}\n userAvatar={userAvatar}\n renderAssistantContent={renderAssistantContent}\n renderUserContent={renderUserContent}\n thinkingDisclosureLabel={thinkingDisclosureLabel}\n logLabel={logLabel}\n emptyState={emptyState}\n onRetry={onRetry}\n onEscalate={onEscalate}\n registerStreamingContentRef={registerStreamingContentRef}\n bottomSentinelRef={bottomSentinelRef}\n testId={testId ? `${testId}-log` : undefined}\n />\n\n\n {/* Jump-to-latest floating button when not stuck to bottom */}\n <div className={chatJumpToLatestVariants({ visible: !isAtBottom && messages.length > 0 })}>\n <Button\n variant=\"secondary\"\n visualSize=\"icon\"\n aria-label=\"Jump to latest message\"\n onPress={(): void => scrollToBottom({ smooth: !prefersReducedMotion })}\n data-action=\"jump-to-latest\"\n >\n <ArrowDown aria-hidden=\"true\" />\n </Button>\n </div>\n\n <ChatInputArea\n status={status}\n consumerDisabled={isDisabled}\n inputComponent={inputComponent}\n placeholder={placeholder}\n maxInputLength={maxInputLength}\n sendShortcut={sendShortcut}\n inputActions={inputActions}\n inputAriaLabel={placeholder}\n onSendMessage={onSendMessage}\n onStopGeneration={onStopGeneration}\n registerInputHandle={(h): void => {\n inputHandleRef.current = h;\n }}\n />\n\n <ChatLiveRegions\n current={announcement}\n testId={testId ? `${testId}-status` : undefined}\n />\n </section>\n );\n }\n);\n\nChatInterface.displayName = 'ChatInterface';\n\n/**\n * CSS.escape fallback for environments missing it. Used to safely build attribute selectors\n * from message ids in `querySelector`. (We avoid this for general id lookups by using\n * Map-based ref tracking; this is solely for finding the retry button in the errored bubble.)\n */\nfunction cssEscape(id: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(id);\n }\n return id.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nexport { ChatInterface };\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/hooks/useReducedMotion.ts","../../../src/elements/Button/Button.styles.ts","../../../src/styles/interaction-states.ts","../../../src/elements/ButtonGroup/ButtonGroupContext.tsx","../../../src/elements/ButtonGroup/ButtonGroup.variants.ts","../../../src/elements/Button/Button.tsx","../../../src/elements/ChatInterface/ChatInterface.styles.ts","../../../src/elements/ChatInterface/ChatLiveRegions.tsx","../../../src/elements/ChatInterface/ChatTypingIndicator.tsx","../../../src/elements/ChatInterface/ChatThinkingDisplay.tsx","../../../src/elements/ChatInterface/ChatMessageBubble.tsx","../../../src/elements/ChatInterface/ChatMessageList.tsx","../../../src/elements/ChatInterface/ChatInterface.hooks.ts","../../../src/elements/ChatInterface/DefaultChatInput.tsx","../../../src/elements/ChatInterface/ChatInputArea.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/ChatInterface/ChatInterface.types.ts","../../../src/elements/ChatInterface/ChatInterface.tsx"],"names":["cn","inputs","twMerge","clsx","QUERY","getInitialState","useReducedMotion","prefersReducedMotion","setPrefersReducedMotion","useState","useEffect","mql","handler","event","buttonOuterVariants","cva","buttonVisualVariants","PRESSED_STYLES","HIGH_CONTRAST_HOVER","HIGH_CONTRAST_PRESSED","ButtonGroupContext","createContext","useButtonGroupContext","useContext","ButtonGroupItemContext","useButtonGroupItemContext","buttonGroupItemVariants","Button","memo","forwardRef","className","buttonVisualClassName","variant","size","visualSize","fullWidth","loading","loadingText","shortcut","children","isDisabled","paywall","paywallRedirect","paywallDescription","onPress","props","ref","paywallDescriptionId","useId","groupContext","itemContext","effectiveVariant","effectiveSize","effectiveIsDisabled","isInVerticalGroup","effectiveFullWidth","positionClassName","effectiveVisualSize","jsx","AriaButton","e","renderProps","jsxs","Fragment","Loader2","Zap","chatRootVariants","chatHeaderVariants","chatLogVariants","chatBubbleRowVariants","chatBubbleVariants","chatAvatarSlotVariants","chatInputAreaVariants","chatInputWrapperVariants","chatInputTextareaVariants","chatTypingIndicatorVariants","chatThinkingPillVariants","chatToolCallRowVariants","chatJumpToLatestVariants","chatStatusRegionVariants","chatErrorActionsVariants","ChatLiveRegions","current","testId","ChatTypingIndicator","ChatThinkingDisplay","parts","mode","disclosureLabel","rows","part","idx","Icon","Check","AlertCircle","statusLabel","shouldSkipRender","prev","next","a","b","i","pa","pb","ChatMessageBubble","message","isStreamingHere","isOffscreen","assistantAvatar","userAvatar","renderAssistantContent","renderUserContent","thinkingDisclosureLabel","contentRef","onRetry","onEscalate","renderSystem","renderUser","renderAssistant","avatar","render","content","args","isError","isThinking","suppressStreamingContent","renderedContent","CACHE_LIMIT","ChatMessageList","messages","streamingMessageId","logLabel","emptyState","registerStreamingContentRef","bottomSentinelRef","onscreenIds","useMemo","ids","m","cacheRef","useRef","lastRendererRef","cachedRenderAssistant","cache","hit","node","oldest","contentRefCacheRef","getContentRef","useCallback","id","cb","STRIP_PATTERN","normaliseAnnouncementText","input","out","truncateAnnouncement","text","maxChars","suffix","requested","clamped","detectFieldSizingSupport","useAutoResizeTextarea","textareaRef","value","maxLines","supportsFieldSizing","pendingRafRef","resize","computed","lineHeight","maxHeight","useLayoutEffect","useTranscriptScroll","scrollContainerRef","sentinelRef","isAtBottom","setIsAtBottom","root","target","obs","entries","last","scrollToBottom","opts","smooth","STILL_WORKING_INTERVALS_MS","useStillWorkingTimer","isStreaming","streamingContentLength","enabled","onAnnounce","lastTokenAtRef","announcementCountRef","lastLengthRef","timerRef","messageRef","onAnnounceRef","clearTimer","arm","targetDelay","elapsed","remaining","sinceToken","idxNow","delayNow","DEFAULT_HOLD_MS","DEFAULT_MAX_QUEUE","useAnnouncementQueue","minIntervalMs","dedupWindowMs","holdMs","maxQueueSize","setCurrent","queueRef","lastFiredAtRef","lastTextRef","lastTextAtRef","flushTimerRef","mountedRef","flush","now","sinceLast","enqueue","DefaultChatInput","placeholder","maxLength","ariaLabel","onSubmit","onChange","onFocus","onBlur","sendShortcut","setValueState","valueRef","isDisabledRef","onSubmitRef","onChangeRef","handleSubmit","v","useImperativeHandle","el","len","markdown","handleKeyDown","isComposing","ChatInputArea","status","consumerDisabled","inputComponent","maxInputLength","inputActions","inputAriaLabel","onSendMessage","onStopGeneration","registerInputHandle","inputIsDisabled","inputHandleRef","setInputHandle","h","handleSubmitFromInput","toSend","handleSendClick","handleStopClick","InputComp","StopCircle","Send","BaseComponentPropsSchema","z","ChatMessageStatusSchema","ChatTopLevelStatusSchema","ThinkingPartSchema","ChatUserMessageSchema","ChatAssistantMessageSchema","ChatSystemMessageSchema","ChatMessageSchema","ChatInterfacePropsSchema","ChatInterface","header","progressAnnouncements","completionAnnouncementMaxChars","hasWarnedLongConversationRef","streamingMessage","found","count","streamingContentRefs","announcement","didMountRef","lastMessagesRef","scrollRafRef","prevInProgressRef","errorFiredThisTickRef","pendingRafIdsRef","nextInProgress","prevMsg","normalised","preview","cssEscape","prevStatusRef","ArrowDown"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\nfunction getInitialState(): boolean {\n if (typeof window === 'undefined') return false;\n return window.matchMedia(QUERY).matches;\n}\n\n/**\n * Detects whether the user prefers reduced motion.\n *\n * - SSR-safe (returns `false` on the server)\n * - Reacts to changes in the OS preference\n * - Cleans up the listener on unmount\n */\nexport function useReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(getInitialState);\n\n useEffect(() => {\n const mql = window.matchMedia(QUERY);\n setPrefersReducedMotion(mql.matches);\n\n const handler = (event: MediaQueryListEvent): void => {\n setPrefersReducedMotion(event.matches);\n };\n\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }, []);\n\n return prefersReducedMotion;\n}\n","import { cva } from 'class-variance-authority';\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * Always transparent, centers the visual button inside\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n *\n * In vertical ButtonGroups, uses items-stretch so the visual layer (Layer 2)\n * can fill the full touch target height, eliminating gaps between buttons.\n */\nexport const buttonOuterVariants = cva(\n \"inline-flex justify-center min-h-[44px] min-w-[44px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n inVerticalGroup: {\n true: \"items-stretch\",\n false: \"items-center\",\n },\n },\n defaultVariants: {\n fullWidth: false,\n inVerticalGroup: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual button appearance (adjustable size)\n * Provides the visual appearance with configurable size\n * Can be smaller than touch target for use cases like carousel dots\n * NOTE: NO focus-visible styles here - focus ring is on Layer 1\n */\nexport const buttonVisualVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-normal ring-offset-background motion-safe:transition-transform motion-safe:duration-200 motion-safe:[transition-timing-function:cubic-bezier(0.34,1.56,0.64,1)] motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 relative cursor-pointer\",\n {\n variants: {\n variant: {\n default:\n \"bg-[var(--primary-action)] text-[var(--primary-action-foreground)] shadow-md hover:bg-[var(--primary-action-hover)] hover:-translate-y-1\",\n destructive:\n \"bg-[var(--destructive-background)] text-[var(--destructive-foreground)] hover:bg-[var(--destructive-background-hover,var(--destructive-background))] motion-safe:hover:btn-warn-shake\",\n outline:\n \"border border-[var(--input-border)] bg-[var(--content-background)] hover:bg-[var(--accent-background)]/50\",\n secondary:\n \"bg-[var(--secondary)] text-[var(--secondary-foreground)] hover:bg-[var(--secondary-hover,var(--secondary))]\",\n ghost:\n \"hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]\",\n link: \"text-[var(--text-link)] underline underline-offset-4 hover:bg-[var(--accent)] hover:text-[var(--text-link-hover)]\",\n },\n fullWidth: {\n true: \"w-full\",\n false: \"\",\n },\n visualSize: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3 text-xs\",\n lg: \"h-11 rounded-md px-6 text-base\",\n icon: \"h-10 w-10\",\n dot: \"h-5 w-5 rounded-full p-0 min-h-0 min-w-0\",\n },\n paywall: {\n true: \"!bg-[var(--paywall)] !text-[var(--paywall-foreground)] hover:!bg-[var(--paywall)]/90 !cursor-not-allowed !border-transparent\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n visualSize: \"default\",\n paywall: false,\n },\n }\n);\n\n/**\n * @deprecated Use buttonVisualVariants instead. This alias is kept for backward compatibility.\n */\nexport const buttonVariants = buttonVisualVariants;\n","/**\n * Global Interaction State Styles\n *\n * Consistent interaction patterns across all Themis components.\n * These styles ensure WCAG 2.2 AAA compliance and predictable UX.\n *\n * @see spec.md FR-010 (Visible focus ring for keyboard navigation)\n * @see spec.md FR-031 (Pressed state feedback)\n * @see spec.md FR-012 (High contrast mode support)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\n/**\n * Focus state styles (FR-010)\n * Visible focus ring for keyboard navigation - WCAG 2.2 Level AAA\n *\n * Components can override by extending these styles:\n * @example\n * cn(FOCUS_STYLES, \"ring-4\") // Increases ring width to 4px\n */\nexport const FOCUS_STYLES = \"data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--themis-ring)] data-[focus-visible]:ring-offset-2\";\n\n/**\n * Pressed/Active state styles (FR-031)\n * Visual feedback for press interactions\n *\n * Components can override the scale amount:\n * @example\n * cn(PRESSED_STYLES_BASE, \"data-[pressed]:scale-[0.95]\") // More pronounced scale\n */\nexport const PRESSED_STYLES = \"data-[pressed]:scale-[0.97]\";\n\n/**\n * Base pressed styles without scale (for components that need different feedback)\n */\nexport const PRESSED_STYLES_BASE = \"data-[pressed]:transition-transform data-[pressed]:duration-100\";\n\n/**\n * Hover state styles\n * Elevation change on hover for better affordance\n *\n * Components can override shadow depth:\n * @example\n * cn(HOVER_STYLES_BASE, \"data-[hovered]:shadow-lg\") // Larger shadow\n */\nexport const HOVER_STYLES = \"\";\n\n/**\n * Base hover styles without shadow (for components that use different hover effects)\n */\nexport const HOVER_STYLES_BASE = \"data-[hovered]:transition-shadow data-[hovered]:duration-200\";\n\n/**\n * High contrast mode focus (FR-012)\n * Enhanced outlines for users requiring high contrast\n *\n * Uses 'hc:' prefix for prefers-contrast: more media query\n */\nexport const HIGH_CONTRAST_FOCUS = \"hc:data-[focus-visible]:outline hc:data-[focus-visible]:outline-4 hc:data-[focus-visible]:outline-offset-2 hc:data-[focus-visible]:outline-foreground\";\n\n/**\n * High contrast mode hover (FR-012)\n * Enhanced outlines for hover in high contrast mode\n */\nexport const HIGH_CONTRAST_HOVER = \"hc:data-[hovered]:outline hc:data-[hovered]:outline-2 hc:data-[hovered]:outline-foreground\";\n\n/**\n * High contrast mode pressed state\n * Enhanced outlines for pressed state in high contrast mode\n */\nexport const HIGH_CONTRAST_PRESSED = \"hc:data-[pressed]:outline hc:data-[pressed]:outline-2 hc:data-[pressed]:outline-offset-1 hc:data-[pressed]:outline-foreground\";\n\n/**\n * Combined high contrast styles\n * Use this for components that need all high contrast interaction states\n */\nexport const HIGH_CONTRAST_INTERACTIONS = `${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Disabled state styles\n * Consistent disabled appearance across all components\n */\nexport const DISABLED_STYLES = \"disabled:pointer-events-none disabled:opacity-50\";\n\n/**\n * Default interaction bundle\n * Most common combination for interactive components\n *\n * Includes: focus ring, pressed scale, hover shadow, high contrast enhancements\n *\n * @example\n * <button className={cn(DEFAULT_INTERACTIONS, \"bg-primary\")}>Click</button>\n */\nexport const DEFAULT_INTERACTIONS = `${FOCUS_STYLES} ${PRESSED_STYLES} ${HOVER_STYLES} ${HIGH_CONTRAST_FOCUS} ${HIGH_CONTRAST_HOVER} ${HIGH_CONTRAST_PRESSED}`;\n\n/**\n * Subtle interaction bundle\n * For components that need less pronounced feedback\n *\n * Includes: focus ring and high contrast, but no hover shadow or pressed scale\n */\nexport const SUBTLE_INTERACTIONS = `${FOCUS_STYLES} ${HIGH_CONTRAST_FOCUS}`;\n\n/**\n * Non-interactive element styles\n * For elements that should indicate they are not interactive\n */\nexport const NON_INTERACTIVE = \"cursor-default select-none\";\n","\"use client\";\n\nimport { createContext, useContext } from 'react';\nimport type {\n ButtonGroupContextValue,\n ButtonGroupItemContextValue,\n} from './ButtonGroup.types';\n\n/**\n * ButtonGroup Context System (Two-Level)\n *\n * Provides a two-level context pattern for ButtonGroup:\n *\n * 1. ButtonGroupContext (group-level):\n * - Provides: orientation, variant, size, isDisabled\n * - Consumed by: Button (for prop inheritance), Separator (for orientation)\n *\n * 2. ButtonGroupItemContext (item-level):\n * - Provides: position ('first' | 'middle' | 'last' | 'only')\n * - Consumed by: Button (for border-radius styling)\n *\n * Both contexts return null when not in a provider, allowing Button\n * to work standalone without any group context.\n *\n * @see plan.md for architecture details\n * @see ButtonGroup.tsx for Provider implementation\n */\n\n// =============================================================================\n// Group-Level Context\n// =============================================================================\n\n/**\n * Context for group-level props (orientation, variant, size, isDisabled)\n * Default value is null to indicate \"not in a group\"\n */\nconst ButtonGroupContext = createContext<ButtonGroupContextValue | null>(null);\n\nButtonGroupContext.displayName = 'ButtonGroupContext';\n\n/**\n * Hook to access group-level context\n * @returns ButtonGroupContextValue if inside a ButtonGroup, null otherwise\n */\nexport function useButtonGroupContext(): ButtonGroupContextValue | null {\n return useContext(ButtonGroupContext);\n}\n\n// =============================================================================\n// Item-Level Context\n// =============================================================================\n\n/**\n * Context for per-button position information\n * Default value is null to indicate \"not wrapped with position context\"\n */\nconst ButtonGroupItemContext =\n createContext<ButtonGroupItemContextValue | null>(null);\n\nButtonGroupItemContext.displayName = 'ButtonGroupItemContext';\n\n/**\n * Hook to access item-level context (position)\n * @returns ButtonGroupItemContextValue if wrapped with position context, null otherwise\n */\nexport function useButtonGroupItemContext(): ButtonGroupItemContextValue | null {\n return useContext(ButtonGroupItemContext);\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { ButtonGroupContext, ButtonGroupItemContext };\n","import { cva } from 'class-variance-authority';\n\n/**\n * ButtonGroup CVA Variants\n *\n * Defines Class Variance Authority (CVA) variants for:\n * - ButtonGroup container (orientation-based layout)\n * - ButtonGroupItem (position-based border-radius)\n * - ButtonGroupSeparator (orientation-based styling)\n *\n * @see plan.md Phase 1: Design & Contracts - CVA Variants\n * @see constitution.md Principle V (Component Quality Standards)\n */\n\n// =============================================================================\n// Container Variants\n// =============================================================================\n\n/**\n * ButtonGroup container variants\n * Controls the layout direction based on orientation\n * Uses gap-0 to ensure buttons are connected (share borders)\n */\nexport const buttonGroupVariants = cva('inline-flex items-center gap-0', {\n variants: {\n orientation: {\n horizontal: 'flex-row',\n vertical: 'flex-col w-full',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n\n// =============================================================================\n// Item Position Variants\n// =============================================================================\n\n/**\n * ButtonGroupItem position variants\n * Applied to Button's visual layer (Layer 2) for position-aware border-radius\n *\n * Compound variants handle both orientation and position combinations:\n * - Horizontal: left/right borders and radii\n * - Vertical: top/bottom borders and radii\n */\nexport const buttonGroupItemVariants = cva('', {\n variants: {\n orientation: {\n // min-w-[44px] ensures visual layer fills touch target width (for icon buttons)\n horizontal: 'min-w-[44px]',\n // flex (overrides inline-flex) + min-h-[44px] makes visual layer fill touch target,\n // eliminating gaps between stacked buttons in vertical orientation\n vertical: 'flex min-h-[44px]',\n },\n position: {\n first: '',\n middle: '',\n last: '',\n only: '', // Single button - no modifications needed\n },\n },\n compoundVariants: [\n // ==========================================================================\n // Horizontal Orientation\n // ==========================================================================\n {\n orientation: 'horizontal',\n position: 'first',\n className: 'rounded-r-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'middle',\n className: 'rounded-none border-r-0',\n },\n {\n orientation: 'horizontal',\n position: 'last',\n className: 'rounded-l-none',\n },\n // ==========================================================================\n // Vertical Orientation\n // Note: w-full is handled by Button's effectiveFullWidth for both layers\n // ==========================================================================\n {\n orientation: 'vertical',\n position: 'first',\n className: 'rounded-b-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'middle',\n className: 'rounded-none border-b-0',\n },\n {\n orientation: 'vertical',\n position: 'last',\n className: 'rounded-t-none',\n },\n ],\n defaultVariants: {\n orientation: 'horizontal',\n position: 'only',\n },\n});\n\n// =============================================================================\n// Separator Variants\n// =============================================================================\n\n/**\n * ButtonGroupSeparator variants\n * Orientation-aware visual divider between button groups\n */\nexport const buttonGroupSeparatorVariants = cva('bg-[var(--border)]', {\n variants: {\n orientation: {\n horizontal: 'w-px h-6 mx-1',\n vertical: 'h-px w-full my-1',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n});\n","\"use client\";\n\n/**\n * Button Component - 3-Layer Architecture\n * Accessible button with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant, transparent)\n * - Layer 2: Visual Button (configurable size, colors, borders)\n * - Layer 3: Content & Effects (text, icons, ripple, loading spinner)\n *\n * @see 3layer-plan.md for architecture details\n * @see spec.md FR-029 to FR-037 (Button Component Requirements)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 7:1 contrast ratio)\n * @see spec.md FR-014 (44x44px minimum touch targets)\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useId } from 'react';\nimport {\n Button as AriaButton,\n type ButtonProps as AriaButtonProps,\n} from 'react-aria-components';\nimport { Loader2, Zap } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport type { ButtonProps } from './Button.types';\nimport { buttonOuterVariants, buttonVisualVariants, buttonVariants } from './Button.styles';\nimport { PRESSED_STYLES, HOVER_STYLES, HIGH_CONTRAST_HOVER, HIGH_CONTRAST_PRESSED } from '../../styles/interaction-states';\nimport {\n useButtonGroupContext,\n useButtonGroupItemContext,\n} from '../ButtonGroup/ButtonGroupContext';\nimport { buttonGroupItemVariants } from '../ButtonGroup/ButtonGroup.variants';\n\n/**\n * Button Component - 3-Layer Architecture\n * Fully accessible button with React Aria and themed styling\n *\n * Layer 1: Touch Target (AriaButton) - 44x44px WCAG AAA compliant\n * Layer 2: Visual Button (span) - configurable appearance\n * Layer 3: Content (children) - text, icons, effects\n */\nconst Button = memo(forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n buttonVisualClassName,\n variant,\n size,\n visualSize,\n fullWidth,\n loading = false,\n loadingText = \"Loading...\",\n shortcut,\n children,\n isDisabled,\n paywall = false,\n paywallRedirect,\n paywallDescription,\n onPress,\n ...props\n },\n ref\n ) => {\n const paywallDescriptionId = useId();\n\n // ==========================================================================\n // ButtonGroup Context Integration\n // ==========================================================================\n\n // Consume group-level context (variant, size, isDisabled, orientation)\n const groupContext = useButtonGroupContext();\n\n // Consume item-level context (position for border-radius styling)\n const itemContext = useButtonGroupItemContext();\n\n // Merge context values with props (props take precedence)\n const effectiveVariant = variant ?? groupContext?.variant ?? 'default';\n const effectiveSize = size ?? groupContext?.size;\n const effectiveIsDisabled = isDisabled ?? groupContext?.isDisabled ?? false;\n\n // In vertical groups, buttons should be full width automatically\n const isInVerticalGroup = groupContext?.orientation === 'vertical';\n const effectiveFullWidth = fullWidth || isInVerticalGroup;\n\n // Position styling for ButtonGroup (only applied when in a group)\n const positionClassName = itemContext\n ? buttonGroupItemVariants({\n orientation: groupContext?.orientation ?? 'horizontal',\n position: itemContext.position,\n })\n : '';\n\n // Default visualSize to size for backward compatibility\n const effectiveVisualSize = visualSize ?? effectiveSize ?? 'default';\n\n // AAA Accessibility: Warn in dev/test if icon/dot variant lacks accessible name\n if (process.env.NODE_ENV !== 'production') {\n if (\n (effectiveVisualSize === 'dot' || effectiveVisualSize === 'icon') &&\n !props['aria-label'] &&\n !children\n ) {\n console.warn(\n '[Button] visualSize=\"dot\" or \"icon\" requires aria-label when no visible text is provided (WCAG 1.1.1)'\n );\n }\n }\n\n /**\n * Handle button press - intercepts action when paywalled\n * If paywalled with redirect URL, opens in new tab\n * Otherwise, calls the normal onPress handler\n */\n const handlePress = (e: Parameters<NonNullable<AriaButtonProps['onPress']>>[0]): void => {\n if (paywall) {\n if (paywallRedirect) {\n window.open(paywallRedirect, '_blank', 'noopener,noreferrer');\n }\n // Don't call onPress when paywalled\n return;\n }\n onPress?.(e);\n };\n\n // Only set isDisabled when we have a reason to disable\n // Otherwise, let slot system control disabled state (e.g., in NumberField)\n const computedIsDisabled = effectiveIsDisabled || loading || undefined;\n\n return (\n <AriaButton\n ref={ref}\n isDisabled={computedIsDisabled}\n aria-disabled={paywall ? true : undefined}\n aria-describedby={paywall ? paywallDescriptionId : undefined}\n onPress={handlePress}\n className={cn(buttonOuterVariants({ fullWidth: effectiveFullWidth, inVerticalGroup: isInVerticalGroup }), className)}\n {...props}\n >\n {(renderProps) => (\n /* Layer 2: Visual Button */\n <span\n className={cn(\n buttonVisualVariants({\n variant: effectiveVariant,\n visualSize: effectiveVisualSize,\n paywall,\n fullWidth: effectiveFullWidth,\n }),\n // Position styling from ButtonGroup context (border-radius adjustments)\n positionClassName,\n buttonVisualClassName,\n // Layer 2 interaction styles (no focus - focus ring is on Layer 1)\n PRESSED_STYLES,\n HOVER_STYLES,\n HIGH_CONTRAST_HOVER,\n HIGH_CONTRAST_PRESSED\n )}\n data-pressed={renderProps.isPressed || undefined}\n >\n {/* Layer 3: Content & Effects */}\n\n {/* FR-033: Loading spinner with screen reader announcement */}\n {/* Uses motion-safe: for WCAG 2.3.3 AAA (Animation from Interactions) */}\n {loading && (\n <>\n <Loader2 className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />\n <span className=\"sr-only\" aria-live=\"polite\">\n {loadingText}\n </span>\n </>\n )}\n\n {/* Hide children during loading */}\n {!loading && children}\n\n {/* Paywall: Lightning bolt icon */}\n {paywall && (\n <Zap\n data-testid=\"zap-icon\"\n aria-hidden=\"true\"\n className=\"ml-1\"\n />\n )}\n\n {/* Paywall: Screen reader description */}\n {paywall && (\n <span id={paywallDescriptionId} className=\"sr-only\">\n Premium feature: {paywallDescription || \"Upgrade required to access this feature\"}\n </span>\n )}\n\n {/* FR-034: Keyboard shortcut display on focus */}\n {renderProps.isFocusVisible && shortcut && (\n <kbd className=\"ml-auto hidden text-xs opacity-60 lg:inline\">\n {shortcut}\n </kbd>\n )}\n\n </span>\n )}\n </AriaButton>\n );\n }\n));\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants, buttonOuterVariants, buttonVisualVariants };\nexport type { ButtonProps } from './Button.types';\n","import { cva } from 'class-variance-authority';\n\n/**\n * Root container — fills the parent.\n */\nexport const chatRootVariants = cva(\n 'flex flex-col h-full w-full bg-[var(--content-background)] text-[var(--content-foreground)]'\n);\n\nexport const chatHeaderVariants = cva(\n 'flex-shrink-0 border-b border-[var(--border)] px-4 py-2'\n);\n\n/**\n * Log region — visual + navigable. NO `aria-live`. Scroll container.\n * `overflow-anchor: none` prevents browser scroll-anchoring fighting auto-scroll while the streaming bubble grows.\n */\nexport const chatLogVariants = cva(\n 'flex-1 min-h-0 overflow-y-auto overflow-x-hidden flex flex-col gap-3 px-4 py-3 outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 [--tw-ring-offset-color:var(--content-background)] [overflow-anchor:none]'\n);\n\n/**\n * Message bubble — wrapper for a single message row.\n * `content-visibility: auto` skips layout for offscreen bubbles. Skip on streaming bubble.\n */\nexport const chatBubbleRowVariants = cva('flex w-full gap-2', {\n variants: {\n align: {\n user: 'justify-end',\n assistant: 'justify-start',\n system: 'justify-center',\n },\n offscreen: {\n true: '[content-visibility:auto] [contain-intrinsic-size:auto_80px]',\n false: '',\n },\n },\n defaultVariants: { align: 'assistant', offscreen: true },\n});\n\n/**\n * The visible bubble itself.\n */\nexport const chatBubbleVariants = cva(\n 'rounded-[var(--radius-md)] px-3 py-2 max-w-[85%] break-words',\n {\n variants: {\n role: {\n user: 'bg-[var(--primary-action)] text-[var(--primary-action-foreground)]',\n assistant:\n 'bg-[var(--muted)] text-[var(--content-foreground)] border border-[var(--border)]',\n system: 'bg-transparent text-[var(--muted-foreground)] text-sm italic',\n },\n isError: {\n true: 'border border-[var(--destructive-background)] bg-[var(--destructive-background)]/10',\n false: '',\n },\n },\n defaultVariants: { role: 'assistant', isError: false },\n }\n);\n\n/**\n * Avatar slot beside a bubble.\n */\nexport const chatAvatarSlotVariants = cva(\n 'flex-shrink-0 self-end',\n {\n variants: {\n role: {\n user: 'order-2',\n assistant: 'order-1',\n system: 'hidden',\n },\n },\n defaultVariants: { role: 'assistant' },\n }\n);\n\n/**\n * Input area at the bottom of the chat.\n */\nexport const chatInputAreaVariants = cva(\n 'flex-shrink-0 flex items-end gap-2 border-t border-[var(--border)] bg-[var(--content-background)] p-3'\n);\n\n/**\n * The default input wrapper (Layer 1 of the input).\n *\n * `min-h-[44px]` matches the Button outer touch-target height so an empty single-line input\n * visually aligns with the Send/Stop button. With `field-sizing: content` on the textarea,\n * the wrapper grows naturally as the user types multi-line; `items-end` on the parent keeps\n * the bottoms aligned (the wrapper's top rises while the button stays anchored).\n */\nexport const chatInputWrapperVariants = cva(\n 'flex-1 min-w-0 min-h-[44px] flex items-center rounded-[var(--radius-lg)] border border-[var(--input-border)] bg-[var(--input-background)] focus-within:border-[var(--ring)] focus-within:ring-2 focus-within:ring-[var(--ring)] focus-within:ring-offset-1 [--tw-ring-offset-color:var(--content-background)] motion-safe:transition-colors',\n {\n variants: {\n isDisabled: {\n true: 'opacity-60 cursor-not-allowed',\n false: '',\n },\n },\n defaultVariants: { isDisabled: false },\n }\n);\n\n/**\n * The textarea inside the wrapper. JS path for browsers that don't support `field-sizing: content`.\n */\nexport const chatInputTextareaVariants = cva(\n 'block w-full resize-none border-0 bg-transparent px-3 py-2 text-sm text-[var(--content-foreground)] placeholder:text-[var(--muted-foreground)] outline-none focus:outline-none disabled:cursor-not-allowed [field-sizing:content]'\n);\n\n/**\n * Typing indicator (three dots) shown during active token streaming.\n */\nexport const chatTypingIndicatorVariants = cva(\n 'inline-flex items-center gap-1 px-2 py-1',\n {\n variants: {\n animate: {\n true: '[&>span]:motion-safe:animate-pulse',\n false: '',\n },\n },\n defaultVariants: { animate: true },\n }\n);\n\n/**\n * Thinking pill shown while assistant message has status === 'thinking'.\n */\nexport const chatThinkingPillVariants = cva(\n 'inline-flex items-center gap-1 rounded-full bg-[var(--muted)] px-2 py-0.5 text-xs text-[var(--muted-foreground)]',\n {\n variants: {\n animate: {\n true: 'motion-safe:animate-pulse',\n false: '',\n },\n },\n defaultVariants: { animate: true },\n }\n);\n\n/**\n * Tool-call row inside the thinking display.\n */\nexport const chatToolCallRowVariants = cva(\n 'flex items-center gap-2 rounded px-2 py-1 text-xs',\n {\n variants: {\n status: {\n running: 'bg-[var(--muted)] text-[var(--muted-foreground)]',\n done: 'bg-[var(--success-background)]/20 text-[var(--content-foreground)]',\n error: 'bg-[var(--destructive-background)]/20 text-[var(--content-foreground)]',\n },\n },\n defaultVariants: { status: 'running' },\n }\n);\n\n/**\n * Jump-to-latest floating button. 44x44 layer-1 + visual layer.\n */\nexport const chatJumpToLatestVariants = cva(\n 'absolute bottom-20 right-4 z-10 motion-safe:transition-transform motion-safe:duration-150',\n {\n variants: {\n visible: {\n true: 'translate-y-0 opacity-100',\n false: 'translate-y-2 opacity-0 pointer-events-none',\n },\n },\n defaultVariants: { visible: false },\n }\n);\n\n/**\n * sr-only utility for the status region. Stays out of visual flow but accessible.\n */\nexport const chatStatusRegionVariants = cva(\n 'sr-only'\n);\n\n/**\n * Action row below an errored assistant bubble (Try again / Submit a support request).\n */\nexport const chatErrorActionsVariants = cva(\n 'mt-2 flex flex-wrap items-center gap-2'\n);\n","'use client';\n\nimport { memo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { chatStatusRegionVariants } from './ChatInterface.styles';\n\n/**\n * Single sr-only `role=\"status\"` region — the canonical AT announcement channel.\n * The log region (separate component) has NO `aria-live`; all announcements flow through here.\n *\n * @see ChatInterface.hooks.ts → useAnnouncementQueue for the FIFO + dedup + rate-limit logic.\n */\nconst ChatLiveRegions = memo(function ChatLiveRegions({\n current,\n className,\n testId,\n}: {\n current: string;\n className?: string;\n testId?: string;\n}) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(chatStatusRegionVariants(), className)}\n data-testid={testId}\n >\n {current}\n </div>\n );\n});\n\nexport { ChatLiveRegions };\n","'use client';\n\nimport { memo } from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { chatTypingIndicatorVariants } from './ChatInterface.styles';\n\n/**\n * Three-dot typing indicator. Visible only during active token streaming\n * (NOT during thinking — that uses the static \"Thinking…\" pill).\n *\n * Animation gated by `useReducedMotion`. Compositor-thread (opacity).\n */\nconst ChatTypingIndicator = memo(function ChatTypingIndicator({\n className,\n}: {\n className?: string;\n}) {\n const prefersReducedMotion = useReducedMotion();\n return (\n <span\n className={cn(chatTypingIndicatorVariants({ animate: !prefersReducedMotion }), className)}\n aria-hidden=\"true\"\n >\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" />\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" style={{ animationDelay: '0.15s' }} />\n <span className=\"inline-block h-1.5 w-1.5 rounded-full bg-[var(--muted-foreground)]\" style={{ animationDelay: '0.3s' }} />\n </span>\n );\n});\n\nexport { ChatTypingIndicator };\n","'use client';\n\nimport { memo } from 'react';\nimport { Loader2, Check, AlertCircle } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport {\n chatThinkingPillVariants,\n chatToolCallRowVariants,\n} from './ChatInterface.styles';\nimport type { ThinkingPart } from './ChatInterface.types';\n\n/**\n * Renders thinking parts visually.\n *\n * Two render modes:\n * - `mode=\"active\"` (status === 'thinking'): rows visible at the top of the bubble, \"Thinking…\" pill shown.\n * - `mode=\"collapsed\"` (status === 'streaming' | 'complete'): wrapped in a `<details>` disclosure\n * labelled by `disclosureLabel`. Closed by default.\n *\n * Tool transitions (running → done → error) are SILENT to AT — no `aria-live` here. The\n * in-bubble \"Still working\" timer in the parent covers slow-response signalling.\n */\nconst ChatThinkingDisplay = memo(function ChatThinkingDisplay({\n parts,\n mode,\n disclosureLabel,\n className,\n}: {\n parts: ThinkingPart[];\n mode: 'active' | 'collapsed';\n disclosureLabel: string;\n className?: string;\n}) {\n const prefersReducedMotion = useReducedMotion();\n if (parts.length === 0) return null;\n\n const rows = (\n <div className=\"flex flex-col gap-1\">\n {parts.map((part, idx) => {\n if (part.kind === 'text') {\n return (\n <p key={idx} className=\"text-xs text-[var(--muted-foreground)]\">\n {part.text}\n </p>\n );\n }\n // Tool call: announce status via text label, not just colour/icon.\n const Icon =\n part.status === 'running'\n ? Loader2\n : part.status === 'done'\n ? Check\n : AlertCircle;\n const statusLabel =\n part.status === 'running'\n ? 'running'\n : part.status === 'done'\n ? 'completed'\n : 'failed';\n return (\n <div key={idx} className={chatToolCallRowVariants({ status: part.status })}>\n <Icon\n className={cn(\n 'h-3 w-3',\n part.status === 'running' && !prefersReducedMotion && 'motion-safe:animate-spin'\n )}\n aria-hidden=\"true\"\n />\n {/* Visually-hidden prefix gives AT users the kind of element they're hearing without\n cluttering the visible row. Visible children remain in document order for both. */}\n <span className=\"sr-only\">{part.name} tool, </span>\n <span className=\"font-medium\">{part.name}</span>\n <span aria-hidden=\"true\" className=\"text-[var(--muted-foreground)]\">·</span>\n <span>{statusLabel}</span>\n {part.summary ? (\n <span className=\"ml-1 text-[var(--muted-foreground)]\">— {part.summary}</span>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n\n if (mode === 'active') {\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <span className={chatThinkingPillVariants({ animate: !prefersReducedMotion })}>\n Thinking…\n </span>\n {rows}\n </div>\n );\n }\n\n // Collapsed disclosure — native <details>/<summary> is keyboard-accessible without ARIA.\n // summary gets min-h 44 to satisfy WCAG 2.5.5 target size.\n return (\n <details className={cn('mt-2 text-xs', className)}>\n <summary className=\"cursor-pointer inline-flex items-center min-h-[44px] py-1 text-[var(--muted-foreground)] hover:text-[var(--content-foreground)]\">\n {disclosureLabel}\n </summary>\n <div className=\"mt-2\">{rows}</div>\n </details>\n );\n});\n\nexport { ChatThinkingDisplay };\n","'use client';\n\nimport { memo, type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { Button } from '../Button/Button';\nimport { ChatTypingIndicator } from './ChatTypingIndicator';\nimport { ChatThinkingDisplay } from './ChatThinkingDisplay';\nimport {\n chatBubbleRowVariants,\n chatBubbleVariants,\n chatAvatarSlotVariants,\n chatErrorActionsVariants,\n} from './ChatInterface.styles';\nimport type {\n ChatAssistantMessage,\n ChatAssistantRenderer,\n ChatMessage,\n ChatSystemMessage,\n ChatUserMessage,\n ChatUserRenderer,\n} from './ChatInterface.types';\n\n/**\n * Single message bubble. Memoized with a custom comparator covering all visual-relevant fields.\n *\n * IMMUTABILITY: consumers MUST pass a new message object when content/parts/status/errorMessage/createdAt changes.\n * In-place mutation will not re-render. (Documented on the public `messages` prop.)\n */\n\ninterface BubbleProps {\n message: ChatMessage;\n isStreamingHere: boolean;\n isOffscreen: boolean;\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n thinkingDisclosureLabel: string;\n contentRef?: React.RefCallback<HTMLDivElement>;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n}\n\nfunction shouldSkipRender(prev: BubbleProps, next: BubbleProps): boolean {\n if (prev.isStreamingHere !== next.isStreamingHere) return false;\n if (prev.isOffscreen !== next.isOffscreen) return false;\n if (prev.thinkingDisclosureLabel !== next.thinkingDisclosureLabel) return false;\n if (prev.assistantAvatar !== next.assistantAvatar) return false;\n if (prev.userAvatar !== next.userAvatar) return false;\n if (prev.renderAssistantContent !== next.renderAssistantContent) return false;\n if (prev.renderUserContent !== next.renderUserContent) return false;\n if (prev.onRetry !== next.onRetry) return false;\n if (prev.onEscalate !== next.onEscalate) return false;\n if (prev.contentRef !== next.contentRef) return false;\n\n const a = prev.message;\n const b = next.message;\n if (a === b) return true;\n if (a.id !== b.id) return false;\n if (a.role !== b.role) return false;\n if (a.status !== b.status) return false;\n if (a.createdAt !== b.createdAt) return false;\n // role-specific\n if (a.role === 'assistant' && b.role === 'assistant') {\n if (a.content !== b.content) return false;\n if (a.errorMessage !== b.errorMessage) return false;\n // Shallow-equal parts: same length AND same kind/status/text/name/summary fields.\n if ((a.parts?.length ?? 0) !== (b.parts?.length ?? 0)) return false;\n if (a.parts && b.parts) {\n for (let i = 0; i < a.parts.length; i++) {\n const pa = a.parts[i];\n const pb = b.parts[i];\n if (!pa || !pb) return false;\n if (pa.kind !== pb.kind) return false;\n if (pa.kind === 'text' && pb.kind === 'text') {\n if (pa.text !== pb.text) return false;\n } else if (pa.kind === 'tool_call' && pb.kind === 'tool_call') {\n if (pa.name !== pb.name) return false;\n if (pa.status !== pb.status) return false;\n if (pa.summary !== pb.summary) return false;\n }\n }\n }\n return true;\n }\n if (a.role === 'user' && b.role === 'user') {\n return a.content === b.content;\n }\n if (a.role === 'system' && b.role === 'system') {\n return a.content === b.content && a.announce === b.announce;\n }\n return false;\n}\n\nconst ChatMessageBubble = memo(function ChatMessageBubble(props: BubbleProps): ReactNode {\n // Reduced-motion is read inside the memo'd component. Hooks always run on render, and\n // useReducedMotion returns a stable value backed by a media-query subscription — so the\n // bubble re-renders if the OS preference flips, even though prefersReducedMotion isn't a prop.\n const prefersReducedMotion = useReducedMotion();\n const {\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n userAvatar,\n renderAssistantContent,\n renderUserContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n } = props;\n\n if (message.role === 'system') {\n return renderSystem(message);\n }\n if (message.role === 'user') {\n return renderUser(message, userAvatar, renderUserContent, isOffscreen);\n }\n return renderAssistant({\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n renderAssistantContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n prefersReducedMotion,\n });\n}, shouldSkipRender);\n\nChatMessageBubble.displayName = 'ChatMessageBubble';\n\nfunction renderSystem(message: ChatSystemMessage): ReactNode {\n return (\n <div className={chatBubbleRowVariants({ align: 'system', offscreen: false })}>\n <div className={chatBubbleVariants({ role: 'system' })} data-message-id={message.id}>\n {message.content}\n </div>\n </div>\n );\n}\n\nfunction renderUser(\n message: ChatUserMessage,\n avatar: ReactNode | undefined,\n render: ChatUserRenderer | undefined,\n isOffscreen: boolean\n): ReactNode {\n const content = render ? (\n render(message)\n ) : (\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n );\n return (\n <div className={chatBubbleRowVariants({ align: 'user', offscreen: isOffscreen })}>\n {avatar ? <div className={chatAvatarSlotVariants({ role: 'user' })}>{avatar}</div> : null}\n <div\n className={cn(\n chatBubbleVariants({ role: 'user', isError: message.status === 'error' }),\n 'order-1'\n )}\n data-message-id={message.id}\n >\n {content}\n </div>\n </div>\n );\n}\n\nfunction renderAssistant(args: {\n message: ChatAssistantMessage;\n isStreamingHere: boolean;\n isOffscreen: boolean;\n assistantAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n thinkingDisclosureLabel: string;\n contentRef?: React.RefCallback<HTMLDivElement>;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n prefersReducedMotion: boolean;\n}): ReactNode {\n const {\n message,\n isStreamingHere,\n isOffscreen,\n assistantAvatar,\n renderAssistantContent,\n thinkingDisclosureLabel,\n contentRef,\n onRetry,\n onEscalate,\n prefersReducedMotion,\n } = args;\n\n const isError = message.status === 'error';\n const isThinking = message.status === 'thinking';\n // Under reduced-motion, partial streaming content (which shifts layout token-by-token) is\n // suppressed. The static \"Generating response…\" placeholder + non-animated typing dots signal\n // activity without the layout churn. Final content snaps in when status flips to 'complete'.\n // Thinking is unaffected — it already renders static parts + a (gated) pill.\n const suppressStreamingContent =\n isStreamingHere && !isThinking && prefersReducedMotion && message.status === 'streaming';\n\n // Plain-text fallback: render markdown source verbatim.\n const renderedContent = renderAssistantContent ? (\n renderAssistantContent(message)\n ) : (\n <div className=\"whitespace-pre-wrap text-sm\">{message.content}</div>\n );\n\n // Skip content-visibility on the streaming bubble (its height changes constantly).\n const skipOffscreen = isStreamingHere || isThinking;\n\n return (\n <div className={chatBubbleRowVariants({ align: 'assistant', offscreen: isOffscreen && !skipOffscreen })}>\n {assistantAvatar ? (\n <div className={chatAvatarSlotVariants({ role: 'assistant' })}>{assistantAvatar}</div>\n ) : null}\n <div\n className={cn(chatBubbleVariants({ role: 'assistant', isError }), 'flex flex-col gap-1')}\n data-message-id={message.id}\n data-status={message.status}\n >\n {/* Thinking parts — active mode while thinking, collapsed disclosure once response begins */}\n {message.parts && message.parts.length > 0 && (\n <ChatThinkingDisplay\n parts={message.parts}\n mode={isThinking ? 'active' : 'collapsed'}\n disclosureLabel={thinkingDisclosureLabel}\n />\n )}\n\n {/* Main content. Hidden mid-stream under reduced-motion; the contentRef is still attached\n so the completion announcement can read innerText once the message snaps to complete. */}\n {!isThinking && !suppressStreamingContent && (\n <div ref={contentRef} data-content>\n {renderedContent}\n </div>\n )}\n\n {/* Reduced-motion placeholder while content is suppressed */}\n {suppressStreamingContent && (\n <div className=\"text-sm text-[var(--muted-foreground)]\" data-streaming-placeholder>\n Generating response…\n </div>\n )}\n\n {/* Typing dots ONLY during active token streaming (not during thinking) */}\n {isStreamingHere && !isThinking && <ChatTypingIndicator />}\n\n {/* Error: show errorMessage + retry/escalate actions */}\n {isError && (\n <>\n {message.errorMessage ? (\n <p className=\"text-xs text-[var(--content-foreground)]\">{message.errorMessage}</p>\n ) : null}\n <div className={chatErrorActionsVariants()}>\n {onRetry ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onPress={() => onRetry(message.id)}\n data-action=\"retry\"\n >\n Try again\n </Button>\n ) : null}\n {onEscalate ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onPress={onEscalate}\n data-action=\"escalate\"\n >\n Submit a support request\n </Button>\n ) : null}\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\nexport { ChatMessageBubble };\n","'use client';\n\nimport { forwardRef, useCallback, useMemo, useRef, type ReactNode } from 'react';\nimport { cn } from '../../utils/cn';\nimport { chatLogVariants } from './ChatInterface.styles';\nimport { ChatMessageBubble } from './ChatMessageBubble';\nimport type {\n ChatAssistantRenderer,\n ChatMessage,\n ChatUserRenderer,\n} from './ChatInterface.types';\n\n\n/**\n * The transcript region. `role=\"log\"` + `aria-label` + `tabIndex={0}` for keyboard scroll.\n * NO `aria-live` — all AT announcements flow through ChatLiveRegions.\n *\n * Maintains a markdown-render cache keyed on `(message.id, content)` to skip re-running\n * `renderAssistantContent` for completed messages whose content hasn't changed.\n * Bounded LRU (max 200 entries) using Map insertion order: delete + set on touch moves to tail.\n */\ninterface ChatMessageListProps {\n messages: ChatMessage[];\n streamingMessageId: string | null;\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n thinkingDisclosureLabel: string;\n logLabel: string;\n /** Rendered inside the log when messages is empty — keeps the landmark stable across empty→populated. */\n emptyState?: ReactNode;\n onRetry?: (id: string) => void;\n onEscalate?: () => void;\n /** Receives the latest assistant content node when its message id flips to streaming/complete. */\n registerStreamingContentRef?: (id: string, node: HTMLDivElement | null) => void;\n bottomSentinelRef: React.RefObject<HTMLDivElement | null>;\n className?: string;\n testId?: string;\n}\n\nconst CACHE_LIMIT = 200;\n\nconst ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(\n function ChatMessageList(\n {\n messages,\n streamingMessageId,\n assistantAvatar,\n userAvatar,\n renderAssistantContent,\n renderUserContent,\n thinkingDisclosureLabel,\n logLabel,\n emptyState,\n onRetry,\n onEscalate,\n registerStreamingContentRef,\n bottomSentinelRef,\n className,\n testId,\n },\n ref\n ) {\n // Mark roughly the last few messages as \"onscreen-most-likely\" — they skip content-visibility.\n // Crude heuristic: only the last 5 messages are likely visible at any moment.\n const onscreenIds = useMemo(() => {\n const ids = new Set<string>();\n for (let i = Math.max(0, messages.length - 5); i < messages.length; i++) {\n const m = messages[i];\n if (m) ids.add(m.id);\n }\n return ids;\n }, [messages]);\n\n // LRU cache for assistant content nodes (used by the assistant render-prop path).\n // Lives on a ref; pruned on every render that looks up a new key.\n const cacheRef = useRef<Map<string, { content: string; node: ReactNode }>>(new Map());\n\n // The wrapper passes a memoised renderer that consults the cache. If the upstream\n // renderer reference changes we invalidate the cache wholesale.\n const lastRendererRef = useRef<ChatAssistantRenderer | undefined>(undefined);\n if (lastRendererRef.current !== renderAssistantContent) {\n cacheRef.current.clear();\n lastRendererRef.current = renderAssistantContent;\n }\n\n const cachedRenderAssistant: ChatAssistantRenderer | undefined = useMemo(() => {\n if (!renderAssistantContent) return undefined;\n return (message): ReactNode => {\n const cache = cacheRef.current;\n const hit = cache.get(message.id);\n if (hit && hit.content === message.content) {\n // Move to tail (LRU touch).\n cache.delete(message.id);\n cache.set(message.id, hit);\n return hit.node;\n }\n const node = renderAssistantContent(message);\n cache.set(message.id, { content: message.content, node });\n // Evict oldest if over cap.\n if (cache.size > CACHE_LIMIT) {\n const oldest = cache.keys().next().value;\n if (oldest !== undefined) cache.delete(oldest);\n }\n return node;\n };\n }, [renderAssistantContent]);\n\n // Stable per-id ref callbacks. Without this we'd allocate a fresh arrow function for every\n // assistant bubble on every parent render, busting the bubble's memo via prop identity change.\n // The Map is keyed by id; entries are GC'd alongside `streamingContentRefs` in the parent\n // when messages are removed (the ref callback fires with `null` on unmount, deleting itself).\n const contentRefCacheRef = useRef(new Map<string, React.RefCallback<HTMLDivElement>>());\n const getContentRef = useCallback(\n (id: string): React.RefCallback<HTMLDivElement> | undefined => {\n if (!registerStreamingContentRef) return undefined;\n const cache = contentRefCacheRef.current;\n let cb = cache.get(id);\n if (!cb) {\n cb = (node: HTMLDivElement | null): void => {\n registerStreamingContentRef(id, node);\n if (node === null) cache.delete(id);\n };\n cache.set(id, cb);\n }\n return cb;\n },\n [registerStreamingContentRef]\n );\n\n return (\n <div\n ref={ref}\n role=\"log\"\n aria-label={logLabel}\n // log region must be keyboard-scrollable per WCAG 2.1.1; arrow keys then scroll inside.\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n className={cn(chatLogVariants(), className)}\n data-testid={testId}\n >\n {messages.length === 0 && emptyState ? (\n <div className=\"flex h-full items-center justify-center\">{emptyState}</div>\n ) : null}\n {messages.map((message) => {\n const isStreamingHere = streamingMessageId === message.id;\n const isOffscreen = !onscreenIds.has(message.id);\n const contentRef =\n message.role === 'assistant' ? getContentRef(message.id) : undefined;\n return (\n <ChatMessageBubble\n key={message.id}\n message={message}\n isStreamingHere={isStreamingHere}\n isOffscreen={isOffscreen}\n assistantAvatar={assistantAvatar}\n userAvatar={userAvatar}\n renderAssistantContent={cachedRenderAssistant}\n renderUserContent={renderUserContent}\n thinkingDisclosureLabel={thinkingDisclosureLabel}\n contentRef={contentRef}\n onRetry={onRetry}\n onEscalate={onEscalate}\n />\n );\n })}\n {/* Bottom sentinel for IntersectionObserver-based stuck-to-bottom detection */}\n <div ref={bottomSentinelRef} aria-hidden=\"true\" style={{ height: 1, flexShrink: 0 }} />\n </div>\n );\n }\n);\n\nexport { ChatMessageList };\n","'use client';\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\n\n/**\n * Strip Unicode that breaks AT announcements:\n * - C0 controls (U+0000–U+001F), DEL (U+007F), C1 controls (U+0080–U+009F)\n * - Zero-width (U+200B–U+200F)\n * - Bidi overrides (U+202A–U+202E)\n * - Isolates (U+2066–U+2069)\n * - BOM/ZWNBSP (U+FEFF)\n *\n * Then NFC-normalises and collapses whitespace.\n *\n * Built via RegExp constructor with Unicode escapes to avoid no-irregular-whitespace lint.\n */\nconst STRIP_PATTERN = new RegExp(\n '[' +\n '\\\\u0000-\\\\u001F' + // C0 controls\n '\\\\u007F' + // DEL\n '\\\\u0080-\\\\u009F' + // C1 controls\n '\\\\u200B-\\\\u200F' + // Zero-width\n '\\\\u202A-\\\\u202E' + // Bidi overrides\n '\\\\u2066-\\\\u2069' + // Isolates\n '\\\\uFEFF' + // BOM/ZWNBSP\n ']',\n 'g'\n);\n\nexport function normaliseAnnouncementText(input: string): string {\n if (!input) return '';\n let out = input.replace(STRIP_PATTERN, '');\n // NFC normalise so combining marks/composites are stable.\n if (typeof out.normalize === 'function') {\n out = out.normalize('NFC');\n }\n // Collapse runs of whitespace (including newlines from innerText) to single spaces.\n out = out.replace(/\\s+/g, ' ').trim();\n return out;\n}\n\n/**\n * Truncate to `maxChars`, suffixing with the supplied `suffix` if truncated.\n * `maxChars` is clamped to [50, 1000] before truncation.\n */\nexport function truncateAnnouncement(\n text: string,\n maxChars: number | undefined,\n suffix = '…full response in transcript'\n): string {\n const requested = typeof maxChars === 'number' ? maxChars : 200;\n const clamped = Math.min(Math.max(requested, 50), 1000);\n if (process.env.NODE_ENV !== 'production' && requested !== clamped) {\n console.warn(\n `[ChatInterface] completionAnnouncementMaxChars must be in [50, 1000]; got ${requested}, clamped to ${clamped}.`\n );\n }\n if (text.length <= clamped) return text;\n return `${text.slice(0, clamped).trimEnd()}${suffix ? ` ${suffix}` : ''}`;\n}\n\n/**\n * Detect whether `field-sizing: content` is supported (Chrome 123+/Safari 18.4+/Firefox 137+).\n * Computed once via `useState` initialiser. SSR-safe (returns false server-side).\n */\nfunction detectFieldSizingSupport(): boolean {\n if (typeof CSS === 'undefined' || typeof CSS.supports !== 'function') return false;\n try {\n return CSS.supports('field-sizing', 'content');\n } catch {\n return false;\n }\n}\n\n/**\n * Auto-resize a textarea based on its scrollHeight, capped at `--chat-input-max-lines × line-height`.\n * On browsers supporting `field-sizing: content`, the JS path is a no-op (CSS handles it).\n * On older browsers, the JS measurement is rAF-batched to collapse N keystrokes per frame into one reflow.\n */\nexport function useAutoResizeTextarea(\n textareaRef: React.RefObject<HTMLTextAreaElement | null>,\n value: string,\n maxLines = 6\n): void {\n const [supportsFieldSizing] = useState(detectFieldSizingSupport);\n const pendingRafRef = useRef<number | null>(null);\n\n const resize = useCallback(() => {\n if (supportsFieldSizing) return;\n const el = textareaRef.current;\n if (!el) return;\n if (pendingRafRef.current !== null) return;\n pendingRafRef.current = requestAnimationFrame(() => {\n pendingRafRef.current = null;\n const node = textareaRef.current;\n if (!node) return;\n const computed = window.getComputedStyle(node);\n const lineHeight = parseFloat(computed.lineHeight);\n const maxHeight = Number.isFinite(lineHeight) ? lineHeight * maxLines : 0;\n node.style.height = 'auto';\n const next = maxHeight > 0 ? Math.min(node.scrollHeight, maxHeight) : node.scrollHeight;\n node.style.height = `${next}px`;\n node.style.overflowY = node.scrollHeight > next ? 'auto' : 'hidden';\n });\n }, [textareaRef, supportsFieldSizing, maxLines]);\n\n // Re-measure when value changes.\n useLayoutEffect(() => {\n resize();\n }, [value, resize]);\n\n // Cleanup any pending rAF on unmount.\n useEffect(() => {\n return (): void => {\n if (pendingRafRef.current !== null) {\n cancelAnimationFrame(pendingRafRef.current);\n pendingRafRef.current = null;\n }\n };\n }, []);\n}\n\n/**\n * Track whether the user is \"stuck to bottom\" using an IntersectionObserver on a sentinel element\n * at the bottom of the log. No scroll listener — browser-native.\n *\n * `rootMargin` is wide (256px) to tolerate scrollHeight churn from `content-visibility: auto`\n * placeholders being measured as bubbles scroll into view.\n *\n * Returns:\n * - `isAtBottom` — true if the bottom sentinel is intersecting the root.\n * - `scrollToBottom(opts?)` — imperative scroll (respects reduced-motion).\n */\nexport function useTranscriptScroll(\n scrollContainerRef: React.RefObject<HTMLDivElement | null>,\n sentinelRef: React.RefObject<HTMLDivElement | null>,\n prefersReducedMotion: boolean\n): {\n isAtBottom: boolean;\n scrollToBottom: (opts?: { smooth?: boolean }) => void;\n} {\n const [isAtBottom, setIsAtBottom] = useState(true);\n\n useEffect(() => {\n const root = scrollContainerRef.current;\n const target = sentinelRef.current;\n if (!root || !target) return;\n if (typeof IntersectionObserver === 'undefined') return;\n\n const obs = new IntersectionObserver(\n (entries) => {\n const last = entries[entries.length - 1];\n if (last) setIsAtBottom(last.isIntersecting);\n },\n { root, rootMargin: '0px 0px 256px 0px', threshold: 0 }\n );\n obs.observe(target);\n return (): void => obs.disconnect();\n }, [scrollContainerRef, sentinelRef]);\n\n const scrollToBottom = useCallback(\n (opts?: { smooth?: boolean }) => {\n const target = sentinelRef.current;\n if (!target) return;\n const smooth = opts?.smooth && !prefersReducedMotion;\n target.scrollIntoView({ behavior: smooth ? 'smooth' : 'auto', block: 'end' });\n },\n [sentinelRef, prefersReducedMotion]\n );\n\n return { isAtBottom, scrollToBottom };\n}\n\n/**\n * Bounded \"still working\" announcement timer.\n *\n * Starts when `isStreaming` becomes true. Pings at 3s, 15s, 60s elapsed silence.\n * - Gated on `document.visibilityState === 'visible'`.\n * - Capped at 3 announcements per stream.\n * - Resets on `streamingContentLength` increase (token arrival is the signal of progress).\n * - Suppressed when `enabled` is false.\n *\n * Implementation: a single long-lived timer. Tokens update `lastTokenAtRef` via render-time read;\n * when the timer fires, it checks elapsed silence — if not enough, it re-arms with the remaining\n * delta. This avoids clear+rearm thrash at high token rates (60Hz × 2 syscalls/tick = 120 timer\n * ops/sec under naive implementations).\n */\nconst STILL_WORKING_INTERVALS_MS = [3000, 15000, 60000];\n\nexport function useStillWorkingTimer(args: {\n isStreaming: boolean;\n streamingContentLength: number;\n enabled: boolean;\n message: string;\n onAnnounce: (text: string) => void;\n}): void {\n const { isStreaming, streamingContentLength, enabled, message, onAnnounce } = args;\n const lastTokenAtRef = useRef<number>(0);\n const announcementCountRef = useRef<number>(0);\n const lastLengthRef = useRef<number>(streamingContentLength);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const messageRef = useRef(message);\n const onAnnounceRef = useRef(onAnnounce);\n\n // Update token timestamp on length increase (cheap render-time bookkeeping).\n if (streamingContentLength > lastLengthRef.current) {\n lastLengthRef.current = streamingContentLength;\n lastTokenAtRef.current = Date.now();\n }\n\n const clearTimer = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n // Keep latest message/onAnnounce in refs so the long-lived timer always uses current values.\n useEffect(() => {\n messageRef.current = message;\n onAnnounceRef.current = onAnnounce;\n });\n\n // Single timer effect: starts on stream start, self-rearms until cap, tears down on stop.\n useEffect(() => {\n if (!isStreaming || !enabled) {\n clearTimer();\n announcementCountRef.current = 0;\n lastLengthRef.current = 0;\n lastTokenAtRef.current = Date.now();\n return clearTimer;\n }\n\n lastTokenAtRef.current = Date.now();\n\n const arm = (): void => {\n const idx = announcementCountRef.current;\n if (idx >= STILL_WORKING_INTERVALS_MS.length) return;\n const targetDelay = STILL_WORKING_INTERVALS_MS[idx];\n if (targetDelay === undefined) return;\n const elapsed = Date.now() - lastTokenAtRef.current;\n const remaining = Math.max(targetDelay - elapsed, 0);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n const sinceToken = Date.now() - lastTokenAtRef.current;\n const idxNow = announcementCountRef.current;\n const delayNow = STILL_WORKING_INTERVALS_MS[idxNow];\n if (delayNow === undefined) return;\n if (sinceToken < delayNow) {\n // Token arrived during the wait — reset with the remaining delta.\n arm();\n return;\n }\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {\n // Skip this firing but advance the count so we don't loop.\n announcementCountRef.current = idxNow + 1;\n arm();\n return;\n }\n announcementCountRef.current = idxNow + 1;\n onAnnounceRef.current(messageRef.current);\n arm();\n }, remaining);\n };\n\n arm();\n return clearTimer;\n // We only care about start/stop transitions — token-driven rearming is handled inside `arm`\n // via the `lastTokenAtRef` read, NOT by re-running this effect.\n }, [isStreaming, enabled, clearTimer]);\n}\n\n/**\n * Status region announcement queue.\n *\n * Provides:\n * - `current` — the text currently in the live region.\n * - `enqueue(text)` — push a ping. Identical text within `dedupWindowMs` is dropped.\n *\n * Each enqueued text holds for `holdMs` (default 200ms — older AT like NVDA/Orca poll live regions\n * every ~80–150ms; sub-150ms holds risk skipped announcements). Sequential distinct pings are\n * spaced at least `minIntervalMs` apart. Queue is capped at `maxQueueSize` (default 32) to prevent\n * unbounded memory growth on misbehaving callers.\n */\nconst DEFAULT_HOLD_MS = 200;\nconst DEFAULT_MAX_QUEUE = 32;\n\nexport function useAnnouncementQueue(opts?: {\n minIntervalMs?: number;\n dedupWindowMs?: number;\n holdMs?: number;\n maxQueueSize?: number;\n}): { current: string; enqueue: (text: string) => void } {\n const minIntervalMs = opts?.minIntervalMs ?? 500;\n const dedupWindowMs = opts?.dedupWindowMs ?? 1000;\n const holdMs = opts?.holdMs ?? DEFAULT_HOLD_MS;\n const maxQueueSize = opts?.maxQueueSize ?? DEFAULT_MAX_QUEUE;\n const [current, setCurrent] = useState('');\n const queueRef = useRef<string[]>([]);\n const lastFiredAtRef = useRef<number>(0);\n const lastTextRef = useRef<string>('');\n const lastTextAtRef = useRef<number>(0);\n const flushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Guards async timer callbacks from running after unmount.\n const mountedRef = useRef(true);\n\n const flush = useCallback(() => {\n if (flushTimerRef.current !== null) return;\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n const next = queueRef.current.shift();\n if (next === undefined) {\n setCurrent('');\n return;\n }\n const now = Date.now();\n const sinceLast = now - lastFiredAtRef.current;\n if (sinceLast < minIntervalMs) {\n // Re-queue at the front and try again after the gap.\n queueRef.current.unshift(next);\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n flush();\n }, minIntervalMs - sinceLast);\n return;\n }\n lastFiredAtRef.current = now;\n lastTextRef.current = next;\n lastTextAtRef.current = now;\n setCurrent(next);\n // Clear after the hold so AT polling has time to catch the change.\n flushTimerRef.current = setTimeout(() => {\n flushTimerRef.current = null;\n if (!mountedRef.current) return;\n setCurrent('');\n if (queueRef.current.length > 0) flush();\n }, holdMs);\n }, 0);\n }, [minIntervalMs, holdMs]);\n\n const enqueue = useCallback(\n (text: string) => {\n if (!text) return;\n const now = Date.now();\n // Dedup: drop if identical to the last fired text within the window.\n if (text === lastTextRef.current && now - lastTextAtRef.current < dedupWindowMs) {\n return;\n }\n // Cap: drop oldest queued ping if the queue is full. The current pending fire is unaffected.\n if (queueRef.current.length >= maxQueueSize) {\n queueRef.current.shift();\n }\n queueRef.current.push(text);\n flush();\n },\n [dedupWindowMs, flush, maxQueueSize]\n );\n\n // Cleanup\n useEffect(() => {\n return (): void => {\n mountedRef.current = false;\n if (flushTimerRef.current !== null) {\n clearTimeout(flushTimerRef.current);\n flushTimerRef.current = null;\n }\n };\n }, []);\n\n return { current, enqueue };\n}\n","'use client';\n\nimport {\n forwardRef,\n useCallback,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n} from 'react';\nimport { cn } from '../../utils/cn';\nimport { useAutoResizeTextarea } from './ChatInterface.hooks';\nimport {\n chatInputTextareaVariants,\n chatInputWrapperVariants,\n} from './ChatInterface.styles';\nimport type {\n ChatInputComponentHandle,\n ChatInputComponentProps,\n} from './ChatInterface.types';\n\n/**\n * Default ChatInterface input.\n *\n * - Multi-line textarea (Enter sends, Shift+Enter newlines).\n * - `field-sizing: content` primary path; rAF-batched JS fallback for older browsers.\n * - IME-safe Enter handling: checks `event.isComposing || event.keyCode === 229`.\n * - `autoComplete=\"off\"` for privacy (no draft persistence in browser autofill).\n * - Implements the ChatInputComponent contract: focus / clear / submit / setValue.\n *\n * Accepts `sendShortcut` ('enter' | 'mod-enter') as a prop (passed by ChatInputArea).\n */\n\ninterface DefaultChatInputOwnProps extends ChatInputComponentProps {\n sendShortcut?: 'enter' | 'mod-enter';\n}\n\nconst DefaultChatInput = forwardRef<ChatInputComponentHandle, DefaultChatInputOwnProps>(\n function DefaultChatInput(\n {\n isDisabled,\n placeholder,\n maxLength,\n 'aria-label': ariaLabel,\n onSubmit,\n onChange,\n onFocus,\n onBlur,\n sendShortcut = 'enter',\n },\n ref\n ) {\n const [value, setValueState] = useState('');\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useAutoResizeTextarea(textareaRef, value);\n\n // Refs that track the latest value/disabled/onSubmit/onChange so the imperative handle's\n // closures see the current values without re-creating the handle.\n //\n // Writes happen in useLayoutEffect — NOT during render — to be safe under React 19 concurrent\n // rendering, where renders may be discarded before commit. useLayoutEffect runs synchronously\n // after commit, before any imperative-handle method could fire (which always originates from\n // a user event after paint).\n const valueRef = useRef(value);\n const isDisabledRef = useRef(isDisabled);\n const onSubmitRef = useRef(onSubmit);\n const onChangeRef = useRef(onChange);\n useLayoutEffect(() => {\n valueRef.current = value;\n isDisabledRef.current = isDisabled;\n onSubmitRef.current = onSubmit;\n onChangeRef.current = onChange;\n });\n\n const handleSubmit = useCallback((): void => {\n if (isDisabledRef.current) return;\n const v = valueRef.current;\n if (v.trim().length === 0) return;\n onSubmitRef.current(v);\n }, []);\n\n useImperativeHandle(\n ref,\n () => ({\n focus: (): void => {\n textareaRef.current?.focus();\n // Place cursor at end if there's existing text.\n const el = textareaRef.current;\n if (el) {\n const len = el.value.length;\n el.setSelectionRange(len, len);\n }\n },\n clear: (): void => {\n setValueState('');\n onChangeRef.current?.('');\n },\n submit: (): void => {\n handleSubmit();\n },\n setValue: (markdown: string): void => {\n setValueState(markdown);\n onChangeRef.current?.(markdown);\n // Cursor at end on next paint.\n requestAnimationFrame(() => {\n const el = textareaRef.current;\n if (!el) return;\n const len = el.value.length;\n el.setSelectionRange(len, len);\n });\n },\n }),\n [handleSubmit]\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>): void => {\n // IME composition guard: use event.isComposing OR keyCode === 229 (Chromium ordering bug\n // means a state-flag tracking compositionstart/end is unreliable).\n const isComposing =\n (e.nativeEvent as KeyboardEvent['nativeEvent'] & { isComposing?: boolean }).isComposing ||\n e.keyCode === 229;\n if (e.key !== 'Enter') return;\n if (isComposing) return;\n if (e.shiftKey) return; // Shift+Enter = newline\n if (sendShortcut === 'mod-enter') {\n if (!(e.metaKey || e.ctrlKey)) return;\n }\n e.preventDefault();\n handleSubmit();\n },\n [handleSubmit, sendShortcut]\n );\n\n return (\n <div className={chatInputWrapperVariants({ isDisabled })}>\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e): void => {\n setValueState(e.target.value);\n onChange?.(e.target.value);\n }}\n onKeyDown={handleKeyDown}\n onFocus={onFocus}\n onBlur={onBlur}\n disabled={isDisabled}\n placeholder={placeholder}\n maxLength={maxLength}\n autoComplete=\"off\"\n aria-label={ariaLabel ?? 'Message'}\n rows={1}\n className={cn(chatInputTextareaVariants())}\n />\n </div>\n );\n }\n);\n\nexport { DefaultChatInput };\n","'use client';\n\nimport { useCallback, useRef, type ReactNode } from 'react';\nimport { Send, StopCircle } from 'lucide-react';\nimport { Button } from '../Button/Button';\nimport { cn } from '../../utils/cn';\nimport { chatInputAreaVariants } from './ChatInterface.styles';\nimport { DefaultChatInput } from './DefaultChatInput';\nimport type {\n ChatInputComponent,\n ChatInputComponentHandle,\n ChatTopLevelStatus,\n} from './ChatInterface.types';\n\n/**\n * Wraps the (default or custom) input + Send/Stop button + inputActions slot.\n *\n * The Send button is ALWAYS enabled when the input area isn't disabled — it does not gate on\n * input emptiness. The contract guarantees `submit()` is no-op on empty input, so harmless\n * clicks on an empty input produce no message.\n *\n * `isDisabled` truth table (forwarded to the input):\n * - consumer.isDisabled === true → true\n * - status === 'streaming' → true\n * - status === 'error' → true\n * - otherwise → false\n *\n * Stop button is shown only when status === 'streaming'.\n */\ninterface ChatInputAreaProps {\n status: ChatTopLevelStatus;\n consumerDisabled: boolean;\n inputComponent?: ChatInputComponent;\n placeholder?: string;\n maxInputLength?: number;\n sendShortcut: 'enter' | 'mod-enter';\n inputActions?: ReactNode;\n inputAriaLabel: string;\n /** Forwarded to consumer's `onSendMessage`. ChatInterface enforces maxInputLength backstop here. */\n onSendMessage: (text: string) => void;\n onStopGeneration?: () => void;\n /** Receives the input handle so the parent can implement `chatRef.current.focusInput()`. */\n registerInputHandle: (handle: ChatInputComponentHandle | null) => void;\n /** Tells ChatInterface that an `error` → `idle` transition just happened so it can move focus. */\n className?: string;\n}\n\nexport function ChatInputArea({\n status,\n consumerDisabled,\n inputComponent,\n placeholder,\n maxInputLength,\n sendShortcut,\n inputActions,\n inputAriaLabel,\n onSendMessage,\n onStopGeneration,\n registerInputHandle,\n className,\n}: ChatInputAreaProps): React.ReactElement {\n // The forwarded isDisabled per the truth table.\n const inputIsDisabled = consumerDisabled || status === 'streaming' || status === 'error';\n\n const inputHandleRef = useRef<ChatInputComponentHandle | null>(null);\n\n const setInputHandle = useCallback(\n (h: ChatInputComponentHandle | null): void => {\n inputHandleRef.current = h;\n registerInputHandle(h);\n },\n [registerInputHandle]\n );\n\n // Wired onSubmit: ChatInterface enforces maxInputLength as defence-in-depth and\n // calls consumer's onSendMessage + clears the input.\n const handleSubmitFromInput = useCallback(\n (markdown: string): void => {\n let toSend = markdown;\n if (typeof maxInputLength === 'number' && toSend.length > maxInputLength) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ChatInterface] Input exceeded maxInputLength (${maxInputLength}); truncating before send.`\n );\n }\n toSend = toSend.slice(0, maxInputLength);\n }\n onSendMessage(toSend);\n inputHandleRef.current?.clear();\n },\n [maxInputLength, onSendMessage]\n );\n\n const handleSendClick = useCallback((): void => {\n inputHandleRef.current?.submit();\n }, []);\n\n const handleStopClick = useCallback((): void => {\n onStopGeneration?.();\n }, [onStopGeneration]);\n\n const isStreaming = status === 'streaming';\n\n // Render the chosen input.\n const InputComp = inputComponent;\n\n return (\n <div className={cn(chatInputAreaVariants(), className)}>\n <div className=\"flex-1 min-w-0\">\n {InputComp ? (\n <InputComp\n ref={setInputHandle}\n isDisabled={inputIsDisabled}\n placeholder={placeholder}\n maxLength={maxInputLength}\n aria-label={inputAriaLabel}\n onSubmit={handleSubmitFromInput}\n />\n ) : (\n <DefaultChatInput\n ref={setInputHandle}\n isDisabled={inputIsDisabled}\n placeholder={placeholder}\n maxLength={maxInputLength}\n aria-label={inputAriaLabel}\n onSubmit={handleSubmitFromInput}\n sendShortcut={sendShortcut}\n />\n )}\n </div>\n\n {inputActions ? <div className=\"flex-shrink-0\">{inputActions}</div> : null}\n\n {isStreaming ? (\n <Button\n variant=\"destructive\"\n visualSize=\"icon\"\n aria-label=\"Stop generating\"\n onPress={handleStopClick}\n data-action=\"stop\"\n >\n <StopCircle aria-hidden=\"true\" />\n </Button>\n ) : (\n <Button\n variant=\"default\"\n visualSize=\"icon\"\n aria-label=\"Send message\"\n isDisabled={consumerDisabled}\n onPress={handleSendClick}\n data-action=\"send\"\n >\n <Send aria-hidden=\"true\" />\n </Button>\n )}\n </div>\n );\n}\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type {\n ForwardRefExoticComponent,\n ReactNode,\n RefAttributes,\n} from 'react';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * ChatInterface — public types and Zod schemas.\n *\n * Schemas use `.strict()` so unknown keys throw rather than strip silently.\n * Production callers should NEVER call `.parse()` directly — internal validation\n * is gated on `process.env.NODE_ENV !== 'production'` so tsup tree-shakes it out.\n */\n\n// ============================================================================\n// Roles & statuses\n// ============================================================================\n\nexport const ChatRoleSchema = z.enum(['user', 'assistant', 'system']);\nexport type ChatRole = z.infer<typeof ChatRoleSchema>;\n\nexport const ChatMessageStatusSchema = z.enum([\n 'complete',\n 'streaming',\n 'thinking',\n 'error',\n]);\nexport type ChatMessageStatus = z.infer<typeof ChatMessageStatusSchema>;\n\nexport const ChatTopLevelStatusSchema = z.enum(['idle', 'streaming', 'error']);\nexport type ChatTopLevelStatus = z.infer<typeof ChatTopLevelStatusSchema>;\n\n// ============================================================================\n// Thinking / tool-call parts\n// ============================================================================\n\nexport const ThinkingPartSchema = z\n .discriminatedUnion('kind', [\n z\n .object({\n kind: z.literal('text'),\n text: z.string(),\n })\n .strict(),\n z\n .object({\n kind: z.literal('tool_call'),\n name: z.string(),\n status: z.enum(['running', 'done', 'error']),\n summary: z.string().optional(),\n })\n .strict(),\n ]);\n\nexport type ThinkingPart = z.infer<typeof ThinkingPartSchema>;\n\n// ============================================================================\n// Message variants (discriminated union over `role`)\n// ============================================================================\n\nconst ChatUserMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('user'),\n /** User messages never stream or think. */\n status: z.enum(['complete', 'error']),\n content: z.string(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport type ChatUserMessage = z.infer<typeof ChatUserMessageSchema>;\n\nconst ChatAssistantMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('assistant'),\n status: ChatMessageStatusSchema,\n /** Markdown source text. ChatInterface treats it as opaque — consumer renders. */\n content: z.string(),\n /** Visible thinking / tool-call parts. */\n parts: z.array(ThinkingPartSchema).optional(),\n /** Free-form error detail when status === 'error'. Never interpolated into AT announcements. */\n errorMessage: z.string().optional(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport type ChatAssistantMessage = z.infer<typeof ChatAssistantMessageSchema>;\n\n/**\n * System messages bypass `renderAssistantContent` / `renderUserContent`.\n * `content` is `ReactNode` (not validated by Zod beyond presence) so consumers\n * can drop in formatted blocks (\"Conversation started\", session expiry notes).\n */\nexport interface ChatSystemMessage {\n id: string;\n role: 'system';\n status: 'complete';\n content: ReactNode;\n /** Default false — system messages are visual-only. Set true to announce via the status region. */\n announce?: boolean;\n createdAt?: number;\n}\n\nconst ChatSystemMessageSchema = z\n .object({\n id: z.string(),\n role: z.literal('system'),\n status: z.literal('complete'),\n // ReactNode isn't validatable by Zod — accept anything; runtime check only verifies role+status.\n content: z.unknown(),\n announce: z.boolean().optional(),\n createdAt: z.number().optional(),\n })\n .strict();\n\nexport const ChatMessageSchema = z.union([\n ChatUserMessageSchema,\n ChatAssistantMessageSchema,\n ChatSystemMessageSchema,\n]);\n\nexport type ChatMessage =\n | ChatUserMessage\n | ChatAssistantMessage\n | ChatSystemMessage;\n\n// ============================================================================\n// Pluggable input contract\n// ============================================================================\n\n/**\n * Props ChatInterface passes to the input component (default OR custom).\n *\n * Trust boundary: the input emits `markdown: string` via `onSubmit`. ChatInterface\n * treats it as opaque and forwards to `props.onSendMessage`. Sanitisation is the\n * consumer's responsibility (in `renderAssistantContent` / `renderUserContent`).\n */\nexport interface ChatInputComponentProps {\n /** True while top-level status is streaming/error or consumer-disabled. Custom inputs MUST honour. */\n isDisabled: boolean;\n placeholder?: string;\n /** Best-effort cap. Default input enforces strictly; ChatInterface enforces a defence-in-depth backstop. */\n maxLength?: number;\n 'aria-label'?: string;\n /**\n * Called when the user confirms (Enter, Cmd+Enter, RTE submit, etc.).\n * The string MUST be markdown. Must NOT be called with empty/whitespace.\n */\n onSubmit: (markdown: string) => void;\n /**\n * Optional. Fires on value changes for char-count UX or analytics.\n * Custom inputs MAY debounce or throttle. ChatInterface does NOT use this to drive Send button state —\n * Send is always enabled when ChatInterface isn't disabled.\n */\n onChange?: (markdown: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n}\n\n/**\n * Imperative methods the input must expose via `forwardRef` + `useImperativeHandle`.\n */\nexport interface ChatInputComponentHandle {\n /** Move focus to the input. ChatInterface delegates `chatRef.current.focusInput()` here. */\n focus: () => void;\n /** Reset the input's value. Called after a successful send. */\n clear: () => void;\n /**\n * Trigger a submit. The input extracts its current value and invokes its own `onSubmit`.\n * No-op when empty/whitespace — never calls `onSubmit` with empty string.\n * This empty-guard is the contract: Send button is always enabled when ChatInterface isn't disabled,\n * and harmless clicks on an empty input produce no message.\n */\n submit: () => void;\n /**\n * Programmatically set the input's value (suggested-question prefill, edit-prior-message).\n * Does NOT change focus. Cursor lands at end. Consumers wanting focus call `focus()` separately.\n * `setValue` followed by `submit` is a documented feature; consumers wanting human-only origination\n * should track origination state themselves.\n */\n setValue: (markdown: string) => void;\n}\n\nexport type ChatInputComponent = ForwardRefExoticComponent<\n ChatInputComponentProps & RefAttributes<ChatInputComponentHandle>\n>;\n\n// ============================================================================\n// Imperative handle on the root component\n// ============================================================================\n\nexport interface ChatInterfaceHandle {\n focusInput: () => void;\n scrollToBottom: (opts?: { smooth?: boolean }) => void;\n}\n\n// ============================================================================\n// Root props\n// ============================================================================\n\n/**\n * Zod schema for runtime validation. Production callers should not invoke `.parse()` —\n * internal validation is dev-only.\n */\nexport const ChatInterfacePropsSchema = BaseComponentPropsSchema.extend({\n // Required\n status: ChatTopLevelStatusSchema,\n\n // Inputs that aren't validatable by Zod (functions, ReactNode, components) accept `unknown`/`any`.\n messages: z.array(ChatMessageSchema),\n onSendMessage: z.custom<(text: string) => void>(),\n onStopGeneration: z.custom<() => void>().optional(),\n onRetry: z.custom<(messageId: string) => void>().optional(),\n onEscalate: z.custom<() => void>().optional(),\n\n renderAssistantContent: z.any().optional(),\n renderUserContent: z.any().optional(),\n\n inputComponent: z.any().optional(),\n\n assistantAvatar: z.any().optional(),\n userAvatar: z.any().optional(),\n\n header: z.any().optional(),\n emptyState: z.any().optional(),\n inputActions: z.any().optional(),\n\n placeholder: z.string().optional(),\n isDisabled: z.boolean().optional(),\n maxInputLength: z.number().int().positive().optional(),\n sendShortcut: z.enum(['enter', 'mod-enter']).optional(),\n\n progressAnnouncements: z.boolean().optional(),\n completionAnnouncementMaxChars: z.number().int().positive().optional(),\n\n logLabel: z.string().optional(),\n thinkingDisclosureLabel: z.string().optional(),\n}).strict();\n\n/**\n * Render-prop renderers. Consumers parse markdown and produce a ReactNode.\n * MUST be pure render functions — no side effects. MUST NOT include `aria-live`,\n * `aria-busy`, or `role=\"log\"` (ChatInterface owns the live-region semantics).\n */\nexport type ChatAssistantRenderer = (message: ChatAssistantMessage) => ReactNode;\nexport type ChatUserRenderer = (message: ChatUserMessage) => ReactNode;\n\nexport interface ChatInterfaceProps {\n // ---- Required state ----\n /**\n * The full conversation transcript. ChatInterface is fully controlled — the consumer\n * owns this array and updates it via setState/reducer/etc.\n *\n * IMMUTABILITY: Pass a new message object when its `content`, `parts`, `status`,\n * `errorMessage`, or `createdAt` changes. In-place mutation will not re-render.\n */\n messages: ChatMessage[];\n /** Top-level status drives the input area (Send vs Stop, disabled state). */\n status: ChatTopLevelStatus;\n /** Required when status can be 'streaming'. */\n onSendMessage: (text: string) => void;\n\n // ---- Optional callbacks ----\n onStopGeneration?: () => void;\n onRetry?: (messageId: string) => void;\n onEscalate?: () => void;\n\n // ---- Content rendering (markdown is consumer-rendered) ----\n /** @see ChatAssistantRenderer for purity rules. */\n renderAssistantContent?: ChatAssistantRenderer;\n renderUserContent?: ChatUserRenderer;\n\n // ---- Pluggable input ----\n /** Custom input component honouring the ChatInputComponent contract. Defaults to the built-in Themis-styled multi-line input. */\n inputComponent?: ChatInputComponent;\n\n // ---- Avatars ----\n /** Memoize inline JSX (e.g. `useMemo(() => <Avatar ... />, [])`) so it doesn't bust the bubble memo. */\n assistantAvatar?: ReactNode;\n userAvatar?: ReactNode;\n\n // ---- Slots ----\n header?: ReactNode;\n emptyState?: ReactNode;\n inputActions?: ReactNode;\n\n // ---- Input config ----\n placeholder?: string;\n isDisabled?: boolean;\n maxInputLength?: number;\n /** Default 'enter'. 'mod-enter' = Cmd/Ctrl+Enter. Only honoured by the default input. */\n sendShortcut?: 'enter' | 'mod-enter';\n\n // ---- Live region behaviour ----\n /**\n * Default true. When false, suppresses the bounded \"still working\" pings (3s/15s/60s slow-response safety net).\n * Note: disabling removes the only AT signal of a hung stream.\n */\n progressAnnouncements?: boolean;\n /** Default 200. Clamped to [50, 1000] at runtime; out-of-range values dev-warn and clamp. */\n completionAnnouncementMaxChars?: number;\n\n // ---- Labelling / overrides ----\n 'aria-label'?: string;\n /** Default \"Conversation transcript\". */\n logLabel?: string;\n /** Default \"Thoughts\". */\n thinkingDisclosureLabel?: string;\n\n // ---- Standard ----\n className?: string;\n 'data-testid'?: string;\n}\n","'use client';\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport { cn } from '../../utils/cn';\nimport { useReducedMotion } from '../../hooks/useReducedMotion';\nimport { Button } from '../Button/Button';\nimport { ArrowDown } from 'lucide-react';\nimport { ChatLiveRegions } from './ChatLiveRegions';\nimport { ChatMessageList } from './ChatMessageList';\nimport { ChatInputArea } from './ChatInputArea';\nimport {\n chatHeaderVariants,\n chatJumpToLatestVariants,\n chatRootVariants,\n} from './ChatInterface.styles';\nimport {\n normaliseAnnouncementText,\n truncateAnnouncement,\n useAnnouncementQueue,\n useStillWorkingTimer,\n useTranscriptScroll,\n} from './ChatInterface.hooks';\nimport { ChatInterfacePropsSchema } from './ChatInterface.types';\nimport type {\n ChatAssistantMessage,\n ChatInputComponentHandle,\n ChatInterfaceHandle,\n ChatInterfaceProps,\n ChatMessage,\n} from './ChatInterface.types';\n\n/**\n * ChatInterface — WCAG 2.2 AAA-compliant chatbot UI.\n *\n * Stateless from the library's perspective: consumer owns the messages array,\n * status, and websocket. ChatInterface renders + emits events.\n *\n * Live-region architecture:\n * - `<div role=\"log\">` — visual + navigable, no `aria-live`. AT users locate via landmarks/rotor.\n * - `<div role=\"status\" aria-live=\"polite\">` — single sr-only AT announcement channel.\n * - Tokens mutate the streaming bubble's text content in place (silent).\n * - On streaming → complete, the bubble's rendered DOM `innerText` is normalised, truncated,\n * and pinged into the status region as `\"Assistant replied: <preview>\"`.\n *\n * IMMUTABILITY: Pass a new message object when its content/parts/status/errorMessage/createdAt\n * changes. In-place mutation will not re-render (memoised bubbles).\n *\n * Markdown rendering is the consumer's responsibility via `renderAssistantContent` /\n * `renderUserContent`. ChatInterface treats content as opaque string. See README for\n * `react-markdown` + `rehype-sanitize` integration guidance.\n */\nconst ChatInterface = forwardRef<ChatInterfaceHandle, ChatInterfaceProps>(\n function ChatInterface(props, ref) {\n const {\n messages,\n status,\n onSendMessage,\n onStopGeneration,\n onRetry,\n onEscalate,\n renderAssistantContent,\n renderUserContent,\n inputComponent,\n assistantAvatar,\n userAvatar,\n header,\n emptyState,\n inputActions,\n placeholder = 'Type a message…',\n isDisabled = false,\n maxInputLength,\n sendShortcut = 'enter',\n progressAnnouncements = true,\n completionAnnouncementMaxChars,\n 'aria-label': ariaLabel = 'Chat',\n logLabel = 'Conversation transcript',\n thinkingDisclosureLabel = 'Thoughts',\n className,\n 'data-testid': testId,\n } = props;\n\n // Dev-only schema validation. Production-stripped by tsup via the NODE_ENV guard.\n if (process.env.NODE_ENV !== 'production') {\n try {\n ChatInterfacePropsSchema.parse(props);\n } catch (e) {\n console.warn('[ChatInterface] props failed validation:', e);\n }\n if (inputComponent && !renderUserContent) {\n console.warn(\n '[ChatInterface] `inputComponent` is set but `renderUserContent` is not. ' +\n 'User messages will render markdown source verbatim — provide `renderUserContent` for parsed output.'\n );\n }\n }\n\n const prefersReducedMotion = useReducedMotion();\n\n // Dev-only: warn once per instance when the conversation exceeds the documented v1 ceiling.\n // `content-visibility: auto` keeps things smooth up to ~1000-2000 messages on a mid-range\n // device; beyond that, consumers should paginate (full virtualisation is a v2 candidate).\n const hasWarnedLongConversationRef = useRef(false);\n if (\n process.env.NODE_ENV !== 'production' &&\n messages.length > 1000 &&\n !hasWarnedLongConversationRef.current\n ) {\n hasWarnedLongConversationRef.current = true;\n console.warn(\n `[ChatInterface] messages.length is ${messages.length} (>1000). Render performance may degrade — ` +\n 'consider paginating in the consumer state. Full virtualisation is a v2 candidate.'\n );\n }\n\n // ---- Derived state ----\n const streamingMessage = useMemo<ChatAssistantMessage | null>(() => {\n let found: ChatAssistantMessage | null = null;\n let count = 0;\n for (const m of messages) {\n if (m.role === 'assistant' && (m.status === 'streaming' || m.status === 'thinking')) {\n if (!found) found = m;\n count += 1;\n }\n }\n if (process.env.NODE_ENV !== 'production' && count > 1) {\n console.warn(\n `[ChatInterface] Found ${count} messages with status='streaming' or 'thinking'; expected at most 1. Using the first.`\n );\n }\n return found;\n }, [messages]);\n\n const streamingMessageId = streamingMessage?.id ?? null;\n\n // ---- Refs ----\n const scrollContainerRef = useRef<HTMLDivElement | null>(null);\n const bottomSentinelRef = useRef<HTMLDivElement | null>(null);\n const inputHandleRef = useRef<ChatInputComponentHandle | null>(null);\n\n // Track the streaming bubble's rendered content node so we can read innerText on completion.\n const streamingContentRefs = useRef<Map<string, HTMLDivElement | null>>(new Map());\n const registerStreamingContentRef = useCallback(\n (id: string, node: HTMLDivElement | null) => {\n if (node) {\n streamingContentRefs.current.set(id, node);\n } else {\n streamingContentRefs.current.delete(id);\n }\n },\n []\n );\n\n // ---- Status region announcement queue ----\n const { current: announcement, enqueue } = useAnnouncementQueue();\n\n // ---- Auto-scroll: stuck-to-bottom via IntersectionObserver sentinel ----\n const { isAtBottom, scrollToBottom } = useTranscriptScroll(\n scrollContainerRef,\n bottomSentinelRef,\n prefersReducedMotion\n );\n\n // Initial mount: scroll to bottom non-animated.\n const didMountRef = useRef(false);\n useEffect(() => {\n if (didMountRef.current) return;\n didMountRef.current = true;\n scrollToBottom({ smooth: false });\n }, [scrollToBottom]);\n\n // On new messages while sticky, scroll to bottom (non-animated to avoid jitter during streaming).\n const lastMessagesRef = useRef<ChatMessage[]>(messages);\n useEffect(() => {\n const prev = lastMessagesRef.current;\n lastMessagesRef.current = messages;\n // Only when new messages are appended or content grows; keep cheap.\n if (messages.length > prev.length && isAtBottom) {\n scrollToBottom({ smooth: false });\n }\n }, [messages, isAtBottom, scrollToBottom]);\n\n // Streaming-bubble growth: keep bottom in view while sticky.\n // `overflow-anchor: none` prevents the browser fighting us; without an active scroll call the\n // streaming text would scroll off-viewport. rAF-throttled so token rates of 100Hz coalesce to\n // ~display-refresh rate (60Hz) — `scrollIntoView` forces layout, so duplicate calls per frame\n // cost more than the user can perceive.\n const streamingContentLength =\n streamingMessage && streamingMessage.status === 'streaming'\n ? streamingMessage.content.length\n : 0;\n const scrollRafRef = useRef<number | null>(null);\n useEffect(() => {\n if (!streamingMessage) return;\n if (!isAtBottom) return;\n if (scrollRafRef.current !== null) return;\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n scrollToBottom({ smooth: false });\n });\n return (): void => {\n if (scrollRafRef.current !== null) {\n cancelAnimationFrame(scrollRafRef.current);\n scrollRafRef.current = null;\n }\n };\n }, [streamingContentLength, streamingMessage, isAtBottom, scrollToBottom]);\n\n // ---- Detect streaming → complete transitions to fire completion announcement ----\n //\n // O(1) tracking: we only need to know the previously-streaming/thinking assistant message.\n // Avoids the O(N) Map rebuild + GC sweep on every messages reference change. At 500 msgs\n // × 60Hz that drops 30k Map ops/sec to ~0.\n //\n // `errorFiredThisTickRef` signals to the top-level-status effect that a per-message error\n // fired, so we don't double-announce \"Response failed\" + \"Generation stopped\" for the same\n // logical event.\n const prevInProgressRef = useRef<{ id: string; status: 'streaming' | 'thinking' } | null>(null);\n const errorFiredThisTickRef = useRef(false);\n const pendingRafIdsRef = useRef<Set<number>>(new Set());\n useEffect(() => {\n errorFiredThisTickRef.current = false;\n const prev = prevInProgressRef.current;\n\n // Find current streaming/thinking message (reuses the upstream useMemo result).\n const nextInProgress = streamingMessage\n ? { id: streamingMessage.id, status: streamingMessage.status as 'streaming' | 'thinking' }\n : null;\n\n // Detect transition: previous in-progress message is no longer in-progress.\n if (prev && (!nextInProgress || nextInProgress.id !== prev.id)) {\n // The previously-streaming message either: completed, errored, was removed, or another\n // message took over as the streaming one. We need to look it up in the current messages.\n const prevMsg = messages.find((m) => m.id === prev.id);\n if (prevMsg && prevMsg.role === 'assistant') {\n if (prevMsg.status === 'complete') {\n // Build preview from rendered DOM innerText. Both lookup and read happen INSIDE rAF\n // so we capture the post-commit DOM state.\n const id = requestAnimationFrame(() => {\n pendingRafIdsRef.current.delete(id);\n const node = streamingContentRefs.current.get(prevMsg.id);\n const text = (node?.innerText ?? prevMsg.content ?? '').toString();\n const normalised = normaliseAnnouncementText(text);\n const preview = truncateAnnouncement(normalised, completionAnnouncementMaxChars);\n if (preview) {\n enqueue(`Assistant replied: ${preview}`);\n }\n });\n pendingRafIdsRef.current.add(id);\n } else if (prevMsg.status === 'error') {\n errorFiredThisTickRef.current = true;\n enqueue('Response failed');\n // Move focus from the (now vanished) Stop button to Try again.\n const id = requestAnimationFrame(() => {\n pendingRafIdsRef.current.delete(id);\n const bubble = scrollContainerRef.current?.querySelector(\n `[data-message-id=\"${cssEscape(prevMsg.id)}\"]`\n );\n // Library invariant: consumer renderer output MUST NOT include `data-action=\"retry\"`.\n const btn = bubble?.querySelector<HTMLButtonElement>('[data-action=\"retry\"]');\n btn?.focus();\n });\n pendingRafIdsRef.current.add(id);\n }\n }\n }\n\n prevInProgressRef.current = nextInProgress;\n }, [streamingMessage, messages, completionAnnouncementMaxChars, enqueue]);\n\n // Cancel any outstanding rAF on unmount.\n useEffect(() => {\n const ids = pendingRafIdsRef.current;\n return (): void => {\n for (const id of ids) cancelAnimationFrame(id);\n ids.clear();\n };\n }, []);\n\n // ---- Detect \"Generation stopped\" via top-level status transition ----\n // Suppressed if a per-message error transition fired in the same tick — they originate from\n // the same logical event (\"user pressed Stop on a failing stream\") and we don't want both pings.\n const prevStatusRef = useRef(status);\n useEffect(() => {\n const prev = prevStatusRef.current;\n prevStatusRef.current = status;\n if (prev === 'streaming' && status === 'idle' && !errorFiredThisTickRef.current) {\n enqueue('Generation stopped');\n }\n }, [status, enqueue]);\n\n // ---- \"Still working\" timer (bounded; resets on token arrival) ----\n // (streamingContentLength derived above for auto-scroll; reused here.)\n useStillWorkingTimer({\n isStreaming: status === 'streaming',\n streamingContentLength,\n enabled: progressAnnouncements,\n message: 'Assistant is still working',\n onAnnounce: enqueue,\n });\n\n // ---- Imperative handle ----\n useImperativeHandle(\n ref,\n () => ({\n focusInput: (): void => inputHandleRef.current?.focus(),\n scrollToBottom: (opts): void => scrollToBottom(opts),\n }),\n // refs are stable; scrollToBottom is memoised by useTranscriptScroll\n [scrollToBottom]\n );\n\n // ---- Render ----\n return (\n <section\n className={cn(chatRootVariants(), className, 'relative')}\n aria-label={ariaLabel}\n data-testid={testId}\n data-themis-chatinterface\n >\n {header ? <div className={chatHeaderVariants()}>{header}</div> : null}\n\n {/* Log region is ALWAYS rendered so the landmark stays consistent across empty→populated.\n The empty state lives inside the log, not in a separate region. */}\n <ChatMessageList\n ref={scrollContainerRef}\n messages={messages}\n streamingMessageId={streamingMessageId}\n assistantAvatar={assistantAvatar}\n userAvatar={userAvatar}\n renderAssistantContent={renderAssistantContent}\n renderUserContent={renderUserContent}\n thinkingDisclosureLabel={thinkingDisclosureLabel}\n logLabel={logLabel}\n emptyState={emptyState}\n onRetry={onRetry}\n onEscalate={onEscalate}\n registerStreamingContentRef={registerStreamingContentRef}\n bottomSentinelRef={bottomSentinelRef}\n testId={testId ? `${testId}-log` : undefined}\n />\n\n\n {/* Jump-to-latest floating button when not stuck to bottom */}\n <div className={chatJumpToLatestVariants({ visible: !isAtBottom && messages.length > 0 })}>\n <Button\n variant=\"secondary\"\n visualSize=\"icon\"\n aria-label=\"Jump to latest message\"\n onPress={(): void => scrollToBottom({ smooth: !prefersReducedMotion })}\n data-action=\"jump-to-latest\"\n >\n <ArrowDown aria-hidden=\"true\" />\n </Button>\n </div>\n\n <ChatInputArea\n status={status}\n consumerDisabled={isDisabled}\n inputComponent={inputComponent}\n placeholder={placeholder}\n maxInputLength={maxInputLength}\n sendShortcut={sendShortcut}\n inputActions={inputActions}\n inputAriaLabel={placeholder}\n onSendMessage={onSendMessage}\n onStopGeneration={onStopGeneration}\n registerInputHandle={(h): void => {\n inputHandleRef.current = h;\n }}\n />\n\n <ChatLiveRegions\n current={announcement}\n testId={testId ? `${testId}-status` : undefined}\n />\n </section>\n );\n }\n);\n\nChatInterface.displayName = 'ChatInterface';\n\n/**\n * CSS.escape fallback for environments missing it. Used to safely build attribute selectors\n * from message ids in `querySelector`. (We avoid this for general id lookups by using\n * Map-based ref tracking; this is solely for finding the retry button in the errored bubble.)\n */\nfunction cssEscape(id: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(id);\n }\n return id.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nexport { ChatInterface };\n"]}
|