{"mappings":"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","sources":["packages/@react-spectrum/ai/src/Chat.tsx"],"sourcesContent":["/*\n * Copyright 2026 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {announce} from 'react-aria/private/live-announcer/LiveAnnouncer';\nimport {ButtonContext} from 'react-aria-components/Button';\nimport {\n  CollectionRendererContext,\n  createLeafComponent\n} from 'react-aria-components/CollectionBuilder';\nimport {\n  createContext,\n  ForwardedRef,\n  forwardRef,\n  ReactNode,\n  RefObject,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState\n} from 'react';\nimport {DEFAULT_SLOT, Provider} from 'react-aria-components/slots';\nimport {DOMRef, forwardRefType, Node} from '@react-types/shared';\nimport {filterDOMProps} from 'react-aria/filterDOMProps';\nimport {focusRing, style, StyleString} from '@react-spectrum/s2/style' with {type: 'macro'};\nimport {\n  GridList,\n  GridListItem,\n  GridListItemProps,\n  GridListLoadMoreItemProps,\n  GridListProps\n} from 'react-aria-components/GridList';\nimport {inertValue} from 'react-aria/private/utils/inertValue';\n// @ts-ignore\nimport intlMessages from '../intl/*.json';\nimport {ListLayout} from './ListLayout';\nimport {ListStateContext} from 'react-aria-components/ListBox';\nimport {LoaderNode} from 'react-aria/private/collections/BaseCollection';\nimport {mergeStyles} from '@react-spectrum/s2/mergeStyles';\nimport {useDOMRef} from './useDOMRef';\nimport {useEnterAnimation, useExitAnimation} from 'react-aria/private/utils/animation';\nimport {useFocusWithin} from 'react-aria/useFocusWithin';\nimport {useLayoutEffect} from 'react-aria/private/utils/useLayoutEffect';\nimport {useLoadMoreSentinel} from 'react-aria/private/utils/useLoadMoreSentinel';\nimport {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter';\nimport {useRenderProps} from 'react-aria-components/useRenderProps';\nimport {Virtualizer} from 'react-aria-components/Virtualizer';\n\nconst scrollButtonWrapper = style({\n  opacity: {\n    isEntering: 0,\n    isExiting: 0\n  },\n  translateY: {\n    isEntering: 4,\n    isExiting: 4\n  },\n  transition: '[opacity, translate]',\n  transitionDuration: 200,\n  transitionTimingFunction: {\n    isExiting: 'in'\n  },\n  pointerEvents: {\n    isExiting: 'none'\n  }\n});\n\nexport interface PromptFocusContextValue {\n  onFocusChange: (isFocused: boolean) => void;\n}\n\nexport const PromptFocusContext = createContext<PromptFocusContextValue>({\n  onFocusChange: () => {}\n});\n\ninterface InternalChatContextValue {\n  announceItem: (text: string) => void;\n  setIsNearBottom: (isNear: boolean) => void;\n  setScrollElement: (element: HTMLElement | null) => void;\n}\n\nconst InternalChatContext = createContext<InternalChatContextValue>({\n  announceItem: text => announce(text, 'polite'),\n  setIsNearBottom: () => {},\n  setScrollElement: () => {}\n});\n\ninterface ThreadScrollButtonContextValue {\n  isNearBottom: boolean;\n  scrollToBottom: () => void;\n  'aria-label': string;\n}\n\nconst ThreadScrollButtonContext = createContext<ThreadScrollButtonContextValue>({\n  isNearBottom: true,\n  scrollToBottom: () => {},\n  'aria-label': ''\n});\n\n// TODO: make this more RAC like (aka default class name and other RAC prop)\nexport interface ChatProps {\n  /**\n   * Spectrum-defined styles, returned by the `style()` macro.\n   */\n  styles?: StyleString;\n  /**\n   * Children of the chat, such as Thread, PromptField, and ThreadScrollButton.\n   */\n  children?: ReactNode;\n}\n\nexport const Chat = /*#__PURE__*/ (forwardRef as forwardRefType)(function Chat(\n  props: ChatProps,\n  ref: DOMRef<HTMLDivElement>\n) {\n  let {children, styles} = props;\n  let domRef = useDOMRef(ref);\n  let isFieldFocusedRef = useRef(false);\n  let isChatFocusWithinRef = useRef(false);\n  let hasNewMessagesRef = useRef(false);\n  let timeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n  let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/ai');\n\n  let scrollRef = useRef<HTMLElement | null>(null);\n  let scrollToBottom = useCallback(() => {\n    let el = scrollRef.current;\n    if (!el) {\n      return;\n    }\n    // TODO: will need some kind of api to programatically set the focused item to\n    // the newest item in the gridlist in the virtualizer case. this works for\n    // non-virtualized for now though\n    // 'scrollend' does not compose across shadow DOM boundaries, but this listener is intentionally\n    // scoped to this specific scroll container element (not a global target), so shadow root\n    // propagation does not apply here.\n    // oxlint-disable-next-line rsp-rules/no-non-composing-event-listener\n    el.addEventListener(\n      'scrollend',\n      () => {\n        let firstRow = el.querySelector<HTMLElement>('[role=\"row\"]');\n        (firstRow ?? el).focus();\n      },\n      {once: true}\n    );\n    el.scrollTo({top: el.scrollHeight - el.clientHeight, behavior: 'smooth'});\n  }, []);\n  let [isNearBottom, setIsNearBottom] = useState(true);\n\n  // only announce new items if user is in the prompt field, otherwise if they\n  // are outside the field, only announce there are new responses. If not in chat at all, don't announce\n  let announceItem = useCallback(\n    (text: string) => {\n      if (isFieldFocusedRef.current) {\n        announce(text, 'polite');\n        return;\n      }\n\n      if (isChatFocusWithinRef.current) {\n        // TODO: ideally announce number of new messages, but only count system messages? maybe threaditem needs\n        // to have a \"type\" prop\n        if (!hasNewMessagesRef.current) {\n          hasNewMessagesRef.current = true;\n          announce(stringFormatter.format('chat.newMessage'), 'polite');\n          // TODO: arbirary amount of time to wait before announcing new message, maybe we don't clear until\n          // we detect they scroll down? Or maybe when we do the message count we do it after a certain number of messages?\n          // or maybe this is fine\n          timeout.current = setTimeout(() => {\n            hasNewMessagesRef.current = false;\n            timeout.current = null;\n          }, 5000);\n        }\n      }\n    },\n    [stringFormatter]\n  );\n\n  let setScrollElement = useCallback((el: HTMLElement | null) => {\n    scrollRef.current = el;\n  }, []);\n\n  let {focusWithinProps} = useFocusWithin({\n    onFocusWithinChange: isFocused => {\n      isChatFocusWithinRef.current = isFocused;\n    }\n  });\n\n  useEffect(() => {\n    return () => {\n      if (timeout.current !== null) {\n        clearTimeout(timeout.current);\n      }\n    };\n  }, []);\n\n  return (\n    <Provider\n      values={[\n        [InternalChatContext, {announceItem, setIsNearBottom, setScrollElement}],\n        [\n          ThreadScrollButtonContext,\n          {\n            isNearBottom,\n            scrollToBottom,\n            'aria-label': stringFormatter.format('chat.scrollToBottom')\n          }\n        ],\n        [\n          PromptFocusContext,\n          {\n            onFocusChange: (focused: boolean) => {\n              isFieldFocusedRef.current = focused;\n            }\n          }\n        ]\n      ]}>\n      <div ref={domRef} className={styles} {...focusWithinProps}>\n        {children}\n      </div>\n    </Provider>\n  );\n});\n\nexport interface ThreadProps<T extends object> extends Pick<\n  GridListProps<T>,\n  'items' | 'children' | 'aria-label' | 'aria-labelledby'\n> {\n  /**\n   * Spectrum-defined styles, returned by the `style()` macro.\n   */\n  styles?: StyleString;\n  /**\n   * The maximum distance in px from the bottom of the content for the\n   * viewport to be considered \"near the end\". While near the end, appended content and streaming\n   * size changes will keep the viewport pinned to the latest output.\n   *\n   * @default 100\n   */\n  scrollEndThreshold?: number;\n}\n\nexport function Thread<T extends object>(props: ThreadProps<T>) {\n  let {\n    items,\n    children,\n    styles,\n    scrollEndThreshold = 100,\n    'aria-label': ariaLabel,\n    'aria-labelledby': ariaLabelledby\n  } = props;\n\n  let {setIsNearBottom, setScrollElement} = useContext(InternalChatContext);\n  let isNearBottomRef = useRef(true);\n  let gridListRef = useRef<HTMLDivElement | null>(null);\n  let callbackRef = useCallback(\n    (el: HTMLDivElement | null) => {\n      gridListRef.current = el;\n      setScrollElement(el);\n    },\n    [setScrollElement]\n  );\n\n  let handleScroll = useCallback(() => {\n    let el = gridListRef.current;\n    if (!el) {\n      return;\n    }\n\n    let nearBottom = el.scrollTop >= el.scrollHeight - el.clientHeight - scrollEndThreshold;\n    isNearBottomRef.current = nearBottom;\n    setIsNearBottom(nearBottom);\n  }, [setIsNearBottom, scrollEndThreshold]);\n\n  return (\n    <Virtualizer\n      layout={ListLayout}\n      layoutOptions={{\n        estimatedRowHeight: 100,\n        padding: 4,\n        gap: 8,\n        anchorTo: 'end',\n        loaderSize: 48,\n        scrollEndThreshold\n      }}\n      shouldObserveItemSize>\n      <GridList\n        ref={callbackRef}\n        disallowTypeAhead\n        onScroll={handleScroll}\n        keyboardNavigationBehavior=\"tab\"\n        UNSTABLE_focusOnEntry=\"last\"\n        items={items}\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabelledby}\n        // TODO: for now we enforce this, but to be configurable?\n        style={{\n          display: 'flex',\n          boxSizing: 'border-box',\n          minWidth: 0\n        }}\n        className={styles}>\n        {children}\n      </GridList>\n    </Virtualizer>\n  );\n}\n\nexport interface ThreadScrollButtonProps {\n  children?: ReactNode;\n}\n\n// TODO: wrapper so we can do the \"if isNearBottom then hide\" logic, could do this via inline styles perhaps\n// and ditch the wrapper?\nexport function ThreadScrollButton({children}: ThreadScrollButtonProps) {\n  let {isNearBottom, scrollToBottom, ...buttonProps} = useContext(ThreadScrollButtonContext);\n  let ref = useRef<HTMLDivElement>(null);\n  let isVisible = !isNearBottom;\n  let isExiting = useExitAnimation(ref, isVisible);\n\n  if (!isVisible && !isExiting) {\n    return null;\n  }\n\n  return (\n    <ButtonContext.Provider\n      value={{slots: {[DEFAULT_SLOT]: {}, scroll: {onPress: scrollToBottom, ...buttonProps}}}}>\n      <ThreadScrollButtonInner domRef={ref} isExiting={isExiting}>\n        {children}\n      </ThreadScrollButtonInner>\n    </ButtonContext.Provider>\n  );\n}\n\ninterface ThreadScrollButtonInnerProps {\n  domRef: RefObject<HTMLDivElement | null>;\n  isExiting: boolean;\n  children?: ReactNode;\n}\n\nfunction ThreadScrollButtonInner({domRef, isExiting, children}: ThreadScrollButtonInnerProps) {\n  let isEntering = useEnterAnimation(domRef);\n  return (\n    <div ref={domRef} className={scrollButtonWrapper({isEntering, isExiting})}>\n      {children}\n    </div>\n  );\n}\n\nconst threadItemBase = style({\n  ...focusRing(),\n  borderRadius: 'default'\n});\n\nexport interface ThreadItemProps extends Pick<\n  GridListItemProps,\n  'children' | 'textValue' | 'focusMode' | 'allowsArrowNavigation' | 'id'\n> {\n  /**\n   * Spectrum-defined styles, returned by the `style()` macro.\n   */\n  styles?: StyleString;\n  /** Whether or not the item's content is currently being streamed in. */\n  isStreaming?: boolean;\n  /** Announce textValue on mount even when isStreaming is provided. */\n  shouldAnnounceOnMount?: boolean;\n}\n\nexport function ThreadItem(props: ThreadItemProps) {\n  let {\n    styles,\n    children,\n    textValue = ' ',\n    isStreaming,\n    shouldAnnounceOnMount,\n    focusMode,\n    allowsArrowNavigation\n  } = props;\n  let {announceItem} = useContext(InternalChatContext);\n\n  // TODO: using aria-live on the gridlist item was pretty chatty and the streaming causes the text announcement\n  // to constantly reset. If we used a live region and updated its contents when streaming finished that worked decently\n  // but still feels quite verbose. Stick with this and get feedback\n  useLayoutEffect(() => {\n    if ((isStreaming === undefined || shouldAnnounceOnMount) && textValue && textValue !== ' ') {\n      announceItem(textValue);\n    }\n\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  let isStreamingNow = isStreaming ?? false;\n  let prevStreamingRef = useRef(isStreamingNow);\n  useLayoutEffect(() => {\n    if (isStreaming === undefined) {\n      return;\n    }\n    let wasStreaming = prevStreamingRef.current;\n    prevStreamingRef.current = isStreamingNow;\n    if (wasStreaming && !isStreamingNow && textValue && textValue !== ' ') {\n      announceItem(textValue);\n    }\n  }, [isStreaming, isStreamingNow, textValue, announceItem]);\n\n  return (\n    <GridListItem\n      textValue={textValue}\n      focusMode={focusMode}\n      allowsArrowNavigation={allowsArrowNavigation}\n      className={renderProps => mergeStyles(threadItemBase({...renderProps}), styles)}>\n      {children}\n    </GridListItem>\n  );\n}\n\nexport interface ThreadLoadMoreItemProps extends GridListLoadMoreItemProps {}\n\n// TODO: Reuse GridListLoadMoreItem instead when Thread component moves into RAC.\n// Re-implementing here so we can avoid passing 'direction' to the LoadMore item\nexport const ThreadLoadMoreItem = createLeafComponent(\n  LoaderNode,\n  function GridListLoadingIndicator(\n    props: GridListLoadMoreItemProps,\n    ref: ForwardedRef<HTMLDivElement>,\n    item: Node<object>\n  ) {\n    let state = useContext(ListStateContext)!;\n    let direction: 'start' | 'end' | undefined = 'start';\n    let {isVirtualized} = useContext(CollectionRendererContext);\n    let {isLoading, onLoadMore, scrollOffset, ...otherProps} = props;\n\n    let sentinelRef = useRef(null);\n    let memoedLoadMoreProps = useMemo(\n      () => ({onLoadMore, collection: state?.collection, scrollOffset, direction}),\n      [onLoadMore, scrollOffset, state?.collection, direction]\n    );\n    useLoadMoreSentinel(memoedLoadMoreProps, sentinelRef);\n\n    let renderProps = useRenderProps({\n      ...otherProps,\n      id: undefined,\n      children: item.rendered,\n      defaultClassName: 'react-aria-GridListLoadingIndicator',\n      values: undefined\n    });\n    // For now don't include aria-posinset and aria-setsize on loader since they aren't keyboard focusable\n    // Arguably shouldn't include them ever since it might be confusing to the user to include the loaders as part of the\n    // item count\n\n    return (\n      <>\n        {/* Alway render the sentinel. For now onus is on the user for styling when using flex + gap (this would introduce a gap even though it doesn't take room) */}\n        {/* @ts-ignore - compatibility with React < 19 */}\n        <div style={{position: 'relative', width: 0, height: 0}} inert={inertValue(true)}>\n          <div\n            data-testid=\"loadMoreSentinel\"\n            ref={sentinelRef}\n            style={{position: 'absolute', height: 1, width: 1}}\n          />\n        </div>\n        {isLoading && renderProps.children && (\n          <div {...renderProps} {...filterDOMProps(props, {global: true})} role=\"row\" ref={ref}>\n            <div aria-colindex={isVirtualized ? 1 : undefined} role=\"gridcell\">\n              {renderProps.children}\n            </div>\n          </div>\n        )}\n      </>\n    );\n  }\n);\n"],"names":[],"version":3,"file":"Chat.cjs.map"}