@redocly/theme 0.59.0-next.7 → 0.59.0-next.8

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 (41) hide show
  1. package/lib/components/Search/SearchAiActionButtons.d.ts +10 -0
  2. package/lib/components/Search/SearchAiActionButtons.js +43 -0
  3. package/lib/components/Search/SearchAiDialog.d.ts +3 -6
  4. package/lib/components/Search/SearchAiDialog.js +20 -9
  5. package/lib/components/Search/SearchAiMessage.d.ts +9 -5
  6. package/lib/components/Search/SearchAiMessage.js +146 -22
  7. package/lib/components/Search/SearchAiNegativeFeedbackForm.d.ts +8 -0
  8. package/lib/components/Search/SearchAiNegativeFeedbackForm.js +169 -0
  9. package/lib/components/Search/variables.js +29 -66
  10. package/lib/core/hooks/index.d.ts +1 -0
  11. package/lib/core/hooks/index.js +1 -0
  12. package/lib/core/hooks/search/use-feedback-tooltip.d.ts +6 -0
  13. package/lib/core/hooks/search/use-feedback-tooltip.js +26 -0
  14. package/lib/core/hooks/use-telemetry-fallback.d.ts +1 -0
  15. package/lib/core/hooks/use-telemetry-fallback.js +1 -0
  16. package/lib/core/types/l10n.d.ts +1 -1
  17. package/lib/core/types/search.d.ts +11 -4
  18. package/lib/core/types/search.js +6 -0
  19. package/lib/core/utils/frontmatter-translate.d.ts +6 -0
  20. package/lib/core/utils/frontmatter-translate.js +14 -0
  21. package/lib/core/utils/index.d.ts +1 -0
  22. package/lib/core/utils/index.js +1 -0
  23. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.d.ts +9 -0
  24. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.js +34 -0
  25. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.d.ts +9 -0
  26. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.js +34 -0
  27. package/package.json +2 -2
  28. package/src/components/Search/SearchAiActionButtons.tsx +76 -0
  29. package/src/components/Search/SearchAiDialog.tsx +52 -23
  30. package/src/components/Search/SearchAiMessage.tsx +172 -43
  31. package/src/components/Search/SearchAiNegativeFeedbackForm.tsx +210 -0
  32. package/src/components/Search/variables.ts +29 -66
  33. package/src/core/hooks/index.ts +1 -0
  34. package/src/core/hooks/search/use-feedback-tooltip.ts +32 -0
  35. package/src/core/hooks/use-telemetry-fallback.ts +1 -0
  36. package/src/core/types/l10n.ts +3 -0
  37. package/src/core/types/search.ts +13 -4
  38. package/src/core/utils/frontmatter-translate.ts +9 -0
  39. package/src/core/utils/index.ts +1 -0
  40. package/src/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.tsx +38 -0
  41. package/src/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.tsx +35 -0
@@ -144,27 +144,24 @@ 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);
@@ -173,40 +170,50 @@ export const search = css`
173
170
  --search-ai-assistant-border: 1px solid var(--border-color-primary);
174
171
  --search-ai-assistant-message-max-width: 80%;
175
172
 
176
- --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);
177
179
  --search-ai-resources-title-font-weight: var(--font-weight-medium);
178
180
  --search-ai-resources-title-font-size: var(--font-size-lg);
179
181
  --search-ai-resources-title-line-height: var(--line-height-lg);
180
182
 
181
- --search-ai-resource-tags-gap: var(--spacing-base);
183
+ --search-ai-resource-tags-gap: var(--spacing-xxs);
182
184
  --search-ai-resource-tag-text-color: var(--text-color-secondary);
183
185
  --search-ai-resource-tag-icon-color: var(--text-color-secondary);
184
186
  --search-ai-resource-tag-icon-size: 16px;
185
187
 
186
- --search-ai-icon-size: 32px;
187
- --search-ai-icon-bg-color: var(--search-ai-gradient);
188
- --search-ai-icon-color: var(--color-static-white);
189
-
190
- --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);
191
196
 
192
197
  --search-ai-thinking-dots-gap: 4px;
193
198
  --search-ai-thinking-dots-padding: 4px 0;
194
199
  --search-ai-thinking-dot-size: 6px;
195
200
  --search-ai-thinking-dot-color: var(--search-ai-gradient);
196
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
+
197
210
  --search-ai-disclaimer-font-size: var(--font-size-sm);
198
211
  --search-ai-disclaimer-line-height: var(--line-height-sm);
199
212
  --search-ai-disclaimer-text-color: var(--text-color-secondary);
200
213
 
201
-
202
214
  --search-ai-welcome-margin: var(--spacing-md);
203
- --search-ai-icon-wrapper-padding: var(--spacing-xs);
204
215
 
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);
216
+ --search-ai-button-icon-color: none;
210
217
 
211
218
  --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
212
219
  --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
@@ -244,52 +251,8 @@ export const search = css`
244
251
 
245
252
  --search-ai-conversation-input-send-button-right: 12px;
246
253
  --search-ai-conversation-input-send-button-padding: 5px;
247
- --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
248
- --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
249
- --search-ai-conversation-input-send-button-bg-color-disabled: var(--button-bg-color-disabled);
250
- --search-ai-conversation-input-send-button-border-disabled: 1px solid var(--button-border-color-disabled);
251
254
  --search-ai-conversation-input-send-button-border-radius: var(--border-radius);
252
-
253
- /**
254
- * @tokens AI Search Response
255
- */
256
- --search-ai-response-padding: var(--spacing-lg);
257
- --search-ai-response-gap: var(--spacing-sm);
258
- --search-ai-response-header-gap: var(--spacing-md);
259
- --search-ai-response-body-gap: var(--spacing-xl);
260
- --search-ai-response-body-padding: 0 40px;
261
-
262
- --search-ai-text-color: var(--text-color-secondary);
263
- --search-ai-text-font-size: var(--font-size-lg);
264
- --search-ai-text-line-height: var(--line-height-lg);
265
-
266
- --search-ai-thinking-text-margin: var(--md-pre-margin) 0;
267
-
268
- --search-ai-question-font-size: var(--font-size-xl);
269
- --search-ai-question-font-weight: var(--font-weight-semibold);
270
- --search-ai-question-line-height: var(--line-height-xl);
271
- --search-ai-question-text-color: var(--text-color-primary);
272
-
273
- --search-ai-resources-gap: var(--spacing-base);
274
- --search-ai-resources-title-font-weight: var(--font-weight-medium);
275
- --search-ai-resources-title-font-size: var(--font-size-lg);
276
- --search-ai-resources-title-line-height: var(--line-height-lg);
277
-
278
- --search-ai-resource-tags-gap: var(--spacing-base);
279
- --search-ai-resource-tag-text-color: var(--text-color-secondary);
280
- --search-ai-resource-tag-icon-color: var(--text-color-secondary);
281
-
282
- --search-ai-suggestions-gap: var(--spacing-sm);
283
- --search-ai-suggestions-margin-left: var(--spacing-xs);
284
- --search-ai-suggestion-item-gap: var(--spacing-xxs);
285
-
286
- --search-ai-suggestions-title-text-color: var(--text-color-description);
287
- --search-ai-suggestions-title-font-size: var(--font-size-base);
288
- --search-ai-suggestions-title-line-height: var(--line-height-base);
289
- --search-ai-suggestions-title-font-weight: var(--font-weight-light);
290
-
291
- --search-ai-spinner-icon-color: var(--icon-color-interactive);
292
- --search-ai-checkmark-icon-color: var(--icon-color-interactive);
255
+ --search-ai-conversation-input-send-button-border-disabled: 1px solid var(--button-border-color-disabled);
293
256
 
294
257
  // @tokens End
295
258
  `;
@@ -26,6 +26,7 @@ export * from './use-language-picker';
26
26
  export * from './use-product-picker';
27
27
  export * from './search/use-search-dialog';
28
28
  export * from './search/use-search-filter';
29
+ export * from './search/use-feedback-tooltip';
29
30
  export * from './use-controlled-state';
30
31
  export * from './use-codeblock-tabs-controls';
31
32
  export * from './code-walkthrough/use-code-walkthrough';
@@ -0,0 +1,32 @@
1
+ import { useState } from 'react';
2
+
3
+ import { useControl } from '../use-control';
4
+
5
+ type UseFeedbackTooltipReturn = {
6
+ isOpen: boolean;
7
+ showTooltip: () => void;
8
+ };
9
+
10
+ const DEFAULT_TOOLTIP_DURATION = 1500;
11
+
12
+ export function useFeedbackTooltip(): UseFeedbackTooltipReturn {
13
+ const [closeTooltipTimeout, setCloseTooltipTimeout] = useState<NodeJS.Timeout | null>(null);
14
+ const tooltipControl = useControl();
15
+
16
+ const showTooltip = () => {
17
+ tooltipControl.handleOpen();
18
+ if (closeTooltipTimeout) {
19
+ clearTimeout(closeTooltipTimeout);
20
+ }
21
+ const newCloseTooltipTimeout = setTimeout(() => {
22
+ tooltipControl.handleClose();
23
+ setCloseTooltipTimeout(null);
24
+ }, DEFAULT_TOOLTIP_DURATION);
25
+ setCloseTooltipTimeout(newCloseTooltipTimeout);
26
+ };
27
+
28
+ return {
29
+ isOpen: tooltipControl.isOpened,
30
+ showTooltip,
31
+ };
32
+ }
@@ -39,6 +39,7 @@ export const useTelemetryFallback = () => ({
39
39
  sendSearchQueryMessage: () => {},
40
40
  sendSearchAiOpenedMessage: () => {},
41
41
  sendSearchAIQueryMessage: () => {},
42
+ sendSearchAIFeedbackMessage: () => {},
42
43
  sendFeedbackMessage: () => {},
43
44
  sendSearchResultClickedMessage: () => {},
44
45
  sendRedirectMessage: () => {},
@@ -95,6 +95,9 @@ export type TranslationKey =
95
95
  | 'search.ai.resourcesFound'
96
96
  | 'search.ai.resourcesFound.basedOn'
97
97
  | 'search.ai.resourcesFound.resources'
98
+ | 'search.ai.feedback.title'
99
+ | 'search.ai.feedback.detailsPlaceholder'
100
+ | 'search.ai.feedback.thanks'
98
101
  | 'search.ai.button'
99
102
  | 'search.ai.label'
100
103
  | 'search.ai.disclaimer'
@@ -93,11 +93,20 @@ export type AiSearchErrorConfig = {
93
93
  messageDefault: string;
94
94
  };
95
95
 
96
+ export enum FeedbackType {
97
+ Like = 'like',
98
+ Dislike = 'dislike',
99
+ }
100
+
101
+ export type SearchAiMessageResource = {
102
+ url: string;
103
+ title: string;
104
+ };
105
+
96
106
  export type AiSearchConversationItem = {
97
107
  role: AiSearchConversationRole;
98
108
  content: string;
99
- resources?: {
100
- url: string;
101
- title: string;
102
- }[];
109
+ resources?: SearchAiMessageResource[];
110
+ messageId?: string;
111
+ feedback?: FeedbackType;
103
112
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Utility function for use in React frontmatter
3
+ * This function creates a string that will be processed by the frontmatter loader
4
+ * to convert it into a translation key object
5
+ */
6
+ export const frontmatterTranslate = (key: string, defaultValue: string): string => {
7
+ const escapedDefaultValue = defaultValue.replace(/'/g, "\\'");
8
+ return `frontmatterTranslate('${key}', '${escapedDefaultValue}')`;
9
+ };
@@ -38,3 +38,4 @@ export * from './enhanced-smoothstep';
38
38
  export * from './icon-resolver';
39
39
  export * from './dynamic';
40
40
  export * from './tabs';
41
+ export * from './frontmatter-translate';
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { IconProps } from '@redocly/theme/icons/types';
5
+
6
+ import { getCssColorVariable } from '@redocly/theme/core/utils';
7
+
8
+ const Icon = (props: IconProps) => {
9
+ const { color, ...restProps } = props;
10
+
11
+ return (
12
+ <svg
13
+ width="14"
14
+ height="14"
15
+ viewBox="0 0 14 14"
16
+ fill="none"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ {...restProps}
19
+ >
20
+ <path
21
+ d="M3.0625 0.875H0.875V7H3.0625V0.875Z"
22
+ fill={getCssColorVariable(color) || '#DCDDE5'}
23
+ />
24
+ <path
25
+ d="M10.0625 0.875H3.9375V7.35118L5.26846 9.34771L5.63828 11.9362C5.66886 12.1443 5.77308 12.3346 5.932 12.4725C6.09092 12.6103 6.29402 12.6866 6.5044 12.6875H6.5625C6.91048 12.6871 7.2441 12.5487 7.49016 12.3027C7.73621 12.0566 7.87462 11.723 7.875 11.375V8.75H11.375C11.839 8.74948 12.2838 8.56494 12.6119 8.23686C12.9399 7.90879 13.1245 7.46397 13.125 7V3.9375C13.1241 3.12555 12.8012 2.34712 12.227 1.77298C11.6529 1.19885 10.8744 0.875903 10.0625 0.875Z"
26
+ fill={getCssColorVariable(color) || '#DCDDE5'}
27
+ />
28
+ </svg>
29
+ );
30
+ };
31
+
32
+ export const ThumbDownFilledIcon = styled(Icon).attrs(() => ({
33
+ 'data-component-name': 'icons/ThumbDownFilledIcon/ThumbDownFilledIcon',
34
+ }))<IconProps>`
35
+ height: ${({ size }) => size || '14px'};
36
+ width: ${({ size }) => size || '14px'};
37
+ color: ${({ color }) => color && getCssColorVariable(color)};
38
+ `;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { IconProps } from '@redocly/theme/icons/types';
5
+
6
+ import { getCssColorVariable } from '@redocly/theme/core/utils';
7
+
8
+ const Icon = (props: IconProps) => {
9
+ const { color, ...restProps } = props;
10
+
11
+ return (
12
+ <svg
13
+ width="14"
14
+ height="14"
15
+ viewBox="0 0 14 14"
16
+ fill="none"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ {...restProps}
19
+ >
20
+ <path d="M3.0625 7H0.875V13.125H3.0625V7Z" fill={getCssColorVariable(color) || '#DCDDE5'} />
21
+ <path
22
+ d="M10.0625 13.125H3.9375V6.64882L5.26846 4.65229L5.63828 2.06382C5.66886 1.85567 5.77308 1.66539 5.932 1.52753C6.09092 1.38967 6.29402 1.31338 6.5044 1.3125H6.5625C6.91048 1.31288 7.2441 1.45129 7.49016 1.69734C7.73621 1.9434 7.87462 2.27702 7.875 2.625V5.25H11.375C11.839 5.25052 12.2838 5.43506 12.6119 5.76314C12.9399 6.09121 13.1245 6.53603 13.125 7V10.0625C13.1241 10.8744 12.8012 11.6529 12.227 12.227C11.6529 12.8012 10.8744 13.1241 10.0625 13.125Z"
23
+ fill={getCssColorVariable(color) || '#DCDDE5'}
24
+ />
25
+ </svg>
26
+ );
27
+ };
28
+
29
+ export const ThumbUpFilledIcon = styled(Icon).attrs(() => ({
30
+ 'data-component-name': 'icons/ThumbUpFilledIcon/ThumbUpFilledIcon',
31
+ }))<IconProps>`
32
+ height: ${({ size }) => size || '14px'};
33
+ width: ${({ size }) => size || '14px'};
34
+ color: ${({ color }) => color && getCssColorVariable(color)};
35
+ `;