@gravity-ui/aikit 0.6.0 → 1.0.1

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 (109) hide show
  1. package/dist/components/atoms/ActionButton/__stories__/ActionButton.stories.d.ts +8 -0
  2. package/dist/components/atoms/ActionButton/__stories__/ActionButton.stories.js +48 -0
  3. package/dist/components/atoms/Alert/__stories__/Alert.stories.d.ts +10 -0
  4. package/dist/components/atoms/Alert/__stories__/Alert.stories.js +72 -0
  5. package/dist/components/atoms/ChatDate/__stories__/ChatDate.stories.d.ts +16 -0
  6. package/dist/components/atoms/ChatDate/__stories__/ChatDate.stories.js +83 -0
  7. package/dist/components/atoms/ContextIndicator/__stories__/ContextIndicator.stories.d.ts +17 -0
  8. package/dist/components/atoms/ContextIndicator/__stories__/ContextIndicator.stories.js +72 -0
  9. package/dist/components/atoms/ContextItem/__stories__/ContextItem.stories.d.ts +8 -0
  10. package/dist/components/atoms/ContextItem/__stories__/ContextItem.stories.js +36 -0
  11. package/dist/components/atoms/DiffStat/__stories__/DiffStat.stories.d.ts +8 -0
  12. package/dist/components/atoms/DiffStat/__stories__/DiffStat.stories.js +45 -0
  13. package/dist/components/atoms/Disclaimer/__stories__/Disclaimer.stories.d.ts +12 -0
  14. package/dist/components/atoms/Disclaimer/__stories__/Disclaimer.stories.js +64 -0
  15. package/dist/components/atoms/Loader/__stories__/Loader.stories.d.ts +8 -0
  16. package/dist/components/atoms/Loader/__stories__/Loader.stories.js +47 -0
  17. package/dist/components/atoms/MarkdownRenderer/__stories__/MarkdownRenderer.stories.d.ts +6 -0
  18. package/dist/components/atoms/MarkdownRenderer/__stories__/MarkdownRenderer.stories.js +49 -0
  19. package/dist/components/atoms/MessageBalloon/__stories__/MessageBalloon.stories.d.ts +6 -0
  20. package/dist/components/atoms/MessageBalloon/__stories__/MessageBalloon.stories.js +32 -0
  21. package/dist/components/atoms/Shimmer/__stories__/Shimmer.stories.d.ts +5 -0
  22. package/dist/components/atoms/Shimmer/__stories__/Shimmer.stories.js +28 -0
  23. package/dist/components/atoms/SubmitButton/__stories__/SubmitButton.stories.d.ts +13 -0
  24. package/dist/components/atoms/SubmitButton/__stories__/SubmitButton.stories.js +98 -0
  25. package/dist/components/atoms/ToolIndicator/__stories__/ToolIndicator.stories.d.ts +9 -0
  26. package/dist/components/atoms/ToolIndicator/__stories__/ToolIndicator.stories.js +34 -0
  27. package/dist/components/molecules/BaseMessage/__stories__/BaseMessage.stories.d.ts +9 -0
  28. package/dist/components/molecules/BaseMessage/__stories__/BaseMessage.stories.js +77 -0
  29. package/dist/components/molecules/BaseMessage/index.d.ts +1 -1
  30. package/dist/components/molecules/BaseMessage/index.js +51 -20
  31. package/dist/components/molecules/ButtonGroup/__stories__/ButtonGroup.stories.d.ts +6 -0
  32. package/dist/components/molecules/ButtonGroup/__stories__/ButtonGroup.stories.js +44 -0
  33. package/dist/components/molecules/PromptInputBody/__stories__/PromptInputBody.stories.d.ts +11 -0
  34. package/dist/components/molecules/PromptInputBody/__stories__/PromptInputBody.stories.js +62 -0
  35. package/dist/components/molecules/PromptInputFooter/__stories__/PromptInputFooter.stories.d.ts +11 -0
  36. package/dist/components/molecules/PromptInputFooter/__stories__/PromptInputFooter.stories.js +96 -0
  37. package/dist/components/molecules/PromptInputHeader/__stories__/PromptInputHeader.stories.d.ts +15 -0
  38. package/dist/components/molecules/PromptInputHeader/__stories__/PromptInputHeader.stories.js +123 -0
  39. package/dist/components/molecules/PromptInputPanel/__stories__/PromptInputPanel.stories.d.ts +8 -0
  40. package/dist/components/molecules/PromptInputPanel/__stories__/PromptInputPanel.stories.js +38 -0
  41. package/dist/components/molecules/Suggestions/__stories__/Suggestions.stories.d.ts +22 -0
  42. package/dist/components/molecules/Suggestions/__stories__/Suggestions.stories.js +182 -0
  43. package/dist/components/molecules/Tabs/__stories__/Tabs.stories.d.ts +9 -0
  44. package/dist/components/molecules/Tabs/__stories__/Tabs.stories.js +103 -0
  45. package/dist/components/molecules/ToolFooter/__stories__/ToolFooter.stories.d.ts +7 -0
  46. package/dist/components/molecules/ToolFooter/__stories__/ToolFooter.stories.js +58 -0
  47. package/dist/components/molecules/ToolFooter/index.js +8 -1
  48. package/dist/components/molecules/ToolHeader/__stories__/ToolHeader.stories.d.ts +8 -0
  49. package/dist/components/molecules/ToolHeader/__stories__/ToolHeader.stories.js +59 -0
  50. package/dist/components/molecules/ToolHeader/index.js +10 -1
  51. package/dist/components/molecules/ToolStatus/__stories__/ToolStatus.stories.d.ts +9 -0
  52. package/dist/components/molecules/ToolStatus/__stories__/ToolStatus.stories.js +44 -0
  53. package/dist/components/organisms/AssistantMessage/__stories__/AssistantMessage.stories.d.ts +13 -0
  54. package/dist/components/organisms/AssistantMessage/__stories__/AssistantMessage.stories.js +151 -0
  55. package/dist/components/organisms/Header/Header.js +16 -17
  56. package/dist/components/organisms/Header/__stories__/Header.stories.d.ts +15 -0
  57. package/dist/components/organisms/Header/__stories__/Header.stories.js +159 -0
  58. package/dist/components/organisms/Header/types.d.ts +2 -3
  59. package/dist/components/organisms/Header/useHeader.d.ts +2 -4
  60. package/dist/components/organisms/Header/useHeader.js +2 -24
  61. package/dist/components/organisms/MessageList/MessageList.js +5 -6
  62. package/dist/components/organisms/MessageList/__stories__/MessageList.stories.d.ts +25 -0
  63. package/dist/components/organisms/MessageList/__stories__/MessageList.stories.js +340 -0
  64. package/dist/components/organisms/PromptInput/__stories__/PromptInput.stories.d.ts +19 -0
  65. package/dist/components/organisms/PromptInput/__stories__/PromptInput.stories.js +304 -0
  66. package/dist/components/organisms/ThinkingMessage/__stories__/ThinkingMessage.stories.d.ts +12 -0
  67. package/dist/components/organisms/ThinkingMessage/__stories__/ThinkingMessage.stories.js +105 -0
  68. package/dist/components/organisms/ToolMessage/__stories__/ToolMessage.stories.d.ts +11 -0
  69. package/dist/components/organisms/ToolMessage/__stories__/ToolMessage.stories.js +70 -0
  70. package/dist/components/organisms/UserMessage/__stories__/UserMessage.stories.d.ts +9 -0
  71. package/dist/components/organisms/UserMessage/__stories__/UserMessage.stories.js +118 -0
  72. package/dist/components/pages/ChatContainer/__stories__/ChatContainer.stories.d.ts +79 -0
  73. package/dist/components/pages/ChatContainer/__stories__/ChatContainer.stories.js +1006 -0
  74. package/dist/components/templates/ChatContent/__stories__/ChatContent.stories.d.ts +14 -0
  75. package/dist/components/templates/ChatContent/__stories__/ChatContent.stories.js +315 -0
  76. package/dist/components/templates/EmptyContainer/__stories__/EmptyContainer.stories.d.ts +20 -0
  77. package/dist/components/templates/EmptyContainer/__stories__/EmptyContainer.stories.js +250 -0
  78. package/dist/components/templates/History/ChatItem.d.ts +2 -2
  79. package/dist/components/templates/History/ChatItem.js +2 -5
  80. package/dist/components/templates/History/History.scss +13 -9
  81. package/dist/components/templates/History/HistoryList.d.ts +3 -1
  82. package/dist/components/templates/History/HistoryList.js +9 -5
  83. package/dist/components/templates/History/__stories__/History.stories.d.ts +18 -0
  84. package/dist/components/templates/History/__stories__/History.stories.js +289 -0
  85. package/dist/demo/ContentWrapper/ContentWrapper.d.ts +4 -0
  86. package/dist/demo/ContentWrapper/ContentWrapper.js +9 -0
  87. package/dist/demo/ContentWrapper/index.d.ts +1 -0
  88. package/dist/demo/ContentWrapper/index.js +1 -0
  89. package/dist/demo/Showcase/Showcase.d.ts +9 -0
  90. package/dist/demo/Showcase/Showcase.js +7 -0
  91. package/dist/demo/Showcase/index.d.ts +1 -0
  92. package/dist/demo/Showcase/index.js +1 -0
  93. package/dist/demo/ShowcaseItem/ShowcaseItem.d.ts +8 -0
  94. package/dist/demo/ShowcaseItem/ShowcaseItem.js +7 -0
  95. package/dist/demo/ShowcaseItem/index.d.ts +1 -0
  96. package/dist/demo/ShowcaseItem/index.js +1 -0
  97. package/dist/demo/SwapArea/SwapArea.d.ts +2 -0
  98. package/dist/demo/SwapArea/SwapArea.js +7 -0
  99. package/dist/demo/SwapArea/index.d.ts +1 -0
  100. package/dist/demo/SwapArea/index.js +1 -0
  101. package/dist/hooks/useSmartScroll.d.ts +7 -2
  102. package/dist/hooks/useSmartScroll.js +24 -22
  103. package/dist/types/common.d.ts +13 -5
  104. package/dist/types/messages.d.ts +9 -6
  105. package/dist/utils/actionUtils.d.ts +14 -0
  106. package/dist/utils/actionUtils.js +17 -0
  107. package/dist/utils/messageUtils.d.ts +7 -9
  108. package/dist/utils/messageUtils.js +7 -1
  109. package/package.json +12 -7
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { DOMProps, QAProps } from '@gravity-ui/uikit';
2
+ import { DOMProps, InputControlSize, QAProps } from '@gravity-ui/uikit';
3
3
  import { ChatType } from '../../../types';
4
4
  import { ChatFilterFunction } from '../../../utils/chatUtils';
5
5
  import './History.scss';
@@ -37,6 +37,8 @@ export interface HistoryListProps extends QAProps, DOMProps {
37
37
  loading?: boolean;
38
38
  /** Callback when chat is clicked (for closing popup in parent) */
39
39
  onChatClick?: (chat: ChatType) => void;
40
+ /** Size of the list */
41
+ size?: InputControlSize;
40
42
  }
41
43
  /**
42
44
  * HistoryList component - displays a list of chats with search, grouping, and actions
@@ -16,7 +16,7 @@ const b = block('history');
16
16
  * @returns React component
17
17
  */
18
18
  export function HistoryList(props) {
19
- const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, emptyFilteredPlaceholder, className, qa, style, filterFunction = defaultChatFilter, loading = false, onChatClick, } = props;
19
+ const { chats, selectedChat, onSelectChat, onDeleteChat, onLoadMore, hasMore = false, searchable = true, groupBy = 'date', showActions = true, emptyPlaceholder, emptyFilteredPlaceholder, className, qa, style, filterFunction = defaultChatFilter, loading = false, onChatClick, size = 'm', } = props;
20
20
  const [filteredItemCount, setFilteredItemCount] = useState(null);
21
21
  // Group chats if needed
22
22
  const groupedChats = useMemo(() => {
@@ -57,7 +57,11 @@ export function HistoryList(props) {
57
57
  const selectedItemIndex = useMemo(() => {
58
58
  return listItems.findIndex((item) => item.type === 'chat' && item.id === (selectedChat === null || selectedChat === void 0 ? void 0 : selectedChat.id));
59
59
  }, [listItems, selectedChat]);
60
- const handleChatClick = (chat) => {
60
+ const handleChatClick = (chat, _, fromKeyboard, event) => {
61
+ var _a;
62
+ if (fromKeyboard && ((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.type) === 'button') {
63
+ return;
64
+ }
61
65
  onSelectChat === null || onSelectChat === void 0 ? void 0 : onSelectChat(chat);
62
66
  onChatClick === null || onChatClick === void 0 ? void 0 : onChatClick(chat);
63
67
  };
@@ -78,11 +82,11 @@ export function HistoryList(props) {
78
82
  };
79
83
  };
80
84
  }, [filterFunction]);
81
- const renderItem = (item) => {
85
+ const renderItem = (item, isActive) => {
82
86
  if (item.type === 'date-header') {
83
87
  return _jsx(DateHeaderItem, { date: item.date }, `date-${item.date}`);
84
88
  }
85
- return (_jsx(ChatItem, { chat: item, showActions: showActions, onChatClick: handleChatClick, onDeleteClick: onDeleteChat ? handleDeleteClick : undefined }, item.id));
89
+ return (_jsx(ChatItem, { chat: item, showActions: showActions, onDeleteClick: onDeleteChat ? handleDeleteClick : undefined, isActive: isActive }, item.id));
86
90
  };
87
91
  const emptyState = emptyPlaceholder || _jsx("div", { className: b('empty'), children: i18n('empty-state') });
88
92
  const emptyFilteredState = emptyFilteredPlaceholder || (_jsx("div", { className: b('empty'), children: i18n('empty-filtered-state') }));
@@ -90,5 +94,5 @@ export function HistoryList(props) {
90
94
  const finalEmptyPlaceholder = filteredItemCount === null || filteredItemCount === listItems.length
91
95
  ? emptyState
92
96
  : emptyFilteredState;
93
- return (_jsxs("div", { className: b('container', className), "data-qa": qa, style: style, children: [_jsx("div", { className: b('list-wrapper'), children: loading ? (_jsx("div", { className: b('loader-wrapper'), children: _jsx(Loader, { view: "loading" }) })) : (_jsx(List, { size: "m", items: listItems, renderItem: renderItem, virtualized: false, filterable: searchable, filterItem: wrappedFilterFunction, filterPlaceholder: i18n('search-placeholder'), filterClassName: b('filter'), emptyPlaceholder: finalEmptyPlaceholder, selectedItemIndex: selectedItemIndex, itemsClassName: b('list'), itemClassName: b('list-item'), onFilterEnd: (data) => setFilteredItemCount(data.items.length) })) }), hasMore && onLoadMore && (_jsx(Button, { view: "flat-action", size: "m", width: "max", onClick: onLoadMore, children: i18n('action-load-more') }))] }));
97
+ return (_jsxs("div", { className: b('container', className), "data-qa": qa, style: style, children: [_jsx("div", { className: b('list-wrapper'), children: loading ? (_jsx("div", { className: b('loader-wrapper'), children: _jsx(Loader, { view: "loading" }) })) : (_jsx(List, { size: size, items: listItems, renderItem: renderItem, virtualized: false, filterable: searchable, filterItem: wrappedFilterFunction, filterPlaceholder: i18n('search-placeholder'), filterClassName: b('filter'), emptyPlaceholder: finalEmptyPlaceholder, selectedItemIndex: selectedItemIndex, itemsClassName: b('list'), itemClassName: b('list-item'), onFilterEnd: (data) => setFilteredItemCount(data.items.length), onItemClick: handleChatClick })) }), hasMore && onLoadMore && (_jsx(Button, { view: "flat-action", size: "m", width: "max", onClick: onLoadMore, children: i18n('action-load-more') }))] }));
94
98
  }
@@ -0,0 +1,18 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { History } from '..';
3
+ declare const _default: Meta;
4
+ export default _default;
5
+ type Story = StoryObj<typeof History>;
6
+ export declare const Playground: Story;
7
+ export declare const WithSelectedChat: Story;
8
+ export declare const WithLoadMore: Story;
9
+ export declare const WithoutSearch: Story;
10
+ export declare const WithoutGrouping: Story;
11
+ export declare const WithoutActions: Story;
12
+ export declare const EmptyState: Story;
13
+ export declare const WithLoadMoreAndDelete: Story;
14
+ export declare const Interactive: Story;
15
+ export declare const WithCustomEmptyPlaceholder: Story;
16
+ export declare const WithCustomFilter: Story;
17
+ export declare const NotForceOpen: Story;
18
+ export declare const Loading: Story;
@@ -0,0 +1,289 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { ClockArrowRotateLeft } from '@gravity-ui/icons';
5
+ import { Icon } from '@gravity-ui/uikit';
6
+ import { History } from '..';
7
+ import { ContentWrapper } from '../../../../demo/ContentWrapper';
8
+ import { ActionButton } from '../../../atoms/ActionButton';
9
+ import MDXDocs from './Docs.mdx';
10
+ export default {
11
+ title: 'templates/History',
12
+ component: History,
13
+ parameters: {
14
+ docs: {
15
+ page: MDXDocs,
16
+ },
17
+ },
18
+ };
19
+ // Generate mock chats
20
+ const generateMockChats = (count) => {
21
+ const now = new Date();
22
+ const chats = [];
23
+ for (let i = 0; i < count; i++) {
24
+ const daysAgo = Math.floor(i / 3); // Group 3 chats per day
25
+ const date = new Date(now);
26
+ date.setDate(date.getDate() - daysAgo);
27
+ chats.push({
28
+ id: `chat-${i}`,
29
+ name: `Chat ${i + 1}`,
30
+ createTime: date.toISOString(),
31
+ lastMessage: i % 3 === 0
32
+ ? `Looooooooong last message for example ellipsis from chat ${i + 1}`
33
+ : `Last message from chat ${i + 1}`,
34
+ metadata: {},
35
+ });
36
+ }
37
+ return chats;
38
+ };
39
+ const mockChats = generateMockChats(15);
40
+ function HistoryWithTrigger(_a) {
41
+ var { initialOpen = true } = _a, props = __rest(_a, ["initialOpen"]);
42
+ const [open, setOpen] = useState(false);
43
+ const anchorRef = useRef(null);
44
+ const setInitialOpen = useRef(false);
45
+ useEffect(() => {
46
+ if (initialOpen && !open && anchorRef.current && !setInitialOpen.current) {
47
+ setOpen(true);
48
+ setInitialOpen.current = true;
49
+ }
50
+ }, [initialOpen, open, anchorRef.current, setInitialOpen.current]);
51
+ return (_jsxs("div", { style: { paddingLeft: '400px' }, children: [_jsx(ActionButton, { ref: anchorRef, view: "flat", size: "m", onClick: () => setOpen(!open), tooltipTitle: "Chat History", children: _jsx(Icon, { data: ClockArrowRotateLeft, size: 16 }) }), _jsx(History, Object.assign({}, props, { open: open, onOpenChange: setOpen, anchorElement: anchorRef.current }))] }));
52
+ }
53
+ const defaultDecorators = [
54
+ (Story) => (_jsx(ContentWrapper, { width: "600px", height: "800px", children: _jsx(Story, {}) })),
55
+ ];
56
+ export const Playground = {
57
+ args: {
58
+ chats: mockChats,
59
+ searchable: true,
60
+ groupBy: 'date',
61
+ showActions: true,
62
+ onSelectChat: (chat) => {
63
+ // eslint-disable-next-line no-console
64
+ console.log('Selected chat:', chat);
65
+ },
66
+ onDeleteChat: (chat) => {
67
+ // eslint-disable-next-line no-console
68
+ console.log('Delete chat:', chat);
69
+ },
70
+ },
71
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
72
+ decorators: defaultDecorators,
73
+ };
74
+ export const WithSelectedChat = {
75
+ args: {
76
+ chats: mockChats,
77
+ selectedChat: mockChats[2],
78
+ searchable: true,
79
+ groupBy: 'date',
80
+ showActions: true,
81
+ onSelectChat: (chat) => {
82
+ // eslint-disable-next-line no-console
83
+ console.log('Selected chat:', chat);
84
+ },
85
+ },
86
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
87
+ decorators: defaultDecorators,
88
+ };
89
+ export const WithLoadMore = {
90
+ args: {
91
+ searchable: true,
92
+ groupBy: 'date',
93
+ showActions: true,
94
+ onSelectChat: (chat) => {
95
+ // eslint-disable-next-line no-console
96
+ console.log('Selected chat:', chat);
97
+ },
98
+ },
99
+ render: (args) => {
100
+ const [chats, setChats] = useState(mockChats.slice(0, 6));
101
+ const [hasMore, setHasMore] = useState(true);
102
+ const handleLoadMore = () => {
103
+ // eslint-disable-next-line no-console
104
+ console.log('Loading more chats...');
105
+ const currentLength = chats.length;
106
+ const nextChats = mockChats.slice(0, currentLength + 6);
107
+ setChats(nextChats);
108
+ if (nextChats.length >= mockChats.length) {
109
+ setHasMore(false);
110
+ }
111
+ };
112
+ return (_jsx(HistoryWithTrigger, Object.assign({}, args, { chats: chats, hasMore: hasMore, onLoadMore: handleLoadMore })));
113
+ },
114
+ decorators: defaultDecorators,
115
+ };
116
+ export const WithoutSearch = {
117
+ args: {
118
+ chats: mockChats,
119
+ searchable: false,
120
+ groupBy: 'date',
121
+ showActions: true,
122
+ onSelectChat: (chat) => {
123
+ // eslint-disable-next-line no-console
124
+ console.log('Selected chat:', chat);
125
+ },
126
+ },
127
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
128
+ decorators: defaultDecorators,
129
+ };
130
+ export const WithoutGrouping = {
131
+ args: {
132
+ chats: mockChats,
133
+ searchable: true,
134
+ groupBy: 'none',
135
+ showActions: true,
136
+ onSelectChat: (chat) => {
137
+ // eslint-disable-next-line no-console
138
+ console.log('Selected chat:', chat);
139
+ },
140
+ },
141
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
142
+ decorators: defaultDecorators,
143
+ };
144
+ export const WithoutActions = {
145
+ args: {
146
+ chats: mockChats,
147
+ searchable: true,
148
+ groupBy: 'date',
149
+ showActions: false,
150
+ onSelectChat: (chat) => {
151
+ // eslint-disable-next-line no-console
152
+ console.log('Selected chat:', chat);
153
+ },
154
+ },
155
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
156
+ decorators: defaultDecorators,
157
+ };
158
+ export const EmptyState = {
159
+ args: {
160
+ chats: [],
161
+ searchable: true,
162
+ groupBy: 'date',
163
+ showActions: true,
164
+ },
165
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
166
+ decorators: defaultDecorators,
167
+ };
168
+ export const WithLoadMoreAndDelete = {
169
+ args: {
170
+ chats: mockChats,
171
+ searchable: true,
172
+ groupBy: 'date',
173
+ showActions: true,
174
+ hasMore: true,
175
+ onSelectChat: (chat) => {
176
+ // eslint-disable-next-line no-console
177
+ console.log('Selected chat:', chat);
178
+ },
179
+ onDeleteChat: (chat) => {
180
+ // eslint-disable-next-line no-console
181
+ console.log('Delete chat:', chat);
182
+ },
183
+ onLoadMore: () => {
184
+ // eslint-disable-next-line no-console
185
+ console.log('Load more chats');
186
+ },
187
+ },
188
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
189
+ decorators: defaultDecorators,
190
+ };
191
+ export const Interactive = {
192
+ args: {
193
+ searchable: true,
194
+ groupBy: 'date',
195
+ showActions: true,
196
+ },
197
+ render: (args) => {
198
+ const [chats, setChats] = useState(mockChats);
199
+ const [selectedChat, setSelectedChat] = useState(null);
200
+ const handleDeleteChat = (chat) => {
201
+ setChats((prev) => prev.filter((c) => c.id !== chat.id));
202
+ if ((selectedChat === null || selectedChat === void 0 ? void 0 : selectedChat.id) === chat.id) {
203
+ setSelectedChat(null);
204
+ }
205
+ };
206
+ return (_jsx(HistoryWithTrigger, Object.assign({}, args, { chats: chats, selectedChat: selectedChat, onSelectChat: setSelectedChat, onDeleteChat: handleDeleteChat })));
207
+ },
208
+ decorators: defaultDecorators,
209
+ };
210
+ export const WithCustomEmptyPlaceholder = {
211
+ args: {
212
+ chats: [],
213
+ searchable: true,
214
+ groupBy: 'date',
215
+ showActions: true,
216
+ emptyPlaceholder: (_jsxs("div", { style: {
217
+ padding: '40px',
218
+ color: '#999',
219
+ width: '100%',
220
+ display: 'flex',
221
+ flexDirection: 'column',
222
+ alignItems: 'center',
223
+ justifyContent: 'center',
224
+ }, children: [_jsx("div", { style: {
225
+ fontSize: '48px',
226
+ height: '48px',
227
+ display: 'flex',
228
+ alignItems: 'center',
229
+ justifyContent: 'center',
230
+ marginBottom: '16px',
231
+ }, children: "\uD83D\uDCAC" }), _jsx("div", { style: {
232
+ fontSize: '16px',
233
+ fontWeight: 'bold',
234
+ marginBottom: '8px',
235
+ }, children: "No conversations yet" }), _jsx("div", { style: { fontSize: '14px' }, children: "Start a new chat to begin" })] })),
236
+ },
237
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
238
+ decorators: defaultDecorators,
239
+ };
240
+ export const WithCustomFilter = {
241
+ args: {
242
+ chats: mockChats,
243
+ searchable: true,
244
+ groupBy: 'date',
245
+ showActions: true,
246
+ onSelectChat: (chat) => {
247
+ // eslint-disable-next-line no-console
248
+ console.log('Selected chat:', chat);
249
+ },
250
+ // Custom filter that searches only in chat names (not in messages)
251
+ filterFunction: (filter) => (item) => {
252
+ if (item.type === 'date-header') {
253
+ return true;
254
+ }
255
+ return item.name.toLowerCase().includes(filter.toLowerCase());
256
+ },
257
+ },
258
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
259
+ decorators: defaultDecorators,
260
+ };
261
+ export const NotForceOpen = {
262
+ args: {
263
+ chats: mockChats,
264
+ searchable: true,
265
+ groupBy: 'date',
266
+ showActions: true,
267
+ onSelectChat: (chat) => {
268
+ // eslint-disable-next-line no-console
269
+ console.log('Selected chat:', chat);
270
+ },
271
+ onDeleteChat: (chat) => {
272
+ // eslint-disable-next-line no-console
273
+ console.log('Delete chat:', chat);
274
+ },
275
+ },
276
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({ initialOpen: false }, args)),
277
+ decorators: defaultDecorators,
278
+ };
279
+ export const Loading = {
280
+ args: {
281
+ chats: [],
282
+ loading: true,
283
+ searchable: true,
284
+ groupBy: 'date',
285
+ showActions: true,
286
+ },
287
+ render: (args) => _jsx(HistoryWithTrigger, Object.assign({}, args)),
288
+ decorators: defaultDecorators,
289
+ };
@@ -0,0 +1,4 @@
1
+ import './ContentWrapper.scss';
2
+ type Props = React.PropsWithChildren<React.CSSProperties>;
3
+ export declare function ContentWrapper(props: Props): import("react/jsx-runtime").JSX.Element;
4
+ export {};
@@ -0,0 +1,9 @@
1
+ import { __rest } from "tslib";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { cn } from '../../utils/cn';
4
+ import './ContentWrapper.scss';
5
+ const b = cn('content-wrapper');
6
+ export function ContentWrapper(props) {
7
+ const { children } = props, style = __rest(props, ["children"]);
8
+ return (_jsx("div", { className: b(), style: style, children: children }));
9
+ }
@@ -0,0 +1 @@
1
+ export { ContentWrapper } from './ContentWrapper';
@@ -0,0 +1 @@
1
+ export { ContentWrapper } from './ContentWrapper';
@@ -0,0 +1,9 @@
1
+ import './Showcase.scss';
2
+ type Props = React.PropsWithChildren<{
3
+ title?: string;
4
+ description?: React.ReactNode;
5
+ direction?: 'row' | 'column';
6
+ className?: string;
7
+ }>;
8
+ export declare function Showcase({ title, description, direction, className, children }: Props): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../../utils/cn';
3
+ import './Showcase.scss';
4
+ const b = cn('showcase');
5
+ export function Showcase({ title, description, direction = 'row', className, children }) {
6
+ return (_jsxs("div", { className: b({ direction }, className), children: [title && _jsx("div", { className: b('title'), children: title }), description && _jsx("div", { className: b('description'), children: description }), _jsx("div", { className: b('content'), children: children })] }));
7
+ }
@@ -0,0 +1 @@
1
+ export { Showcase } from './Showcase';
@@ -0,0 +1 @@
1
+ export { Showcase } from './Showcase';
@@ -0,0 +1,8 @@
1
+ import './ShowcaseItem.scss';
2
+ interface ShowcaseItemProps {
3
+ title: string;
4
+ children: React.ReactNode;
5
+ width?: string | number;
6
+ }
7
+ export declare function ShowcaseItem({ title, children, width }: ShowcaseItemProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../../utils/cn';
3
+ import './ShowcaseItem.scss';
4
+ const b = cn('showcase-item');
5
+ export function ShowcaseItem({ title, children, width }) {
6
+ return (_jsxs("div", { className: b(), style: { width }, children: [_jsx("div", { className: b('title'), children: title }), _jsx("div", { className: b('content'), children: children })] }));
7
+ }
@@ -0,0 +1 @@
1
+ export { ShowcaseItem } from './ShowcaseItem';
@@ -0,0 +1 @@
1
+ export { ShowcaseItem } from './ShowcaseItem';
@@ -0,0 +1,2 @@
1
+ import './SwapArea.scss';
2
+ export declare const SwapArea: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { block } from '../../utils/cn';
3
+ import './SwapArea.scss';
4
+ const b = block('swap-area');
5
+ export const SwapArea = () => {
6
+ return _jsx("div", { className: b(), children: "Swap Area" });
7
+ };
@@ -0,0 +1 @@
1
+ export { SwapArea } from './SwapArea';
@@ -0,0 +1 @@
1
+ export { SwapArea } from './SwapArea';
@@ -1,7 +1,12 @@
1
1
  import { type RefObject } from 'react';
2
+ import type { ChatStatus } from '../types';
2
3
  export interface UseSmartScrollReturn<T extends HTMLElement> {
3
4
  containerRef: RefObject<T>;
4
5
  endRef: RefObject<T>;
5
- scrollToBottom: () => void;
6
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
6
7
  }
7
- export declare function useSmartScroll<T extends HTMLElement>(isStreaming?: boolean, messagesCount?: number): UseSmartScrollReturn<T>;
8
+ export declare function useSmartScroll<T extends HTMLElement>({ isStreaming, messagesCount, status, }: {
9
+ isStreaming?: boolean;
10
+ messagesCount: number;
11
+ status?: ChatStatus;
12
+ }): UseSmartScrollReturn<T>;
@@ -1,26 +1,21 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useEffect, useRef } from 'react';
2
2
  const SCROLL_THRESHOLD = 10;
3
- export function useSmartScroll(isStreaming = false, messagesCount = 0) {
3
+ export function useSmartScroll({ isStreaming = false, messagesCount, status, }) {
4
4
  const containerRef = useRef(null);
5
5
  const endRef = useRef(null);
6
- const [userScrolledUp, setUserScrolledUp] = useState(false);
7
- const prevMessagesCount = useRef(messagesCount);
8
- const checkIfUserScrolledUp = useCallback(() => {
9
- const container = containerRef.current;
10
- if (!container)
11
- return false;
12
- const { scrollTop, scrollHeight, clientHeight } = container;
13
- const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
14
- return distanceFromBottom > SCROLL_THRESHOLD;
15
- }, []);
6
+ const userScrolledUpRef = useRef(false);
16
7
  const scrollToBottom = useCallback((behavior = 'instant') => {
17
- if (!userScrolledUp) {
8
+ if (!userScrolledUpRef.current) {
18
9
  const end = endRef.current;
19
10
  if (end) {
20
11
  end.scrollIntoView({ behavior, block: 'end' });
21
12
  }
22
13
  }
23
- }, [userScrolledUp]);
14
+ }, []);
15
+ // Initial scroll to bottom
16
+ useEffect(() => {
17
+ scrollToBottom();
18
+ }, []);
24
19
  // Handle user scroll events
25
20
  useEffect(() => {
26
21
  const container = containerRef.current;
@@ -28,14 +23,19 @@ export function useSmartScroll(isStreaming = false, messagesCount = 0) {
28
23
  return undefined;
29
24
  }
30
25
  const handleScroll = () => {
31
- const scrolledUp = checkIfUserScrolledUp();
32
- setUserScrolledUp(scrolledUp);
26
+ if (!container) {
27
+ return;
28
+ }
29
+ const { scrollTop, scrollHeight, clientHeight } = container;
30
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
31
+ const scrolledUp = distanceFromBottom > SCROLL_THRESHOLD;
32
+ userScrolledUpRef.current = scrolledUp;
33
33
  };
34
34
  container.addEventListener('scroll', handleScroll, { passive: true });
35
35
  return () => {
36
36
  container.removeEventListener('scroll', handleScroll);
37
37
  };
38
- }, [checkIfUserScrolledUp]);
38
+ }, []);
39
39
  // Handle DOM mutations during streaming
40
40
  useEffect(() => {
41
41
  const container = containerRef.current;
@@ -54,14 +54,16 @@ export function useSmartScroll(isStreaming = false, messagesCount = 0) {
54
54
  return () => {
55
55
  observer.disconnect();
56
56
  };
57
- }, [isStreaming, scrollToBottom]);
58
- // Handle new messages
57
+ }, [isStreaming]);
58
+ // Handle status changes
59
+ useEffect(() => {
60
+ scrollToBottom('smooth');
61
+ }, [status]);
59
62
  useEffect(() => {
60
- if (messagesCount > prevMessagesCount.current) {
63
+ if (messagesCount) {
61
64
  scrollToBottom('smooth');
62
65
  }
63
- prevMessagesCount.current = messagesCount;
64
- }, [messagesCount, scrollToBottom]);
66
+ }, [messagesCount]);
65
67
  return {
66
68
  containerRef,
67
69
  endRef,
@@ -1,7 +1,15 @@
1
- import { ButtonView } from '@gravity-ui/uikit';
2
- export type Action = {
1
+ import { ButtonButtonProps } from '@gravity-ui/uikit';
2
+ /**
3
+ * Action configuration object with properties
4
+ */
5
+ export type ActionConfig = {
6
+ actionType?: string;
3
7
  label?: string;
4
- onClick?: () => void;
5
- view?: ButtonView;
6
8
  icon?: React.ReactNode;
7
- };
9
+ } & ButtonButtonProps;
10
+ /**
11
+ * Action can be either:
12
+ * - ActionConfig object with properties (label, icon, onClick, view)
13
+ * - React.ReactNode for fully custom content
14
+ */
15
+ export type Action = ActionConfig | React.ReactNode;
@@ -1,14 +1,17 @@
1
1
  import type React from 'react';
2
- import type { IconData } from '@gravity-ui/uikit';
2
+ import { ActionConfig } from './common';
3
3
  import { ToolMessageProps } from './tool';
4
+ export type BaseMessageActionConfig = ActionConfig;
5
+ /**
6
+ * BaseMessage action can be either:
7
+ * - BaseMessageActionConfig object with properties (including type for default icons)
8
+ * - React.ReactNode for fully custom content
9
+ */
10
+ export type BaseMessageAction = BaseMessageActionConfig | React.ReactNode;
4
11
  export type BaseMessageProps = {
5
12
  children: React.ReactNode;
6
13
  role: TMessageRole;
7
- actions?: Array<{
8
- type: string;
9
- onClick: () => void;
10
- icon?: IconData;
11
- }>;
14
+ actions?: BaseMessageAction[];
12
15
  timestamp?: string;
13
16
  showTimestamp?: boolean;
14
17
  showActionsOnHover?: boolean;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { Action, ActionConfig } from '../types/common';
3
+ /**
4
+ * Type guard для проверки, является ли action React элементом
5
+ * @param action - Проверяемое значение
6
+ * @returns true если action является React.ReactNode, false если это ActionConfig
7
+ */
8
+ export declare function isReactNodeAction(action: Action): action is React.ReactNode;
9
+ /**
10
+ * Type guard для проверки, является ли action объектом конфигурации
11
+ * @param action - Проверяемое значение
12
+ * @returns true если action является ActionConfig, false если это React.ReactNode
13
+ */
14
+ export declare function isActionConfig(action: Action): action is ActionConfig;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ /**
3
+ * Type guard для проверки, является ли action React элементом
4
+ * @param action - Проверяемое значение
5
+ * @returns true если action является React.ReactNode, false если это ActionConfig
6
+ */
7
+ export function isReactNodeAction(action) {
8
+ return React.isValidElement(action);
9
+ }
10
+ /**
11
+ * Type guard для проверки, является ли action объектом конфигурации
12
+ * @param action - Проверяемое значение
13
+ * @returns true если action является ActionConfig, false если это React.ReactNode
14
+ */
15
+ export function isActionConfig(action) {
16
+ return !React.isValidElement(action);
17
+ }