Skip to content
Merged
Show file tree
Hide file tree
Changes from 88 commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
40636a0
feat(ui-components): introduce chat components
FabienMotte Jul 23, 2025
6f1ed20
chore: add passphrase input
FabienMotte Jul 23, 2025
0b70363
wip
Haroenv Jul 30, 2025
78ed732
toggle and header part of satellite
Haroenv Aug 1, 2025
62880e9
revert js example
shaejaz Sep 3, 2025
9f83bde
revert react example
shaejaz Sep 3, 2025
13b1e28
remove todo
shaejaz Sep 3, 2025
556c1d0
remove toggle button duplicate
shaejaz Sep 3, 2025
5d2e92b
add tests
shaejaz Sep 3, 2025
6ceffff
update bundlesize
shaejaz Sep 3, 2025
dd12b04
fix lint
shaejaz Sep 3, 2025
b095c41
Merge branch 'master' into feat/chat-components
shaejaz Sep 4, 2025
040c035
markdown
Haroenv Aug 29, 2025
8adc9ef
add chat component
shaejaz Sep 4, 2025
4b2531e
update chat message
shaejaz Sep 4, 2025
deb8c8a
fix polyfill errors
shaejaz Sep 4, 2025
5ca5ec2
update lock file
shaejaz Sep 4, 2025
000f255
add chat test
shaejaz Sep 4, 2025
adf7f8f
address comments
shaejaz Sep 4, 2025
95a463e
address comments
shaejaz Sep 5, 2025
032cd1d
add base lib in is.js
shaejaz Sep 5, 2025
c978854
add hook in react core
shaejaz Sep 5, 2025
51f350f
add react widget
shaejaz Sep 5, 2025
c2df1ac
move transport handling to hook
shaejaz Sep 5, 2025
205ab67
update types
shaejaz Sep 8, 2025
2c7406e
export it anyway
Haroenv Sep 8, 2025
8b26037
add chat component props
shaejaz Sep 8, 2025
88cf87d
add refine button
shaejaz Sep 8, 2025
69ab089
address comment
shaejaz Sep 8, 2025
5cc91f7
add tests
shaejaz Sep 9, 2025
2609211
update tools usage
shaejaz Sep 10, 2025
09e8b51
fix ci
shaejaz Sep 10, 2025
e5e5259
fix type error
shaejaz Sep 10, 2025
e71e483
address comments
shaejaz Sep 10, 2025
9041a1a
Merge branch 'master' into feat/react-chat
shaejaz Sep 10, 2025
2229762
fix is version
shaejaz Sep 10, 2025
bfc6b25
just one zod
Haroenv Sep 10, 2025
1dd7a0a
add more tests
shaejaz Sep 11, 2025
729f629
use user provided search index tool
shaejaz Sep 11, 2025
678e292
init
shaejaz Aug 13, 2025
52ea485
remove stick to bottom hook
shaejaz Aug 13, 2025
989b274
move ui to ui components
shaejaz Aug 13, 2025
93d7756
add chat components
shaejaz Aug 14, 2025
f5b06d3
fix widget types
shaejaz Aug 15, 2025
3fea3b3
forward props
shaejaz Aug 18, 2025
e82095a
wip: use ai dependency in connector
Haroenv Aug 18, 2025
e219a32
move usechat to react core
shaejaz Aug 19, 2025
c136118
remove is components and widgets
shaejaz Aug 19, 2025
ad39f22
remove unecessary react widgets
shaejaz Aug 19, 2025
84b919c
fix test
shaejaz Aug 20, 2025
3b1b853
deps
Haroenv Aug 20, 2025
527a280
revert rollup alias
shaejaz Aug 20, 2025
38c0d1c
init
shaejaz Aug 28, 2025
373ce70
use carousel
shaejaz Aug 29, 2025
a39aece
add tools prop
shaejaz Sep 2, 2025
3473921
add carousel default tool
shaejaz Sep 2, 2025
bfc6910
add transport to props
shaejaz Sep 2, 2025
d79465c
feat(react-instantsearch-ui): implement chat components design
FabienMotte Sep 11, 2025
5cd47c9
chore: review considered
FabienMotte Sep 15, 2025
72375c6
chore: review considered
FabienMotte Sep 16, 2025
5da6059
remove
FabienMotte Sep 16, 2025
692b1d0
chore: review considered
FabienMotte Sep 16, 2025
3ddfcde
chore: review considered
FabienMotte Sep 16, 2025
37ecdbd
chore: revert lock
FabienMotte Sep 16, 2025
1b4c24f
chore: clean types
FabienMotte Sep 17, 2025
a493c5d
chore: fix types
FabienMotte Sep 17, 2025
33da4d5
chore: clean comments
FabienMotte Sep 17, 2025
e83ff45
chore: fix conflicts
FabienMotte Sep 18, 2025
002ce28
feat: add clear button
FabienMotte Sep 18, 2025
69eecf6
feat: add default chat prompt disclaimer
FabienMotte Sep 18, 2025
6d392a5
chore: revert
FabienMotte Sep 18, 2025
3580993
chore: use polyfills
FabienMotte Sep 18, 2025
d7fc569
chore: revert
FabienMotte Sep 18, 2025
c59e143
chore: revert
FabienMotte Sep 18, 2025
20d493f
feat: add header/prompt gradients
FabienMotte Sep 18, 2025
8ef5bff
fix: chat prompt gradient z-index
FabienMotte Sep 18, 2025
39dfe62
refactor: move `use-stick-to-bottom` to Chat widget
FabienMotte Sep 18, 2025
f17db28
chore: add `useStickToBottom` hook instead of lib
FabienMotte Sep 18, 2025
87851d2
fix: mobile fixes
FabienMotte Sep 18, 2025
37656d9
chore: update bundlesizes
FabienMotte Sep 18, 2025
daf1409
Merge master into feat/chat-design
FabienMotte Sep 18, 2025
fbafb54
test: fix
FabienMotte Sep 19, 2025
85332c0
test: fix lint
FabienMotte Sep 19, 2025
43716b2
test: fix
FabienMotte Sep 19, 2025
e8a8eab
chore: remove example
FabienMotte Sep 22, 2025
194f3eb
fix: chat message image styles
FabienMotte Sep 22, 2025
4063a99
refactor: move chat styles to components folder
FabienMotte Sep 22, 2025
830c6fb
chore: fix bundlesize
FabienMotte Sep 22, 2025
484d358
chore: revert yarn lock
FabienMotte Sep 22, 2025
655600b
chore: revert yarn lock
FabienMotte Sep 22, 2025
f827ceb
chore: avoid casting as unknown
FabienMotte Sep 22, 2025
9b0939c
chore: review considered
FabienMotte Sep 22, 2025
1f193ff
chore: fix type issue
FabienMotte Sep 22, 2025
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
18 changes: 13 additions & 5 deletions bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
{
"path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js",
"maxSize": "77.5 kB"
"maxSize": "81.25 kB"
},
{
"path": "packages/vue-instantsearch/vue2/umd/index.js",
Expand All @@ -42,11 +42,11 @@
},
{
"path": "./packages/instantsearch.css/themes/algolia.css",
"maxSize": "4 kB"
"maxSize": "6.75 kB"
},
{
"path": "./packages/instantsearch.css/themes/algolia-min.css",
"maxSize": "3.75 kB"
"maxSize": "6.25 kB"
},
{
"path": "./packages/instantsearch.css/themes/reset.css",
Expand All @@ -58,11 +58,19 @@
},
{
"path": "./packages/instantsearch.css/themes/satellite.css",
"maxSize": "7 kB"
"maxSize": "7.90 kB"
},
{
"path": "./packages/instantsearch.css/themes/satellite-min.css",
"maxSize": "6.25 kB"
"maxSize": "7.25 kB"
},
{
"path": "./packages/instantsearch.css/components/chat.css",
"maxSize": "3.50 kB"
},
{
"path": "./packages/instantsearch.css/components/chat-min.css",
"maxSize": "3.25 kB"
}
]
}
45 changes: 31 additions & 14 deletions packages/instantsearch-ui-components/src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ import { createChatMessagesComponent } from './ChatMessages';
import { createChatPromptComponent } from './ChatPrompt';
import { createChatToggleButtonComponent } from './ChatToggleButton';

import type { Renderer } from '../../types';
import type { MutableRef, Renderer } from '../../types';
import type { ChatHeaderProps } from './ChatHeader';
import type { ChatMessagesProps } from './ChatMessages';
import type { ChatPromptProps } from './ChatPrompt';
import type { ChatToggleButtonProps } from './ChatToggleButton';

export type ChatClassNames = {
container?: string | string[];
root?: string | string[];
};

export type ChatProps = {
/*
* Whether the chat is open or closed.
*/
open: boolean;
/*
* Whether the chat is maximized or not.
*/
maximized?: boolean;
/*
* Props for the ChatHeader component.
*/
Expand Down Expand Up @@ -53,27 +57,40 @@ export function createChatComponent({ createElement, Fragment }: Renderer) {
const ChatMessages = createChatMessagesComponent({ createElement, Fragment });
const ChatPrompt = createChatPromptComponent({ createElement, Fragment });

const promptRef: MutableRef<HTMLTextAreaElement | null> = { current: null };

return function Chat({
open,
maximized = false,
headerProps,
toggleButtonProps,
messagesProps,
promptProps,
promptProps = {},
classNames = {},
}: ChatProps) {
return (
<>
{!open ? (
<ChatToggleButton {...toggleButtonProps} />
) : (
<div className={cx('ais-Chat-container', classNames.container)}>
<ChatHeader {...headerProps} />
<ChatMessages {...messagesProps} />
<div className={cx('ais-Chat', maximized && 'ais-Chat--maximized')}>
<div
className={cx(
'ais-Chat-container',
open && 'ais-Chat-container--open',
maximized && 'ais-Chat-container--maximized',
classNames.root
)}
>
<ChatHeader {...headerProps} maximized={maximized} />
<ChatMessages {...messagesProps} />
<ChatPrompt {...promptProps} ref={promptRef} />
</div>

<ChatPrompt {...promptProps} />
</div>
)}
</>
<ChatToggleButton
{...toggleButtonProps}
onClick={() => {
toggleButtonProps.onClick?.();
promptRef.current?.focus();
}}
/>
</div>
);
};
}
179 changes: 163 additions & 16 deletions packages/instantsearch-ui-components/src/components/chat/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,204 @@
/** @jsx createElement */
import { cx } from '../../lib';

import {
SparklesIconComponent,
MaximizeIconComponent as MaximizeIconComponentDefault,
MinimizeIconComponent as MinimizeIconComponentDefault,
CloseIconComponent as CloseIconComponentDefault,
} from './icons';

import type { Renderer, ComponentProps } from '../../types';

export type ChatHeaderTranslations = {
/**
* The title to display in the header
*/
title: string;
/**
* Accessible label for the minimize button
*/
minimizedLabel: string;
/**
* Accessible label for the maximize button
*/
maximizeLabel: string;
/**
* Accessible label for the close button
*/
closeLabel: string;
/**
* Text for the clear button
*/
clearLabel: string;
};

export type ChatHeaderClassNames = {
/**
* Class names to apply to the root element
*/
root?: string | string[];
/**
* Class names to apply to the title element
*/
title?: string | string[];
/**
* Class names to apply to the title icon element
*/
titleIcon?: string | string[];
/**
* Class names to apply to the maximize button element
*/
maximize?: string | string[];
/**
* Class names to apply to the close button element
*/
close?: string | string[];
/**
* Class names to apply to the clear button element
*/
clear?: string | string[];
};

export type ChatHeaderProps = Omit<ComponentProps<'div'>, 'title'> & {
export type ChatHeaderProps = ComponentProps<'div'> & {
/**
* The title to display in the header
* Whether the chat is maximized
*/
maximized?: boolean;
/**
* Callback when the maximize button is clicked
*/
title?: string;
onToggleMaximize?: () => void;
/**
* Callback when the close button is clicked
*/
onClose: () => void;
/**
* Accessible label for the close button
* Callback when the clear button is clicked
*/
onClear?: () => void;
/**
* Whether the clear button is enabled
*/
canClear?: boolean;
/**
* Optional close icon component
*/
closeLabel?: string;
closeIconComponent?: () => JSX.Element;
/**
* Optional minimize icon component
*/
minimizeIconComponent?: () => JSX.Element;
/**
* Optional maximize icon component
*/
maximizeIconComponent?: (props: { maximized: boolean }) => JSX.Element;
/**
* Optional title icon component (defaults to sparkles)
*/
titleIconComponent?: () => JSX.Element;
/**
* Optional class names for elements
*/
classNames?: Partial<ChatHeaderClassNames>;
/**
* Optional translations
*/
translations?: Partial<ChatHeaderTranslations>;
};

export function createChatHeaderComponent({ createElement }: Renderer) {
return function ChatHeader({
title = 'Chat',
maximized = false,
onToggleMaximize,
onClose,
closeLabel = 'Close chat',
onClear,
canClear = false,
closeIconComponent: CloseIconComponent,
minimizeIconComponent: MinimizeIconComponent,
maximizeIconComponent: MaximizeIconComponent,
titleIconComponent: TitleIconComponent,
classNames = {},
translations: userTranslations,
...props
}: ChatHeaderProps) {
const translations: Required<ChatHeaderTranslations> = {
title: 'Chat',
minimizedLabel: 'Minimize chat',
maximizeLabel: 'Maximize chat',
closeLabel: 'Close chat',
clearLabel: 'Clear',
...userTranslations,
};

const defaultMaximizeIcon = maximized ? (
<MinimizeIconComponentDefault createElement={createElement} />
) : (
<MaximizeIconComponentDefault createElement={createElement} />
);

return (
<div
className={cx('ais-ChatHeader', classNames.root, props.className)}
{...props}
>
<span className={cx('ais-ChatHeader-title', classNames.title)}>
{title}
<span
className={cx('ais-ChatHeader-titleIcon', classNames.titleIcon)}
>
{TitleIconComponent ? (
<TitleIconComponent />
) : (
<SparklesIconComponent
createElement={createElement}
width={20}
height={20}
/>
)}
</span>
{translations.title}
</span>
<button
className={cx('ais-ChatHeader-close', classNames.close)}
onClick={onClose}
aria-label={closeLabel}
type="button"
>
×
</button>
<div className={cx('ais-ChatHeader-actions')}>
{onClear && (
<button
className={cx('ais-ChatHeader-clear', classNames.clear)}
onClick={onClear}
disabled={!canClear}
type="button"
>
{translations.clearLabel}
</button>
)}
<button
className={cx('ais-ChatHeader-maximize', classNames.maximize)}
onClick={onToggleMaximize}
aria-label={
maximized
? translations.minimizedLabel
: translations.maximizeLabel
}
type="button"
>
{MaximizeIconComponent ? (
<MaximizeIconComponent maximized={maximized} />
) : (
defaultMaximizeIcon
)}
</button>
<button
className={cx('ais-ChatHeader-close', classNames.close)}
onClick={onClose}
aria-label={translations.closeLabel}
title={translations.closeLabel}
type="button"
>
{CloseIconComponent ? (
<CloseIconComponent />
) : (
<CloseIconComponentDefault createElement={createElement} />
)}
</button>
</div>
</div>
);
};
Expand Down
Loading