Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e81d477
chore: setup S2 AI components package
snowystinger May 22, 2026
d9e1578
fix all the lints
snowystinger May 22, 2026
94d0817
cleanup
snowystinger May 22, 2026
9eae6b9
fix lint
snowystinger May 22, 2026
ae6bb8c
add test, fix implementation, remove extra subpaths, split components
snowystinger May 22, 2026
d872fe4
fix ts
snowystinger May 22, 2026
71c5fe2
fix build
snowystinger May 22, 2026
33cf8f5
Merge branch 'main' into setup-ai-components-package
snowystinger May 22, 2026
89b7e76
Merge branch 'main' into setup-ai-components-package
snowystinger May 28, 2026
124504c
copy code and reduce new exports
snowystinger May 28, 2026
e194b37
reduce diff
snowystinger May 28, 2026
29a82b7
undo
snowystinger May 28, 2026
87f3c5a
fix all the things
snowystinger May 28, 2026
783f5ad
feat: messagesource/sourcelist/responsestatus (#10079)
yihuiliao Jun 1, 2026
3176d6c
feat: add MessageFeedback + UserMessage (#10086)
DPandyan Jun 1, 2026
51cfeee
Merge branch 'main' into setup-ai-components-package
yihuiliao Jun 1, 2026
6f97b01
fix install?
yihuiliao Jun 1, 2026
cf56a64
Revert "fix install?"
yihuiliao Jun 1, 2026
7cc67b2
fix install...
yihuiliao Jun 1, 2026
154cd4f
add messagesuggestionlist/messagesuggestion component
yihuiliao Jun 1, 2026
8027f0c
fix format and lint
yihuiliao Jun 1, 2026
c20b128
cleanup
yihuiliao Jun 2, 2026
410428b
fix lint
yihuiliao Jun 2, 2026
5652e92
more cleanup
yihuiliao Jun 2, 2026
17e5090
fix: convert MessageFeedback to RSP ToggleButtonGroup, address PR com…
DPandyan Jun 2, 2026
004dd0c
move stuff to peer deps instead of dep
LFDanLu Jun 2, 2026
eeddba4
remove one more dep and update lockfile
LFDanLu Jun 2, 2026
4c4d967
followup
yihuiliao Jun 2, 2026
8aaf7f2
fix: remove modalities in favor of of percentage
DPandyan Jun 2, 2026
6f60281
fix lint
yihuiliao Jun 2, 2026
dd51170
fix format
yihuiliao Jun 2, 2026
b705222
add export
yihuiliao Jun 2, 2026
0dabd25
pls fix the tests
yihuiliao Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,11 @@
}
},
{
"files": ["packages/@react-spectrum/s2/**", "packages/dev/s2-docs/**"],
"files": [
"packages/@react-spectrum/s2/**",
"packages/dev/s2-docs/**",
"packages/@react-spectrum/s2-ai/**"
],
"rules": {
"react/react-in-jsx-scope": "off"
}
Expand Down
3 changes: 2 additions & 1 deletion .storybook-s2/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const localAddon = (rel: string) => fileURLToPath(import.meta.resolve(rel));
const config: StorybookConfig = {
stories: [
'./docs/*.mdx',
'../packages/@react-spectrum/s2/stories/*.stories.@(js|jsx|mjs|ts|tsx)'
'../packages/@react-spectrum/s2/stories/*.stories.@(js|jsx|mjs|ts|tsx)',
'../packages/@react-spectrum/s2-ai/stories/*.stories.@(js|jsx|mjs|ts|tsx)'
],
addons: [
localAddon('./custom-addons/provider/preset.ts'),
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module.exports = {
moduleNameMapper: {
'^bundle-text:.*\\.svg$': '<rootDir>/__mocks__/fileMock.js',
'\\.svg$': '<rootDir>/__mocks__/svg.js',
'^@react-spectrum/s2/icons/.+$': '<rootDir>/__mocks__/svg.js',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
'\\.(css|styl)$': 'identity-obj-proxy'
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@
"packages/@adobe/react-spectrum/src/color/*.tsx",
"packages/@react-spectrum/s2/**/*.{js,ts,tsx}",
"packages/@react-spectrum/s2/stories/**",
"packages/@react-spectrum/s2-ai/**/*.{js,ts,tsx}",
"packages/@react-spectrum/s2-ai/stories/**",
".storybook-s2/**",
"packages/dev/s2-docs/**",
"starters/**"
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2-ai/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

3 changes: 3 additions & 0 deletions packages/@react-spectrum/s2-ai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @react-spectrum/s2-ai
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.

Do we want to just call it @react-spectrum/ai?

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.

im fine with that. s2 might imply spectrum and i don't know how closely linked they want to be


This package is part of [react-spectrum](https://github.com/adobe/react-spectrum). See the repo for more details.
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2-ai/exports/AssetList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {Asset, AssetList} from '../src/AssetList';
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2-ai/exports/HorizontalCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {BasicHorizontalCard, HorizontalCard, CardPreview} from '../src/HorizontalCard';
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2-ai/exports/MessageFeedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {MessageFeedback} from '../src/MessageFeedback';
15 changes: 15 additions & 0 deletions packages/@react-spectrum/s2-ai/exports/MessageSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export {
MessageSource,
MessageSourceContext,
SourceList,
SourceListItem,
NumberBadge,
NumberBadgeContext
} from '../src/MessageSource';

export type {
MessageSourceProps,
SourceListProps,
SourceListItemProps,
NumberBadgeProps
} from '../src/MessageSource';
12 changes: 12 additions & 0 deletions packages/@react-spectrum/s2-ai/exports/ResponseStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export {
ResponseStatus,
ResponseStatusContext,
ResponseStatusTitle,
ResponseStatusPanel
} from '../src/ResponseStatus';

export type {
ResponseStatusProps,
ResponseStatusTitleProps,
ResponseStatusPanelProps
} from '../src/ResponseStatus';
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2-ai/exports/UserMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {UserMessage} from '../src/UserMessage';
4 changes: 4 additions & 0 deletions packages/@react-spectrum/s2-ai/exports/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {Asset, AssetList} from '../src/AssetList';
export {BasicHorizontalCard, HorizontalCard} from '../src/HorizontalCard';
export {MessageFeedback} from '../src/MessageFeedback';
export {UserMessage} from '../src/UserMessage';
5 changes: 5 additions & 0 deletions packages/@react-spectrum/s2-ai/intl/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"messagefeedback.thumbDown": "Bad response",
"messagefeedback.thumbUp": "Good response",
"responsestatus.loading": "Loading"
}
90 changes: 90 additions & 0 deletions packages/@react-spectrum/s2-ai/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"name": "@react-spectrum/s2-ai",
"version": "1.0.0-alpha.1",
"description": "Spectrum 2 UI AI components in React",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/adobe/react-spectrum"
},
"source": "exports/index.ts",
"files": [
"dist",
"src",
"exports",
"*.js"
],
"sideEffects": [
"*.css"
],
"main": "./dist/exports/index.cjs",
"module": "./dist/exports/index.mjs",
"types": "./dist/types/exports/index.d.ts",
"exports": {
".": {
"source": "./exports/index.ts",
"types": "./dist/types/exports/index.d.ts",
"module": "./dist/exports/index.mjs",
"import": "./dist/exports/index.mjs",
"require": "./dist/exports/index.cjs"
},
"./package.json": "./package.json",
"./*": {
"source": "./exports/*.ts",
"types": "./dist/types/exports/*.d.ts",
"import": "./dist/exports/*.mjs",
"require": "./dist/exports/*.cjs"
},
"./private/*": null
},
"publishConfig": {
"access": "public"
},
"scripts": {
"prepack": "npm pkg delete scripts devDependencies resolutions alias targets",
"postpack": "git checkout -- package.json"
},
"dependencies": {
"@internationalized/date": "^3.12.2",
"@internationalized/number": "^3.6.7",
"@parcel/macros": "^2.16.3",
"@react-spectrum/s2": "^1.4.0",
"@react-types/shared": "^3.35.0",
"react-aria": "3.49.0",
"react-aria-components": "1.18.0",
"react-stately": "3.47.0"
},
"devDependencies": {
"@react-aria/test-utils": "^1.0.0-alpha.8",
"@storybook/jest": "^0.2.3",
"@testing-library/dom": "^10.1.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.0.0",
"react": "^19.0.0-rc.1",
"react-dom": "^19.0.0-rc.1"
},
"peerDependencies": {
"react": "^19.0.0-rc.1",
"react-dom": "^19.0.0-rc.1"
},
"browserslist": "last 2 Chrome versions, last 2 Safari versions, last 2 Firefox versions, last 2 Edge versions",
"targets": {
"module": false,
"main": false,
"types": false,
"exports-module": {
"source": "exports/*.ts",
"distDir": "dist",
"isLibrary": true,
"outputFormat": "esmodule",
"includeNodeModules": false
},
"exports-main": {
"source": "exports/*.ts",
"distDir": "dist",
"isLibrary": true,
"outputFormat": "commonjs",
"includeNodeModules": false
}
}
}
151 changes: 151 additions & 0 deletions packages/@react-spectrum/s2-ai/src/AssetList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {AriaLabelingProps, DOMRef} from '@react-types/shared';
import {baseColor, focusRing, style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {BasicHorizontalCard} from './HorizontalCard';
import {Button} from 'react-aria-components/Button';
import {CardProps} from '@react-spectrum/s2/Card';
import Close from '@react-spectrum/s2/icons/Close';
import {controlSize, getAllowedOverrides} from './style-utils-copy' with {type: 'macro'};
import {forwardRef, useRef} from 'react';
import {iconStyle} from '@react-spectrum/s2/style' with {type: 'macro'};
import {pressScale} from '@react-spectrum/s2/pressScale';
import {Tag, TagGroup, TagList} from 'react-aria-components/TagGroup';
import {useDOMRef} from './useDOMRef';

const hoverBackground = {
default: 'gray-200',
isStaticColor: 'transparent-overlay-200'
} as const;

const styles = style<{
isDisabled: boolean;
isHovered: boolean;
isFocusVisible: boolean;
isPressed: boolean;
size: 'S' | 'M' | 'L' | 'XL';
}>(
{
...focusRing(),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
size: controlSize(),
flexShrink: 0,
borderRadius: 'full',
padding: 0,
borderStyle: 'none',
transition: 'default',
backgroundColor: {
default: 'gray-200',
isHovered: hoverBackground,
isFocusVisible: hoverBackground,
isPressed: hoverBackground
},
'--iconPrimary': {
type: 'color',
value: {
default: baseColor('neutral'),
isDisabled: 'disabled',
forcedColors: {
default: 'ButtonText',
isDisabled: 'GrayText'
}
}
},
outlineColor: {
default: 'focus-ring',
forcedColors: 'Highlight'
},
disableTapHighlight: true
},
getAllowedOverrides()
);

const closeIconStyle = ({size = 'M'}) => {
if (size === 'S') return iconStyle({size: 'XS'});
else if (size === 'M') return iconStyle({size: 'S'});
else if (size === 'L') return iconStyle({size: 'M'});
else if (size === 'XL') return iconStyle({size: 'L'});
else return iconStyle({size: 'S'});
};

const CloseButton = function CloseButton(props) {
let ref = useRef(null);
return (
<Button
{...props}
ref={ref}
slot="remove"
style={pressScale(ref, {})}
className={renderProps => styles({...renderProps, size: props.size || 'M'}, props.styles)}>
<Close styles={closeIconStyle({size: props.size ?? 'M'})} />
</Button>
);
};

let assetListStyles = style({}, getAllowedOverrides());

export const AssetList = forwardRef(function AssetList(props: any, ref: DOMRef<HTMLDivElement>) {
let domRef = useDOMRef(ref);
return (
<TagGroup {...props} className={assetListStyles(props.styles)} ref={domRef}>
<TagList
className={style({
display: 'flex',
flexDirection: 'row',
gap: 16,
flexWrap: 'wrap',
alignItems: 'center',
width: 'full'
})}>
{props.children}
</TagList>
</TagGroup>
);
});

export const Asset = forwardRef(function Asset(
Comment thread
yihuiliao marked this conversation as resolved.
Outdated
props: CardProps & AriaLabelingProps,
ref: DOMRef<HTMLDivElement>
) {
let {
textValue,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
'aria-describedby': ariaDescribedby,
...otherProps
} = props;
let domRef = useDOMRef(ref);
return (
<Tag
textValue={textValue}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledby}
aria-describedby={ariaDescribedby}
ref={domRef}
className={style({flexShrink: 0, flexGrow: 0, position: 'relative'})}>
Comment thread
yihuiliao marked this conversation as resolved.
Outdated
<BasicHorizontalCard {...otherProps}>{props.children}</BasicHorizontalCard>
{/** Definitely not a close button, though looks like one. */}
<div
className={style({
position: 'absolute',
top: 0,
insetEnd: 0,
transform: 'translate(50%, -50%)'
})}>
<CloseButton size="XS" />
</div>
</Tag>
);
});
54 changes: 54 additions & 0 deletions packages/@react-spectrum/s2-ai/src/CenterBaseline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {css} from './style-macro' with {type: 'macro'};
import {CSSProperties, ReactNode} from 'react';
import {DOMAttributes} from '@react-types/shared';
import {filterDOMProps} from 'react-aria/filterDOMProps';
import {mergeStyles} from './mergeStyles';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
import {StyleString} from './types';

interface CenterBaselineProps extends DOMAttributes {
style?: CSSProperties;
styles?: StyleString;
children: ReactNode;
slot?: string;
}

const styles = style({
display: 'flex',
alignItems: 'center'
});

export function CenterBaseline(props: CenterBaselineProps): ReactNode {
let domProps = filterDOMProps(props);
return (
<div
{...domProps}
slot={props.slot}
style={props.style}
className={mergeStyles(styles, props.styles) + ' ' + centerBaselineBefore}>
{props.children}
</div>
);
}

export const centerBaselineBefore = css(
'&::before { content: "\u00a0"; width: 0; visibility: hidden }'
);

export function centerBaseline(
props: Omit<CenterBaselineProps, 'children'> = {}
): (icon: ReactNode) => ReactNode {
return (icon: ReactNode) => <CenterBaseline {...props}>{icon}</CenterBaseline>;
}
Loading