Skip to content

chore: Setup ai components package#10095

Merged
yihuiliao merged 34 commits into
mainfrom
setup-ai-components-package
Jun 5, 2026
Merged

chore: Setup ai components package#10095
yihuiliao merged 34 commits into
mainfrom
setup-ai-components-package

Conversation

@snowystinger

@snowystinger snowystinger commented May 22, 2026

Copy link
Copy Markdown
Member

Closes

Sets up a new @react-spectrum/s2-ai package where we can develop the new and nebulous ai components that Spectrum is experimenting with.

Some things to note:

  • Consider exporting CenterBaseline. There isn’t a need for it if you’re using icons within S2 components but when you start using S2 icons outside of S2 components, I find myself reaching for it
    • export CenterBaseline (the component)
  • Would be nice to support runtime functions for iconStyle. I couldn't provide a size prop to it
    • types are too complicated, just ts-ignore
  • Because we have a copy of style-utils for getAllowedOverrides() and controlSize(), we also have to export type so something to think about
    • allow everything!
  • Copied useDOMRef and useSpectrumContextProps to match how we do things in S2
    • keep useDOMRef but get rid of useSpectrumContextProps, can add contexts later but no need to complicate things for now

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@rspbot

rspbot commented May 22, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented May 22, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented May 28, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented May 28, 2026

Copy link
Copy Markdown

Comment thread packages/@react-spectrum/s2-ai/README.md
Comment thread packages/@react-spectrum/s2-ai/src/AssetList.tsx Outdated
Comment thread packages/@react-spectrum/s2-ai/src/AssetList.tsx Outdated
<Checkbox slot="selection" excludeFromTabOrder size={size === 'XS' ? 'S' : size} />
</div>
);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can any of this be reused from the existing Card component? The idea there was to have the generic Card not specify any layout and for specific card types to extend from it. What were the things that prevented you from implementing it that way and necessitated copying the entire Card component here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but I copied it so that it was standalone and we wouldn't be releasing horizontal cards in s2 yet

Comment thread packages/@react-spectrum/s2-ai/src/mergeStyles.ts Outdated
Comment thread packages/@react-spectrum/s2-ai/src/style-utils-copy.ts Outdated

export const AIAssetList: Story = {
render: args => (
<AssetList {...args} styles={style({width: 400})}>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the args supposed to apply to the individual assets too? Like I noticed changing the variant wasn't affecting the stories.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all of it was hooked up :-/ there weren't any designs for a number of things, so probably overlooked it

* support custom typography on s2 disclosure

* initialize message source

* add numberbadge, cleanup, formatting

* more cleanup

* remove story

* minor fixes

* support t-shirt sizing

* add response status component

* move files to ai package

* fix imports

* fix lint

* fix formatting

* actually fix lint?

* move to correct storybook folder

* add exports
@rspbot

rspbot commented Jun 1, 2026

Copy link
Copy Markdown

* fix: rebase commits + add stories

* feat: migrate MessageFeedback and UserMessage components to s2-ai package

* Apply suggestions from code review

Co-authored-by: Yihui Liao <44729383+yihuiliao@users.noreply.github.com>

* add missing file

---------

Co-authored-by: Yihui Liao <44729383+yihuiliao@users.noreply.github.com>
@rspbot

rspbot commented Jun 1, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 2, 2026

Copy link
Copy Markdown

Comment on lines +338 to +346
const panelStyles = style({
font: 'body',
height: '--disclosure-panel-height',
overflow: 'clip',
transition: {
default: '[height]',
'@media (prefers-reduced-motion: reduce)': 'none'
}
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing I noticed (and might be present in our S2 Disclosure as well) was that if the user didn't provide a ResponseStatusPanel while the ResponseStatus was loading but then provided one once loading finished, the disclosure would render with its non-expanded styles but the contents of the panel would be visible still. Maybe a non-issue but users might run into it accidentally

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh interesting. i think my expectation was the a ResponseStatusPanel would always be provided even while ResponseStatus was loading so it would never be "missing" from the code and the actual content of the panel would get injected at some point. not sure if that's the right way to think about it tho

@rspbot

rspbot commented Jun 2, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 2, 2026

Copy link
Copy Markdown

let isRTL = direction === 'rtl';

return (
<RACButton

@yihuiliao yihuiliao Jun 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the only reason i ended up using a RACButton is because i needed the font weight to be less and there's no way to customize the typography in a button. similar to how we were open to supporting custom typography in disclosure, would we open to it in buttons as well?

* base setup, transferring the stuff from the prototypes repo

* add dynamic story

* more things to fix

* explorations

* add scroll to bottom button and scroll when new messages are added if near button

* use aria live to announce new items

* tentative column reverse investigation and update stories to reflect that

* handle keyboard navigation when using column reverse

* fix pageup/page down looping in column reverse

* force shift tab and tab to go to newest card on first enty

* make it so whenever you tab into the thread, you go to newest content regardless of prev focused key

* virtualized story and testing stuff

* announcement of new items only

* add streaming example

* make example more realistic and fine tune announcement behavior

* add thinking response status

* tried aria live on gridlist item

* only announce if in thread or in field

* only announce "new message" if user is in the thread, fix focus loss to body

* forgot a new option doc

* move to more RAC like API

* move to ai components repo

* clean up

* add tests

* fix 16/17?

* update story to use available ai components
@rspbot

rspbot commented Jun 4, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 4, 2026

Copy link
Copy Markdown
## API Changes

@react-spectrum/s2

/@react-spectrum/s2:DisclosureTitle

 DisclosureTitle {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   children: React.ReactNode
   id?: string
   level?: number = 3
+  styles?: StylesPropWithFont
 }

@react-spectrum/s2-ai

/@react-spectrum/s2-ai:Attachment

+Attachment {
+
+}

/@react-spectrum/s2-ai:AttachmentList

+AttachmentList {
+
+}

/@react-spectrum/s2-ai:BasicHorizontalCard

+BasicHorizontalCard {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  children: ReactNode | (CardRenderProps) => ReactNode
+  density?: 'compact' | 'regular' | 'spacious' = 'regular'
+  download?: boolean | string
+  href?: Href
+  hrefLang?: string
+  id?: Key
+  isDisabled?: boolean
+  onAction?: () => void
+  onPress?: (PressEvent) => void
+  onPressChange?: (boolean) => void
+  onPressEnd?: (PressEvent) => void
+  onPressStart?: (PressEvent) => void
+  onPressUp?: (PressEvent) => void
+  ping?: string
+  referrerPolicy?: HTMLAttributeReferrerPolicy
+  rel?: string
+  routerOptions?: RouterOptions
+  size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
+  styles?: StylesProp
+  target?: HTMLAttributeAnchorTarget
+  textValue?: string
+  value?: T
+  variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
+}

/@react-spectrum/s2-ai:HorizontalCard

+HorizontalCard {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  children: ReactNode | (CardRenderProps) => ReactNode
+  density?: 'compact' | 'regular' | 'spacious' = 'regular'
+  download?: boolean | string
+  href?: Href
+  hrefLang?: string
+  id?: Key
+  isDisabled?: boolean
+  onAction?: () => void
+  onPress?: (PressEvent) => void
+  onPressChange?: (boolean) => void
+  onPressEnd?: (PressEvent) => void
+  onPressStart?: (PressEvent) => void
+  onPressUp?: (PressEvent) => void
+  ping?: string
+  referrerPolicy?: HTMLAttributeReferrerPolicy
+  rel?: string
+  routerOptions?: RouterOptions
+  size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
+  styles?: StylesProp
+  target?: HTMLAttributeAnchorTarget
+  textValue?: string
+  value?: T
+  variant?: 'primary' | 'secondary' | 'tertiary' | 'quiet' = 'primary'
+}

/@react-spectrum/s2-ai:MessageFeedback

+MessageFeedback {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  defaultValue?: MessageFeedbackValue
+  id?: string
+  isDisabled?: boolean
+  onChange?: (MessageFeedbackValue) => void
+  slot?: string | null
+  styles?: StylesProp
+  thumbDownLabel?: string
+  thumbUpLabel?: string
+  value?: MessageFeedbackValue
+}

/@react-spectrum/s2-ai:MessageSuggestion

+MessageSuggestion {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  aria-controls?: string
+  aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
+  aria-describedby?: string
+  aria-details?: string
+  aria-disabled?: boolean | 'true' | 'false'
+  aria-expanded?: boolean | 'true' | 'false'
+  aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
+  aria-label?: string
+  aria-labelledby?: string
+  aria-pressed?: boolean | 'true' | 'false' | 'mixed'
+  autoFocus?: boolean
+  children: ReactNode
+  excludeFromTabOrder?: boolean
+  form?: string
+  formAction?: ButtonHTMLAttributes<HTMLButtonElement>['formAction']
+  formEncType?: string
+  formMethod?: string
+  formNoValidate?: boolean
+  formTarget?: string
+  id?: string
+  name?: string
+  onBlur?: (FocusEvent<Target>) => void
+  onClick?: (MouseEvent<FocusableElement>) => void
+  onFocus?: (FocusEvent<Target>) => void
+  onFocusChange?: (boolean) => void
+  onHoverChange?: (boolean) => void
+  onHoverEnd?: (HoverEvent) => void
+  onHoverStart?: (HoverEvent) => void
+  onKeyDown?: (KeyboardEvent) => void
+  onKeyUp?: (KeyboardEvent) => void
+  onPress?: (PressEvent) => void
+  onPressChange?: (boolean) => void
+  onPressEnd?: (PressEvent) => void
+  onPressStart?: (PressEvent) => void
+  onPressUp?: (PressEvent) => void
+  preventFocusOnPress?: boolean
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, ButtonRenderProps>
+  size?: 'S' | 'M' | 'L' | 'XL'
+  slot?: string | null
+  styles?: StylesProp
+  type?: 'button' | 'submit' | 'reset' = 'button'
+  value?: string
+}

/@react-spectrum/s2-ai:MessageSuggestionContext

+MessageSuggestionContext {
+  UNTYPED
+}

/@react-spectrum/s2-ai:MessageSuggestionList

+MessageSuggestionList {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  children: ReactNode
+  id?: string
+  size?: 'S' | 'M' | 'L' | 'XL'
+  slot?: string | null
+  styles?: StylesProp
+  title: string
+}

/@react-spectrum/s2-ai:MessageSuggestionListContext

+MessageSuggestionListContext {
+  UNTYPED
+}

/@react-spectrum/s2-ai:ResponseStatus

+ResponseStatus {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  children: ReactNode
+  defaultExpanded?: boolean
+  density?: 'compact' | 'regular' | 'spacious' = 'regular'
+  id?: Key
+  isDisabled?: boolean
+  isExpanded?: boolean
+  isLoading?: boolean
+  onExpandedChange?: (boolean) => void
+  size?: 'S' | 'M' | 'L' | 'XL' = 'M'
+  slot?: string | null
+  styles?: StylesProp
+}

/@react-spectrum/s2-ai:ResponseStatusPanel

+ResponseStatusPanel {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  children: React.ReactNode
+  id?: string
+  label?: ReactNode
+  labelElementType?: ElementType = 'label'
+  role?: 'group' | 'region' = 'group'
+}

/@react-spectrum/s2-ai:UserMessage

+UserMessage {
+  UNSAFE_className?: UnsafeClassName
+  UNSAFE_style?: CSSProperties
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  children: ReactNode
+  id?: string
+  slot?: string | null
+  styles?: StylesProp
+}

@rspbot

rspbot commented Jun 4, 2026

Copy link
Copy Markdown

Agent Skills Changes

Modified (1)
Install

React Spectrum S2:

npx skills add https://d1pzu54gtk2aed.cloudfront.net/pr/95b1f8125c8422d9a6c97de64c3b90f7428d6451/

React Aria:

npx skills add https://d5iwopk28bdhl.cloudfront.net/pr/95b1f8125c8422d9a6c97de64c3b90f7428d6451/

@LFDanLu LFDanLu added this pull request to the merge queue Jun 5, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 5, 2026
@yihuiliao yihuiliao added this pull request to the merge queue Jun 5, 2026
Merged via the queue into main with commit 2157581 Jun 5, 2026
30 checks passed
@yihuiliao yihuiliao deleted the setup-ai-components-package branch June 5, 2026 18:08
@snowystinger snowystinger mentioned this pull request Jun 10, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants