@redocly/theme 0.70.0-next.6 → 0.70.0-next.8

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 (87) hide show
  1. package/lib/components/Buttons/ConnectMCPButton.js +11 -1
  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 +2 -2
  5. package/lib/components/JsonViewer/Helpers.js +3 -3
  6. package/lib/components/MCP/McpConnectionTabs.js +9 -2
  7. package/lib/components/Portal/Portal.js +37 -5
  8. package/lib/components/Search/SearchAiActionButtons.js +8 -3
  9. package/lib/components/Search/SearchDialog.js +10 -0
  10. package/lib/components/Search/SearchItem.js +1 -1
  11. package/lib/components/Search/SearchTrigger.js +33 -8
  12. package/lib/components/Search/variables.js +2 -0
  13. package/lib/core/constants/mcp.d.ts +2 -2
  14. package/lib/core/constants/mcp.js +1 -1
  15. package/lib/core/contexts/PortalContainerContext.d.ts +2 -0
  16. package/lib/core/contexts/PortalContainerContext.js +7 -0
  17. package/lib/core/hooks/code-walkthrough/use-code-panel.d.ts +1 -1
  18. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.d.ts +1 -1
  19. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +1 -1
  20. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +1 -1
  21. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +1 -2
  22. package/lib/core/hooks/code-walkthrough/use-renderable-files.d.ts +1 -1
  23. package/lib/core/hooks/index.d.ts +1 -0
  24. package/lib/core/hooks/index.js +1 -0
  25. package/lib/core/hooks/search/use-persisted-quick-filter.d.ts +21 -0
  26. package/lib/core/hooks/search/use-persisted-quick-filter.js +92 -0
  27. package/lib/core/hooks/use-connect-mcp-button.js +1 -0
  28. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  29. package/lib/core/hooks/use-page-actions.js +6 -0
  30. package/lib/core/styles/global.js +1 -0
  31. package/lib/core/types/code-walkthrough.d.ts +84 -0
  32. package/lib/core/types/hooks.d.ts +1 -1
  33. package/lib/core/types/l10n.d.ts +1 -1
  34. package/lib/core/types/mcp.d.ts +1 -1
  35. package/lib/core/utils/download-code-walkthrough.d.ts +1 -1
  36. package/lib/core/utils/get-code-walkthrough-file-text.d.ts +1 -1
  37. package/lib/core/utils/match-code-walkthrough-conditions.d.ts +1 -1
  38. package/lib/core/utils/mcp.js +7 -2
  39. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.d.ts +1 -1
  40. package/lib/markdoc/components/CodeWalkthrough/CodePanel.d.ts +1 -1
  41. package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.d.ts +1 -1
  42. package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.d.ts +1 -1
  43. package/lib/markdoc/components/CodeWalkthrough/CodeStep.d.ts +1 -1
  44. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.d.ts +1 -1
  45. package/lib/markdoc/components/Tabs/TabList.js +8 -6
  46. package/lib/markdoc/tags/code-walkthrough.d.ts +1 -1
  47. package/package.json +4 -3
  48. package/src/components/Buttons/ConnectMCPButton.tsx +13 -2
  49. package/src/components/Buttons/variables.ts +4 -0
  50. package/src/components/Catalog/Catalog.tsx +5 -3
  51. package/src/components/JsonViewer/Helpers.tsx +5 -3
  52. package/src/components/MCP/McpConnectionTabs.tsx +22 -5
  53. package/src/components/Portal/Portal.tsx +4 -2
  54. package/src/components/Search/SearchAiActionButtons.tsx +31 -19
  55. package/src/components/Search/SearchDialog.tsx +11 -0
  56. package/src/components/Search/SearchItem.tsx +3 -1
  57. package/src/components/Search/SearchTrigger.tsx +42 -7
  58. package/src/components/Search/variables.ts +2 -0
  59. package/src/core/constants/mcp.ts +1 -1
  60. package/src/core/contexts/PortalContainerContext.tsx +4 -0
  61. package/src/core/hooks/__mocks__/index.ts +1 -0
  62. package/src/core/hooks/code-walkthrough/use-code-panel.ts +1 -1
  63. package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +2 -2
  64. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +1 -1
  65. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +3 -3
  66. package/src/core/hooks/code-walkthrough/use-renderable-files.ts +1 -1
  67. package/src/core/hooks/index.ts +1 -0
  68. package/src/core/hooks/search/use-persisted-quick-filter.ts +125 -0
  69. package/src/core/hooks/use-connect-mcp-button.ts +7 -1
  70. package/src/core/hooks/use-page-actions.ts +17 -0
  71. package/src/core/styles/global.ts +2 -1
  72. package/src/core/types/code-walkthrough.ts +103 -0
  73. package/src/core/types/hooks.ts +1 -0
  74. package/src/core/types/l10n.ts +6 -0
  75. package/src/core/types/mcp.ts +1 -1
  76. package/src/core/utils/download-code-walkthrough.ts +1 -1
  77. package/src/core/utils/get-code-walkthrough-file-text.ts +1 -1
  78. package/src/core/utils/match-code-walkthrough-conditions.ts +1 -1
  79. package/src/core/utils/mcp.ts +8 -2
  80. package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +1 -1
  81. package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +1 -1
  82. package/src/markdoc/components/CodeWalkthrough/CodePanelHeader.tsx +1 -1
  83. package/src/markdoc/components/CodeWalkthrough/CodePanelToolbar.tsx +1 -1
  84. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +1 -1
  85. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +1 -1
  86. package/src/markdoc/components/Tabs/TabList.tsx +9 -12
  87. package/src/markdoc/tags/code-walkthrough.ts +1 -1
@@ -34,6 +34,13 @@ const MENU_OPTIONS = {
34
34
  descriptionTranslationKey: 'page.actions.connectMcp.vscodeDescription',
35
35
  descriptionDefault: 'Install MCP server on VS Code',
36
36
  },
37
+ claude: {
38
+ icon: ClaudeIcon_1.ClaudeIcon,
39
+ titleTranslationKey: 'page.actions.connectMcp.claude',
40
+ titleDefault: 'Connect to Claude',
41
+ descriptionTranslationKey: 'page.actions.connectMcp.claudeDescription',
42
+ descriptionDefault: 'Add MCP server to your Claude account',
43
+ },
37
44
  'claude-code': {
38
45
  icon: ClaudeIcon_1.ClaudeIcon,
39
46
  titleTranslationKey: 'page.actions.connectMcp.claudeCode',
@@ -70,7 +77,7 @@ function ConnectMCPButton({ placement = 'bottom', alignment = 'end', options = c
70
77
  const PrimaryIcon = primaryOption.icon;
71
78
  const showPrimaryCopied = copiedOption === primaryOption.key;
72
79
  return (react_1.default.createElement(ConnectMCPButtonWrapper, { "data-component-name": "Buttons/ConnectMCPButton" }, react_1.default.createElement(SplitButton_1.SplitButton, { variant: "secondary", placement: placement, alignment: alignment, closeOnSelect: false, toggleAriaLabel: translate('page.actions.connectMcp.moreOptions', 'More options'), button: react_1.default.createElement(Button_1.Button, { variant: "secondary", icon: showPrimaryCopied ? (react_1.default.createElement(CheckmarkFilledIcon_1.CheckmarkFilledIcon, { color: "var(--color-success-base)" })) : (react_1.default.createElement(PrimaryIcon, null)), onClick: () => handleAction(primaryOption.key) }, translate(primaryOption.titleTranslationKey, primaryOption.titleDefault)) }, dropdownOptions.length > 0
73
- ? ({ close }) => (react_1.default.createElement(DropdownMenu_1.DropdownMenu, null, dropdownOptions.map((option) => {
80
+ ? ({ close }) => (react_1.default.createElement(StyledDropdownMenu, null, dropdownOptions.map((option) => {
74
81
  const Icon = option.icon;
75
82
  const showCheckmark = copiedOption === option.key;
76
83
  return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: option.key, onAction: () => {
@@ -82,6 +89,9 @@ function ConnectMCPButton({ placement = 'bottom', alignment = 'end', options = c
82
89
  })))
83
90
  : null)));
84
91
  }
92
+ const StyledDropdownMenu = (0, styled_components_1.default)(DropdownMenu_1.DropdownMenu) `
93
+ --dropdown-menu-max-height: var(--connect-mcp-button-dropdown-max-height);
94
+ `;
85
95
  const ConnectMCPButtonWrapper = styled_components_1.default.div `
86
96
  display: inline-block;
87
97
  position: relative;
@@ -1 +1,2 @@
1
1
  export declare const aiAssistantButton: import("styled-components").RuleSet<object>;
2
+ export declare const connectMcpButton: import("styled-components").RuleSet<object>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.aiAssistantButton = void 0;
3
+ exports.connectMcpButton = exports.aiAssistantButton = void 0;
4
4
  const styled_components_1 = require("styled-components");
5
5
  /* eslint-disable theme/no-raw-colors-in-styles */
6
6
  exports.aiAssistantButton = (0, styled_components_1.css) `
@@ -52,4 +52,7 @@ exports.aiAssistantButton = (0, styled_components_1.css) `
52
52
  // @tokens End
53
53
  `;
54
54
  /* eslint-enable theme/no-raw-colors-in-styles */
55
+ exports.connectMcpButton = (0, styled_components_1.css) `
56
+ --connect-mcp-button-dropdown-max-height: fit-content;
57
+ `;
55
58
  //# sourceMappingURL=variables.js.map
@@ -94,9 +94,9 @@ function Catalog(props) {
94
94
  react_1.default.createElement(CatalogPageContentWrapper, null,
95
95
  react_1.default.createElement(CatalogTitleWrapper, null,
96
96
  react_1.default.createElement(CatalogTitle, { "data-translation-key": catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.titleTranslationKey },
97
- (catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.titleTranslationKey)
97
+ react_1.default.createElement("span", null, (catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.titleTranslationKey)
98
98
  ? translate(catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.titleTranslationKey)
99
- : catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.slug,
99
+ : catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.slug),
100
100
  ' '),
101
101
  react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, entitiesCounter)),
102
102
  react_1.default.createElement(CatalogDescription, { "data-translation-key": catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.descriptionTranslationKey }, translate(catalogConfig === null || catalogConfig === void 0 ? void 0 : catalogConfig.descriptionTranslationKey)),
@@ -43,10 +43,10 @@ function JsonValue(props) {
43
43
  const valueElement = Array.isArray(value)
44
44
  ? value.map((item, index) => (react_1.default.createElement(react_1.default.Fragment, { key: index },
45
45
  react_1.default.createElement(JsonValue, { value: item, level: level + 1, path: `${path}/${index}`, standalone: true, fold: fold, maxStringLength: maxStringLength }),
46
- index < value.length - 1 ? ',\n' : null)))
46
+ index < value.length - 1 ? react_1.default.createElement("span", null, ',\n') : null)))
47
47
  : keys.map((key, index) => (react_1.default.createElement(react_1.default.Fragment, { key: key },
48
48
  react_1.default.createElement(JsonKeyValue, { name: key, value: value[key], level: level + 1, path: `${path}/${key}`, fold: fold, maxStringLength: maxStringLength }),
49
- index < keys.length - 1 ? ',\n' : null)));
49
+ index < keys.length - 1 ? react_1.default.createElement("span", null, ',\n') : null)));
50
50
  const openingPunctuationElement = standalone ? (react_1.default.createElement(react_1.default.Fragment, null,
51
51
  indent,
52
52
  level > 0 ? (react_1.default.createElement(ExpandIcon, { onClick: () => fold.setExpanded(path, !isExpanded), isExpanded: isExpanded })) : null,
@@ -79,7 +79,7 @@ function JsonKeyValue(props) {
79
79
  indent,
80
80
  isFoldable(value) ? (react_1.default.createElement(ExpandIcon, { onClick: () => fold.setExpanded(path, !isExpanded), isExpanded: isExpanded })) : null,
81
81
  react_1.default.createElement("span", { className: "property token string" }, `"${name}"`),
82
- ": ",
82
+ react_1.default.createElement("span", null, ': '),
83
83
  valueElement));
84
84
  }
85
85
  function TruncatedStringValue({ value, maxStringLength, }) {
@@ -41,6 +41,7 @@ const react_1 = __importStar(require("react"));
41
41
  const styled_components_1 = __importDefault(require("styled-components"));
42
42
  const hooks_1 = require("../../core/hooks");
43
43
  const utils_1 = require("../../core/utils");
44
+ const Button_1 = require("../../components/Button/Button");
44
45
  const CodeBlock_1 = require("../../components/CodeBlock/CodeBlock");
45
46
  const ClaudeIcon_1 = require("../../icons/ClaudeIcon/ClaudeIcon");
46
47
  const ChatGptIcon_1 = require("../../icons/ChatGptIcon/ChatGptIcon");
@@ -111,9 +112,11 @@ function renderTabContent(option, connection, translate, snippetHeader) {
111
112
  react_1.default.createElement(CodeBlock_1.CodeBlock, { lang: "shell", source: (0, utils_1.generateClaudeCodeCommand)(connection), header: snippetHeader(option, 'claude-code'), wrapContents: true }))),
112
113
  },
113
114
  {
114
- headline: translate('page.mcpNotAllowed.snippets.claudeDesktop.headline', 'Claude Desktop'),
115
+ headline: translate('page.mcpNotAllowed.snippets.claudeDesktop.headline', 'Claude app'),
115
116
  content: (react_1.default.createElement(react_1.default.Fragment, null,
116
- react_1.default.createElement(TabHint, null, translate('page.mcpNotAllowed.snippets.claudeDesktop.instruction', 'Open Customize → Connectors → Add custom connector and paste this server URL:')),
117
+ react_1.default.createElement(TabHint, null, translate('page.mcpNotAllowed.snippets.claudeDesktop.accountHint', 'Claude adds the connector to your account and syncs it between Claude on the web and Claude Desktop.')),
118
+ react_1.default.createElement(ConnectButton, { variant: "secondary", icon: react_1.default.createElement(ClaudeIcon_1.ClaudeIcon, null), onClick: () => window.open((0, utils_1.generateMCPDeepLink)('claude', connection), '_blank') }, translate('page.actions.connectMcp.claude', 'Connect to Claude')),
119
+ react_1.default.createElement(TabHint, null, translate('page.mcpNotAllowed.snippets.claudeDesktop.instruction', 'Or open Customize → Connectors → Add custom connector and paste this server URL:')),
117
120
  react_1.default.createElement(CodeBlock_1.CodeBlock, { lang: "text", source: connection.url, header: snippetHeader(option, 'claude-desktop') }))),
118
121
  },
119
122
  ]);
@@ -154,6 +157,10 @@ const McpConnectionTabsWrapper = styled_components_1.default.div `
154
157
  padding-right: var(--page-mcp-405-snippets-code-padding-right);
155
158
  }
156
159
  `;
160
+ const ConnectButton = (0, styled_components_1.default)(Button_1.Button) `
161
+ align-self: flex-start;
162
+ margin-bottom: var(--spacing-sm);
163
+ `;
157
164
  const TabHint = styled_components_1.default.p `
158
165
  margin: 0 0 var(--spacing-xs);
159
166
  color: var(--page-mcp-405-snippets-hint-text-color);
@@ -1,14 +1,46 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
5
35
  Object.defineProperty(exports, "__esModule", { value: true });
6
36
  exports.Portal = Portal;
7
- const react_1 = __importDefault(require("react"));
37
+ const react_1 = __importStar(require("react"));
8
38
  const react_dom_1 = require("react-dom");
9
39
  const utils_1 = require("../../core/utils");
40
+ const PortalContainerContext_1 = require("../../core/contexts/PortalContainerContext");
10
41
  function Portal({ children, mountId = 'root', }) {
11
- const mount = utils_1.IS_BROWSER ? document.getElementById(mountId) || document.body : null;
42
+ const container = (0, react_1.useContext)(PortalContainerContext_1.PortalContainerContext);
43
+ const mount = utils_1.IS_BROWSER ? container || document.getElementById(mountId) || document.body : null;
12
44
  if (!mount) {
13
45
  return react_1.default.createElement(react_1.default.Fragment, null, children);
14
46
  }
@@ -13,11 +13,16 @@ const ThumbUpFilledIcon_1 = require("../../icons/ThumbUpFilledIcon/ThumbUpFilled
13
13
  const ThumbDownIcon_1 = require("../../icons/ThumbDownIcon/ThumbDownIcon");
14
14
  const ThumbDownFilledIcon_1 = require("../../icons/ThumbDownFilledIcon/ThumbDownFilledIcon");
15
15
  const CopyButton_1 = require("../../components/Buttons/CopyButton");
16
+ const Tooltip_1 = require("../../components/Tooltip/Tooltip");
17
+ const hooks_1 = require("../../core/hooks");
16
18
  function SearchAiActionButtons({ content, className, feedback, onFeedback, disabled, }) {
19
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
20
+ const { translate } = useTranslate();
17
21
  return (react_1.default.createElement(ActionButtonsWrapper, { className: className, "data-component-name": "Search/SearchAiActionButtons" },
18
- react_1.default.createElement(CopyButton_1.CopyButton, { data: content }),
19
- react_1.default.createElement(FeedbackButton, { variant: "text", size: "small", icon: feedback === types_1.FeedbackType.Like ? react_1.default.createElement(ThumbUpFilledIcon_1.ThumbUpFilledIcon, null) : react_1.default.createElement(ThumbUpIcon_1.ThumbUpIcon, null), onClick: () => !disabled && onFeedback(types_1.FeedbackType.Like), extraClass: feedback === types_1.FeedbackType.Like ? 'active' : '', "aria-label": "Like this response", disabled: disabled }),
20
- react_1.default.createElement(FeedbackButton, { variant: "text", size: "small", icon: feedback === types_1.FeedbackType.Dislike ? react_1.default.createElement(ThumbDownFilledIcon_1.ThumbDownFilledIcon, null) : react_1.default.createElement(ThumbDownIcon_1.ThumbDownIcon, null), onClick: () => !disabled && onFeedback(types_1.FeedbackType.Dislike), extraClass: feedback === types_1.FeedbackType.Dislike ? 'active' : '', "aria-label": "Dislike this response", disabled: disabled })));
22
+ react_1.default.createElement(Tooltip_1.Tooltip, { tip: translate('search.ai.actions.copy', 'Copy the response'), placement: "top" },
23
+ react_1.default.createElement(CopyButton_1.CopyButton, { data: content })),
24
+ react_1.default.createElement(Tooltip_1.Tooltip, { tip: translate('search.ai.actions.like', 'Like the response'), placement: "top" }, react_1.default.createElement(FeedbackButton, { variant: "text", size: "small", icon: feedback === types_1.FeedbackType.Like ? react_1.default.createElement(ThumbUpFilledIcon_1.ThumbUpFilledIcon, null) : react_1.default.createElement(ThumbUpIcon_1.ThumbUpIcon, null), onClick: () => !disabled && onFeedback(types_1.FeedbackType.Like), extraClass: feedback === types_1.FeedbackType.Like ? 'active' : '', "aria-label": "Like this response", disabled: disabled })),
25
+ react_1.default.createElement(Tooltip_1.Tooltip, { tip: translate('search.ai.actions.dislike', 'Dislike the response'), placement: "top" }, react_1.default.createElement(FeedbackButton, { variant: "text", size: "small", icon: feedback === types_1.FeedbackType.Dislike ? react_1.default.createElement(ThumbDownFilledIcon_1.ThumbDownFilledIcon, null) : react_1.default.createElement(ThumbDownIcon_1.ThumbDownIcon, null), onClick: () => !disabled && onFeedback(types_1.FeedbackType.Dislike), extraClass: feedback === types_1.FeedbackType.Dislike ? 'active' : '', "aria-label": "Dislike this response", disabled: disabled }))));
21
26
  }
22
27
  const ActionButtonsWrapper = styled_components_1.default.div `
23
28
  display: flex;
@@ -76,6 +76,16 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
76
76
  const autoSearchDisabled = mode !== 'search';
77
77
  const { query, setQuery, filter, setFilter, items, isSearchLoading, facets, groupCounts, setLoadMore, advancedSearch, askAi, groupField, searchError, retrySearch, } = useSearch(product === null || product === void 0 ? void 0 : product.name, autoSearchDisabled);
78
78
  const { isFilterOpen, onFilterToggle, onFilterChange, onFilterReset, onFacetReset, onQuickFilterReset, } = (0, hooks_1.useSearchFilter)(filter, setFilter);
79
+ (0, hooks_1.usePersistedQuickFilter)({
80
+ filter,
81
+ setFilter,
82
+ facets,
83
+ groupField,
84
+ query,
85
+ isSearchLoading,
86
+ searchError,
87
+ enabled: initialMode === 'search',
88
+ });
79
89
  const { addSearchHistoryItem } = (0, hooks_1.useRecentSearches)();
80
90
  const aiSearch = useAiSearch({ filter: filter, product: product === null || product === void 0 ? void 0 : product.name });
81
91
  (0, hooks_1.useModalScrollLock)(true);
@@ -76,7 +76,7 @@ function SearchItem({ onClick, item, className, product, innerRef, }) {
76
76
  product.name)),
77
77
  document.httpMethod ? (react_1.default.createElement(SearchItemOperation, null,
78
78
  react_1.default.createElement(SearchItemOperationTag, { color: httpColor }, document.httpMethod.toUpperCase()),
79
- highlight.httpPath ? (0, SearchHighlight_1.searchHighlight)(highlight.httpPath) : document.httpPath)) : null,
79
+ react_1.default.createElement("span", null, highlight.httpPath ? (0, SearchHighlight_1.searchHighlight)(highlight.httpPath) : document.httpPath))) : null,
80
80
  react_1.default.createElement(SearchItemTitleWrapper, null,
81
81
  react_1.default.createElement(SearchItemTitle, null, highlight.title ? (0, SearchHighlight_1.searchHighlight)(highlight.title) : document.title),
82
82
  document.deprecated ? (react_1.default.createElement(SearchItemBadge, { deprecated: true }, translate('search.searchItem.deprecated', 'Deprecated'))) : null,
@@ -32,18 +32,17 @@ 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.SearchTrigger = SearchTrigger;
40
37
  const React = __importStar(require("react"));
41
- const styled_components_1 = __importDefault(require("styled-components"));
38
+ const styled_components_1 = __importStar(require("styled-components"));
42
39
  const hooks_1 = require("../../core/hooks");
43
40
  const Button_1 = require("../../components/Button/Button");
44
41
  const SearchIcon_1 = require("../../icons/SearchIcon/SearchIcon");
45
42
  const utils_1 = require("../../core/utils");
46
43
  const constants_1 = require("../../core/constants");
44
+ const MAC_SHORTCUT_LABEL = '⌘K';
45
+ const SHORTCUT_LABEL = 'Ctrl+K';
47
46
  function SearchTrigger({ onClick, className }) {
48
47
  var _a;
49
48
  var _b;
@@ -61,8 +60,8 @@ function SearchTrigger({ onClick, className }) {
61
60
  }
62
61
  }
63
62
  mainShortcutKey = mainShortcutKey === null || mainShortcutKey === void 0 ? void 0 : mainShortcutKey.toUpperCase();
64
- // A page can be cached and served to every platform, so the server renders a hidden
65
- // placeholder and the browser picks the label after hydration.
63
+ // A page can be cached and served to every platform, so the server renders an empty
64
+ // slot and the browser picks the label after hydration.
66
65
  const [isMac, setIsMac] = React.useState(undefined);
67
66
  React.useEffect(() => setIsMac((0, utils_1.getUserAgent)().includes('Mac')), []);
68
67
  return (React.createElement(SearchTriggerWrapper, { onClick: onClick, className: className, "data-component-name": "Search/SearchTrigger" },
@@ -70,7 +69,7 @@ function SearchTrigger({ onClick, className }) {
70
69
  React.createElement(SearchTriggerInput, { "data-testid": "search-trigger-input", "data-translation-key": "search.navbar.label" },
71
70
  React.createElement(SearchIcon_1.SearchIcon, null),
72
71
  translate('search.navbar.label', 'Search'),
73
- mainShortcutKey === constants_1.DEFAULT_KEYBOARD_SHORTCUTS ? (React.createElement(SearchTriggerShortcut, { $isPending: isMac === undefined }, isMac ? '⌘K' : 'Ctrl+K')) : (React.createElement("span", null, mainShortcutKey)))));
72
+ mainShortcutKey === constants_1.DEFAULT_KEYBOARD_SHORTCUTS ? (React.createElement(SearchTriggerShortcut, null, isMac === undefined ? null : (React.createElement(SearchTriggerShortcutLabel, null, isMac ? MAC_SHORTCUT_LABEL : SHORTCUT_LABEL)))) : (React.createElement("span", null, mainShortcutKey)))));
74
73
  }
75
74
  const SearchTriggerWrapper = styled_components_1.default.div `
76
75
  color: var(--search-trigger-color);
@@ -88,8 +87,34 @@ const SearchTriggerButton = (0, styled_components_1.default)(Button_1.Button) `
88
87
  display: none;
89
88
  }
90
89
  `;
90
+ // The hidden copy of the wider label keeps the slot the same width before and after
91
+ // hydration on every platform, so the trigger does not shift when the label appears.
91
92
  const SearchTriggerShortcut = styled_components_1.default.span `
92
- visibility: ${({ $isPending }) => ($isPending ? 'hidden' : 'visible')};
93
+ display: inline-flex;
94
+ flex-direction: column;
95
+ align-items: flex-end;
96
+
97
+ &::after {
98
+ content: '${SHORTCUT_LABEL}';
99
+ height: 0;
100
+ overflow: hidden;
101
+ visibility: hidden;
102
+ }
103
+ `;
104
+ const shortcutAppear = (0, styled_components_1.keyframes) `
105
+ from {
106
+ opacity: 0;
107
+ filter: blur(2px);
108
+ transform: translateY(3px);
109
+ }
110
+ `;
111
+ const SearchTriggerShortcutLabel = styled_components_1.default.span `
112
+ animation: ${shortcutAppear} var(--search-trigger-shortcut-appear-duration)
113
+ var(--search-trigger-shortcut-appear-easing);
114
+
115
+ @media (prefers-reduced-motion: reduce) {
116
+ animation: none;
117
+ }
93
118
  `;
94
119
  const SearchTriggerInput = styled_components_1.default.div `
95
120
  display: none;
@@ -146,6 +146,8 @@ exports.search = (0, styled_components_1.css) `
146
146
  --search-trigger-bg-color: var(--navbar-bg-color);
147
147
  --search-trigger-icon-size: 16px;
148
148
  --search-trigger-line-height: var(--line-height-base);
149
+ --search-trigger-shortcut-appear-duration: 300ms;
150
+ --search-trigger-shortcut-appear-easing: ease-out;
149
151
 
150
152
  /**
151
153
  * @tokens AI Search
@@ -1,5 +1,5 @@
1
1
  export declare const DEFAULT_MCP_SERVER_NAME = "mcp-server";
2
2
  export declare const MCP_ROUTE = "/mcp";
3
3
  export declare const PUBLIC_MCP_ROUTE = "/mcp-public";
4
- export declare const MCP_OPTIONS: readonly ['cursor', 'vscode', 'claude-code', 'codex', 'copy'];
5
- export declare const DEFAULT_MCP_OPTIONS: ("claude-code" | "codex" | "copy" | "cursor" | "vscode")[];
4
+ export declare const MCP_OPTIONS: readonly ['cursor', 'vscode', 'claude', 'claude-code', 'codex', 'copy'];
5
+ export declare const DEFAULT_MCP_OPTIONS: ("claude" | "claude-code" | "codex" | "copy" | "cursor" | "vscode")[];
@@ -4,6 +4,6 @@ exports.DEFAULT_MCP_OPTIONS = exports.MCP_OPTIONS = exports.PUBLIC_MCP_ROUTE = e
4
4
  exports.DEFAULT_MCP_SERVER_NAME = 'mcp-server';
5
5
  exports.MCP_ROUTE = '/mcp';
6
6
  exports.PUBLIC_MCP_ROUTE = '/mcp-public';
7
- exports.MCP_OPTIONS = ['cursor', 'vscode', 'claude-code', 'codex', 'copy'];
7
+ exports.MCP_OPTIONS = ['cursor', 'vscode', 'claude', 'claude-code', 'codex', 'copy'];
8
8
  exports.DEFAULT_MCP_OPTIONS = [...exports.MCP_OPTIONS];
9
9
  //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,2 @@
1
+ /** Where theme portals mount; set it inside a shadow root (the AI assistant web component), else `document.body`. */
2
+ export declare const PortalContainerContext: import("react").Context<HTMLElement | null>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PortalContainerContext = void 0;
4
+ const react_1 = require("react");
5
+ /** Where theme portals mount; set it inside a shadow root (the AI assistant web component), else `document.body`. */
6
+ exports.PortalContainerContext = (0, react_1.createContext)(null);
7
+ //# sourceMappingURL=PortalContainerContext.js.map
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughFile } from '@redocly/config';
1
+ import type { CodeWalkthroughFile } from '../../../core/types';
2
2
  export declare function useCodePanel(files: CodeWalkthroughFile[]): {
3
3
  readonly activeFile: CodeWalkthroughFile;
4
4
  readonly handleTabSwitch: (name: string) => void;
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughFile, CodeWalkthroughFilter, InputsMarkdocAttr, TogglesMarkdocAttr, CodeWalkthroughFilterItem, CodeWalkthroughConditionsObject } from '@redocly/config';
1
+ import type { CodeWalkthroughFile, CodeWalkthroughFilter, InputsMarkdocAttr, TogglesMarkdocAttr, CodeWalkthroughFilterItem, CodeWalkthroughConditionsObject } from '../../../core/types';
2
2
  export type ActiveFilter = {
3
3
  id: string;
4
4
  label?: string;
@@ -42,7 +42,7 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
42
42
  Object.entries(newState).forEach(([id, control]) => {
43
43
  const existingControl = controlsState[id];
44
44
  if (existingControl && existingControl.type === control.type) {
45
- // @ts-expect-error
45
+ // @ts-expect-error: TS2322 - the spread loses the link between `type` and `value` in the control union
46
46
  newState[id] = Object.assign(Object.assign({}, control), { value: existingControl.value });
47
47
  }
48
48
  });
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughStepAttr } from '@redocly/config';
1
+ import type { CodeWalkthroughStepAttr } from '../../../core/types';
2
2
  import type { WalkthroughStepsState } from '../../types/code-walkthrough';
3
3
  type CodeWalkthroughStep = CodeWalkthroughStepAttr & {
4
4
  compRef?: HTMLElement;
@@ -1,5 +1,4 @@
1
- import type { CodeWalkthroughFile, CodeWalkthroughStepAttr, CodeWalkthroughAttr } from '@redocly/config';
2
- import type { WalkthroughStepsState } from '../../types/code-walkthrough';
1
+ import type { CodeWalkthroughAttr, CodeWalkthroughFile, CodeWalkthroughStepAttr, WalkthroughStepsState } from '../../types/code-walkthrough';
3
2
  import { type WalkthroughControlsState } from './use-code-walkthrough-controls';
4
3
  export type WalkthroughState = {
5
4
  stepsState: WalkthroughStepsState;
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughFile } from '@redocly/config';
1
+ import type { CodeWalkthroughFile } from '../../../core/types';
2
2
  export type RenderableFile = CodeWalkthroughFile & {
3
3
  fileIcon: React.JSX.Element;
4
4
  parentFolder: string;
@@ -31,6 +31,7 @@ export * from './search/use-search-dialog';
31
31
  export * from './search/use-search-filter';
32
32
  export * from './search/use-feedback-tooltip';
33
33
  export * from './search/use-search-ai-activity';
34
+ export * from './search/use-persisted-quick-filter';
34
35
  export * from './use-controlled-state';
35
36
  export * from './use-codeblock-tabs-controls';
36
37
  export * from './code-walkthrough/use-code-walkthrough';
@@ -47,6 +47,7 @@ __exportStar(require("./search/use-search-dialog"), exports);
47
47
  __exportStar(require("./search/use-search-filter"), exports);
48
48
  __exportStar(require("./search/use-feedback-tooltip"), exports);
49
49
  __exportStar(require("./search/use-search-ai-activity"), exports);
50
+ __exportStar(require("./search/use-persisted-quick-filter"), exports);
50
51
  __exportStar(require("./use-controlled-state"), exports);
51
52
  __exportStar(require("./use-codeblock-tabs-controls"), exports);
52
53
  __exportStar(require("./code-walkthrough/use-code-walkthrough"), exports);
@@ -0,0 +1,21 @@
1
+ import type { Dispatch, SetStateAction } from 'react';
2
+ import type { SearchFacet, SearchFilterItem } from '../../types/search';
3
+ type UsePersistedQuickFilterOptions = {
4
+ filter: SearchFilterItem[];
5
+ setFilter: Dispatch<SetStateAction<SearchFilterItem[]>>;
6
+ facets: SearchFacet[];
7
+ groupField: string;
8
+ query: string;
9
+ isSearchLoading: boolean;
10
+ searchError: string | null;
11
+ enabled?: boolean;
12
+ };
13
+ /**
14
+ * Remembers the quick filter on `groupField` for the browser tab and restores it on mount unless
15
+ * one is already selected. Restored values are dropped once known not to exist: absent from the
16
+ * full value list, or from the `groupField` facet left by the first search that finishes without
17
+ * error on an empty query (the restored value is that search's filter, so a value without
18
+ * documents is not counted). Does nothing while `enabled` is false.
19
+ */
20
+ export declare function usePersistedQuickFilter({ filter, setFilter, facets, groupField, query, isSearchLoading, searchError, enabled, }: UsePersistedQuickFilterOptions): void;
21
+ export {};
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePersistedQuickFilter = usePersistedQuickFilter;
4
+ const react_1 = require("react");
5
+ const use_store_1 = require("../use-store");
6
+ const quickFilterStore = (0, use_store_1.createStore)({
7
+ storageKey: 'searchQuickFilter',
8
+ storageType: 'sessionStorage',
9
+ });
10
+ const findQuickFilter = (filter) => filter.find((item) => item.isQuickFilter);
11
+ const withQuickFilter = (filter, quickFilter) => [
12
+ ...filter.filter((item) => !item.isQuickFilter),
13
+ ...(quickFilter.values.length ? [Object.assign(Object.assign({}, quickFilter), { isQuickFilter: true })] : []),
14
+ ];
15
+ const groupFacetValues = (facets, field) => {
16
+ var _a;
17
+ const facet = facets.find((item) => item.field === field);
18
+ const values = (_a = facet === null || facet === void 0 ? void 0 : facet.values) !== null && _a !== void 0 ? _a : [];
19
+ return {
20
+ present: facet !== undefined,
21
+ values: values.map((item) => (typeof item === 'string' ? item : item.value)),
22
+ counted: values.some((item) => typeof item !== 'string' && item.isCounterVisible),
23
+ };
24
+ };
25
+ const isRestorable = (stored, groupField) => !!stored &&
26
+ stored.field === groupField &&
27
+ Array.isArray(stored.values) &&
28
+ stored.values.length > 0 &&
29
+ stored.values.every((value) => typeof value === 'string');
30
+ /**
31
+ * Remembers the quick filter on `groupField` for the browser tab and restores it on mount unless
32
+ * one is already selected. Restored values are dropped once known not to exist: absent from the
33
+ * full value list, or from the `groupField` facet left by the first search that finishes without
34
+ * error on an empty query (the restored value is that search's filter, so a value without
35
+ * documents is not counted). Does nothing while `enabled` is false.
36
+ */
37
+ function usePersistedQuickFilter({ filter, setFilter, facets, groupField, query, isSearchLoading, searchError, enabled = true, }) {
38
+ const [stored, setStored] = (0, use_store_1.useStore)(quickFilterStore, null);
39
+ const [initial] = (0, react_1.useState)(() => enabled && isRestorable(stored, groupField) && !findQuickFilter(filter) ? stored.values : null);
40
+ const phaseRef = (0, react_1.useRef)(initial ? 'restoring' : 'done');
41
+ const searchedRef = (0, react_1.useRef)(false);
42
+ (0, react_1.useEffect)(() => {
43
+ if (!initial)
44
+ return;
45
+ // 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
46
+ setFilter((current) => withQuickFilter(current, { field: groupField, values: initial }));
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- applies only to the initial state
48
+ }, []);
49
+ (0, react_1.useEffect)(() => {
50
+ if (!enabled)
51
+ return;
52
+ const quickFilter = findQuickFilter(filter);
53
+ // The commit that starts the restore still sees the empty initial filter.
54
+ if (phaseRef.current === 'restoring') {
55
+ if (!quickFilter)
56
+ return;
57
+ phaseRef.current = 'validating';
58
+ }
59
+ if (phaseRef.current === 'validating' && initial) {
60
+ if (isSearchLoading)
61
+ searchedRef.current = true;
62
+ const settled = searchedRef.current && !isSearchLoading && !searchError && !query.trim();
63
+ const { present, values: known, counted } = groupFacetValues(facets, groupField);
64
+ if ((known.length && !counted) || (settled && present)) {
65
+ phaseRef.current = 'done';
66
+ const values = quickFilter === null || quickFilter === void 0 ? void 0 : quickFilter.values.filter((value) => known.includes(value) || !initial.includes(value));
67
+ if (quickFilter && values && values.length !== quickFilter.values.length) {
68
+ // oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- the facets arrive after the restore
69
+ setFilter((current) => withQuickFilter(current, { field: groupField, values }));
70
+ return;
71
+ }
72
+ }
73
+ }
74
+ const next = quickFilter ? { field: quickFilter.field, values: quickFilter.values } : null;
75
+ if (JSON.stringify(next) !== JSON.stringify(stored)) {
76
+ setStored(next);
77
+ }
78
+ }, [
79
+ enabled,
80
+ filter,
81
+ facets,
82
+ groupField,
83
+ query,
84
+ isSearchLoading,
85
+ searchError,
86
+ initial,
87
+ stored,
88
+ setFilter,
89
+ setStored,
90
+ ]);
91
+ }
92
+ //# sourceMappingURL=use-persisted-quick-filter.js.map
@@ -41,6 +41,7 @@ function useConnectMCPButton({ options = mcp_1.DEFAULT_MCP_OPTIONS, } = {}) {
41
41
  const urlMap = {
42
42
  cursor: cursorUrl,
43
43
  vscode: vscodeUrl,
44
+ claude: (0, mcp_2.generateMCPDeepLink)('claude', connection),
44
45
  };
45
46
  window.open(urlMap[action], '_blank');
46
47
  }, [cursorUrl, vscodeUrl, serverUrl, serverName, resetCopied, telemetry]);
@@ -1,3 +1,3 @@
1
1
  import type { PageAction } from '../types';
2
- export type PageActionType = 'copy' | 'view' | 'chatgpt' | 'claude' | 'docs-mcp-cursor' | 'docs-mcp-vscode' | 'mcp-cursor' | 'mcp-vscode';
2
+ export type PageActionType = 'copy' | 'view' | 'chatgpt' | 'claude' | 'docs-mcp-cursor' | 'docs-mcp-vscode' | 'docs-mcp-claude' | 'mcp-cursor' | 'mcp-vscode';
3
3
  export declare function usePageActions(pageSlug: string, mcpUrl?: string, actions?: PageActionType[]): PageAction[];
@@ -32,6 +32,7 @@ const DEFAULT_ENABLED_ACTIONS = [
32
32
  'claude',
33
33
  'docs-mcp-cursor',
34
34
  'docs-mcp-vscode',
35
+ 'docs-mcp-claude',
35
36
  ];
36
37
  function usePageActions(pageSlug, mcpUrl, actions) {
37
38
  var _a;
@@ -84,6 +85,7 @@ function usePageActions(pageSlug, mcpUrl, actions) {
84
85
  const actionHandlers = {
85
86
  'docs-mcp-cursor': createMCPHandler('cursor', false),
86
87
  'docs-mcp-vscode': createMCPHandler('vscode', false),
88
+ 'docs-mcp-claude': createMCPHandler('claude', false),
87
89
  'mcp-cursor': createMCPHandler('cursor', true),
88
90
  'mcp-vscode': createMCPHandler('vscode', true),
89
91
  copy: () => ({
@@ -189,6 +191,9 @@ function createMCPAction({ clientType, mcpConfig, translate, onClickCallback, })
189
191
  if (clientType === 'cursor') {
190
192
  return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'), title: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'), description: translate('page.actions.connectMcp.cursorDescription', 'Install MCP server on Cursor'), iconComponent: CursorIcon_1.CursorIcon });
191
193
  }
194
+ if (clientType === 'claude') {
195
+ return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.claude', 'Connect to Claude'), title: translate('page.actions.connectMcp.claude', 'Connect to Claude'), description: translate('page.actions.connectMcp.claudeDescription', 'Add MCP server to your Claude account'), iconComponent: ClaudeIcon_1.ClaudeIcon });
196
+ }
192
197
  return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), title: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), description: translate('page.actions.connectMcp.vscodeDescription', 'Install MCP server on VS Code'), iconComponent: VSCodeIcon_1.VSCodeIcon });
193
198
  }
194
199
  function shouldHideAction(action, { pageProps, themeConfig, apiDocsExcludeFromSearch, isApiDocsPage, isPublic, mcpUrl, isMcpDisabled, }) {
@@ -221,6 +226,7 @@ function shouldHideActionByType(action, { isPublic, mcpUrl, isMcpDisabled, }) {
221
226
  return !isPublic;
222
227
  case 'docs-mcp-cursor':
223
228
  case 'docs-mcp-vscode':
229
+ case 'docs-mcp-claude':
224
230
  return Boolean(mcpUrl) || isMcpDisabled;
225
231
  case 'mcp-cursor':
226
232
  case 'mcp-vscode':
@@ -1320,6 +1320,7 @@ exports.tokens = (0, styled_components_1.css) `
1320
1320
  ${variables_5.breadcrumbs}
1321
1321
  ${variables_20.button}
1322
1322
  ${variables_21.aiAssistantButton}
1323
+ ${variables_21.connectMcpButton}
1323
1324
  ${variables_43.cards}
1324
1325
  ${variables_8.catalog}
1325
1326
  ${variables_11.catalogClassic}