@redocly/theme 0.51.0-next.2 → 0.51.0-next.4

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 (85) hide show
  1. package/lib/components/Catalog/Catalog.js +2 -26
  2. package/lib/components/Catalog/CatalogVirtualizedGroups.d.ts +11 -0
  3. package/lib/components/Catalog/CatalogVirtualizedGroups.js +125 -0
  4. package/lib/components/Search/SearchAiConversationInput.d.ts +8 -0
  5. package/lib/components/Search/SearchAiConversationInput.js +114 -0
  6. package/lib/components/Search/SearchAiDialog.d.ts +18 -0
  7. package/lib/components/Search/SearchAiDialog.js +165 -0
  8. package/lib/components/Search/SearchAiMessage.d.ts +12 -0
  9. package/lib/components/Search/SearchAiMessage.js +146 -0
  10. package/lib/components/Search/SearchAiResponse.d.ts +1 -0
  11. package/lib/components/Search/SearchAiResponse.js +39 -3
  12. package/lib/components/Search/SearchDialog.js +83 -25
  13. package/lib/components/Search/variables.js +112 -6
  14. package/lib/core/constants/search.d.ts +4 -0
  15. package/lib/core/constants/search.js +6 -1
  16. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +39 -10
  17. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +13 -12
  18. package/lib/core/hooks/index.d.ts +1 -0
  19. package/lib/core/hooks/index.js +1 -0
  20. package/lib/core/hooks/use-element-size.d.ts +7 -0
  21. package/lib/core/hooks/use-element-size.js +51 -0
  22. package/lib/core/types/hooks.d.ts +6 -4
  23. package/lib/core/types/l10n.d.ts +1 -1
  24. package/lib/core/types/search.d.ts +9 -0
  25. package/lib/icons/AiStarsIcon/AiStarsIcon.d.ts +9 -6
  26. package/lib/icons/AiStarsIcon/AiStarsIcon.js +38 -4
  27. package/lib/icons/ChatIcon/ChatIcon.d.ts +9 -0
  28. package/lib/icons/ChatIcon/ChatIcon.js +24 -0
  29. package/lib/icons/CheckboxFilledIcon/CheckboxFilledIcon.d.ts +9 -0
  30. package/lib/icons/CheckboxFilledIcon/CheckboxFilledIcon.js +22 -0
  31. package/lib/icons/DataRefineryIcon/DataRefineryIcon.d.ts +9 -0
  32. package/lib/icons/DataRefineryIcon/DataRefineryIcon.js +24 -0
  33. package/lib/icons/DraggableIcon/DraggableIcon.d.ts +9 -0
  34. package/lib/icons/DraggableIcon/DraggableIcon.js +27 -0
  35. package/lib/icons/FlowIcon/FlowIcon.d.ts +9 -0
  36. package/lib/icons/FlowIcon/FlowIcon.js +22 -0
  37. package/lib/icons/PlaylistIcon/PlaylistIcon.d.ts +9 -0
  38. package/lib/icons/PlaylistIcon/PlaylistIcon.js +24 -0
  39. package/lib/icons/SendIcon/SendIcon.d.ts +5 -0
  40. package/lib/icons/SendIcon/SendIcon.js +22 -0
  41. package/lib/icons/SettingsCogIcon/SettingsCogIcon.d.ts +9 -0
  42. package/lib/icons/SettingsCogIcon/SettingsCogIcon.js +25 -0
  43. package/lib/icons/TaskViewIcon/TaskViewIcon.d.ts +9 -0
  44. package/lib/icons/TaskViewIcon/TaskViewIcon.js +24 -0
  45. package/lib/icons/WarningAltFilled/WarningAltFilled.d.ts +9 -0
  46. package/lib/icons/WarningAltFilled/WarningAltFilled.js +23 -0
  47. package/lib/icons/WarningAltFilledIcon/WarningAltFilledIcon.d.ts +9 -0
  48. package/lib/icons/WarningAltFilledIcon/WarningAltFilledIcon.js +23 -0
  49. package/lib/icons/WorkflowAutomationIcon/WorkflowAutomationIcon.d.ts +9 -0
  50. package/lib/icons/WorkflowAutomationIcon/WorkflowAutomationIcon.js +24 -0
  51. package/lib/index.d.ts +11 -0
  52. package/lib/index.js +11 -0
  53. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +2 -28
  54. package/package.json +5 -4
  55. package/src/components/Catalog/Catalog.tsx +3 -37
  56. package/src/components/Catalog/CatalogVirtualizedGroups.tsx +152 -0
  57. package/src/components/Search/SearchAiConversationInput.tsx +133 -0
  58. package/src/components/Search/SearchAiDialog.tsx +238 -0
  59. package/src/components/Search/SearchAiMessage.tsx +209 -0
  60. package/src/components/Search/SearchAiResponse.tsx +59 -3
  61. package/src/components/Search/SearchDialog.tsx +148 -56
  62. package/src/components/Search/variables.ts +112 -6
  63. package/src/core/constants/search.ts +4 -0
  64. package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +51 -11
  65. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +15 -12
  66. package/src/core/hooks/index.ts +1 -0
  67. package/src/core/hooks/use-element-size.ts +61 -0
  68. package/src/core/types/hooks.ts +15 -3
  69. package/src/core/types/l10n.ts +7 -0
  70. package/src/core/types/search.ts +10 -0
  71. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +49 -14
  72. package/src/icons/ChatIcon/ChatIcon.tsx +35 -0
  73. package/src/icons/CheckboxFilledIcon/CheckboxFilledIcon.tsx +23 -0
  74. package/src/icons/DataRefineryIcon/DataRefineryIcon.tsx +34 -0
  75. package/src/icons/DraggableIcon/DraggableIcon.tsx +28 -0
  76. package/src/icons/FlowIcon/FlowIcon.tsx +26 -0
  77. package/src/icons/PlaylistIcon/PlaylistIcon.tsx +25 -0
  78. package/src/icons/SendIcon/SendIcon.tsx +33 -0
  79. package/src/icons/SettingsCogIcon/SettingsCogIcon.tsx +32 -0
  80. package/src/icons/TaskViewIcon/TaskViewIcon.tsx +34 -0
  81. package/src/icons/WarningAltFilled/WarningAltFilled.tsx +24 -0
  82. package/src/icons/WarningAltFilledIcon/WarningAltFilledIcon.tsx +24 -0
  83. package/src/icons/WorkflowAutomationIcon/WorkflowAutomationIcon.tsx +34 -0
  84. package/src/index.ts +11 -0
  85. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +2 -5
@@ -10,13 +10,12 @@ const styled_components_1 = __importDefault(require("styled-components"));
10
10
  const utils_1 = require("../../core/utils");
11
11
  const hooks_1 = require("../../core/hooks");
12
12
  const H2_1 = require("../../components/Typography/H2");
13
- const CatalogCard_1 = require("../../components/Catalog/CatalogCard");
14
13
  const FilterContent_1 = require("../../components/Filter/FilterContent");
15
14
  const FilterPopover_1 = require("../../components/Filter/FilterPopover");
16
15
  const CatalogHighlight_1 = require("../../components/Catalog/CatalogHighlight");
17
16
  const CatalogActions_1 = require("../../components/Catalog/CatalogActions");
18
17
  const Sidebar_1 = require("../../components/Sidebar/Sidebar");
19
- const CounterTag_1 = require("../../components/Tags/CounterTag");
18
+ const CatalogVirtualizedGroups_1 = require("../../components/Catalog/CatalogVirtualizedGroups");
20
19
  function Catalog(props) {
21
20
  const { catalogConfig } = props.pageProps;
22
21
  const { useTranslate, useCatalog } = (0, hooks_1.useThemeHooks)();
@@ -39,25 +38,8 @@ function Catalog(props) {
39
38
  ' ',
40
39
  translate(catalogConfig.descriptionTranslationKey, catalogConfig.description),
41
40
  ' ')) : null),
42
- groups.map((group) => (react_1.default.createElement(react_1.default.Fragment, { key: group.title },
43
- react_1.default.createElement(CatalogSeparator, { "data-testid": "catalog-separator" },
44
- react_1.default.createElement(CatalogSeparatorLabel, null, group.title),
45
- react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, group.items.length)),
46
- react_1.default.createElement(CatalogCards, null, group.items.map((item) => (react_1.default.createElement(CatalogCard_1.CatalogCard, { item: item, key: item.link })))))))))));
41
+ react_1.default.createElement(CatalogVirtualizedGroups_1.CatalogVirtualizedGroups, { groups: groups })))));
47
42
  }
48
- const CatalogSeparator = styled_components_1.default.div `
49
- display: flex;
50
- align-items: center;
51
- color: var(--catalog-separator-color);
52
- font-size: var(--catalog-separator-font-size);
53
- font-weight: var(--catalog-separator-font-weight);
54
- border-top: 1px solid var(--catalog-separator-border-color);
55
- margin: var(--catalog-separator-margin);
56
- padding: var(--catalog-separator-padding);
57
- `;
58
- const CatalogSeparatorLabel = styled_components_1.default.div `
59
- margin: var(--catalog-separator-label-margin);
60
- `;
61
43
  exports.CatalogPageContent = styled_components_1.default.main `
62
44
  flex: 1;
63
45
  width: 90%;
@@ -68,12 +50,6 @@ exports.CatalogPageContent = styled_components_1.default.main `
68
50
  padding: var(--catalog-page-padding);
69
51
  }
70
52
  `;
71
- const CatalogCards = styled_components_1.default.div `
72
- display: grid;
73
- grid-template-columns: repeat(auto-fill, minmax(var(--api-catalog-card-min-width), 1fr));
74
- gap: 32px;
75
- margin: var(--catalog-cards-group-margin);
76
- `;
77
53
  exports.CatalogTitle = (0, styled_components_1.default)(H2_1.H2) `
78
54
  color: var(--catalog-title-text-color);
79
55
  font-weight: var(--catalog-title-font-weight);
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { CatalogItem } from '../../core/types';
3
+ type Group = {
4
+ title: string;
5
+ items: CatalogItem[];
6
+ };
7
+ type CatalogVirtualizedGroupsProps = {
8
+ groups: Group[];
9
+ };
10
+ export declare function CatalogVirtualizedGroups({ groups }: CatalogVirtualizedGroupsProps): React.JSX.Element;
11
+ export {};
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.CatalogVirtualizedGroups = CatalogVirtualizedGroups;
30
+ const react_1 = __importStar(require("react"));
31
+ const react_virtual_1 = require("@tanstack/react-virtual");
32
+ const styled_components_1 = __importDefault(require("styled-components"));
33
+ const use_element_size_1 = require("../../core/hooks/use-element-size");
34
+ const CatalogCard_1 = require("../../components/Catalog/CatalogCard");
35
+ const CounterTag_1 = require("../../components/Tags/CounterTag");
36
+ const GAP_SIZE = 32;
37
+ const CARD_MIN_WIDTH_VAR = '--api-catalog-card-min-width';
38
+ function CatalogVirtualizedGroups({ groups }) {
39
+ var _a, _b;
40
+ const parentRef = (0, react_1.useRef)(null);
41
+ const [isClient, setIsClient] = (0, react_1.useState)(typeof window !== 'undefined');
42
+ const { width } = (0, use_element_size_1.useElementSize)(parentRef);
43
+ (0, react_1.useEffect)(() => {
44
+ setIsClient(true);
45
+ }, []);
46
+ const columnCount = (0, react_1.useMemo)(() => {
47
+ if (!isClient)
48
+ return 1;
49
+ const cardMinWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue(CARD_MIN_WIDTH_VAR), 10);
50
+ return Math.max(1, Math.floor((width + GAP_SIZE) / (cardMinWidth + GAP_SIZE)));
51
+ }, [width, isClient]);
52
+ const flatRows = (0, react_1.useMemo)(() => {
53
+ const rows = [];
54
+ groups.forEach((group) => {
55
+ rows.push({
56
+ type: 'header',
57
+ groupTitle: group.title,
58
+ groupCount: group.items.length,
59
+ key: `header-${group.title}`,
60
+ });
61
+ const numRows = Math.ceil(group.items.length / columnCount);
62
+ for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
63
+ const startIndex = rowIndex * columnCount;
64
+ const rowItems = group.items.slice(startIndex, startIndex + columnCount);
65
+ rows.push({
66
+ type: 'cardRow',
67
+ groupTitle: group.title,
68
+ items: rowItems,
69
+ key: `${group.title}-row-${rowIndex}`,
70
+ });
71
+ }
72
+ });
73
+ return rows;
74
+ }, [groups, columnCount]);
75
+ const estimateSize = (0, react_1.useCallback)((index) => {
76
+ const row = flatRows[index];
77
+ return row.type === 'header' ? 50 : 250;
78
+ }, [flatRows]);
79
+ const virtualizer = (0, react_virtual_1.useWindowVirtualizer)({
80
+ count: flatRows.length,
81
+ estimateSize,
82
+ overscan: 5,
83
+ initialOffset: (_b = (_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) !== null && _b !== void 0 ? _b : 0,
84
+ });
85
+ return (react_1.default.createElement("div", { ref: parentRef },
86
+ react_1.default.createElement("div", { style: {
87
+ position: 'relative',
88
+ height: isClient ? `${virtualizer.getTotalSize()}px` : 'auto',
89
+ } }, isClient &&
90
+ virtualizer.getVirtualItems().map((virtualRow) => {
91
+ const rowData = flatRows[virtualRow.index];
92
+ if (rowData.type === 'header') {
93
+ return (react_1.default.createElement(HeaderRow, { key: rowData.key, ref: virtualizer.measureElement, "data-index": virtualRow.index, style: { transform: `translateY(${virtualRow.start}px)` } },
94
+ react_1.default.createElement(CatalogSeparatorLabel, null, rowData.groupTitle),
95
+ react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, rowData.groupCount)));
96
+ }
97
+ return (react_1.default.createElement(VirtualRow, { key: rowData.key, ref: virtualizer.measureElement, "data-index": virtualRow.index, style: { transform: `translateY(${virtualRow.start}px)` } }, rowData.items.map((item) => (react_1.default.createElement(CatalogCard_1.CatalogCard, { key: item.link, item: item })))));
98
+ }))));
99
+ }
100
+ const VirtualRow = styled_components_1.default.div `
101
+ position: absolute;
102
+ left: 0;
103
+ width: 100%;
104
+ display: grid;
105
+ grid-template-columns: repeat(auto-fill, minmax(var(${CARD_MIN_WIDTH_VAR}), 1fr));
106
+ gap: ${GAP_SIZE}px;
107
+ padding-bottom: 32px;
108
+ `;
109
+ const HeaderRow = styled_components_1.default.div `
110
+ position: absolute;
111
+ left: 0;
112
+ width: 100%;
113
+ display: flex;
114
+ align-items: center;
115
+ padding: var(--catalog-separator-padding);
116
+ border-top: 1px solid var(--catalog-separator-border-color);
117
+ padding-bottom: calc(4px * 4);
118
+ color: var(--catalog-separator-color);
119
+ font-size: var(--catalog-separator-font-size);
120
+ font-weight: var(--catalog-separator-font-weight);
121
+ `;
122
+ const CatalogSeparatorLabel = styled_components_1.default.div `
123
+ margin: var(--catalog-separator-label-margin);
124
+ `;
125
+ //# sourceMappingURL=CatalogVirtualizedGroups.js.map
@@ -0,0 +1,8 @@
1
+ type SearchAiConversationInputProps = {
2
+ onMessageSent: (message: string) => void;
3
+ isGeneratingResponse: boolean;
4
+ placeholder?: string;
5
+ className?: string;
6
+ };
7
+ export declare function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, className, placeholder, }: SearchAiConversationInputProps): JSX.Element;
8
+ export {};
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.SearchAiConversationInput = SearchAiConversationInput;
30
+ const react_1 = __importStar(require("react"));
31
+ const styled_components_1 = __importDefault(require("styled-components"));
32
+ const Button_1 = require("../../components/Button/Button");
33
+ const SendIcon_1 = require("../../icons/SendIcon/SendIcon");
34
+ const hooks_1 = require("../../core/hooks");
35
+ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, className, placeholder, }) {
36
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
37
+ const { translate } = useTranslate();
38
+ const inputRef = (0, react_1.useRef)(null);
39
+ const [query, setQuery] = (0, react_1.useState)('');
40
+ (0, react_1.useEffect)(() => {
41
+ if (!isGeneratingResponse && inputRef.current) {
42
+ requestAnimationFrame(() => {
43
+ var _a;
44
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
45
+ });
46
+ }
47
+ }, [isGeneratingResponse]);
48
+ const handleSendMessage = () => {
49
+ setQuery('');
50
+ onMessageSent(query);
51
+ };
52
+ const handleOnKeyUp = (e) => {
53
+ if (e.key === 'Enter' && !isGeneratingResponse) {
54
+ handleSendMessage();
55
+ }
56
+ };
57
+ const isDisabled = isGeneratingResponse || query.trim().length === 0;
58
+ return (react_1.default.createElement(SearchAiConversationInputWrapper, { "data-component-name": "Search/SearchAiConversationInput", className: className },
59
+ react_1.default.createElement(ConversationInput, { ref: inputRef, type: "text", placeholder: placeholder || translate('search.ai.followUpQuestion', 'Ask a follow up question?'), onChange: (e) => setQuery(e.target.value), onKeyUp: handleOnKeyUp, value: query, disabled: isGeneratingResponse }),
60
+ react_1.default.createElement(SendButton, { disabled: isDisabled, size: "small", icon: react_1.default.createElement(SendIcon_1.SendIcon, { color: isDisabled
61
+ ? '--button-content-color-disabled'
62
+ : 'var(--search-ai-conversation-input-send-button-icon-color)' }), onClick: handleSendMessage })));
63
+ }
64
+ const SearchAiConversationInputWrapper = styled_components_1.default.div `
65
+ width: 100%;
66
+ display: flex;
67
+ flex-direction: column;
68
+ justify-content: flex-end;
69
+ background-color: var(--search-ai-conversation-input-bg-color);
70
+ position: relative;
71
+ `;
72
+ const ConversationInput = styled_components_1.default.input `
73
+ width: 100%;
74
+ padding: var(--search-ai-conversation-input-padding);
75
+ border: var(--search-ai-conversation-input-border);
76
+ border-radius: var(--search-ai-conversation-input-border-radius);
77
+ background-color: var(--search-ai-conversation-input-bg-color);
78
+ position: relative;
79
+ font-size: var(--search-ai-conversation-input-font-size);
80
+
81
+ &::placeholder {
82
+ color: var(--search-ai-conversation-input-placeholder-color);
83
+ }
84
+
85
+ &:focus {
86
+ outline: none;
87
+ border-color: var(--search-ai-conversation-input-border-color-focus);
88
+ }
89
+
90
+ &:focus:disabled {
91
+ border-color: var(--search-ai-conversation-input-border-color-disabled);
92
+ }
93
+ `;
94
+ const SendButton = (0, styled_components_1.default)(Button_1.Button) `
95
+ position: absolute;
96
+ right: var(--search-ai-conversation-input-send-button-right);
97
+ top: 50%;
98
+ transform: translateY(-50%);
99
+ transition: background-color 0.2s ease;
100
+ background-color: var(--search-ai-conversation-input-send-button-bg-color);
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+
105
+ &:hover {
106
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
107
+ }
108
+
109
+ &:disabled {
110
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-disabled);
111
+ border: var(--search-ai-conversation-input-send-button-border-disabled);
112
+ }
113
+ `;
114
+ //# sourceMappingURL=SearchAiConversationInput.js.map
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { AiSearchError } from '../../core/constants';
3
+ import { AiSearchConversationItem } from '../../core/types';
4
+ export type SearchAiDialogProps = {
5
+ response: string | undefined;
6
+ isGeneratingResponse: boolean;
7
+ error: AiSearchError | null;
8
+ resources: {
9
+ url: string;
10
+ title: string;
11
+ }[];
12
+ initialMessage?: string;
13
+ className?: string;
14
+ conversation: AiSearchConversationItem[];
15
+ setConversation: React.Dispatch<React.SetStateAction<AiSearchConversationItem[]>>;
16
+ onMessageSent: (message: string, history?: AiSearchConversationItem[]) => void;
17
+ };
18
+ export declare function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error, resources, onMessageSent, className, conversation, setConversation, }: SearchAiDialogProps): JSX.Element;
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.SearchAiDialog = SearchAiDialog;
30
+ const react_1 = __importStar(require("react"));
31
+ const styled_components_1 = __importDefault(require("styled-components"));
32
+ const hooks_1 = require("../../core/hooks");
33
+ const Button_1 = require("../../components/Button/Button");
34
+ const SearchAiConversationInput_1 = require("../../components/Search/SearchAiConversationInput");
35
+ const constants_1 = require("../../core/constants");
36
+ const SearchAiMessage_1 = require("../../components/Search/SearchAiMessage");
37
+ const Admonition_1 = require("../../components/Admonition/Admonition");
38
+ const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
39
+ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error, resources, onMessageSent, className, conversation, setConversation, }) {
40
+ var _a;
41
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
42
+ const { search } = (0, hooks_1.useThemeConfig)();
43
+ const { translate } = useTranslate();
44
+ const conversationEndRef = react_1.default.useRef(null);
45
+ const suggestions = (_a = search === null || search === void 0 ? void 0 : search.ai) === null || _a === void 0 ? void 0 : _a.suggestions;
46
+ const placeholder = isGeneratingResponse
47
+ ? translate('search.ai.generatingResponse', 'Generating response...')
48
+ : conversation.length > 0
49
+ ? translate('search.ai.followUpQuestion', 'Ask a follow up question?')
50
+ : translate('search.ai.placeholder', 'Ask a question...');
51
+ const scrollToBottom = () => {
52
+ var _a;
53
+ (_a = conversationEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'end' });
54
+ };
55
+ const handleOnMessageSent = (message) => {
56
+ if (!message.trim()) {
57
+ return;
58
+ }
59
+ const mappedHistory = conversation.map(({ role, content }) => ({
60
+ role,
61
+ content,
62
+ }));
63
+ onMessageSent(message, mappedHistory);
64
+ setConversation((prev) => [...prev, { role: constants_1.AiSearchConversationRole.USER, content: message }]);
65
+ };
66
+ (0, react_1.useEffect)(() => {
67
+ if (!(initialMessage === null || initialMessage === void 0 ? void 0 : initialMessage.trim().length)) {
68
+ return;
69
+ }
70
+ setConversation((prev) => [
71
+ ...prev,
72
+ { role: constants_1.AiSearchConversationRole.USER, content: initialMessage },
73
+ ]);
74
+ }, [initialMessage, setConversation]);
75
+ (0, react_1.useEffect)(() => {
76
+ if (response === undefined || conversation.length === 0 || error) {
77
+ return;
78
+ }
79
+ setConversation((prev) => {
80
+ const lastMessage = prev[prev.length - 1];
81
+ const content = response || '';
82
+ if (lastMessage && lastMessage.role === constants_1.AiSearchConversationRole.ASSISTANT) {
83
+ return [
84
+ ...prev.slice(0, -1),
85
+ { role: constants_1.AiSearchConversationRole.ASSISTANT, content, resources },
86
+ ];
87
+ }
88
+ return [...prev, { role: constants_1.AiSearchConversationRole.ASSISTANT, content }];
89
+ });
90
+ }, [response, conversation.length, error, resources, setConversation]);
91
+ (0, react_1.useEffect)(() => {
92
+ if (error) {
93
+ setConversation((prev) => prev.slice(0, -1));
94
+ }
95
+ }, [error, setConversation]);
96
+ (0, react_1.useEffect)(() => {
97
+ scrollToBottom();
98
+ }, [conversation, isGeneratingResponse]);
99
+ return (react_1.default.createElement(SearchAiDialogWrapper, { "data-component-name": "Search/SearchAiDialog", className: className },
100
+ !conversation.length && (react_1.default.createElement(WelcomeWrapper, null,
101
+ react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { color: "var(--search-ai-icon-color)", size: "32px", background: "var(--search-ai-icon-bg-color)", borderRadius: "var(--border-radius-lg)", margin: "0 var(--spacing-xs) 0 0" }),
102
+ translate('search.ai.welcomeText', 'Welcome to AI search! Feel free to ask me anything. How can I help you? '))),
103
+ react_1.default.createElement(ConversationWrapper, null,
104
+ conversation.map((item, index) => (react_1.default.createElement(SearchAiMessage_1.SearchAiMessage, { key: `search-ai-message-${index}`, role: item.role, content: item.content, isThinking: item.role === constants_1.AiSearchConversationRole.ASSISTANT &&
105
+ isGeneratingResponse &&
106
+ index === conversation.length - 1, resources: item.resources }))),
107
+ error && (react_1.default.createElement(Admonition_1.Admonition, { type: "danger", name: translate(constants_1.AI_SEARCH_ERROR_CONFIG[error].headerKey, constants_1.AI_SEARCH_ERROR_CONFIG[error].headerDefault) }, translate(constants_1.AI_SEARCH_ERROR_CONFIG[error].messageKey, constants_1.AI_SEARCH_ERROR_CONFIG[error].messageDefault))),
108
+ !conversation.length && !error && (react_1.default.createElement(SuggestionsWrapper, null, suggestions === null || suggestions === void 0 ? void 0 : suggestions.map((suggestion) => (react_1.default.createElement(Button_1.Button, { key: suggestion, variant: "outlined", onClick: () => handleOnMessageSent(suggestion) }, suggestion))))),
109
+ react_1.default.createElement("div", { ref: conversationEndRef })),
110
+ react_1.default.createElement(ConversationInputWrapper, null,
111
+ react_1.default.createElement(SearchAiConversationInput_1.SearchAiConversationInput, { onMessageSent: handleOnMessageSent, isGeneratingResponse: isGeneratingResponse, placeholder: placeholder }))));
112
+ }
113
+ const SearchAiDialogWrapper = styled_components_1.default.div `
114
+ display: flex;
115
+ flex-direction: column;
116
+ flex: 1 1 auto;
117
+ width: 100%;
118
+ min-width: 0;
119
+ min-height: 0;
120
+ position: relative;
121
+ background: var(--search-ai-dialog-bg-color);
122
+ overflow: hidden;
123
+ `;
124
+ const ConversationWrapper = styled_components_1.default.div `
125
+ flex: 1 1 auto;
126
+ overflow-y: auto;
127
+ overflow-x: hidden;
128
+ padding: var(--search-ai-dialog-body-padding);
129
+ padding-bottom: 0;
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: stretch;
133
+ gap: var(--search-ai-dialog-body-gap);
134
+ min-height: 0;
135
+
136
+ > :first-child {
137
+ margin-top: auto;
138
+ }
139
+ > :last-child {
140
+ margin-bottom: 0;
141
+ gap: 0;
142
+ }
143
+ `;
144
+ const SuggestionsWrapper = styled_components_1.default.div `
145
+ display: flex;
146
+ flex-direction: row;
147
+ flex-wrap: wrap;
148
+ gap: var(--search-ai-suggestions-gap);
149
+ align-items: center;
150
+ justify-content: center;
151
+ `;
152
+ const ConversationInputWrapper = styled_components_1.default.div `
153
+ padding: var(--search-ai-dialog-input-padding);
154
+ border-top: var(--search-ai-dialog-input-border);
155
+ background: var(--search-ai-dialog-input-bg-color);
156
+ `;
157
+ const WelcomeWrapper = styled_components_1.default.div `
158
+ display: flex;
159
+ flex-direction: row;
160
+ align-items: center;
161
+ width: auto;
162
+ margin: var(--search-ai-welcome-margin);
163
+ position: relative;
164
+ `;
165
+ //# sourceMappingURL=SearchAiDialog.js.map
@@ -0,0 +1,12 @@
1
+ import { AiSearchConversationRole } from '../../core/constants';
2
+ export type SearchAiMessageProps = {
3
+ role: AiSearchConversationRole;
4
+ content: string;
5
+ isThinking?: boolean;
6
+ resources?: {
7
+ url: string;
8
+ title: string;
9
+ }[];
10
+ className?: string;
11
+ };
12
+ export declare function SearchAiMessage({ role, content, isThinking, resources, className, }: SearchAiMessageProps): JSX.Element;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SearchAiMessage = SearchAiMessage;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const Link_1 = require("../../components/Link/Link");
10
+ const Tag_1 = require("../../components/Tag/Tag");
11
+ const constants_1 = require("../../core/constants");
12
+ const hooks_1 = require("../../core/hooks");
13
+ const Markdown_1 = require("../../components/Markdown/Markdown");
14
+ const DocumentIcon_1 = require("../../icons/DocumentIcon/DocumentIcon");
15
+ const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
16
+ function SearchAiMessage({ role, content, isThinking, resources, className, }) {
17
+ const { useMarkdownText, useTranslate } = (0, hooks_1.useThemeHooks)();
18
+ const markDownContent = useMarkdownText(content || '');
19
+ const { translate } = useTranslate();
20
+ return (react_1.default.createElement(SearchAiMessageWrapper, { "data-component-name": "Search/SearchAiMessage", role: role, className: className },
21
+ role === constants_1.AiSearchConversationRole.ASSISTANT && (react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { size: "32px", background: "var(--search-ai-icon-bg-color)", borderRadius: "var(--border-radius-lg)", color: "var(--search-ai-icon-color)", margin: "0 var(--spacing-xs) 0 0" })),
22
+ react_1.default.createElement(MessageWrapper, { role: role },
23
+ role === constants_1.AiSearchConversationRole.ASSISTANT ? (react_1.default.createElement(react_1.default.Fragment, null,
24
+ react_1.default.createElement(ResponseText, { as: "div", children: markDownContent }),
25
+ !isThinking && resources && resources.length > 0 && (react_1.default.createElement(ResourcesWrapper, null,
26
+ react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
27
+ resources.length,
28
+ " ",
29
+ translate('search.ai.resourcesFound', 'resources found')),
30
+ react_1.default.createElement(ResourceTagsWrapper, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
31
+ react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title))))))))) : (content),
32
+ isThinking && content.length === 0 && (react_1.default.createElement(ThinkingDotsWrapper, null,
33
+ react_1.default.createElement(ThinkingDot, null),
34
+ react_1.default.createElement(ThinkingDot, null),
35
+ react_1.default.createElement(ThinkingDot, null))))));
36
+ }
37
+ const SearchAiMessageWrapper = styled_components_1.default.div `
38
+ display: flex;
39
+ flex-direction: row;
40
+ align-items: flex-start;
41
+ width: 100%;
42
+ justify-content: ${({ role }) => role === constants_1.AiSearchConversationRole.USER ? 'flex-end' : 'flex-start'};
43
+ `;
44
+ const ResponseText = (0, styled_components_1.default)(Markdown_1.Markdown) `
45
+ color: var(--search-ai-text-color);
46
+ font-size: var(--search-ai-text-font-size);
47
+ line-height: var(--search-ai-text-line-height);
48
+ font-family: inherit;
49
+ white-space: break-spaces;
50
+
51
+ article {
52
+ > *:first-child {
53
+ margin-top: 0;
54
+ }
55
+ > *:last-child {
56
+ margin-bottom: 0;
57
+ }
58
+ }
59
+ `;
60
+ const MessageWrapper = styled_components_1.default.div `
61
+ padding: ${({ role }) => role === constants_1.AiSearchConversationRole.USER ? 'var(--spacing-sm)' : 'var(--spacing-xs)'}
62
+ var(--spacing-sm);
63
+ border-radius: var(--border-radius-lg);
64
+ max-width: 80%;
65
+ word-wrap: break-word;
66
+ white-space: pre-wrap;
67
+ background-color: ${({ role }) => role === constants_1.AiSearchConversationRole.USER
68
+ ? 'var(--search-ai-user-bg-color)'
69
+ : 'var(--search-ai-assistant-bg-color)'};
70
+ border: ${({ role }) => role === constants_1.AiSearchConversationRole.USER ? 'none' : 'var(--search-ai-assistant-border)'};
71
+ color: ${({ role }) => role === constants_1.AiSearchConversationRole.USER
72
+ ? 'var(--search-ai-user-text-color)'
73
+ : 'var(--search-ai-assistant-text-color)'};
74
+ `;
75
+ const ResourcesWrapper = styled_components_1.default.div `
76
+ gap: var(--search-ai-resources-gap);
77
+ display: flex;
78
+ flex-direction: column;
79
+ margin: var(--spacing-xs) 0;
80
+ `;
81
+ const ResourcesTitle = styled_components_1.default.div `
82
+ font-weight: var(--search-ai-resources-title-font-weight);
83
+ font-size: var(--search-ai-resources-title-font-size);
84
+ line-height: var(--search-ai-resources-title-line-height);
85
+ `;
86
+ const ResourceTagsWrapper = styled_components_1.default.div `
87
+ display: flex;
88
+ flex-wrap: wrap;
89
+ gap: var(--search-ai-resource-tags-gap);
90
+ `;
91
+ const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag) `
92
+ .tag-default {
93
+ --tag-color: var(--search-ai-resource-tag-text-color);
94
+ max-width: 100%;
95
+ overflow: hidden;
96
+ white-space: nowrap;
97
+ display: inline-block;
98
+ }
99
+ svg {
100
+ min-width: var(--search-ai-resource-tag-icon-size);
101
+ min-height: var(--search-ai-resource-tag-icon-size);
102
+ flex-shrink: 0;
103
+ }
104
+ > div {
105
+ overflow: hidden;
106
+ text-overflow: ellipsis;
107
+ white-space: nowrap;
108
+ max-width: 100%;
109
+ }
110
+ `;
111
+ const ThinkingDotsWrapper = styled_components_1.default.div `
112
+ display: flex;
113
+ gap: var(--search-ai-thinking-dots-gap);
114
+ padding: var(--search-ai-thinking-dots-padding);
115
+ `;
116
+ const ThinkingDot = styled_components_1.default.div `
117
+ width: var(--search-ai-thinking-dot-size);
118
+ height: var(--search-ai-thinking-dot-size);
119
+ border-radius: 50%;
120
+ background: var(--search-ai-thinking-dot-color);
121
+ animation: bounce 1.4s infinite ease-in-out;
122
+
123
+ &:nth-child(1) {
124
+ animation-delay: -0.32s;
125
+ }
126
+ &:nth-child(2) {
127
+ animation-delay: -0.16s;
128
+ }
129
+ &:nth-child(3) {
130
+ animation-delay: 0s;
131
+ }
132
+
133
+ @keyframes bounce {
134
+ 0%,
135
+ 80%,
136
+ 100% {
137
+ opacity: 0.2;
138
+ transform: scale(0.8);
139
+ }
140
+ 40% {
141
+ opacity: 1;
142
+ transform: scale(1);
143
+ }
144
+ }
145
+ `;
146
+ //# sourceMappingURL=SearchAiMessage.js.map
@@ -8,5 +8,6 @@ export type SearchAiResponseProps = {
8
8
  url: string;
9
9
  title: string;
10
10
  }[];
11
+ onSuggestionClick: (suggestion: string) => void;
11
12
  };
12
13
  export declare function SearchAiResponse(props: SearchAiResponseProps): JSX.Element;