@redocly/theme 0.59.0-rc.2 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/LICENSE +7 -1
  2. package/lib/components/Buttons/AIAssistantButton.js +6 -2
  3. package/lib/components/Buttons/ConnectMCPButton.d.ts +8 -0
  4. package/lib/components/Buttons/ConnectMCPButton.js +145 -0
  5. package/lib/components/Buttons/variables.d.ts +1 -0
  6. package/lib/components/Buttons/variables.js +42 -2
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntityInfoBar.js +1 -0
  8. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  9. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  10. package/lib/components/Catalog/CatalogEntityIcon.js +2 -1
  11. package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +4 -0
  12. package/lib/components/Catalog/CatalogTagsWithTooltip.js +1 -1
  13. package/lib/components/Catalog/variables.js +1 -1
  14. package/lib/components/Dropdown/Dropdown.d.ts +16 -2
  15. package/lib/components/Dropdown/Dropdown.js +5 -5
  16. package/lib/components/Menu/MenuItem.js +1 -1
  17. package/lib/components/Navbar/NavbarItem.js +3 -3
  18. package/lib/components/PageActions/PageActions.js +4 -1
  19. package/lib/components/PageActions/variables.js +2 -0
  20. package/lib/components/Search/FilterFields/SearchFilterFieldTags.js +1 -2
  21. package/lib/components/Search/SearchAiActionButtons.d.ts +10 -0
  22. package/lib/components/Search/SearchAiActionButtons.js +43 -0
  23. package/lib/components/Search/SearchAiConversationInput.d.ts +3 -1
  24. package/lib/components/Search/SearchAiConversationInput.js +39 -7
  25. package/lib/components/Search/SearchAiDialog.d.ts +3 -6
  26. package/lib/components/Search/SearchAiDialog.js +20 -9
  27. package/lib/components/Search/SearchAiMessage.d.ts +9 -5
  28. package/lib/components/Search/SearchAiMessage.js +146 -22
  29. package/lib/components/Search/SearchAiNegativeFeedbackForm.d.ts +8 -0
  30. package/lib/components/Search/SearchAiNegativeFeedbackForm.js +169 -0
  31. package/lib/components/Search/SearchDialog.js +36 -5
  32. package/lib/components/Search/SearchGroups.js +2 -2
  33. package/lib/components/Search/variables.js +36 -64
  34. package/lib/components/Segmented/Segmented.d.ts +1 -8
  35. package/lib/components/Segmented/Segmented.js +3 -1
  36. package/lib/components/Select/SelectInput.js +1 -1
  37. package/lib/components/Select/variables.js +2 -2
  38. package/lib/components/Tag/Tag.d.ts +2 -1
  39. package/lib/components/Tag/Tag.js +66 -17
  40. package/lib/components/Tag/variables.dark.js +135 -36
  41. package/lib/components/Tag/variables.js +78 -61
  42. package/lib/core/constants/index.d.ts +1 -0
  43. package/lib/core/constants/index.js +1 -0
  44. package/lib/core/constants/mcp.d.ts +1 -0
  45. package/lib/core/constants/mcp.js +5 -0
  46. package/lib/core/constants/search.d.ts +5 -4
  47. package/lib/core/constants/search.js +4 -5
  48. package/lib/core/hooks/index.d.ts +3 -0
  49. package/lib/core/hooks/index.js +3 -0
  50. package/lib/core/hooks/menu/use-nested-menu.js +1 -1
  51. package/lib/core/hooks/search/use-feedback-tooltip.d.ts +6 -0
  52. package/lib/core/hooks/search/use-feedback-tooltip.js +26 -0
  53. package/lib/core/hooks/use-connect-mcp-button.d.ts +13 -0
  54. package/lib/core/hooks/use-connect-mcp-button.js +50 -0
  55. package/lib/core/hooks/use-mcp-config.d.ts +9 -0
  56. package/lib/core/hooks/use-mcp-config.js +27 -0
  57. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  58. package/lib/core/hooks/use-page-actions.js +99 -119
  59. package/lib/core/hooks/use-product-picker.js +2 -1
  60. package/lib/core/hooks/use-tabs.d.ts +3 -2
  61. package/lib/core/hooks/use-tabs.js +115 -57
  62. package/lib/core/hooks/use-telemetry-fallback.d.ts +10 -8
  63. package/lib/core/hooks/use-telemetry-fallback.js +10 -8
  64. package/lib/core/openapi/index.d.ts +1 -0
  65. package/lib/core/styles/dark.js +4 -0
  66. package/lib/core/styles/global.js +5 -0
  67. package/lib/core/types/hooks.d.ts +2 -2
  68. package/lib/core/types/index.d.ts +1 -0
  69. package/lib/core/types/index.js +1 -0
  70. package/lib/core/types/l10n.d.ts +1 -1
  71. package/lib/core/types/mcp.d.ts +6 -0
  72. package/lib/core/types/mcp.js +3 -0
  73. package/lib/core/types/search.d.ts +11 -4
  74. package/lib/core/types/search.js +6 -0
  75. package/lib/core/types/segmented.d.ts +12 -0
  76. package/lib/core/types/segmented.js +3 -0
  77. package/lib/core/utils/frontmatter-translate.d.ts +6 -0
  78. package/lib/core/utils/frontmatter-translate.js +14 -0
  79. package/lib/core/utils/index.d.ts +2 -0
  80. package/lib/core/utils/index.js +2 -0
  81. package/lib/core/utils/mcp.d.ts +2 -0
  82. package/lib/core/utils/mcp.js +31 -0
  83. package/lib/icons/AiStarsGradientIcon/AiStarsGradientIcon.js +44 -4
  84. package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
  85. package/lib/icons/ConnectIcon/ConnectIcon.d.ts +9 -0
  86. package/lib/icons/ConnectIcon/ConnectIcon.js +17 -0
  87. package/lib/icons/CubeIcon/CubeIcon.d.ts +9 -0
  88. package/lib/icons/CubeIcon/CubeIcon.js +17 -0
  89. package/lib/icons/HashtagIcon/HashtagIcon.d.ts +9 -0
  90. package/lib/icons/HashtagIcon/HashtagIcon.js +22 -0
  91. package/lib/icons/RedoclyIcon/RedoclyIcon.js +4 -7
  92. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.d.ts +9 -0
  93. package/lib/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.js +34 -0
  94. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.d.ts +9 -0
  95. package/lib/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.js +34 -0
  96. package/lib/icons/VSCodeIcon/VSCodeIcon.d.ts +9 -0
  97. package/lib/icons/VSCodeIcon/VSCodeIcon.js +17 -0
  98. package/lib/index.d.ts +1 -2
  99. package/lib/index.js +1 -2
  100. package/lib/markdoc/components/Cards/Card.js +1 -28
  101. package/lib/markdoc/components/ConnectMCP/ConnectMCP.d.ts +8 -0
  102. package/lib/markdoc/components/ConnectMCP/ConnectMCP.js +19 -0
  103. package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
  104. package/lib/markdoc/components/Tabs/TabList.js +197 -47
  105. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
  106. package/lib/markdoc/components/Tabs/Tabs.js +57 -12
  107. package/lib/markdoc/components/default.d.ts +1 -0
  108. package/lib/markdoc/components/default.js +1 -0
  109. package/lib/markdoc/default.d.ts +6 -0
  110. package/lib/markdoc/default.js +2 -0
  111. package/lib/markdoc/tags/card.js +0 -1
  112. package/lib/markdoc/tags/connect-mcp.d.ts +2 -0
  113. package/lib/markdoc/tags/connect-mcp.js +27 -0
  114. package/package.json +6 -6
  115. package/src/components/Buttons/AIAssistantButton.tsx +6 -2
  116. package/src/components/Buttons/ConnectMCPButton.tsx +180 -0
  117. package/src/components/Buttons/variables.ts +42 -1
  118. package/src/components/Catalog/CatalogEntity/CatalogEntityInfoBar.tsx +1 -0
  119. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  120. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  121. package/src/components/Catalog/CatalogEntityIcon.tsx +2 -1
  122. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +5 -0
  123. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +1 -5
  124. package/src/components/Catalog/variables.ts +1 -1
  125. package/src/components/Dropdown/Dropdown.tsx +84 -79
  126. package/src/components/Menu/MenuItem.tsx +1 -0
  127. package/src/components/Navbar/NavbarItem.tsx +6 -5
  128. package/src/components/PageActions/PageActions.tsx +5 -1
  129. package/src/components/PageActions/variables.ts +2 -0
  130. package/src/components/Search/FilterFields/SearchFilterFieldTags.tsx +3 -3
  131. package/src/components/Search/SearchAiActionButtons.tsx +76 -0
  132. package/src/components/Search/SearchAiConversationInput.tsx +61 -18
  133. package/src/components/Search/SearchAiDialog.tsx +52 -23
  134. package/src/components/Search/SearchAiMessage.tsx +172 -43
  135. package/src/components/Search/SearchAiNegativeFeedbackForm.tsx +210 -0
  136. package/src/components/Search/SearchDialog.tsx +49 -13
  137. package/src/components/Search/SearchGroups.tsx +2 -0
  138. package/src/components/Search/variables.ts +36 -64
  139. package/src/components/Segmented/Segmented.tsx +15 -20
  140. package/src/components/Select/SelectInput.tsx +1 -0
  141. package/src/components/Select/variables.ts +2 -2
  142. package/src/components/Tag/Tag.tsx +35 -19
  143. package/src/components/Tag/variables.dark.ts +135 -36
  144. package/src/components/Tag/variables.ts +78 -61
  145. package/src/core/constants/index.ts +1 -0
  146. package/src/core/constants/mcp.ts +1 -0
  147. package/src/core/constants/search.ts +8 -4
  148. package/src/core/hooks/index.ts +3 -0
  149. package/src/core/hooks/menu/use-nested-menu.ts +2 -2
  150. package/src/core/hooks/search/use-feedback-tooltip.ts +32 -0
  151. package/src/core/hooks/use-connect-mcp-button.ts +79 -0
  152. package/src/core/hooks/use-mcp-config.ts +43 -0
  153. package/src/core/hooks/use-page-actions.ts +141 -150
  154. package/src/core/hooks/use-product-picker.ts +2 -1
  155. package/src/core/hooks/use-tabs.ts +168 -86
  156. package/src/core/hooks/use-telemetry-fallback.ts +10 -8
  157. package/src/core/openapi/index.ts +1 -0
  158. package/src/core/styles/dark.ts +4 -0
  159. package/src/core/styles/global.ts +6 -1
  160. package/src/core/types/hooks.ts +5 -1
  161. package/src/core/types/index.ts +1 -0
  162. package/src/core/types/l10n.ts +13 -0
  163. package/src/core/types/mcp.ts +8 -0
  164. package/src/core/types/search.ts +13 -4
  165. package/src/core/types/segmented.ts +14 -0
  166. package/src/core/utils/frontmatter-translate.ts +9 -0
  167. package/src/core/utils/index.ts +2 -0
  168. package/src/core/utils/mcp.ts +34 -0
  169. package/src/icons/AiStarsGradientIcon/AiStarsGradientIcon.tsx +13 -4
  170. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
  171. package/src/icons/ConnectIcon/ConnectIcon.tsx +27 -0
  172. package/src/icons/CubeIcon/CubeIcon.tsx +27 -0
  173. package/src/icons/HashtagIcon/HashtagIcon.tsx +23 -0
  174. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +4 -22
  175. package/src/icons/ThumbDownFilledIcon/ThumbDownFilledIcon.tsx +38 -0
  176. package/src/icons/ThumbUpFilledIcon/ThumbUpFilledIcon.tsx +35 -0
  177. package/src/icons/VSCodeIcon/VSCodeIcon.tsx +29 -0
  178. package/src/index.ts +1 -2
  179. package/src/markdoc/components/Cards/Card.tsx +1 -28
  180. package/src/markdoc/components/ConnectMCP/ConnectMCP.tsx +28 -0
  181. package/src/markdoc/components/Tabs/TabList.tsx +312 -105
  182. package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
  183. package/src/markdoc/components/default.ts +1 -0
  184. package/src/markdoc/default.ts +2 -0
  185. package/src/markdoc/tags/card.ts +0 -1
  186. package/src/markdoc/tags/connect-mcp.ts +25 -0
  187. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.d.ts +0 -1
  188. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.js +0 -11
  189. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.d.ts +0 -1
  190. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.js +0 -5
  191. package/lib/ext/process-scorecard.d.ts +0 -5
  192. package/lib/ext/process-scorecard.js +0 -11
  193. package/src/components/OpenApiDocs/hooks/AdditionalOverviewInfo.tsx +0 -9
  194. package/src/components/OpenApiDocs/hooks/AfterOpenApiDescription.tsx +0 -1
  195. package/src/ext/process-scorecard.ts +0 -13
@@ -44,47 +44,144 @@ const DropdownMenuItem_1 = require("../../../components/Dropdown/DropdownMenuIte
44
44
  const Button_1 = require("../../../components/Button/Button");
45
45
  const hooks_1 = require("../../../core/hooks");
46
46
  const utils_1 = require("../../../core/utils");
47
- function TabList({ childrenArray, size, activeTab, onTabChange, }) {
48
- const tabsContainerRef = (0, react_1.useRef)(null);
49
- const { allTabsHidden, overflowTabs, visibleTabs, handleKeyboard, onTabClick, setTabRef } = (0, hooks_1.useTabs)({
47
+ /**
48
+ * Calculates optimal dropdown position relative to viewport to ensure visibility.
49
+ * Positions below the button by default, but moves above if insufficient space.
50
+ * Adjusts horizontal position to prevent overflow off screen edges.
51
+ */
52
+ const calculateDropdownPosition = (buttonRect, dropdownRect) => {
53
+ const gap = 4;
54
+ const margin = 16;
55
+ const spaceBelow = window.innerHeight - buttonRect.bottom;
56
+ const spaceAbove = buttonRect.top;
57
+ // Position below button, or above if dropdown doesn't fit below
58
+ const top = spaceBelow < dropdownRect.height + gap && spaceAbove > spaceBelow
59
+ ? buttonRect.top - gap
60
+ : buttonRect.bottom + gap;
61
+ // Align with button left edge, adjust if overflows screen
62
+ const idealLeft = buttonRect.left;
63
+ const rightEdge = idealLeft + dropdownRect.width;
64
+ const overflowsRight = rightEdge > window.innerWidth - margin;
65
+ const left = overflowsRight
66
+ ? window.innerWidth - dropdownRect.width - margin
67
+ : Math.max(margin, idealLeft);
68
+ return { top, left };
69
+ };
70
+ /**
71
+ * Manages dropdown positioning and updates on scroll/resize events for TabList.
72
+ */
73
+ const useDropdownPosition = (hasOverflow, dropdownRef) => {
74
+ const [dropdownPosition, setDropdownPosition] = (0, react_1.useState)({});
75
+ const [isDropdownOpen, setIsDropdownOpen] = (0, react_1.useState)(false);
76
+ const updateDropdownPosition = (0, react_1.useCallback)(() => {
77
+ if (!dropdownRef.current)
78
+ return;
79
+ const button = dropdownRef.current.querySelector('button');
80
+ const dropdownMenu = dropdownRef.current.querySelector('div:last-child');
81
+ if (!button || !dropdownMenu)
82
+ return;
83
+ const buttonRect = button.getBoundingClientRect();
84
+ const dropdownRect = dropdownMenu.getBoundingClientRect();
85
+ const position = calculateDropdownPosition(buttonRect, dropdownRect);
86
+ setDropdownPosition(position);
87
+ }, [dropdownRef]);
88
+ // Track when dropdown menu appears and recalculate position
89
+ (0, react_1.useEffect)(() => {
90
+ if (!hasOverflow || !isDropdownOpen || !dropdownRef.current)
91
+ return;
92
+ const dropdownMenu = dropdownRef.current.querySelector('div:last-child');
93
+ if (!dropdownMenu)
94
+ return;
95
+ // ResizeObserver tracks both initial render and size changes
96
+ const resizeObserver = new ResizeObserver(() => {
97
+ updateDropdownPosition();
98
+ });
99
+ resizeObserver.observe(dropdownMenu);
100
+ return () => resizeObserver.disconnect();
101
+ }, [hasOverflow, isDropdownOpen, dropdownRef, updateDropdownPosition]);
102
+ // Update position on scroll/resize
103
+ (0, react_1.useEffect)(() => {
104
+ if (!hasOverflow || !isDropdownOpen)
105
+ return;
106
+ window.addEventListener('scroll', updateDropdownPosition, true);
107
+ window.addEventListener('resize', updateDropdownPosition);
108
+ return () => {
109
+ window.removeEventListener('scroll', updateDropdownPosition, true);
110
+ window.removeEventListener('resize', updateDropdownPosition);
111
+ };
112
+ }, [hasOverflow, isDropdownOpen, updateDropdownPosition]);
113
+ return {
114
+ dropdownPosition,
115
+ isDropdownOpen,
116
+ setIsDropdownOpen,
117
+ setDropdownPosition,
118
+ updateDropdownPosition,
119
+ };
120
+ };
121
+ const renderTab = (child, index, size, setTabRef, handleKeyboard, onTabClick) => {
122
+ const { label, icon } = child.props;
123
+ const tabId = (0, utils_1.getTabId)(label, index);
124
+ return (react_1.default.createElement(Tab_1.Tab, { key: `key-${tabId}`, tabId: tabId, label: label, icon: icon, size: size, disabled: child.props.disable, setRef: (el) => setTabRef(el, index), onKeyDown: (event) => handleKeyboard(event, index), onClick: () => {
125
+ var _a, _b;
126
+ (_b = (_a = child.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a);
127
+ onTabClick(label);
128
+ } }));
129
+ };
130
+ function TabList({ childrenArray, size, activeTab, onTabChange, containerRef, onReadyChange, }) {
131
+ const dropdownRef = (0, react_1.useRef)(null);
132
+ const totalTabs = childrenArray.length;
133
+ const { overflowTabs, visibleTabs, handleKeyboard, onTabClick, setTabRef, isReady } = (0, hooks_1.useTabs)({
50
134
  activeTab,
51
135
  onTabChange,
52
- containerRef: tabsContainerRef,
53
- totalTabs: childrenArray.length,
136
+ containerRef,
137
+ totalTabs,
54
138
  });
139
+ (0, react_1.useEffect)(() => {
140
+ onReadyChange === null || onReadyChange === void 0 ? void 0 : onReadyChange(isReady);
141
+ }, [isReady, onReadyChange]);
55
142
  const { highlightStyle } = useHighlightBarAnimation({
56
143
  activeTab,
57
144
  childrenArray,
58
145
  overflowTabs,
59
- tabsContainerRef,
146
+ tabsContainerRef: containerRef,
60
147
  visibleTabs,
61
148
  });
62
- return (react_1.default.createElement(exports.TabListContainer, { role: "tablist", ref: tabsContainerRef },
149
+ const hasOverflow = overflowTabs.length > 0;
150
+ const isMoreActive = hasOverflow &&
151
+ overflowTabs.some((i) => childrenArray[i] && activeTab === childrenArray[i].props.label);
152
+ // Show as selector when no visible tabs (all tabs in dropdown)
153
+ const showAsSelector = visibleTabs.length === 0 && hasOverflow;
154
+ const { dropdownPosition, setIsDropdownOpen, setDropdownPosition } = useDropdownPosition(hasOverflow, dropdownRef);
155
+ return (react_1.default.createElement(exports.TabListContainer, { role: "tablist", ref: containerRef },
63
156
  react_1.default.createElement(HighlightBar, { size: size, style: highlightStyle },
64
157
  react_1.default.createElement("div", null)),
65
158
  childrenArray.map((child, index) => {
66
- if (!visibleTabs.includes(index))
159
+ // Show all tabs before ready (for measurement), then only visible ones
160
+ const shouldRender = !isReady || visibleTabs.includes(index);
161
+ if (!shouldRender)
67
162
  return null;
68
- const { label, icon } = child.props;
69
- const tabId = (0, utils_1.getTabId)(label, index);
70
- return (react_1.default.createElement(Tab_1.Tab, { key: `key-${tabId}`, tabId: tabId, label: label, icon: icon, size: size, disabled: child.props.disable, setRef: (el) => setTabRef(el, index), onKeyDown: (event) => handleKeyboard(event, index), onClick: () => {
71
- var _a, _b;
72
- (_b = (_a = child.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a);
73
- onTabClick(label);
74
- } }));
163
+ return renderTab(child, index, size, setTabRef, handleKeyboard, onTabClick);
75
164
  }),
76
- react_1.default.createElement(exports.TabItem, { size: size, active: overflowTabs.some((index) => activeTab === childrenArray[index].props.label), tabIndex: 0 }, overflowTabs.length > 0 && (react_1.default.createElement(Dropdown_1.Dropdown, { trigger: react_1.default.createElement(exports.TabButtonLink, { size: size, className: overflowTabs.some((index) => activeTab === childrenArray[index].props.label)
77
- ? 'active'
78
- : undefined }, allTabsHidden ? activeTab : 'More'), alignment: "start", withArrow: true },
79
- react_1.default.createElement(DropdownMenu_1.DropdownMenu, null, overflowTabs.map((index) => {
80
- const { label } = childrenArray[index].props;
81
- const tabId = (0, utils_1.getTabId)(label, index);
82
- return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: `more-${tabId}`, active: activeTab === label, onAction: () => {
83
- var _a, _b;
84
- (_b = (_a = childrenArray[index].props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a);
85
- onTabClick(index);
86
- }, disabled: childrenArray[index].props.disable }, label));
87
- })))))));
165
+ hasOverflow && (react_1.default.createElement(exports.TabItem, { size: size, active: isMoreActive || showAsSelector, tabIndex: 0, className: "dropdown-tab" },
166
+ react_1.default.createElement(DropdownWrapper, { "$top": dropdownPosition.top, "$left": dropdownPosition.left, onClickCapture: () => {
167
+ setIsDropdownOpen(true);
168
+ } },
169
+ react_1.default.createElement(FixedPositionDropdown, { ref: dropdownRef, trigger: react_1.default.createElement(exports.TabButtonLink, { size: size, className: isMoreActive || showAsSelector ? 'active' : undefined }, showAsSelector ? react_1.default.createElement(TabButtonText, null, activeTab) : 'More'), alignment: "start", withArrow: true, onClose: () => {
170
+ setIsDropdownOpen(false);
171
+ setDropdownPosition({});
172
+ } },
173
+ react_1.default.createElement(DropdownMenu_1.DropdownMenu, null, overflowTabs.map((index) => {
174
+ const child = childrenArray[index];
175
+ if (!child)
176
+ return null;
177
+ const { label } = child.props;
178
+ const tabId = (0, utils_1.getTabId)(label, index);
179
+ return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: `more-${tabId}`, active: activeTab === label, onAction: () => {
180
+ var _a, _b;
181
+ (_b = (_a = child.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a);
182
+ onTabClick(index);
183
+ }, disabled: child.props.disable }, label));
184
+ }))))))));
88
185
  }
89
186
  const useHighlightBarAnimation = (props) => {
90
187
  const { childrenArray, activeTab, tabsContainerRef, visibleTabs, overflowTabs } = props;
@@ -99,18 +196,11 @@ const useHighlightBarAnimation = (props) => {
99
196
  setHighlightStyle({ left: 0, width: 0 });
100
197
  return;
101
198
  }
102
- const activeTabElement = container.querySelector(`[data-label="${activeTab}"]`);
103
- if (!activeTabElement)
104
- return;
199
+ // Remove active class from all tabs first
105
200
  container.querySelectorAll('[data-label]').forEach((el) => {
106
201
  el.classList.remove('active');
107
202
  });
108
- const { offsetLeft, offsetWidth } = activeTabElement;
109
- if (visibleTabs.includes(activeIndex)) {
110
- activeTabElement.classList.add('active');
111
- setHighlightStyle({ left: offsetLeft, width: offsetWidth });
112
- return;
113
- }
203
+ // Check if active tab is in overflow first
114
204
  if (overflowTabs.includes(activeIndex)) {
115
205
  const moreButton = container.querySelector('button');
116
206
  if (!moreButton)
@@ -123,6 +213,16 @@ const useHighlightBarAnimation = (props) => {
123
213
  });
124
214
  return;
125
215
  }
216
+ // Active tab is visible, find its element
217
+ const activeTabElement = container.querySelector(`[data-label="${activeTab}"]`);
218
+ if (!activeTabElement)
219
+ return;
220
+ const { offsetLeft, offsetWidth } = activeTabElement;
221
+ if (visibleTabs.includes(activeIndex)) {
222
+ activeTabElement.classList.add('active');
223
+ setHighlightStyle({ left: offsetLeft, width: offsetWidth });
224
+ return;
225
+ }
126
226
  }, [activeTab, childrenArray, visibleTabs, overflowTabs, tabsContainerRef]);
127
227
  return { highlightStyle };
128
228
  };
@@ -132,15 +232,11 @@ exports.TabListContainer = styled_components_1.default.ul `
132
232
  gap: var(--md-tabs-gap);
133
233
  width: 100%;
134
234
  min-width: 0;
135
- position: relative;
136
235
 
137
236
  &::before {
138
237
  content: '';
139
238
  position: absolute;
140
- top: 0px;
141
- left: 0px;
142
- right: 0px;
143
- bottom: 0px;
239
+ inset: 0;
144
240
  border: var(--md-tabs-border);
145
241
  border-width: var(--md-tabs-border-width);
146
242
  pointer-events: none;
@@ -148,11 +244,17 @@ exports.TabListContainer = styled_components_1.default.ul `
148
244
 
149
245
  && {
150
246
  padding: var(--md-tabs-padding);
151
- margin-block-end: 0;
152
247
  margin: 0;
153
248
 
154
249
  & > li {
155
- margin-bottom: 0px;
250
+ margin-bottom: 0;
251
+ flex-shrink: 0;
252
+
253
+ &.dropdown-tab {
254
+ flex-shrink: 1;
255
+ min-width: 0;
256
+ max-width: 100%;
257
+ }
156
258
  }
157
259
  }
158
260
  `;
@@ -162,7 +264,7 @@ exports.TabItem = styled_components_1.default.li `
162
264
  cursor: pointer;
163
265
  align-items: center;
164
266
  padding: var(--md-tabs-tab-wrapper-padding);
165
- z-index: 1;
267
+ z-index: var(--z-index-surface);
166
268
 
167
269
  ${({ active, size }) => active
168
270
  ? (0, styled_components_1.css) `
@@ -199,6 +301,46 @@ exports.TabItem = styled_components_1.default.li `
199
301
  }
200
302
  }
201
303
  `;
304
+ const DropdownWrapper = styled_components_1.default.div.attrs((props) => ({
305
+ style: Object.assign(Object.assign({}, (props.$top !== undefined && { '--dropdown-top': `${props.$top}px` })), (props.$left !== undefined && { '--dropdown-left': `${props.$left}px` })),
306
+ })) `
307
+ position: static;
308
+ z-index: var(--z-index-raised);
309
+ width: 100%;
310
+ min-width: 0;
311
+ `;
312
+ const FixedPositionDropdown = (0, styled_components_1.default)(Dropdown_1.Dropdown) `
313
+ position: static;
314
+ width: 100%;
315
+ min-width: 0;
316
+
317
+ > div:first-child {
318
+ width: 100%;
319
+ min-width: 0;
320
+ }
321
+
322
+ > div:last-child {
323
+ position: fixed;
324
+ top: var(--dropdown-top, 0);
325
+ left: var(--dropdown-left, 0);
326
+ right: auto;
327
+ bottom: auto;
328
+ transform: none;
329
+ padding-top: 0;
330
+ max-width: min(400px, calc(100vw - 32px));
331
+ max-height: calc(100vh - var(--dropdown-top, 0) - 32px);
332
+ overflow-y: auto;
333
+ z-index: var(--z-index-raised);
334
+
335
+ ul {
336
+ li {
337
+ overflow: hidden;
338
+ text-overflow: ellipsis;
339
+ white-space: nowrap;
340
+ }
341
+ }
342
+ }
343
+ `;
202
344
  const HighlightBar = styled_components_1.default.div `
203
345
  position: absolute;
204
346
  top: 0;
@@ -218,11 +360,19 @@ const HighlightBar = styled_components_1.default.div `
218
360
  border-radius: var(--md-tabs-${({ size }) => size}-active-tab-border-radius);
219
361
  }
220
362
  `;
363
+ const TabButtonText = styled_components_1.default.span `
364
+ overflow: hidden;
365
+ text-overflow: ellipsis;
366
+ white-space: nowrap;
367
+ flex: 1;
368
+ min-width: 0;
369
+ `;
221
370
  exports.TabButtonLink = (0, styled_components_1.default)(Button_1.Button) `
222
371
  color: var(--md-tabs-tab-text-color);
223
372
  font-family: var(--md-tabs-tab-font-family);
224
373
  font-style: var(--md-tabs-tab-font-style);
225
374
  background-color: var(--md-tabs-tab-bg-color);
375
+ width: 100%;
226
376
 
227
377
  transition:
228
378
  background-color 300ms ease-in-out,
@@ -241,9 +391,9 @@ exports.TabButtonLink = (0, styled_components_1.default)(Button_1.Button) `
241
391
 
242
392
  &.active {
243
393
  color: var(--md-tabs-active-tab-text-color);
244
- font-size: var(--md-tabs-${({ size }) => size}-active-tab-font-size);
245
394
  font-family: var(--md-tabs-active-tab-font-family);
246
395
  font-style: var(--md-tabs-active-tab-font-style);
396
+ font-size: var(--md-tabs-${({ size }) => size}-active-tab-font-size);
247
397
  font-weight: var(--md-tabs-${({ size }) => size}-active-tab-font-weight);
248
398
  line-height: var(--md-tabs-${({ size }) => size}-active-tab-line-height);
249
399
  background-color: var(--md-tabs-active-tab-bg-color);
@@ -253,12 +403,12 @@ exports.TabButtonLink = (0, styled_components_1.default)(Button_1.Button) `
253
403
 
254
404
  &:hover {
255
405
  color: var(--md-tabs-hover-tab-text-color);
256
- font-size: var(--md-tabs-${({ size }) => size}-hover-tab-font-size);
257
406
  font-family: var(--md-tabs-hover-tab-font-family);
258
407
  font-style: var(--md-tabs-hover-tab-font-style);
408
+ font-size: var(--md-tabs-${({ size }) => size}-hover-tab-font-size);
259
409
  font-weight: var(--md-tabs-${({ size }) => size}-hover-tab-font-weight);
260
- background-color: var(--md-tabs-hover-tab-bg-color);
261
410
  line-height: var(--md-tabs-${({ size }) => size}-hover-tab-line-height);
411
+ background-color: var(--md-tabs-hover-tab-bg-color);
262
412
  border-radius: var(--md-tabs-${({ size }) => size}-hover-tab-border-radius);
263
413
  padding: var(--md-tabs-${({ size }) => size}-hover-tab-padding);
264
414
  }
@@ -17,7 +17,8 @@ type TabsProps = {
17
17
  className?: string;
18
18
  size: TabsSize;
19
19
  initialTab?: string;
20
+ forceReady?: boolean;
20
21
  };
21
- export declare function Tabs({ id, children, className, size, initialTab: propInitialTab, }: TabsProps): JSX.Element;
22
+ export declare function Tabs({ id, children, className, size, initialTab: propInitialTab, forceReady, }: TabsProps): JSX.Element;
22
23
  export declare const TabContent: import("styled-components").StyledComponent<"div", any, {}, never>;
23
24
  export {};
@@ -32,14 +32,12 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
36
  exports.TabContent = exports.TabsSize = void 0;
40
37
  exports.Tabs = Tabs;
41
38
  const react_1 = __importStar(require("react"));
42
- const styled_components_1 = __importDefault(require("styled-components"));
39
+ const react_router_dom_1 = require("react-router-dom");
40
+ const styled_components_1 = __importStar(require("styled-components"));
43
41
  const hooks_1 = require("../../../core/hooks");
44
42
  const TabList_1 = require("../../../markdoc/components/Tabs/TabList");
45
43
  const utils_1 = require("../../../core/utils");
@@ -48,22 +46,62 @@ var TabsSize;
48
46
  TabsSize["SMALL"] = "small";
49
47
  TabsSize["MEDIUM"] = "medium";
50
48
  })(TabsSize || (exports.TabsSize = TabsSize = {}));
51
- function Tabs({ id, children, className, size, initialTab: propInitialTab, }) {
49
+ function Tabs({ id, children, className, size, initialTab: propInitialTab, forceReady = false, }) {
52
50
  var _a, _b;
53
- const [childrenArray, setChildrenArray] = (0, react_1.useState)(react_1.default.Children.toArray(children));
51
+ const childrenArray = react_1.default.Children.toArray(children);
52
+ const [isReady, setIsReady] = (0, react_1.useState)(false);
53
+ const containerRef = (0, react_1.useRef)(null);
54
54
  const initialTab = (_b = propInitialTab !== null && propInitialTab !== void 0 ? propInitialTab : (_a = childrenArray[0]) === null || _a === void 0 ? void 0 : _a.props.label) !== null && _b !== void 0 ? _b : '';
55
- const { activeTab, setActiveTab } = (0, hooks_1.useActiveTab)({ tabsId: id, initialTab });
56
- (0, react_1.useEffect)(() => {
57
- setChildrenArray(react_1.default.Children.toArray(children));
58
- }, [children]);
59
- return (react_1.default.createElement(TabsContainer, { "data-component-name": "Markdoc/Tabs/Tabs", className: className, key: id },
60
- react_1.default.createElement(TabList_1.TabList, { size: size, childrenArray: childrenArray, activeTab: activeTab, onTabChange: setActiveTab }),
55
+ const labelsHash = childrenArray.map((c) => c.props.label).join('|');
56
+ const handleReadyChange = (0, react_1.useCallback)((ready) => {
57
+ setIsReady(ready);
58
+ }, []);
59
+ // Reset isReady when children change (new page/tabs)
60
+ // Use useLayoutEffect to run synchronously before paint
61
+ (0, react_1.useLayoutEffect)(() => {
62
+ setIsReady(false);
63
+ }, [labelsHash]);
64
+ const inRouter = (0, react_router_dom_1.useInRouterContext)();
65
+ return (react_1.default.createElement(TabsView, { id: id, className: className, size: size, childrenArray: childrenArray, initialTab: initialTab, useActiveTab: inRouter ? useActiveTabWithRouter : useActiveTabWithoutRouter, isReady: isReady || forceReady, labelsHash: labelsHash, containerRef: containerRef, onReadyChange: handleReadyChange }));
66
+ }
67
+ function TabsView({ id, className, size, childrenArray, useActiveTab, initialTab, isReady, labelsHash, containerRef, onReadyChange, }) {
68
+ const { activeTab, setActiveTab } = useActiveTab(initialTab, id, childrenArray);
69
+ return (react_1.default.createElement(TabsContainer, { "data-component-name": "Markdoc/Tabs/Tabs", className: className, key: id, "$isReady": isReady },
70
+ react_1.default.createElement(TabList_1.TabList, { key: labelsHash, size: size, childrenArray: childrenArray, activeTab: activeTab, onTabChange: setActiveTab, containerRef: containerRef, onReadyChange: onReadyChange }),
61
71
  childrenArray.map((child, index) => {
62
72
  const { label } = child.props;
63
73
  const tabId = (0, utils_1.getTabId)(label, index);
64
74
  return label === activeTab ? (react_1.default.createElement(exports.TabContent, { key: `content-${tabId}`, id: `panel-${tabId}`, "aria-labelledby": `tab-${tabId}`, tabIndex: 0, role: "tabpanel" }, child.props.children)) : null;
65
75
  })));
66
76
  }
77
+ /**
78
+ * Validates that the active tab exists in children and resets to initial tab if not found.
79
+ * Ensures tab state remains consistent when tab structure changes.
80
+ */
81
+ function useValidateActiveTab(activeTab, setActiveTab, childrenArray, initialTab) {
82
+ (0, react_1.useEffect)(() => {
83
+ const availableLabels = childrenArray.map((child) => child.props.label);
84
+ if (activeTab && !availableLabels.includes(activeTab) && availableLabels.length > 0) {
85
+ setActiveTab(initialTab);
86
+ }
87
+ }, [childrenArray, activeTab, initialTab, setActiveTab]);
88
+ }
89
+ const useActiveTabWithRouter = (initialTab, tabsId, childrenArray) => {
90
+ const { activeTab, setActiveTab } = (0, hooks_1.useActiveTab)({ initialTab, tabsId });
91
+ useValidateActiveTab(activeTab, setActiveTab, childrenArray, initialTab);
92
+ return {
93
+ activeTab,
94
+ setActiveTab,
95
+ };
96
+ };
97
+ const useActiveTabWithoutRouter = (initialTab, _tabsId, childrenArray) => {
98
+ const [activeTab, setActiveTab] = (0, react_1.useState)(initialTab);
99
+ useValidateActiveTab(activeTab, setActiveTab, childrenArray, initialTab);
100
+ return {
101
+ activeTab,
102
+ setActiveTab,
103
+ };
104
+ };
67
105
  const TabsContainer = styled_components_1.default.div `
68
106
  position: relative;
69
107
  color: var(--md-tabs-container-text-color);
@@ -80,6 +118,13 @@ const TabsContainer = styled_components_1.default.div `
80
118
  margin: 0;
81
119
  padding: 0;
82
120
  }
121
+
122
+ ${({ $isReady }) => !$isReady &&
123
+ (0, styled_components_1.css) `
124
+ opacity: 0;
125
+ pointer-events: none;
126
+ overflow: hidden;
127
+ `}
83
128
  `;
84
129
  exports.TabContent = styled_components_1.default.div `
85
130
  color: var(--md-tabs-content-text-color);
@@ -20,3 +20,4 @@ export * from '../../markdoc/components/CodeWalkthrough/CodeToggle';
20
20
  export * from '../../markdoc/components/CodeWalkthrough/CodeContainer';
21
21
  export * from '../../markdoc/components/CodeGroup/CodeGroup';
22
22
  export * from '../../markdoc/components/Icon/Icon';
23
+ export * from '../../markdoc/components/ConnectMCP/ConnectMCP';
@@ -36,4 +36,5 @@ __exportStar(require("../../markdoc/components/CodeWalkthrough/CodeToggle"), exp
36
36
  __exportStar(require("../../markdoc/components/CodeWalkthrough/CodeContainer"), exports);
37
37
  __exportStar(require("../../markdoc/components/CodeGroup/CodeGroup"), exports);
38
38
  __exportStar(require("../../markdoc/components/Icon/Icon"), exports);
39
+ __exportStar(require("../../markdoc/components/ConnectMCP/ConnectMCP"), exports);
39
40
  //# sourceMappingURL=default.js.map
@@ -22,6 +22,7 @@ import { input } from '../markdoc/tags/input';
22
22
  import { toggle } from '../markdoc/tags/code-toggle';
23
23
  import { codeGroup } from '../markdoc/tags/code-group';
24
24
  import { icon } from '../markdoc/tags/icon';
25
+ import { connectMcp } from '../markdoc/tags/connect-mcp';
25
26
  export declare const tags: {
26
27
  [admonition.tagName]: import("@markdoc/markdoc").Schema & {
27
28
  attributes?: Record<string, import("@markdoc/markdoc").SchemaAttribute & {
@@ -113,4 +114,9 @@ export declare const tags: {
113
114
  resolver?: string;
114
115
  }>;
115
116
  };
117
+ [connectMcp.tagName]: import("@markdoc/markdoc").Schema & {
118
+ attributes?: Record<string, import("@markdoc/markdoc").SchemaAttribute & {
119
+ resolver?: string;
120
+ }>;
121
+ };
116
122
  };
@@ -64,6 +64,7 @@ const input_1 = require("../markdoc/tags/input");
64
64
  const code_toggle_1 = require("../markdoc/tags/code-toggle");
65
65
  const code_group_1 = require("../markdoc/tags/code-group");
66
66
  const icon_1 = require("../markdoc/tags/icon");
67
+ const connect_mcp_1 = require("../markdoc/tags/connect-mcp");
67
68
  exports.tags = {
68
69
  [admonition_1.admonition.tagName]: admonition_1.admonition.schema,
69
70
  [debug_1.debug.tagName]: debug_1.debug.schema,
@@ -83,5 +84,6 @@ exports.tags = {
83
84
  [input_1.input.tagName]: input_1.input.schema,
84
85
  [code_group_1.codeGroup.tagName]: code_group_1.codeGroup.schema,
85
86
  [icon_1.icon.tagName]: icon_1.icon.schema,
87
+ [connect_mcp_1.connectMcp.tagName]: connect_mcp_1.connectMcp.schema,
86
88
  };
87
89
  //# sourceMappingURL=default.js.map
@@ -54,7 +54,6 @@ exports.card = {
54
54
  },
55
55
  to: { type: String, resolver: 'link' },
56
56
  },
57
- children: ['paragraph', 'tag', 'list'],
58
57
  },
59
58
  tagName: 'card',
60
59
  };
@@ -0,0 +1,2 @@
1
+ import type { MarkdocSchemaWrapper } from '../../markdoc/tags/types';
2
+ export declare const connectMcp: MarkdocSchemaWrapper;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectMcp = void 0;
4
+ exports.connectMcp = {
5
+ schema: {
6
+ render: 'ConnectMCP',
7
+ attributes: {
8
+ placement: {
9
+ type: String,
10
+ default: 'bottom',
11
+ matches: ['top', 'bottom'],
12
+ },
13
+ alignment: {
14
+ type: String,
15
+ default: 'start',
16
+ matches: ['start', 'end'],
17
+ },
18
+ options: {
19
+ type: Array,
20
+ default: ['cursor', 'vscode', 'copy'],
21
+ },
22
+ },
23
+ selfClosing: true,
24
+ },
25
+ tagName: 'connect-mcp',
26
+ };
27
+ //# sourceMappingURL=connect-mcp.js.map
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.59.0-rc.2",
3
+ "version": "0.59.0",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
7
7
  "redocly"
8
8
  ],
9
9
  "author": "team@redocly.com",
10
- "license": "SEE LICENSE IN LICENSE",
10
+ "license": "MIT",
11
11
  "main": "lib/index.js",
12
12
  "types": "lib/index.d.ts",
13
13
  "exports": {
@@ -44,7 +44,7 @@
44
44
  "@types/highlight-words-core": "1.2.3",
45
45
  "@types/lodash.debounce": "4.0.9",
46
46
  "@types/lodash.throttle": "4.1.9",
47
- "@types/node": "22.15.3",
47
+ "@types/node": "22.18.13",
48
48
  "@types/nprogress": "0.2.3",
49
49
  "@types/react": "^19.1.4",
50
50
  "@types/react-dom": "^19.1.4",
@@ -84,11 +84,11 @@
84
84
  "lodash.debounce": "^4.0.8",
85
85
  "lodash.throttle": "4.1.1",
86
86
  "nprogress": "0.2.0",
87
- "openapi-sampler": "1.6.1",
87
+ "openapi-sampler": "1.6.2",
88
88
  "react-calendar": "5.1.0",
89
89
  "react-date-picker": "11.0.0",
90
- "@redocly/realm-asyncapi-sdk": "0.5.0-next.0",
91
- "@redocly/config": "0.36.0-rc.1"
90
+ "@redocly/config": "0.38.0",
91
+ "@redocly/realm-asyncapi-sdk": "0.5.0"
92
92
  },
93
93
  "scripts": {
94
94
  "watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
@@ -21,7 +21,7 @@ interface AIAssistantButtonConfig {
21
21
  const defaultConfig: Required<AIAssistantButtonConfig> = {
22
22
  hide: false,
23
23
  inputType: 'button',
24
- inputIcon: 'redocly',
24
+ inputIcon: 'sparkles',
25
25
  };
26
26
 
27
27
  const getIcon = (
@@ -56,8 +56,9 @@ const getIcon = (
56
56
  export function AIAssistantButton() {
57
57
  const [isOpen, setIsOpen] = useState(false);
58
58
  const themeConfig = useThemeConfig();
59
- const { useTranslate } = useThemeHooks();
59
+ const { useTranslate, useTelemetry } = useThemeHooks();
60
60
  const { translate } = useTranslate();
61
+ const telemetry = useTelemetry();
61
62
 
62
63
  const buttonConfig = {
63
64
  ...defaultConfig,
@@ -75,6 +76,9 @@ export function AIAssistantButton() {
75
76
 
76
77
  const handleOpen = () => {
77
78
  setIsOpen(true);
79
+ telemetry.sendSearchAiOpenedMessage({
80
+ method: 'ai_trigger_button',
81
+ });
78
82
  };
79
83
 
80
84
  const handleClose = () => {