@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
@@ -13,16 +13,21 @@ const Tag_1 = require("../../components/Tag/Tag");
13
13
  const Link_1 = require("../../components/Link/Link");
14
14
  const hooks_1 = require("../../core/hooks");
15
15
  const Markdown_1 = require("../../components/Markdown/Markdown");
16
+ const Typography_1 = require("../../components/Typography/Typography");
16
17
  const Admonition_1 = require("../../components/Admonition/Admonition");
17
18
  const ErrorFilledIcon_1 = require("../../icons/ErrorFilledIcon/ErrorFilledIcon");
18
19
  const constants_1 = require("../../core/constants");
20
+ const ChatIcon_1 = require("../../icons/ChatIcon/ChatIcon");
19
21
  function SearchAiResponse(props) {
22
+ var _a;
20
23
  const { useMarkdownText } = (0, hooks_1.useThemeHooks)();
21
- const { question, response, isGeneratingResponse, resources, error } = props;
24
+ const { question, response, isGeneratingResponse, resources, error, onSuggestionClick } = props;
25
+ const { search } = (0, hooks_1.useThemeConfig)();
22
26
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
23
27
  const { translate } = useTranslate();
24
28
  const markdownResponse = useMarkdownText(response || '');
25
29
  let responseContainer = null;
30
+ const suggestions = (_a = search === null || search === void 0 ? void 0 : search.ai) === null || _a === void 0 ? void 0 : _a.suggestions;
26
31
  const hasPendingOrReceivedResponse = response || isGeneratingResponse || error;
27
32
  if (hasPendingOrReceivedResponse) {
28
33
  let icon;
@@ -53,7 +58,15 @@ function SearchAiResponse(props) {
53
58
  react_1.default.createElement(ResourceTags, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
54
59
  react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title))))))) : null)));
55
60
  }
56
- return (react_1.default.createElement(ResponseWrapper, { "data-component-name": "Search/SearchAiResponse" }, responseContainer));
61
+ return (react_1.default.createElement(ResponseWrapper, { "data-component-name": "Search/SearchAiResponse" },
62
+ !question && (react_1.default.createElement(react_1.default.Fragment, null, (suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) && (react_1.default.createElement(SuggestionsWrapper, null,
63
+ react_1.default.createElement(SuggestionsTitle, null, translate('search.ai.suggestionsTitle', 'Suggestions')),
64
+ suggestions.map((suggestion, idx) => (react_1.default.createElement(SuggestionItem, { key: idx.toString(), onClick: () => {
65
+ onSuggestionClick(suggestion);
66
+ } },
67
+ react_1.default.createElement(ChatIcon_1.ChatIcon, { color: "--color-blueberry-6" }),
68
+ react_1.default.createElement(Typography_1.Typography, { color: "--search-ai-suggestions-text-color" }, suggestion)))))))),
69
+ responseContainer));
57
70
  }
58
71
  const ResponseWrapper = styled_components_1.default.div `
59
72
  display: flex;
@@ -128,10 +141,33 @@ const ResourceTags = styled_components_1.default.div `
128
141
  `;
129
142
  const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag).attrs({
130
143
  className: 'tag-resource',
131
- 'data-component-name': 'Tags/SearchAiResourceTag',
132
144
  }) `
133
145
  .tag-default {
134
146
  --tag-color: --search-ai-resource-tag-text-color;
135
147
  }
136
148
  `;
149
+ const SuggestionsWrapper = styled_components_1.default.div `
150
+ display: flex;
151
+ flex-direction: column;
152
+ justify-content: flex-start;
153
+
154
+ gap: var(--spacing-sm);
155
+ margin-left: var(--spacing-xs);
156
+ `;
157
+ const SuggestionsTitle = (0, styled_components_1.default)(Typography_1.Typography) `
158
+ font-size: var(--search-ai-suggestions-title-font-size);
159
+ line-height: var(--search-ai-suggestions-title-line-height);
160
+ font-weight: var(--search-ai-suggestions-title-font-weight);
161
+
162
+ color: var(--search-ai-suggestions-title-text-color);
163
+ `;
164
+ const SuggestionItem = styled_components_1.default.div `
165
+ cursor: pointer;
166
+
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: flex-start;
170
+
171
+ gap: var(--spacing-xs);
172
+ `;
137
173
  //# sourceMappingURL=SearchAiResponse.js.map
@@ -31,7 +31,6 @@ const react_1 = __importStar(require("react"));
31
31
  const styled_components_1 = __importDefault(require("styled-components"));
32
32
  const SearchInput_1 = require("../../components/Search/SearchInput");
33
33
  const SearchShortcut_1 = require("../../components/Search/SearchShortcut");
34
- const SearchAiResponse_1 = require("../../components/Search/SearchAiResponse");
35
34
  const Button_1 = require("../../components/Button/Button");
36
35
  const utils_1 = require("../../core/utils");
37
36
  const SearchItem_1 = require("../../components/Search/SearchItem");
@@ -42,9 +41,13 @@ const Tag_1 = require("../../components/Tag/Tag");
42
41
  const CloseIcon_1 = require("../../icons/CloseIcon/CloseIcon");
43
42
  const SearchFilter_1 = require("../../components/Search/SearchFilter");
44
43
  const SearchGroups_1 = require("../../components/Search/SearchGroups");
44
+ const Typography_1 = require("../../components/Typography/Typography");
45
45
  const SpinnerLoader_1 = require("../../components/Loaders/SpinnerLoader");
46
+ const SearchAiDialog_1 = require("../../components/Search/SearchAiDialog");
46
47
  const SettingsIcon_1 = require("../../icons/SettingsIcon/SettingsIcon");
47
48
  const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
49
+ const ChevronLeftIcon_1 = require("../../icons/ChevronLeftIcon/ChevronLeftIcon");
50
+ const EditIcon_1 = require("../../icons/EditIcon/EditIcon");
48
51
  function SearchDialog({ onClose, className }) {
49
52
  const { useTranslate, useCurrentProduct, useSearch, useProducts, useAiSearch, useOtelTelemetry } = (0, hooks_1.useThemeHooks)();
50
53
  const otelTelemetry = useOtelTelemetry();
@@ -59,6 +62,11 @@ function SearchDialog({ onClose, className }) {
59
62
  const modalRef = (0, react_1.useRef)(null);
60
63
  const { translate } = useTranslate();
61
64
  (0, hooks_1.useDialogHotKeys)(modalRef, onClose);
65
+ (0, react_1.useEffect)(() => {
66
+ if (mode === 'ai-dialog' && aiSearch.isGeneratingResponse) {
67
+ setQuery('');
68
+ }
69
+ }, [mode, aiSearch.isGeneratingResponse, setQuery]);
62
70
  const handleOverlayClick = (event) => {
63
71
  var _a;
64
72
  const target = event.target;
@@ -106,37 +114,49 @@ function SearchDialog({ onClose, className }) {
106
114
  const showResults = !!((filter && filter.length) || query);
107
115
  const showSearchFilterButton = advancedSearch && mode === 'search';
108
116
  const showAiSearchButton = askAi && mode === 'search';
117
+ const showAiSearchItem = showAiSearchButton && query;
109
118
  const showHeaderButtons = showSearchFilterButton || showAiSearchButton;
110
119
  return (react_1.default.createElement(SearchOverlay, { "data-component-name": "Search/SearchDialog", ref: modalRef, onClick: handleOverlayClick, className: (0, utils_1.concatClassNames)('overlay', className) },
111
120
  react_1.default.createElement(SearchDialogWrapper, { className: "scroll-lock", role: "dialog" },
112
121
  react_1.default.createElement(SearchDialogHeader, null,
113
- product && (react_1.default.createElement(react_1.default.Fragment, null,
114
- react_1.default.createElement(SearchProductTag, { color: "product" },
115
- product.name,
116
- react_1.default.createElement(CloseIcon_1.CloseIcon, { onClick: () => setProduct(undefined), color: "--icon-color-additional" })))),
117
- react_1.default.createElement(SearchInput_1.SearchInput, { value: query, onChange: setQuery, placeholder: mode === 'search'
118
- ? translate('search.label', 'Search docs...')
119
- : translate('search.ai.label', 'Ask AI assistant'), isLoading: isSearchLoading, showReturnButton: mode === 'ai-dialog', onReturn: () => {
120
- setMode('search');
121
- aiSearch.clearAiSearchState();
122
- }, onSubmit: mode === 'ai-dialog'
123
- ? () => {
124
- setQuery('');
125
- aiSearch.askQuestion(query);
126
- }
127
- : undefined, "data-translation-key": mode === 'search' ? 'search.label' : 'search.ai.label' }),
128
- showHeaderButtons && (react_1.default.createElement(SearchHeaderButtons, null,
129
- showAiSearchButton ? (react_1.default.createElement(SearchAiButton, { icon: react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, null), onClick: () => {
130
- setMode('ai-dialog');
131
- if (query.trim()) {
132
- setQuery('');
122
+ product && (react_1.default.createElement(SearchProductTag, { color: "product" },
123
+ product.name,
124
+ react_1.default.createElement(CloseIcon_1.CloseIcon, { onClick: () => setProduct(undefined), color: "--icon-color-additional" }))),
125
+ mode === 'search' ? (react_1.default.createElement(react_1.default.Fragment, null,
126
+ react_1.default.createElement(SearchInput_1.SearchInput, { value: query, onChange: setQuery, placeholder: translate('search.label', 'Search docs...'), isLoading: isSearchLoading, onSubmit: showAiSearchButton
127
+ ? () => {
128
+ setMode('ai-dialog');
133
129
  aiSearch.askQuestion(query);
134
130
  }
135
- } }, translate('search.ai.button', 'Search with AI'))) : null,
136
- showSearchFilterButton && (react_1.default.createElement(SearchFilterToggleButton, { icon: react_1.default.createElement(SettingsIcon_1.SettingsIcon, null), onClick: onFilterToggle }))))),
131
+ : undefined, "data-translation-key": "search.label" }),
132
+ showHeaderButtons && (react_1.default.createElement(SearchHeaderButtons, null,
133
+ showAiSearchButton ? (react_1.default.createElement(SearchAiButton, { icon: react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { gradient: true }), onClick: () => {
134
+ setMode('ai-dialog');
135
+ if (query.trim()) {
136
+ aiSearch.askQuestion(query);
137
+ }
138
+ } }, translate('search.ai.button', 'Search with AI'))) : null,
139
+ showSearchFilterButton && (react_1.default.createElement(SearchFilterToggleButton, { icon: react_1.default.createElement(SettingsIcon_1.SettingsIcon, null), onClick: onFilterToggle })))))) : (react_1.default.createElement(AiDialogHeaderWrapper, null,
140
+ react_1.default.createElement(Button_1.Button, { variant: "secondary", onClick: () => {
141
+ setMode('search');
142
+ aiSearch.clearConversation();
143
+ }, icon: react_1.default.createElement(ChevronLeftIcon_1.ChevronLeftIcon, null) }, translate('search.ai.backToSearch', 'Back to search')),
144
+ react_1.default.createElement(Button_1.Button, { variant: "secondary", disabled: !aiSearch.conversation.length, onClick: () => aiSearch.clearConversation(), icon: react_1.default.createElement(EditIcon_1.EditIcon, null) }, translate('search.ai.newConversation', 'New conversation'))))),
137
145
  react_1.default.createElement(SearchDialogBody, null, mode === 'search' ? (react_1.default.createElement(react_1.default.Fragment, null,
138
146
  react_1.default.createElement(SearchDialogBodyMainView, null,
139
147
  react_1.default.createElement(SearchGroups_1.SearchGroups, { facets: facets, searchFilter: filter, onFilterChange: onFilterChange, onQuickFilterReset: onQuickFilterReset, groupField: groupField }),
148
+ showAiSearchItem && (react_1.default.createElement(SearchWithAI, { onClick: () => {
149
+ setMode('ai-dialog');
150
+ if (query.trim()) {
151
+ aiSearch.askQuestion(query);
152
+ }
153
+ }, tabIndex: 0, role: "option", "aria-selected": "true" },
154
+ react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { color: "var(--search-ai-icon-color)", size: "36px", background: "var(--search-ai-icon-bg-color)", margin: "0 var(--spacing-md) 0 0", borderRadius: "var(--border-radius-lg)" }),
155
+ react_1.default.createElement(Typography_1.Typography, { fontWeight: "var(--font-weight-semibold)" }, query),
156
+ react_1.default.createElement(Typography_1.Typography, null,
157
+ "- ",
158
+ translate('search.ai.label', 'Ask AI assistant')),
159
+ react_1.default.createElement(ReturnKey, null, "\u23CE"))),
140
160
  showResults ? (items && Object.keys(items).some((key) => { var _a; return (_a = items[key]) === null || _a === void 0 ? void 0 : _a.length; }) ? (Object.keys(items).map((key) => {
141
161
  var _a, _b, _c;
142
162
  return ((_a = items[key]) === null || _a === void 0 ? void 0 : _a.length) ? (react_1.default.createElement(react_1.Fragment, { key: key },
@@ -150,7 +170,7 @@ function SearchDialog({ onClose, className }) {
150
170
  react_1.default.createElement(SearchRecent_1.SearchRecent, { onSelect: setQuery }),
151
171
  react_1.default.createElement(SearchSuggestedPages_1.SearchSuggestedPages, null)))),
152
172
  advancedSearch && mode === 'search' && isFilterOpen && (react_1.default.createElement(SearchDialogBodyFilterView, null,
153
- react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiResponse_1.SearchAiResponse, { question: aiSearch.question, isGeneratingResponse: aiSearch.isGeneratingResponse, response: aiSearch.response, resources: aiSearch.resources, error: aiSearch.error }))),
173
+ react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiDialog_1.SearchAiDialog, { initialMessage: query, response: aiSearch.response, isGeneratingResponse: aiSearch.isGeneratingResponse, error: aiSearch.error, resources: aiSearch.resources, conversation: aiSearch.conversation, setConversation: aiSearch.setConversation, onMessageSent: aiSearch.askQuestion }))),
154
174
  react_1.default.createElement(SearchDialogFooter, null, mode === 'ai-dialog' ? (react_1.default.createElement(AiDisclaimer, null, translate('search.ai.disclaimer', 'AI search might provide incomplete or incorrect results. Verify important information.'))) : (react_1.default.createElement(react_1.default.Fragment, null,
155
175
  react_1.default.createElement(SearchShortcuts, null,
156
176
  react_1.default.createElement(SearchShortcut_1.SearchShortcut, { "data-translation-key": "search.keys.navigate", combination: "Tab", text: translate('search.keys.navigate', 'to navigate') }),
@@ -215,10 +235,17 @@ const SearchDialogHeader = styled_components_1.default.header `
215
235
  background-color: var(--search-modal-header-bg-color);
216
236
  padding: var(--search-modal-header-padding);
217
237
  `;
238
+ const AiDialogHeaderWrapper = styled_components_1.default.div `
239
+ display: flex;
240
+ justify-content: space-between;
241
+ align-items: center;
242
+ width: 100%;
243
+ `;
218
244
  const SearchDialogBody = styled_components_1.default.div `
219
245
  display: flex;
220
246
  flex-direction: row;
221
- flex-grow: 1;
247
+ flex: 1;
248
+ min-height: 0;
222
249
  overflow: hidden;
223
250
 
224
251
  @media screen and (max-width: ${utils_1.breakpoints.small}) {
@@ -314,4 +341,35 @@ const AiDisclaimer = styled_components_1.default.div `
314
341
  color: var(--search-ai-disclaimer-text-color);
315
342
  margin: 0 auto;
316
343
  `;
344
+ const SearchWithAI = styled_components_1.default.div `
345
+ display: flex;
346
+ justify-content: flex-start;
347
+ align-items: center;
348
+ cursor: pointer;
349
+ gap: var(--spacing-unit);
350
+ padding: var(--spacing-md);
351
+
352
+ color: var(--search-item-text-color);
353
+ background-color: var(--search-item-bg-color);
354
+ text-decoration: none;
355
+ white-space: normal;
356
+ outline: none;
357
+ border: none;
358
+
359
+ transition: all 0.3s ease;
360
+
361
+ &:focus,
362
+ &:hover {
363
+ color: var(--search-item-text-color-hover);
364
+ background-color: var(--search-item-bg-color-hover);
365
+ }
366
+
367
+ & > :first-child {
368
+ margin-right: var(--spacing-xs);
369
+ }
370
+ `;
371
+ const ReturnKey = (0, styled_components_1.default)(Typography_1.Typography) `
372
+ color: var(--search-item-text-color);
373
+ margin-left: auto;
374
+ `;
317
375
  //# sourceMappingURL=SearchDialog.js.map
@@ -144,12 +144,12 @@ exports.search = (0, styled_components_1.css) `
144
144
  --search-trigger-icon-size: 16px;
145
145
  --search-trigger-line-height: var(--line-height-base);
146
146
 
147
- // @tokens End
148
-
149
147
  /**
150
- * @tokens Ai Search
148
+ * @tokens AI Search
151
149
  */
152
150
 
151
+ --search-ai-gradient: linear-gradient(to right, #715efe, #ff5cdc);
152
+
153
153
  --search-ai-spinner-icon-color: var(--icon-color-interactive);
154
154
  --search-ai-checkmark-icon-color: var(--icon-color-interactive);
155
155
  --search-ai-response-padding: var(--spacing-lg);
@@ -165,9 +165,15 @@ exports.search = (0, styled_components_1.css) `
165
165
  --search-ai-response-body-gap: var(--spacing-xl);
166
166
  --search-ai-response-body-padding: 0 40px;
167
167
 
168
- --search-ai-response-text-color: var(--text-color-secondary);
169
- --search-ai-response-text-font-size: var(--font-size-lg);
170
- --search-ai-response-text-line-height: var(--line-height-lg);
168
+ --search-ai-text-color: var(--text-color-secondary);
169
+ --search-ai-text-font-size: var(--font-size-lg);
170
+ --search-ai-text-line-height: var(--line-height-lg);
171
+
172
+ --search-ai-user-bg-color: var(--color-blue-6);
173
+ --search-ai-user-text-color: var(--color-static-white);
174
+ --search-ai-assistant-bg-color: var(--layer-color);
175
+ --search-ai-assistant-text-color: var(--text-color-primary);
176
+ --search-ai-assistant-border: 1px solid var(--border-color-primary);
171
177
 
172
178
  --search-ai-resources-gap: var(--spacing-base);
173
179
  --search-ai-resources-title-font-weight: var(--font-weight-medium);
@@ -177,11 +183,111 @@ exports.search = (0, styled_components_1.css) `
177
183
  --search-ai-resource-tags-gap: var(--spacing-base);
178
184
  --search-ai-resource-tag-text-color: var(--text-color-secondary);
179
185
  --search-ai-resource-tag-icon-color: var(--text-color-secondary);
186
+ --search-ai-resource-tag-icon-size: 16px;
187
+
188
+ --search-ai-icon-size: 32px;
189
+ --search-ai-icon-bg-color: var(--search-ai-gradient);
190
+ --search-ai-icon-color: var(--color-static-white);
191
+
192
+ --search-ai-thinking-dots-gap: 4px;
193
+ --search-ai-thinking-dots-padding: 4px 0;
194
+ --search-ai-thinking-dot-size: 6px;
195
+ --search-ai-thinking-dot-color: var(--search-ai-gradient);
180
196
 
181
197
  --search-ai-disclaimer-font-size: var(--font-size-sm);
182
198
  --search-ai-disclaimer-line-height: var(--line-height-sm);
183
199
  --search-ai-disclaimer-text-color: var(--text-color-secondary);
184
200
 
201
+
202
+ --search-ai-welcome-margin: var(--spacing-md);
203
+ --search-ai-icon-wrapper-padding: var(--spacing-xs);
204
+
205
+ --search-ai-suggestions-title-text-color: var(--text-color-description);
206
+ --search-ai-suggestions-title-font-size: var(--font-size-base);
207
+ --search-ai-suggestions-title-line-height: var(--line-height-base);
208
+ --search-ai-suggestions-title-font-weight: var(--font-weight-light);
209
+ --search-ai-suggestions-text-color: var(--text-color-description);
210
+
211
+ --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
212
+ --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
213
+ --search-ai-conversation-input-send-button-bg-color-disabled: var(--button-bg-color-disabled);
214
+ --search-ai-conversation-input-send-button-border-color-disabled: var(--button-border-color-disabled);
215
+ --search-ai-conversation-input-send-button-icon-color: var(--color-static-white);
216
+
217
+ /**
218
+ * @tokens AI Search Dialog
219
+ */
220
+ --search-ai-dialog-bg-color: var(--bg-color);
221
+ --search-ai-dialog-header-border: var(--search-modal-border);
222
+ --search-ai-dialog-header-bg-color: var(--search-modal-header-bg-color);
223
+ --search-ai-dialog-header-padding: var(--search-modal-header-padding);
224
+
225
+ --search-ai-dialog-body-padding: var(--search-ai-response-padding);
226
+ --search-ai-dialog-body-gap: var(--spacing-sm);
227
+
228
+ --search-ai-dialog-input-padding: var(--spacing-sm) var(--search-ai-response-padding);
229
+ --search-ai-dialog-input-border: 1px solid var(--border-color-secondary);
230
+ --search-ai-dialog-input-bg-color: var(--bg-color);
231
+
232
+ /**
233
+ * @tokens AI Search Conversation Input
234
+ */
235
+ --search-ai-conversation-input-bg-color: var(--bg-color);
236
+ --search-ai-conversation-input-padding: var(--spacing-sm) var(--spacing-md);
237
+ --search-ai-conversation-input-border: 1px solid var(--border-color-secondary);
238
+ --search-ai-conversation-input-border-radius: var(--border-radius-lg);
239
+ --search-ai-conversation-input-font-size: var(--font-size-base);
240
+ --search-ai-conversation-input-placeholder-color: var(--search-input-placeholder-color);
241
+ --search-ai-conversation-input-border-color-focus: var(--color-blue-6);
242
+ --search-ai-conversation-input-border-color-disabled: var(--border-color-secondary);
243
+
244
+ --search-ai-conversation-input-send-button-right: 12px;
245
+ --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
246
+ --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
247
+ --search-ai-conversation-input-send-button-bg-color-disabled: var(--button-bg-color-disabled);
248
+ --search-ai-conversation-input-send-button-border-disabled: 1px solid var(--button-border-color-disabled);
249
+
250
+ /**
251
+ * @tokens AI Search Response
252
+ */
253
+ --search-ai-response-padding: var(--spacing-lg);
254
+ --search-ai-response-gap: var(--spacing-sm);
255
+ --search-ai-response-header-gap: var(--spacing-md);
256
+ --search-ai-response-body-gap: var(--spacing-xl);
257
+ --search-ai-response-body-padding: 0 40px;
258
+
259
+ --search-ai-text-color: var(--text-color-secondary);
260
+ --search-ai-text-font-size: var(--font-size-lg);
261
+ --search-ai-text-line-height: var(--line-height-lg);
262
+
263
+ --search-ai-thinking-text-margin: var(--md-pre-margin) 0;
264
+
265
+ --search-ai-question-font-size: var(--font-size-xl);
266
+ --search-ai-question-font-weight: var(--font-weight-semibold);
267
+ --search-ai-question-line-height: var(--line-height-xl);
268
+ --search-ai-question-text-color: var(--text-color-primary);
269
+
270
+ --search-ai-resources-gap: var(--spacing-base);
271
+ --search-ai-resources-title-font-weight: var(--font-weight-medium);
272
+ --search-ai-resources-title-font-size: var(--font-size-lg);
273
+ --search-ai-resources-title-line-height: var(--line-height-lg);
274
+
275
+ --search-ai-resource-tags-gap: var(--spacing-base);
276
+ --search-ai-resource-tag-text-color: var(--text-color-secondary);
277
+ --search-ai-resource-tag-icon-color: var(--text-color-secondary);
278
+
279
+ --search-ai-suggestions-gap: var(--spacing-sm);
280
+ --search-ai-suggestions-margin-left: var(--spacing-xs);
281
+ --search-ai-suggestion-item-gap: var(--spacing-xs);
282
+
283
+ --search-ai-suggestions-title-text-color: var(--text-color-description);
284
+ --search-ai-suggestions-title-font-size: var(--font-size-base);
285
+ --search-ai-suggestions-title-line-height: var(--line-height-base);
286
+ --search-ai-suggestions-title-font-weight: var(--font-weight-light);
287
+
288
+ --search-ai-spinner-icon-color: var(--icon-color-interactive);
289
+ --search-ai-checkmark-icon-color: var(--icon-color-interactive);
290
+
185
291
  // @tokens End
186
292
  `;
187
293
  //# sourceMappingURL=variables.js.map
@@ -6,4 +6,8 @@ export declare enum AiSearchError {
6
6
  EmptyResponse = "empty_response",
7
7
  ErrorProcessingResponse = "error_processing_response"
8
8
  }
9
+ export declare const enum AiSearchConversationRole {
10
+ USER = "user",
11
+ ASSISTANT = "assistant"
12
+ }
9
13
  export declare const AI_SEARCH_ERROR_CONFIG: Record<AiSearchError, AiSearchErrorConfig>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AI_SEARCH_ERROR_CONFIG = exports.AiSearchError = void 0;
3
+ exports.AI_SEARCH_ERROR_CONFIG = exports.AiSearchConversationRole = exports.AiSearchError = void 0;
4
4
  var AiSearchError;
5
5
  (function (AiSearchError) {
6
6
  AiSearchError["Unauthorized"] = "ai_search_unauthorized";
@@ -9,6 +9,11 @@ var AiSearchError;
9
9
  AiSearchError["EmptyResponse"] = "empty_response";
10
10
  AiSearchError["ErrorProcessingResponse"] = "error_processing_response";
11
11
  })(AiSearchError || (exports.AiSearchError = AiSearchError = {}));
12
+ var AiSearchConversationRole;
13
+ (function (AiSearchConversationRole) {
14
+ AiSearchConversationRole["USER"] = "user";
15
+ AiSearchConversationRole["ASSISTANT"] = "assistant";
16
+ })(AiSearchConversationRole || (exports.AiSearchConversationRole = AiSearchConversationRole = {}));
12
17
  const defaultErrorConfig = {
13
18
  headerKey: 'search.ai.error.header',
14
19
  headerDefault: 'Oops! Something went wrong.',
@@ -13,21 +13,49 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
13
13
  const location = (0, react_router_dom_1.useLocation)();
14
14
  const navigate = (0, react_router_dom_1.useNavigate)();
15
15
  const searchParams = (0, react_1.useMemo)(() => new URLSearchParams(location.search), [location.search]);
16
- const [controlsState, setControlsState] = (0, react_1.useState)(() => {
16
+ const filtersRef = (0, react_1.useRef)(filters);
17
+ const inputsRef = (0, react_1.useRef)(inputs);
18
+ const togglesRef = (0, react_1.useRef)(toggles);
19
+ const getInitialState = () => {
17
20
  var _a, _b, _c, _d;
18
- const initialState = {};
21
+ const state = {};
19
22
  for (const [id, toggle] of Object.entries(toggles)) {
20
- initialState[id] = Object.assign(Object.assign({}, toggle), { render: true, type: 'toggle', value: enableDeepLink ? searchParams.get(id) === 'true' : false });
23
+ state[id] = Object.assign(Object.assign({}, toggle), { render: true, type: 'toggle', value: enableDeepLink ? searchParams.get(id) === 'true' : false });
21
24
  }
22
25
  for (const [id, input] of Object.entries(inputs)) {
23
- initialState[id] = Object.assign(Object.assign({}, input), { render: true, type: 'input', value: enableDeepLink ? ((_a = searchParams.get(id)) !== null && _a !== void 0 ? _a : input.value) : input.value });
26
+ state[id] = Object.assign(Object.assign({}, input), { render: true, type: 'input', value: enableDeepLink ? ((_a = searchParams.get(id)) !== null && _a !== void 0 ? _a : input.value) : input.value });
24
27
  }
25
28
  for (const [id, filter] of Object.entries(filters)) {
26
29
  const defaultValue = ((_c = (_b = filter === null || filter === void 0 ? void 0 : filter.items) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value) || '';
27
- initialState[id] = Object.assign(Object.assign({}, filter), { render: true, type: 'filter', value: enableDeepLink ? ((_d = searchParams.get(id)) !== null && _d !== void 0 ? _d : defaultValue) : defaultValue });
30
+ state[id] = Object.assign(Object.assign({}, filter), { render: true, type: 'filter', value: enableDeepLink ? ((_d = searchParams.get(id)) !== null && _d !== void 0 ? _d : defaultValue) : defaultValue });
28
31
  }
29
- return initialState;
30
- });
32
+ return state;
33
+ };
34
+ const [controlsState, setControlsState] = (0, react_1.useState)(getInitialState);
35
+ (0, react_1.useEffect)(() => {
36
+ const sameProps = [
37
+ JSON.stringify(filters) === JSON.stringify(filtersRef.current),
38
+ JSON.stringify(inputs) === JSON.stringify(inputsRef.current),
39
+ JSON.stringify(toggles) === JSON.stringify(togglesRef.current),
40
+ ];
41
+ if (sameProps.every(Boolean)) {
42
+ return;
43
+ }
44
+ filtersRef.current = filters;
45
+ inputsRef.current = inputs;
46
+ togglesRef.current = toggles;
47
+ const newState = getInitialState();
48
+ // Preserve existing values where control type hasn't changed
49
+ Object.entries(newState).forEach(([id, control]) => {
50
+ const existingControl = controlsState[id];
51
+ if (existingControl && existingControl.type === control.type) {
52
+ // @ts-ignore
53
+ newState[id] = Object.assign(Object.assign({}, control), { value: existingControl.value });
54
+ }
55
+ });
56
+ setControlsState(newState);
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, [filters, inputs, toggles, enableDeepLink]);
31
59
  const changeControlState = (id, value) => {
32
60
  setControlsState((prev) => {
33
61
  const control = prev[id];
@@ -124,7 +152,8 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
124
152
  getFileText,
125
153
  populateInputsWithValue,
126
154
  };
127
- }, [filters, controlsState]);
155
+ // eslint-disable-next-line react-hooks/exhaustive-deps
156
+ }, [controlsState]);
128
157
  /**
129
158
  * Update the URL search params with the current state of the filters and inputs
130
159
  */
@@ -144,10 +173,10 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
144
173
  const newSearch = newSearchParams.toString();
145
174
  if (newSearch === location.search.substring(1))
146
175
  return;
147
- navigate({ search: newSearch });
176
+ navigate({ search: newSearch }, { replace: true });
148
177
  // Ignore searchParams in dependency array to avoid infinite re-renders
149
178
  // eslint-disable-next-line react-hooks/exhaustive-deps
150
- }, [filters, controlsState, navigate, location]);
179
+ }, [controlsState]);
151
180
  return Object.assign({ changeControlState,
152
181
  getControlState }, walkthroughContext);
153
182
  }
@@ -15,35 +15,37 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
15
15
  // Track observed elements in case new observer needs to be created
16
16
  const observedElementsRef = (0, react_1.useRef)(new Set());
17
17
  const [activeStep, setActiveStep] = (0, react_1.useState)(enableDeepLink ? searchParams.get(constants_1.ACTIVE_STEP_QUERY_PARAM) : null);
18
+ // eslint-disable-next-line react-hooks/exhaustive-deps
19
+ const _steps = (0, react_1.useMemo)(() => steps, [JSON.stringify(steps)]);
18
20
  const register = (0, react_1.useCallback)((element) => {
19
21
  // for some reason, the observer is not ready immediately
20
22
  setTimeout(() => {
21
23
  if (observerRef.current) {
22
24
  const stepKey = Number(element.dataset.stepKey);
23
- if (Number.isInteger(stepKey) && stepKey >= 0) {
24
- steps[stepKey].compRef = element;
25
+ if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
26
+ _steps[stepKey].compRef = element;
25
27
  }
26
28
  observerRef.current.observe(element);
27
29
  observedElementsRef.current.add(element);
28
30
  }
29
31
  }, 10);
30
- }, [steps]);
32
+ }, [_steps]);
31
33
  const unregister = (0, react_1.useCallback)((element) => {
32
34
  if (observerRef.current) {
33
35
  const stepKey = Number(element.dataset.stepKey);
34
- if (Number.isInteger(stepKey) && stepKey >= 0) {
35
- steps[stepKey].compRef = undefined;
36
+ if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
37
+ _steps[stepKey].compRef = undefined;
36
38
  }
37
39
  observerRef.current.unobserve(element);
38
40
  observedElementsRef.current.delete(element);
39
41
  }
40
- }, [steps]);
42
+ }, [_steps]);
41
43
  const observerCallback = (0, react_1.useCallback)((entries) => {
42
44
  var _a, _b, _c;
43
45
  if (lockObserver.current) {
44
46
  return;
45
47
  }
46
- const renderedSteps = steps.filter((step) => Boolean(step.compRef));
48
+ const renderedSteps = _steps.filter((step) => Boolean(step.compRef));
47
49
  if (renderedSteps.length < 2) {
48
50
  setActiveStep(((_a = renderedSteps[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
49
51
  return;
@@ -54,7 +56,7 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
54
56
  continue;
55
57
  }
56
58
  const { intersectionRatio, boundingClientRect, rootBounds, isIntersecting } = entry;
57
- const step = steps[stepKey];
59
+ const step = _steps[stepKey];
58
60
  const stepIndex = renderedSteps.findIndex((renderedStep) => renderedStep.stepKey === step.stepKey);
59
61
  const { next } = (0, utils_1.getAdjacentValues)(renderedSteps, stepIndex);
60
62
  const intersectionAtTop = (rootBounds === null || rootBounds === void 0 ? void 0 : rootBounds.bottom) !== undefined && boundingClientRect.top < rootBounds.top;
@@ -80,7 +82,7 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
80
82
  break;
81
83
  }
82
84
  }
83
- }, [steps, activeStep]);
85
+ }, [_steps, activeStep]);
84
86
  (0, react_1.useEffect)(() => {
85
87
  var _a, _b, _c;
86
88
  const filtersElementHeight = ((_a = filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
@@ -113,10 +115,9 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
113
115
  const newSearch = newSearchParams.toString();
114
116
  if (newSearch === location.search.substring(1))
115
117
  return;
116
- navigate({ search: newSearch });
117
- // Ignore searchParams in dependency array to avoid infinite re-renders
118
+ navigate({ search: newSearch }, { replace: true });
118
119
  // eslint-disable-next-line react-hooks/exhaustive-deps
119
- }, [activeStep, navigate, location]);
120
+ }, [activeStep]);
120
121
  return { register, unregister, lockObserver, filtersElementRef, activeStep, setActiveStep };
121
122
  }
122
123
  //# sourceMappingURL=use-code-walkthrough-steps.js.map
@@ -33,3 +33,4 @@ export * from '../../core/hooks/code-walkthrough/use-code-walkthrough-steps';
33
33
  export * from '../../core/hooks/code-walkthrough/use-code-walkthrough-controls';
34
34
  export * from '../../core/hooks/code-walkthrough/use-code-panel';
35
35
  export * from '../../core/hooks/code-walkthrough/use-renderable-files';
36
+ export * from '../../core/hooks/use-element-size';
@@ -49,4 +49,5 @@ __exportStar(require("../../core/hooks/code-walkthrough/use-code-walkthrough-ste
49
49
  __exportStar(require("../../core/hooks/code-walkthrough/use-code-walkthrough-controls"), exports);
50
50
  __exportStar(require("../../core/hooks/code-walkthrough/use-code-panel"), exports);
51
51
  __exportStar(require("../../core/hooks/code-walkthrough/use-renderable-files"), exports);
52
+ __exportStar(require("../../core/hooks/use-element-size"), exports);
52
53
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ import { RefObject } from 'react';
2
+ type Size = {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ export declare const useElementSize: <T extends HTMLElement = HTMLElement>(ref?: RefObject<T>, delay?: number) => Size;
7
+ export {};