@redocly/theme 0.59.0-rc.2 → 0.60.0-next.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 (199) 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/Link/Link.d.ts +1 -0
  17. package/lib/components/Menu/MenuItem.js +1 -1
  18. package/lib/components/Navbar/NavbarItem.js +3 -3
  19. package/lib/components/PageActions/PageActions.js +4 -1
  20. package/lib/components/PageActions/variables.js +2 -0
  21. package/lib/components/Search/FilterFields/SearchFilterFieldTags.js +1 -2
  22. package/lib/components/Search/SearchAiActionButtons.d.ts +10 -0
  23. package/lib/components/Search/SearchAiActionButtons.js +43 -0
  24. package/lib/components/Search/SearchAiConversationInput.d.ts +3 -1
  25. package/lib/components/Search/SearchAiConversationInput.js +39 -7
  26. package/lib/components/Search/SearchAiDialog.d.ts +3 -6
  27. package/lib/components/Search/SearchAiDialog.js +20 -9
  28. package/lib/components/Search/SearchAiMessage.d.ts +9 -5
  29. package/lib/components/Search/SearchAiMessage.js +146 -22
  30. package/lib/components/Search/SearchAiNegativeFeedbackForm.d.ts +8 -0
  31. package/lib/components/Search/SearchAiNegativeFeedbackForm.js +169 -0
  32. package/lib/components/Search/SearchDialog.js +36 -5
  33. package/lib/components/Search/SearchGroups.js +2 -2
  34. package/lib/components/Search/variables.js +36 -64
  35. package/lib/components/Segmented/Segmented.d.ts +1 -8
  36. package/lib/components/Segmented/Segmented.js +3 -1
  37. package/lib/components/Select/SelectInput.js +1 -1
  38. package/lib/components/Select/variables.js +2 -2
  39. package/lib/components/Tag/Tag.d.ts +2 -1
  40. package/lib/components/Tag/Tag.js +66 -17
  41. package/lib/components/Tag/variables.dark.js +135 -36
  42. package/lib/components/Tag/variables.js +78 -61
  43. package/lib/config.d.ts +2 -2
  44. package/lib/core/constants/index.d.ts +1 -0
  45. package/lib/core/constants/index.js +1 -0
  46. package/lib/core/constants/mcp.d.ts +1 -0
  47. package/lib/core/constants/mcp.js +5 -0
  48. package/lib/core/constants/search.d.ts +5 -4
  49. package/lib/core/constants/search.js +4 -5
  50. package/lib/core/hooks/index.d.ts +3 -0
  51. package/lib/core/hooks/index.js +3 -0
  52. package/lib/core/hooks/menu/use-nested-menu.js +1 -1
  53. package/lib/core/hooks/search/use-feedback-tooltip.d.ts +6 -0
  54. package/lib/core/hooks/search/use-feedback-tooltip.js +26 -0
  55. package/lib/core/hooks/use-connect-mcp-button.d.ts +13 -0
  56. package/lib/core/hooks/use-connect-mcp-button.js +50 -0
  57. package/lib/core/hooks/use-mcp-config.d.ts +9 -0
  58. package/lib/core/hooks/use-mcp-config.js +27 -0
  59. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  60. package/lib/core/hooks/use-page-actions.js +99 -119
  61. package/lib/core/hooks/use-product-picker.js +2 -1
  62. package/lib/core/hooks/use-tabs.d.ts +3 -2
  63. package/lib/core/hooks/use-tabs.js +115 -57
  64. package/lib/core/hooks/use-telemetry-fallback.d.ts +10 -8
  65. package/lib/core/hooks/use-telemetry-fallback.js +10 -8
  66. package/lib/core/openapi/index.d.ts +1 -0
  67. package/lib/core/styles/dark.js +4 -0
  68. package/lib/core/styles/global.js +5 -0
  69. package/lib/core/types/hooks.d.ts +2 -2
  70. package/lib/core/types/index.d.ts +1 -0
  71. package/lib/core/types/index.js +1 -0
  72. package/lib/core/types/l10n.d.ts +1 -1
  73. package/lib/core/types/mcp.d.ts +6 -0
  74. package/lib/core/types/mcp.js +3 -0
  75. package/lib/core/types/search.d.ts +11 -4
  76. package/lib/core/types/search.js +6 -0
  77. package/lib/core/types/segmented.d.ts +12 -0
  78. package/lib/core/types/segmented.js +3 -0
  79. package/lib/core/utils/frontmatter-translate.d.ts +6 -0
  80. package/lib/core/utils/frontmatter-translate.js +14 -0
  81. package/lib/core/utils/index.d.ts +2 -0
  82. package/lib/core/utils/index.js +2 -0
  83. package/lib/core/utils/mcp.d.ts +2 -0
  84. package/lib/core/utils/mcp.js +31 -0
  85. package/lib/icons/AiStarsGradientIcon/AiStarsGradientIcon.js +44 -4
  86. package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
  87. package/lib/icons/ConnectIcon/ConnectIcon.d.ts +9 -0
  88. package/lib/icons/ConnectIcon/ConnectIcon.js +17 -0
  89. package/lib/icons/CubeIcon/CubeIcon.d.ts +9 -0
  90. package/lib/icons/CubeIcon/CubeIcon.js +17 -0
  91. package/lib/icons/HashtagIcon/HashtagIcon.d.ts +9 -0
  92. package/lib/icons/HashtagIcon/HashtagIcon.js +22 -0
  93. package/lib/icons/RedoclyIcon/RedoclyIcon.js +4 -7
  94. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.d.ts +9 -0
  95. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.js +34 -0
  96. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.d.ts +9 -0
  97. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.js +34 -0
  98. package/lib/icons/VSCodeIcon/VSCodeIcon.d.ts +9 -0
  99. package/lib/icons/VSCodeIcon/VSCodeIcon.js +17 -0
  100. package/lib/index.d.ts +1 -2
  101. package/lib/index.js +1 -2
  102. package/lib/markdoc/components/Cards/Card.js +1 -28
  103. package/lib/markdoc/components/ConnectMCP/ConnectMCP.d.ts +8 -0
  104. package/lib/markdoc/components/ConnectMCP/ConnectMCP.js +19 -0
  105. package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
  106. package/lib/markdoc/components/Tabs/TabList.js +197 -47
  107. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
  108. package/lib/markdoc/components/Tabs/Tabs.js +57 -12
  109. package/lib/markdoc/components/default.d.ts +1 -0
  110. package/lib/markdoc/components/default.js +1 -0
  111. package/lib/markdoc/default.d.ts +6 -0
  112. package/lib/markdoc/default.js +2 -0
  113. package/lib/markdoc/tags/card.js +0 -1
  114. package/lib/markdoc/tags/connect-mcp.d.ts +2 -0
  115. package/lib/markdoc/tags/connect-mcp.js +27 -0
  116. package/package.json +6 -6
  117. package/src/components/Buttons/AIAssistantButton.tsx +6 -2
  118. package/src/components/Buttons/ConnectMCPButton.tsx +180 -0
  119. package/src/components/Buttons/variables.ts +42 -1
  120. package/src/components/Catalog/CatalogEntity/CatalogEntityInfoBar.tsx +1 -0
  121. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  122. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  123. package/src/components/Catalog/CatalogEntityIcon.tsx +2 -1
  124. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +5 -0
  125. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +1 -5
  126. package/src/components/Catalog/variables.ts +1 -1
  127. package/src/components/Dropdown/Dropdown.tsx +84 -79
  128. package/src/components/Link/Link.tsx +1 -0
  129. package/src/components/Menu/MenuItem.tsx +1 -0
  130. package/src/components/Navbar/NavbarItem.tsx +6 -5
  131. package/src/components/PageActions/PageActions.tsx +5 -1
  132. package/src/components/PageActions/variables.ts +2 -0
  133. package/src/components/Search/FilterFields/SearchFilterFieldTags.tsx +3 -3
  134. package/src/components/Search/SearchAiActionButtons.tsx +76 -0
  135. package/src/components/Search/SearchAiConversationInput.tsx +61 -18
  136. package/src/components/Search/SearchAiDialog.tsx +52 -23
  137. package/src/components/Search/SearchAiMessage.tsx +172 -43
  138. package/src/components/Search/SearchAiNegativeFeedbackForm.tsx +210 -0
  139. package/src/components/Search/SearchDialog.tsx +49 -13
  140. package/src/components/Search/SearchGroups.tsx +2 -0
  141. package/src/components/Search/variables.ts +36 -64
  142. package/src/components/Segmented/Segmented.tsx +15 -20
  143. package/src/components/Select/SelectInput.tsx +1 -0
  144. package/src/components/Select/variables.ts +2 -2
  145. package/src/components/Tag/Tag.tsx +35 -19
  146. package/src/components/Tag/variables.dark.ts +135 -36
  147. package/src/components/Tag/variables.ts +78 -61
  148. package/src/config.ts +2 -0
  149. package/src/core/constants/index.ts +1 -0
  150. package/src/core/constants/mcp.ts +1 -0
  151. package/src/core/constants/search.ts +8 -4
  152. package/src/core/hooks/index.ts +3 -0
  153. package/src/core/hooks/menu/use-nested-menu.ts +2 -2
  154. package/src/core/hooks/search/use-feedback-tooltip.ts +32 -0
  155. package/src/core/hooks/use-connect-mcp-button.ts +79 -0
  156. package/src/core/hooks/use-mcp-config.ts +43 -0
  157. package/src/core/hooks/use-page-actions.ts +141 -150
  158. package/src/core/hooks/use-product-picker.ts +2 -1
  159. package/src/core/hooks/use-tabs.ts +168 -86
  160. package/src/core/hooks/use-telemetry-fallback.ts +10 -8
  161. package/src/core/openapi/index.ts +1 -0
  162. package/src/core/styles/dark.ts +4 -0
  163. package/src/core/styles/global.ts +6 -1
  164. package/src/core/types/hooks.ts +5 -1
  165. package/src/core/types/index.ts +1 -0
  166. package/src/core/types/l10n.ts +13 -0
  167. package/src/core/types/mcp.ts +8 -0
  168. package/src/core/types/search.ts +13 -4
  169. package/src/core/types/segmented.ts +14 -0
  170. package/src/core/utils/frontmatter-translate.ts +9 -0
  171. package/src/core/utils/index.ts +2 -0
  172. package/src/core/utils/mcp.ts +34 -0
  173. package/src/icons/AiStarsGradientIcon/AiStarsGradientIcon.tsx +13 -4
  174. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
  175. package/src/icons/ConnectIcon/ConnectIcon.tsx +27 -0
  176. package/src/icons/CubeIcon/CubeIcon.tsx +27 -0
  177. package/src/icons/HashtagIcon/HashtagIcon.tsx +23 -0
  178. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +4 -22
  179. package/src/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.tsx +38 -0
  180. package/src/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.tsx +35 -0
  181. package/src/icons/VSCodeIcon/VSCodeIcon.tsx +29 -0
  182. package/src/index.ts +1 -2
  183. package/src/markdoc/components/Cards/Card.tsx +1 -28
  184. package/src/markdoc/components/ConnectMCP/ConnectMCP.tsx +28 -0
  185. package/src/markdoc/components/Tabs/TabList.tsx +312 -105
  186. package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
  187. package/src/markdoc/components/default.ts +1 -0
  188. package/src/markdoc/default.ts +2 -0
  189. package/src/markdoc/tags/card.ts +0 -1
  190. package/src/markdoc/tags/connect-mcp.ts +25 -0
  191. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.d.ts +0 -1
  192. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.js +0 -11
  193. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.d.ts +0 -1
  194. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.js +0 -5
  195. package/lib/ext/process-scorecard.d.ts +0 -5
  196. package/lib/ext/process-scorecard.js +0 -11
  197. package/src/components/OpenApiDocs/hooks/AdditionalOverviewInfo.tsx +0 -9
  198. package/src/components/OpenApiDocs/hooks/AfterOpenApiDescription.tsx +0 -1
  199. package/src/ext/process-scorecard.ts +0 -13
@@ -0,0 +1,180 @@
1
+ import React, { useMemo } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { MCPOption } from '@redocly/theme/core/types';
6
+
7
+ import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
8
+ import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
9
+ import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
10
+ import { Button } from '@redocly/theme/components/Button/Button';
11
+ import { CursorIcon } from '@redocly/theme/icons/CursorIcon/CursorIcon';
12
+ import { VSCodeIcon } from '@redocly/theme/icons/VSCodeIcon/VSCodeIcon';
13
+ import { CopyIcon } from '@redocly/theme/icons/CopyIcon/CopyIcon';
14
+ import { CheckmarkFilledIcon } from '@redocly/theme/icons/CheckmarkFilledIcon/CheckmarkFilledIcon';
15
+ import { ConnectIcon } from '@redocly/theme/icons/ConnectIcon/ConnectIcon';
16
+ import { useConnectMCPButton } from '@redocly/theme/core/hooks/use-connect-mcp-button';
17
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
18
+
19
+ type TriggerButtonProps = {
20
+ text: string;
21
+ };
22
+
23
+ const TriggerButton = React.memo(({ text }: TriggerButtonProps): JSX.Element => {
24
+ return (
25
+ <StyledButton variant="secondary" icon={<ConnectIcon />}>
26
+ {text}
27
+ </StyledButton>
28
+ );
29
+ });
30
+
31
+ type MenuOption = {
32
+ key: MCPOption;
33
+ icon: React.ComponentType;
34
+ titleTranslationKey: string;
35
+ titleDefault: string;
36
+ descriptionTranslationKey: string;
37
+ descriptionDefault: string;
38
+ };
39
+
40
+ const MENU_OPTIONS: MenuOption[] = [
41
+ {
42
+ key: 'cursor',
43
+ icon: CursorIcon,
44
+ titleTranslationKey: 'page.actions.connectMcp.cursor',
45
+ titleDefault: 'Connect to Cursor',
46
+ descriptionTranslationKey: 'page.actions.connectMcp.cursorDescription',
47
+ descriptionDefault: 'Install MCP server on Cursor',
48
+ },
49
+ {
50
+ key: 'vscode',
51
+ icon: VSCodeIcon,
52
+ titleTranslationKey: 'page.actions.connectMcp.vscode',
53
+ titleDefault: 'Connect to VS Code',
54
+ descriptionTranslationKey: 'page.actions.connectMcp.vscodeDescription',
55
+ descriptionDefault: 'Install MCP server on VS Code',
56
+ },
57
+ {
58
+ key: 'copy',
59
+ icon: CopyIcon,
60
+ titleTranslationKey: 'page.actions.connectMcp.copyConfig',
61
+ titleDefault: 'Copy MCP Configuration',
62
+ descriptionTranslationKey: 'page.actions.connectMcp.copyConfigDescription',
63
+ descriptionDefault: 'Copy MCP JSON Configuration',
64
+ },
65
+ ];
66
+
67
+ export type ConnectMCPButtonProps = {
68
+ placement?: 'top' | 'bottom';
69
+ alignment?: 'start' | 'end';
70
+ options?: MCPOption[];
71
+ };
72
+
73
+ export function ConnectMCPButton({
74
+ placement = 'bottom',
75
+ alignment = 'end',
76
+ options = ['cursor', 'vscode', 'copy'],
77
+ }: ConnectMCPButtonProps): JSX.Element {
78
+ const { useTranslate } = useThemeHooks();
79
+ const { translate } = useTranslate();
80
+
81
+ const { isCopied, triggerButtonText, visibleOptions, handleAction } = useConnectMCPButton({
82
+ options,
83
+ });
84
+
85
+ const menuOptions = useMemo(
86
+ () => MENU_OPTIONS.filter((option) => visibleOptions.includes(option.key)),
87
+ [visibleOptions],
88
+ );
89
+
90
+ return (
91
+ <ConnectMCPButtonWrapper data-component-name="Buttons/ConnectMCPButton">
92
+ <Dropdown
93
+ trigger={<TriggerButton text={triggerButtonText} />}
94
+ triggerEvent="hover"
95
+ placement={placement}
96
+ alignment={alignment}
97
+ closeOnClick={false}
98
+ >
99
+ <DropdownMenu>
100
+ {menuOptions.map((option) => {
101
+ const Icon = option.icon;
102
+ const showCheckmark = option.key === 'copy' && isCopied;
103
+
104
+ return (
105
+ <DropdownMenuItem key={option.key} onAction={() => handleAction(option.key)}>
106
+ <MenuItemContent>
107
+ <MenuItemIcon>
108
+ {showCheckmark ? (
109
+ <CheckmarkFilledIcon color="var(--color-success-base)" />
110
+ ) : (
111
+ <Icon />
112
+ )}
113
+ </MenuItemIcon>
114
+ <MenuItemText>
115
+ <MenuItemTitle>
116
+ {translate(option.titleTranslationKey, option.titleDefault)}
117
+ </MenuItemTitle>
118
+ <MenuItemDescription>
119
+ {translate(option.descriptionTranslationKey, option.descriptionDefault)}
120
+ </MenuItemDescription>
121
+ </MenuItemText>
122
+ </MenuItemContent>
123
+ </DropdownMenuItem>
124
+ );
125
+ })}
126
+ </DropdownMenu>
127
+ </Dropdown>
128
+ </ConnectMCPButtonWrapper>
129
+ );
130
+ }
131
+
132
+ const ConnectMCPButtonWrapper = styled.div`
133
+ display: inline-block;
134
+ position: relative;
135
+ `;
136
+
137
+ const StyledButton = styled(Button)`
138
+ --button-gap: var(--connect-mcp-button-gap);
139
+ `;
140
+
141
+ const MenuItemContent = styled.div`
142
+ display: flex;
143
+ align-items: center;
144
+ gap: var(--connect-mcp-button-menu-item-gap);
145
+ padding: var(--connect-mcp-button-menu-item-padding-block)
146
+ var(--connect-mcp-button-menu-item-padding-inline);
147
+ `;
148
+
149
+ const MenuItemIcon = styled.div`
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ width: var(--connect-mcp-button-menu-item-icon-size);
154
+ height: var(--connect-mcp-button-menu-item-icon-size);
155
+ flex-shrink: 0;
156
+ border: var(--connect-mcp-button-menu-item-icon-border);
157
+ border-radius: var(--connect-mcp-button-menu-item-icon-border-radius);
158
+ color: var(--connect-mcp-button-menu-item-icon-color);
159
+ `;
160
+
161
+ const MenuItemText = styled.div`
162
+ display: flex;
163
+ flex-direction: column;
164
+ gap: var(--connect-mcp-button-menu-item-text-gap);
165
+ flex: 1;
166
+ `;
167
+
168
+ const MenuItemTitle = styled.div`
169
+ font-size: var(--connect-mcp-button-menu-item-title-font-size);
170
+ font-weight: var(--connect-mcp-button-menu-item-title-font-weight);
171
+ line-height: var(--connect-mcp-button-menu-item-title-line-height);
172
+ color: var(--connect-mcp-button-menu-item-title-color);
173
+ `;
174
+
175
+ const MenuItemDescription = styled.div`
176
+ font-size: var(--connect-mcp-button-menu-item-description-font-size);
177
+ font-weight: var(--connect-mcp-button-menu-item-description-font-weight);
178
+ line-height: var(--connect-mcp-button-menu-item-description-line-height);
179
+ color: var(--connect-mcp-button-menu-item-description-color);
180
+ `;
@@ -21,7 +21,7 @@ export const aiAssistantButton = css`
21
21
  /* Positioning */
22
22
  --ai-assistant-button-bottom: var(--spacing-xl);
23
23
  --ai-assistant-button-right: var(--spacing-xl);
24
- --ai-assistant-button-z-index: 1000;
24
+ --ai-assistant-button-z-index: 50
25
25
 
26
26
  /* Typography */
27
27
  --ai-assistant-button-font-size: var(--font-size-base);
@@ -46,3 +46,44 @@ export const aiAssistantButton = css`
46
46
  /* Transition */
47
47
  --ai-assistant-button-transition: box-shadow 0.3s ease, transform 0.2s ease;
48
48
  `;
49
+
50
+ export const connectMCPButton = css`
51
+ /**
52
+ * @tokens Connect MCP Button
53
+ * @presenter Color
54
+ */
55
+
56
+ /* Button gap */
57
+ --connect-mcp-button-gap: var(--spacing-xs);
58
+
59
+ /* Menu item layout */
60
+ --connect-mcp-button-menu-item-gap: var(--spacing-sm);
61
+ --connect-mcp-button-menu-item-padding-block: var(--spacing-xxs);
62
+ --connect-mcp-button-menu-item-padding-inline: 0;
63
+
64
+ /* Menu item icon */
65
+ --connect-mcp-button-menu-item-icon-size: 32px;
66
+ --connect-mcp-button-menu-item-icon-border-color: var(--border-color-secondary);
67
+ --connect-mcp-button-menu-item-icon-border-style: solid;
68
+ --connect-mcp-button-menu-item-icon-border-width: 1px;
69
+ --connect-mcp-button-menu-item-icon-border: var(--connect-mcp-button-menu-item-icon-border-width)
70
+ var(--connect-mcp-button-menu-item-icon-border-style)
71
+ var(--connect-mcp-button-menu-item-icon-border-color);
72
+ --connect-mcp-button-menu-item-icon-border-radius: var(--border-radius);
73
+ --connect-mcp-button-menu-item-icon-color: var(--icon-color-secondary);
74
+
75
+ /* Menu item text */
76
+ --connect-mcp-button-menu-item-text-gap: var(--spacing-xxs);
77
+
78
+ /* Menu item title */
79
+ --connect-mcp-button-menu-item-title-font-size: var(--font-size-base);
80
+ --connect-mcp-button-menu-item-title-font-weight: var(--font-weight-regular);
81
+ --connect-mcp-button-menu-item-title-line-height: var(--line-height-base);
82
+ --connect-mcp-button-menu-item-title-color: var(--text-color-secondary);
83
+
84
+ /* Menu item description */
85
+ --connect-mcp-button-menu-item-description-font-size: var(--font-size-sm);
86
+ --connect-mcp-button-menu-item-description-font-weight: var(--font-weight-regular);
87
+ --connect-mcp-button-menu-item-description-line-height: var(--line-height-sm);
88
+ --connect-mcp-button-menu-item-description-color: var(--text-color-description);
89
+ `;
@@ -41,6 +41,7 @@ const InfoBarWrapper = styled.div<{ hoverEffect: boolean }>`
41
41
  transition: all 0.2s ease-in-out;
42
42
  height: 100%;
43
43
  padding: var(--catalog-card-gap);
44
+ margin-bottom: var(--spacing-xs);
44
45
 
45
46
  ${({ hoverEffect }) =>
46
47
  hoverEffect &&
@@ -46,7 +46,7 @@ export function CatalogEntityApiDescriptionRelations({
46
46
  }: CatalogEntityApiDescriptionRelationsProps): JSX.Element {
47
47
  return (
48
48
  <div data-component-name="Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations">
49
- <Tabs key={entity.id} size={TabsSize.MEDIUM}>
49
+ <Tabs key={entity.id} forceReady={relations.length > 0} size={TabsSize.MEDIUM}>
50
50
  <TabItem
51
51
  label="Operations"
52
52
  icon={<MoleculesIcon />}
@@ -74,7 +74,7 @@ export function CatalogEntityTeamRelations({
74
74
  }: CatalogEntityTeamRelationsProps): JSX.Element {
75
75
  return (
76
76
  <div data-component-name="Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations">
77
- <Tabs size={TabsSize.MEDIUM}>
77
+ <Tabs forceReady={relations.length > 0} size={TabsSize.MEDIUM}>
78
78
  <TabItem label="Members" icon={<PeopleIcon />} onClick={() => setFilter('type:user')}>
79
79
  <CatalogEntityRelationsTable
80
80
  key="members-table"
@@ -11,6 +11,7 @@ import { HierarchyIcon } from '@redocly/theme/icons/HierarchyIcon/HierarchyIcon'
11
11
  import { Image } from '@redocly/theme/components/Image/Image';
12
12
  import { PREDEFINED_ENTITY_TYPES, useThemeConfig } from '@redocly/theme/core';
13
13
  import { NoteIcon } from '@redocly/theme/icons/NoteIcon/NoteIcon';
14
+ import { CubeIcon } from '@redocly/theme/icons/CubeIcon/CubeIcon';
14
15
 
15
16
  export type CatalogEntityIconProps = {
16
17
  entityType: string;
@@ -59,7 +60,7 @@ const getEntityIcon = ({
59
60
  );
60
61
  }
61
62
 
62
- return <CodeIcon color="var(--catalog-entity-icon-color)" />; // @TODO: add default icon
63
+ return <CubeIcon color="var(--catalog-entity-icon-color)" />;
63
64
  };
64
65
 
65
66
  export function CatalogEntityIcon({
@@ -26,6 +26,11 @@ export function CatalogFilter({
26
26
  }: CatalogFilterProps): JSX.Element | null {
27
27
  if (!filter.parentUsed) return null;
28
28
 
29
+ const filteredOptions = filter.filteredOptions || filter.options;
30
+ if (!filteredOptions || filteredOptions.length === 0) {
31
+ return null;
32
+ }
33
+
29
34
  const FilterComponent = filterComponents[(filter.type || 'checkboxes') as FilterTypes];
30
35
 
31
36
  return (
@@ -23,11 +23,7 @@ export function CatalogTagsWithTooltip({
23
23
  if (showPlaceholder) {
24
24
  return (
25
25
  <CatalogTagsWrapper data-component-name="Catalog/CatalogTagsWithTooltip">
26
- <Tag
27
- variant="outline"
28
- style={{ backgroundColor: 'var(--catalog-tags-placeholder-bg-color)' }}
29
- color="grey"
30
- >
26
+ <Tag style={{ backgroundColor: 'var(--catalog-tags-placeholder-bg-color)' }} color="grey">
31
27
  N/A
32
28
  </Tag>
33
29
  </CatalogTagsWrapper>
@@ -97,7 +97,7 @@ export const catalog = css`
97
97
  /**
98
98
  * @tokens Catalog entity icon
99
99
  */
100
- --catalog-entity-icon-color: var(--color-primary-base);
100
+ --catalog-entity-icon-color: var(--color-persian-green-8);
101
101
  --catalog-entity-border-color: var(--border-color-secondary);
102
102
  --catalog-entity-bg-color: var(--bg-color-tonal);
103
103
 
@@ -1,7 +1,7 @@
1
- import React, { cloneElement, useRef } from 'react';
1
+ import React, { cloneElement, useRef, forwardRef } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
- import type { PropsWithChildren, ReactElement, JSX } from 'react';
4
+ import type { PropsWithChildren, ReactElement } from 'react';
5
5
 
6
6
  import { useOutsideClick, useControlledState } from '@redocly/theme/core/hooks';
7
7
  import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
@@ -32,84 +32,89 @@ export type DropdownProps = PropsWithChildren<{
32
32
  onClose?: () => void;
33
33
  }>;
34
34
 
35
- export function Dropdown({
36
- children,
37
- className,
38
- active,
39
- trigger,
40
- triggerEvent = 'click',
41
- closeOnClick = true,
42
- withArrow,
43
- dataAttributes,
44
- placement,
45
- alignment,
46
- onClick,
47
- onClose,
48
- }: DropdownProps): JSX.Element {
49
- const dropdownRef = useRef<HTMLDivElement | null>(null);
50
- const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
51
-
52
- const handleOpen = () => {
53
- setIsOpen(true);
54
- };
55
-
56
- const handleClose = () => {
57
- setIsOpen(false);
58
- onClose?.();
59
- };
60
-
61
- const handleChildClick = () => {
62
- handleClose();
63
- };
64
-
65
- const handleToggle = (event: React.UIEvent) => {
66
- event.stopPropagation();
67
- event.preventDefault();
68
- setIsOpen(!isOpen);
69
- };
70
-
71
- const handleKeyDown = (event: React.KeyboardEvent) => {
72
- if (event.key === 'Enter' || event.key === ' ') {
73
- handleToggle(event);
74
- }
75
- };
76
-
77
- useOutsideClick(dropdownRef, handleClose);
78
-
79
- const triggerChild = React.Children.only(trigger) as ReactElement<TriggerProps>;
80
-
81
- const dropdownTrigger = cloneElement(triggerChild, {
82
- onClick: triggerEvent === 'click' ? handleToggle : undefined,
83
- icon: withArrow ? isOpen ? <ChevronUpIcon /> : <ChevronDownIcon /> : undefined,
84
- ...(withArrow ? { iconPosition: 'right' } : {}),
85
- ...triggerChild.props,
86
- onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined,
87
- });
88
-
89
- return (
90
- <DropdownWrapper
91
- data-component-name="Dropdown/Dropdown"
92
- data-testid="dropdown"
93
- {...dataAttributes}
94
- className={className}
95
- ref={dropdownRef}
96
- onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
97
- onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
98
- onClick={onClick}
99
- >
100
- {dropdownTrigger}
101
-
102
- <ChildrenWrapper
103
- placement={placement}
104
- alignment={alignment}
105
- isOpen={isOpen}
106
- onClick={closeOnClick ? handleChildClick : undefined}
35
+ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
36
+ (
37
+ {
38
+ children,
39
+ className,
40
+ active,
41
+ trigger,
42
+ triggerEvent = 'click',
43
+ closeOnClick = true,
44
+ withArrow,
45
+ dataAttributes,
46
+ placement,
47
+ alignment,
48
+ onClick,
49
+ onClose,
50
+ },
51
+ ref,
52
+ ) => {
53
+ const dropdownRef = useRef<HTMLDivElement | null>(null);
54
+ const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
55
+
56
+ const handleOpen = () => {
57
+ setIsOpen(true);
58
+ };
59
+
60
+ const handleClose = () => {
61
+ setIsOpen(false);
62
+ onClose?.();
63
+ };
64
+
65
+ const handleChildClick = () => {
66
+ handleClose();
67
+ };
68
+
69
+ const handleToggle = (event: React.UIEvent) => {
70
+ event.stopPropagation();
71
+ event.preventDefault();
72
+ setIsOpen(!isOpen);
73
+ };
74
+
75
+ const handleKeyDown = (event: React.KeyboardEvent) => {
76
+ if (event.key === 'Enter' || event.key === ' ') {
77
+ handleToggle(event);
78
+ }
79
+ };
80
+
81
+ useOutsideClick((ref as React.RefObject<HTMLElement | null>) || dropdownRef, handleClose);
82
+
83
+ const triggerChild = React.Children.only(trigger) as ReactElement<TriggerProps>;
84
+
85
+ const dropdownTrigger = cloneElement(triggerChild, {
86
+ onClick: triggerEvent === 'click' ? handleToggle : undefined,
87
+ icon: withArrow ? isOpen ? <ChevronUpIcon /> : <ChevronDownIcon /> : undefined,
88
+ ...(withArrow ? { iconPosition: 'right' } : {}),
89
+ ...triggerChild.props,
90
+ onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined,
91
+ });
92
+
93
+ return (
94
+ <DropdownWrapper
95
+ data-component-name="Dropdown/Dropdown"
96
+ data-testid="dropdown"
97
+ {...dataAttributes}
98
+ className={className}
99
+ ref={ref || dropdownRef}
100
+ onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
101
+ onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
102
+ onClick={onClick}
107
103
  >
108
- {children}
109
- </ChildrenWrapper>
110
- </DropdownWrapper>
111
- );
112
- }
104
+ {dropdownTrigger}
105
+
106
+ <ChildrenWrapper
107
+ placement={placement}
108
+ alignment={alignment}
109
+ isOpen={isOpen}
110
+ onClick={closeOnClick ? handleChildClick : undefined}
111
+ >
112
+ {children}
113
+ </ChildrenWrapper>
114
+ </DropdownWrapper>
115
+ );
116
+ },
117
+ );
113
118
 
114
119
  const DropdownWrapper = styled.div`
115
120
  --button-gap: var(--spacing-xxs);
@@ -16,6 +16,7 @@ export type LinkProps = {
16
16
  languageInsensitive?: boolean;
17
17
  onClick?: () => void;
18
18
  [key: string]: unknown;
19
+ rel?: string;
19
20
  };
20
21
 
21
22
  export function Link(props: React.PropsWithChildren<LinkProps>): JSX.Element {
@@ -82,6 +82,7 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
82
82
  role={item.link ? 'none' : 'link'}
83
83
  tabIndex={!item.link ? 0 : undefined}
84
84
  data-testid="menu-item-label"
85
+ data-active={item.active}
85
86
  >
86
87
  {hasChevron ? <ChevronWrapper>{chevron}</ChevronWrapper> : null}
87
88
  <MenuItemIcon icon={item.icon} srcSet={item.srcSet} />
@@ -34,12 +34,13 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
34
34
  if (navItem.type !== 'link' && !navItem.items) return null;
35
35
 
36
36
  const item = navItem as ResolvedNavLinkItem;
37
- const normalizedPath =
38
- (item.link && item.link !== '/' ? removeTrailingSlash(item.link) : item.link) || '';
37
+ const normalizedPath = (item.link ? removeTrailingSlash(item.link) : item.link) || '';
39
38
 
40
- const isActive =
41
- pathname ===
42
- withPathPrefix(getPathnameForLocale(normalizedPath, defaultLocale, currentLocale, locales));
39
+ const pathWithPathPrefix = withPathPrefix(
40
+ getPathnameForLocale(normalizedPath, defaultLocale, currentLocale, locales),
41
+ );
42
+
43
+ const isActive = removeTrailingSlash(pathname) === removeTrailingSlash(pathWithPathPrefix);
43
44
 
44
45
  const itemContent = (
45
46
  <NavbarMenuItem
@@ -77,7 +77,7 @@ export function PageActions(props: PageActionProps): JSX.Element | null {
77
77
  </Button>
78
78
  {actions.length > 1 ? (
79
79
  <Dropdown withArrow trigger={<Button />} placement="bottom" alignment="end">
80
- <DropdownMenu items={menuItems} />
80
+ <StyledDropdownMenu items={menuItems} />
81
81
  </Dropdown>
82
82
  ) : null}
83
83
  </ButtonGroup>
@@ -110,3 +110,7 @@ const LinkMenuItem = styled(Link)`
110
110
  text-decoration: none;
111
111
  --link-decoration-hover: none;
112
112
  `;
113
+
114
+ const StyledDropdownMenu = styled(DropdownMenu)`
115
+ --dropdown-menu-max-height: var(--page-actions-dropdown-max-height);
116
+ `;
@@ -7,6 +7,8 @@ export const pageActions = css`
7
7
  --page-actions-button-text-color: var(--text-color-secondary);
8
8
  --page-actions-button-padding: 5px 14px 5px var(--spacing-sm);
9
9
 
10
+ --page-actions-dropdown-max-height: fit-content;
11
+
10
12
  --page-actions-menu-item-padding: 3px 0;
11
13
  --page-actions-menu-item-gap: var(--spacing-xs);
12
14
  --page-actions-menu-item-icon-color: var(--icon-color-secondary);
@@ -3,7 +3,7 @@ import styled from 'styled-components';
3
3
 
4
4
  import type { SearchFacet, SearchFacetCount } from '@redocly/theme/core/types';
5
5
 
6
- import { Tag, type TagProps } from '@redocly/theme/components/Tag/Tag';
6
+ import { Tag } from '@redocly/theme/components/Tag/Tag';
7
7
 
8
8
  type SearchFilterFieldTagsProps = {
9
9
  className?: string;
@@ -47,6 +47,7 @@ export function SearchFilterFieldTags({
47
47
  }}
48
48
  active={active}
49
49
  borderless
50
+ selectable
50
51
  >
51
52
  {value} {isCounterVisible && <span>{count}</span>}
52
53
  </FilterTagWrapper>
@@ -62,9 +63,8 @@ const FilterTagsWrapper = styled.div`
62
63
  gap: var(--search-filter-field-tags-gap);
63
64
  `;
64
65
 
65
- const FilterTagWrapper = styled(Tag)<{ color: TagProps['color'] }>`
66
+ const FilterTagWrapper = styled(Tag)`
66
67
  text-transform: uppercase;
67
68
  cursor: pointer;
68
- ${({ color }) => color && `background-color: var(--tag-operation-bg-color-${color});`}
69
69
  margin: var(--search-filter-field-tags-tag-margin);
70
70
  `;
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+
6
+ import { FeedbackType } from '@redocly/theme/core/types';
7
+ import { Button } from '@redocly/theme/components/Button/Button';
8
+ import { ThumbUpIcon } from '@redocly/theme/icons/ThumbUpIcon/ThumbUpIcon';
9
+ import { ThumbUpFilledIcon } from '@redocly/theme/icons/ThumbUpFilledIcon/ThumbUpFilledIcon';
10
+ import { ThumbDownIcon } from '@redocly/theme/icons/ThumbDownIcon/ThumbDownIcon';
11
+ import { ThumbDownFilledIcon } from '@redocly/theme/icons/ThumbDownFilledIcon/ThumbDownFilledIcon';
12
+ import { CopyButton } from '@redocly/theme/components/Buttons/CopyButton';
13
+
14
+ export type SearchAiActionButtonsProps = {
15
+ content: string;
16
+ className?: string;
17
+ feedback?: FeedbackType;
18
+ onFeedback: (feedback: FeedbackType) => void;
19
+ disabled?: boolean;
20
+ };
21
+
22
+ export function SearchAiActionButtons({
23
+ content,
24
+ className,
25
+ feedback,
26
+ onFeedback,
27
+ disabled,
28
+ }: SearchAiActionButtonsProps): JSX.Element {
29
+ return (
30
+ <ActionButtonsWrapper className={className} data-component-name="Search/SearchAiActionButtons">
31
+ <CopyButton data={content} />
32
+ <FeedbackButton
33
+ variant="text"
34
+ size="small"
35
+ icon={feedback === FeedbackType.Like ? <ThumbUpFilledIcon /> : <ThumbUpIcon />}
36
+ onClick={() => !disabled && onFeedback(FeedbackType.Like)}
37
+ extraClass={feedback === FeedbackType.Like ? 'active' : ''}
38
+ aria-label="Like this response"
39
+ disabled={disabled}
40
+ />
41
+
42
+ <FeedbackButton
43
+ variant="text"
44
+ size="small"
45
+ icon={feedback === FeedbackType.Dislike ? <ThumbDownFilledIcon /> : <ThumbDownIcon />}
46
+ onClick={() => !disabled && onFeedback(FeedbackType.Dislike)}
47
+ extraClass={feedback === FeedbackType.Dislike ? 'active' : ''}
48
+ aria-label="Dislike this response"
49
+ disabled={disabled}
50
+ />
51
+ </ActionButtonsWrapper>
52
+ );
53
+ }
54
+
55
+ const ActionButtonsWrapper = styled.div`
56
+ display: flex;
57
+ align-items: center;
58
+ gap: var(--search-ai-feedback-gap);
59
+ `;
60
+
61
+ const FeedbackButton = styled(Button)`
62
+ &:disabled {
63
+ pointer-events: none;
64
+ cursor: default;
65
+ opacity: 1;
66
+ background-color: var(--button-bg-color);
67
+ color: var(--button-color);
68
+ border-color: var(--button-border-color);
69
+ }
70
+
71
+ &:disabled.active {
72
+ background-color: var(--button-bg-color-active);
73
+ border-color: var(--button-border-color-active);
74
+ color: var(--button-color-active);
75
+ }
76
+ `;