import { ComponentPropsWithoutRef } from 'react';
import { Icon, IconProps } from '../Icon/Icon';
import './thinking-state.css';
export type ThinkingStateSize = 'xs' | 's' | 'm';
export type ThinkingStateState = 'active' | 'completed' | 'stopped';
export interface ThinkingStateProps extends ComponentPropsWithoutRef<'div'> {
/**
* Additional class names
*/
className?: string;
/**
* Which icon to render before the label.
*/
iconName?: IconProps['iconName'];
/**
* What the agent is doing (for example "Thinking..."). Announced to screen
* readers.
*/
label: string;
/**
* Size. Sets the text size, and the icon size follows it (the icon is the
* same height as the text); the time is one step smaller and the gap grows
* with the size.
*/
size?: ThinkingStateSize;
/**
* Where the run is. `active` sweeps a shimmer across the label (the PDS
* `pds-ai-active-shimmer` utility) and pulses the icon. `completed` shows
* the same text without motion. `stopped` is for a run the user cut off:
* no motion, and the icon and label are muted to show it did not finish.
*/
state?: ThinkingStateState;
/**
* Should the elapsed time be shown? It only renders when `time` is also set.
*/
showTimer?: boolean;
/**
* Elapsed time, formatted by the consumer (for example "17.1s"). Hidden from
* screen readers so a ticking value isn't announced.
*/
time?: string;
}
// Stand-in for the v2 `mergeClasses` util, which the published package does
// not export. The port uses the real one.
const mergeClasses = (classes: Array<string | null | undefined>) =>
classes.filter(Boolean).join(' ');
/**
* ThinkingState UI component. Shows that an agent is working: an icon, what it
* is doing, and how long it has been at it.
*/
export const ThinkingState = ({
className,
iconName = 'zappy',
label,
showTimer = false,
size = 's',
state = 'active',
time,
...props
}: ThinkingStateProps) => {
const baseClass = 'pds-thinking-state';
const isActive = state === 'active';
return (
<div
className={mergeClasses([
baseClass,
size !== 's' ? `${baseClass}--${size}` : null,
`${baseClass}--${state}`,
className,
])}
role='status'
{...props}
>
<Icon className={`${baseClass}__icon`} iconName={iconName} size={size} />
<span
className={mergeClasses([
`${baseClass}__label`,
isActive ? 'pds-ai-active-shimmer' : null,
])}
>
{label}
</span>
{showTimer && time && (
<span aria-hidden='true' className={`${baseClass}__time`}>
{time}
</span>
)}
</div>
);
};
Thinking State
A small inline status: the agent icon (Zappy by default), a label ("Thinking..."), and the elapsed time in the code font ("17.1s").
Net-new componentOpen in Storybook