diff --git a/packages/@react-spectrum/ai/exports/index.ts b/packages/@react-spectrum/ai/exports/index.ts index e5c1154a495..06b0c4e3924 100644 --- a/packages/@react-spectrum/ai/exports/index.ts +++ b/packages/@react-spectrum/ai/exports/index.ts @@ -17,7 +17,13 @@ export { PromptToken, PromptFieldVoiceButton } from '../src/PromptField'; -export {ResponseStatus, ResponseStatusTitle, ResponseStatusPanel} from '../src/ResponseStatus'; +export { + ExecutionTrace, + ExecutionTraceItem, + ResponseStatus, + ResponseStatusTitle, + ResponseStatusPanel +} from '../src/ResponseStatus'; export { Chat, Thread, @@ -46,6 +52,8 @@ export type {MessageFeedbackProps} from '../src/MessageFeedback'; export type {MessageSourceProps, SourceListProps, SourceListItemProps} from '../src/MessageSource'; export type {MessageSuggestionProps, MessageSuggestionListProps} from '../src/MessageSuggestion'; export type { + ExecutionTraceProps, + ExecutionTraceItemProps, ResponseStatusProps, ResponseStatusTitleProps, ResponseStatusPanelProps diff --git a/packages/@react-spectrum/ai/src/ResponseStatus.tsx b/packages/@react-spectrum/ai/src/ResponseStatus.tsx index 37d84e4819b..7fbfac324ed 100644 --- a/packages/@react-spectrum/ai/src/ResponseStatus.tsx +++ b/packages/@react-spectrum/ai/src/ResponseStatus.tsx @@ -14,7 +14,7 @@ import {AriaLabelingProps, DOMProps, DOMRef, GlobalDOMAttributes} from '@react-t import { baseColor, focusRing, - lightDark, + iconStyle, space, style } from '@react-spectrum/s2/style' with {type: 'macro'}; @@ -56,12 +56,6 @@ export interface ResponseStatusProps extends Omit< RACDisclosureProps, 'className' | 'style' | 'render' | 'children' | keyof GlobalDOMAttributes > { - /** - * The size of the response status. - * - * @default 'M' - */ - size?: 'S' | 'M' | 'L' | 'XL'; /** * The amount of space between stacked response statuses. * @@ -86,7 +80,6 @@ export interface ResponseStatusProps extends Omit< } const ResponseStatusContext = createContext<{ - size?: 'S' | 'M' | 'L' | 'XL'; density?: 'compact' | 'regular' | 'spacious'; status: 'loading' | 'failed' | 'success'; hasPanelContent: boolean; @@ -111,7 +104,7 @@ export const ResponseStatus = forwardRef(function ResponseStatus( props: ResponseStatusProps, ref: DOMRef ) { - let {size = 'M', density = 'regular', status = 'loading', styles} = props; + let {density = 'regular', status = 'loading', styles} = props; let domRef = useDOMRef(ref); let [hasPanelContent, setHasPanelContent] = useState(false); let registerPanel = useCallback((mounted: boolean) => setHasPanelContent(mounted), []); @@ -123,8 +116,7 @@ export const ResponseStatus = forwardRef(function ResponseStatus( } return ( - + - ) : isInteractive ? ( - - - - ) : null} - {props.children} - {!isLoading && ( + ) : ( @@ -335,6 +261,12 @@ export const ResponseStatusTitle = forwardRef(function ResponseStatusTitle( )} + {props.children} + {isInteractive ? ( + + + + ) : null} ); @@ -342,7 +274,7 @@ export const ResponseStatusTitle = forwardRef(function ResponseStatusTitle( + ); +} + +export interface ExecutionTraceItemProps extends DOMProps, AriaLabelingProps { + /** + * The label describing the step. + */ + children: ReactNode; + detail?: ReactNode; + /** + * Spectrum-defined styles, returned by the `style()` macro. + */ + /** + * An icon shown at the leading edge of the row. If omitted, a checkmark is rendered by default. + */ + icon?: ReactNode; + /** Allows detail content to render but prevents the row from being collapsible. */ + isAlwaysOpen?: boolean; + /** + * Additional detail revealed when the step is expanded, such as tool call input or output. + * If omitted, the row is static and cannot be expanded. + */ + + styles?: StyleString; +} + +const executionTraceItemStyles = style({ + display: 'flex', + font: 'body', + gap: 4, + '--divider-display': { + type: 'display', + value: { + default: 'block', + ':last-child': 'none' + } + } +}); + +const executionTraceItemIconContainerStyles = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + flexShrink: 0 +}); + +const executionTraceItemDividerStyles = style({ + width: 1, + flexGrow: 1, + marginY: 2, + backgroundColor: 'gray-500', + display: 'var(--divider-display, flex)' +}); + +const executionTraceItemBaseStyles = { + paddingBottom: 12, + paddingStart: 8 +} as const; + +const executionTraceWithoutDisclosureStyles = style({ + ...executionTraceItemBaseStyles, + display: 'flex', + flexDirection: 'column', + minHeight: 24 +}); + +const executionTraceDetailPanelStyles = style(executionTraceItemBaseStyles); + +/** + * An ExecutionTraceItem represents a single step within an ExecutionTrace, such as + * a tool call or search. When a `detail` is provided, the row can be expanded to reveal it. + */ +export const ExecutionTraceItem = forwardRef(function ExecutionTraceItem( + props: ExecutionTraceItemProps, + ref: DOMRef +) { + let { + isAlwaysOpen, + detail, + icon =