@redocly/theme 0.59.0-rc.2 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +7 -1
  2. package/lib/components/Buttons/AIAssistantButton.js +6 -2
  3. package/lib/components/Buttons/ConnectMCPButton.d.ts +8 -0
  4. package/lib/components/Buttons/ConnectMCPButton.js +145 -0
  5. package/lib/components/Buttons/variables.d.ts +1 -0
  6. package/lib/components/Buttons/variables.js +42 -2
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntityInfoBar.js +1 -0
  8. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  9. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  10. package/lib/components/Catalog/CatalogEntityIcon.js +2 -1
  11. package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +4 -0
  12. package/lib/components/Catalog/CatalogTagsWithTooltip.js +1 -1
  13. package/lib/components/Catalog/variables.js +1 -1
  14. package/lib/components/Dropdown/Dropdown.d.ts +16 -2
  15. package/lib/components/Dropdown/Dropdown.js +5 -5
  16. package/lib/components/Menu/MenuItem.js +1 -1
  17. package/lib/components/Navbar/NavbarItem.js +3 -3
  18. package/lib/components/PageActions/PageActions.js +4 -1
  19. package/lib/components/PageActions/variables.js +2 -0
  20. package/lib/components/Search/FilterFields/SearchFilterFieldTags.js +1 -2
  21. package/lib/components/Search/SearchAiActionButtons.d.ts +10 -0
  22. package/lib/components/Search/SearchAiActionButtons.js +43 -0
  23. package/lib/components/Search/SearchAiConversationInput.d.ts +3 -1
  24. package/lib/components/Search/SearchAiConversationInput.js +39 -7
  25. package/lib/components/Search/SearchAiDialog.d.ts +3 -6
  26. package/lib/components/Search/SearchAiDialog.js +20 -9
  27. package/lib/components/Search/SearchAiMessage.d.ts +9 -5
  28. package/lib/components/Search/SearchAiMessage.js +146 -22
  29. package/lib/components/Search/SearchAiNegativeFeedbackForm.d.ts +8 -0
  30. package/lib/components/Search/SearchAiNegativeFeedbackForm.js +169 -0
  31. package/lib/components/Search/SearchDialog.js +36 -5
  32. package/lib/components/Search/SearchGroups.js +2 -2
  33. package/lib/components/Search/variables.js +36 -64
  34. package/lib/components/Segmented/Segmented.d.ts +1 -8
  35. package/lib/components/Segmented/Segmented.js +3 -1
  36. package/lib/components/Select/SelectInput.js +1 -1
  37. package/lib/components/Select/variables.js +2 -2
  38. package/lib/components/Tag/Tag.d.ts +2 -1
  39. package/lib/components/Tag/Tag.js +66 -17
  40. package/lib/components/Tag/variables.dark.js +135 -36
  41. package/lib/components/Tag/variables.js +78 -61
  42. package/lib/core/constants/index.d.ts +1 -0
  43. package/lib/core/constants/index.js +1 -0
  44. package/lib/core/constants/mcp.d.ts +1 -0
  45. package/lib/core/constants/mcp.js +5 -0
  46. package/lib/core/constants/search.d.ts +5 -4
  47. package/lib/core/constants/search.js +4 -5
  48. package/lib/core/hooks/index.d.ts +3 -0
  49. package/lib/core/hooks/index.js +3 -0
  50. package/lib/core/hooks/menu/use-nested-menu.js +1 -1
  51. package/lib/core/hooks/search/use-feedback-tooltip.d.ts +6 -0
  52. package/lib/core/hooks/search/use-feedback-tooltip.js +26 -0
  53. package/lib/core/hooks/use-connect-mcp-button.d.ts +13 -0
  54. package/lib/core/hooks/use-connect-mcp-button.js +50 -0
  55. package/lib/core/hooks/use-mcp-config.d.ts +9 -0
  56. package/lib/core/hooks/use-mcp-config.js +27 -0
  57. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  58. package/lib/core/hooks/use-page-actions.js +99 -119
  59. package/lib/core/hooks/use-product-picker.js +2 -1
  60. package/lib/core/hooks/use-tabs.d.ts +3 -2
  61. package/lib/core/hooks/use-tabs.js +115 -57
  62. package/lib/core/hooks/use-telemetry-fallback.d.ts +10 -8
  63. package/lib/core/hooks/use-telemetry-fallback.js +10 -8
  64. package/lib/core/openapi/index.d.ts +1 -0
  65. package/lib/core/styles/dark.js +4 -0
  66. package/lib/core/styles/global.js +5 -0
  67. package/lib/core/types/hooks.d.ts +2 -2
  68. package/lib/core/types/index.d.ts +1 -0
  69. package/lib/core/types/index.js +1 -0
  70. package/lib/core/types/l10n.d.ts +1 -1
  71. package/lib/core/types/mcp.d.ts +6 -0
  72. package/lib/core/types/mcp.js +3 -0
  73. package/lib/core/types/search.d.ts +11 -4
  74. package/lib/core/types/search.js +6 -0
  75. package/lib/core/types/segmented.d.ts +12 -0
  76. package/lib/core/types/segmented.js +3 -0
  77. package/lib/core/utils/frontmatter-translate.d.ts +6 -0
  78. package/lib/core/utils/frontmatter-translate.js +14 -0
  79. package/lib/core/utils/index.d.ts +2 -0
  80. package/lib/core/utils/index.js +2 -0
  81. package/lib/core/utils/mcp.d.ts +2 -0
  82. package/lib/core/utils/mcp.js +31 -0
  83. package/lib/icons/AiStarsGradientIcon/AiStarsGradientIcon.js +44 -4
  84. package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
  85. package/lib/icons/ConnectIcon/ConnectIcon.d.ts +9 -0
  86. package/lib/icons/ConnectIcon/ConnectIcon.js +17 -0
  87. package/lib/icons/CubeIcon/CubeIcon.d.ts +9 -0
  88. package/lib/icons/CubeIcon/CubeIcon.js +17 -0
  89. package/lib/icons/HashtagIcon/HashtagIcon.d.ts +9 -0
  90. package/lib/icons/HashtagIcon/HashtagIcon.js +22 -0
  91. package/lib/icons/RedoclyIcon/RedoclyIcon.js +4 -7
  92. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.d.ts +9 -0
  93. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.js +34 -0
  94. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.d.ts +9 -0
  95. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.js +34 -0
  96. package/lib/icons/VSCodeIcon/VSCodeIcon.d.ts +9 -0
  97. package/lib/icons/VSCodeIcon/VSCodeIcon.js +17 -0
  98. package/lib/index.d.ts +1 -2
  99. package/lib/index.js +1 -2
  100. package/lib/markdoc/components/Cards/Card.js +1 -28
  101. package/lib/markdoc/components/ConnectMCP/ConnectMCP.d.ts +8 -0
  102. package/lib/markdoc/components/ConnectMCP/ConnectMCP.js +19 -0
  103. package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
  104. package/lib/markdoc/components/Tabs/TabList.js +197 -47
  105. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
  106. package/lib/markdoc/components/Tabs/Tabs.js +57 -12
  107. package/lib/markdoc/components/default.d.ts +1 -0
  108. package/lib/markdoc/components/default.js +1 -0
  109. package/lib/markdoc/default.d.ts +6 -0
  110. package/lib/markdoc/default.js +2 -0
  111. package/lib/markdoc/tags/card.js +0 -1
  112. package/lib/markdoc/tags/connect-mcp.d.ts +2 -0
  113. package/lib/markdoc/tags/connect-mcp.js +27 -0
  114. package/package.json +6 -6
  115. package/src/components/Buttons/AIAssistantButton.tsx +6 -2
  116. package/src/components/Buttons/ConnectMCPButton.tsx +180 -0
  117. package/src/components/Buttons/variables.ts +42 -1
  118. package/src/components/Catalog/CatalogEntity/CatalogEntityInfoBar.tsx +1 -0
  119. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  120. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  121. package/src/components/Catalog/CatalogEntityIcon.tsx +2 -1
  122. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +5 -0
  123. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +1 -5
  124. package/src/components/Catalog/variables.ts +1 -1
  125. package/src/components/Dropdown/Dropdown.tsx +84 -79
  126. package/src/components/Menu/MenuItem.tsx +1 -0
  127. package/src/components/Navbar/NavbarItem.tsx +6 -5
  128. package/src/components/PageActions/PageActions.tsx +5 -1
  129. package/src/components/PageActions/variables.ts +2 -0
  130. package/src/components/Search/FilterFields/SearchFilterFieldTags.tsx +3 -3
  131. package/src/components/Search/SearchAiActionButtons.tsx +76 -0
  132. package/src/components/Search/SearchAiConversationInput.tsx +61 -18
  133. package/src/components/Search/SearchAiDialog.tsx +52 -23
  134. package/src/components/Search/SearchAiMessage.tsx +172 -43
  135. package/src/components/Search/SearchAiNegativeFeedbackForm.tsx +210 -0
  136. package/src/components/Search/SearchDialog.tsx +49 -13
  137. package/src/components/Search/SearchGroups.tsx +2 -0
  138. package/src/components/Search/variables.ts +36 -64
  139. package/src/components/Segmented/Segmented.tsx +15 -20
  140. package/src/components/Select/SelectInput.tsx +1 -0
  141. package/src/components/Select/variables.ts +2 -2
  142. package/src/components/Tag/Tag.tsx +35 -19
  143. package/src/components/Tag/variables.dark.ts +135 -36
  144. package/src/components/Tag/variables.ts +78 -61
  145. package/src/core/constants/index.ts +1 -0
  146. package/src/core/constants/mcp.ts +1 -0
  147. package/src/core/constants/search.ts +8 -4
  148. package/src/core/hooks/index.ts +3 -0
  149. package/src/core/hooks/menu/use-nested-menu.ts +2 -2
  150. package/src/core/hooks/search/use-feedback-tooltip.ts +32 -0
  151. package/src/core/hooks/use-connect-mcp-button.ts +79 -0
  152. package/src/core/hooks/use-mcp-config.ts +43 -0
  153. package/src/core/hooks/use-page-actions.ts +141 -150
  154. package/src/core/hooks/use-product-picker.ts +2 -1
  155. package/src/core/hooks/use-tabs.ts +168 -86
  156. package/src/core/hooks/use-telemetry-fallback.ts +10 -8
  157. package/src/core/openapi/index.ts +1 -0
  158. package/src/core/styles/dark.ts +4 -0
  159. package/src/core/styles/global.ts +6 -1
  160. package/src/core/types/hooks.ts +5 -1
  161. package/src/core/types/index.ts +1 -0
  162. package/src/core/types/l10n.ts +13 -0
  163. package/src/core/types/mcp.ts +8 -0
  164. package/src/core/types/search.ts +13 -4
  165. package/src/core/types/segmented.ts +14 -0
  166. package/src/core/utils/frontmatter-translate.ts +9 -0
  167. package/src/core/utils/index.ts +2 -0
  168. package/src/core/utils/mcp.ts +34 -0
  169. package/src/icons/AiStarsGradientIcon/AiStarsGradientIcon.tsx +13 -4
  170. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
  171. package/src/icons/ConnectIcon/ConnectIcon.tsx +27 -0
  172. package/src/icons/CubeIcon/CubeIcon.tsx +27 -0
  173. package/src/icons/HashtagIcon/HashtagIcon.tsx +23 -0
  174. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +4 -22
  175. package/src/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.tsx +38 -0
  176. package/src/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.tsx +35 -0
  177. package/src/icons/VSCodeIcon/VSCodeIcon.tsx +29 -0
  178. package/src/index.ts +1 -2
  179. package/src/markdoc/components/Cards/Card.tsx +1 -28
  180. package/src/markdoc/components/ConnectMCP/ConnectMCP.tsx +28 -0
  181. package/src/markdoc/components/Tabs/TabList.tsx +312 -105
  182. package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
  183. package/src/markdoc/components/default.ts +1 -0
  184. package/src/markdoc/default.ts +2 -0
  185. package/src/markdoc/tags/card.ts +0 -1
  186. package/src/markdoc/tags/connect-mcp.ts +25 -0
  187. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.d.ts +0 -1
  188. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.js +0 -11
  189. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.d.ts +0 -1
  190. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.js +0 -5
  191. package/lib/ext/process-scorecard.d.ts +0 -5
  192. package/lib/ext/process-scorecard.js +0 -11
  193. package/src/components/OpenApiDocs/hooks/AdditionalOverviewInfo.tsx +0 -9
  194. package/src/components/OpenApiDocs/hooks/AfterOpenApiDescription.tsx +0 -1
  195. package/src/ext/process-scorecard.ts +0 -13
@@ -0,0 +1,210 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FeedbackType } from '@redocly/theme/core/types';
6
+
7
+ import { Button } from '@redocly/theme/components/Button/Button';
8
+ import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
9
+ import { ArrowLeftIcon } from '@redocly/theme/icons/ArrowLeftIcon/ArrowLeftIcon';
10
+ import { SendIcon } from '@redocly/theme/icons/SendIcon/SendIcon';
11
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
12
+
13
+ export type SearchAiNegativeFeedbackFormProps = {
14
+ messageId: string;
15
+ onClose: (messageId: string, feedback: FeedbackType | undefined) => void;
16
+ onSubmit: (reason: string) => void;
17
+ };
18
+
19
+ const NEGATIVE_FEEDBACK_DEFAULT_REASONS = [
20
+ "Didn't answer my question",
21
+ "Couldn't find what I was looking for",
22
+ 'Wrong topic',
23
+ 'Partially helpful, but missing details',
24
+ ];
25
+
26
+ export function SearchAiNegativeFeedbackForm({
27
+ messageId,
28
+ onClose,
29
+ onSubmit,
30
+ }: SearchAiNegativeFeedbackFormProps): JSX.Element {
31
+ const { useTranslate } = useThemeHooks();
32
+ const { translate } = useTranslate();
33
+ const [showMoreInput, setShowMoreInput] = useState(false);
34
+ const [detailedFeedback, setDetailedFeedback] = useState('');
35
+ const textAreaRef = React.useRef<HTMLTextAreaElement>(null);
36
+
37
+ const adjustTextAreaHeight = () => {
38
+ const textArea = textAreaRef.current;
39
+ if (textArea) {
40
+ textArea.style.height = 'auto';
41
+ textArea.style.height = `${textArea.scrollHeight}px`;
42
+ }
43
+ };
44
+
45
+ const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
46
+ setDetailedFeedback(e.target.value);
47
+ adjustTextAreaHeight();
48
+ };
49
+
50
+ useEffect(() => {
51
+ if (showMoreInput) {
52
+ adjustTextAreaHeight();
53
+ }
54
+ }, [showMoreInput]);
55
+
56
+ return (
57
+ <FeedbackFormWrapper data-component-name="Search/SearchAiNegativeFeedbackForm">
58
+ <FeedbackHeader>
59
+ {showMoreInput ? (
60
+ <BackButton
61
+ variant="text"
62
+ size="small"
63
+ icon={<ArrowLeftIcon />}
64
+ onClick={() => setShowMoreInput(false)}
65
+ aria-label="Back to feedback reasons"
66
+ />
67
+ ) : null}
68
+ <FeedbackTitle data-translation-key="search.ai.feedback.title">
69
+ {translate('search.ai.feedback.title', "What didn't you like about this response?")}
70
+ </FeedbackTitle>
71
+ <CloseButton
72
+ variant="text"
73
+ size="small"
74
+ icon={<CloseIcon />}
75
+ onClick={() => onClose(messageId, undefined)}
76
+ aria-label="Close feedback form"
77
+ />
78
+ </FeedbackHeader>
79
+
80
+ {!showMoreInput ? (
81
+ <FeedbackReasonsWrapper>
82
+ {NEGATIVE_FEEDBACK_DEFAULT_REASONS.map((reason) => (
83
+ <Button key={reason} variant="outlined" size="small" onClick={() => onSubmit(reason)}>
84
+ {reason}
85
+ </Button>
86
+ ))}
87
+ <Button variant="outlined" size="small" onClick={() => setShowMoreInput(true)}>
88
+ More...
89
+ </Button>
90
+ </FeedbackReasonsWrapper>
91
+ ) : (
92
+ <FeedbackInputWrapper>
93
+ <FeedbackTextArea
94
+ ref={textAreaRef}
95
+ placeholder={translate('search.ai.feedback.detailsPlaceholder', 'Add specific details')}
96
+ value={detailedFeedback}
97
+ onChange={handleTextChange}
98
+ rows={1}
99
+ autoFocus
100
+ />
101
+ <SendButton
102
+ size="small"
103
+ icon={
104
+ <SendIcon
105
+ color={
106
+ !detailedFeedback.trim()
107
+ ? '--button-content-color-disabled'
108
+ : 'var(--search-ai-conversation-input-send-button-icon-color)'
109
+ }
110
+ />
111
+ }
112
+ onClick={() => onSubmit(detailedFeedback)}
113
+ disabled={!detailedFeedback.trim()}
114
+ aria-label="Send feedback"
115
+ />
116
+ </FeedbackInputWrapper>
117
+ )}
118
+ </FeedbackFormWrapper>
119
+ );
120
+ }
121
+
122
+ const FeedbackFormWrapper = styled.div`
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: var(--spacing-sm);
126
+ padding: var(--spacing-xs);
127
+ background: var(--search-ai-feedback-form-bg-color);
128
+ border: 1px solid var(--search-ai-feedback-form-border-color);
129
+ border-radius: var(--border-radius-lg);
130
+ `;
131
+
132
+ const FeedbackHeader = styled.div`
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ gap: var(--spacing-sm);
137
+ `;
138
+
139
+ const FeedbackTitle = styled.div`
140
+ font-size: var(--font-size-base);
141
+ color: var(--text-color);
142
+ flex: 1;
143
+ `;
144
+
145
+ const BackButton = styled(Button)`
146
+ flex-shrink: 0;
147
+ `;
148
+
149
+ const CloseButton = styled(Button)`
150
+ flex-shrink: 0;
151
+ `;
152
+
153
+ const FeedbackReasonsWrapper = styled.div`
154
+ display: flex;
155
+ flex-wrap: wrap;
156
+ gap: var(--spacing-xs);
157
+ `;
158
+
159
+ const FeedbackInputWrapper = styled.div`
160
+ position: relative;
161
+ width: 100%;
162
+ `;
163
+
164
+ const FeedbackTextArea = styled.textarea`
165
+ width: 100%;
166
+ min-height: 5rem;
167
+ max-height: 12.5rem;
168
+ padding: var(--spacing-xs);
169
+ padding-right: 3rem;
170
+ border: 1px solid var(--border-color-primary);
171
+ border-radius: var(--border-radius-md);
172
+ font-family: inherit;
173
+ font-size: var(--font-size-base);
174
+ line-height: var(--line-height-base);
175
+ background: var(--background-color);
176
+ color: var(--text-color);
177
+ resize: none;
178
+ overflow-y: auto;
179
+
180
+ &:focus {
181
+ outline: 1px solid var(--button-border-color-focused);
182
+ border-color: var(--button-border-color-focused);
183
+ }
184
+
185
+ &::placeholder {
186
+ color: var(--text-color-helper);
187
+ }
188
+ `;
189
+
190
+ const SendButton = styled(Button)`
191
+ position: absolute;
192
+ right: var(--search-ai-conversation-input-send-button-right);
193
+ bottom: var(--spacing-sm);
194
+ transition: background-color 0.2s ease;
195
+ background-color: var(--search-ai-conversation-input-send-button-bg-color);
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: center;
199
+ border-radius: var(--search-ai-conversation-input-send-button-border-radius);
200
+ padding: var(--search-ai-conversation-input-send-button-padding);
201
+
202
+ &:hover {
203
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
204
+ }
205
+
206
+ &:disabled {
207
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-disabled);
208
+ border: var(--search-ai-conversation-input-send-button-border-disabled);
209
+ }
210
+ `;
@@ -48,6 +48,7 @@ export function SearchDialog({
48
48
  const products = useProducts();
49
49
  const currentProduct = useCurrentProduct();
50
50
  const [product, setProduct] = useState(currentProduct);
51
+ const searchSessionId = `search-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
51
52
  const [mode, setMode] = useState<'search' | 'ai-dialog'>(initialMode);
52
53
  const autoSearchDisabled = mode !== 'search';
53
54
  const {
@@ -62,7 +63,7 @@ export function SearchDialog({
62
63
  advancedSearch,
63
64
  askAi,
64
65
  groupField,
65
- } = useSearch(product?.name, autoSearchDisabled);
66
+ } = useSearch(product?.name, autoSearchDisabled, searchSessionId);
66
67
  const {
67
68
  isFilterOpen,
68
69
  onFilterToggle,
@@ -72,10 +73,24 @@ export function SearchDialog({
72
73
  onQuickFilterReset,
73
74
  } = useSearchFilter(filter, setFilter);
74
75
  const { addSearchHistoryItem } = useRecentSearches();
75
- const aiSearch = useAiSearch({ filter });
76
+ const aiSearch = useAiSearch({ filter }, searchSessionId);
76
77
 
77
78
  const searchInputRef = useRef<HTMLInputElement>(null);
78
79
  const modalRef = useRef<HTMLDivElement>(null);
80
+
81
+ const [isMobile, setIsMobile] = useState(false);
82
+
83
+ useEffect(() => {
84
+ const mediaQuery = window.matchMedia(`(max-width: ${breakpoints.small})`);
85
+ setIsMobile(mediaQuery.matches);
86
+
87
+ const handleChange = (e: MediaQueryListEvent) => {
88
+ setIsMobile(e.matches);
89
+ };
90
+ mediaQuery.addEventListener('change', handleChange);
91
+ return () => mediaQuery.removeEventListener('change', handleChange);
92
+ }, []);
93
+
79
94
  const aiQueryRef = useRef<HTMLDivElement>(null);
80
95
  const firstSearchResultRef = useRef<HTMLAnchorElement>(null);
81
96
  const searchKeysWithResults = items ? Object.keys(items).filter((key) => items[key]?.length) : [];
@@ -150,13 +165,14 @@ export function SearchDialog({
150
165
  totalResults: results.length.toString(),
151
166
  index: index.toString(),
152
167
  searchEngine: mode,
168
+ searchSessionId,
153
169
  });
154
170
  onClose();
155
171
  }}
156
172
  />
157
173
  );
158
174
  },
159
- [onClose, product, products, addSearchHistoryItem, query, telemetry, mode],
175
+ [onClose, product, products, addSearchHistoryItem, query, telemetry, mode, searchSessionId],
160
176
  );
161
177
 
162
178
  const showLoadMore = useCallback(
@@ -226,6 +242,9 @@ export function SearchDialog({
226
242
  if (query.trim()) {
227
243
  aiSearch.askQuestion(query);
228
244
  }
245
+ telemetry.sendSearchAiOpenedMessage({
246
+ method: 'ai_search_button',
247
+ });
229
248
  }}
230
249
  >
231
250
  {translate('search.ai.button', 'Search with AI')}
@@ -249,17 +268,22 @@ export function SearchDialog({
249
268
  tabIndex={0}
250
269
  icon={<ChevronLeftIcon />}
251
270
  >
252
- {translate('search.ai.backToSearch', 'Back to search')}
253
- </Button>
254
- <Button
255
- variant="secondary"
256
- disabled={!aiSearch.conversation.length}
257
- onClick={() => aiSearch.clearConversation()}
258
- tabIndex={0}
259
- icon={<EditIcon />}
260
- >
261
- {translate('search.ai.newConversation', 'New conversation')}
271
+ {isMobile
272
+ ? translate('search.ai.back', 'Back')
273
+ : translate('search.ai.backToSearch', 'Back to search')}
262
274
  </Button>
275
+ <AiDialogHeaderActionsWrapper>
276
+ <Button
277
+ variant="secondary"
278
+ disabled={!aiSearch.conversation.length}
279
+ onClick={() => aiSearch.clearConversation()}
280
+ tabIndex={0}
281
+ icon={<EditIcon />}
282
+ >
283
+ {translate('search.ai.newConversation', 'New conversation')}
284
+ </Button>
285
+ {isMobile && <Button variant="text" icon={<CloseIcon />} onClick={handleClose} />}
286
+ </AiDialogHeaderActionsWrapper>
263
287
  </AiDialogHeaderWrapper>
264
288
  )}
265
289
  </SearchDialogHeader>
@@ -296,6 +320,9 @@ export function SearchDialog({
296
320
  if (query.trim()) {
297
321
  aiSearch.askQuestion(query);
298
322
  }
323
+ telemetry.sendSearchAiOpenedMessage({
324
+ method: 'ai_search_input',
325
+ });
299
326
  }}
300
327
  onKeyDown={(e) => {
301
328
  if (e.key === 'Enter') {
@@ -303,6 +330,9 @@ export function SearchDialog({
303
330
  if (query.trim()) {
304
331
  aiSearch.askQuestion(query);
305
332
  }
333
+ telemetry.sendSearchAiOpenedMessage({
334
+ method: 'ai_search_input',
335
+ });
306
336
  }
307
337
  }}
308
338
  ref={aiQueryRef}
@@ -389,6 +419,7 @@ export function SearchDialog({
389
419
  telemetry.sendSearchRecentClickedMessage({
390
420
  query,
391
421
  index: index.toString(),
422
+ searchSessionId,
392
423
  });
393
424
  setQuery(query);
394
425
  focusSearchInput();
@@ -524,6 +555,10 @@ const AiDialogHeaderWrapper = styled.div`
524
555
  align-items: center;
525
556
  width: 100%;
526
557
  `;
558
+ const AiDialogHeaderActionsWrapper = styled.div`
559
+ display: flex;
560
+ gap: var(--spacing-xxs);
561
+ `;
527
562
 
528
563
  const SearchDialogBody = styled.div`
529
564
  display: flex;
@@ -640,6 +675,7 @@ const AiDisclaimer = styled.div`
640
675
  line-height: var(--search-ai-disclaimer-line-height);
641
676
  color: var(--search-ai-disclaimer-text-color);
642
677
  margin: 0 auto;
678
+ text-align: center;
643
679
  `;
644
680
 
645
681
  const SearchWithAI = styled.div`
@@ -45,6 +45,7 @@ export function SearchGroups({
45
45
  borderless
46
46
  active={!searchFilter.some((item) => item.isQuickFilter)}
47
47
  onClick={() => searchFilter.some((item) => item.isQuickFilter) && onQuickFilterReset()}
48
+ selectable
48
49
  >
49
50
  All
50
51
  </GroupTag>
@@ -61,6 +62,7 @@ export function SearchGroups({
61
62
  onClick={() => handleGroupTagClick(value, facet.field, active, currentValues)}
62
63
  active={active}
63
64
  borderless
65
+ selectable
64
66
  >
65
67
  {value} {isCounterVisible && <span>{count}</span>}
66
68
  </GroupTag>
@@ -144,68 +144,76 @@ export const search = css`
144
144
  /**
145
145
  * @tokens AI Search
146
146
  */
147
-
147
+
148
148
  --search-ai-gradient: linear-gradient(to right, #715efe, #ff5cdc);
149
149
 
150
- --search-ai-spinner-icon-color: var(--icon-color-interactive);
151
- --search-ai-checkmark-icon-color: var(--icon-color-interactive);
152
150
  --search-ai-response-padding: var(--spacing-lg);
153
151
  --search-ai-response-gap: var(--spacing-sm);
154
-
155
152
  --search-ai-response-header-gap: var(--spacing-md);
153
+ --search-ai-response-body-gap: var(--spacing-xl);
154
+ --search-ai-response-body-padding: 0 40px;
156
155
 
157
156
  --search-ai-question-font-size: var(--font-size-xl);
158
157
  --search-ai-question-font-weight: var(--font-weight-semibold);
159
158
  --search-ai-question-line-height: var(--line-height-xl);
160
159
  --search-ai-question-text-color: var(--text-color-primary);
161
160
 
162
- --search-ai-response-body-gap: var(--spacing-xl);
163
- --search-ai-response-body-padding: 0 40px;
164
-
165
161
  --search-ai-text-color: var(--text-color-secondary);
166
162
  --search-ai-text-font-size: var(--font-size-lg);
167
163
  --search-ai-text-line-height: var(--line-height-lg);
164
+ --search-ai-thinking-text-margin: var(--md-pre-margin) 0;
168
165
 
169
166
  --search-ai-user-bg-color: var(--color-blue-6);
170
167
  --search-ai-user-text-color: var(--color-static-white);
171
168
  --search-ai-assistant-bg-color: var(--layer-color);
172
169
  --search-ai-assistant-text-color: var(--text-color-primary);
173
170
  --search-ai-assistant-border: 1px solid var(--border-color-primary);
171
+ --search-ai-assistant-message-max-width: 80%;
174
172
 
175
- --search-ai-resources-gap: var(--spacing-base);
173
+ --search-ai-icon-size: 32px;
174
+ --search-ai-icon-bg-color: var(--search-ai-gradient);
175
+ --search-ai-icon-color: var(--color-static-white);
176
+ --search-ai-icon-wrapper-padding: var(--spacing-xs);
177
+
178
+ --search-ai-resources-gap: var(--spacing-xxs);
176
179
  --search-ai-resources-title-font-weight: var(--font-weight-medium);
177
180
  --search-ai-resources-title-font-size: var(--font-size-lg);
178
181
  --search-ai-resources-title-line-height: var(--line-height-lg);
179
182
 
180
- --search-ai-resource-tags-gap: var(--spacing-base);
183
+ --search-ai-resource-tags-gap: var(--spacing-xxs);
181
184
  --search-ai-resource-tag-text-color: var(--text-color-secondary);
182
185
  --search-ai-resource-tag-icon-color: var(--text-color-secondary);
183
186
  --search-ai-resource-tag-icon-size: 16px;
184
187
 
185
- --search-ai-icon-size: 32px;
186
- --search-ai-icon-bg-color: var(--search-ai-gradient);
187
- --search-ai-icon-color: var(--color-static-white);
188
-
189
- --search-ai-button-icon-color: none;
188
+ --search-ai-suggestions-gap: var(--spacing-sm);
189
+ --search-ai-suggestions-margin-left: var(--spacing-xs);
190
+ --search-ai-suggestion-item-gap: var(--spacing-xxs);
191
+ --search-ai-suggestions-title-text-color: var(--text-color-description);
192
+ --search-ai-suggestions-title-font-size: var(--font-size-base);
193
+ --search-ai-suggestions-title-line-height: var(--line-height-base);
194
+ --search-ai-suggestions-title-font-weight: var(--font-weight-light);
195
+ --search-ai-suggestions-text-color: var(--text-color-description);
190
196
 
191
197
  --search-ai-thinking-dots-gap: 4px;
192
198
  --search-ai-thinking-dots-padding: 4px 0;
193
199
  --search-ai-thinking-dot-size: 6px;
194
200
  --search-ai-thinking-dot-color: var(--search-ai-gradient);
195
201
 
202
+ --search-ai-spinner-icon-color: var(--icon-color-interactive);
203
+ --search-ai-checkmark-icon-color: var(--icon-color-interactive);
204
+
205
+ --search-ai-feedback-gap: var(--spacing-xxs);
206
+
207
+ --search-ai-feedback-form-bg-color: var(--background-color-secondary);
208
+ --search-ai-feedback-form-border-color: var(--border-color-primary);
209
+
196
210
  --search-ai-disclaimer-font-size: var(--font-size-sm);
197
211
  --search-ai-disclaimer-line-height: var(--line-height-sm);
198
212
  --search-ai-disclaimer-text-color: var(--text-color-secondary);
199
213
 
200
-
201
214
  --search-ai-welcome-margin: var(--spacing-md);
202
- --search-ai-icon-wrapper-padding: var(--spacing-xs);
203
215
 
204
- --search-ai-suggestions-title-text-color: var(--text-color-description);
205
- --search-ai-suggestions-title-font-size: var(--font-size-base);
206
- --search-ai-suggestions-title-line-height: var(--line-height-base);
207
- --search-ai-suggestions-title-font-weight: var(--font-weight-light);
208
- --search-ai-suggestions-text-color: var(--text-color-description);
216
+ --search-ai-button-icon-color: none;
209
217
 
210
218
  --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
211
219
  --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
@@ -232,6 +240,7 @@ export const search = css`
232
240
  * @tokens AI Search Conversation Input
233
241
  */
234
242
  --search-ai-conversation-input-bg-color: var(--bg-color);
243
+ --search-ai-conversation-input-bg-color-disabled: var(--color-warm-grey-1);
235
244
  --search-ai-conversation-input-padding: var(--spacing-sm) var(--spacing-md);
236
245
  --search-ai-conversation-input-border: 1px solid var(--border-color-secondary);
237
246
  --search-ai-conversation-input-border-radius: var(--border-radius-lg);
@@ -241,51 +250,14 @@ export const search = css`
241
250
  --search-ai-conversation-input-border-color-disabled: var(--border-color-secondary);
242
251
 
243
252
  --search-ai-conversation-input-send-button-right: 12px;
244
- --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
245
- --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
246
- --search-ai-conversation-input-send-button-bg-color-disabled: var(--button-bg-color-disabled);
253
+ --search-ai-conversation-input-send-button-padding: 5px;
254
+ --search-ai-conversation-input-send-button-border-radius: var(--border-radius);
247
255
  --search-ai-conversation-input-send-button-border-disabled: 1px solid var(--button-border-color-disabled);
248
256
 
249
- /**
250
- * @tokens AI Search Response
251
- */
252
- --search-ai-response-padding: var(--spacing-lg);
253
- --search-ai-response-gap: var(--spacing-sm);
254
- --search-ai-response-header-gap: var(--spacing-md);
255
- --search-ai-response-body-gap: var(--spacing-xl);
256
- --search-ai-response-body-padding: 0 40px;
257
-
258
- --search-ai-text-color: var(--text-color-secondary);
259
- --search-ai-text-font-size: var(--font-size-lg);
260
- --search-ai-text-line-height: var(--line-height-lg);
261
-
262
- --search-ai-thinking-text-margin: var(--md-pre-margin) 0;
263
-
264
- --search-ai-question-font-size: var(--font-size-xl);
265
- --search-ai-question-font-weight: var(--font-weight-semibold);
266
- --search-ai-question-line-height: var(--line-height-xl);
267
- --search-ai-question-text-color: var(--text-color-primary);
268
-
269
- --search-ai-resources-gap: var(--spacing-base);
270
- --search-ai-resources-title-font-weight: var(--font-weight-medium);
271
- --search-ai-resources-title-font-size: var(--font-size-lg);
272
- --search-ai-resources-title-line-height: var(--line-height-lg);
273
-
274
- --search-ai-resource-tags-gap: var(--spacing-base);
275
- --search-ai-resource-tag-text-color: var(--text-color-secondary);
276
- --search-ai-resource-tag-icon-color: var(--text-color-secondary);
277
-
278
- --search-ai-suggestions-gap: var(--spacing-sm);
279
- --search-ai-suggestions-margin-left: var(--spacing-xs);
280
- --search-ai-suggestion-item-gap: var(--spacing-xs);
281
-
282
- --search-ai-suggestions-title-text-color: var(--text-color-description);
283
- --search-ai-suggestions-title-font-size: var(--font-size-base);
284
- --search-ai-suggestions-title-line-height: var(--line-height-base);
285
- --search-ai-suggestions-title-font-weight: var(--font-weight-light);
286
-
287
- --search-ai-spinner-icon-color: var(--icon-color-interactive);
288
- --search-ai-checkmark-icon-color: var(--icon-color-interactive);
257
+ --search-ai-conversation-input-min-height: 48px;
258
+ --search-ai-conversation-input-max-height: 120px;
259
+ --search-ai-conversation-input-padding-right: calc(var(--search-ai-conversation-input-send-button-right) + 30px);
260
+ --search-ai-conversation-input-send-button-bottom: calc(var(--search-ai-conversation-input-min-height) / 2);
289
261
 
290
262
  // @tokens End
291
263
  `;
@@ -2,18 +2,10 @@ import React, { forwardRef } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
 
4
4
  import type { ForwardedRef, ReactElement } from 'react';
5
- import type { SelectOption } from '@redocly/theme/core/types/select';
5
+ import type { SegmentedProps } from '@redocly/theme/core/types/segmented';
6
6
 
7
7
  import { typedMemo } from '@redocly/theme/core/hoc/typedMemo';
8
8
 
9
- export type SegmentedProps<T> = {
10
- options: SelectOption<T>[];
11
- value: T;
12
- onChange: ({ label, value }: SelectOption<T>) => void;
13
- className?: string;
14
- size?: 'regular' | 'small';
15
- };
16
-
17
9
  function SegmentedComponent<T>(
18
10
  { options, onChange, value, className = '', size = 'regular' }: SegmentedProps<T>,
19
11
  ref?: ForwardedRef<HTMLDivElement>,
@@ -25,17 +17,20 @@ function SegmentedComponent<T>(
25
17
  className={`tag-grey ${size} ${className}`}
26
18
  role="tablist"
27
19
  >
28
- {options.map((opt) => (
29
- <SegmentedItem
30
- key={opt.label}
31
- role="tab"
32
- title={opt.label}
33
- onClick={() => onChange(opt)}
34
- $isActive={value == opt.value}
35
- $size={size}
36
- >
37
- {opt.label}
38
- </SegmentedItem>
20
+ {options.map((opt, index) => (
21
+ <React.Fragment key={index}>
22
+ {opt.divider}
23
+ <SegmentedItem
24
+ key={opt.label}
25
+ role="tab"
26
+ title={opt.label}
27
+ onClick={() => onChange(opt)}
28
+ $isActive={value == opt.value}
29
+ $size={size}
30
+ >
31
+ {opt.element || opt.label}
32
+ </SegmentedItem>
33
+ </React.Fragment>
39
34
  ))}
40
35
  </SegmentedGroup>
41
36
  );
@@ -83,6 +83,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
83
83
  const selectTags = selectedOptions.map((option, index) => (
84
84
  <SelectInputTag
85
85
  closable
86
+ tabIndex={0}
86
87
  key={index}
87
88
  onClose={() => {
88
89
  clearHandler?.(option);
@@ -10,7 +10,7 @@ export const select = css`
10
10
  --select-text-color: var(--text-color-secondary); // @presenter Color
11
11
  --select-border-radius: var(--border-radius); // @presenter BorderRadius
12
12
  --select-border: var(--border-width) var(--border-style) var(--border-color-primary); // @presenter Border
13
-
13
+
14
14
  --select-input-padding-vertical: 6px; // @presenter Spacing
15
15
  --select-input-padding-horizontal: 6px; // @presenter Spacing
16
16
  --select-input-padding: var(--select-input-padding-vertical) var(--select-input-padding-horizontal);
@@ -38,7 +38,7 @@ export const select = css`
38
38
  --select-list-item-border-radius: var(--border-radius); // @presenter BorderRadius
39
39
  --select-list-item-bg-color-active: transparent; // @presenter Color
40
40
  --select-list-item-bg-color-hover: var(--menu-item-bg-color-hover); // @presenter Color
41
- --select-list-item-font-weight-active: var(--font-weight-medium); // @presenter Color
41
+ --select-list-item-font-weight-active: var(--font-weight-medium); // @presenter Color
42
42
 
43
43
  --select-placeholder-color: var(--color-warm-grey-6); // @presenter Color
44
44
  // @tokens End