@gravity-ui/aikit 2.19.2 → 2.19.3-beta.af9c6fb1cce2b60fd6c198cca08d00a762b16671.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/build/cjs/components/molecules/ButtonGroup/ButtonGroup.css +1 -1
  2. package/build/cjs/components/molecules/Suggestions/Suggestions.css +4 -0
  3. package/build/cjs/components/molecules/Suggestions/Suggestions.js +1 -1
  4. package/build/cjs/components/molecules/Suggestions/Suggestions.js.map +1 -1
  5. package/build/cjs/components/organisms/Header/Header.css +23 -1
  6. package/build/cjs/components/organisms/Header/Header.js +6 -3
  7. package/build/cjs/components/organisms/Header/Header.js.map +1 -1
  8. package/build/cjs/components/organisms/Header/types.d.ts +2 -0
  9. package/build/cjs/components/organisms/Header/types.js.map +1 -1
  10. package/build/cjs/components/organisms/Header/useHeader.d.ts +1 -0
  11. package/build/cjs/components/organisms/Header/useHeader.js +2 -1
  12. package/build/cjs/components/organisms/Header/useHeader.js.map +1 -1
  13. package/build/cjs/components/organisms/PromptInput/PromptInput.css +2 -2
  14. package/build/cjs/components/pages/ChatContainer/ChatContainer.css +67 -5
  15. package/build/cjs/components/pages/ChatContainer/ChatContainer.js +6 -2
  16. package/build/cjs/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  17. package/build/cjs/components/pages/ChatContainer/types.d.ts +18 -0
  18. package/build/cjs/components/pages/ChatContainer/types.js.map +1 -1
  19. package/build/cjs/components/templates/ChatContent/ChatContent.css +1 -1
  20. package/build/cjs/components/templates/History/ChatItem.d.ts +4 -1
  21. package/build/cjs/components/templates/History/ChatItem.js +10 -3
  22. package/build/cjs/components/templates/History/ChatItem.js.map +1 -1
  23. package/build/cjs/components/templates/History/History.css +22 -0
  24. package/build/cjs/components/templates/History/History.d.ts +2 -0
  25. package/build/cjs/components/templates/History/History.js +2 -2
  26. package/build/cjs/components/templates/History/History.js.map +1 -1
  27. package/build/cjs/components/templates/History/HistoryList.js +4 -2
  28. package/build/cjs/components/templates/History/HistoryList.js.map +1 -1
  29. package/build/cjs/package.json +1 -1
  30. package/build/cjs/themes/common.css +38 -2
  31. package/build/cjs/themes/variables.css +38 -2
  32. package/build/docs/components/molecules/ButtonGroup.md +3 -3
  33. package/build/docs/components/organisms/Header.md +15 -0
  34. package/build/docs/components/organisms/PromptInput.md +12 -10
  35. package/build/docs/components/pages/ChatContainer.md +120 -66
  36. package/build/docs/components/templates/ChatContent.md +5 -4
  37. package/build/docs/components/templates/History.md +59 -26
  38. package/build/docs/guides/THEMING.md +70 -19
  39. package/build/esm/components/molecules/ButtonGroup/ButtonGroup.css +1 -1
  40. package/build/esm/components/molecules/Suggestions/Suggestions.css +4 -0
  41. package/build/esm/components/molecules/Suggestions/Suggestions.js +1 -1
  42. package/build/esm/components/molecules/Suggestions/Suggestions.js.map +1 -1
  43. package/build/esm/components/organisms/Header/Header.css +23 -1
  44. package/build/esm/components/organisms/Header/Header.js +6 -3
  45. package/build/esm/components/organisms/Header/Header.js.map +1 -1
  46. package/build/esm/components/organisms/Header/types.d.ts +2 -0
  47. package/build/esm/components/organisms/Header/types.js.map +1 -1
  48. package/build/esm/components/organisms/Header/useHeader.d.ts +1 -0
  49. package/build/esm/components/organisms/Header/useHeader.js +2 -1
  50. package/build/esm/components/organisms/Header/useHeader.js.map +1 -1
  51. package/build/esm/components/organisms/PromptInput/PromptInput.css +2 -2
  52. package/build/esm/components/pages/ChatContainer/ChatContainer.css +67 -5
  53. package/build/esm/components/pages/ChatContainer/ChatContainer.js +6 -2
  54. package/build/esm/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  55. package/build/esm/components/pages/ChatContainer/types.d.ts +18 -0
  56. package/build/esm/components/pages/ChatContainer/types.js.map +1 -1
  57. package/build/esm/components/templates/ChatContent/ChatContent.css +1 -1
  58. package/build/esm/components/templates/History/ChatItem.d.ts +4 -1
  59. package/build/esm/components/templates/History/ChatItem.js +10 -3
  60. package/build/esm/components/templates/History/ChatItem.js.map +1 -1
  61. package/build/esm/components/templates/History/History.css +22 -0
  62. package/build/esm/components/templates/History/History.d.ts +2 -0
  63. package/build/esm/components/templates/History/History.js +2 -2
  64. package/build/esm/components/templates/History/History.js.map +1 -1
  65. package/build/esm/components/templates/History/HistoryList.js +4 -2
  66. package/build/esm/components/templates/History/HistoryList.js.map +1 -1
  67. package/build/esm/package.json +1 -1
  68. package/build/esm/themes/common.css +38 -2
  69. package/build/esm/themes/variables.css +38 -2
  70. package/docs/THEMING.md +70 -19
  71. package/llms-full.txt +77 -25
  72. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"HistoryList.js","sourceRoot":"../../../../../src","sources":["components/templates/History/HistoryList.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';\n\nimport {Button, DOMProps, InputControlSize, List, ListItemData, QAProps} from '@gravity-ui/uikit';\n\nimport {DateLocaleConfig} from '../../../hooks';\nimport {ChatType, ListItemChatData} from '../../../types';\nimport {ChatFilterFunction, defaultChatFilter, groupChatsByDate} from '../../../utils/chatUtils';\nimport {block} from '../../../utils/cn';\nimport {IntersectionContainer} from '../../atoms/IntersectionContainer';\nimport {Loader} from '../../atoms/Loader';\n\nimport {ChatItem} from './ChatItem';\nimport {DateHeaderItem} from './DateHeaderItem';\nimport {i18n} from './i18n';\n\nimport './History.scss';\n\nconst b = block('history');\n\n/** Result of lazy load more callback - either array of new chats or object with chats and hasMore flag */\nexport type LoadMoreLazyResult = ChatType[] | {chats: ChatType[]; hasMore?: boolean};\n\n/** Callback for lazy load more - returns only new chats, not previously loaded */\nexport type OnLoadMoreLazy = (offset: number) => Promise<LoadMoreLazyResult>;\n\n/** Load mode: full (button, parent manages all data) or lazy (scroll, component accumulates data) */\nexport type HistoryLoadMode = 'full' | 'lazy';\n\n/**\n * Props for the HistoryList component\n */\nexport interface HistoryListProps extends QAProps, DOMProps {\n /** Array of chat items */\n chats: ChatType[];\n /** Currently selected chat */\n selectedChat?: ChatType | null;\n /** Callback when a chat is selected */\n onSelectChat?: (chat: ChatType) => void;\n /** Callback when a chat is deleted */\n onDeleteChat?: (chat: ChatType) => Promise<void>;\n /**\n * Callback to load more chats.\n * - Full mode: () => void - triggers load, parent updates chats prop\n * - Lazy mode: (offset) => Promise<ChatType[]|{chats,hasMore}> - returns only new chats\n */\n onLoadMore?: (() => void) | OnLoadMoreLazy;\n /** Whether there are more chats to load */\n hasMore?: boolean;\n /**\n * Load mode: 'full' (button click, parent provides all data) or 'lazy' (scroll, callback returns new data only)\n * @default 'full'\n */\n loadMode?: HistoryLoadMode;\n /** Enable search functionality */\n searchable?: boolean;\n /** Group chats by date or none */\n groupBy?: 'date' | 'none';\n /** Custom dayjs format for non-relative date headers */\n dateFormat?: string;\n /** Locale for non-relative date headers */\n dateLocale?: string;\n /** Dayjs locale configuration for non-relative date headers */\n dateLocaleConfig?: DateLocaleConfig;\n /** Show action buttons (delete, etc.) */\n showActions?: boolean;\n /** Empty state placeholder */\n emptyPlaceholder?: React.ReactNode;\n /** Empty filtered state placeholder (when search returns no results) */\n emptyFilteredPlaceholder?: React.ReactNode;\n /** Placeholder text for the search/filter input (defaults to built-in i18n string) */\n searchPlaceholder?: string;\n /** Additional CSS class */\n className?: string;\n /** Custom filter function for search */\n filterFunction?: ChatFilterFunction;\n /** Loading state */\n loading?: boolean;\n /** Callback when chat is clicked (for closing popup in parent) */\n onChatClick?: (chat: ChatType) => void;\n /** Size of the list */\n size?: InputControlSize;\n}\n\n/**\n * HistoryList component - displays a list of chats with search, grouping, and actions\n *\n * @param props - {@link HistoryListProps}\n * @returns Rendered history list (search, grouping, lazy/full load, chat rows).\n */\nexport function HistoryList(props: HistoryListProps) {\n const {\n chats,\n selectedChat,\n onSelectChat,\n onDeleteChat,\n onLoadMore,\n hasMore = false,\n loadMode = 'full',\n searchable = true,\n groupBy = 'date',\n dateFormat,\n dateLocale,\n dateLocaleConfig,\n showActions = true,\n emptyPlaceholder,\n emptyFilteredPlaceholder,\n searchPlaceholder,\n className,\n qa,\n style,\n filterFunction = defaultChatFilter,\n loading = false,\n onChatClick,\n size = 'm',\n } = props;\n\n const [filteredItemCount, setFilteredItemCount] = useState<number | null>(null);\n const [loadedChats, setLoadedChats] = useState<ChatType[]>([]);\n const [lazyHasMore, setLazyHasMore] = useState(hasMore);\n const [deletedChats, setDeletedChats] = useState<Set<string>>(new Set());\n\n const loadingMoreRef = useRef(false);\n\n const displayChats = useMemo(() => {\n const totalChats = loadMode === 'lazy' ? [...chats, ...loadedChats] : chats;\n\n const filteredChats = totalChats.filter((chat) => !deletedChats.has(chat.id));\n\n return filteredChats;\n }, [loadMode, chats, loadedChats, deletedChats]);\n\n const displayChatsLengthRef = useRef(displayChats.length);\n displayChatsLengthRef.current = displayChats.length;\n\n useEffect(() => {\n if (loadMode === 'lazy') {\n setLazyHasMore(hasMore);\n }\n }, [loadMode, hasMore]);\n\n useEffect(() => {\n if (loadMode === 'lazy') {\n setLoadedChats([]);\n }\n }, [chats, loadMode]);\n\n const groupedChats = useMemo(() => {\n if (groupBy === 'none') {\n return new Map([['all', displayChats]]);\n }\n return groupChatsByDate(displayChats);\n }, [displayChats, groupBy]);\n\n const listItems: ListItemData<ListItemChatData>[] = useMemo(() => {\n const items: ListItemData<ListItemChatData>[] = [];\n\n const sortedGroups = Array.from(groupedChats.entries()).sort(([dateA], [dateB]) => {\n if (dateA === 'all') return 0;\n return new Date(dateB).getTime() - new Date(dateA).getTime();\n });\n\n sortedGroups.forEach(([dateKey, groupChats]) => {\n if (groupChats.length === 0) {\n return;\n }\n\n if (groupBy === 'date' && dateKey !== 'all') {\n items.push({\n type: 'date-header',\n disabled: true,\n date: dateKey,\n });\n }\n\n groupChats.forEach((chat) => {\n items.push({\n type: 'chat',\n ...chat,\n });\n });\n });\n\n return items;\n }, [groupedChats, groupBy]);\n\n const selectedItemIndex = useMemo(() => {\n return listItems.findIndex((item) => item.type === 'chat' && item.id === selectedChat?.id);\n }, [listItems, selectedChat]);\n\n const effectiveHasMore = loadMode === 'lazy' ? lazyHasMore : hasMore;\n\n /**\n * Unified load more handler.\n * full: fires onLoadMore(), parent manages data and hasMore\n * lazy: calls onLoadMore(offset), accumulates returned chats internally\n */\n const handleLoadMore = useCallback(async () => {\n if (!onLoadMore || loadingMoreRef.current) {\n return;\n }\n\n if (loadMode === 'full') {\n (onLoadMore as () => void)();\n return;\n }\n\n loadingMoreRef.current = true;\n try {\n const offset = displayChatsLengthRef.current;\n const result = await (onLoadMore as OnLoadMoreLazy)(offset);\n const newChats = Array.isArray(result) ? result : result.chats;\n const resultHasMore = Array.isArray(result) ? newChats.length > 0 : result.hasMore;\n setLoadedChats((prev) => [...prev, ...newChats]);\n setLazyHasMore(resultHasMore ?? newChats.length > 0);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('HistoryList: failed to load more chats', error);\n } finally {\n loadingMoreRef.current = false;\n }\n }, [onLoadMore, loadMode]);\n\n const handleChatClick = (\n chat: ListItemData<ListItemChatData>,\n _: number,\n fromKeyboard?: boolean,\n event?: React.KeyboardEvent<HTMLElement> | React.MouseEvent<HTMLElement>,\n ) => {\n if (fromKeyboard && (event?.target as HTMLButtonElement)?.type === 'button') {\n return;\n }\n onSelectChat?.(chat as ChatType);\n onChatClick?.(chat as ChatType);\n };\n\n const handleDeleteClick = async (e: React.MouseEvent, chat: ChatType) => {\n e.stopPropagation();\n try {\n await onDeleteChat?.(chat);\n setDeletedChats((prevDeletedChats) => new Set([...prevDeletedChats, chat.id]));\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('HistoryList: failed to delete chat', error);\n }\n };\n\n const wrappedFilterFunction = useMemo(() => {\n return (filter: string) => {\n const userFilter = filterFunction(filter);\n return (item: ListItemData<ListItemChatData>): boolean => {\n if (filter && item.type === 'date-header') {\n return false;\n }\n return userFilter(item);\n };\n };\n }, [filterFunction]);\n\n const renderItem = (item: ListItemData<ListItemChatData>, isActive: boolean) => {\n if (item.type === 'date-header') {\n return (\n <DateHeaderItem\n key={`date-${item.date}`}\n date={item.date}\n format={dateFormat}\n locale={dateLocale}\n localeConfig={dateLocaleConfig}\n />\n );\n }\n\n return (\n <ChatItem\n key={item.id}\n chat={item}\n showActions={showActions}\n onDeleteClick={onDeleteChat ? handleDeleteClick : undefined}\n isActive={isActive}\n />\n );\n };\n\n const emptyState = emptyPlaceholder || <div className={b('empty')}>{i18n('empty-state')}</div>;\n\n const emptyFilteredState = emptyFilteredPlaceholder || (\n <div className={b('empty')}>{i18n('empty-filtered-state')}</div>\n );\n\n const finalEmptyPlaceholder =\n filteredItemCount === null || filteredItemCount === listItems.length\n ? emptyState\n : emptyFilteredState;\n\n return (\n <div className={b('container', className)} data-qa={qa} style={style}>\n <div className={b('list-wrapper')}>\n {loading ? (\n <div className={b('loader-wrapper')}>\n <Loader view=\"loading\" />\n </div>\n ) : (\n <React.Fragment>\n <List\n size={size}\n items={listItems}\n renderItem={renderItem}\n virtualized={false}\n filterable={searchable}\n filterItem={wrappedFilterFunction}\n filterPlaceholder={searchPlaceholder ?? i18n('search-placeholder')}\n filterClassName={b('filter')}\n emptyPlaceholder={finalEmptyPlaceholder}\n selectedItemIndex={selectedItemIndex}\n itemsClassName={b('list')}\n itemClassName={b('list-item')}\n onFilterEnd={(data) => setFilteredItemCount(data.items.length)}\n onItemClick={handleChatClick}\n />\n {loadMode === 'lazy' && effectiveHasMore && onLoadMore && (\n <IntersectionContainer\n key={displayChats.length}\n onIntersect={handleLoadMore}\n className={b('lazy-loader')}\n >\n <Loader view=\"loading\" />\n </IntersectionContainer>\n )}\n </React.Fragment>\n )}\n </div>\n\n {loadMode === 'full' && hasMore && onLoadMore && (\n <Button view=\"flat-action\" size=\"m\" width=\"max\" onClick={handleLoadMore}>\n {i18n('action-load-more')}\n </Button>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"HistoryList.js","sourceRoot":"../../../../../src","sources":["components/templates/History/HistoryList.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';\n\nimport {Button, DOMProps, InputControlSize, List, ListItemData, QAProps} from '@gravity-ui/uikit';\n\nimport {DateLocaleConfig} from '../../../hooks';\nimport {useMobileControlSize} from '../../../hooks/useMobileControlSize';\nimport {ChatType, ListItemChatData} from '../../../types';\nimport {ChatFilterFunction, defaultChatFilter, groupChatsByDate} from '../../../utils/chatUtils';\nimport {block} from '../../../utils/cn';\nimport {IntersectionContainer} from '../../atoms/IntersectionContainer';\nimport {Loader} from '../../atoms/Loader';\n\nimport {ChatItem} from './ChatItem';\nimport {DateHeaderItem} from './DateHeaderItem';\nimport {i18n} from './i18n';\n\nimport './History.scss';\n\nconst b = block('history');\n\n/** Result of lazy load more callback - either array of new chats or object with chats and hasMore flag */\nexport type LoadMoreLazyResult = ChatType[] | {chats: ChatType[]; hasMore?: boolean};\n\n/** Callback for lazy load more - returns only new chats, not previously loaded */\nexport type OnLoadMoreLazy = (offset: number) => Promise<LoadMoreLazyResult>;\n\n/** Load mode: full (button, parent manages all data) or lazy (scroll, component accumulates data) */\nexport type HistoryLoadMode = 'full' | 'lazy';\n\n/**\n * Props for the HistoryList component\n */\nexport interface HistoryListProps extends QAProps, DOMProps {\n /** Array of chat items */\n chats: ChatType[];\n /** Currently selected chat */\n selectedChat?: ChatType | null;\n /** Callback when a chat is selected */\n onSelectChat?: (chat: ChatType) => void;\n /** Callback when a chat is deleted */\n onDeleteChat?: (chat: ChatType) => Promise<void>;\n /**\n * Callback to load more chats.\n * - Full mode: () => void - triggers load, parent updates chats prop\n * - Lazy mode: (offset) => Promise<ChatType[]|{chats,hasMore}> - returns only new chats\n */\n onLoadMore?: (() => void) | OnLoadMoreLazy;\n /** Whether there are more chats to load */\n hasMore?: boolean;\n /**\n * Load mode: 'full' (button click, parent provides all data) or 'lazy' (scroll, callback returns new data only)\n * @default 'full'\n */\n loadMode?: HistoryLoadMode;\n /** Enable search functionality */\n searchable?: boolean;\n /** Group chats by date or none */\n groupBy?: 'date' | 'none';\n /** Custom dayjs format for non-relative date headers */\n dateFormat?: string;\n /** Locale for non-relative date headers */\n dateLocale?: string;\n /** Dayjs locale configuration for non-relative date headers */\n dateLocaleConfig?: DateLocaleConfig;\n /** Show action buttons (delete, etc.) */\n showActions?: boolean;\n /** Empty state placeholder */\n emptyPlaceholder?: React.ReactNode;\n /** Empty filtered state placeholder (when search returns no results) */\n emptyFilteredPlaceholder?: React.ReactNode;\n /** Placeholder text for the search/filter input (defaults to built-in i18n string) */\n searchPlaceholder?: string;\n /** Additional CSS class */\n className?: string;\n /** Custom filter function for search */\n filterFunction?: ChatFilterFunction;\n /** Loading state */\n loading?: boolean;\n /** Callback when chat is clicked (for closing popup in parent) */\n onChatClick?: (chat: ChatType) => void;\n /** Size of the list */\n size?: InputControlSize;\n}\n\n/**\n * HistoryList component - displays a list of chats with search, grouping, and actions\n *\n * @param props - {@link HistoryListProps}\n * @returns Rendered history list (search, grouping, lazy/full load, chat rows).\n */\nexport function HistoryList(props: HistoryListProps) {\n const {\n chats,\n selectedChat,\n onSelectChat,\n onDeleteChat,\n onLoadMore,\n hasMore = false,\n loadMode = 'full',\n searchable = true,\n groupBy = 'date',\n dateFormat,\n dateLocale,\n dateLocaleConfig,\n showActions = true,\n emptyPlaceholder,\n emptyFilteredPlaceholder,\n searchPlaceholder,\n className,\n qa,\n style,\n filterFunction = defaultChatFilter,\n loading = false,\n onChatClick,\n size: sizeProp,\n } = props;\n\n const size = useMobileControlSize(sizeProp, 'm', 'xl');\n\n const [filteredItemCount, setFilteredItemCount] = useState<number | null>(null);\n const [loadedChats, setLoadedChats] = useState<ChatType[]>([]);\n const [lazyHasMore, setLazyHasMore] = useState(hasMore);\n const [deletedChats, setDeletedChats] = useState<Set<string>>(new Set());\n\n const loadingMoreRef = useRef(false);\n\n const displayChats = useMemo(() => {\n const totalChats = loadMode === 'lazy' ? [...chats, ...loadedChats] : chats;\n\n const filteredChats = totalChats.filter((chat) => !deletedChats.has(chat.id));\n\n return filteredChats;\n }, [loadMode, chats, loadedChats, deletedChats]);\n\n const displayChatsLengthRef = useRef(displayChats.length);\n displayChatsLengthRef.current = displayChats.length;\n\n useEffect(() => {\n if (loadMode === 'lazy') {\n setLazyHasMore(hasMore);\n }\n }, [loadMode, hasMore]);\n\n useEffect(() => {\n if (loadMode === 'lazy') {\n setLoadedChats([]);\n }\n }, [chats, loadMode]);\n\n const groupedChats = useMemo(() => {\n if (groupBy === 'none') {\n return new Map([['all', displayChats]]);\n }\n return groupChatsByDate(displayChats);\n }, [displayChats, groupBy]);\n\n const listItems: ListItemData<ListItemChatData>[] = useMemo(() => {\n const items: ListItemData<ListItemChatData>[] = [];\n\n const sortedGroups = Array.from(groupedChats.entries()).sort(([dateA], [dateB]) => {\n if (dateA === 'all') return 0;\n return new Date(dateB).getTime() - new Date(dateA).getTime();\n });\n\n sortedGroups.forEach(([dateKey, groupChats]) => {\n if (groupChats.length === 0) {\n return;\n }\n\n if (groupBy === 'date' && dateKey !== 'all') {\n items.push({\n type: 'date-header',\n disabled: true,\n date: dateKey,\n });\n }\n\n groupChats.forEach((chat) => {\n items.push({\n type: 'chat',\n ...chat,\n });\n });\n });\n\n return items;\n }, [groupedChats, groupBy]);\n\n const selectedItemIndex = useMemo(() => {\n return listItems.findIndex((item) => item.type === 'chat' && item.id === selectedChat?.id);\n }, [listItems, selectedChat]);\n\n const effectiveHasMore = loadMode === 'lazy' ? lazyHasMore : hasMore;\n\n /**\n * Unified load more handler.\n * full: fires onLoadMore(), parent manages data and hasMore\n * lazy: calls onLoadMore(offset), accumulates returned chats internally\n */\n const handleLoadMore = useCallback(async () => {\n if (!onLoadMore || loadingMoreRef.current) {\n return;\n }\n\n if (loadMode === 'full') {\n (onLoadMore as () => void)();\n return;\n }\n\n loadingMoreRef.current = true;\n try {\n const offset = displayChatsLengthRef.current;\n const result = await (onLoadMore as OnLoadMoreLazy)(offset);\n const newChats = Array.isArray(result) ? result : result.chats;\n const resultHasMore = Array.isArray(result) ? newChats.length > 0 : result.hasMore;\n setLoadedChats((prev) => [...prev, ...newChats]);\n setLazyHasMore(resultHasMore ?? newChats.length > 0);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('HistoryList: failed to load more chats', error);\n } finally {\n loadingMoreRef.current = false;\n }\n }, [onLoadMore, loadMode]);\n\n const handleChatClick = (\n chat: ListItemData<ListItemChatData>,\n _: number,\n fromKeyboard?: boolean,\n event?: React.KeyboardEvent<HTMLElement> | React.MouseEvent<HTMLElement>,\n ) => {\n if (fromKeyboard && (event?.target as HTMLButtonElement)?.type === 'button') {\n return;\n }\n onSelectChat?.(chat as ChatType);\n onChatClick?.(chat as ChatType);\n };\n\n const handleDeleteClick = async (e: React.MouseEvent, chat: ChatType) => {\n e.stopPropagation();\n try {\n await onDeleteChat?.(chat);\n setDeletedChats((prevDeletedChats) => new Set([...prevDeletedChats, chat.id]));\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('HistoryList: failed to delete chat', error);\n }\n };\n\n const wrappedFilterFunction = useMemo(() => {\n return (filter: string) => {\n const userFilter = filterFunction(filter);\n return (item: ListItemData<ListItemChatData>): boolean => {\n if (filter && item.type === 'date-header') {\n return false;\n }\n return userFilter(item);\n };\n };\n }, [filterFunction]);\n\n const renderItem = (item: ListItemData<ListItemChatData>, isActive: boolean) => {\n if (item.type === 'date-header') {\n return (\n <DateHeaderItem\n key={`date-${item.date}`}\n date={item.date}\n format={dateFormat}\n locale={dateLocale}\n localeConfig={dateLocaleConfig}\n />\n );\n }\n\n return (\n <ChatItem\n key={item.id}\n chat={item}\n showActions={showActions}\n onDeleteClick={onDeleteChat ? handleDeleteClick : undefined}\n isActive={isActive}\n size={sizeProp}\n />\n );\n };\n\n const emptyState = emptyPlaceholder || <div className={b('empty')}>{i18n('empty-state')}</div>;\n\n const emptyFilteredState = emptyFilteredPlaceholder || (\n <div className={b('empty')}>{i18n('empty-filtered-state')}</div>\n );\n\n const finalEmptyPlaceholder =\n filteredItemCount === null || filteredItemCount === listItems.length\n ? emptyState\n : emptyFilteredState;\n\n return (\n <div className={b('container', className)} data-qa={qa} style={style}>\n <div className={b('list-wrapper')}>\n {loading ? (\n <div className={b('loader-wrapper')}>\n <Loader view=\"loading\" />\n </div>\n ) : (\n <React.Fragment>\n <List\n size={size}\n items={listItems}\n renderItem={renderItem}\n virtualized={false}\n filterable={searchable}\n filterItem={wrappedFilterFunction}\n filterPlaceholder={searchPlaceholder ?? i18n('search-placeholder')}\n filterClassName={b('filter')}\n emptyPlaceholder={finalEmptyPlaceholder}\n selectedItemIndex={selectedItemIndex}\n itemsClassName={b('list')}\n itemClassName={b('list-item')}\n onFilterEnd={(data) => setFilteredItemCount(data.items.length)}\n onItemClick={handleChatClick}\n />\n {loadMode === 'lazy' && effectiveHasMore && onLoadMore && (\n <IntersectionContainer\n key={displayChats.length}\n onIntersect={handleLoadMore}\n className={b('lazy-loader')}\n >\n <Loader view=\"loading\" />\n </IntersectionContainer>\n )}\n </React.Fragment>\n )}\n </div>\n\n {loadMode === 'full' && hasMore && onLoadMore && (\n <Button view=\"flat-action\" size=\"m\" width=\"max\" onClick={handleLoadMore}>\n {i18n('action-load-more')}\n </Button>\n )}\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":"2.19.2","type":"commonjs","sideEffects":["*.css","*.scss"]}
1
+ {"version":"2.19.3-beta.af9c6fb1cce2b60fd6c198cca08d00a762b16671.0","type":"commonjs","sideEffects":["*.css","*.scss"]}
@@ -20,8 +20,17 @@
20
20
  /* === Suggestions === */
21
21
  --g-aikit-suggestions-box-shadow: 0 3px 10px rgba(198, 172, 255, 0.52);
22
22
 
23
+ /* === Button Group === */
24
+ --g-aikit-button-group-gap: var(--g-spacing-1);
25
+
23
26
  /* === Header === */
24
27
  --g-aikit-header-background: none;
28
+ --g-aikit-header-menu-sheet-inline-padding: var(--g-spacing-5);
29
+ --g-aikit-header-menu-sheet-bottom-padding: var(--g-spacing-5);
30
+ --g-aikit-header-menu-item-min-height: 44px;
31
+ --g-aikit-header-menu-item-inline-padding: 6px;
32
+ --g-aikit-header-menu-item-icon-gap: var(--g-spacing-4);
33
+ --g-aikit-header-menu-item-font-size: 16px;
25
34
 
26
35
  /* === Context Indicator === */
27
36
  --g-aikit-ci-color-progress-1: var(
@@ -47,9 +56,20 @@
47
56
  --g-aikit-history-width: 360px;
48
57
  --g-aikit-history-max-height: 605px;
49
58
  --g-aikit-history-item-height: 24px;
59
+ --g-aikit-history-sheet-inline-padding: var(--g-spacing-3);
60
+ --g-aikit-history-sheet-bottom-padding: var(--g-spacing-5);
61
+ --g-aikit-history-sheet-list-padding: 0;
62
+ --g-aikit-history-sheet-filter-inline-padding: 0;
63
+ --g-aikit-history-sheet-filter-border-bottom: none;
64
+ --g-aikit-history-sheet-item-inline-end-padding: 0;
65
+ --g-aikit-history-mobile-item-font-size: 16px;
66
+ --g-aikit-history-mobile-item-line-height: 24px;
50
67
 
51
68
  /* === Prompt Input === */
52
69
  --g-aikit-prompt-input-panel-max-height: 500px;
70
+ --g-aikit-prompt-input-simple-padding: var(--g-spacing-1) var(--g-spacing-1) var(--g-spacing-1)
71
+ var(--g-spacing-3);
72
+ --g-aikit-prompt-input-full-padding: var(--g-spacing-2);
53
73
 
54
74
  /* === Empty Container === */
55
75
  --g-aikit-empty-container-background: var(--g-color-base-background);
@@ -73,6 +93,7 @@
73
93
  --g-aikit-chat-content-background: var(--g-color-base-background);
74
94
  --g-aikit-chat-content-padding: 0 var(--g-aikit-layout-base-padding-m) var(--g-spacing-4)
75
95
  var(--g-aikit-layout-base-padding-m);
96
+ --g-aikit-chat-content-message-list-inline-padding: var(--g-spacing-2);
76
97
 
77
98
  /* === Chat Container === */
78
99
  --g-aikit-chat-container-header-background: var(--g-color-base-background);
@@ -80,14 +101,29 @@
80
101
  --g-aikit-chat-container-footer-background: var(--g-color-base-background);
81
102
  --g-aikit-chat-container-mobile-font-size: 16px;
82
103
  --g-aikit-chat-container-mobile-line-height: 24px;
104
+ --g-aikit-chat-container-mobile-body-2-font-size: 16px;
105
+ --g-aikit-chat-container-mobile-body-2-line-height: 20px;
83
106
  --g-aikit-chat-container-mobile-padding: var(--g-spacing-4);
84
- --g-aikit-chat-container-mobile-empty-container-padding: 80px 0 0;
85
- --g-aikit-chat-container-mobile-keyboard-empty-container-padding: var(--g-spacing-4) 0 0;
107
+ --g-aikit-chat-container-mobile-empty-container-padding: 80px var(--g-spacing-4) 0;
108
+ --g-aikit-chat-container-mobile-keyboard-empty-container-padding: var(--g-spacing-4)
109
+ var(--g-spacing-4) 0;
110
+ --g-aikit-chat-container-mobile-message-list-inline-padding: calc(
111
+ var(--g-aikit-layout-base-padding-m) + var(--g-spacing-2)
112
+ );
86
113
  --g-aikit-chat-container-background: var(--g-color-base-background);
87
114
  --g-aikit-chat-container-content-empty-background: var(--g-color-base-background);
88
115
  --g-aikit-chat-container-content-chat-background: var(--g-color-base-background);
89
116
  --g-aikit-chat-container-footer-empty-background: var(--g-color-base-background);
90
117
  --g-aikit-chat-container-footer-chat-background: var(--g-color-base-background);
118
+ --g-aikit-chat-container-header-height: auto;
119
+ --g-aikit-chat-container-mobile-header-height: 60px;
120
+ --g-aikit-chat-container-mobile-header-padding: var(--g-spacing-2) var(--g-spacing-3);
121
+ --g-aikit-chat-container-mobile-footer-padding: var(--g-spacing-4)
122
+ var(--g-aikit-chat-container-mobile-padding) var(--g-spacing-2);
123
+ --g-aikit-chat-container-mobile-footer-empty-padding: var(--g-spacing-2)
124
+ var(--g-aikit-chat-container-mobile-padding);
125
+ --g-aikit-chat-container-mobile-suggestions-max-height: 40vh;
126
+ --g-aikit-chat-container-mobile-suggestions-title-min-height: 48px;
91
127
 
92
128
  /* === Action Popup === */
93
129
  --g-aikit-action-popup-min-width: 280px;
@@ -22,8 +22,17 @@
22
22
  /* === Suggestions === */
23
23
  --g-aikit-suggestions-box-shadow: 0 3px 10px rgba(198, 172, 255, 0.52);
24
24
 
25
+ /* === Button Group === */
26
+ --g-aikit-button-group-gap: var(--g-spacing-1);
27
+
25
28
  /* === Header === */
26
29
  --g-aikit-header-background: none;
30
+ --g-aikit-header-menu-sheet-inline-padding: var(--g-spacing-5);
31
+ --g-aikit-header-menu-sheet-bottom-padding: var(--g-spacing-5);
32
+ --g-aikit-header-menu-item-min-height: 44px;
33
+ --g-aikit-header-menu-item-inline-padding: 6px;
34
+ --g-aikit-header-menu-item-icon-gap: var(--g-spacing-4);
35
+ --g-aikit-header-menu-item-font-size: 16px;
27
36
 
28
37
  /* === Context Indicator === */
29
38
  --g-aikit-ci-color-progress-1: var(
@@ -49,9 +58,20 @@
49
58
  --g-aikit-history-width: 360px;
50
59
  --g-aikit-history-max-height: 560px;
51
60
  --g-aikit-history-item-height: 24px;
61
+ --g-aikit-history-sheet-inline-padding: var(--g-spacing-3);
62
+ --g-aikit-history-sheet-bottom-padding: var(--g-spacing-5);
63
+ --g-aikit-history-sheet-list-padding: 0;
64
+ --g-aikit-history-sheet-filter-inline-padding: 0;
65
+ --g-aikit-history-sheet-filter-border-bottom: none;
66
+ --g-aikit-history-sheet-item-inline-end-padding: 0;
67
+ --g-aikit-history-mobile-item-font-size: 16px;
68
+ --g-aikit-history-mobile-item-line-height: 24px;
52
69
 
53
70
  /* === Prompt Input === */
54
71
  --g-aikit-prompt-input-panel-max-height: 500px;
72
+ --g-aikit-prompt-input-simple-padding: var(--g-spacing-1) var(--g-spacing-1) var(--g-spacing-1)
73
+ var(--g-spacing-3);
74
+ --g-aikit-prompt-input-full-padding: var(--g-spacing-2);
55
75
 
56
76
  /* === Empty Container === */
57
77
  --g-aikit-empty-container-background: var(--g-color-base-background);
@@ -75,6 +95,7 @@
75
95
  --g-aikit-chat-content-background: var(--g-color-base-background);
76
96
  --g-aikit-chat-content-padding: 0 var(--g-aikit-layout-base-padding-m) var(--g-spacing-4)
77
97
  var(--g-aikit-layout-base-padding-m);
98
+ --g-aikit-chat-content-message-list-inline-padding: var(--g-spacing-2);
78
99
 
79
100
  /* === Chat Container === */
80
101
  --g-aikit-chat-container-header-background: var(--g-color-base-background);
@@ -82,14 +103,29 @@
82
103
  --g-aikit-chat-container-footer-background: var(--g-color-base-background);
83
104
  --g-aikit-chat-container-mobile-font-size: 16px;
84
105
  --g-aikit-chat-container-mobile-line-height: 24px;
106
+ --g-aikit-chat-container-mobile-body-2-font-size: 16px;
107
+ --g-aikit-chat-container-mobile-body-2-line-height: 20px;
85
108
  --g-aikit-chat-container-mobile-padding: var(--g-spacing-4);
86
- --g-aikit-chat-container-mobile-empty-container-padding: 80px 0 0;
87
- --g-aikit-chat-container-mobile-keyboard-empty-container-padding: var(--g-spacing-4) 0 0;
109
+ --g-aikit-chat-container-mobile-empty-container-padding: 80px var(--g-spacing-4) 0;
110
+ --g-aikit-chat-container-mobile-keyboard-empty-container-padding: var(--g-spacing-4)
111
+ var(--g-spacing-4) 0;
112
+ --g-aikit-chat-container-mobile-message-list-inline-padding: calc(
113
+ var(--g-aikit-layout-base-padding-m) + var(--g-spacing-2)
114
+ );
88
115
  --g-aikit-chat-container-background: var(--g-color-base-background);
89
116
  --g-aikit-chat-container-content-empty-background: var(--g-color-base-background);
90
117
  --g-aikit-chat-container-content-chat-background: var(--g-color-base-background);
91
118
  --g-aikit-chat-container-footer-empty-background: var(--g-color-base-background);
92
119
  --g-aikit-chat-container-footer-chat-background: var(--g-color-base-background);
120
+ --g-aikit-chat-container-header-height: auto;
121
+ --g-aikit-chat-container-mobile-header-height: 60px;
122
+ --g-aikit-chat-container-mobile-header-padding: var(--g-spacing-2) var(--g-spacing-3);
123
+ --g-aikit-chat-container-mobile-footer-padding: var(--g-spacing-4)
124
+ var(--g-aikit-chat-container-mobile-padding) var(--g-spacing-2);
125
+ --g-aikit-chat-container-mobile-footer-empty-padding: var(--g-spacing-2)
126
+ var(--g-aikit-chat-container-mobile-padding);
127
+ --g-aikit-chat-container-mobile-suggestions-max-height: 40vh;
128
+ --g-aikit-chat-container-mobile-suggestions-title-min-height: 48px;
93
129
 
94
130
  /* === Action Popup === */
95
131
  --g-aikit-action-popup-min-width: 280px;
@@ -33,6 +33,6 @@ import {ButtonGroup} from '@/components/molecules/ButtonGroup';
33
33
 
34
34
  The component uses CSS variables for theming:
35
35
 
36
- | Variable | Description |
37
- | --------------- | ------------------- |
38
- | `--g-spacing-1` | Gap between buttons |
36
+ | Variable | Description |
37
+ | ---------------------------- | ----------------------------------------------------------------------------------------- |
38
+ | `--g-aikit-button-group-gap` | Gap between buttons (default: `--g-spacing-1`, `--g-spacing-2` in the mobile chat header) |
@@ -336,6 +336,20 @@ The component uses CSS variables for theming:
336
336
  --g-spacing-2 /* Header padding and gap between elements */
337
337
  ```
338
338
 
339
+ ### Menu sheet
340
+
341
+ In mobile mode additional actions open in a `Sheet`. Its metrics are variables,
342
+ `env(safe-area-inset-*)` is added by the component:
343
+
344
+ ```css
345
+ --g-aikit-header-menu-sheet-inline-padding /* Side padding of the sheet (default: --g-spacing-5) */
346
+ --g-aikit-header-menu-sheet-bottom-padding /* Bottom padding of the sheet (default: --g-spacing-5) */
347
+ --g-aikit-header-menu-item-min-height /* Menu item height, a touch target (default: 44px) */
348
+ --g-aikit-header-menu-item-inline-padding /* Side padding of a menu item (default: 6px) */
349
+ --g-aikit-header-menu-item-icon-gap /* Gap between the icon and the label (default: --g-spacing-4) */
350
+ --g-aikit-header-menu-item-font-size /* Menu font size; the sheet is portaled to the body, so the chat typography does not reach it (default: 16px) */
351
+ ```
352
+
339
353
  ## Properties
340
354
 
341
355
  ### HeaderProps
@@ -366,6 +380,7 @@ The component uses CSS variables for theming:
366
380
  | `titlePosition` | `'left' \| 'center'` | - | `'left'` | Title alignment position |
367
381
  | `withIcon` | `boolean` | - | `true` | Whether to show icon area |
368
382
  | `showTitle` | `boolean` | - | `true` | Whether to show title and preview |
383
+ | `showSheetTitle` | `boolean` | - | `true` | Whether to show the mobile menu sheet title; it stays in the dialog `aria-label` either way |
369
384
  | `qa` | `string` | - | - | QA/test identifier on header root |
370
385
  | `actionQa` | `Partial<Record<HeaderAction, string>>` | - | - | Overrides `data-qa` for base actions (default `header-action-${id}`) |
371
386
  | `actionTooltipTexts` | `HeaderActionTooltipTexts` | - | - | Overrides tooltip texts for base actions |
@@ -245,16 +245,18 @@ All state management is handled internally by the `usePromptInput` hook.
245
245
 
246
246
  The component uses CSS variables for theming:
247
247
 
248
- | Variable | Description | Default Value |
249
- | ----------------------------------------- | ----------------------------------------- | ------------- |
250
- | `--g-color-line-generic` | Border color around the component | - |
251
- | `--g-color-base-float` | Background color of the component | - |
252
- | `--g-color-base-simple-hover-solid` | Background color for panels | - |
253
- | `--g-border-radius-xl` | Border radius for component & panels | - |
254
- | `--g-spacing-1` | Small spacing (8px) | - |
255
- | `--g-spacing-2` | Medium spacing (12px) | - |
256
- | `--g-spacing-3` | Large spacing (16px) | - |
257
- | `--g-aikit-prompt-input-panel-max-height` | Max height for open panel (for animation) | `500px` |
248
+ | Variable | Description | Default Value |
249
+ | ----------------------------------------- | ----------------------------------------- | ------------------ |
250
+ | `--g-color-line-generic` | Border color around the component | - |
251
+ | `--g-color-base-float` | Background color of the component | - |
252
+ | `--g-color-base-simple-hover-solid` | Background color for panels | - |
253
+ | `--g-border-radius-xl` | Border radius for component & panels | - |
254
+ | `--g-spacing-1` | Small spacing (8px) | - |
255
+ | `--g-spacing-2` | Medium spacing (12px) | - |
256
+ | `--g-spacing-3` | Large spacing (16px) | - |
257
+ | `--g-aikit-prompt-input-panel-max-height` | Max height for open panel (for animation) | `500px` |
258
+ | `--g-aikit-prompt-input-simple-padding` | Padding of the `simple` view | `4px 4px 4px 12px` |
259
+ | `--g-aikit-prompt-input-full-padding` | Padding of the `full` view | `8px` |
258
260
 
259
261
  ```css
260
262
  /* Example: Custom theme */
@@ -397,47 +397,48 @@ Use the `texts` prop with type `ChatContainerTexts` for a **flat** API over user
397
397
 
398
398
  ## Props
399
399
 
400
- | Prop | Type | Required | Default | Description |
401
- | --------------------------- | -------------------------------------- | ------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
402
- | `messages` | `TMessage[]` | - | `[]` | Array of messages in current chat |
403
- | `onSendMessage` | `(data: TSubmitData) => Promise<void>` | ✓ | - | Callback when user sends a message |
404
- | `chats` | `ChatType[]` | - | `[]` | Array of chats for history |
405
- | `activeChat` | `ChatType \| null` | - | `null` | Currently active chat |
406
- | `onSelectChat` | `(chat: ChatType) => void` | - | - | Callback when user selects a chat from history |
407
- | `onCreateChat` | `() => void` | - | - | Callback when user creates a new chat |
408
- | `onDeleteChat` | `(chat: ChatType) => void` | - | - | Callback when user deletes a chat |
409
- | `onDeleteAllChats` | `() => Promise<void>` | - | - | Callback when user deletes all chats |
410
- | `onFold` | `(value: 'collapsed' | 'opened') => void` | - | - | Callback when user folds/unfolds the chat |
411
- | `onClose` | `() => void` | - | - | Callback when user closes the chat |
412
- | `onCancel` | `() => Promise<void>` | - | - | Callback when user cancels streaming |
413
- | `status` | `ChatStatus` | - | `'ready'` | Chat status: `'submitted'` \| `'streaming'` \| `'ready'` \| `'error'` |
414
- | `error` | `Error \| null` | - | `null` | Error state |
415
- | `onRetry` | `() => void` | - | - | Callback to retry after error |
416
- | `showActionsOnHover` | `boolean` | - | `false` | Show message actions (copy, like, edit) on hover |
417
- | `contextItems` | `ContextItemConfig[]` | - | `[]` | Array of context items to display in prompt input header |
418
- | `transformOptions` | `OptionsType` | - | - | Transform options for markdown rendering |
419
- | `openMarkdownLinksInNewTab` | `boolean` | - | `false` | Open markdown links rendered by default message renderers in a new tab, except hash-only and relative same-document anchors |
420
- | `messageListConfig` | `MessageListConfig` | - | - | Configuration for MessageList (actions, loader statuses) |
421
- | `headerProps` | `Partial<HeaderProps>` | - | - | Props override for Header component |
422
- | `contentProps` | `Partial<ChatContentProps>` | - | - | Props override for ChatContent component |
423
- | `emptyContainerProps` | `Partial<EmptyContainerProps>` | - | - | Props override for EmptyContainer |
424
- | `promptInputProps` | `Partial<PromptInputProps>` | - | - | Props override for PromptInput component |
425
- | `disclaimerProps` | `Partial<DisclaimerProps>` | - | - | Props override for Disclaimer component |
426
- | `historyProps` | `Partial<HistoryProps>` | - | - | Props override for History component |
427
- | `welcomeConfig` | `WelcomeConfig` | - | - | Welcome screen configuration for empty state |
428
- | `texts` | `ChatContainerTexts` | - | - | Flat unified copy overrides (see **Texts**) |
429
- | `showHistory` | `boolean` | - | `true` | Show chat history feature |
430
- | `showNewChat` | `boolean` | - | `true` | Show new chat button |
431
- | `showFolding` | `boolean` | - | `false` | Show folding button |
432
- | `showClose` | `boolean` | - | `false` | Show close button |
433
- | `hideTitleOnEmptyChat` | `boolean` | - | `false` | Hide header title and preview when chat is empty |
434
- | `isMobile` | `boolean` | - | - | Mobile mode for the chat and all inner components; falls back to `useMobile()` from `@gravity-ui/uikit` `MobileProvider` |
435
- | `adjustToKeyboard` | `boolean` | - | `true` | In mobile mode, shrink the container to the visible viewport while the on-screen keyboard is open |
436
- | `className` | `string` | - | - | Additional CSS class |
437
- | `headerClassName` | `string` | - | - | Additional CSS class for header section |
438
- | `contentClassName` | `string` | - | - | Additional CSS class for content section |
439
- | `footerClassName` | `string` | - | - | Additional CSS class for footer section |
440
- | `qa` | `string \| ChatContainerQa` | - | - | QA/test identifiers: string = root only; object = map or `prefix` (see **QA**) |
400
+ | Prop | Type | Required | Default | Description |
401
+ | --------------------------- | -------------------------------------- | ------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
402
+ | `messages` | `TMessage[]` | - | `[]` | Array of messages in current chat |
403
+ | `onSendMessage` | `(data: TSubmitData) => Promise<void>` | ✓ | - | Callback when user sends a message |
404
+ | `chats` | `ChatType[]` | - | `[]` | Array of chats for history |
405
+ | `activeChat` | `ChatType \| null` | - | `null` | Currently active chat |
406
+ | `onSelectChat` | `(chat: ChatType) => void` | - | - | Callback when user selects a chat from history |
407
+ | `onCreateChat` | `() => void` | - | - | Callback when user creates a new chat |
408
+ | `onDeleteChat` | `(chat: ChatType) => void` | - | - | Callback when user deletes a chat |
409
+ | `onDeleteAllChats` | `() => Promise<void>` | - | - | Callback when user deletes all chats |
410
+ | `onFold` | `(value: 'collapsed' | 'opened') => void` | - | - | Callback when user folds/unfolds the chat |
411
+ | `onClose` | `() => void` | - | - | Callback when user closes the chat |
412
+ | `onCancel` | `() => Promise<void>` | - | - | Callback when user cancels streaming |
413
+ | `status` | `ChatStatus` | - | `'ready'` | Chat status: `'submitted'` \| `'streaming'` \| `'ready'` \| `'error'` |
414
+ | `error` | `Error \| null` | - | `null` | Error state |
415
+ | `onRetry` | `() => void` | - | - | Callback to retry after error |
416
+ | `showActionsOnHover` | `boolean` | - | `false` | Show message actions (copy, like, edit) on hover |
417
+ | `contextItems` | `ContextItemConfig[]` | - | `[]` | Array of context items to display in prompt input header |
418
+ | `transformOptions` | `OptionsType` | - | - | Transform options for markdown rendering |
419
+ | `openMarkdownLinksInNewTab` | `boolean` | - | `false` | Open markdown links rendered by default message renderers in a new tab, except hash-only and relative same-document anchors |
420
+ | `messageListConfig` | `MessageListConfig` | - | - | Configuration for MessageList (actions, loader statuses) |
421
+ | `headerProps` | `Partial<HeaderProps>` | - | - | Props override for Header component |
422
+ | `contentProps` | `Partial<ChatContentProps>` | - | - | Props override for ChatContent component |
423
+ | `emptyContainerProps` | `Partial<EmptyContainerProps>` | - | - | Props override for EmptyContainer |
424
+ | `promptInputProps` | `Partial<PromptInputProps>` | - | - | Props override for PromptInput component |
425
+ | `disclaimerProps` | `Partial<DisclaimerProps>` | - | - | Props override for Disclaimer component |
426
+ | `historyProps` | `Partial<HistoryProps>` | - | - | Props override for History component |
427
+ | `welcomeConfig` | `WelcomeConfig` | - | - | Welcome screen configuration for empty state |
428
+ | `texts` | `ChatContainerTexts` | - | - | Flat unified copy overrides (see **Texts**) |
429
+ | `showHistory` | `boolean` | - | `true` | Show chat history feature |
430
+ | `showNewChat` | `boolean` | - | `true` | Show new chat button |
431
+ | `showFolding` | `boolean` | - | `false` | Show folding button |
432
+ | `showClose` | `boolean` | - | `false` | Show close button |
433
+ | `hideTitleOnEmptyChat` | `boolean` | - | `false` | Hide header title and preview when chat is empty |
434
+ | `isMobile` | `boolean` | - | - | Mobile mode for the chat and all inner components; falls back to `useMobile()` from `@gravity-ui/uikit` `MobileProvider` |
435
+ | `adjustToKeyboard` | `boolean` | - | `true` | In mobile mode, shrink the container to the visible viewport while the on-screen keyboard is open |
436
+ | `floatingHeader` | `boolean` | - | `false` | Render the header above the content instead of stacking it, so the content scrolls underneath; the content offset is on the consumer, see [Floating header](#floating-header) |
437
+ | `className` | `string` | - | - | Additional CSS class |
438
+ | `headerClassName` | `string` | - | - | Additional CSS class for header section |
439
+ | `contentClassName` | `string` | - | - | Additional CSS class for content section |
440
+ | `footerClassName` | `string` | - | - | Additional CSS class for footer section |
441
+ | `qa` | `string \| ChatContainerQa` | - | - | QA/test identifiers: string = root only; object = map or `prefix` (see **QA**) |
441
442
 
442
443
  ## Types
443
444
 
@@ -1047,38 +1048,86 @@ function App() {
1047
1048
 
1048
1049
  The component uses CSS variables for theming:
1049
1050
 
1050
- | Variable | Description |
1051
- | --------------------------------------------------- | ------------------------------------------------------------------------------------- |
1052
- | `--g-aikit-chat-container-background` | Background color of the entire chat container |
1053
- | `--g-aikit-chat-container-header-background` | Background color of the header section |
1054
- | `--g-aikit-chat-container-content-background` | Background color of the content section (general) |
1055
- | `--g-aikit-chat-container-content-empty-background` | Background color of the content section in empty view |
1056
- | `--g-aikit-chat-container-content-chat-background` | Background color of the content section in chat view |
1057
- | `--g-aikit-chat-container-footer-background` | Background color of the footer section (general) |
1058
- | `--g-aikit-chat-container-footer-empty-background` | Background color of the footer section in empty view |
1059
- | `--g-aikit-chat-container-footer-chat-background` | Background color of the footer section in chat view |
1060
- | `--g-aikit-layout-base-padding-m` | Padding for header, content, and footer sections (default: 12px; 16px in mobile mode) |
1061
- | `--g-aikit-chat-container-mobile-font-size` | Body text size inside the chat in mobile mode (default: 16px) |
1062
- | `--g-aikit-chat-container-mobile-line-height` | Body line height inside the chat in mobile mode (default: 24px) |
1063
- | `--g-spacing-1` | Gap between footer elements (default: 4px) |
1064
- | `--g-spacing-2` | Gap between header elements (default: 8px) |
1065
- | `--g-spacing-4` | Bottom padding for content section (default: 16px) |
1051
+ | Variable | Description |
1052
+ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
1053
+ | `--g-aikit-chat-container-background` | Background color of the entire chat container |
1054
+ | `--g-aikit-chat-container-header-background` | Background color of the header section |
1055
+ | `--g-aikit-chat-container-content-background` | Background color of the content section (general) |
1056
+ | `--g-aikit-chat-container-content-empty-background` | Background color of the content section in empty view |
1057
+ | `--g-aikit-chat-container-content-chat-background` | Background color of the content section in chat view |
1058
+ | `--g-aikit-chat-container-footer-background` | Background color of the footer section (general) |
1059
+ | `--g-aikit-chat-container-footer-empty-background` | Background color of the footer section in empty view |
1060
+ | `--g-aikit-chat-container-footer-chat-background` | Background color of the footer section in chat view |
1061
+ | `--g-aikit-layout-base-padding-m` | Padding for header, content, and footer sections (default: 12px; 16px in mobile mode) |
1062
+ | `--g-aikit-chat-container-mobile-font-size` | Body text size inside the chat in mobile mode (default: 16px) |
1063
+ | `--g-aikit-chat-container-mobile-line-height` | Body line height inside the chat in mobile mode (default: 24px) |
1064
+ | `--g-aikit-chat-container-mobile-body-2-font-size` | `body-2` text size in mobile mode, mapped onto `--g-text-body-2-font-size` (default: 16px) |
1065
+ | `--g-aikit-chat-container-mobile-body-2-line-height` | `body-2` line height in mobile mode, mapped onto `--g-text-body-2-line-height` (default: 20px) |
1066
+ | `--g-spacing-1` | Gap between footer elements (default: 4px) |
1067
+ | `--g-spacing-2` | Gap between header elements (default: 8px) |
1068
+ | `--g-spacing-4` | Bottom padding for content section (default: 16px) |
1069
+
1070
+ Header and footer metrics, each with a `mobile-` counterpart applied in mobile mode:
1071
+
1072
+ | Variable | Description |
1073
+ | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
1074
+ | `--g-aikit-chat-container-header-height` | Header height (default: `auto`) |
1075
+ | `--g-aikit-chat-container-header-padding` | Header padding (default: `var(--g-aikit-layout-base-padding-m)`) |
1076
+ | `--g-aikit-chat-container-mobile-header-height` | Minimum header height in mobile mode (default: `60px`) |
1077
+ | `--g-aikit-chat-container-mobile-header-padding` | Header padding in mobile mode |
1078
+ | `--g-aikit-chat-container-footer-padding` | Footer padding (default: `var(--g-aikit-layout-base-padding-m)`) |
1079
+ | `--g-aikit-chat-container-footer-empty-padding` | Footer padding on the welcome screen |
1080
+ | `--g-aikit-chat-container-mobile-footer-padding` | Footer padding in mobile mode |
1081
+ | `--g-aikit-chat-container-mobile-footer-empty-padding` | Welcome-screen footer padding in mobile mode |
1082
+ | `--g-aikit-chat-container-mobile-suggestions-max-height` | Max height of the suggestions block above the input in mobile mode; it scrolls beyond that (default: `40vh`) |
1083
+ | `--g-aikit-chat-container-mobile-suggestions-title-min-height` | Floor of the suggestions title in mobile mode: it shrinks down to this height and scrolls inside itself, so the buttons keep their place (default: `48px`) |
1084
+
1085
+ ### Floating header
1086
+
1087
+ With `floatingHeader` the header is taken out of the flow and the content scrolls underneath it.
1088
+ The header keeps its own height, and the offset of the content is left to the consumer, because
1089
+ it depends on the layout - a `padding-top` keeps the content background under the header, a
1090
+ `margin-top` leaves the container background visible above it. Set an explicit header height
1091
+ first: `--g-aikit-chat-container-header-height` is `auto` by default (and `auto` in a
1092
+ `padding-top` resolves to `0`), in mobile mode the height comes from
1093
+ `--g-aikit-chat-container-mobile-header-height` (`60px`). In mobile mode this token becomes the
1094
+ fixed height of the floating header, so the header cannot grow past the offset the content uses
1095
+ and cover the beginning of the conversation.
1096
+
1097
+ ```css
1098
+ .my-chat {
1099
+ --g-aikit-chat-container-header-height: 60px;
1100
+ --g-aikit-chat-container-header-background: transparent;
1101
+ }
1102
+
1103
+ .my-chat .g-aikit-chat-container__content {
1104
+ padding-top: var(--g-aikit-chat-container-header-height);
1105
+ }
1106
+ ```
1066
1107
 
1067
1108
  ### Mobile mode
1068
1109
 
1069
1110
  In mobile mode (`isMobile` prop, or the uikit `MobileProvider` context) the container
1070
1111
  rebinds its layout tokens to the mobile ones:
1071
1112
 
1072
- | Token bound on the container | Mobile source token | Default |
1073
- | ------------------------------------------ | --------------------------------------------------------- | -------------------- |
1074
- | `--g-aikit-layout-base-padding-m` | `--g-aikit-chat-container-mobile-padding` | `var(--g-spacing-4)` |
1075
- | `--g-aikit-empty-container-mobile-padding` | `--g-aikit-chat-container-mobile-empty-container-padding` | `80px 0 0` |
1113
+ | Token bound on the container | Mobile source token | Default |
1114
+ | ---------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------- |
1115
+ | `--g-aikit-layout-base-padding-m` | `--g-aikit-chat-container-mobile-padding` | `var(--g-spacing-4)` |
1116
+ | `--g-aikit-empty-container-mobile-padding` | `--g-aikit-chat-container-mobile-empty-container-padding` | `80px var(--g-spacing-4) 0` |
1117
+ | `--g-aikit-chat-content-message-list-inline-padding` | `--g-aikit-chat-container-mobile-message-list-inline-padding` | `calc(var(--g-aikit-layout-base-padding-m) + var(--g-spacing-2))` (= `20px`) |
1076
1118
 
1077
- Horizontal padding of the empty state is zero inside the chat because the content
1078
- section already provides it. The remaining mobile values of the empty state
1119
+ The empty state adds its own inline padding on top of the padding the content section
1120
+ already provides, so the mobile welcome screen is inset by
1121
+ `var(--g-aikit-chat-container-mobile-padding) + var(--g-spacing-4)` per side in total.
1122
+ The remaining mobile values of the empty state
1079
1123
  (`--g-aikit-empty-container-mobile-content-gap`, `--g-aikit-empty-container-mobile-welcome-gap`)
1080
1124
  are applied by `EmptyContainer` itself and work the same way standalone.
1081
1125
 
1126
+ The mobile header padding is intentionally decoupled from
1127
+ `--g-aikit-chat-container-mobile-padding`: overriding the base mobile padding moves the
1128
+ content and the footer, while the header keeps its own
1129
+ `--g-aikit-chat-container-mobile-header-padding`.
1130
+
1082
1131
  All mobile values, including typography
1083
1132
  (`--g-aikit-chat-container-mobile-font-size` / `--g-aikit-chat-container-mobile-line-height`),
1084
1133
  are declared in the themes and can be overridden from `.g-root`.
@@ -1109,9 +1158,14 @@ under the keyboard, so there is nothing to clamp.
1109
1158
  While the keyboard is open, the root gets a `_keyboard-open` modifier and the welcome screen
1110
1159
  trades its large top padding for the remaining space:
1111
1160
 
1112
- | Token bound on the container | Keyboard source token | Default |
1113
- | ------------------------------------------ | ------------------------------------------------------------------ | ------------------------ |
1114
- | `--g-aikit-empty-container-mobile-padding` | `--g-aikit-chat-container-mobile-keyboard-empty-container-padding` | `var(--g-spacing-4) 0 0` |
1161
+ | Token bound on the container | Keyboard source token | Default |
1162
+ | ------------------------------------------ | ------------------------------------------------------------------ | ----------------------------------------- |
1163
+ | `--g-aikit-empty-container-mobile-padding` | `--g-aikit-chat-container-mobile-keyboard-empty-container-padding` | `var(--g-spacing-4) var(--g-spacing-4) 0` |
1164
+
1165
+ `--g-aikit-chat-container-mobile-suggestions-max-height` is relative to the layout viewport,
1166
+ which does not shrink with the keyboard, so the suggestions block above the input is also
1167
+ shrinkable: when the clamped height leaves it less than its cap, it gives the space back to the
1168
+ prompt input and scrolls inside itself instead of pushing the input out of the container.
1115
1169
 
1116
1170
  ```css
1117
1171
  /* Example: Custom theme */