@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,10 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { Button, Text } from '@gravity-ui/uikit';
4
+ import { isActionConfig } from '../../../utils/actionUtils';
3
5
  import { block } from '../../../utils/cn';
4
6
  import { Loader, Shimmer } from '../../atoms';
5
7
  import { ButtonGroup } from '../ButtonGroup';
6
8
  import './ToolFooter.scss';
7
9
  const b = block('tool-footer');
8
10
  export function ToolFooter({ actions, content, showLoader = true, className, qa }) {
9
- return (_jsxs("div", { className: b('', className), "data-qa": qa, children: [_jsxs("div", { className: b('left'), children: [showLoader && _jsx(Loader, { view: "loading", size: "xs" }), content && (_jsx(Shimmer, { children: _jsx(Text, { children: content }) }))] }), _jsx(ButtonGroup, { children: actions.map((action, index) => (_jsx(Button, { view: action.view, onClick: action.onClick, size: "s", children: action.label }, index))) })] }));
11
+ return (_jsxs("div", { className: b('', className), "data-qa": qa, children: [_jsxs("div", { className: b('left'), children: [showLoader && _jsx(Loader, { view: "loading", size: "xs" }), content && (_jsx(Shimmer, { children: _jsx(Text, { children: content }) }))] }), _jsx(ButtonGroup, { children: actions.map((action, index) => {
12
+ if (!isActionConfig(action)) {
13
+ return _jsx(React.Fragment, { children: action }, index);
14
+ }
15
+ return (_jsx(Button, { view: action.view, onClick: action.onClick, size: "s", children: action.label }, index));
16
+ }) })] }));
10
17
  }
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryFn, StoryObj } from '@storybook/react-webpack5';
2
+ import type { ToolHeaderProps } from '../../../../types/tool';
3
+ declare const _default: Meta;
4
+ export default _default;
5
+ export declare const Playground: StoryFn<ToolHeaderProps>;
6
+ export declare const Loading: StoryObj<ToolHeaderProps>;
7
+ export declare const Success: StoryObj<ToolHeaderProps>;
8
+ export declare const Cancelled: StoryObj<ToolHeaderProps>;
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ChevronDown, Copy, Pencil } from '@gravity-ui/icons';
3
+ import { Icon, Text } from '@gravity-ui/uikit';
4
+ import { ToolHeader } from '..';
5
+ import { ContentWrapper } from '../../../../demo/ContentWrapper';
6
+ import { Showcase } from '../../../../demo/Showcase';
7
+ import MDXDocs from './Docs.mdx';
8
+ export default {
9
+ title: 'molecules/ToolHeader',
10
+ component: ToolHeader,
11
+ parameters: {
12
+ docs: {
13
+ page: MDXDocs,
14
+ },
15
+ },
16
+ argTypes: {
17
+ toolName: {
18
+ control: 'text',
19
+ description: 'Name of the tool',
20
+ },
21
+ status: {
22
+ control: 'select',
23
+ options: ['success', 'error', 'loading', 'cancelled'],
24
+ description: 'Status indicator',
25
+ },
26
+ },
27
+ };
28
+ const defaultDecorators = [
29
+ (Story) => (_jsx(Showcase, { children: _jsx(Story, {}) })),
30
+ ];
31
+ const copyAction = {
32
+ label: 'Copy',
33
+ onClick: () => alert('Copied'),
34
+ icon: _jsx(Icon, { data: Copy, size: 16 }),
35
+ };
36
+ const collapseAction = {
37
+ label: 'Collapse',
38
+ onClick: () => alert('Collapsed'),
39
+ icon: _jsx(Icon, { data: ChevronDown, size: 16 }),
40
+ };
41
+ export const Playground = (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolHeader, Object.assign({}, args)) }));
42
+ Playground.args = {
43
+ toolIcon: _jsx(Icon, { data: Pencil, size: 16 }),
44
+ toolName: 'Writing',
45
+ content: (_jsx(Text, { color: "secondary", variant: "body-1", children: "expectScreenshotFixture.ts" })),
46
+ status: 'success',
47
+ };
48
+ export const Loading = {
49
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolHeader, Object.assign({}, args, { toolIcon: _jsx(Icon, { data: Pencil, size: 16 }), toolName: "Writing", content: _jsx(Text, { color: "secondary", variant: "body-1", children: "expectScreenshotFixture.ts" }), status: "loading" })) })),
50
+ decorators: defaultDecorators,
51
+ };
52
+ export const Success = {
53
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolHeader, Object.assign({}, args, { toolIcon: _jsx(Icon, { data: Pencil, size: 16 }), toolName: "Writing", content: _jsx(Text, { color: "secondary", variant: "body-1", children: "expectScreenshotFixture.ts" }), actions: [collapseAction, copyAction], status: "success" })) })),
54
+ decorators: defaultDecorators,
55
+ };
56
+ export const Cancelled = {
57
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolHeader, Object.assign({}, args, { toolIcon: _jsx(Icon, { data: Pencil, size: 16 }), toolName: "Writing", content: _jsx(Text, { color: "secondary", variant: "body-1", children: "expectScreenshotFixture.ts" }), status: "cancelled" })) })),
58
+ decorators: defaultDecorators,
59
+ };
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { Text } from '@gravity-ui/uikit';
4
+ import { isActionConfig } from '../../../utils/actionUtils';
3
5
  import { block } from '../../../utils/cn';
4
6
  import { ActionButton } from '../../atoms';
5
7
  import { ButtonGroup } from '../ButtonGroup';
@@ -9,5 +11,12 @@ const b = block('tool-header');
9
11
  export function ToolHeader(props) {
10
12
  const { toolIcon, toolName, content, actions, status, className, qa } = props;
11
13
  const hasActions = actions && actions.length > 0;
12
- return (_jsxs("div", { className: b('', className), "data-qa": qa, children: [_jsxs("div", { className: b('left'), children: [toolIcon, _jsx(Text, { children: toolName }), content] }), _jsxs("div", { className: b('right'), children: [hasActions && (_jsx(ButtonGroup, { children: actions.map((action, index) => (_jsx(ActionButton, Object.assign({ tooltipTitle: action.label, view: "flat-secondary", size: "s" }, action, { children: action.icon }), index))) })), _jsx(ToolStatus, { status: status })] })] }));
14
+ return (_jsxs("div", { className: b('', className), "data-qa": qa, children: [_jsxs("div", { className: b('left'), children: [toolIcon, _jsx(Text, { children: toolName }), content] }), _jsxs("div", { className: b('right'), children: [hasActions && (_jsx(ButtonGroup, { children: actions.map((action, index) => {
15
+ // Check if action is a ReactNode (not an object with properties)
16
+ if (!isActionConfig(action)) {
17
+ return _jsx(React.Fragment, { children: action }, index);
18
+ }
19
+ // TypeScript now knows that action is ActionConfig
20
+ return (_jsx(ActionButton, { tooltipTitle: action.label, view: action.view || 'flat-secondary', size: "s", onClick: action.onClick, children: action.icon }, index));
21
+ }) })), _jsx(ToolStatus, { status: status })] })] }));
13
22
  }
@@ -0,0 +1,9 @@
1
+ import { Meta, StoryFn, StoryObj } from '@storybook/react-webpack5';
2
+ import type { ToolStatusProps } from '../index';
3
+ declare const _default: Meta;
4
+ export default _default;
5
+ export declare const Playground: StoryFn<ToolStatusProps>;
6
+ export declare const Success: StoryObj<ToolStatusProps>;
7
+ export declare const Error: StoryObj<ToolStatusProps>;
8
+ export declare const Loading: StoryObj<ToolStatusProps>;
9
+ export declare const Cancelled: StoryObj<ToolStatusProps>;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ToolStatus } from '..';
3
+ import { ContentWrapper } from '../../../../demo/ContentWrapper';
4
+ import { Showcase } from '../../../../demo/Showcase';
5
+ import MDXDocs from './Docs.mdx';
6
+ export default {
7
+ title: 'molecules/ToolStatus',
8
+ component: ToolStatus,
9
+ parameters: {
10
+ docs: {
11
+ page: MDXDocs,
12
+ },
13
+ },
14
+ argTypes: {
15
+ status: {
16
+ control: 'select',
17
+ options: ['success', 'error', 'loading', 'cancelled', undefined],
18
+ description: 'Tool status',
19
+ },
20
+ },
21
+ };
22
+ const defaultDecorators = [
23
+ (Story) => (_jsx(Showcase, { children: _jsx(Story, {}) })),
24
+ ];
25
+ export const Playground = (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolStatus, Object.assign({}, args)) }));
26
+ Playground.args = {
27
+ status: 'success',
28
+ };
29
+ export const Success = {
30
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolStatus, Object.assign({}, args, { status: "success" })) })),
31
+ decorators: defaultDecorators,
32
+ };
33
+ export const Error = {
34
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolStatus, Object.assign({}, args, { status: "error" })) })),
35
+ decorators: defaultDecorators,
36
+ };
37
+ export const Loading = {
38
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolStatus, Object.assign({}, args, { status: "loading" })) })),
39
+ decorators: defaultDecorators,
40
+ };
41
+ export const Cancelled = {
42
+ render: (args) => (_jsx(ContentWrapper, { width: "430px", children: _jsx(ToolStatus, Object.assign({}, args, { status: "cancelled" })) })),
43
+ decorators: defaultDecorators,
44
+ };
@@ -0,0 +1,13 @@
1
+ import { Meta, StoryFn, StoryObj } from '@storybook/react-webpack5';
2
+ import { type AssistantMessageProps } from '..';
3
+ import type { TMessageContent } from '../../../../types/messages';
4
+ declare const _default: Meta;
5
+ export default _default;
6
+ export declare const Playground: StoryFn<AssistantMessageProps>;
7
+ export declare const WithToolCall: StoryObj<AssistantMessageProps>;
8
+ interface CustomMessageData {
9
+ title: string;
10
+ description: string;
11
+ }
12
+ type CustomMessageContent = TMessageContent<'custom', CustomMessageData>;
13
+ export declare const WithCustomRenderer: StoryObj<AssistantMessageProps<CustomMessageContent>>;
@@ -0,0 +1,151 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable no-console */
3
+ import { Pencil } from '@gravity-ui/icons';
4
+ import { Icon, Text } from '@gravity-ui/uikit';
5
+ import { AssistantMessage } from '..';
6
+ import { ContentWrapper } from '../../../../demo/ContentWrapper';
7
+ import { Showcase } from '../../../../demo/Showcase';
8
+ import { ShowcaseItem } from '../../../../demo/ShowcaseItem';
9
+ import { createMessageRendererRegistry, registerMessageRenderer, } from '../../../../utils/messageTypeRegistry';
10
+ import MDXDocs from './Docs.mdx';
11
+ export default {
12
+ title: 'organisms/AssistantMessage',
13
+ component: AssistantMessage,
14
+ parameters: {
15
+ docs: {
16
+ page: MDXDocs,
17
+ },
18
+ },
19
+ argTypes: {
20
+ content: {
21
+ control: 'object',
22
+ description: 'Message content',
23
+ },
24
+ actions: {
25
+ control: 'object',
26
+ description: 'Message actions',
27
+ },
28
+ timestamp: {
29
+ control: 'text',
30
+ description: 'Message timestamp',
31
+ },
32
+ id: {
33
+ control: 'text',
34
+ description: 'Message ID',
35
+ },
36
+ messageRendererRegistry: {
37
+ control: false,
38
+ description: 'Custom message renderer registry',
39
+ },
40
+ showActionsOnHover: {
41
+ control: 'boolean',
42
+ description: 'Show action buttons on hover',
43
+ },
44
+ showTimestamp: {
45
+ control: 'boolean',
46
+ description: 'Show timestamp in actions area',
47
+ },
48
+ className: {
49
+ control: 'text',
50
+ description: 'Additional CSS class',
51
+ },
52
+ qa: {
53
+ control: 'text',
54
+ description: 'QA/test identifier',
55
+ },
56
+ },
57
+ };
58
+ const simpleMessage = {
59
+ id: '1',
60
+ role: 'assistant',
61
+ content: 'Hello! How can I help you today?',
62
+ };
63
+ const multiPartMessage = {
64
+ id: '4',
65
+ role: 'assistant',
66
+ content: [
67
+ {
68
+ type: 'text',
69
+ data: {
70
+ text: "I'll scan the SCSS structure: global styles and mixins, theme files, and a couple of component styles. I'll also search for the custom Sass function usage and theming patterns.",
71
+ },
72
+ },
73
+ {
74
+ type: 'tool',
75
+ data: {
76
+ toolName: 'Reading',
77
+ headerContent: (_jsx(Text, { color: "secondary", variant: "body-1", children: "Warning.css" })),
78
+ status: 'success',
79
+ toolIcon: _jsx(Icon, { data: Pencil }),
80
+ },
81
+ },
82
+ {
83
+ type: 'text',
84
+ data: {
85
+ text: 'I’m organizing which files to read first. I’ll start with the essentials like package.json, README.md, tsconfig.json, jest.config.js, and eslint.config.mjs.',
86
+ },
87
+ },
88
+ ],
89
+ };
90
+ const actions = [
91
+ // eslint-disable-next-line no-console
92
+ { actionType: 'copy', onClick: () => console.log('Copy clicked') },
93
+ // eslint-disable-next-line no-console
94
+ { actionType: 'like', onClick: () => console.log('Like clicked') },
95
+ // eslint-disable-next-line no-console
96
+ { actionType: 'unlike', onClick: () => console.log('Unlike clicked') },
97
+ ];
98
+ export const Playground = (args) => (_jsx(ContentWrapper, { width: "480px", children: _jsx(AssistantMessage, Object.assign({}, args)) }));
99
+ Playground.args = {
100
+ content: simpleMessage.content,
101
+ actions,
102
+ timestamp: simpleMessage.timestamp,
103
+ id: simpleMessage.id,
104
+ };
105
+ export const WithToolCall = {
106
+ render: (args) => (_jsx(ShowcaseItem, { title: "With Tool Call", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(AssistantMessage, Object.assign({}, args, { content: multiPartMessage.content, actions: actions, timestamp: multiPartMessage.timestamp, id: multiPartMessage.id })) }) })),
107
+ decorators: [
108
+ (StoryComponent) => (_jsx(Showcase, { children: _jsx(StoryComponent, {}) })),
109
+ ],
110
+ };
111
+ const CustomMessageView = ({ part, }) => {
112
+ const { title, description } = part.data;
113
+ return (_jsxs("div", { style: {
114
+ padding: '16px',
115
+ border: '1px solid var(--g-color-line-generic)',
116
+ borderRadius: '8px',
117
+ backgroundColor: 'var(--g-color-base-float)',
118
+ }, children: [_jsx("div", { style: { fontWeight: 'bold', marginBottom: '8px' }, children: title }), _jsx("div", { style: { color: 'var(--g-color-text-secondary)' }, children: description })] }));
119
+ };
120
+ export const WithCustomRenderer = {
121
+ render: (args) => {
122
+ const customRegistry = createMessageRendererRegistry();
123
+ registerMessageRenderer(customRegistry, 'custom', {
124
+ component: CustomMessageView,
125
+ });
126
+ const customMessage = {
127
+ id: '5',
128
+ role: 'assistant',
129
+ timestamp: '2024-01-01T00:00:04Z',
130
+ content: [
131
+ {
132
+ type: 'text',
133
+ data: {
134
+ text: "I'll scan the SCSS structure: global styles and mixins, theme files, and a couple of component styles. I'll also search for the custom Sass function usage and theming patterns.",
135
+ },
136
+ },
137
+ {
138
+ type: 'custom',
139
+ data: {
140
+ title: 'Custom Message',
141
+ description: 'This is a custom message part rendered with a custom renderer.',
142
+ },
143
+ },
144
+ ],
145
+ };
146
+ return (_jsx(ShowcaseItem, { title: "With Custom Renderer", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(AssistantMessage, Object.assign({}, args, { content: customMessage.content, actions: actions, timestamp: customMessage.timestamp, id: customMessage.id, messageRendererRegistry: customRegistry })) }) }));
147
+ },
148
+ decorators: [
149
+ (StoryComponent) => (_jsx(Showcase, { children: _jsx(StoryComponent, {}) })),
150
+ ],
151
+ };
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
2
+ import React, { useCallback } from 'react';
3
3
  import { ChevronsCollapseUpRight, ChevronsExpandUpRight, ClockArrowRotateLeft, Plus, Sparkles, Xmark, } from '@gravity-ui/icons';
4
- import { Button, Icon, Text } from '@gravity-ui/uikit';
4
+ import { Icon, Text } from '@gravity-ui/uikit';
5
+ import { isActionConfig } from '../../../utils/actionUtils';
5
6
  import { block } from '../../../utils/cn';
6
7
  import { ActionButton } from '../../atoms';
7
8
  import { ButtonGroup } from '../../molecules';
@@ -30,8 +31,11 @@ const FOLDING_ICONS = {
30
31
  */
31
32
  export function Header(props) {
32
33
  const { title, preview, icon, baseActions, additionalActions, titlePosition, withIcon, showTitle = true, className, historyButtonRef, } = useHeader(props);
34
+ // Determine class for title positioning
35
+ const titlePositionClass = b('title-container', { position: titlePosition });
36
+ const iconElement = icon ? (_jsx("div", { className: b('icon'), children: icon })) : (_jsx(Icon, { data: Sparkles, size: 16 }));
33
37
  // Render base action
34
- const renderBaseAction = (action) => {
38
+ const renderBaseAction = useCallback((action, ref) => {
35
39
  let IconComponent = ACTION_ICONS[action.id];
36
40
  // Handle folding icon based on state
37
41
  if (action.id === HeaderAction.Folding && action.foldingState) {
@@ -46,21 +50,16 @@ export function Header(props) {
46
50
  tooltipKey = `action-tooltip-folding-${action.foldingState}`;
47
51
  }
48
52
  // Determine ref for history button
49
- const buttonRef = action.id === HeaderAction.History ? historyButtonRef : undefined;
53
+ const buttonRef = action.id === HeaderAction.History ? ref : undefined;
50
54
  return (_jsx(ActionButton, { ref: buttonRef, tooltipTitle: i18n(tooltipKey), size: "m", view: "flat", onClick: action.onClick, className: b('action-button'), qa: `header-action-${action.id}`, children: _jsx(Icon, { data: IconComponent, size: 16 }) }, action.id));
51
- };
55
+ }, []);
52
56
  // Render additional action
53
- const renderAdditionalAction = (action, index) => {
54
- if (action.content && React.isValidElement(action.content)) {
55
- return (_jsx(React.Fragment, { children: action.content }, action.id || `additional-${index}`));
56
- }
57
- if (action.buttonProps) {
58
- return _jsx(Button, Object.assign({}, action.buttonProps), action.id);
57
+ const renderAdditionalAction = useCallback((action, index) => {
58
+ const id = `additional-${index}`;
59
+ if (!isActionConfig(action)) {
60
+ return _jsx(React.Fragment, { children: action }, id);
59
61
  }
60
- return null;
61
- };
62
- // Determine class for title positioning
63
- const titlePositionClass = b('title-container', { position: titlePosition });
64
- const iconElement = icon ? (_jsx("div", { className: b('icon'), children: icon })) : (_jsx(Icon, { data: Sparkles, size: 16 }));
65
- return (_jsxs("div", { className: b('', className), children: [withIcon && iconElement, showTitle && (_jsxs("div", { className: titlePositionClass, children: [title && (_jsx(Text, { as: "div", variant: "subheader-2", className: b('title'), children: title })), preview && _jsx("div", { className: b('preview'), children: preview })] })), _jsxs(ButtonGroup, { children: [additionalActions.map((action, index) => renderAdditionalAction(action, index)), baseActions.map((action) => renderBaseAction(action))] })] }));
62
+ return (_jsx(ActionButton, Object.assign({}, action, { view: action.view || 'flat', size: "m", children: action.icon || action.label }), `${index}`));
63
+ }, []);
64
+ return (_jsxs("div", { className: b('', className), children: [withIcon && iconElement, showTitle && (_jsxs("div", { className: titlePositionClass, children: [title && (_jsx(Text, { as: "div", variant: "subheader-2", className: b('title'), children: title })), preview && _jsx("div", { className: b('preview'), children: preview })] })), _jsxs(ButtonGroup, { children: [additionalActions.map((action, index) => renderAdditionalAction(action, index)), baseActions.map((action) => renderBaseAction(action, historyButtonRef))] })] }));
66
65
  }
@@ -0,0 +1,15 @@
1
+ import { Meta, StoryFn, StoryObj } from '@storybook/react-webpack5';
2
+ import { type HeaderProps } from '../index';
3
+ declare const _default: Meta;
4
+ export default _default;
5
+ export declare const Playground: StoryFn<HeaderProps>;
6
+ export declare const WithTitle: StoryFn<HeaderProps>;
7
+ export declare const WithIcon: StoryFn<HeaderProps>;
8
+ export declare const WithoutIcon: StoryFn<HeaderProps>;
9
+ export declare const WithPreview: StoryFn<HeaderProps>;
10
+ export declare const WithoutTitle: StoryFn<HeaderProps>;
11
+ export declare const TitlePositions: StoryObj<HeaderProps>;
12
+ export declare const BaseActions: StoryObj<HeaderProps>;
13
+ export declare const AdditionalActions: StoryFn<HeaderProps>;
14
+ export declare const FullExample: StoryFn<HeaderProps>;
15
+ export declare const FoldingInteractive: StoryFn<HeaderProps>;
@@ -0,0 +1,159 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Gear } from '@gravity-ui/icons';
4
+ import { Icon } from '@gravity-ui/uikit';
5
+ import { ContentWrapper } from '../../../../demo/ContentWrapper';
6
+ import { Showcase } from '../../../../demo/Showcase';
7
+ import { ShowcaseItem } from '../../../../demo/ShowcaseItem';
8
+ import { Header, HeaderAction } from '../index';
9
+ import MDXDocs from './Docs.mdx';
10
+ export default {
11
+ title: 'organisms/Header',
12
+ component: Header,
13
+ parameters: {
14
+ docs: {
15
+ page: MDXDocs,
16
+ },
17
+ },
18
+ argTypes: {
19
+ titlePosition: {
20
+ control: 'radio',
21
+ options: ['left', 'center'],
22
+ description: 'Title position',
23
+ },
24
+ },
25
+ };
26
+ const defaultDecorators = [
27
+ (Story) => (_jsx(Showcase, { children: _jsx(Story, {}) })),
28
+ ];
29
+ // Mock handlers for actions
30
+ const mockHandlers = {
31
+ handleNewChat: () => {
32
+ // eslint-disable-next-line no-console
33
+ console.log('New chat clicked');
34
+ },
35
+ handleHistoryToggle: () => {
36
+ // eslint-disable-next-line no-console
37
+ console.log('History toggle clicked');
38
+ },
39
+ handleFolding: (value) => {
40
+ // eslint-disable-next-line no-console
41
+ console.log('Folding clicked:', value);
42
+ },
43
+ handleClose: () => {
44
+ // eslint-disable-next-line no-console
45
+ console.log('Close clicked');
46
+ },
47
+ };
48
+ export const Playground = (args) => {
49
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({}, args)) }));
50
+ };
51
+ Playground.args = Object.assign({ title: 'Chat Header', baseActions: [
52
+ HeaderAction.NewChat,
53
+ HeaderAction.History,
54
+ HeaderAction.Folding,
55
+ HeaderAction.Close,
56
+ ], foldingState: 'opened' }, mockHandlers);
57
+ export const WithTitle = (args) => {
58
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Chat Header", baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close] }, mockHandlers, args)) }));
59
+ };
60
+ export const WithIcon = (args) => {
61
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ icon: _jsx("div", { style: { width: 24, height: 24, background: '#ccc', borderRadius: 4 } }), title: "Chat Header", baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close] }, mockHandlers, args)) }));
62
+ };
63
+ export const WithoutIcon = (args) => {
64
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ withIcon: false, title: "Chat Header", baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close] }, mockHandlers, args)) }));
65
+ };
66
+ export const WithPreview = (args) => {
67
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Chat Header", preview: _jsx("div", { children: "Preview" }), baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close] }, mockHandlers, args)) }));
68
+ };
69
+ export const WithoutTitle = (args) => {
70
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Chat Header", preview: _jsx("div", { children: "Preview" }), showTitle: false, baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close] }, mockHandlers, args)) }));
71
+ };
72
+ export const TitlePositions = {
73
+ render: (args) => {
74
+ return (_jsxs(_Fragment, { children: [_jsx(ShowcaseItem, { title: "Left", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Left Title", titlePosition: "left", baseActions: [HeaderAction.NewChat, HeaderAction.Close] }, mockHandlers, args)) }) }), _jsx(ShowcaseItem, { title: "Center", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Center Title", titlePosition: "center", baseActions: [HeaderAction.NewChat, HeaderAction.Close] }, mockHandlers, args)) }) })] }));
75
+ },
76
+ decorators: defaultDecorators,
77
+ };
78
+ export const BaseActions = {
79
+ render: (args) => {
80
+ return (_jsxs(_Fragment, { children: [_jsx(ShowcaseItem, { title: "All actions", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "All Actions", baseActions: [
81
+ HeaderAction.NewChat,
82
+ HeaderAction.History,
83
+ HeaderAction.Folding,
84
+ HeaderAction.Close,
85
+ ], foldingState: "opened" }, mockHandlers)) }) }), _jsx(ShowcaseItem, { title: "Folding opened", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "With Folding (opened)", baseActions: [
86
+ HeaderAction.NewChat,
87
+ HeaderAction.History,
88
+ HeaderAction.Folding,
89
+ ], foldingState: "opened" }, mockHandlers)) }) }), _jsx(ShowcaseItem, { title: "Folding collapsed", children: _jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "With Folding (collapsed)", baseActions: [
90
+ HeaderAction.NewChat,
91
+ HeaderAction.History,
92
+ HeaderAction.Folding,
93
+ ], foldingState: "collapsed" }, mockHandlers, args)) }) })] }));
94
+ },
95
+ decorators: defaultDecorators,
96
+ };
97
+ // Статический массив для additionalActions - вынесен за пределы компонента,
98
+ // чтобы не создавать новый массив при каждом рендере
99
+ const additionalActionsConfig = [
100
+ {
101
+ label: 'Action 1',
102
+ onClick: () => {
103
+ // eslint-disable-next-line no-console
104
+ console.log('Additional action 1');
105
+ },
106
+ },
107
+ {
108
+ icon: _jsx(Icon, { data: Gear, size: 16 }),
109
+ onClick: () => {
110
+ // eslint-disable-next-line no-console
111
+ console.log('Settings clicked');
112
+ },
113
+ view: 'flat',
114
+ },
115
+ // Custom ReactNode
116
+ _jsx("button", { onClick: () => {
117
+ // eslint-disable-next-line no-console
118
+ console.log('Custom button');
119
+ }, style: { padding: '6px 12px', cursor: 'pointer' }, children: "Custom" }, "custom"),
120
+ ];
121
+ export const AdditionalActions = (args) => {
122
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "With Additional Actions", baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close], additionalActions: additionalActionsConfig }, mockHandlers, args)) }));
123
+ };
124
+ // Статический массив для FullExample - вынесен за пределы компонента
125
+ const fullExampleAdditionalActions = [
126
+ {
127
+ label: 'Settings',
128
+ view: 'outlined',
129
+ onClick: () => {
130
+ // eslint-disable-next-line no-console
131
+ console.log('Settings clicked');
132
+ },
133
+ },
134
+ {
135
+ icon: _jsx(Icon, { data: Gear, size: 16 }),
136
+ onClick: () => {
137
+ // eslint-disable-next-line no-console
138
+ console.log('Settings icon clicked');
139
+ },
140
+ view: 'flat',
141
+ },
142
+ ];
143
+ export const FullExample = (args) => {
144
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ icon: _jsx("div", { style: { width: 24, height: 24, background: '#ccc', borderRadius: 4 } }), title: "Chat Header", preview: _jsx("div", { children: "Preview" }), baseActions: [HeaderAction.NewChat, HeaderAction.History, HeaderAction.Close], additionalActions: fullExampleAdditionalActions, titlePosition: "center" }, mockHandlers, args)) }));
145
+ };
146
+ // Interactive story to demonstrate folding state toggle
147
+ export const FoldingInteractive = (args) => {
148
+ const [foldingState, setFoldingState] = useState('opened');
149
+ return (_jsx(ContentWrapper, { width: "480px", children: _jsx(Header, Object.assign({ title: "Interactive Folding", baseActions: [
150
+ HeaderAction.NewChat,
151
+ HeaderAction.History,
152
+ HeaderAction.Folding,
153
+ HeaderAction.Close,
154
+ ], foldingState: foldingState, handleFolding: (value) => {
155
+ setFoldingState(value);
156
+ // eslint-disable-next-line no-console
157
+ console.log('Folding state changed to:', value);
158
+ }, handleNewChat: mockHandlers.handleNewChat, handleHistoryToggle: mockHandlers.handleHistoryToggle, handleClose: mockHandlers.handleClose }, args)) }));
159
+ };
@@ -1,11 +1,10 @@
1
- import type { ButtonProps } from '@gravity-ui/uikit';
1
+ import type { Action } from '../../../types/common';
2
2
  export declare enum HeaderAction {
3
3
  NewChat = "newChat",
4
4
  History = "history",
5
5
  Folding = "folding",
6
6
  Close = "close"
7
7
  }
8
- export type AdditionalActionsConfig = ButtonProps | React.ReactNode;
9
8
  export type HeaderProps = {
10
9
  icon?: React.ReactNode;
11
10
  title?: string;
@@ -15,7 +14,7 @@ export type HeaderProps = {
15
14
  handleHistoryToggle?: () => void;
16
15
  handleFolding?: (value: 'collapsed' | 'opened') => void;
17
16
  handleClose?: () => void;
18
- additionalActions?: AdditionalActionsConfig[];
17
+ additionalActions?: Action[];
19
18
  historyButtonRef?: React.RefObject<HTMLElement>;
20
19
  foldingState?: 'collapsed' | 'opened';
21
20
  titlePosition?: 'left' | 'center';
@@ -1,12 +1,10 @@
1
1
  import React from 'react';
2
- import type { ButtonProps } from '@gravity-ui/uikit';
2
+ import type { Action } from '../../../types/common';
3
3
  import { type HeaderProps } from './types';
4
4
  export type ActionItem = {
5
5
  id: string;
6
6
  type: 'base' | 'additional';
7
- content: React.ReactNode;
8
7
  onClick?: () => void;
9
- buttonProps?: ButtonProps;
10
8
  foldingState?: 'collapsed' | 'opened';
11
9
  };
12
10
  export declare function useHeader(props: HeaderProps): {
@@ -14,7 +12,7 @@ export declare function useHeader(props: HeaderProps): {
14
12
  preview: React.ReactNode | undefined;
15
13
  icon: React.ReactNode | undefined;
16
14
  baseActions: ActionItem[];
17
- additionalActions: ActionItem[];
15
+ additionalActions: Action[];
18
16
  titlePosition: 'left' | 'center';
19
17
  withIcon: boolean;
20
18
  showTitle: boolean;