@redocly/theme 0.61.1 → 0.62.0-custom.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 (188) hide show
  1. package/lib/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.d.ts +1 -0
  2. package/lib/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.js +12 -0
  3. package/lib/components/Badge/Badge.d.ts +2 -1
  4. package/lib/components/Badge/Badge.js +24 -2
  5. package/lib/components/Banner/Banner.js +19 -1
  6. package/lib/components/Banner/variables.js +1 -0
  7. package/lib/components/Breadcrumbs/Breadcrumb.js +1 -1
  8. package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +9 -6
  9. package/lib/components/Breadcrumbs/Breadcrumbs.js +24 -15
  10. package/lib/components/Buttons/AIAssistantButton.js +7 -4
  11. package/lib/components/Catalog/CatalogEntities.js +10 -8
  12. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +2 -2
  13. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +1 -1
  14. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.js +2 -2
  15. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.js +13 -11
  16. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +7 -5
  17. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +9 -7
  18. package/lib/components/Catalog/CatalogTableView/CatalogTableViewRow.js +1 -1
  19. package/lib/components/Catalog/CatalogTagsWithTooltip.js +2 -2
  20. package/lib/components/CatalogClassic/CatalogClassicInfoBlock.js +1 -1
  21. package/lib/components/CodeBlock/CodeBlockControls.js +8 -6
  22. package/lib/components/Filter/FilterCheckboxes.js +1 -1
  23. package/lib/components/JsonViewer/JsonViewer.js +2 -2
  24. package/lib/components/JsonViewer/{Helpers.js → helpers.js} +2 -1
  25. package/lib/components/LanguagePicker/LanguagePicker.js +1 -1
  26. package/lib/components/Markdown/Markdown.js +2 -2
  27. package/lib/components/Menu/MenuItem.js +41 -15
  28. package/lib/components/Navbar/NavbarItem.js +1 -1
  29. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.d.ts +1 -0
  30. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.js +12 -0
  31. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.d.ts +1 -0
  32. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.js +6 -0
  33. package/lib/components/PageActions/PageActions.js +25 -8
  34. package/lib/components/Search/SearchAiDialog.d.ts +4 -2
  35. package/lib/components/Search/SearchAiDialog.js +23 -4
  36. package/lib/components/Search/SearchAiMessage.d.ts +4 -2
  37. package/lib/components/Search/SearchAiMessage.js +82 -23
  38. package/lib/components/Search/SearchDialog.js +50 -25
  39. package/lib/components/Select/variables.js +2 -2
  40. package/lib/components/SvgViewer/SvgViewer.d.ts +15 -0
  41. package/lib/components/SvgViewer/SvgViewer.js +312 -0
  42. package/lib/components/SvgViewer/variables.d.ts +1 -0
  43. package/lib/components/SvgViewer/variables.dark.d.ts +1 -0
  44. package/lib/components/SvgViewer/variables.dark.js +8 -0
  45. package/lib/components/SvgViewer/variables.js +17 -0
  46. package/lib/components/Tag/Tag.js +1 -1
  47. package/lib/components/Tag/variables.dark.js +6 -0
  48. package/lib/components/Tag/variables.js +6 -0
  49. package/lib/components/Tooltip/Tooltip.d.ts +2 -3
  50. package/lib/components/Tooltip/Tooltip.js +66 -113
  51. package/lib/components/Tooltip/variables.dark.js +4 -0
  52. package/lib/components/Tooltip/variables.js +3 -3
  53. package/lib/components/UserMenu/LoginButton.d.ts +8 -2
  54. package/lib/components/UserMenu/LoginButton.js +4 -3
  55. package/lib/core/constants/search.d.ts +5 -1
  56. package/lib/core/constants/search.js +24 -1
  57. package/lib/core/hooks/search/use-search-dialog.js +2 -2
  58. package/lib/core/hooks/use-color-switcher.js +3 -1
  59. package/lib/core/hooks/use-mcp-config.js +2 -1
  60. package/lib/core/hooks/use-modal-scroll-lock.js +24 -10
  61. package/lib/core/hooks/use-outside-click.d.ts +3 -1
  62. package/lib/core/hooks/use-outside-click.js +8 -4
  63. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  64. package/lib/core/hooks/use-page-actions.js +44 -11
  65. package/lib/core/hooks/use-product-picker.js +1 -1
  66. package/lib/core/hooks/use-unique-svg-ids.d.ts +6 -0
  67. package/lib/core/hooks/use-unique-svg-ids.js +15 -0
  68. package/lib/core/openapi/index.d.ts +1 -0
  69. package/lib/core/openapi/index.js +3 -1
  70. package/lib/core/styles/dark.js +2 -0
  71. package/lib/core/styles/global.js +31 -15
  72. package/lib/core/types/catalog.d.ts +1 -1
  73. package/lib/core/types/hooks.d.ts +23 -2
  74. package/lib/core/types/l10n.d.ts +1 -1
  75. package/lib/core/types/search.d.ts +24 -0
  76. package/lib/core/types/search.js +9 -1
  77. package/lib/core/utils/content-segments.d.ts +2 -0
  78. package/lib/core/utils/content-segments.js +22 -0
  79. package/lib/core/utils/index.d.ts +1 -0
  80. package/lib/core/utils/index.js +1 -0
  81. package/lib/core/utils/transform-revisions-to-version-history.js +8 -51
  82. package/lib/ext/process-scorecard.d.ts +5 -0
  83. package/lib/ext/process-scorecard.js +11 -0
  84. package/lib/icons/FitToViewIcon/FitToViewIcon.d.ts +9 -0
  85. package/lib/icons/FitToViewIcon/FitToViewIcon.js +25 -0
  86. package/lib/index.d.ts +8 -0
  87. package/lib/index.js +8 -0
  88. package/lib/layouts/DocumentationLayout.js +4 -25
  89. package/lib/layouts/DocumentationLayoutBottom.d.ts +11 -0
  90. package/lib/layouts/DocumentationLayoutBottom.js +28 -0
  91. package/lib/layouts/DocumentationLayoutTop.d.ts +13 -0
  92. package/lib/layouts/DocumentationLayoutTop.js +33 -0
  93. package/lib/layouts/Forbidden.js +22 -18
  94. package/lib/markdoc/components/Cards/Card.js +1 -0
  95. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +1 -1
  96. package/lib/markdoc/components/Heading/Heading.js +40 -2
  97. package/lib/markdoc/components/LoginButton/LoginButton.d.ts +9 -0
  98. package/lib/markdoc/components/LoginButton/LoginButton.js +48 -0
  99. package/lib/markdoc/components/Mermaid/Mermaid.js +70 -2
  100. package/lib/markdoc/components/default.d.ts +1 -0
  101. package/lib/markdoc/components/default.js +1 -0
  102. package/lib/markdoc/default.d.ts +6 -0
  103. package/lib/markdoc/default.js +2 -0
  104. package/lib/markdoc/tags/login-button.d.ts +2 -0
  105. package/lib/markdoc/tags/login-button.js +32 -0
  106. package/package.json +8 -8
  107. package/src/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.tsx +10 -0
  108. package/src/components/Badge/Badge.tsx +18 -2
  109. package/src/components/Banner/Banner.tsx +23 -1
  110. package/src/components/Banner/variables.ts +1 -0
  111. package/src/components/Breadcrumbs/Breadcrumb.tsx +3 -3
  112. package/src/components/Breadcrumbs/BreadcrumbDropdown.tsx +11 -8
  113. package/src/components/Breadcrumbs/Breadcrumbs.tsx +24 -15
  114. package/src/components/Buttons/AIAssistantButton.tsx +7 -4
  115. package/src/components/Catalog/CatalogEntities.tsx +10 -8
  116. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +1 -1
  117. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +1 -2
  118. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.tsx +1 -1
  119. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.tsx +13 -11
  120. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +7 -5
  121. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +9 -7
  122. package/src/components/Catalog/CatalogTableView/CatalogTableViewRow.tsx +1 -2
  123. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +9 -5
  124. package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +3 -1
  125. package/src/components/CodeBlock/CodeBlockControls.tsx +16 -10
  126. package/src/components/Filter/FilterCheckboxes.tsx +1 -1
  127. package/src/components/JsonViewer/JsonViewer.tsx +1 -2
  128. package/src/components/JsonViewer/{Helpers.tsx → helpers.tsx} +1 -0
  129. package/src/components/LanguagePicker/LanguagePicker.tsx +1 -1
  130. package/src/components/Markdown/Markdown.tsx +2 -2
  131. package/src/components/Menu/MenuItem.tsx +61 -16
  132. package/src/components/Navbar/NavbarItem.tsx +3 -1
  133. package/src/components/OpenApiDocs/hooks/AdditionalOverviewInfo.tsx +10 -0
  134. package/src/components/OpenApiDocs/hooks/AfterOpenApiDescription.tsx +2 -0
  135. package/src/components/PageActions/PageActions.tsx +38 -15
  136. package/src/components/Search/SearchAiDialog.tsx +31 -2
  137. package/src/components/Search/SearchAiMessage.tsx +103 -17
  138. package/src/components/Search/SearchDialog.tsx +70 -37
  139. package/src/components/Select/variables.ts +2 -2
  140. package/src/components/SvgViewer/SvgViewer.tsx +405 -0
  141. package/src/components/SvgViewer/variables.dark.ts +5 -0
  142. package/src/components/SvgViewer/variables.ts +14 -0
  143. package/src/components/Tag/Tag.tsx +2 -1
  144. package/src/components/Tag/variables.dark.ts +6 -0
  145. package/src/components/Tag/variables.ts +6 -0
  146. package/src/components/Tooltip/Tooltip.tsx +77 -120
  147. package/src/components/Tooltip/variables.dark.ts +4 -0
  148. package/src/components/Tooltip/variables.ts +3 -3
  149. package/src/components/UserMenu/LoginButton.tsx +23 -8
  150. package/src/core/constants/search.ts +27 -1
  151. package/src/core/hooks/__mocks__/use-theme-hooks.ts +10 -1
  152. package/src/core/hooks/search/use-search-dialog.ts +2 -2
  153. package/src/core/hooks/use-color-switcher.ts +3 -1
  154. package/src/core/hooks/use-mcp-config.ts +2 -1
  155. package/src/core/hooks/use-modal-scroll-lock.ts +29 -10
  156. package/src/core/hooks/use-outside-click.ts +16 -5
  157. package/src/core/hooks/use-page-actions.ts +66 -25
  158. package/src/core/hooks/use-product-picker.ts +1 -1
  159. package/src/core/hooks/use-unique-svg-ids.ts +12 -0
  160. package/src/core/openapi/index.ts +1 -0
  161. package/src/core/styles/dark.ts +2 -0
  162. package/src/core/styles/global.ts +31 -15
  163. package/src/core/types/catalog.ts +1 -1
  164. package/src/core/types/hooks.ts +29 -1
  165. package/src/core/types/l10n.ts +12 -1
  166. package/src/core/types/search.ts +19 -0
  167. package/src/core/utils/content-segments.ts +27 -0
  168. package/src/core/utils/index.ts +1 -0
  169. package/src/core/utils/transform-revisions-to-version-history.ts +8 -80
  170. package/src/ext/process-scorecard.ts +14 -0
  171. package/src/icons/FitToViewIcon/FitToViewIcon.tsx +26 -0
  172. package/src/index.ts +8 -0
  173. package/src/layouts/DocumentationLayout.tsx +4 -30
  174. package/src/layouts/DocumentationLayoutBottom.tsx +42 -0
  175. package/src/layouts/DocumentationLayoutTop.tsx +52 -0
  176. package/src/layouts/Forbidden.tsx +36 -21
  177. package/src/markdoc/components/Cards/Card.tsx +1 -0
  178. package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +1 -1
  179. package/src/markdoc/components/Heading/Heading.tsx +52 -4
  180. package/src/markdoc/components/LoginButton/LoginButton.tsx +38 -0
  181. package/src/markdoc/components/Mermaid/Mermaid.tsx +57 -8
  182. package/src/markdoc/components/default.ts +1 -0
  183. package/src/markdoc/default.ts +2 -0
  184. package/src/markdoc/tags/login-button.ts +30 -0
  185. package/lib/components/Tooltip/TooltipWrapper.d.ts +0 -12
  186. package/lib/components/Tooltip/TooltipWrapper.js +0 -34
  187. package/src/components/Tooltip/TooltipWrapper.tsx +0 -70
  188. /package/lib/components/JsonViewer/{Helpers.d.ts → helpers.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import React, { useEffect, memo, useRef, useState, useCallback } from 'react';
1
+ import React, { memo, useEffect, useRef, useId } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
 
4
4
  import type { JSX, PropsWithChildren, ReactNode } from 'react';
@@ -15,10 +15,10 @@ export type TooltipProps = {
15
15
  width?: string;
16
16
  dataTestId?: string;
17
17
  disabled?: boolean;
18
- arrowPosition?: 'top' | 'bottom' | 'left' | 'right' | 'center';
18
+ arrowPosition?: 'left' | 'right' | 'center';
19
19
  };
20
20
 
21
- export function TooltipComponent({
21
+ function TooltipComponent({
22
22
  children,
23
23
  isOpen,
24
24
  tip,
@@ -30,111 +30,55 @@ export function TooltipComponent({
30
30
  disabled = false,
31
31
  arrowPosition = 'center',
32
32
  }: PropsWithChildren<TooltipProps>): JSX.Element {
33
- const ref = useRef<HTMLDivElement | null>(null);
33
+ const tooltipWrapperRef = useRef<HTMLDivElement | null>(null);
34
+ const tooltipBodyRef = useRef<HTMLDivElement | null>(null);
34
35
  const { isOpened, handleOpen, handleClose } = useControl(isOpen);
35
- const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
36
+ const anchorName = `--tooltip${useId().replace(/:/g, '')}`;
36
37
 
37
- useOutsideClick(ref, handleClose);
38
+ useOutsideClick(isOpened ? [tooltipWrapperRef, tooltipBodyRef] : tooltipWrapperRef, handleClose);
38
39
 
39
40
  const isControlled = isOpen !== undefined;
40
41
 
41
- const updateTooltipPosition = useCallback(() => {
42
- if (isOpened && ref.current) {
43
- const rect = ref.current.getBoundingClientRect();
44
-
45
- let top = 0;
46
- let left = 0;
47
-
48
- switch (placement) {
49
- case 'top':
50
- top = rect.top;
51
- if (arrowPosition === 'left') {
52
- left = rect.left - 24;
53
- } else if (arrowPosition === 'right') {
54
- left = rect.right + 24;
55
- } else {
56
- left = rect.left + rect.width / 2;
57
- }
58
- break;
59
- case 'bottom':
60
- top = rect.bottom;
61
- if (arrowPosition === 'left') {
62
- left = rect.left - 24;
63
- } else if (arrowPosition === 'right') {
64
- left = rect.right + 24;
65
- } else {
66
- left = rect.left + rect.width / 2;
67
- }
68
- break;
69
- case 'left':
70
- top = rect.top + rect.height / 2;
71
- left = rect.left;
72
- break;
73
- case 'right':
74
- top = rect.top + rect.height / 2;
75
- left = rect.right;
76
- break;
77
- }
78
-
79
- setTooltipPosition({ top, left });
80
- }
81
- }, [isOpened, placement, arrowPosition]);
82
-
83
42
  useEffect(() => {
84
- if (isOpened && ref.current) {
85
- updateTooltipPosition();
86
-
87
- const handleScroll = () => updateTooltipPosition();
88
- const handleResize = () => updateTooltipPosition();
89
-
90
- window.addEventListener('scroll', handleScroll, true);
91
- window.addEventListener('resize', handleResize);
92
-
93
- return () => {
94
- window.removeEventListener('scroll', handleScroll, true);
95
- window.removeEventListener('resize', handleResize);
96
- };
97
- }
98
- }, [isOpened, placement, updateTooltipPosition]);
43
+ if (!isControlled) return;
99
44
 
100
- useEffect(() => {
101
45
  if (isOpen && !disabled) {
102
46
  handleOpen();
103
47
  } else {
104
48
  handleClose();
105
49
  }
106
- }, [isOpen, handleOpen, handleClose, disabled]);
107
-
108
- const controllers = !isControlled &&
109
- !disabled && {
110
- onMouseEnter: handleOpen,
111
- onMouseLeave: handleClose,
112
- onClick: handleClose,
113
- onFocus: handleOpen,
114
- onBlur: handleClose,
115
- };
50
+ }, [isOpen, disabled, isControlled, handleOpen, handleClose]);
51
+
52
+ const controllers =
53
+ !isControlled && !disabled
54
+ ? {
55
+ onMouseEnter: handleOpen,
56
+ onMouseLeave: handleClose,
57
+ onClick: handleClose,
58
+ onFocus: handleOpen,
59
+ onBlur: handleClose,
60
+ }
61
+ : {};
116
62
 
117
63
  return (
118
64
  <TooltipWrapper
119
- ref={ref}
65
+ ref={tooltipWrapperRef}
120
66
  {...controllers}
121
67
  className={`tooltip-${className}`}
122
68
  data-component-name="Tooltip/Tooltip"
69
+ anchorName={anchorName}
123
70
  >
124
71
  {children}
125
72
  {isOpened && !disabled && (
126
73
  <Portal>
127
74
  <TooltipBody
75
+ ref={tooltipBodyRef}
128
76
  data-testid={dataTestId || (typeof tip === 'string' ? tip : '')}
129
77
  placement={placement}
130
78
  width={width}
131
79
  withArrow={withArrow}
132
80
  arrowPosition={arrowPosition}
133
- style={{
134
- position: 'fixed',
135
- top: tooltipPosition.top,
136
- left: tooltipPosition.left,
137
- }}
81
+ anchorName={anchorName}
138
82
  >
139
83
  {tip}
140
84
  </TooltipBody>
@@ -148,20 +92,21 @@ export const Tooltip = memo<PropsWithChildren<TooltipProps>>(TooltipComponent);
148
92
 
149
93
  const PLACEMENTS = {
150
94
  top: css<Pick<TooltipProps, 'withArrow' | 'arrowPosition'>>`
95
+ bottom: anchor(top);
151
96
  ${({ withArrow, arrowPosition }) =>
152
97
  withArrow && arrowPosition === 'left'
153
98
  ? css`
154
- transform: translate(0, -100%);
155
- margin-top: -10px;
99
+ transform: translate(-32px, -6px);
100
+ left: anchor(center);
156
101
  `
157
102
  : arrowPosition === 'right'
158
103
  ? css`
159
- transform: translate(-100%, -100%);
160
- margin-top: -10px;
104
+ transform: translate(32px, -6px);
105
+ right: anchor(center);
161
106
  `
162
107
  : css`
163
- transform: translate(-50%, -100%);
164
- margin-top: -10px;
108
+ transform: translate(-50%, -6px);
109
+ left: anchor(center);
165
110
  `}
166
111
 
167
112
  ${({ withArrow, arrowPosition }) =>
@@ -174,27 +119,28 @@ const PLACEMENTS = {
174
119
  border-top-style: solid;
175
120
  border-radius: 2px;
176
121
  bottom: 0;
177
- ${arrowPosition === 'left' && 'left: 16px; transform: translateY(99%);'}
178
- ${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, 99%);'}
179
- ${arrowPosition === 'right' && 'right: 16px; transform: translateY(99%);'}
122
+ ${arrowPosition === 'left' && 'left: 16px; transform: translateY(100%);'}
123
+ ${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, 100%);'}
124
+ ${arrowPosition === 'right' && 'right: 16px; transform: translateY(100%);'}
180
125
  }
181
126
  `}
182
127
  `,
183
128
  bottom: css<Pick<TooltipProps, 'withArrow' | 'arrowPosition'>>`
129
+ top: anchor(bottom);
184
130
  ${({ withArrow, arrowPosition }) =>
185
131
  withArrow && arrowPosition === 'left'
186
132
  ? css`
187
- transform: translate(0, 10px);
188
- margin-top: 0;
133
+ transform: translate(-32px, 6px);
134
+ left: anchor(center);
189
135
  `
190
136
  : arrowPosition === 'right'
191
137
  ? css`
192
- transform: translate(-100%, 10px);
193
- margin-top: 0;
138
+ transform: translate(32px, 6px);
139
+ right: anchor(center);
194
140
  `
195
141
  : css`
196
- transform: translate(-50%, 10px);
197
- margin-top: 0;
142
+ transform: translate(-50%, 6px);
143
+ left: anchor(center);
198
144
  `}
199
145
 
200
146
  ${({ withArrow, arrowPosition }) =>
@@ -207,17 +153,19 @@ const PLACEMENTS = {
207
153
  border-bottom-style: solid;
208
154
  border-radius: 0 0 2px 2px;
209
155
  top: 0;
210
- ${arrowPosition === 'left' && 'left: 16px; transform: translateY(-99%);'}
211
- ${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, -99%);'}
212
- ${arrowPosition === 'right' && 'right: 16px; transform: translateY(-99%);'}
156
+ ${arrowPosition === 'left' && 'left: 16px; transform: translateY(-100%);'}
157
+ ${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, -100%);'}
158
+ ${arrowPosition === 'right' && 'right: 16px; transform: translateY(-100%);'}
213
159
  }
214
160
  `}
215
161
  `,
216
162
  left: css<Pick<TooltipProps, 'withArrow' | 'arrowPosition'>>`
217
163
  transform: translate(-100%, -50%);
218
- margin-left: -10px;
164
+ margin-left: -7px;
165
+ top: anchor(center);
166
+ left: anchor(left);
219
167
 
220
- ${({ withArrow, arrowPosition }) =>
168
+ ${({ withArrow }) =>
221
169
  withArrow &&
222
170
  css`
223
171
  &::after {
@@ -226,19 +174,19 @@ const PLACEMENTS = {
226
174
  border-left-width: 8px;
227
175
  border-left-style: solid;
228
176
  border-radius: 2px 0 0 2px;
177
+ right: -9px;
229
178
  top: 50%;
230
- right: 0;
231
- ${arrowPosition === 'top' && 'top: 16px; transform: translateX(99%);'}
232
- ${arrowPosition === 'center' && 'top: 50%; transform: translate(99%, -50%);'}
233
- ${arrowPosition === 'bottom' && 'bottom: 16px; transform: translateX(99%);'}
179
+ transform: translateY(-50%);
234
180
  }
235
181
  `}
236
182
  `,
237
183
  right: css<Pick<TooltipProps, 'withArrow' | 'arrowPosition'>>`
238
184
  transform: translate(0, -50%);
239
- margin-left: 10px;
185
+ margin-left: 7px;
186
+ top: anchor(center);
187
+ left: anchor(right);
240
188
 
241
- ${({ withArrow, arrowPosition }) =>
189
+ ${({ withArrow }) =>
242
190
  withArrow &&
243
191
  css`
244
192
  &::after {
@@ -247,31 +195,40 @@ const PLACEMENTS = {
247
195
  border-right-width: 8px;
248
196
  border-right-style: solid;
249
197
  border-radius: 0 2px 2px 0;
198
+ left: -9px;
250
199
  top: 50%;
251
- left: 0;
252
- ${arrowPosition === 'top' && 'top: 16px; transform: translateX(-99%);'}
253
- ${arrowPosition === 'center' && 'top: 50%; transform: translate(-99%, -50%);'}
254
- ${arrowPosition === 'bottom' && 'bottom: 16px; transform: translateX(-99%);'}
200
+ transform: translateY(-50%);
255
201
  }
256
202
  `}
257
203
  `,
258
204
  };
259
205
 
260
- const TooltipWrapper = styled.div`
261
- position: relative;
206
+ const TooltipWrapper = styled.div.attrs<{ anchorName: string }>(({ anchorName }) => ({
207
+ style: {
208
+ anchorName: anchorName,
209
+ } as React.CSSProperties,
210
+ }))<{ anchorName: string }>`
262
211
  display: flex;
263
212
  `;
264
- /* eslint-disable theme/no-raw-colors-in-styles */
265
- const TooltipBody = styled.span<
266
- Pick<Required<TooltipProps>, 'placement' | 'withArrow' | 'arrowPosition'> & { width?: string }
267
- >`
268
- display: inline-block;
269
213
 
214
+ const TooltipBody = styled.span.attrs<{ anchorName: string }>(({ anchorName }) => ({
215
+ style: {
216
+ positionAnchor: anchorName,
217
+ } as React.CSSProperties,
218
+ }))<
219
+ Pick<Required<TooltipProps>, 'placement' | 'withArrow' | 'arrowPosition'> & {
220
+ width?: string;
221
+ anchorName: string;
222
+ }
223
+ >`
224
+ position: fixed;
225
+ min-width: 64px;
270
226
  padding: var(--tooltip-padding);
271
- max-width: ${({ width }) => width || 'var(--tooltip-max-width)'};
227
+ max-width: var(--tooltip-max-width);
272
228
  white-space: normal;
273
229
  word-break: normal;
274
230
  overflow-wrap: break-word;
231
+ text-align: left;
275
232
 
276
233
  border-radius: var(--border-radius-md);
277
234
  transition: opacity 0.3s ease-out;
@@ -299,9 +256,9 @@ const TooltipBody = styled.span<
299
256
  0px 8px 24px 8px #0000000a,
300
257
  0px 4px 12px 0px #00000014;
301
258
 
302
- width: ${({ width }) => width || 'auto'};
259
+ width: ${({ width }) => width || 'max-content'};
260
+
303
261
  ${({ placement }) => css`
304
262
  ${PLACEMENTS[placement]};
305
263
  `}
306
264
  `;
307
- /* eslint-enable theme/no-raw-colors-in-styles */
@@ -3,4 +3,8 @@ import { css } from 'styled-components';
3
3
  export const tooltipDarkMode = css`
4
4
  --tooltip-bg-color: var(--color-warm-grey-4);
5
5
  --tooltip-border-color: var(--color-warm-grey-5);
6
+
7
+ .tooltip-copy-button {
8
+ --tooltip-bg-color: var(--color-warm-grey-4);
9
+ }
6
10
  `;
@@ -18,9 +18,9 @@ export const tooltip = css`
18
18
  --tooltip-max-width: 400px;
19
19
 
20
20
  .tooltip-copy-button {
21
- --tooltip-text-color: var(--text-color-secondary);
22
- --tooltip-bg-color: var(--bg-color-raised);
23
- --tooltip-arrow-color: var(--tooltip-bg-color);
21
+ --tooltip-text-color: var(--color-static-white);
22
+ --tooltip-bg-color: var(--color-warm-grey-10);
23
+ --tooltip-arrow-color: var(--tooltip-border-color);
24
24
  }
25
25
 
26
26
  .tooltip-catalog {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- import type { JSX } from 'react';
3
+ import type { ButtonVariant, ButtonSize } from '@redocly/theme/components/Button/Button';
4
4
 
5
5
  import { useThemeHooks } from '@redocly/theme/core/hooks';
6
6
  import { Button } from '@redocly/theme/components/Button/Button';
@@ -8,26 +8,41 @@ import { Button } from '@redocly/theme/components/Button/Button';
8
8
  export type LoginButtonProps = {
9
9
  href: string;
10
10
  className?: string;
11
+ variant?: ButtonVariant;
12
+ size?: ButtonSize;
13
+ label?: string;
14
+ labelTranslationKey?: string;
15
+ ['data-component-name']?: string;
11
16
  };
12
17
 
13
- export function LoginButton({ href, className }: LoginButtonProps): JSX.Element {
18
+ export function LoginButton({
19
+ href,
20
+ className,
21
+ variant = 'primary',
22
+ size = 'medium',
23
+ label,
24
+ labelTranslationKey = 'userMenu.login',
25
+ 'data-component-name': componentName = 'UserMenu/LoginButton',
26
+ }: LoginButtonProps) {
14
27
  const { useTranslate, useTelemetry } = useThemeHooks();
15
28
  const { translate } = useTranslate();
16
29
  const telemetry = useTelemetry();
17
30
 
31
+ const buttonLabel = label || translate(labelTranslationKey, 'Login');
32
+
18
33
  return (
19
- <div data-component-name="UserMenu/LoginButton" className={className}>
34
+ <span data-component-name={componentName} className={className}>
20
35
  <Button
21
- data-translation-key="userMenu.login"
36
+ data-translation-key={label ? undefined : labelTranslationKey}
22
37
  to={href}
23
38
  onClick={() => telemetry.sendLoginButtonClickedMessage()}
24
39
  data-testid="login-btn"
25
40
  extraClass={className}
26
- variant="primary"
27
- size="medium"
41
+ variant={variant}
42
+ size={size}
28
43
  >
29
- {translate('userMenu.login', 'Login')}
44
+ {buttonLabel}
30
45
  </Button>
31
- </div>
46
+ </span>
32
47
  );
33
48
  }
@@ -1,4 +1,4 @@
1
- import type { AiSearchErrorConfig } from '../types/search';
1
+ import { type AiSearchErrorConfig, ToolCallName } from '../types/search';
2
2
 
3
3
  export enum AiSearchError {
4
4
  Unauthorized = 'ai_search_unauthorized',
@@ -45,3 +45,29 @@ export const AI_SEARCH_ERROR_CONFIG: Record<AiSearchError, AiSearchErrorConfig>
45
45
  export const AI_SEARCH_MAX_MESSAGE_LENGTH = 45000;
46
46
 
47
47
  export const SEARCH_DEBOUNCE_TIME_MS = 300;
48
+
49
+ export const TOOL_CALL_DISPLAY_TEXT: Record<
50
+ ToolCallName,
51
+ { inProgressText: string; completedText: string }
52
+ > = {
53
+ [ToolCallName.SearchDocumentation]: {
54
+ inProgressText: 'Searching documentation...',
55
+ completedText: 'Search completed',
56
+ },
57
+ [ToolCallName.ListApis]: {
58
+ inProgressText: 'Browsing APIs...',
59
+ completedText: 'Browsed APIs',
60
+ },
61
+ [ToolCallName.GetEndpoints]: {
62
+ inProgressText: 'Browsing endpoints for API...',
63
+ completedText: 'Browsed endpoints for API',
64
+ },
65
+ [ToolCallName.GetSecuritySchemes]: {
66
+ inProgressText: 'Browsing security schemes for API...',
67
+ completedText: 'Browsed security schemes for API',
68
+ },
69
+ [ToolCallName.GetFullApiDescription]: {
70
+ inProgressText: 'Reading API description...',
71
+ completedText: 'Read API description',
72
+ },
73
+ };
@@ -16,6 +16,7 @@ export const useThemeHooks = vi.fn(() => ({
16
16
  send: vi.fn(),
17
17
  sendCodeSnippetReportedMessage: vi.fn(),
18
18
  sendPageActionsButtonClickedMessage: vi.fn(),
19
+ sendColorModeSwitchedMessage: vi.fn(),
19
20
  })),
20
21
  useBreadcrumbs: vi.fn().mockReturnValue({ breadcrumbs: [], siblings: undefined }),
21
22
  useBanner: vi.fn(() => ({
@@ -54,7 +55,15 @@ export const useThemeHooks = vi.fn(() => ({
54
55
  })),
55
56
  useAiSearch: vi.fn(() => ({
56
57
  askQuestion: vi.fn(),
57
- references: [],
58
+ isGeneratingResponse: false,
59
+ question: '',
60
+ response: undefined,
61
+ resources: [],
62
+ clearConversation: vi.fn(),
63
+ error: null,
64
+ conversation: [],
65
+ setConversation: vi.fn(),
66
+ toolCalls: [],
58
67
  })),
59
68
  useFacetQuery: vi.fn(() => ({
60
69
  searchFacet: null,
@@ -20,7 +20,7 @@ export function useSearchDialog() {
20
20
  if (hotKeys) {
21
21
  hotkeys(hotKeys, (ev) => {
22
22
  setIsOpen(true);
23
- telemetry.sendSearchOpenedMessage({ method: 'shortcut' });
23
+ telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'shortcut' }]);
24
24
  ev.preventDefault();
25
25
  });
26
26
 
@@ -30,7 +30,7 @@ export function useSearchDialog() {
30
30
  }, [hotKeys]);
31
31
 
32
32
  const onOpen = useCallback(function () {
33
- telemetry.sendSearchOpenedMessage({ method: 'click' });
33
+ telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'click' }]);
34
34
  setIsOpen(true);
35
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
36
36
  }, []);
@@ -50,7 +50,9 @@ export const useColorSwitcher = () => {
50
50
  window.requestAnimationFrame(() => {
51
51
  root.classList.remove('notransition');
52
52
  });
53
- telemetry.sendColorModeSwitchedMessage({ from: activeColorMode, to: newMode });
53
+ telemetry.sendColorModeSwitchedMessage([
54
+ { object: 'color_mode', from: activeColorMode, to: newMode },
55
+ ]);
54
56
  setActiveColorMode(newMode);
55
57
  };
56
58
 
@@ -4,6 +4,7 @@ import { useThemeHooks } from './use-theme-hooks';
4
4
  import { IS_BROWSER } from '../utils/dom';
5
5
  import { DEFAULT_MCP_SERVER_NAME } from '../constants';
6
6
  import { generateMCPDeepLink } from '../utils/mcp';
7
+ import { withPathPrefix } from '../utils/urls';
7
8
 
8
9
  export type McpConfig = {
9
10
  serverName: string;
@@ -25,7 +26,7 @@ export function useMCPConfig(): McpConfig {
25
26
  ? window.location.origin
26
27
  : ((globalThis as { SSR_HOSTNAME?: string })['SSR_HOSTNAME'] ?? '');
27
28
  const serverName = name || DEFAULT_MCP_SERVER_NAME;
28
- const serverUrl = `${origin}/mcp`;
29
+ const serverUrl = `${origin}${withPathPrefix('/mcp')}`;
29
30
  const isMcpDisabled = !enabled || false;
30
31
 
31
32
  const cursorUrl = useMemo(
@@ -1,21 +1,40 @@
1
1
  import { useEffect } from 'react';
2
2
 
3
- export function useModalScrollLock(isOpen: boolean) {
3
+ export function useModalScrollLock(isOpen: boolean): void {
4
4
  useEffect(() => {
5
- const originalOverflow = document.body.style.overflow;
6
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
5
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
6
+ return;
7
+ }
8
+
9
+ const { body, documentElement } = document;
10
+ const originalOverflow = body.style.overflow;
11
+ const originalPaddingRight = body.style.paddingRight;
12
+ const originalScrollbarWidth =
13
+ documentElement.style.getPropertyValue('--modal-scrollbar-width');
14
+
15
+ const restoreScrollState = () => {
16
+ body.style.overflow = originalOverflow;
17
+ body.style.paddingRight = originalPaddingRight;
18
+ if (originalScrollbarWidth) {
19
+ documentElement.style.setProperty('--modal-scrollbar-width', originalScrollbarWidth);
20
+ } else {
21
+ documentElement.style.removeProperty('--modal-scrollbar-width');
22
+ }
23
+ };
7
24
 
8
25
  if (isOpen) {
9
- document.body.style.overflow = 'hidden';
10
- document.body.style.marginRight = `${scrollbarWidth}px`;
11
- } else {
12
- document.body.style.overflow = originalOverflow;
13
- document.body.style.marginRight = '';
26
+ const scrollbarWidth = window.innerWidth - documentElement.clientWidth;
27
+
28
+ body.style.overflow = 'hidden';
29
+
30
+ if (scrollbarWidth > 0) {
31
+ body.style.paddingRight = `${scrollbarWidth}px`;
32
+ documentElement.style.setProperty('--modal-scrollbar-width', `${scrollbarWidth}px`);
33
+ }
14
34
  }
15
35
 
16
36
  return () => {
17
- document.body.style.overflow = originalOverflow;
18
- document.body.style.marginRight = '';
37
+ restoreScrollState();
19
38
  };
20
39
  }, [isOpen]);
21
40
  }
@@ -2,22 +2,33 @@ import { useEffect } from 'react';
2
2
 
3
3
  import type { RefObject } from 'react';
4
4
 
5
+ type OutsideClickRef = RefObject<HTMLElement | null>;
6
+
5
7
  export function useOutsideClick(
6
- ref: RefObject<HTMLElement | null>,
8
+ refs: OutsideClickRef | OutsideClickRef[],
7
9
  handler?: (event: MouseEvent | TouchEvent) => void,
8
10
  ): void {
9
11
  useEffect(() => {
12
+ const refList = Array.isArray(refs) ? refs : [refs];
13
+
10
14
  const listener = (event: MouseEvent | TouchEvent) => {
11
- if (!handler || !ref.current || ref.current.contains(event.target as Node)) {
12
- return;
15
+ if (!handler) return;
16
+
17
+ const target = event.target as Node;
18
+
19
+ const isInside = refList.some((ref) => ref.current && ref.current.contains(target));
20
+
21
+ if (!isInside) {
22
+ handler(event);
13
23
  }
14
- handler(event);
15
24
  };
25
+
16
26
  document.addEventListener('mousedown', listener);
17
27
  document.addEventListener('touchstart', listener);
28
+
18
29
  return () => {
19
30
  document.removeEventListener('mousedown', listener);
20
31
  document.removeEventListener('touchstart', listener);
21
32
  };
22
- }, [ref, handler]);
33
+ }, [refs, handler]);
23
34
  }