@redocly/theme 0.69.0-next.1 → 0.69.0-next.11

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 (280) hide show
  1. package/lib/components/Accordion/Accordion.js +5 -1
  2. package/lib/components/Accordion/variables.js +1 -1
  3. package/lib/components/Admonition/Admonition.d.ts +1 -1
  4. package/lib/components/Admonition/Admonition.js +1 -1
  5. package/lib/components/Admonition/variables.dark.js +2 -2
  6. package/lib/components/Admonition/variables.js +10 -10
  7. package/lib/components/Badge/Badge.d.ts +3 -1
  8. package/lib/components/Badge/Badge.js +7 -2
  9. package/lib/components/Banner/variables.dark.js +2 -0
  10. package/lib/components/Banner/variables.js +3 -1
  11. package/lib/components/Breadcrumbs/Breadcrumb.js +1 -1
  12. package/lib/components/Breadcrumbs/variables.js +1 -1
  13. package/lib/components/Button/ButtonGroup.d.ts +3 -5
  14. package/lib/components/Button/ButtonGroup.js +25 -5
  15. package/lib/components/Button/variables.js +27 -4
  16. package/lib/components/Buttons/ConnectMCPButton.js +39 -123
  17. package/lib/components/Buttons/CopyButton.js +12 -1
  18. package/lib/components/Buttons/variables.d.ts +0 -1
  19. package/lib/components/Buttons/variables.js +3 -44
  20. package/lib/components/Catalog/CatalogEntities.js +1 -0
  21. package/lib/components/Catalog/CatalogEntity/CatalogEntityLinks.js +1 -1
  22. package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +1 -1
  23. package/lib/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.js +1 -1
  24. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.js +5 -3
  25. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +1 -1
  26. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.js +1 -0
  27. package/lib/components/Catalog/CatalogViewModeToggle.d.ts +1 -1
  28. package/lib/components/Catalog/CatalogViewModeToggle.js +2 -43
  29. package/lib/components/Catalog/variables.js +5 -5
  30. package/lib/components/CatalogClassic/variables.js +2 -2
  31. package/lib/components/CodeBlock/CodeBlock.js +2 -1
  32. package/lib/components/CodeBlock/CodeBlockContainer.js +1 -0
  33. package/lib/components/Dropdown/Dropdown.d.ts +10 -20
  34. package/lib/components/Dropdown/Dropdown.js +13 -2
  35. package/lib/components/Dropdown/DropdownMenuItemContent.d.ts +9 -0
  36. package/lib/components/Dropdown/DropdownMenuItemContent.js +56 -0
  37. package/lib/components/Dropdown/variables.js +19 -2
  38. package/lib/components/Feedback/Mood.js +18 -14
  39. package/lib/components/Feedback/Rating.js +18 -13
  40. package/lib/components/Feedback/Scale.js +27 -22
  41. package/lib/components/Feedback/Sentiment.js +17 -13
  42. package/lib/components/Filter/FilterInput.js +4 -2
  43. package/lib/components/Filter/FilterSelect.js +1 -0
  44. package/lib/components/Filter/variables.js +2 -2
  45. package/lib/components/Image/variables.js +5 -3
  46. package/lib/components/JsonViewer/Helpers.js +12 -42
  47. package/lib/components/LanguagePicker/variables.js +1 -1
  48. package/lib/components/MCP/McpConnectionTabs.d.ts +7 -0
  49. package/lib/components/MCP/McpConnectionTabs.js +161 -0
  50. package/lib/components/Markdown/styles/base-table.js +8 -0
  51. package/lib/components/Markdown/variables.js +1 -1
  52. package/lib/components/Menu/MenuItem.js +1 -1
  53. package/lib/components/Menu/variables.js +1 -1
  54. package/lib/components/Navbar/variables.js +1 -1
  55. package/lib/components/PageActions/PageActions.js +4 -6
  56. package/lib/components/PageActions/PageActionsMenuItem.d.ts +4 -4
  57. package/lib/components/PageActions/PageActionsMenuItem.js +27 -43
  58. package/lib/components/Panel/Panel.js +5 -1
  59. package/lib/components/Panel/variables.js +1 -1
  60. package/lib/components/Product/variables.js +2 -1
  61. package/lib/components/ReplayGate/ReplayGate.d.ts +1 -0
  62. package/lib/components/Search/FilterFields/SearchFilterFieldSelect.js +22 -32
  63. package/lib/components/Search/SearchAiDialog.js +4 -1
  64. package/lib/components/Search/SearchAiMessage.js +1 -1
  65. package/lib/components/Search/SearchAiNegativeFeedbackForm.js +2 -2
  66. package/lib/components/Search/SearchDialog.js +2 -2
  67. package/lib/components/Search/SearchItem.js +6 -3
  68. package/lib/components/Search/SearchRecent.js +1 -0
  69. package/lib/components/Search/SearchSuggestedPages.js +1 -0
  70. package/lib/components/Search/variables.js +9 -5
  71. package/lib/components/Segmented/Segmented.js +3 -3
  72. package/lib/components/Select/Select.js +30 -23
  73. package/lib/components/Select/variables.js +3 -3
  74. package/lib/components/Sidebar/Sidebar.js +2 -38
  75. package/lib/components/SplitButton/SplitButton.d.ts +21 -0
  76. package/lib/components/SplitButton/SplitButton.js +37 -0
  77. package/lib/components/Switch/variables.dark.js +2 -2
  78. package/lib/components/Switch/variables.js +2 -2
  79. package/lib/components/Tag/variables.js +2 -2
  80. package/lib/components/Tags/variables.js +1 -1
  81. package/lib/components/Toast/variables.js +1 -1
  82. package/lib/components/Tooltip/AnchorTooltip.js +2 -2
  83. package/lib/components/Tooltip/JsTooltip.js +2 -2
  84. package/lib/components/UserMenu/variables.js +1 -1
  85. package/lib/core/constants/mcp.d.ts +4 -0
  86. package/lib/core/constants/mcp.js +10 -1
  87. package/lib/core/constants/search.js +2 -2
  88. package/lib/core/contexts/AccordionGroupContext.d.ts +1 -0
  89. package/lib/core/contexts/AccordionGroupContext.js +6 -0
  90. package/lib/core/contexts/PageActionsContext.d.ts +5 -0
  91. package/lib/core/contexts/PageActionsContext.js +10 -0
  92. package/lib/core/contexts/index.d.ts +2 -0
  93. package/lib/core/contexts/index.js +2 -0
  94. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +5 -17
  95. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +2 -0
  96. package/lib/core/hooks/menu/use-menu-item-expanded.js +1 -0
  97. package/lib/core/hooks/use-active-heading.js +63 -15
  98. package/lib/core/hooks/use-active-section-id.js +3 -1
  99. package/lib/core/hooks/use-connect-mcp-button.d.ts +1 -1
  100. package/lib/core/hooks/use-connect-mcp-button.js +23 -15
  101. package/lib/core/hooks/use-control.js +3 -0
  102. package/lib/core/hooks/use-local-state.js +1 -0
  103. package/lib/core/hooks/use-mcp-config.d.ts +2 -0
  104. package/lib/core/hooks/use-mcp-config.js +14 -4
  105. package/lib/core/hooks/use-mobile-menu.js +10 -2
  106. package/lib/core/hooks/use-tabs.js +4 -2
  107. package/lib/core/hooks/use-telemetry-fallback.d.ts +2 -0
  108. package/lib/core/hooks/use-telemetry-fallback.js +2 -0
  109. package/lib/core/styles/global.js +16 -11
  110. package/lib/core/styles/palette.js +2 -0
  111. package/lib/core/types/button-group.d.ts +2 -0
  112. package/lib/core/types/button-group.js +3 -0
  113. package/lib/core/types/catalog.d.ts +2 -4
  114. package/lib/core/types/common.d.ts +2 -0
  115. package/lib/core/types/hooks.d.ts +1 -0
  116. package/lib/core/types/index.d.ts +1 -0
  117. package/lib/core/types/l10n.d.ts +1 -1
  118. package/lib/core/types/mcp.d.ts +1 -1
  119. package/lib/core/utils/RenderWithLink.d.ts +2 -0
  120. package/lib/core/utils/RenderWithLink.js +20 -0
  121. package/lib/core/utils/index.d.ts +1 -0
  122. package/lib/core/utils/index.js +1 -0
  123. package/lib/core/utils/mcp.d.ts +5 -0
  124. package/lib/core/utils/mcp.js +35 -0
  125. package/lib/icons/CodexIcon/CodexIcon.d.ts +25 -0
  126. package/lib/icons/CodexIcon/CodexIcon.js +22 -0
  127. package/lib/icons/LinkIcon/LinkIcon.js +1 -1
  128. package/lib/index.d.ts +2 -0
  129. package/lib/index.js +2 -0
  130. package/lib/layouts/McpNotAllowed.js +10 -10
  131. package/lib/markdoc/components/Accordion/Accordion.js +6 -6
  132. package/lib/markdoc/components/Accordion/AccordionGroup.d.ts +2 -1
  133. package/lib/markdoc/components/Accordion/AccordionGroup.js +39 -3
  134. package/lib/markdoc/components/Cards/Card.d.ts +8 -1
  135. package/lib/markdoc/components/Cards/Card.js +55 -16
  136. package/lib/markdoc/components/Cards/CardBadge.d.ts +9 -0
  137. package/lib/markdoc/components/Cards/CardBadge.js +30 -0
  138. package/lib/markdoc/components/Cards/CardIcon.d.ts +5 -4
  139. package/lib/markdoc/components/Cards/CardIcon.js +6 -3
  140. package/lib/markdoc/components/Cards/variables.dark.js +32 -0
  141. package/lib/markdoc/components/Cards/variables.js +81 -7
  142. package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +1 -0
  143. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +1 -1
  144. package/lib/markdoc/components/CodeWalkthrough/variables.js +2 -1
  145. package/lib/markdoc/components/Heading/Heading.js +5 -2
  146. package/lib/markdoc/components/Tabs/TabList.js +6 -2
  147. package/lib/markdoc/components/Tabs/Tabs.js +4 -2
  148. package/lib/markdoc/components/Tabs/variables.js +4 -4
  149. package/lib/markdoc/tags/accordion-group.js +3 -0
  150. package/lib/markdoc/tags/admonition.js +1 -1
  151. package/lib/markdoc/tags/card.js +14 -0
  152. package/lib/markdoc/tags/connect-mcp.js +2 -1
  153. package/package.json +19 -10
  154. package/src/components/Accordion/Accordion.tsx +7 -3
  155. package/src/components/Accordion/variables.ts +1 -1
  156. package/src/components/Admonition/Admonition.tsx +2 -2
  157. package/src/components/Admonition/variables.dark.ts +2 -2
  158. package/src/components/Admonition/variables.ts +10 -10
  159. package/src/components/Badge/Badge.tsx +16 -2
  160. package/src/components/Banner/variables.dark.ts +2 -0
  161. package/src/components/Banner/variables.ts +3 -1
  162. package/src/components/Breadcrumbs/Breadcrumb.tsx +1 -1
  163. package/src/components/Breadcrumbs/variables.ts +1 -1
  164. package/src/components/Button/ButtonGroup.tsx +28 -10
  165. package/src/components/Button/variables.ts +27 -4
  166. package/src/components/Buttons/ConnectMCPButton.tsx +88 -145
  167. package/src/components/Buttons/CopyButton.tsx +14 -2
  168. package/src/components/Buttons/variables.ts +2 -44
  169. package/src/components/Catalog/CatalogEntities.tsx +1 -0
  170. package/src/components/Catalog/CatalogEntity/CatalogEntityLinks.tsx +1 -1
  171. package/src/components/Catalog/CatalogEntity/CatalogEntityMetadata.tsx +1 -1
  172. package/src/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.tsx +1 -1
  173. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.tsx +6 -4
  174. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +1 -1
  175. package/src/components/Catalog/CatalogFilter/CatalogFilterSelect.tsx +1 -0
  176. package/src/components/Catalog/CatalogViewModeToggle.tsx +2 -17
  177. package/src/components/Catalog/variables.ts +5 -5
  178. package/src/components/CatalogClassic/variables.ts +2 -2
  179. package/src/components/CodeBlock/CodeBlock.tsx +2 -1
  180. package/src/components/CodeBlock/CodeBlockContainer.tsx +1 -0
  181. package/src/components/Dropdown/Dropdown.tsx +25 -4
  182. package/src/components/Dropdown/DropdownMenuItemContent.tsx +72 -0
  183. package/src/components/Dropdown/variables.ts +19 -2
  184. package/src/components/Feedback/Mood.tsx +22 -15
  185. package/src/components/Feedback/Rating.tsx +22 -14
  186. package/src/components/Feedback/Scale.tsx +40 -32
  187. package/src/components/Feedback/Sentiment.tsx +21 -14
  188. package/src/components/Filter/FilterInput.tsx +5 -3
  189. package/src/components/Filter/FilterSelect.tsx +1 -0
  190. package/src/components/Filter/variables.ts +2 -2
  191. package/src/components/Image/variables.ts +5 -3
  192. package/src/components/JsonViewer/Helpers.tsx +13 -9
  193. package/src/components/LanguagePicker/variables.ts +1 -1
  194. package/src/components/MCP/McpConnectionTabs.tsx +285 -0
  195. package/src/components/Markdown/styles/base-table.ts +8 -0
  196. package/src/components/Markdown/variables.ts +1 -1
  197. package/src/components/Menu/MenuItem.tsx +2 -2
  198. package/src/components/Menu/variables.ts +1 -1
  199. package/src/components/Navbar/variables.ts +1 -1
  200. package/src/components/PageActions/PageActions.tsx +22 -23
  201. package/src/components/PageActions/PageActionsMenuItem.tsx +41 -55
  202. package/src/components/Panel/Panel.tsx +7 -3
  203. package/src/components/Panel/variables.ts +1 -1
  204. package/src/components/Product/variables.ts +2 -1
  205. package/src/components/ReplayGate/ReplayGate.tsx +3 -0
  206. package/src/components/Search/FilterFields/SearchFilterFieldSelect.tsx +32 -40
  207. package/src/components/Search/SearchAiDialog.tsx +4 -1
  208. package/src/components/Search/SearchAiMessage.tsx +1 -1
  209. package/src/components/Search/SearchAiNegativeFeedbackForm.tsx +2 -2
  210. package/src/components/Search/SearchDialog.tsx +2 -2
  211. package/src/components/Search/SearchItem.tsx +7 -1
  212. package/src/components/Search/SearchRecent.tsx +1 -0
  213. package/src/components/Search/SearchSuggestedPages.tsx +1 -0
  214. package/src/components/Search/variables.ts +9 -5
  215. package/src/components/Segmented/Segmented.tsx +6 -3
  216. package/src/components/Select/Select.tsx +36 -22
  217. package/src/components/Select/variables.ts +3 -3
  218. package/src/components/Sidebar/Sidebar.tsx +2 -6
  219. package/src/components/SplitButton/SplitButton.tsx +95 -0
  220. package/src/components/Switch/variables.dark.ts +2 -2
  221. package/src/components/Switch/variables.ts +2 -2
  222. package/src/components/Tag/variables.ts +2 -2
  223. package/src/components/Tags/variables.ts +1 -1
  224. package/src/components/Toast/variables.ts +1 -1
  225. package/src/components/Tooltip/AnchorTooltip.tsx +2 -2
  226. package/src/components/Tooltip/JsTooltip.tsx +2 -2
  227. package/src/components/UserMenu/variables.ts +1 -1
  228. package/src/core/constants/mcp.ts +14 -0
  229. package/src/core/constants/search.ts +2 -2
  230. package/src/core/contexts/AccordionGroupContext.tsx +3 -0
  231. package/src/core/contexts/PageActionsContext.tsx +7 -0
  232. package/src/core/contexts/index.ts +2 -0
  233. package/src/core/hooks/__mocks__/use-theme-hooks.ts +2 -0
  234. package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +6 -20
  235. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +2 -0
  236. package/src/core/hooks/menu/use-menu-item-expanded.ts +1 -0
  237. package/src/core/hooks/use-active-heading.ts +83 -15
  238. package/src/core/hooks/use-active-section-id.ts +3 -1
  239. package/src/core/hooks/use-connect-mcp-button.ts +31 -17
  240. package/src/core/hooks/use-control.ts +5 -1
  241. package/src/core/hooks/use-local-state.ts +1 -0
  242. package/src/core/hooks/use-mcp-config.ts +15 -4
  243. package/src/core/hooks/use-mobile-menu.ts +11 -3
  244. package/src/core/hooks/use-tabs.ts +4 -2
  245. package/src/core/hooks/use-telemetry-fallback.ts +2 -0
  246. package/src/core/openapi/index.ts +1 -1
  247. package/src/core/styles/global.ts +17 -12
  248. package/src/core/styles/palette.ts +2 -0
  249. package/src/core/types/button-group.ts +2 -0
  250. package/src/core/types/catalog.ts +2 -4
  251. package/src/core/types/common.ts +3 -0
  252. package/src/core/types/hooks.ts +1 -0
  253. package/src/core/types/index.ts +1 -0
  254. package/src/core/types/l10n.ts +29 -1
  255. package/src/core/types/mcp.ts +1 -1
  256. package/src/core/utils/RenderWithLink.tsx +19 -0
  257. package/src/core/utils/index.ts +1 -0
  258. package/src/core/utils/mcp.ts +39 -0
  259. package/src/icons/CodexIcon/CodexIcon.tsx +23 -0
  260. package/src/icons/LinkIcon/LinkIcon.tsx +1 -1
  261. package/src/index.ts +2 -0
  262. package/src/layouts/McpNotAllowed.tsx +37 -11
  263. package/src/markdoc/components/Accordion/Accordion.tsx +8 -2
  264. package/src/markdoc/components/Accordion/AccordionGroup.tsx +9 -2
  265. package/src/markdoc/components/Cards/Card.tsx +79 -14
  266. package/src/markdoc/components/Cards/CardBadge.tsx +39 -0
  267. package/src/markdoc/components/Cards/CardIcon.tsx +16 -5
  268. package/src/markdoc/components/Cards/variables.dark.ts +32 -0
  269. package/src/markdoc/components/Cards/variables.ts +81 -7
  270. package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +1 -0
  271. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +1 -1
  272. package/src/markdoc/components/CodeWalkthrough/variables.ts +2 -1
  273. package/src/markdoc/components/Heading/Heading.tsx +7 -4
  274. package/src/markdoc/components/Tabs/TabList.tsx +6 -2
  275. package/src/markdoc/components/Tabs/Tabs.tsx +4 -2
  276. package/src/markdoc/components/Tabs/variables.ts +4 -4
  277. package/src/markdoc/tags/accordion-group.ts +3 -0
  278. package/src/markdoc/tags/admonition.ts +1 -1
  279. package/src/markdoc/tags/card.ts +14 -0
  280. package/src/markdoc/tags/connect-mcp.ts +3 -1
@@ -0,0 +1,285 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX, ReactNode } from 'react';
5
+ import type { TFunction } from '@redocly/theme/core/types';
6
+ import type { AsyncApiRealmUI } from '@redocly/realm-asyncapi-sdk';
7
+ import type { CodeBlockControlsProps } from '@redocly/theme/components/CodeBlock/CodeBlockControls';
8
+
9
+ import { useMCPConfig, useThemeHooks } from '@redocly/theme/core/hooks';
10
+ import {
11
+ generateClaudeCodeCommand,
12
+ generateClaudeDesktopConfig,
13
+ generateCodexCommand,
14
+ generateMcpInstallPrompt,
15
+ getBaseDataAttributes,
16
+ } from '@redocly/theme/core/utils';
17
+ import { CodeBlock } from '@redocly/theme/components/CodeBlock/CodeBlock';
18
+ import { ClaudeIcon } from '@redocly/theme/icons/ClaudeIcon/ClaudeIcon';
19
+ import { ChatGptIcon } from '@redocly/theme/icons/ChatGptIcon/ChatGptIcon';
20
+ import { Tabs, TabsSize } from '@redocly/theme/markdoc/components/Tabs/Tabs';
21
+ import { TabItemFragment } from '@redocly/theme/markdoc/components/Tabs/TabItemFragment';
22
+ import { NumberedList } from '@redocly/theme/markdoc/components/NumberedList/NumberedList';
23
+ import { NumberedItem } from '@redocly/theme/markdoc/components/NumberedList/NumberedItem';
24
+
25
+ export type McpConnectionTab = 'claude' | 'chatgpt' | 'json';
26
+
27
+ export type McpConnectionTabsProps = {
28
+ options?: McpConnectionTab[];
29
+ className?: string;
30
+ };
31
+
32
+ const DEFAULT_TAB_OPTIONS: McpConnectionTab[] = ['claude', 'chatgpt', 'json'];
33
+
34
+ const TAB_LABELS: Record<
35
+ McpConnectionTab,
36
+ { translationKey: string; defaultLabel: string; icon?: React.ComponentType }
37
+ > = {
38
+ claude: {
39
+ translationKey: 'page.mcpNotAllowed.tabs.claude',
40
+ defaultLabel: 'Claude',
41
+ icon: ClaudeIcon,
42
+ },
43
+ chatgpt: {
44
+ translationKey: 'page.mcpNotAllowed.tabs.chatgpt',
45
+ defaultLabel: 'ChatGPT',
46
+ icon: ChatGptIcon,
47
+ },
48
+ json: { translationKey: 'page.mcpNotAllowed.tabs.json', defaultLabel: 'JSON' },
49
+ };
50
+
51
+ type TabClient = { headline: string; content: ReactNode };
52
+
53
+ type SnippetId =
54
+ AsyncApiRealmUI.EventPayload<'com.redocly.mcpConnectionSnippet.copied'>[0]['snippet'];
55
+
56
+ type SnippetHeader = (client: McpConnectionTab, snippet: SnippetId) => CodeBlockControlsProps;
57
+
58
+ export function McpConnectionTabs({
59
+ options = DEFAULT_TAB_OPTIONS,
60
+ className,
61
+ }: McpConnectionTabsProps): JSX.Element | null {
62
+ const { useTranslate, useTelemetry } = useThemeHooks();
63
+ const { translate } = useTranslate();
64
+ const telemetry = useTelemetry();
65
+ const { serverName, serverUrl, isMcpDisabled } = useMCPConfig();
66
+ // The SSR markup of error pages is cached with the build-time origin, so the
67
+ // tabs remount after hydration to pick up the real browser origin.
68
+ const [hydrated, setHydrated] = useState(false);
69
+
70
+ useEffect(() => setHydrated(true), []);
71
+
72
+ if (isMcpDisabled) {
73
+ return null;
74
+ }
75
+
76
+ const connection = { serverName, url: serverUrl };
77
+
78
+ const snippetHeader: SnippetHeader = (client, snippet) => ({
79
+ controls: {
80
+ copy: {
81
+ onClick: () =>
82
+ telemetry.sendMcpConnectionSnippetCopiedMessage([
83
+ { ...getBaseDataAttributes('mcpConnectionSnippet', 'button'), client, snippet },
84
+ ]),
85
+ },
86
+ // The report control is docs feedback, not useful on the connection page.
87
+ report: { hidden: true },
88
+ },
89
+ });
90
+
91
+ return (
92
+ <McpConnectionTabsWrapper
93
+ key={hydrated ? 'client' : 'ssr'}
94
+ data-component-name="MCP/McpConnectionTabs"
95
+ data-testid="mcp-connection-tabs"
96
+ className={className}
97
+ >
98
+ <Tabs size={TabsSize.SMALL}>
99
+ {options.map((option) => {
100
+ const { translationKey, defaultLabel, icon: TabIcon } = TAB_LABELS[option];
101
+
102
+ return (
103
+ <TabItemFragment
104
+ key={option}
105
+ label={translate(translationKey, defaultLabel)}
106
+ icon={TabIcon ? <TabIcon /> : undefined}
107
+ >
108
+ {renderTabContent(option, connection, translate, snippetHeader)}
109
+ </TabItemFragment>
110
+ );
111
+ })}
112
+ </Tabs>
113
+ </McpConnectionTabsWrapper>
114
+ );
115
+ }
116
+
117
+ function renderTabContent(
118
+ option: McpConnectionTab,
119
+ connection: { serverName: string; url: string },
120
+ translate: TFunction,
121
+ snippetHeader: SnippetHeader,
122
+ ): ReactNode {
123
+ const terminalHint = (
124
+ <TabHint>
125
+ {translate('page.mcpNotAllowed.snippets.terminal.hint', 'Run this command in your terminal:')}
126
+ </TabHint>
127
+ );
128
+
129
+ const urlBlock = (
130
+ <CodeBlock lang="text" source={connection.url} header={snippetHeader(option, 'chatgpt-app')} />
131
+ );
132
+
133
+ const assistantPromptClient: TabClient = {
134
+ headline: translate('page.mcpNotAllowed.snippets.prompt.headline', 'Prompt'),
135
+ content: (
136
+ <>
137
+ <TabHint>
138
+ {translate(
139
+ 'page.mcpNotAllowed.snippets.prompt.hint',
140
+ 'Or paste this prompt into the chat to have it add the server for you:',
141
+ )}
142
+ </TabHint>
143
+ <CodeBlock
144
+ lang="text"
145
+ source={generateMcpInstallPrompt(connection)}
146
+ header={snippetHeader(option, 'prompt')}
147
+ wrapContents
148
+ />
149
+ </>
150
+ ),
151
+ };
152
+
153
+ switch (option) {
154
+ case 'claude':
155
+ return renderClientList([
156
+ {
157
+ headline: translate('page.mcpNotAllowed.snippets.claudeCode.headline', 'Claude Code'),
158
+ content: (
159
+ <>
160
+ {terminalHint}
161
+ <CodeBlock
162
+ lang="shell"
163
+ source={generateClaudeCodeCommand(connection)}
164
+ header={snippetHeader(option, 'claude-code')}
165
+ wrapContents
166
+ />
167
+ </>
168
+ ),
169
+ },
170
+ {
171
+ headline: translate(
172
+ 'page.mcpNotAllowed.snippets.claudeDesktop.headline',
173
+ 'Claude Desktop',
174
+ ),
175
+ content: (
176
+ <>
177
+ <TabHint>
178
+ {translate(
179
+ 'page.mcpNotAllowed.snippets.claudeDesktop.instruction',
180
+ 'In Claude Desktop, open Settings → Developer → Edit Config, add this to the configuration file, and restart Claude Desktop:',
181
+ )}
182
+ </TabHint>
183
+ <CodeBlock
184
+ lang="json"
185
+ source={generateClaudeDesktopConfig(connection)}
186
+ header={snippetHeader(option, 'claude-desktop')}
187
+ />
188
+ </>
189
+ ),
190
+ },
191
+ ]);
192
+ case 'chatgpt':
193
+ return renderClientList([
194
+ {
195
+ headline: translate('page.mcpNotAllowed.snippets.codexCli.headline', 'Codex CLI'),
196
+ content: (
197
+ <>
198
+ {terminalHint}
199
+ <CodeBlock
200
+ lang="shell"
201
+ source={generateCodexCommand(connection)}
202
+ header={snippetHeader(option, 'codex')}
203
+ wrapContents
204
+ />
205
+ </>
206
+ ),
207
+ },
208
+ {
209
+ headline: translate(
210
+ 'page.mcpNotAllowed.snippets.chatgptApp.headline',
211
+ 'ChatGPT desktop app',
212
+ ),
213
+ content: (
214
+ <>
215
+ <TabHint>
216
+ {translate(
217
+ 'page.mcpNotAllowed.snippets.chatgptApp.instruction',
218
+ 'In the ChatGPT desktop app, go to Settings → Plugins → MCPs and add a server with the "Streamable HTTP" type and this URL (restart the app afterwards):',
219
+ )}
220
+ </TabHint>
221
+ {urlBlock}
222
+ </>
223
+ ),
224
+ },
225
+ assistantPromptClient,
226
+ ]);
227
+ case 'json':
228
+ return (
229
+ <>
230
+ <TabHint>
231
+ {translate(
232
+ 'page.mcpNotAllowed.snippets.json.hint',
233
+ 'Add this server entry to your MCP client configuration file:',
234
+ )}
235
+ </TabHint>
236
+ <CodeBlock
237
+ lang="json"
238
+ source={JSON.stringify(
239
+ { [connection.serverName]: { url: connection.url, description: 'MCP Server' } },
240
+ null,
241
+ 2,
242
+ )}
243
+ header={snippetHeader(option, 'json')}
244
+ />
245
+ </>
246
+ );
247
+ }
248
+ }
249
+
250
+ function renderClientList(clients: TabClient[]): ReactNode {
251
+ return (
252
+ <NumberedList>
253
+ {clients.map((client, index) => (
254
+ <NumberedItem
255
+ key={index}
256
+ type="dot"
257
+ size="small"
258
+ headline={client.headline}
259
+ index={index + 1}
260
+ isLast={index === clients.length - 1}
261
+ >
262
+ {client.content}
263
+ </NumberedItem>
264
+ ))}
265
+ </NumberedList>
266
+ );
267
+ }
268
+
269
+ const McpConnectionTabsWrapper = styled.div`
270
+ --md-numbered-list-margin: 0 0 var(--spacing-sm);
271
+
272
+ font-size: var(--page-mcp-405-snippets-font-size);
273
+ line-height: var(--page-mcp-405-snippets-line-height);
274
+ text-align: left;
275
+
276
+ /* The copy control floats over the code, so wrapped lines need room to clear it. */
277
+ pre[data-testid='source-code'] {
278
+ padding-right: var(--page-mcp-405-snippets-code-padding-right);
279
+ }
280
+ `;
281
+
282
+ const TabHint = styled.p`
283
+ margin: 0 0 var(--spacing-xs);
284
+ color: var(--page-mcp-405-snippets-hint-text-color);
285
+ `;
@@ -45,6 +45,14 @@ export const markdownBaseTableCss = css`
45
45
  background-color: var(--md-table-header-bg-color);
46
46
  }
47
47
 
48
+ /* hide the header row when every cell is empty; one labelled cell keeps it visible */
49
+ thead:has(th:empty) {
50
+ display: none;
51
+ }
52
+ thead:has(th:not(:empty)) {
53
+ display: table-header-group;
54
+ }
55
+
48
56
  tr {
49
57
  background-color: var(--md-table-stripe-bg-color);
50
58
  }
@@ -40,7 +40,7 @@ export const markdown = css`
40
40
  --md-table-line-height: var(--line-height-md); // @presenter FontSize
41
41
  --md-table-margin-vertical: 20px; // @presenter Spacing
42
42
  --md-table-bg-color: transparent; // @presenter Color
43
- --md-table-border-radius: var(--border-radius);
43
+ --md-table-border-radius: var(--border-radius-xl);
44
44
 
45
45
  --md-table-border-width: 1px;
46
46
  --md-table-header-bg-color: var(--layer-color);
@@ -122,8 +122,8 @@ function MenuItemImpl(props: React.PropsWithChildren<MenuItemProps>): JSX.Elemen
122
122
  onClick={handleOnClick}
123
123
  onKeyDown={handleExpandOnEnter}
124
124
  ref={labelRef}
125
- role={item.link ? 'none' : 'link'}
126
- tabIndex={!item.link ? 0 : undefined}
125
+ role={item.link || isSeparator ? 'none' : 'link'}
126
+ tabIndex={!item.link && !isSeparator ? 0 : undefined}
127
127
  data-testid="menu-item-label"
128
128
  data-active={item.active}
129
129
  >
@@ -56,7 +56,7 @@ export const menu = css`
56
56
  --menu-item-label-margin: var(--menu-item-label-margin-vertical) var(--menu-item-label-margin-horizontal);
57
57
  --menu-item-label-padding: var(--menu-item-padding-vertical) var(--menu-item-padding-horizontal);
58
58
  --menu-item-label-gap: var(--spacing-xxs);
59
- --menu-item-label-border-radius: 6px;
59
+ --menu-item-label-border-radius: var(--border-radius-lg);
60
60
  --menu-item-label-chevron-size: 12px;
61
61
  --menu-item-label-chevron-offset: calc(var(--menu-item-label-gap) + var(--menu-item-label-chevron-size));
62
62
 
@@ -27,7 +27,7 @@ export const navbar = css`
27
27
 
28
28
  --navbar-item-line-height: var(--line-height-base);
29
29
  --navbar-item-bg-color-active: var(--bg-color-active);
30
- --navbar-item-border-radius: var(--border-radius);
30
+ --navbar-item-border-radius: var(--border-radius-lg);
31
31
  --navbar-item-bottom-border: none;
32
32
 
33
33
  --navbar-item-text-color-hover: var(--text-color-primary);
@@ -7,13 +7,12 @@ import type { PageAction } from '@redocly/theme/core/types';
7
7
  import { PageActionsMenuItem } from '@redocly/theme/components/PageActions/PageActionsMenuItem';
8
8
  import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
9
9
  import { Link } from '@redocly/theme/components/Link/Link';
10
- import { ButtonGroup } from '@redocly/theme/components/Button/ButtonGroup';
11
10
  import { Button } from '@redocly/theme/components/Button/Button';
12
- import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
11
+ import { SplitButton } from '@redocly/theme/components/SplitButton/SplitButton';
13
12
  import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
14
13
  import { Spinner } from '@redocly/theme/icons/Spinner/Spinner';
15
14
  import { CheckmarkFilledIcon } from '@redocly/theme/icons/CheckmarkFilledIcon/CheckmarkFilledIcon';
16
- import { PageActionType, usePageActions } from '@redocly/theme/core/hooks';
15
+ import { PageActionType, usePageActions, useThemeHooks } from '@redocly/theme/core/hooks';
17
16
 
18
17
  type ActionState = 'idle' | 'processing' | 'done';
19
18
 
@@ -28,6 +27,8 @@ const ACTION_DONE_DISPLAY_DURATION = 1000;
28
27
  export function PageActions(props: PageActionProps): JSX.Element | null {
29
28
  const { pageSlug, mcpUrl } = props;
30
29
 
30
+ const { useTranslate } = useThemeHooks();
31
+ const { translate } = useTranslate();
31
32
  const actions = usePageActions(pageSlug || '/', mcpUrl, props.actions);
32
33
 
33
34
  const [actionState, setActionState] = useState<ActionState>('idle');
@@ -73,27 +74,25 @@ export function PageActions(props: PageActionProps): JSX.Element | null {
73
74
 
74
75
  return (
75
76
  <PageActionsWrapper data-component-name="PageActions/PageActions">
76
- <ButtonGroup variant="outlined" size="medium">
77
- <Button
78
- icon={renderIcon(buttonAction, actionState)}
79
- to={'link' in buttonAction ? buttonAction.link : undefined}
80
- external
81
- onClick={() => handleActionClick(buttonAction)}
82
- >
83
- {buttonAction.buttonText}
84
- </Button>
85
- {actions.length > 1 ? (
86
- <Dropdown
87
- withArrow
88
- portalled
89
- trigger={<Button aria-label="More actions" />}
90
- placement="bottom"
91
- alignment="end"
77
+ <SplitButton
78
+ portalled
79
+ variant="outlined"
80
+ size="medium"
81
+ toggleAriaLabel={translate('page.actions.moreActions', 'More actions')}
82
+ button={
83
+ <Button
84
+ variant="outlined"
85
+ icon={renderIcon(buttonAction, actionState)}
86
+ to={'link' in buttonAction ? buttonAction.link : undefined}
87
+ external
88
+ onClick={() => handleActionClick(buttonAction)}
92
89
  >
93
- <StyledDropdownMenu>{menuItems}</StyledDropdownMenu>
94
- </Dropdown>
95
- ) : null}
96
- </ButtonGroup>
90
+ {buttonAction.buttonText}
91
+ </Button>
92
+ }
93
+ >
94
+ {actions.length > 1 ? <StyledDropdownMenu>{menuItems}</StyledDropdownMenu> : null}
95
+ </SplitButton>
97
96
  </PageActionsWrapper>
98
97
  );
99
98
  }
@@ -1,73 +1,59 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
+ import type { JSX } from 'react';
4
5
  import type { PageAction } from '@redocly/theme/core/types';
5
6
 
7
+ import { DropdownMenuItemContent } from '@redocly/theme/components/Dropdown/DropdownMenuItemContent';
6
8
  import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
7
9
 
8
- type PageActionsMenuItemProps = {
10
+ export type PageActionsMenuItemProps = {
9
11
  pageAction: PageAction;
12
+ className?: string;
10
13
  };
11
14
 
12
- export function PageActionsMenuItem(props: PageActionsMenuItemProps) {
13
- const { pageAction } = props;
15
+ export function PageActionsMenuItem(props: PageActionsMenuItemProps): JSX.Element {
16
+ const { pageAction, className } = props;
14
17
 
15
18
  return (
16
- <MenuItemWrapper data-component-name="PageActions/PageActionsMenuItem">
17
- <IconWrapper>
18
- <pageAction.iconComponent />
19
- </IconWrapper>
20
- <ContentWrapper>
21
- <Title>
22
- {pageAction.title}
23
- {'link' in pageAction ? (
24
- <LaunchIcon color="var(--page-actions-menu-item-title-icon-color)" size="14px" />
25
- ) : null}
26
- </Title>
27
- <Description>{pageAction.description}</Description>
28
- </ContentWrapper>
19
+ <MenuItemWrapper data-component-name="PageActions/PageActionsMenuItem" className={className}>
20
+ <DropdownMenuItemContent
21
+ icon={<pageAction.iconComponent />}
22
+ title={
23
+ <>
24
+ {pageAction.title}
25
+ {'link' in pageAction ? (
26
+ <LaunchIcon color="var(--page-actions-menu-item-title-icon-color)" size="14px" />
27
+ ) : null}
28
+ </>
29
+ }
30
+ description={pageAction.description}
31
+ />
29
32
  </MenuItemWrapper>
30
33
  );
31
34
  }
32
35
 
33
36
  const MenuItemWrapper = styled.div`
34
- padding: var(--page-actions-menu-item-padding);
35
- display: flex;
36
- flex-direction: row;
37
- align-items: center;
38
- gap: var(--page-actions-menu-item-gap);
39
- `;
40
-
41
- const IconWrapper = styled.div`
42
- color: var(--page-actions-menu-item-icon-color);
43
- border: 1px solid var(--page-actions-menu-item-icon-border-color);
44
- border-radius: var(--page-actions-menu-item-icon-border-radius);
45
- width: var(--page-actions-menu-item-icon-wrapper-size);
46
- height: var(--page-actions-menu-item-icon-wrapper-size);
47
- display: flex;
48
- align-items: center;
49
- justify-content: center;
50
- `;
51
-
52
- const ContentWrapper = styled.div`
53
- display: flex;
54
- flex-direction: column;
55
- gap: var(--page-actions-menu-item-text-gap);
56
- `;
57
-
58
- const Title = styled.div`
59
- font-weight: var(--page-actions-menu-item-title-font-weight);
60
- font-size: var(--page-actions-menu-item-title-font-size);
61
- line-height: var(--page-actions-menu-item-title-line-height);
62
- color: var(--page-actions-menu-item-title-color);
63
- display: flex;
64
- align-items: center;
65
- gap: var(--page-actions-menu-item-title-icon-gap);
66
- `;
67
-
68
- const Description = styled.div`
69
- font-weight: var(--page-actions-menu-item-description-font-weight);
70
- font-size: var(--page-actions-menu-item-description-font-size);
71
- line-height: var(--page-actions-menu-item-description-line-height);
72
- color: var(--page-actions-menu-item-description-color);
37
+ --dropdown-menu-item-content-gap: var(--page-actions-menu-item-gap);
38
+ --dropdown-menu-item-content-padding: var(--page-actions-menu-item-padding);
39
+ --dropdown-menu-item-content-icon-size: var(--page-actions-menu-item-icon-wrapper-size);
40
+ --dropdown-menu-item-content-icon-color: var(--page-actions-menu-item-icon-color);
41
+ --dropdown-menu-item-content-icon-border: 1px solid var(--page-actions-menu-item-icon-border-color);
42
+ --dropdown-menu-item-content-icon-border-radius: var(--page-actions-menu-item-icon-border-radius);
43
+ --dropdown-menu-item-content-text-gap: var(--page-actions-menu-item-text-gap);
44
+ --dropdown-menu-item-content-title-icon-gap: var(--page-actions-menu-item-title-icon-gap);
45
+ --dropdown-menu-item-content-title-font-weight: var(--page-actions-menu-item-title-font-weight);
46
+ --dropdown-menu-item-content-title-font-size: var(--page-actions-menu-item-title-font-size);
47
+ --dropdown-menu-item-content-title-line-height: var(--page-actions-menu-item-title-line-height);
48
+ --dropdown-menu-item-content-title-color: var(--page-actions-menu-item-title-color);
49
+ --dropdown-menu-item-content-description-font-weight: var(
50
+ --page-actions-menu-item-description-font-weight
51
+ );
52
+ --dropdown-menu-item-content-description-font-size: var(
53
+ --page-actions-menu-item-description-font-size
54
+ );
55
+ --dropdown-menu-item-content-description-line-height: var(
56
+ --page-actions-menu-item-description-line-height
57
+ );
58
+ --dropdown-menu-item-content-description-color: var(--page-actions-menu-item-description-color);
73
59
  `;
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import type { BaseSyntheticEvent, PropsWithChildren, ReactNode, JSX } from 'react';
@@ -34,6 +34,12 @@ export function Panel({
34
34
  }: PropsWithChildren<PanelProps>): JSX.Element {
35
35
  const [isExpanded, setExpanded] = useState(expanded);
36
36
  const [animate, setAnimate] = useState(false);
37
+ const [prevExpanded, setPrevExpanded] = useState(expanded);
38
+
39
+ if (prevExpanded !== expanded) {
40
+ setPrevExpanded(expanded);
41
+ setExpanded(expanded);
42
+ }
37
43
 
38
44
  const toggle = ({ target }: BaseSyntheticEvent): void => {
39
45
  if (target instanceof HTMLAnchorElement || !isExpandable) return;
@@ -42,8 +48,6 @@ export function Panel({
42
48
  onToggle?.(!isExpanded);
43
49
  };
44
50
 
45
- useEffect(() => setExpanded(expanded), [expanded]);
46
-
47
51
  return (
48
52
  <PanelWrapper
49
53
  className={className}
@@ -178,7 +178,7 @@ export const apiReferencePanels = css`
178
178
  * @tokens Panel common
179
179
  */
180
180
 
181
- --panel-border-radius: var(--border-radius); // @presenter BorderRadius
181
+ --panel-border-radius: var(--border-radius-xl); // @presenter BorderRadius
182
182
  --panel-border: 1px solid var(--border-color-secondary); // @presenter Border
183
183
 
184
184
  --panel-line-height: var(--line-height-base); // @presenter LineHeight
@@ -25,7 +25,7 @@ export const productPicker = css`
25
25
  --product-picker-dropdown-item-padding-vertical: var(--dropdown-menu-item-padding-vertical); // @presenter Spacing
26
26
  --product-picker-dropdown-item-separator-padding-top: var(--dropdown-menu-item-separator-padding-top ); // @presenter Spacing
27
27
  --product-picker-dropdown-item-separator-padding-bottom: var(--dropdown-menu-item-separator-padding-bottom ); // @presenter Spacing
28
- --product-picker-dropdown-item-border-radius: var(--dropdown-menu-item-border-radius ); // @presenter BorderRadius
28
+ --product-picker-dropdown-item-border-radius: var(--dropdown-menu-item-border-radius); // @presenter BorderRadius
29
29
  --product-picker-dropdown-item-bg-color-active: var(--dropdown-menu-item-bg-color-active); // @presenter Color
30
30
  --product-picker-dropdown-item-bg-color-hover: var(--dropdown-menu-item-bg-color-hover); // @presenter Color
31
31
  --product-picker-dropdown-item-bg-color-disabled: var(--dropdown-menu-item-bg-color-disabled); // @presenter Color
@@ -36,4 +36,5 @@ export const productPicker = css`
36
36
  --product-picker-dropdown-item-color-hover: var(--dropdown-menu-item-color-hover);
37
37
  --product-picker-dropdown-item-border-color-focused: var(--dropdown-menu-item-border-color-focused);
38
38
 
39
+ // @tokens End
39
40
  `;
@@ -10,6 +10,9 @@ export type ReplayGateProps = {
10
10
  // The current OpenAPI description's id (from x-metadata), when known. Lets a custom
11
11
  // ReplayGate vary behavior per API in a multi-API catalog.
12
12
  apiId?: string;
13
+ // Closes the whole Replay overlay. A gate that denies access calls this instead of
14
+ // rendering `children`, so a dismissed gate doesn't leave Replay reachable.
15
+ onClose: () => void;
13
16
  children: React.ReactNode;
14
17
  };
15
18