@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
@@ -43,7 +43,7 @@ const constants_1 = require("../../core/constants");
43
43
  const Button_1 = require("../../components/Button/Button");
44
44
  const SendIcon_1 = require("../../icons/SendIcon/SendIcon");
45
45
  const hooks_1 = require("../../core/hooks");
46
- function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, className, placeholder, }) {
46
+ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, className, placeholder, disabled, multiline = false, }) {
47
47
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
48
48
  const { translate } = useTranslate();
49
49
  const inputRef = (0, react_1.useRef)(null);
@@ -57,17 +57,31 @@ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, classN
57
57
  }
58
58
  }, [isGeneratingResponse]);
59
59
  const handleSendMessage = () => {
60
+ if (disabled)
61
+ return;
60
62
  setQuery('');
61
63
  onMessageSent(query);
62
64
  };
63
- const handleOnKeyUp = (e) => {
65
+ const handleOnKeyDown = (e) => {
64
66
  if (e.key === 'Enter' && !isGeneratingResponse) {
67
+ if (multiline && e.shiftKey) {
68
+ return; // Allow new line in textarea
69
+ }
70
+ e.preventDefault();
65
71
  handleSendMessage();
66
72
  }
67
73
  };
68
- const isDisabled = isGeneratingResponse || query.trim().length === 0;
74
+ const isDisabled = disabled || isGeneratingResponse || query.trim().length === 0;
75
+ const commonProps = {
76
+ placeholder: placeholder || translate('search.ai.followUpQuestion', 'Ask a follow up question?'),
77
+ onChange: (e) => setQuery(e.target.value),
78
+ onKeyDown: handleOnKeyDown,
79
+ value: query,
80
+ disabled: disabled || isGeneratingResponse,
81
+ maxLength: constants_1.AI_SEARCH_MAX_MESSAGE_LENGTH,
82
+ };
69
83
  return (react_1.default.createElement(SearchAiConversationInputWrapper, { "data-component-name": "Search/SearchAiConversationInput", className: className },
70
- 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, maxLength: constants_1.AI_SEARCH_MAX_MESSAGE_LENGTH }),
84
+ multiline ? (react_1.default.createElement(ConversationTextarea, Object.assign({ ref: inputRef }, commonProps))) : (react_1.default.createElement(ConversationInput, Object.assign({ ref: inputRef, type: "text" }, commonProps))),
71
85
  react_1.default.createElement(SendButton, { disabled: isDisabled, size: "small", icon: react_1.default.createElement(SendIcon_1.SendIcon, { color: isDisabled
72
86
  ? '--button-content-color-disabled'
73
87
  : 'var(--search-ai-conversation-input-send-button-icon-color)' }), onClick: handleSendMessage })));
@@ -80,9 +94,10 @@ const SearchAiConversationInputWrapper = styled_components_1.default.div `
80
94
  background-color: var(--search-ai-conversation-input-bg-color);
81
95
  position: relative;
82
96
  `;
83
- const ConversationInput = styled_components_1.default.input `
97
+ const inputStyles = `
84
98
  width: 100%;
85
99
  padding: var(--search-ai-conversation-input-padding);
100
+ padding-right: var(--search-ai-conversation-input-padding-right, 52px);
86
101
  border: var(--search-ai-conversation-input-border);
87
102
  border-radius: var(--search-ai-conversation-input-border-radius);
88
103
  background-color: var(--search-ai-conversation-input-bg-color);
@@ -98,20 +113,37 @@ const ConversationInput = styled_components_1.default.input `
98
113
  border-color: var(--search-ai-conversation-input-border-color-focus);
99
114
  }
100
115
 
116
+ &:disabled {
117
+ background-color: var(--search-ai-conversation-input-bg-color-disabled);
118
+ }
119
+
101
120
  &:focus:disabled {
102
121
  border-color: var(--search-ai-conversation-input-border-color-disabled);
103
122
  }
104
123
  `;
124
+ const ConversationInput = styled_components_1.default.input `
125
+ ${inputStyles}
126
+ height: var(--search-ai-conversation-input-min-height);
127
+ `;
128
+ const ConversationTextarea = styled_components_1.default.textarea `
129
+ ${inputStyles}
130
+ min-height: var(--search-ai-conversation-input-min-height);
131
+ max-height: var(--search-ai-conversation-input-max-height);
132
+ resize: none;
133
+ field-sizing: content;
134
+ `;
105
135
  const SendButton = (0, styled_components_1.default)(Button_1.Button) `
106
136
  position: absolute;
107
137
  right: var(--search-ai-conversation-input-send-button-right);
108
- top: 50%;
109
- transform: translateY(-50%);
138
+ bottom: var(--search-ai-conversation-input-send-button-bottom);
139
+ transform: translateY(50%);
110
140
  transition: background-color 0.2s ease;
111
141
  background-color: var(--search-ai-conversation-input-send-button-bg-color);
112
142
  display: flex;
113
143
  align-items: center;
114
144
  justify-content: center;
145
+ border-radius: var(--search-ai-conversation-input-send-button-border-radius);
146
+ padding: var(--search-ai-conversation-input-send-button-padding);
115
147
 
116
148
  &:hover {
117
149
  background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
@@ -1,19 +1,16 @@
1
1
  import React from 'react';
2
2
  import type { JSX } from 'react';
3
+ import type { AiSearchConversationItem, SearchAiMessageResource } from '../../core/types';
3
4
  import { AiSearchError } from '../../core/constants';
4
- import { AiSearchConversationItem } from '../../core/types';
5
5
  export type SearchAiDialogProps = {
6
6
  response: string | undefined;
7
7
  isGeneratingResponse: boolean;
8
8
  error: AiSearchError | null;
9
- resources: {
10
- url: string;
11
- title: string;
12
- }[];
9
+ resources: SearchAiMessageResource[];
13
10
  initialMessage?: string;
14
11
  className?: string;
15
12
  conversation: AiSearchConversationItem[];
16
13
  setConversation: React.Dispatch<React.SetStateAction<AiSearchConversationItem[]>>;
17
- onMessageSent: (message: string, history?: AiSearchConversationItem[]) => void;
14
+ onMessageSent: (message: string, history?: AiSearchConversationItem[], messageId?: string) => void;
18
15
  };
19
16
  export declare function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error, resources, onMessageSent, className, conversation, setConversation, }: SearchAiDialogProps): JSX.Element;
@@ -57,11 +57,11 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
57
57
  : conversation.length > 0
58
58
  ? translate('search.ai.followUpQuestion', 'Ask a follow up question?')
59
59
  : translate('search.ai.placeholder', 'Ask a question...');
60
- const scrollToBottom = () => {
60
+ const scrollToBottom = (0, react_1.useCallback)(() => {
61
61
  var _a;
62
62
  (_a = conversationEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'end' });
63
- };
64
- const handleOnMessageSent = (message) => {
63
+ }, []);
64
+ const handleOnMessageSent = (0, react_1.useCallback)((message) => {
65
65
  if (!message.trim()) {
66
66
  return;
67
67
  }
@@ -70,8 +70,11 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
70
70
  content,
71
71
  }));
72
72
  onMessageSent(message, mappedHistory);
73
- setConversation((prev) => [...prev, { role: constants_1.AiSearchConversationRole.USER, content: message }]);
74
- };
73
+ setConversation((prev) => [
74
+ ...prev,
75
+ { role: constants_1.AiSearchConversationRole.USER, content: message },
76
+ ]);
77
+ }, [conversation, onMessageSent, setConversation]);
75
78
  (0, react_1.useEffect)(() => {
76
79
  if (!(initialMessage === null || initialMessage === void 0 ? void 0 : initialMessage.trim().length)) {
77
80
  return;
@@ -91,10 +94,15 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
91
94
  if (lastMessage && lastMessage.role === constants_1.AiSearchConversationRole.ASSISTANT) {
92
95
  return [
93
96
  ...prev.slice(0, -1),
94
- { role: constants_1.AiSearchConversationRole.ASSISTANT, content, resources },
97
+ {
98
+ role: constants_1.AiSearchConversationRole.ASSISTANT,
99
+ content,
100
+ resources,
101
+ messageId: lastMessage.messageId,
102
+ },
95
103
  ];
96
104
  }
97
- return [...prev, { role: constants_1.AiSearchConversationRole.ASSISTANT, content }];
105
+ return [...prev, { role: constants_1.AiSearchConversationRole.ASSISTANT, content, resources }];
98
106
  });
99
107
  }, [response, conversation.length, error, resources, setConversation]);
100
108
  (0, react_1.useEffect)(() => {
@@ -104,7 +112,10 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
104
112
  }, [error, setConversation]);
105
113
  (0, react_1.useEffect)(() => {
106
114
  scrollToBottom();
107
- }, [conversation, isGeneratingResponse]);
115
+ }, [conversation, isGeneratingResponse, scrollToBottom]);
116
+ const handleFeedbackChange = (0, react_1.useCallback)((messageId, feedback) => {
117
+ setConversation((prev) => prev.map((item) => (item.messageId === messageId ? Object.assign(Object.assign({}, item), { feedback }) : item)));
118
+ }, [setConversation]);
108
119
  return (react_1.default.createElement(SearchAiDialogWrapper, { "data-component-name": "Search/SearchAiDialog", className: className },
109
120
  !conversation.length && (react_1.default.createElement(WelcomeWrapper, null,
110
121
  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" }),
@@ -112,7 +123,7 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
112
123
  react_1.default.createElement(ConversationWrapper, null,
113
124
  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 &&
114
125
  isGeneratingResponse &&
115
- index === conversation.length - 1, resources: item.resources }))),
126
+ index === conversation.length - 1, resources: item.resources, messageId: item.messageId, feedback: item.feedback, onFeedbackChange: handleFeedbackChange }))),
116
127
  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))),
117
128
  !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))))),
118
129
  react_1.default.createElement("div", { ref: conversationEndRef })),
@@ -1,13 +1,17 @@
1
+ import React from 'react';
1
2
  import type { JSX } from 'react';
3
+ import { FeedbackType, type SearchAiMessageResource } from '../../core/types';
2
4
  import { AiSearchConversationRole } from '../../core/constants';
3
5
  export type SearchAiMessageProps = {
4
6
  role: AiSearchConversationRole;
5
7
  content: string;
6
8
  isThinking?: boolean;
7
- resources?: {
8
- url: string;
9
- title: string;
10
- }[];
9
+ resources?: SearchAiMessageResource[];
11
10
  className?: string;
11
+ messageId?: string;
12
+ feedback?: FeedbackType;
13
+ onFeedbackChange: (messageId: string, feedback: FeedbackType | undefined) => void;
12
14
  };
13
- export declare function SearchAiMessage({ role, content, isThinking, resources, className, }: SearchAiMessageProps): JSX.Element;
15
+ declare function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, }: SearchAiMessageProps): JSX.Element;
16
+ export declare const SearchAiMessage: React.MemoExoticComponent<typeof SearchAiMessageComponent>;
17
+ export {};
@@ -1,11 +1,45 @@
1
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 () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SearchAiMessage = SearchAiMessage;
7
- const react_1 = __importDefault(require("react"));
39
+ exports.SearchAiMessage = void 0;
40
+ const react_1 = __importStar(require("react"));
8
41
  const styled_components_1 = __importDefault(require("styled-components"));
42
+ const types_1 = require("../../core/types");
9
43
  const Link_1 = require("../../components/Link/Link");
10
44
  const Tag_1 = require("../../components/Tag/Tag");
11
45
  const constants_1 = require("../../core/constants");
@@ -13,29 +47,91 @@ const hooks_1 = require("../../core/hooks");
13
47
  const Markdown_1 = require("../../components/Markdown/Markdown");
14
48
  const DocumentIcon_1 = require("../../icons/DocumentIcon/DocumentIcon");
15
49
  const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
16
- function SearchAiMessage({ role, content, isThinking, resources, className, }) {
17
- const { useMarkdownText, useTranslate } = (0, hooks_1.useThemeHooks)();
50
+ const CheckmarkOutlineIcon_1 = require("../../icons/CheckmarkOutlineIcon/CheckmarkOutlineIcon");
51
+ const SearchAiActionButtons_1 = require("./SearchAiActionButtons");
52
+ const SearchAiNegativeFeedbackForm_1 = require("./SearchAiNegativeFeedbackForm");
53
+ function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, }) {
54
+ var _a;
55
+ const { useMarkdownText, useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
18
56
  const markDownContent = useMarkdownText(content || '');
19
57
  const { translate } = useTranslate();
58
+ const telemetry = useTelemetry();
59
+ const [feedbackSent, setFeedbackSent] = (0, react_1.useState)(false);
60
+ const hasResources = !isThinking && resources && resources.length > 0;
61
+ const resourcesCount = (_a = resources === null || resources === void 0 ? void 0 : resources.length) !== null && _a !== void 0 ? _a : 0;
62
+ const showSuccessMessage = feedbackSent && feedback;
63
+ const sendFeedbackTelemetry = (feedbackValue, dislikeReason) => {
64
+ if (!messageId)
65
+ return;
66
+ try {
67
+ telemetry.sendSearchAIFeedbackMessage({
68
+ feedback: feedbackValue,
69
+ messageId,
70
+ reason: dislikeReason,
71
+ });
72
+ }
73
+ catch (error) {
74
+ console.error('Error sending feedback', error);
75
+ }
76
+ };
77
+ const handleFeedbackClick = (feedbackValue, reason) => {
78
+ if (!messageId) {
79
+ return;
80
+ }
81
+ if (!reason) {
82
+ onFeedbackChange(messageId, feedbackValue);
83
+ }
84
+ sendFeedbackTelemetry(feedbackValue, reason);
85
+ if (feedbackValue === types_1.FeedbackType.Like || reason) {
86
+ setFeedbackSent(true);
87
+ }
88
+ };
20
89
  return (react_1.default.createElement(SearchAiMessageWrapper, { "data-component-name": "Search/SearchAiMessage", role: role, className: className, "data-testid": "search-ai-message" },
21
90
  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, "data-testid": "response-text" }),
25
- !isThinking && resources && resources.length > 0 && (react_1.default.createElement(ResourcesWrapper, { "data-testid": "resources-wrapper" },
26
- react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
27
- translate('search.ai.resourcesFound.basedOn', 'Based on'),
28
- " ",
29
- resources.length,
30
- ' ',
31
- translate('search.ai.resourcesFound.resources', 'resources')),
32
- react_1.default.createElement(ResourceTagsWrapper, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
33
- 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),
34
- isThinking && content.length === 0 && (react_1.default.createElement(ThinkingDotsWrapper, { "data-testid": "thinking-dots-wrapper" },
35
- react_1.default.createElement(ThinkingDot, null),
36
- react_1.default.createElement(ThinkingDot, null),
37
- react_1.default.createElement(ThinkingDot, null))))));
91
+ react_1.default.createElement(MessageContentWrapper, null,
92
+ react_1.default.createElement(MessageWrapper, { role: role },
93
+ role === constants_1.AiSearchConversationRole.ASSISTANT ? (react_1.default.createElement(react_1.default.Fragment, null,
94
+ react_1.default.createElement(ResponseText, { as: "div", children: markDownContent, "data-testid": "response-text" }),
95
+ hasResources && (react_1.default.createElement(react_1.default.Fragment, null,
96
+ react_1.default.createElement(ResourcesWrapper, { "data-testid": "resources-wrapper" },
97
+ react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
98
+ translate('search.ai.resourcesFound.basedOn', 'Based on'),
99
+ " ",
100
+ resourcesCount,
101
+ ' ',
102
+ translate('search.ai.resourcesFound.resources', 'resources')),
103
+ react_1.default.createElement(ResourceTagsWrapper, null, resources === null || resources === void 0 ? void 0 : resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: `${resource.url}-${idx}`, to: resource.url, target: "_blank" },
104
+ react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title)))))),
105
+ react_1.default.createElement(FeedbackWrapper, null,
106
+ react_1.default.createElement(SearchAiActionButtons_1.SearchAiActionButtons, { content: content, feedback: feedback, onFeedback: handleFeedbackClick, disabled: feedbackSent })))))) : (content),
107
+ isThinking && content.length === 0 && (react_1.default.createElement(ThinkingDotsWrapper, { "data-testid": "thinking-dots-wrapper" },
108
+ react_1.default.createElement(ThinkingDot, null),
109
+ react_1.default.createElement(ThinkingDot, null),
110
+ react_1.default.createElement(ThinkingDot, null)))),
111
+ messageId && feedback === types_1.FeedbackType.Dislike && !showSuccessMessage && (react_1.default.createElement(SearchAiNegativeFeedbackForm_1.SearchAiNegativeFeedbackForm, { messageId: messageId, onClose: onFeedbackChange, onSubmit: (reason) => handleFeedbackClick(types_1.FeedbackType.Dislike, reason) })),
112
+ showSuccessMessage && (react_1.default.createElement(SuccessMessageWrapper, { "data-component-name": "Search/SearchAiMessage/Success" },
113
+ react_1.default.createElement(CheckmarkOutlineIcon_1.CheckmarkOutlineIcon, { size: "20px", color: "var(--color-success-base)" }),
114
+ react_1.default.createElement(SuccessMessageText, null, translate('search.ai.feedback.thanks', 'Thank you for your feedback!')))))));
115
+ }
116
+ function areResourcesEqual(prev, next) {
117
+ if (prev === next)
118
+ return true;
119
+ if (!prev || !next || prev.length !== next.length)
120
+ return false;
121
+ return prev.every((resource, index) => {
122
+ const nextResource = next[index];
123
+ return resource.url === nextResource.url && resource.title === nextResource.title;
124
+ });
38
125
  }
126
+ exports.SearchAiMessage = (0, react_1.memo)(SearchAiMessageComponent, (prevProps, nextProps) => {
127
+ return (prevProps.role === nextProps.role &&
128
+ prevProps.content === nextProps.content &&
129
+ prevProps.isThinking === nextProps.isThinking &&
130
+ prevProps.messageId === nextProps.messageId &&
131
+ prevProps.feedback === nextProps.feedback &&
132
+ prevProps.onFeedbackChange === nextProps.onFeedbackChange &&
133
+ areResourcesEqual(prevProps.resources, nextProps.resources));
134
+ });
39
135
  const SearchAiMessageWrapper = styled_components_1.default.div `
40
136
  display: flex;
41
137
  flex-direction: row;
@@ -43,6 +139,13 @@ const SearchAiMessageWrapper = styled_components_1.default.div `
43
139
  width: 100%;
44
140
  justify-content: ${({ role }) => role === constants_1.AiSearchConversationRole.USER ? 'flex-end' : 'flex-start'};
45
141
  `;
142
+ const MessageContentWrapper = styled_components_1.default.div `
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: var(--spacing-sm);
146
+ max-width: 80%;
147
+ min-width: 0;
148
+ `;
46
149
  const ResponseText = (0, styled_components_1.default)(Markdown_1.Markdown) `
47
150
  color: var(--search-ai-text-color);
48
151
  font-size: var(--search-ai-text-font-size);
@@ -63,7 +166,8 @@ const MessageWrapper = styled_components_1.default.div `
63
166
  padding: ${({ role }) => role === constants_1.AiSearchConversationRole.USER ? 'var(--spacing-sm)' : 'var(--spacing-xs)'}
64
167
  var(--spacing-sm);
65
168
  border-radius: var(--border-radius-lg);
66
- max-width: 80%;
169
+ width: fit-content;
170
+ max-width: 100%;
67
171
  word-wrap: break-word;
68
172
  white-space: pre-wrap;
69
173
  background-color: ${({ role }) => role === constants_1.AiSearchConversationRole.USER
@@ -78,7 +182,13 @@ const ResourcesWrapper = styled_components_1.default.div `
78
182
  gap: var(--search-ai-resources-gap);
79
183
  display: flex;
80
184
  flex-direction: column;
81
- margin: var(--spacing-xs) 0;
185
+ margin: 0;
186
+ `;
187
+ const FeedbackWrapper = styled_components_1.default.div `
188
+ display: flex;
189
+ flex-direction: row;
190
+ gap: var(--search-ai-feedback-gap);
191
+ margin-top: var(--spacing-sm);
82
192
  `;
83
193
  const ResourcesTitle = styled_components_1.default.div `
84
194
  font-weight: var(--search-ai-resources-title-font-weight);
@@ -144,4 +254,18 @@ const ThinkingDot = styled_components_1.default.div `
144
254
  }
145
255
  }
146
256
  `;
257
+ const SuccessMessageWrapper = styled_components_1.default.div `
258
+ max-width: fit-content;
259
+ display: flex;
260
+ align-items: center;
261
+ gap: var(--spacing-sm);
262
+ padding: var(--spacing-sm);
263
+ background: var(--color-success-bg);
264
+ border: 1px solid var(--color-success-border);
265
+ border-radius: var(--border-radius-lg);
266
+ `;
267
+ const SuccessMessageText = styled_components_1.default.div `
268
+ font-size: var(--font-size-base);
269
+ color: var(--color-success-darker);
270
+ `;
147
271
  //# sourceMappingURL=SearchAiMessage.js.map
@@ -0,0 +1,8 @@
1
+ import type { JSX } from 'react';
2
+ import type { FeedbackType } from '../../core/types';
3
+ export type SearchAiNegativeFeedbackFormProps = {
4
+ messageId: string;
5
+ onClose: (messageId: string, feedback: FeedbackType | undefined) => void;
6
+ onSubmit: (reason: string) => void;
7
+ };
8
+ export declare function SearchAiNegativeFeedbackForm({ messageId, onClose, onSubmit, }: SearchAiNegativeFeedbackFormProps): JSX.Element;
@@ -0,0 +1,169 @@
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 () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SearchAiNegativeFeedbackForm = SearchAiNegativeFeedbackForm;
40
+ const react_1 = __importStar(require("react"));
41
+ const styled_components_1 = __importDefault(require("styled-components"));
42
+ const Button_1 = require("../../components/Button/Button");
43
+ const CloseIcon_1 = require("../../icons/CloseIcon/CloseIcon");
44
+ const ArrowLeftIcon_1 = require("../../icons/ArrowLeftIcon/ArrowLeftIcon");
45
+ const SendIcon_1 = require("../../icons/SendIcon/SendIcon");
46
+ const hooks_1 = require("../../core/hooks");
47
+ const NEGATIVE_FEEDBACK_DEFAULT_REASONS = [
48
+ "Didn't answer my question",
49
+ "Couldn't find what I was looking for",
50
+ 'Wrong topic',
51
+ 'Partially helpful, but missing details',
52
+ ];
53
+ function SearchAiNegativeFeedbackForm({ messageId, onClose, onSubmit, }) {
54
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
55
+ const { translate } = useTranslate();
56
+ const [showMoreInput, setShowMoreInput] = (0, react_1.useState)(false);
57
+ const [detailedFeedback, setDetailedFeedback] = (0, react_1.useState)('');
58
+ const textAreaRef = react_1.default.useRef(null);
59
+ const adjustTextAreaHeight = () => {
60
+ const textArea = textAreaRef.current;
61
+ if (textArea) {
62
+ textArea.style.height = 'auto';
63
+ textArea.style.height = `${textArea.scrollHeight}px`;
64
+ }
65
+ };
66
+ const handleTextChange = (e) => {
67
+ setDetailedFeedback(e.target.value);
68
+ adjustTextAreaHeight();
69
+ };
70
+ (0, react_1.useEffect)(() => {
71
+ if (showMoreInput) {
72
+ adjustTextAreaHeight();
73
+ }
74
+ }, [showMoreInput]);
75
+ return (react_1.default.createElement(FeedbackFormWrapper, { "data-component-name": "Search/SearchAiNegativeFeedbackForm" },
76
+ react_1.default.createElement(FeedbackHeader, null,
77
+ showMoreInput ? (react_1.default.createElement(BackButton, { variant: "text", size: "small", icon: react_1.default.createElement(ArrowLeftIcon_1.ArrowLeftIcon, null), onClick: () => setShowMoreInput(false), "aria-label": "Back to feedback reasons" })) : null,
78
+ react_1.default.createElement(FeedbackTitle, { "data-translation-key": "search.ai.feedback.title" }, translate('search.ai.feedback.title', "What didn't you like about this response?")),
79
+ react_1.default.createElement(CloseButton, { variant: "text", size: "small", icon: react_1.default.createElement(CloseIcon_1.CloseIcon, null), onClick: () => onClose(messageId, undefined), "aria-label": "Close feedback form" })),
80
+ !showMoreInput ? (react_1.default.createElement(FeedbackReasonsWrapper, null,
81
+ NEGATIVE_FEEDBACK_DEFAULT_REASONS.map((reason) => (react_1.default.createElement(Button_1.Button, { key: reason, variant: "outlined", size: "small", onClick: () => onSubmit(reason) }, reason))),
82
+ react_1.default.createElement(Button_1.Button, { variant: "outlined", size: "small", onClick: () => setShowMoreInput(true) }, "More..."))) : (react_1.default.createElement(FeedbackInputWrapper, null,
83
+ react_1.default.createElement(FeedbackTextArea, { ref: textAreaRef, placeholder: translate('search.ai.feedback.detailsPlaceholder', 'Add specific details'), value: detailedFeedback, onChange: handleTextChange, rows: 1, autoFocus: true }),
84
+ react_1.default.createElement(SendButton, { size: "small", icon: react_1.default.createElement(SendIcon_1.SendIcon, { color: !detailedFeedback.trim()
85
+ ? '--button-content-color-disabled'
86
+ : 'var(--search-ai-conversation-input-send-button-icon-color)' }), onClick: () => onSubmit(detailedFeedback), disabled: !detailedFeedback.trim(), "aria-label": "Send feedback" })))));
87
+ }
88
+ const FeedbackFormWrapper = styled_components_1.default.div `
89
+ display: flex;
90
+ flex-direction: column;
91
+ gap: var(--spacing-sm);
92
+ padding: var(--spacing-xs);
93
+ background: var(--search-ai-feedback-form-bg-color);
94
+ border: 1px solid var(--search-ai-feedback-form-border-color);
95
+ border-radius: var(--border-radius-lg);
96
+ `;
97
+ const FeedbackHeader = styled_components_1.default.div `
98
+ display: flex;
99
+ justify-content: space-between;
100
+ align-items: center;
101
+ gap: var(--spacing-sm);
102
+ `;
103
+ const FeedbackTitle = styled_components_1.default.div `
104
+ font-size: var(--font-size-base);
105
+ color: var(--text-color);
106
+ flex: 1;
107
+ `;
108
+ const BackButton = (0, styled_components_1.default)(Button_1.Button) `
109
+ flex-shrink: 0;
110
+ `;
111
+ const CloseButton = (0, styled_components_1.default)(Button_1.Button) `
112
+ flex-shrink: 0;
113
+ `;
114
+ const FeedbackReasonsWrapper = styled_components_1.default.div `
115
+ display: flex;
116
+ flex-wrap: wrap;
117
+ gap: var(--spacing-xs);
118
+ `;
119
+ const FeedbackInputWrapper = styled_components_1.default.div `
120
+ position: relative;
121
+ width: 100%;
122
+ `;
123
+ const FeedbackTextArea = styled_components_1.default.textarea `
124
+ width: 100%;
125
+ min-height: 5rem;
126
+ max-height: 12.5rem;
127
+ padding: var(--spacing-xs);
128
+ padding-right: 3rem;
129
+ border: 1px solid var(--border-color-primary);
130
+ border-radius: var(--border-radius-md);
131
+ font-family: inherit;
132
+ font-size: var(--font-size-base);
133
+ line-height: var(--line-height-base);
134
+ background: var(--background-color);
135
+ color: var(--text-color);
136
+ resize: none;
137
+ overflow-y: auto;
138
+
139
+ &:focus {
140
+ outline: 1px solid var(--button-border-color-focused);
141
+ border-color: var(--button-border-color-focused);
142
+ }
143
+
144
+ &::placeholder {
145
+ color: var(--text-color-helper);
146
+ }
147
+ `;
148
+ const SendButton = (0, styled_components_1.default)(Button_1.Button) `
149
+ position: absolute;
150
+ right: var(--search-ai-conversation-input-send-button-right);
151
+ bottom: var(--spacing-sm);
152
+ transition: background-color 0.2s ease;
153
+ background-color: var(--search-ai-conversation-input-send-button-bg-color);
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ border-radius: var(--search-ai-conversation-input-send-button-border-radius);
158
+ padding: var(--search-ai-conversation-input-send-button-padding);
159
+
160
+ &:hover {
161
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
162
+ }
163
+
164
+ &:disabled {
165
+ background-color: var(--search-ai-conversation-input-send-button-bg-color-disabled);
166
+ border: var(--search-ai-conversation-input-send-button-border-disabled);
167
+ }
168
+ `;
169
+ //# sourceMappingURL=SearchAiNegativeFeedbackForm.js.map