@redocly/theme 0.70.0-next.1 → 0.70.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 (378) hide show
  1. package/lib/components/Buttons/ConnectMCPButton.js +19 -14
  2. package/lib/components/Buttons/variables.d.ts +1 -0
  3. package/lib/components/Buttons/variables.js +4 -1
  4. package/lib/components/Catalog/Catalog.js +3 -3
  5. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.d.ts +1 -1
  6. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +1 -1
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +2 -2
  8. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +1 -1
  9. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +30 -11
  10. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +2 -1
  11. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.js +1 -1
  12. package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.js +2 -2
  13. package/lib/components/CodeBlock/CodeBlockControls.js +3 -5
  14. package/lib/components/Consent/Consent.d.ts +2 -0
  15. package/lib/components/Consent/Consent.js +17 -0
  16. package/lib/components/Consent/ConsentBanner.d.ts +2 -0
  17. package/lib/components/Consent/ConsentBanner.js +120 -0
  18. package/lib/components/Consent/ConsentDialog.d.ts +2 -0
  19. package/lib/components/Consent/ConsentDialog.js +128 -0
  20. package/lib/components/Consent/ConsentSettingsLink.d.ts +2 -0
  21. package/lib/components/Consent/ConsentSettingsLink.js +35 -0
  22. package/lib/components/Consent/variables.d.ts +1 -0
  23. package/lib/components/Consent/variables.js +31 -0
  24. package/lib/components/Dropdown/Dropdown.d.ts +4 -2
  25. package/lib/components/Dropdown/Dropdown.js +31 -9
  26. package/lib/components/Dropdown/DropdownMenu.d.ts +1 -0
  27. package/lib/components/Dropdown/DropdownMenu.js +1 -1
  28. package/lib/components/Feedback/Reasons.js +3 -2
  29. package/lib/components/Filter/FilterCheckboxes.js +30 -11
  30. package/lib/components/Filter/FilterDateRange.js +36 -2
  31. package/lib/components/Footer/Footer.js +4 -1
  32. package/lib/components/Image/Image.js +24 -4
  33. package/lib/components/JsonViewer/Helpers.d.ts +10 -2
  34. package/lib/components/JsonViewer/Helpers.js +32 -36
  35. package/lib/components/JsonViewer/JsonViewer.js +15 -18
  36. package/lib/components/LoadProgressBar/LoadProgressBar.d.ts +8 -0
  37. package/lib/components/LoadProgressBar/LoadProgressBar.js +122 -0
  38. package/lib/components/MCP/McpConnectionTabs.js +9 -2
  39. package/lib/components/Markdown/Markdown.js +3 -2
  40. package/lib/components/Markdown/styles/base-table.js +14 -1
  41. package/lib/components/Markdown/variables.js +1 -0
  42. package/lib/components/Menu/Menu.js +4 -0
  43. package/lib/components/Menu/MenuSelect.d.ts +7 -0
  44. package/lib/components/Menu/MenuSelect.js +129 -0
  45. package/lib/components/Menu/variables.js +43 -4
  46. package/lib/components/Navbar/Navbar.js +44 -5
  47. package/lib/components/Navbar/variables.js +3 -0
  48. package/lib/components/NavigationTabs/NavigationTab.d.ts +7 -0
  49. package/lib/components/NavigationTabs/NavigationTab.js +93 -0
  50. package/lib/components/NavigationTabs/NavigationTabContent.d.ts +7 -0
  51. package/lib/components/NavigationTabs/NavigationTabContent.js +50 -0
  52. package/lib/components/NavigationTabs/NavigationTabMenu.d.ts +7 -0
  53. package/lib/components/NavigationTabs/NavigationTabMenu.js +18 -0
  54. package/lib/components/NavigationTabs/NavigationTabs.d.ts +7 -0
  55. package/lib/components/NavigationTabs/NavigationTabs.js +97 -0
  56. package/lib/components/NavigationTabs/variables.d.ts +1 -0
  57. package/lib/components/NavigationTabs/variables.js +37 -0
  58. package/lib/components/Portal/Portal.js +37 -5
  59. package/lib/components/Product/utils.js +1 -1
  60. package/lib/components/Search/SearchAiActionButtons.js +8 -3
  61. package/lib/components/Search/SearchAiActivity.d.ts +10 -0
  62. package/lib/components/Search/SearchAiActivity.js +278 -0
  63. package/lib/components/Search/SearchAiConversationInput.js +10 -6
  64. package/lib/components/Search/SearchAiDialog.js +13 -2
  65. package/lib/components/Search/SearchAiMessage.d.ts +2 -1
  66. package/lib/components/Search/SearchAiMessage.js +21 -109
  67. package/lib/components/Search/SearchAiResponse.js +1 -0
  68. package/lib/components/Search/SearchDialog.js +10 -0
  69. package/lib/components/Search/SearchItem.js +11 -10
  70. package/lib/components/Search/SearchTrigger.js +37 -6
  71. package/lib/components/Search/variables.js +17 -2
  72. package/lib/components/Segmented/Segmented.js +23 -3
  73. package/lib/components/Select/Select.js +10 -3
  74. package/lib/components/Select/SelectInput.d.ts +1 -0
  75. package/lib/components/Select/SelectInput.js +5 -11
  76. package/lib/components/Sidebar/Sidebar.js +2 -2
  77. package/lib/components/SplitButton/SplitButton.js +37 -3
  78. package/lib/components/TableOfContent/TableOfContent.js +2 -2
  79. package/lib/core/constants/common.d.ts +1 -0
  80. package/lib/core/constants/common.js +1 -0
  81. package/lib/core/constants/consent.d.ts +2 -0
  82. package/lib/core/constants/consent.js +30 -0
  83. package/lib/core/constants/index.d.ts +2 -0
  84. package/lib/core/constants/index.js +2 -0
  85. package/lib/core/constants/mcp.d.ts +2 -2
  86. package/lib/core/constants/mcp.js +3 -8
  87. package/lib/core/constants/prompt.d.ts +9 -0
  88. package/lib/core/constants/prompt.js +33 -0
  89. package/lib/core/constants/search.d.ts +1 -1
  90. package/lib/core/constants/search.js +6 -2
  91. package/lib/core/contexts/PortalContainerContext.d.ts +2 -0
  92. package/lib/core/contexts/PortalContainerContext.js +7 -0
  93. package/lib/core/hooks/code-walkthrough/use-code-panel.d.ts +1 -1
  94. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.d.ts +1 -1
  95. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +1 -1
  96. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +1 -1
  97. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +1 -2
  98. package/lib/core/hooks/code-walkthrough/use-renderable-files.d.ts +1 -1
  99. package/lib/core/hooks/index.d.ts +9 -0
  100. package/lib/core/hooks/index.js +9 -0
  101. package/lib/core/hooks/menu/use-collapse.js +1 -1
  102. package/lib/core/hooks/menu/use-mobile-menu-items.js +75 -51
  103. package/lib/core/hooks/menu/use-mobile-menu-levels.js +12 -7
  104. package/lib/core/hooks/prompt/use-prompt-actions.d.ts +7 -0
  105. package/lib/core/hooks/prompt/use-prompt-actions.js +19 -0
  106. package/lib/core/hooks/prompt/use-prompt-text-limit.d.ts +19 -0
  107. package/lib/core/hooks/prompt/use-prompt-text-limit.js +35 -0
  108. package/lib/core/hooks/search/use-persisted-quick-filter.d.ts +21 -0
  109. package/lib/core/hooks/search/use-persisted-quick-filter.js +92 -0
  110. package/lib/core/hooks/search/use-search-ai-activity.d.ts +11 -0
  111. package/lib/core/hooks/search/use-search-ai-activity.js +15 -0
  112. package/lib/core/hooks/use-connect-mcp-button.js +3 -1
  113. package/lib/core/hooks/use-consent-dialog.d.ts +12 -0
  114. package/lib/core/hooks/use-consent-dialog.js +36 -0
  115. package/lib/core/hooks/use-consent-telemetry.d.ts +1 -0
  116. package/lib/core/hooks/use-consent-telemetry.js +42 -0
  117. package/lib/core/hooks/use-dialog-hotkeys.js +4 -2
  118. package/lib/core/hooks/use-full-height.d.ts +1 -1
  119. package/lib/core/hooks/use-full-height.js +8 -8
  120. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  121. package/lib/core/hooks/use-page-actions.js +10 -8
  122. package/lib/core/hooks/use-pointer-border-highlight.d.ts +7 -0
  123. package/lib/core/hooks/use-pointer-border-highlight.js +29 -0
  124. package/lib/core/hooks/use-scroll-item-into-view.d.ts +2 -0
  125. package/lib/core/hooks/use-scroll-item-into-view.js +16 -0
  126. package/lib/core/hooks/use-tab-list.d.ts +31 -0
  127. package/lib/core/hooks/use-tab-list.js +49 -0
  128. package/lib/core/hooks/use-telemetry-fallback.d.ts +2 -0
  129. package/lib/core/hooks/use-telemetry-fallback.js +2 -0
  130. package/lib/core/hooks/use-theme-hooks.js +16 -0
  131. package/lib/core/styles/dark.js +14 -12
  132. package/lib/core/styles/global.d.ts +2 -0
  133. package/lib/core/styles/global.js +115 -136
  134. package/lib/core/styles/index.d.ts +1 -0
  135. package/lib/core/styles/index.js +3 -1
  136. package/lib/core/styles/search-ai-appear.d.ts +1 -0
  137. package/lib/core/styles/search-ai-appear.js +18 -0
  138. package/lib/core/types/code-walkthrough.d.ts +84 -0
  139. package/lib/core/types/consent.d.ts +54 -0
  140. package/lib/core/types/consent.js +3 -0
  141. package/lib/core/types/dropdown.d.ts +1 -0
  142. package/lib/core/types/dropdown.js +3 -0
  143. package/lib/core/types/hooks.d.ts +8 -1
  144. package/lib/core/types/index.d.ts +4 -0
  145. package/lib/core/types/l10n.d.ts +1 -1
  146. package/lib/core/types/mcp.d.ts +3 -2
  147. package/lib/core/types/navigation-tabs.d.ts +49 -0
  148. package/lib/core/types/navigation-tabs.js +3 -0
  149. package/lib/core/types/prompt.d.ts +9 -0
  150. package/lib/core/types/prompt.js +3 -0
  151. package/lib/core/types/search.d.ts +25 -0
  152. package/lib/core/utils/Dynamic.d.ts +8 -1
  153. package/lib/core/utils/Dynamic.js +34 -3
  154. package/lib/core/utils/assign-ref.d.ts +2 -0
  155. package/lib/core/utils/assign-ref.js +12 -0
  156. package/lib/core/utils/consent-settings-link-placement.d.ts +3 -0
  157. package/lib/core/utils/consent-settings-link-placement.js +15 -0
  158. package/lib/core/utils/content-segments.d.ts +12 -1
  159. package/lib/core/utils/content-segments.js +90 -5
  160. package/lib/core/utils/download-code-walkthrough.d.ts +1 -1
  161. package/lib/core/utils/dropdown.d.ts +2 -0
  162. package/lib/core/utils/dropdown.js +12 -0
  163. package/lib/core/utils/get-ai-prompt-link.d.ts +15 -0
  164. package/lib/core/utils/get-ai-prompt-link.js +29 -0
  165. package/lib/core/utils/get-code-walkthrough-file-text.d.ts +1 -1
  166. package/lib/core/utils/index.d.ts +5 -0
  167. package/lib/core/utils/index.js +5 -0
  168. package/lib/core/utils/match-code-walkthrough-conditions.d.ts +1 -1
  169. package/lib/core/utils/mcp.d.ts +2 -1
  170. package/lib/core/utils/mcp.js +13 -3
  171. package/lib/core/utils/menu.d.ts +4 -0
  172. package/lib/core/utils/menu.js +48 -8
  173. package/lib/core/utils/navigation-tabs.d.ts +8 -0
  174. package/lib/core/utils/navigation-tabs.js +71 -0
  175. package/lib/core/utils/search-ai-activity.d.ts +4 -0
  176. package/lib/core/utils/search-ai-activity.js +83 -0
  177. package/lib/core/utils/urls.js +3 -1
  178. package/lib/core/utils/with-load-progress.d.ts +3 -0
  179. package/lib/core/utils/with-load-progress.js +24 -19
  180. package/lib/ext/configure.d.ts +1 -1
  181. package/lib/ext/configure.js +0 -1
  182. package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.d.ts +29 -0
  183. package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.js +81 -0
  184. package/lib/icons/LinkIcon/LinkIconSymbol.d.ts +12 -0
  185. package/lib/icons/LinkIcon/LinkIconSymbol.js +41 -0
  186. package/lib/index.d.ts +7 -0
  187. package/lib/index.js +9 -1
  188. package/lib/layouts/PageLayout.js +1 -1
  189. package/lib/layouts/RootLayout.js +2 -2
  190. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.d.ts +1 -1
  191. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +1 -1
  192. package/lib/markdoc/components/CodeWalkthrough/CodePanel.d.ts +1 -1
  193. package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +3 -3
  194. package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.d.ts +1 -1
  195. package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.d.ts +1 -1
  196. package/lib/markdoc/components/CodeWalkthrough/CodeStep.d.ts +1 -1
  197. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.d.ts +1 -1
  198. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +1 -1
  199. package/lib/markdoc/components/Prompt/Prompt.d.ts +15 -0
  200. package/lib/markdoc/components/Prompt/Prompt.js +163 -0
  201. package/lib/markdoc/components/Prompt/PromptActions.d.ts +13 -0
  202. package/lib/markdoc/components/Prompt/PromptActions.js +43 -0
  203. package/lib/markdoc/components/Prompt/PromptBody.d.ts +14 -0
  204. package/lib/markdoc/components/Prompt/PromptBody.js +99 -0
  205. package/lib/markdoc/components/Prompt/PromptCopyButton.d.ts +10 -0
  206. package/lib/markdoc/components/Prompt/PromptCopyButton.js +28 -0
  207. package/lib/markdoc/components/Prompt/PromptHeader.d.ts +9 -0
  208. package/lib/markdoc/components/Prompt/PromptHeader.js +57 -0
  209. package/lib/markdoc/components/Prompt/PromptShowHideButton.d.ts +7 -0
  210. package/lib/markdoc/components/Prompt/PromptShowHideButton.js +21 -0
  211. package/lib/markdoc/components/Prompt/PromptToggleLabel.d.ts +7 -0
  212. package/lib/markdoc/components/Prompt/PromptToggleLabel.js +28 -0
  213. package/lib/markdoc/components/Prompt/variables.d.ts +1 -0
  214. package/lib/markdoc/components/Prompt/variables.dark.d.ts +1 -0
  215. package/lib/markdoc/components/Prompt/variables.dark.js +8 -0
  216. package/lib/markdoc/components/Prompt/variables.js +44 -0
  217. package/lib/markdoc/components/Tabs/TabList.js +8 -6
  218. package/lib/markdoc/components/default.d.ts +1 -0
  219. package/lib/markdoc/components/default.js +1 -0
  220. package/lib/markdoc/default.d.ts +2 -0
  221. package/lib/markdoc/default.js +2 -0
  222. package/lib/markdoc/tags/code-walkthrough.d.ts +1 -1
  223. package/lib/markdoc/tags/connect-mcp.js +15 -0
  224. package/lib/markdoc/tags/prompt.d.ts +2 -0
  225. package/lib/markdoc/tags/prompt.js +56 -0
  226. package/package.json +4 -8
  227. package/src/components/Buttons/ConnectMCPButton.tsx +21 -16
  228. package/src/components/Buttons/variables.ts +4 -0
  229. package/src/components/Catalog/Catalog.tsx +6 -4
  230. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.tsx +3 -1
  231. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +2 -2
  232. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +1 -1
  233. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +37 -22
  234. package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +3 -2
  235. package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.tsx +1 -1
  236. package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.tsx +2 -2
  237. package/src/components/CodeBlock/CodeBlockControls.tsx +4 -7
  238. package/src/components/Consent/Consent.tsx +18 -0
  239. package/src/components/Consent/ConsentBanner.tsx +110 -0
  240. package/src/components/Consent/ConsentDialog.tsx +210 -0
  241. package/src/components/Consent/ConsentSettingsLink.tsx +44 -0
  242. package/src/components/Consent/variables.ts +28 -0
  243. package/src/components/Dropdown/Dropdown.tsx +32 -7
  244. package/src/components/Dropdown/DropdownMenu.tsx +7 -1
  245. package/src/components/Feedback/Reasons.tsx +3 -2
  246. package/src/components/Filter/FilterCheckboxes.tsx +33 -18
  247. package/src/components/Filter/FilterDateRange.tsx +3 -2
  248. package/src/components/Footer/Footer.tsx +2 -0
  249. package/src/components/Image/Image.tsx +26 -5
  250. package/src/components/JsonViewer/Helpers.tsx +51 -54
  251. package/src/components/JsonViewer/JsonViewer.tsx +23 -25
  252. package/src/components/LoadProgressBar/LoadProgressBar.tsx +113 -0
  253. package/src/components/MCP/McpConnectionTabs.tsx +22 -5
  254. package/src/components/Markdown/Markdown.tsx +3 -2
  255. package/src/components/Markdown/styles/base-table.ts +14 -1
  256. package/src/components/Markdown/variables.ts +1 -0
  257. package/src/components/Menu/Menu.tsx +5 -0
  258. package/src/components/Menu/MenuSelect.tsx +198 -0
  259. package/src/components/Menu/variables.ts +43 -4
  260. package/src/components/Navbar/Navbar.tsx +22 -5
  261. package/src/components/Navbar/variables.ts +3 -0
  262. package/src/components/NavigationTabs/NavigationTab.tsx +148 -0
  263. package/src/components/NavigationTabs/NavigationTabContent.tsx +66 -0
  264. package/src/components/NavigationTabs/NavigationTabMenu.tsx +40 -0
  265. package/src/components/NavigationTabs/NavigationTabs.tsx +83 -0
  266. package/src/components/NavigationTabs/variables.ts +34 -0
  267. package/src/components/Portal/Portal.tsx +4 -2
  268. package/src/components/Product/utils.ts +1 -1
  269. package/src/components/Search/SearchAiActionButtons.tsx +31 -19
  270. package/src/components/Search/SearchAiActivity.tsx +318 -0
  271. package/src/components/Search/SearchAiConversationInput.tsx +13 -7
  272. package/src/components/Search/SearchAiDialog.tsx +20 -4
  273. package/src/components/Search/SearchAiMessage.tsx +28 -136
  274. package/src/components/Search/SearchAiResponse.tsx +1 -0
  275. package/src/components/Search/SearchDialog.tsx +11 -0
  276. package/src/components/Search/SearchItem.tsx +19 -10
  277. package/src/components/Search/SearchTrigger.tsx +47 -3
  278. package/src/components/Search/variables.ts +17 -2
  279. package/src/components/Segmented/Segmented.tsx +25 -16
  280. package/src/components/Select/Select.tsx +12 -6
  281. package/src/components/Select/SelectInput.tsx +10 -18
  282. package/src/components/Sidebar/Sidebar.tsx +2 -2
  283. package/src/components/SplitButton/SplitButton.tsx +8 -2
  284. package/src/components/TableOfContent/TableOfContent.tsx +2 -2
  285. package/src/core/constants/common.ts +1 -0
  286. package/src/core/constants/consent.ts +29 -0
  287. package/src/core/constants/index.ts +2 -0
  288. package/src/core/constants/mcp.ts +3 -9
  289. package/src/core/constants/prompt.ts +34 -0
  290. package/src/core/constants/search.ts +6 -2
  291. package/src/core/contexts/PortalContainerContext.tsx +4 -0
  292. package/src/core/hooks/__mocks__/index.ts +5 -0
  293. package/src/core/hooks/__mocks__/use-theme-hooks.ts +20 -0
  294. package/src/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.ts +1 -2
  295. package/src/core/hooks/code-walkthrough/__mocks__/mock-intersection-observer.ts +1 -2
  296. package/src/core/hooks/code-walkthrough/use-code-panel.ts +1 -1
  297. package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +2 -2
  298. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +1 -1
  299. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +3 -3
  300. package/src/core/hooks/code-walkthrough/use-renderable-files.ts +1 -1
  301. package/src/core/hooks/index.ts +9 -0
  302. package/src/core/hooks/menu/use-collapse.ts +1 -1
  303. package/src/core/hooks/menu/use-mobile-menu-items.ts +91 -56
  304. package/src/core/hooks/menu/use-mobile-menu-levels.ts +14 -6
  305. package/src/core/hooks/prompt/use-prompt-actions.ts +28 -0
  306. package/src/core/hooks/prompt/use-prompt-text-limit.ts +59 -0
  307. package/src/core/hooks/search/use-persisted-quick-filter.ts +125 -0
  308. package/src/core/hooks/search/use-search-ai-activity.ts +31 -0
  309. package/src/core/hooks/use-connect-mcp-button.ts +13 -2
  310. package/src/core/hooks/use-consent-dialog.ts +51 -0
  311. package/src/core/hooks/use-consent-telemetry.ts +56 -0
  312. package/src/core/hooks/use-dialog-hotkeys.ts +11 -5
  313. package/src/core/hooks/use-full-height.ts +8 -8
  314. package/src/core/hooks/use-page-actions.ts +21 -9
  315. package/src/core/hooks/use-pointer-border-highlight.ts +46 -0
  316. package/src/core/hooks/use-scroll-item-into-view.ts +18 -0
  317. package/src/core/hooks/use-tab-list.ts +63 -0
  318. package/src/core/hooks/use-telemetry-fallback.ts +2 -0
  319. package/src/core/hooks/use-theme-hooks.ts +16 -0
  320. package/src/core/styles/dark.ts +2 -0
  321. package/src/core/styles/global.ts +96 -116
  322. package/src/core/styles/index.ts +2 -1
  323. package/src/core/styles/search-ai-appear.ts +16 -0
  324. package/src/core/types/code-walkthrough.ts +103 -0
  325. package/src/core/types/consent.ts +51 -0
  326. package/src/core/types/dropdown.ts +1 -0
  327. package/src/core/types/hooks.ts +8 -0
  328. package/src/core/types/index.ts +4 -0
  329. package/src/core/types/l10n.ts +46 -2
  330. package/src/core/types/mcp.ts +4 -2
  331. package/src/core/types/navigation-tabs.ts +54 -0
  332. package/src/core/types/prompt.ts +11 -0
  333. package/src/core/types/search.ts +23 -0
  334. package/src/core/utils/Dynamic.tsx +67 -35
  335. package/src/core/utils/assign-ref.ts +9 -0
  336. package/src/core/utils/consent-settings-link-placement.ts +18 -0
  337. package/src/core/utils/content-segments.ts +98 -6
  338. package/src/core/utils/download-code-walkthrough.ts +1 -1
  339. package/src/core/utils/dropdown.ts +14 -0
  340. package/src/core/utils/get-ai-prompt-link.ts +30 -0
  341. package/src/core/utils/get-code-walkthrough-file-text.ts +1 -1
  342. package/src/core/utils/index.ts +5 -0
  343. package/src/core/utils/match-code-walkthrough-conditions.ts +1 -1
  344. package/src/core/utils/mcp.ts +16 -4
  345. package/src/core/utils/menu.ts +54 -9
  346. package/src/core/utils/navigation-tabs.ts +99 -0
  347. package/src/core/utils/search-ai-activity.ts +110 -0
  348. package/src/core/utils/urls.ts +3 -1
  349. package/src/core/utils/with-load-progress.ts +23 -18
  350. package/src/ext/configure.ts +1 -1
  351. package/src/icons/CheckboxIcon/CheckboxIcon.tsx +1 -0
  352. package/src/icons/ChevronSortIcon/ChevronSortIcon.tsx +1 -0
  353. package/src/icons/ExpandCollapseIcon/ExpandCollapseIcon.tsx +56 -0
  354. package/src/icons/LinkIcon/LinkIconSymbol.tsx +51 -0
  355. package/src/index.ts +8 -0
  356. package/src/layouts/PageLayout.tsx +1 -1
  357. package/src/layouts/RootLayout.tsx +2 -2
  358. package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +2 -2
  359. package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +4 -4
  360. package/src/markdoc/components/CodeWalkthrough/CodePanelHeader.tsx +1 -1
  361. package/src/markdoc/components/CodeWalkthrough/CodePanelToolbar.tsx +1 -1
  362. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +1 -1
  363. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +2 -2
  364. package/src/markdoc/components/Prompt/Prompt.tsx +219 -0
  365. package/src/markdoc/components/Prompt/PromptActions.tsx +104 -0
  366. package/src/markdoc/components/Prompt/PromptBody.tsx +107 -0
  367. package/src/markdoc/components/Prompt/PromptCopyButton.tsx +73 -0
  368. package/src/markdoc/components/Prompt/PromptHeader.tsx +81 -0
  369. package/src/markdoc/components/Prompt/PromptShowHideButton.tsx +43 -0
  370. package/src/markdoc/components/Prompt/PromptToggleLabel.tsx +41 -0
  371. package/src/markdoc/components/Prompt/variables.dark.ts +5 -0
  372. package/src/markdoc/components/Prompt/variables.ts +41 -0
  373. package/src/markdoc/components/Tabs/TabList.tsx +9 -12
  374. package/src/markdoc/components/default.ts +1 -0
  375. package/src/markdoc/default.ts +2 -0
  376. package/src/markdoc/tags/code-walkthrough.ts +1 -1
  377. package/src/markdoc/tags/connect-mcp.ts +16 -1
  378. package/src/markdoc/tags/prompt.ts +58 -0
@@ -0,0 +1,59 @@
1
+ import { useLayoutEffect, useRef, useState } from 'react';
2
+
3
+ import type { RefObject } from 'react';
4
+
5
+ export type PromptTextLimit = 'none' | 'oneLine' | 'maxLines';
6
+
7
+ export type UsePromptTextLimitParams = {
8
+ prompt: string;
9
+ singleLine: boolean;
10
+ isPromptVisible: boolean;
11
+ };
12
+
13
+ export type PromptTextLimitState = {
14
+ textRef: RefObject<HTMLDivElement | null>;
15
+ textLimit: PromptTextLimit;
16
+ isExpanded: boolean;
17
+ hasHiddenText: boolean;
18
+ toggleExpanded: () => void;
19
+ };
20
+
21
+ /**
22
+ * Tracks the line limit of the prompt text (one line or the maximum lines) and whether the
23
+ * reader expanded it. `hasHiddenText` is true only when the limit hides some text.
24
+ */
25
+ export function usePromptTextLimit({
26
+ prompt,
27
+ singleLine,
28
+ isPromptVisible,
29
+ }: UsePromptTextLimitParams): PromptTextLimitState {
30
+ const textRef = useRef<HTMLDivElement>(null);
31
+ const [isExpanded, setIsExpanded] = useState(false);
32
+ const [hasHiddenText, setHasHiddenText] = useState(false);
33
+
34
+ /* The text mounts late with hidePrompt and re-wraps on resize, so the observer is
35
+ attached each time the text (re)appears. */
36
+ useLayoutEffect(() => {
37
+ const text = textRef.current;
38
+ if (!text || isExpanded) return;
39
+
40
+ const measure = () =>
41
+ setHasHiddenText(
42
+ text.scrollHeight > text.clientHeight || text.scrollWidth > text.clientWidth,
43
+ );
44
+ measure();
45
+
46
+ if (typeof ResizeObserver === 'undefined') return;
47
+ const observer = new ResizeObserver(measure);
48
+ observer.observe(text);
49
+ return () => observer.disconnect();
50
+ }, [prompt, singleLine, isExpanded, isPromptVisible]);
51
+
52
+ return {
53
+ textRef,
54
+ textLimit: isExpanded ? 'none' : singleLine ? 'oneLine' : 'maxLines',
55
+ isExpanded,
56
+ hasHiddenText,
57
+ toggleExpanded: () => setIsExpanded((expanded) => !expanded),
58
+ };
59
+ }
@@ -0,0 +1,125 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ import type { Dispatch, SetStateAction } from 'react';
4
+ import type { SearchFacet, SearchFilterItem } from '../../types/search';
5
+
6
+ import { createStore, useStore } from '../use-store';
7
+
8
+ type StoredQuickFilter = Pick<SearchFilterItem, 'field' | 'values'>;
9
+
10
+ const quickFilterStore = createStore<StoredQuickFilter | null>({
11
+ storageKey: 'searchQuickFilter',
12
+ storageType: 'sessionStorage',
13
+ });
14
+
15
+ const findQuickFilter = (filter: SearchFilterItem[]) => filter.find((item) => item.isQuickFilter);
16
+
17
+ const withQuickFilter = (filter: SearchFilterItem[], quickFilter: StoredQuickFilter) => [
18
+ ...filter.filter((item) => !item.isQuickFilter),
19
+ ...(quickFilter.values.length ? [{ ...quickFilter, isQuickFilter: true }] : []),
20
+ ];
21
+
22
+ const groupFacetValues = (facets: SearchFacet[], field: string) => {
23
+ const facet = facets.find((item) => item.field === field);
24
+ const values = facet?.values ?? [];
25
+ return {
26
+ present: facet !== undefined,
27
+ values: values.map((item) => (typeof item === 'string' ? item : item.value)),
28
+ counted: values.some((item) => typeof item !== 'string' && item.isCounterVisible),
29
+ };
30
+ };
31
+
32
+ const isRestorable = (
33
+ stored: StoredQuickFilter | null,
34
+ groupField: string,
35
+ ): stored is StoredQuickFilter =>
36
+ !!stored &&
37
+ stored.field === groupField &&
38
+ Array.isArray(stored.values) &&
39
+ stored.values.length > 0 &&
40
+ stored.values.every((value) => typeof value === 'string');
41
+
42
+ type UsePersistedQuickFilterOptions = {
43
+ filter: SearchFilterItem[];
44
+ setFilter: Dispatch<SetStateAction<SearchFilterItem[]>>;
45
+ facets: SearchFacet[];
46
+ groupField: string;
47
+ query: string;
48
+ isSearchLoading: boolean;
49
+ searchError: string | null;
50
+ enabled?: boolean;
51
+ };
52
+
53
+ /**
54
+ * Remembers the quick filter on `groupField` for the browser tab and restores it on mount unless
55
+ * one is already selected. Restored values are dropped once known not to exist: absent from the
56
+ * full value list, or from the `groupField` facet left by the first search that finishes without
57
+ * error on an empty query (the restored value is that search's filter, so a value without
58
+ * documents is not counted). Does nothing while `enabled` is false.
59
+ */
60
+ export function usePersistedQuickFilter({
61
+ filter,
62
+ setFilter,
63
+ facets,
64
+ groupField,
65
+ query,
66
+ isSearchLoading,
67
+ searchError,
68
+ enabled = true,
69
+ }: UsePersistedQuickFilterOptions): void {
70
+ const [stored, setStored] = useStore(quickFilterStore, null);
71
+ const [initial] = useState(() =>
72
+ enabled && isRestorable(stored, groupField) && !findQuickFilter(filter) ? stored.values : null,
73
+ );
74
+ const phaseRef = useRef<'restoring' | 'validating' | 'done'>(initial ? 'restoring' : 'done');
75
+ const searchedRef = useRef(false);
76
+
77
+ useEffect(() => {
78
+ if (!initial) return;
79
+ // oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- the filter state belongs to the product's `useSearch` and starts empty on every mount
80
+ setFilter((current) => withQuickFilter(current, { field: groupField, values: initial }));
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- applies only to the initial state
82
+ }, []);
83
+
84
+ useEffect(() => {
85
+ if (!enabled) return;
86
+ const quickFilter = findQuickFilter(filter);
87
+ // The commit that starts the restore still sees the empty initial filter.
88
+ if (phaseRef.current === 'restoring') {
89
+ if (!quickFilter) return;
90
+ phaseRef.current = 'validating';
91
+ }
92
+ if (phaseRef.current === 'validating' && initial) {
93
+ if (isSearchLoading) searchedRef.current = true;
94
+ const settled = searchedRef.current && !isSearchLoading && !searchError && !query.trim();
95
+ const { present, values: known, counted } = groupFacetValues(facets, groupField);
96
+ if ((known.length && !counted) || (settled && present)) {
97
+ phaseRef.current = 'done';
98
+ const values = quickFilter?.values.filter(
99
+ (value) => known.includes(value) || !initial.includes(value),
100
+ );
101
+ if (quickFilter && values && values.length !== quickFilter.values.length) {
102
+ // oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- the facets arrive after the restore
103
+ setFilter((current) => withQuickFilter(current, { field: groupField, values }));
104
+ return;
105
+ }
106
+ }
107
+ }
108
+ const next = quickFilter ? { field: quickFilter.field, values: quickFilter.values } : null;
109
+ if (JSON.stringify(next) !== JSON.stringify(stored)) {
110
+ setStored(next);
111
+ }
112
+ }, [
113
+ enabled,
114
+ filter,
115
+ facets,
116
+ groupField,
117
+ query,
118
+ isSearchLoading,
119
+ searchError,
120
+ initial,
121
+ stored,
122
+ setFilter,
123
+ setStored,
124
+ ]);
125
+ }
@@ -0,0 +1,31 @@
1
+ import type { SearchAiActivityStatus, SearchAiActivityStep, ToolCall } from '../../types/search';
2
+
3
+ import { useThemeHooks } from '../use-theme-hooks';
4
+ import { getSearchAiActivityStatus, getSearchAiActivityStep } from '../../utils/search-ai-activity';
5
+
6
+ type UseSearchAiActivityParams = {
7
+ toolCalls: ToolCall[];
8
+ isActive: boolean;
9
+ };
10
+
11
+ type UseSearchAiActivityReturn = {
12
+ status: SearchAiActivityStatus;
13
+ steps: SearchAiActivityStep[];
14
+ };
15
+
16
+ export function useSearchAiActivity({
17
+ toolCalls,
18
+ isActive,
19
+ }: UseSearchAiActivityParams): UseSearchAiActivityReturn {
20
+ const { useTranslate } = useThemeHooks();
21
+ const { translate } = useTranslate();
22
+
23
+ const runningToolCall = isActive ? toolCalls.find((toolCall) => !toolCall.result) : undefined;
24
+
25
+ return {
26
+ status: getSearchAiActivityStatus(toolCalls, isActive, runningToolCall, translate),
27
+ steps: toolCalls.map((toolCall) =>
28
+ getSearchAiActivityStep(toolCall, isActive && !toolCall.result, translate),
29
+ ),
30
+ };
31
+ }
@@ -7,7 +7,12 @@ import { useThemeHooks } from './use-theme-hooks';
7
7
  import { useMCPConfig } from './use-mcp-config';
8
8
  import { ClipboardService } from '../utils/clipboard-service';
9
9
  import { DEFAULT_MCP_OPTIONS } from '../constants/mcp';
10
- import { generateClaudeCodeCommand, generateCodexCommand } from '../utils/mcp';
10
+ import {
11
+ generateClaudeCodeCommand,
12
+ generateCodexCommand,
13
+ generateMCPDeepLink,
14
+ isMCPOption,
15
+ } from '../utils/mcp';
11
16
  import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
12
17
 
13
18
  export type McpButtonOptions = {
@@ -68,6 +73,7 @@ export function useConnectMCPButton({
68
73
  const urlMap: Partial<Record<MCPOption, string>> = {
69
74
  cursor: cursorUrl,
70
75
  vscode: vscodeUrl,
76
+ claude: generateMCPDeepLink('claude', connection),
71
77
  };
72
78
 
73
79
  window.open(urlMap[action], '_blank');
@@ -80,7 +86,12 @@ export function useConnectMCPButton({
80
86
  [translate],
81
87
  );
82
88
 
83
- const visibleOptions = useMemo(() => options.filter(Boolean), [options]);
89
+ // Markdoc reports attribute errors but still renders the tag, so `options` can be any value.
90
+ const visibleOptions = useMemo(
91
+ () =>
92
+ Array.isArray(options) ? [...new Set(options.filter(isMCPOption))] : [...DEFAULT_MCP_OPTIONS],
93
+ [options],
94
+ );
84
95
 
85
96
  return {
86
97
  copiedOption,
@@ -0,0 +1,51 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ import type { RefObject } from 'react';
4
+ import type { ConsentCategory, ConsentCategoryId, UseConsentResult } from '../types/consent';
5
+
6
+ import { CONSENT_CATEGORIES } from '../constants/consent';
7
+ import { useFocusTrap } from './use-focus-trap';
8
+
9
+ export type ConsentDraft = Record<Exclude<ConsentCategoryId, 'necessary'>, boolean>;
10
+
11
+ export type UseConsentDialogResult = {
12
+ draft: ConsentDraft;
13
+ setCategory: (id: keyof ConsentDraft, value: boolean) => void;
14
+ dialogRef: RefObject<HTMLDivElement | null>;
15
+ close: () => void;
16
+ save: () => void;
17
+ categories: readonly ConsentCategory[];
18
+ };
19
+
20
+ export function useConsentDialog(consent: UseConsentResult): UseConsentDialogResult {
21
+ const [draft, setDraft] = useState<ConsentDraft>(() => ({
22
+ analytics: consent.state?.analytics ?? consent.mode === 'opt-out',
23
+ marketing: (consent.state?.marketing ?? consent.mode === 'opt-out') && !consent.gpc,
24
+ }));
25
+ const dialogRef = useRef<HTMLDivElement | null>(null);
26
+ const close = consent.closeDialog;
27
+ const closeRef = useRef(close);
28
+ closeRef.current = close;
29
+
30
+ useEffect(() => {
31
+ const previous = document.activeElement as HTMLElement | null;
32
+ const onKeyDown = (event: KeyboardEvent) => {
33
+ if (event.key === 'Escape') closeRef.current();
34
+ };
35
+ document.addEventListener('keydown', onKeyDown);
36
+ return () => {
37
+ document.removeEventListener('keydown', onKeyDown);
38
+ previous?.focus?.();
39
+ };
40
+ }, []);
41
+
42
+ useFocusTrap(dialogRef);
43
+
44
+ const setCategory = (id: keyof ConsentDraft, value: boolean) => {
45
+ setDraft((current) => ({ ...current, [id]: value }));
46
+ };
47
+
48
+ const save = () => consent.save(draft);
49
+
50
+ return { draft, setCategory, dialogRef, close, save, categories: CONSENT_CATEGORIES };
51
+ }
@@ -0,0 +1,56 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import type { ConsentRuntime } from '../types/consent';
4
+
5
+ import { useThemeHooks } from './use-theme-hooks';
6
+ import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
7
+
8
+ type RealmOwnedRuntime = ConsentRuntime & {
9
+ mode: 'opt-in' | 'opt-out';
10
+ configuredMode: 'auto' | 'opt-in' | 'opt-out';
11
+ region: { source: 'timezone' | 'config' | 'none'; timeZone: string | null };
12
+ };
13
+
14
+ function isRealmOwned(runtime: ConsentRuntime): runtime is RealmOwnedRuntime {
15
+ return runtime.region.source !== 'provider';
16
+ }
17
+
18
+ function getConsentRuntime(): ConsentRuntime | undefined {
19
+ if (typeof window === 'undefined') return undefined;
20
+ return (window as Window & { redocly?: { consent?: ConsentRuntime } }).redocly?.consent;
21
+ }
22
+
23
+ // Sends one consent.changed event per visitor choice in the modes Realm owns.
24
+ export function useConsentTelemetry(): void {
25
+ const { useTelemetry } = useThemeHooks();
26
+ const telemetry = useTelemetry();
27
+ const telemetryRef = useRef(telemetry);
28
+ telemetryRef.current = telemetry;
29
+
30
+ useEffect(() => {
31
+ const found = getConsentRuntime();
32
+ const runtime = found && isRealmOwned(found) ? found : null;
33
+ if (!runtime) return;
34
+ return runtime.on('change', (state) => {
35
+ const choice =
36
+ state.analytics && state.marketing
37
+ ? 'accept'
38
+ : !state.analytics && !state.marketing
39
+ ? 'reject'
40
+ : 'partial';
41
+ const { origin, pathname, search, hash } = window.location;
42
+ const uri = `${origin}${pathname}${search}${hash}`;
43
+ telemetryRef.current.sendConsentChangedMessage([
44
+ {
45
+ ...getBaseDataAttributes('consent', 'consent'),
46
+ choice,
47
+ mode: runtime.mode,
48
+ configuredMode: runtime.configuredMode,
49
+ regionSource: runtime.region.source,
50
+ gpc: runtime.gpc,
51
+ },
52
+ getBaseDataAttributes(uri, 'page', uri),
53
+ ]);
54
+ });
55
+ }, []);
56
+ }
@@ -21,12 +21,18 @@ export function useDialogHotKeys(ref: RefObject<HTMLElement | null>, onClose: ()
21
21
  };
22
22
 
23
23
  const onFocusableElementsHandler = () => {
24
- const focusableElements = ref.current?.querySelectorAll(
25
- 'input, [href], [tabindex]:not([tabindex="-1"])',
24
+ const focusableElements = Array.from(
25
+ ref.current?.querySelectorAll<HTMLElement>(
26
+ 'input, textarea, select, button, [href], [tabindex]:not([tabindex="-1"])',
27
+ ) ?? [],
28
+ ).filter(
29
+ (element) =>
30
+ !element.hasAttribute('disabled') &&
31
+ (typeof element.checkVisibility !== 'function' || element.checkVisibility()),
26
32
  );
27
- if (focusableElements && focusableElements.length > 0) {
28
- firstFocusableRef.current = focusableElements[0] as HTMLElement;
29
- lastFocusableRef.current = focusableElements[focusableElements.length - 1] as HTMLElement;
33
+ if (focusableElements.length > 0) {
34
+ firstFocusableRef.current = focusableElements[0];
35
+ lastFocusableRef.current = focusableElements[focusableElements.length - 1];
30
36
  }
31
37
  };
32
38
 
@@ -3,9 +3,9 @@ import throttle from 'lodash.throttle';
3
3
 
4
4
  import type { RefObject } from 'react';
5
5
 
6
- export function useFullHeight(element: RefObject<HTMLElement | null>): void {
6
+ export function useFullHeight(elementRef: RefObject<HTMLElement | null>): void {
7
7
  const handler = useCallback(() => {
8
- if (!element.current) {
8
+ if (!elementRef.current) {
9
9
  return;
10
10
  }
11
11
 
@@ -14,19 +14,19 @@ export function useFullHeight(element: RefObject<HTMLElement | null>): void {
14
14
  return;
15
15
  }
16
16
 
17
- const elementTop = element.current.getBoundingClientRect().top;
17
+ const elementTop = elementRef.current.getBoundingClientRect().top;
18
18
  const footerOffset = footer.offsetTop - window.scrollY - elementTop;
19
19
  if (footerOffset > window.innerHeight) {
20
- element.current.style.maxHeight = `${window.innerHeight - elementTop}px`;
20
+ elementRef.current.style.maxHeight = `${window.innerHeight - elementTop}px`;
21
21
  } else {
22
- element.current.style.maxHeight = `${footerOffset}px`;
22
+ elementRef.current.style.maxHeight = `${footerOffset}px`;
23
23
  }
24
- }, [element]);
24
+ }, [elementRef]);
25
25
 
26
26
  const throttledHandler = useMemo(() => throttle(handler, 150), [handler]);
27
27
 
28
28
  useEffect(() => {
29
- if (typeof window === 'undefined' || !element.current) {
29
+ if (typeof window === 'undefined' || !elementRef.current) {
30
30
  return () => {
31
31
  // nope
32
32
  };
@@ -45,5 +45,5 @@ export function useFullHeight(element: RefObject<HTMLElement | null>): void {
45
45
  window.removeEventListener('scroll', throttledHandler);
46
46
  window.removeEventListener('resize', throttledHandler);
47
47
  };
48
- }, [throttledHandler, element, handler]);
48
+ }, [throttledHandler, elementRef, handler]);
49
49
  }
@@ -23,6 +23,7 @@ import {
23
23
  withoutPathPrefix,
24
24
  } from '../utils/urls';
25
25
  import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
26
+ import { getAiPromptLink } from '../utils/get-ai-prompt-link';
26
27
 
27
28
  const DEFAULT_ENABLED_ACTIONS = [
28
29
  'copy',
@@ -31,6 +32,7 @@ const DEFAULT_ENABLED_ACTIONS = [
31
32
  'claude',
32
33
  'docs-mcp-cursor',
33
34
  'docs-mcp-vscode',
35
+ 'docs-mcp-claude',
34
36
  ] as const;
35
37
 
36
38
  export type PageActionType =
@@ -40,6 +42,7 @@ export type PageActionType =
40
42
  | 'claude'
41
43
  | 'docs-mcp-cursor'
42
44
  | 'docs-mcp-vscode'
45
+ | 'docs-mcp-claude'
43
46
  // Used in OpenAPI docs x-mcp
44
47
  | 'mcp-cursor'
45
48
  | 'mcp-vscode';
@@ -110,10 +113,12 @@ export function usePageActions(
110
113
  const mdPageUrl = isRoot
111
114
  ? combineUrls(origin, pathname, 'index.html.md')
112
115
  : combineUrls(origin, removeTrailingSlash(pathname) + '.md');
116
+ const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
113
117
 
114
118
  const actionHandlers: Record<PageActionType, () => PageAction> = {
115
119
  'docs-mcp-cursor': createMCPHandler('cursor', false),
116
120
  'docs-mcp-vscode': createMCPHandler('vscode', false),
121
+ 'docs-mcp-claude': createMCPHandler('claude', false),
117
122
  'mcp-cursor': createMCPHandler('cursor', true),
118
123
  'mcp-vscode': createMCPHandler('vscode', true),
119
124
 
@@ -159,7 +164,7 @@ export function usePageActions(
159
164
  }),
160
165
 
161
166
  chatgpt: () => {
162
- const link = getExternalAiPromptLink('https://chat.openai.com', mdPageUrl);
167
+ const link = getAiPromptLink('chatgpt', externalAiPrompt);
163
168
  return {
164
169
  buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
165
170
  title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
@@ -179,7 +184,7 @@ export function usePageActions(
179
184
  },
180
185
 
181
186
  claude: () => {
182
- const link = getExternalAiPromptLink('https://claude.ai/new', mdPageUrl);
187
+ const link = getAiPromptLink('claude', externalAiPrompt);
183
188
  return {
184
189
  buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
185
190
  title: translate('page.actions.claudeTitle', 'Open in Claude'),
@@ -220,13 +225,6 @@ export function usePageActions(
220
225
  return result;
221
226
  }
222
227
 
223
- function getExternalAiPromptLink(baseUrl: string, mdPageUrl: string): string {
224
- const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
225
- const url = new URL(baseUrl);
226
- url.searchParams.set('q', externalAiPrompt);
227
- return url.toString();
228
- }
229
-
230
228
  type CreateMCPActionParams = {
231
229
  clientType: MCPClientType;
232
230
  mcpConfig: McpConnectionParams;
@@ -261,6 +259,19 @@ function createMCPAction({
261
259
  };
262
260
  }
263
261
 
262
+ if (clientType === 'claude') {
263
+ return {
264
+ ...sharedProps,
265
+ buttonText: translate('page.actions.connectMcp.claude', 'Connect to Claude'),
266
+ title: translate('page.actions.connectMcp.claude', 'Connect to Claude'),
267
+ description: translate(
268
+ 'page.actions.connectMcp.claudeDescription',
269
+ 'Add MCP server to your Claude account',
270
+ ),
271
+ iconComponent: ClaudeIcon,
272
+ };
273
+ }
274
+
264
275
  return {
265
276
  ...sharedProps,
266
277
  buttonText: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'),
@@ -337,6 +348,7 @@ function shouldHideActionByType(
337
348
  return !isPublic;
338
349
  case 'docs-mcp-cursor':
339
350
  case 'docs-mcp-vscode':
351
+ case 'docs-mcp-claude':
340
352
  return Boolean(mcpUrl) || isMcpDisabled;
341
353
  case 'mcp-cursor':
342
354
  case 'mcp-vscode':
@@ -0,0 +1,46 @@
1
+ import { useCallback, useRef } from 'react';
2
+
3
+ import type { PointerEvent, RefObject } from 'react';
4
+
5
+ const EDGE_PROXIMITY_DISTANCE = 80;
6
+
7
+ export type PointerBorderHighlight<T extends HTMLElement> = {
8
+ ref: RefObject<T | null>;
9
+ onPointerMove: (event: PointerEvent<T>) => void;
10
+ onPointerLeave: () => void;
11
+ };
12
+
13
+ /* Writes `--pointer-x`, `--pointer-y` and `--pointer-edge-proximity` (1 at the border, 0 at
14
+ `distance` px inside) to the element's inline style, so the highlight moves without re-renders. */
15
+ export function usePointerBorderHighlight<T extends HTMLElement>(
16
+ distance = EDGE_PROXIMITY_DISTANCE,
17
+ ): PointerBorderHighlight<T> {
18
+ const ref = useRef<T | null>(null);
19
+
20
+ const onPointerMove = useCallback(
21
+ (event: PointerEvent<T>) => {
22
+ const element = ref.current;
23
+ if (!element) return;
24
+
25
+ const rect = element.getBoundingClientRect();
26
+ const x = event.clientX - rect.left;
27
+ const y = event.clientY - rect.top;
28
+ const edgeDistance = Math.min(x, y, rect.width - x, rect.height - y);
29
+ const proximity = Math.min(1, Math.max(0, 1 - edgeDistance / distance));
30
+
31
+ element.style.setProperty('--pointer-x', `${x}px`);
32
+ element.style.setProperty('--pointer-y', `${y}px`);
33
+ element.style.setProperty(
34
+ '--pointer-edge-proximity',
35
+ String(Math.round(proximity * 100) / 100),
36
+ );
37
+ },
38
+ [distance],
39
+ );
40
+
41
+ const onPointerLeave = useCallback(() => {
42
+ ref.current?.style.setProperty('--pointer-edge-proximity', '0');
43
+ }, []);
44
+
45
+ return { ref, onPointerMove, onPointerLeave };
46
+ }
@@ -0,0 +1,18 @@
1
+ import { useEffect } from 'react';
2
+
3
+ import type { RefObject } from 'react';
4
+
5
+ export function useScrollItemIntoView(
6
+ listRef: RefObject<HTMLElement | null>,
7
+ activeIndex: number,
8
+ ): void {
9
+ useEffect(() => {
10
+ const list = listRef.current;
11
+ const activeItem = activeIndex === -1 ? undefined : list?.children[activeIndex];
12
+ if (!list || !activeItem) return;
13
+
14
+ const listRect = list.getBoundingClientRect();
15
+ const itemRect = activeItem.getBoundingClientRect();
16
+ list.scrollLeft += itemRect.left - listRect.left - (listRect.width - itemRect.width) / 2;
17
+ }, [listRef, activeIndex]);
18
+ }
@@ -0,0 +1,63 @@
1
+ import { useCallback, useId, useRef } from 'react';
2
+
3
+ import type { KeyboardEvent } from 'react';
4
+
5
+ type UseTabListProps = {
6
+ count: number;
7
+ activeIndex: number;
8
+ onChange: (index: number) => void;
9
+ };
10
+
11
+ /**
12
+ * Props for a WAI-ARIA tab list with automatic activation: only the active tab is a tab stop,
13
+ * and Left/Right/Home/End select and focus another tab.
14
+ */
15
+ export function useTabList({ count, activeIndex, onChange }: UseTabListProps) {
16
+ const idPrefix = useId();
17
+ const focusableIndex = activeIndex >= 0 ? activeIndex : 0;
18
+ const tabRefs = useRef<(HTMLElement | null)[]>([]);
19
+
20
+ const select = useCallback(
21
+ (index: number) => {
22
+ onChange(index);
23
+ tabRefs.current[index]?.focus();
24
+ },
25
+ [onChange],
26
+ );
27
+
28
+ const onKeyDown = useCallback(
29
+ (event: KeyboardEvent, index: number) => {
30
+ const target = (<Record<string, number>>{
31
+ ArrowRight: (index + 1) % count,
32
+ ArrowLeft: (index - 1 + count) % count,
33
+ Home: 0,
34
+ End: count - 1,
35
+ })[event.key];
36
+ if (target === undefined) return;
37
+ event.preventDefault();
38
+ select(target);
39
+ },
40
+ [count, select],
41
+ );
42
+
43
+ const getTabProps = (index: number) => ({
44
+ id: `${idPrefix}-tab-${index}`,
45
+ role: 'tab',
46
+ 'aria-selected': index === activeIndex,
47
+ 'aria-controls': `${idPrefix}-panel-${index}`,
48
+ tabIndex: index === focusableIndex ? 0 : -1,
49
+ ref: (element: HTMLElement | null) => {
50
+ tabRefs.current[index] = element;
51
+ },
52
+ onClick: () => select(index),
53
+ onKeyDown: (event: KeyboardEvent) => onKeyDown(event, index),
54
+ });
55
+
56
+ const getTabPanelProps = (index: number) => ({
57
+ id: `${idPrefix}-panel-${index}`,
58
+ role: 'tabpanel',
59
+ 'aria-labelledby': `${idPrefix}-tab-${index}`,
60
+ });
61
+
62
+ return { tabListProps: { role: 'tablist' }, getTabProps, getTabPanelProps };
63
+ }
@@ -38,6 +38,8 @@ export const useTelemetryFallback = () => ({
38
38
  sendCodeSnippetReportedMessage: () => {},
39
39
  sendConnectMcpClickedMessage: () => {},
40
40
  sendMcpConnectionSnippetCopiedMessage: () => {},
41
+ sendPromptTagCopiedMessage: () => {},
42
+ sendPromptTagOpenedMessage: () => {},
41
43
  sendMobileMenuClosedMessage: () => {},
42
44
  sendMobileMenuOpenedMessage: () => {},
43
45
  sendPageViewedMessage: () => {},