@redocly/theme 0.71.0-next.4 → 0.71.0-next.5

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.
@@ -0,0 +1,8 @@
1
+ import type { JSX } from 'react';
2
+ export type MobileMenuButtonProps = {
3
+ isOpen: boolean;
4
+ /** Called inside a transition; the button shows the requested state until it commits. */
5
+ onOpenChange: (isOpen: boolean) => void;
6
+ className?: string;
7
+ };
8
+ export declare function MobileMenuButton({ isOpen, onOpenChange, className, }: MobileMenuButtonProps): JSX.Element;
@@ -0,0 +1,58 @@
1
+ "use strict";
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
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.MobileMenuButton = MobileMenuButton;
40
+ const react_1 = __importStar(require("react"));
41
+ const styled_components_1 = __importDefault(require("styled-components"));
42
+ const utils_1 = require("../../core/utils");
43
+ const Button_1 = require("../../components/Button/Button");
44
+ const MenuIcon_1 = require("../../icons/MenuIcon/MenuIcon");
45
+ const CloseIcon_1 = require("../../icons/CloseIcon/CloseIcon");
46
+ function MobileMenuButton({ isOpen, onOpenChange, className, }) {
47
+ const [isToggling, startToggle] = (0, react_1.useTransition)();
48
+ const showClose = isToggling ? !isOpen : isOpen;
49
+ return (react_1.default.createElement(MobileMenuButtonWrapper, { "data-component-name": "Buttons/MobileMenuButton", className: className, variant: "text", "data-testid": "mobile-menu-button", onClick: () => startToggle(() => onOpenChange(!showClose)), icon: showClose ? react_1.default.createElement(CloseIcon_1.CloseIcon, null) : react_1.default.createElement(MenuIcon_1.MenuIcon, null), "aria-label": showClose ? 'Close menu button' : 'Open menu button' }));
50
+ }
51
+ const MobileMenuButtonWrapper = (0, styled_components_1.default)(Button_1.Button) `
52
+ margin-left: 0px !important;
53
+
54
+ @media screen and (min-width: ${utils_1.breakpoints.medium}) {
55
+ display: none;
56
+ }
57
+ `;
58
+ //# sourceMappingURL=MobileMenuButton.js.map
@@ -21,7 +21,7 @@ function CatalogFilter({ filter, filterValuesCasing, showCounter = true, classNa
21
21
  if (!filteredOptions || filteredOptions.length === 0) {
22
22
  return null;
23
23
  }
24
- const FilterComponent = filterComponents[(filter.type || 'checkboxes')];
24
+ const FilterComponent = filterComponents[filter.type || 'checkboxes'];
25
25
  return (react_1.default.createElement(CatalogFilterGroup, { className: className, "data-component-name": "Catalog/CatalogFilter", key: filter.property + filter.title },
26
26
  react_1.default.createElement(FilterComponent, { filter: filter, filterValuesCasing: filterValuesCasing, showCounter: showCounter })));
27
27
  }
@@ -50,9 +50,7 @@ const Search_1 = require("../../components/Search/Search");
50
50
  const UserMenu_1 = require("../../components/UserMenu/UserMenu");
51
51
  const LanguagePicker_1 = require("../../components/LanguagePicker/LanguagePicker");
52
52
  const ProductPicker_1 = require("../../components/Product/ProductPicker");
53
- const Button_1 = require("../../components/Button/Button");
54
- const MenuIcon_1 = require("../../icons/MenuIcon/MenuIcon");
55
- const CloseIcon_1 = require("../../icons/CloseIcon/CloseIcon");
53
+ const MobileMenuButton_1 = require("../../components/Buttons/MobileMenuButton");
56
54
  const Banner_1 = require("../../components/Banner/Banner");
57
55
  function Navbar({ className }) {
58
56
  var _a;
@@ -81,19 +79,20 @@ function Navbar({ className }) {
81
79
  hideSearch ? null : react_1.default.createElement(Search_1.Search, null),
82
80
  react_1.default.createElement(LanguagePicker_1.LanguagePicker, { onChangeLanguage: changeLanguage, onlyIcon: true, alignment: "end" }),
83
81
  react_1.default.createElement(ColorModeSwitcher_1.ColorModeSwitcher, null),
84
- react_1.default.createElement(MobileMenuButton, { variant: "text", "data-testid": "mobile-menu-button", onClick: isOpen
85
- ? () => {
82
+ react_1.default.createElement(MobileMenuButton_1.MobileMenuButton, { isOpen: isOpen, onOpenChange: (open) => {
83
+ if (open) {
84
+ openMobileMenu();
85
+ telemetry.sendMobileMenuOpenedMessage([
86
+ (0, utils_1.getBaseDataAttributes)('mobileMenuOpen', 'button'),
87
+ ]);
88
+ }
89
+ else {
86
90
  closeMobileMenu();
87
91
  telemetry.sendMobileMenuClosedMessage([
88
92
  (0, utils_1.getBaseDataAttributes)('mobileMenuClose', 'button'),
89
93
  ]);
90
94
  }
91
- : () => {
92
- openMobileMenu();
93
- telemetry.sendMobileMenuOpenedMessage([
94
- (0, utils_1.getBaseDataAttributes)('mobileMenuOpen', 'button'),
95
- ]);
96
- }, icon: isOpen ? react_1.default.createElement(CloseIcon_1.CloseIcon, null) : react_1.default.createElement(MenuIcon_1.MenuIcon, null), "aria-label": isOpen ? 'Close menu button' : 'Open menu button' }),
95
+ } }),
97
96
  hideUserMenu ? null : react_1.default.createElement(UserMenu_1.UserMenu, null)),
98
97
  navigationTabsConfig && react_1.default.createElement(NavigationTabs_1.NavigationTabs, { config: navigationTabsConfig })));
99
98
  }
@@ -147,11 +146,4 @@ const NavbarRow = styled_components_1.default.div `
147
146
  margin-right: auto;
148
147
  }
149
148
  `;
150
- const MobileMenuButton = (0, styled_components_1.default)(Button_1.Button) `
151
- margin-left: 0px !important;
152
-
153
- @media screen and (min-width: ${utils_1.breakpoints.medium}) {
154
- display: none;
155
- }
156
- `;
157
149
  //# sourceMappingURL=Navbar.js.map
@@ -2,15 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useFocusTrap = useFocusTrap;
4
4
  const react_1 = require("react");
5
+ const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
5
6
  function useFocusTrap(containerRef) {
6
7
  (0, react_1.useEffect)(() => {
8
+ var _a;
7
9
  const trapElement = containerRef.current;
8
10
  if (!trapElement)
9
11
  return;
10
- const focusableElements = trapElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
11
- const firstElement = focusableElements[0];
12
+ const getFocusableElements = () => trapElement.querySelectorAll(FOCUSABLE_SELECTOR);
12
13
  const handleKeyDown = (event) => {
13
14
  if (event.key === 'Tab') {
15
+ const focusableElements = getFocusableElements();
16
+ const firstElement = focusableElements[0];
14
17
  const lastElement = focusableElements[focusableElements.length - 1];
15
18
  if (event.shiftKey && document.activeElement === firstElement) {
16
19
  lastElement === null || lastElement === void 0 ? void 0 : lastElement.focus();
@@ -23,9 +26,7 @@ function useFocusTrap(containerRef) {
23
26
  }
24
27
  };
25
28
  trapElement.addEventListener('keydown', handleKeyDown);
26
- if (firstElement) {
27
- firstElement.focus();
28
- }
29
+ (_a = getFocusableElements()[0]) === null || _a === void 0 ? void 0 : _a.focus();
29
30
  return () => {
30
31
  trapElement.removeEventListener('keydown', handleKeyDown);
31
32
  };
@@ -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' | 'docs-mcp-claude' | 'mcp-cursor' | 'mcp-vscode';
2
+ export type PageActionType = 'copy' | 'view' | 'chatgpt' | 'claude' | 'grok' | '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[];
@@ -14,6 +14,7 @@ const react_1 = require("react");
14
14
  const CopyIcon_1 = require("../../icons/CopyIcon/CopyIcon");
15
15
  const ChatGptIcon_1 = require("../../icons/ChatGptIcon/ChatGptIcon");
16
16
  const ClaudeIcon_1 = require("../../icons/ClaudeIcon/ClaudeIcon");
17
+ const GrokIcon_1 = require("../../icons/GrokIcon/GrokIcon");
17
18
  const MarkdownFullIcon_1 = require("../../icons/MarkdownFullIcon/MarkdownFullIcon");
18
19
  const VSCodeIcon_1 = require("../../icons/VSCodeIcon/VSCodeIcon");
19
20
  const CursorIcon_1 = require("../../icons/CursorIcon/CursorIcon");
@@ -31,6 +32,7 @@ const DEFAULT_ENABLED_ACTIONS = [
31
32
  'view',
32
33
  'chatgpt',
33
34
  'claude',
35
+ 'grok',
34
36
  'docs-mcp-cursor',
35
37
  'docs-mcp-vscode',
36
38
  'docs-mcp-claude',
@@ -84,6 +86,16 @@ function usePageActions(pageSlug, mcpUrl, actions) {
84
86
  ? (0, urls_1.combineUrls)(origin, pathname, 'index.html.md')
85
87
  : (0, urls_1.combineUrls)(origin, (0, urls_1.removeTrailingSlash)(pathname) + '.md');
86
88
  const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
89
+ const createAiPromptAction = (target, iconComponent, texts) => () => {
90
+ const link = (0, get_ai_prompt_link_1.getAiPromptLink)(target, externalAiPrompt);
91
+ return Object.assign(Object.assign({}, texts), { iconComponent,
92
+ link, onClick: () => {
93
+ telemetry.sendPageActionsClickedMessage([
94
+ Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('pageActions', 'button')), { actionType: target }),
95
+ ]);
96
+ window.location.href = link;
97
+ } });
98
+ };
87
99
  const actionHandlers = {
88
100
  'docs-mcp-cursor': createMCPHandler('cursor', false),
89
101
  'docs-mcp-vscode': createMCPHandler('vscode', false),
@@ -124,38 +136,21 @@ function usePageActions(pageSlug, mcpUrl, actions) {
124
136
  ]);
125
137
  },
126
138
  }),
127
- chatgpt: () => {
128
- const link = (0, get_ai_prompt_link_1.getAiPromptLink)('chatgpt', externalAiPrompt);
129
- return {
130
- buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
131
- title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
132
- description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
133
- iconComponent: ChatGptIcon_1.ChatGptIcon,
134
- link,
135
- onClick: () => {
136
- telemetry.sendPageActionsClickedMessage([
137
- Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('pageActions', 'button')), { actionType: 'chatgpt' }),
138
- ]);
139
- window.location.href = link;
140
- },
141
- };
142
- },
143
- claude: () => {
144
- const link = (0, get_ai_prompt_link_1.getAiPromptLink)('claude', externalAiPrompt);
145
- return {
146
- buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
147
- title: translate('page.actions.claudeTitle', 'Open in Claude'),
148
- description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
149
- iconComponent: ClaudeIcon_1.ClaudeIcon,
150
- link,
151
- onClick: () => {
152
- telemetry.sendPageActionsClickedMessage([
153
- Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('pageActions', 'button')), { actionType: 'claude' }),
154
- ]);
155
- window.location.href = link;
156
- },
157
- };
158
- },
139
+ chatgpt: createAiPromptAction('chatgpt', ChatGptIcon_1.ChatGptIcon, {
140
+ buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
141
+ title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
142
+ description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
143
+ }),
144
+ claude: createAiPromptAction('claude', ClaudeIcon_1.ClaudeIcon, {
145
+ buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
146
+ title: translate('page.actions.claudeTitle', 'Open in Claude'),
147
+ description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
148
+ }),
149
+ grok: createAiPromptAction('grok', GrokIcon_1.GrokIcon, {
150
+ buttonText: translate('page.actions.grokButtonText', 'Open in Grok'),
151
+ title: translate('page.actions.grokTitle', 'Open in Grok'),
152
+ description: translate('page.actions.grokDescription', 'Get insights from Grok'),
153
+ }),
159
154
  };
160
155
  return (((_b = (_a = themeConfig.navigation) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.items) || actions || DEFAULT_ENABLED_ACTIONS)
161
156
  .filter((action) => !shouldHideAction(action, hideActionContext))
@@ -198,7 +193,7 @@ function shouldHideAction(action, { pageProps, themeConfig, apiDocsExcludeFromSe
198
193
  if ((_b = (_a = themeConfig.navigation) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.hide) {
199
194
  return true;
200
195
  }
201
- const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude'].includes(action);
196
+ const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude', 'grok'].includes(action);
202
197
  if (actionDependsOnLlmsTxt) {
203
198
  // Can't use any actions if search is globally disabled (markdown files are not generated)
204
199
  if ((_c = themeConfig.search) === null || _c === void 0 ? void 0 : _c.hide) {
@@ -219,6 +214,7 @@ function shouldHideActionByType(action, { isPublic, mcpUrl, isMcpDisabled, }) {
219
214
  switch (action) {
220
215
  case 'chatgpt':
221
216
  case 'claude':
217
+ case 'grok':
222
218
  return !isPublic;
223
219
  case 'docs-mcp-cursor':
224
220
  case 'docs-mcp-vscode':
@@ -1,5 +1,5 @@
1
1
  import type { TOptions } from 'i18next';
2
- export type TranslationKey = 'dev.newApp' | 'dev.newApp.text' | 'dev.sidebar.header' | 'dev.sidebar.footer.text' | 'dev.create.app.dialog.appName.placeholder' | 'dev.create.app.dialog.appName.error' | 'dev.create.app.dialog.selectAPIs' | 'dev.create.app.dialog.description' | 'dev.create.app.dialog.description.placeholder' | 'dev.create.app.dialog.create' | 'dev.create.app.dialog.cancel' | 'dev.main.tab.appKeys' | 'dev.main.tab.logs' | 'dev.app.description.title' | 'dev.edit.description.dialog.title' | 'dev.edit.description.dialog.save' | 'dev.edit.description.dialog.cancel' | 'dev.create.app.dialog.callbackUrls' | 'dev.create.app.dialog.callbackUrls.placeholder' | 'dev.create.app.dialog.callbackUrls.hint' | 'dev.app.callbackUrls.title' | 'dev.edit.callbackUrls.dialog.title' | 'dev.edit.callbackUrls.dialog.placeholder' | 'dev.edit.callbackUrls.dialog.hint' | 'dev.edit.callbackUrls.dialog.save' | 'dev.edit.callbackUrls.dialog.cancel' | 'dev.edit.apis.dialog.selectedAPIs' | 'dev.app.key.create' | 'dev.create.key.dialog.title' | 'dev.create.key.dialog.create' | 'dev.create.key.dialog.cancel' | 'dev.app.edit' | 'dev.app.delete' | 'dev.edit.app.dialog.title' | 'dev.edit.app.dialog.save' | 'dev.edit.app.dialog.cancel' | 'dev.delete.app.dialog.title' | 'dev.delete.app.dialog.confirmation' | 'dev.delete.app.dialog.delete' | 'dev.delete.app.dialog.cancel' | 'dev.app.key.roll' | 'dev.roll.key.dialog.title' | 'dev.roll.key.dialog.apiKey' | 'dev.roll.key.dialog.expires' | 'dev.roll.key.dialog.confirmation' | 'dev.roll.key.dialog.cancel' | 'dev.roll.key.dialog.roll' | 'dev.update.key.dialog.title' | 'dev.update.key.dialog.update' | 'dev.update.key.dialog.cancel' | 'dev.app.key.api.name' | 'dev.app.key.api.status' | 'dev.app.key.api.edit' | 'dev.edit.apis.dialog.title' | 'dev.edit.apis.dialog.apiKey' | 'dev.edit.apis.dialog.save' | 'dev.edit.apis.dialog.cancel' | 'dev.select.placeholder' | 'dev.app.overview.status.pending' | 'dev.app.overview.status.approved' | 'dev.app.overview.status.revoked' | 'dev.app.overview.status' | 'dev.app.overview.non-production' | 'dev.app.overview.production' | 'dev.app.overview.clientId' | 'dev.app.overview.apiKey' | 'dev.app.key.revoke' | 'dev.revoke.key.dialog.title' | 'dev.revoke.key.dialog.apiKey' | 'dev.revoke.key.dialog.expires' | 'dev.revoke.key.dialog.confirmation' | 'dev.revoke.key.dialog.revoke' | 'dev.revoke.key.dialog.cancel' | 'dev.app.overview.expires' | 'dev.app.overview.created' | 'dev.app.overview.visibilityToggle.hide' | 'dev.app.overview.visibilityToggle.show' | 'search.loading' | 'search.noResults.title' | 'search.keys.navigate' | 'search.keys.select' | 'search.keys.exit' | 'search.label' | 'search.cancel' | 'search.recent' | 'search.navbar.label' | 'search.suggested' | 'search.showMore' | 'search.error.title' | 'search.error.button' | 'search.filter.title' | 'search.filter.reset' | 'search.filter.field.reset' | 'search.ai.welcomeText' | 'search.ai.newConversation' | 'search.ai.backToSearch' | 'search.ai.back' | 'search.ai.assistant' | 'search.ai.placeholder' | 'search.ai.generatingResponse' | 'search.ai.followUpQuestion' | 'search.ai.send' | 'search.ai.suggestionsTitle' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.resourcesFound.basedOn' | 'search.ai.resourcesFound.resources' | 'search.ai.feedback.title' | 'search.ai.feedback.detailsPlaceholder' | 'search.ai.feedback.thanks' | 'search.ai.toolCall.executed' | 'search.ai.toolCall.executing' | 'search.ai.toolCall.usedTools' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'search.ai.error.description' | 'search.ai.error.description.forbidden' | 'search.ai.error.description.unauthorized' | 'search.ai.error.header' | 'search.ai.error.header.forbidden' | 'search.ai.error.header.unauthorized' | 'search.ai.feedback.more' | 'search.ai.actions.copy' | 'search.ai.actions.like' | 'search.ai.actions.dislike' | 'search.searchItem.deprecated' | 'search.searchItem.enum' | 'search.searchItem.example' | 'search.groups.all' | 'search.filter.field.footer' | 'aiAssistant.trigger' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.forbidden.description' | 'page.notFound.title' | 'page.notFound.description' | 'page.apiNotFound.title' | 'page.apiNotFound.description' | 'page.apiNotFound.hint' | 'page.apiNotFound.backButton' | 'page.mcpNotAllowed.title' | 'page.mcpNotAllowed.description' | 'page.mcpNotAllowed.publicNote' | 'page.mcpNotAllowed.publicEndpointHint' | 'page.mcpNotAllowed.mainEndpointHint' | 'page.mcpNotAllowed.snippets.terminal.hint' | 'page.mcpNotAllowed.snippets.claudeCode.headline' | 'page.mcpNotAllowed.snippets.claudeDesktop.headline' | 'page.mcpNotAllowed.snippets.claudeDesktop.accountHint' | 'page.mcpNotAllowed.snippets.claudeDesktop.instruction' | 'page.mcpNotAllowed.snippets.chatgptApp.headline' | 'page.mcpNotAllowed.snippets.chatgptApp.instruction' | 'page.mcpNotAllowed.snippets.codexCli.headline' | 'page.mcpNotAllowed.snippets.prompt.headline' | 'page.mcpNotAllowed.snippets.prompt.hint' | 'page.mcpNotAllowed.snippets.json.hint' | 'page.mcpNotAllowed.tabs.claude' | 'page.mcpNotAllowed.tabs.chatgpt' | 'page.mcpNotAllowed.tabs.json' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.add' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'catalog.catalogs.all.title' | 'catalog.catalogs.all.description' | 'catalog.catalogs.all.switcherLabel' | 'catalog.catalogs.service.title' | 'catalog.catalogs.service.description' | 'catalog.catalogs.service.switcherLabel' | 'catalog.catalogs.user.title' | 'catalog.catalogs.user.description' | 'catalog.catalogs.user.switcherLabel' | 'catalog.catalogs.team.title' | 'catalog.catalogs.team.description' | 'catalog.catalogs.team.switcherLabel' | 'catalog.catalogs.domain.title' | 'catalog.catalogs.domain.description' | 'catalog.catalogs.domain.switcherLabel' | 'catalog.catalogs.apiDescription.title' | 'catalog.catalogs.apiDescription.description' | 'catalog.catalogs.apiDescription.switcherLabel' | 'catalog.catalogs.dataSchema.title' | 'catalog.catalogs.dataSchema.description' | 'catalog.catalogs.dataSchema.switcherLabel' | 'catalog.catalogs.apiOperation.title' | 'catalog.catalogs.apiOperation.description' | 'catalog.catalogs.apiOperation.switcherLabel' | 'catalog.entity.metadata.title' | 'catalog.entity.schema.title' | 'catalog.entity.properties.apiDescription.title' | 'catalog.backToAllLabel' | 'catalog.notConnected' | 'catalog.tags.label' | 'catalog.sort' | 'catalog.catalogs.label' | 'catalog.owners.label' | 'catalog.repositories.label' | 'catalog.email.label' | 'catalog.format.label' | 'catalog.entityType.label' | 'catalog.domains.label' | 'catalog.contact.label' | 'catalog.methodAndPath.label' | 'catalog.links.label' | 'catalog.metadata.domains' | 'catalog.metadata.owners' | 'catalog.history.button.label' | 'catalog.history.sidebar.title' | 'catalog.history.sidebar.close' | 'catalog.history.version.label' | 'catalog.filters.close' | 'catalog.history.version.notSpecified' | 'catalog.history.version.default' | 'catalog.history.revisions.limitMessage' | 'catalog.history.revision.current' | 'catalog.history.revisions.showLess' | 'catalog.history.revisions.showMore' | 'navigationTabs.label' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeToSingleColumn' | 'sidebar.actions.changeToTwoColumns' | 'sidebar.actions.singleColumn' | 'sidebar.actions.twoColumns' | 'versionPicker.label' | 'versionPicker.unversioned' | 'codeSnippet.copy.buttonText' | 'codeSnippet.copy.tooltipText' | 'codeSnippet.copy.toasterText' | 'markdown.editPage.text' | 'feedback.settings.comment.submitText' | 'feedback.settings.comment.label' | 'feedback.settings.comment.send' | 'feedback.settings.comment.cancel' | 'feedback.settings.comment.maxLength' | 'feedback.settings.comment.satisfiedLabel' | 'feedback.settings.comment.neutralLabel' | 'feedback.settings.comment.dissatisfiedLabel' | 'feedback.settings.submitText' | 'feedback.settings.label' | 'feedback.settings.reasons.label' | 'feedback.submit' | 'feedback.cancel' | 'feedback.settings.comment.likeLabel' | 'feedback.settings.comment.dislikeLabel' | 'feedback.sentiment.thumbUp' | 'feedback.sentiment.thumbDown' | 'feedback.settings.leftScaleLabel' | 'feedback.settings.rightScaleLabel' | 'feedback.settings.optionalEmail.placeholder' | 'feedback.settings.optionalEmail.label' | 'codeSnippet.report.buttonText' | 'codeSnippet.report.tooltipText' | 'codeSnippet.report.label' | 'codeSnippet.expand.tooltipText' | 'codeSnippet.collapse.tooltipText' | 'jsonViewer.showMore' | 'jsonViewer.showLess' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'mobileMenu.version' | 'navbar.label' | 'navbar.products' | 'page.nextButton' | 'page.previousButton' | 'page.actions.copyButtonText' | 'page.actions.copyTitle' | 'page.actions.copyDescription' | 'page.actions.viewAsMdTitle' | 'page.actions.viewAsMdButtonText' | 'page.actions.viewAsMdDescription' | 'page.actions.chatGptTitle' | 'page.actions.chatGptButtonText' | 'page.actions.chatGptDescription' | 'page.actions.claudeTitle' | 'page.actions.claudeButtonText' | 'page.actions.claudeDescription' | 'page.actions.cursorMcpButtonText' | 'page.actions.cursorMcpTitle' | 'page.actions.cursorMcpDescription' | 'page.actions.connectMcp' | 'page.actions.connectMcp.cursor' | 'page.actions.connectMcp.cursorDescription' | 'page.actions.connectMcp.vscode' | 'page.actions.connectMcp.vscodeDescription' | 'page.actions.connectMcp.claude' | 'page.actions.connectMcp.claudeDescription' | 'page.actions.connectMcp.claudeCode' | 'page.actions.connectMcp.claudeCodeDescription' | 'page.actions.connectMcp.codex' | 'page.actions.connectMcp.codexDescription' | 'page.actions.connectMcp.copyConfig' | 'page.actions.connectMcp.copyConfigDescription' | 'page.actions.connectMcp.moreOptions' | 'page.actions.moreActions' | 'promptTag.copy.buttonText' | 'promptTag.showPrompt' | 'promptTag.hidePrompt' | 'promptTag.showFullPrompt' | 'promptTag.showLess' | 'promptTag.openIn.cursor' | 'promptTag.openIn.cursorDescription' | 'promptTag.openIn.claude' | 'promptTag.openIn.claudeDescription' | 'promptTag.openIn.chatgpt' | 'promptTag.openIn.chatgptDescription' | 'openapi.download.description.title' | 'openapi.info.title' | 'openapi.info.contact.url' | 'openapi.info.contact.name' | 'openapi.info.license' | 'openapi.info.termsOfService' | 'openapi.info.metadata.title' | 'graphql.info.metadata.title' | 'openapi.key' | 'openapi.value' | 'openapi.enum' | 'openapi.items' | 'openapi.default' | 'openapi.variable' | 'openapi.variables' | 'openapi.actions.show' | 'openapi.actions.hide' | 'openapi.actions.more' | 'openapi.languages.title' | 'openapi.languages.moreButton.tooltipText' | 'openapi.servers.title' | 'openapi.operations' | 'openapi.schemas' | 'openapi.webhooks' | 'openapi.description' | 'openapi.badges.deprecated' | 'openapi.badges.required' | 'openapi.badges.webhook' | 'openapi.request' | 'openapi.path' | 'openapi.query' | 'openapi.querystring' | 'openapi.cookie' | 'openapi.header' | 'openapi.body' | 'openapi.responses' | 'openapi.response' | 'openapi.callbacks' | 'openapi.callbackRequest' | 'openapi.callbackResponse' | 'openapi.payload' | 'openapi.discriminator' | 'openapi.contentType' | 'openapi.tryIt' | 'openapi.loading' | 'openapi.example' | 'openapi.examples' | 'openapi.additionalProperties' | 'openapi.patternProperties' | 'openapi.required' | 'openapi.recursive' | 'openapi.complex' | 'openapi.property' | 'openapi.properties' | 'openapi.details' | 'openapi.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.deepLink.tooltip' | 'openapi.collapseAll' | 'openapi.viewSecurityDetails' | 'openapi.noResponseExample' | 'openapi.discriminator.searchPlaceholder' | 'openapi.discriminator.searchNoResults' | 'openapi.discriminator.defaultMapping' | 'openapi.discriminator.defaultMappingTooltip' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.noRequestData' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.showOptionalScopes' | 'openapi.hideOptionalScopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'openapi.schemaCatalogLink.title' | 'openapi.schemaCatalogLink.copyButtonTooltip' | 'openapi.schemaCatalogLink.copiedTooltip' | 'openapi.mcp.title' | 'openapi.mcp.endpoint' | 'openapi.mcp.tools' | 'openapi.mcp.protocolVersion' | 'openapi.mcp.capabilities' | 'openapi.mcp.experimentalCapabilities' | 'openapi.mcp.inputSchema' | 'openapi.mcp.inputExample' | 'openapi.mcp.outputSchema' | 'openapi.mcp.outputExample' | 'openapi.mcp.exampleTitle' | 'openapi.mcp.argumentsExample' | 'asyncapi.download.description.title' | 'asyncapi.info.title' | 'asyncapi.info.contact.url' | 'asyncapi.info.contact.name' | 'asyncapi.info.license' | 'asyncapi.info.termsOfService' | 'asyncapi.key' | 'asyncapi.value' | 'asyncapi.variables' | 'asyncapi.required' | 'asyncapi.requiredScopes' | 'asyncapi.viewSecurityDetails' | 'graphql.download.description.title' | 'graphql.info.title' | 'graphql.info.contact.url' | 'graphql.info.contact.name' | 'graphql.info.license' | 'graphql.info.termsOfService' | 'graphql.overview' | 'graphql.metadata' | 'graphql.key' | 'graphql.value' | 'graphql.queries' | 'graphql.mutations' | 'graphql.subscriptions' | 'graphql.directives' | 'graphql.objects' | 'graphql.interfaces' | 'graphql.unions' | 'graphql.enums' | 'graphql.inputs' | 'graphql.scalars' | 'graphql.actions.show' | 'graphql.actions.hide' | 'graphql.actions.more' | 'graphql.fields' | 'graphql.inputFields' | 'graphql.values' | 'graphql.arguments.label' | 'graphql.arguments.show' | 'graphql.arguments.hide' | 'graphql.arguments.here' | 'graphql.returnTypes.label' | 'graphql.returnTypes.show' | 'graphql.returnTypes.hide' | 'graphql.possibleTypes' | 'graphql.defaultValue' | 'graphql.deprecationReason' | 'graphql.requiredScopes' | 'graphql.viewSecurityDetails' | 'graphql.objectScopes' | 'graphql.fieldScopes' | 'graphql.implementedInterfaces' | 'graphql.implementedBy' | 'graphql.nonNull' | 'graphql.required' | 'asyncapi.badges.deprecated' | 'graphql.badges.deprecated' | 'asyncapi.actions.show' | 'asyncapi.actions.hide' | 'asyncapi.actions.more' | 'asyncapi.payload' | 'asyncapi.parameters' | 'asyncapi.messages' | 'asyncapi.contentType' | 'asyncapi.moreDetails' | 'asyncapi.property' | 'asyncapi.properties' | 'asyncapi.details' | 'asyncapi.reply' | 'asyncapi.servers.title' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'graphql.content.fragment' | 'graphql.tryIt' | 'graphql.loading' | 'button.download.tooltipText' | 'button.externalLink.tooltipText' | 'button.email.tooltipText' | 'codeWalkthrough.download' | 'codeWalkthrough.preview' | 'time.justNow' | 'time.past.second' | 'time.past.seconds' | 'time.past.minute' | 'time.past.minutes' | 'time.past.hour' | 'time.past.hours' | 'time.past.day' | 'time.past.days' | 'time.past.week' | 'time.past.weeks' | 'time.past.month' | 'time.past.months' | 'time.past.year' | 'time.past.years' | 'page.internalServerError.title' | 'page.internalServerError.description' | 'page.skipToContent.label' | 'select.noResults' | 'loaders.loading' | 'filter.dateRange.from' | 'filter.dateRange.to' | 'diagram.openFullscreen' | 'diagram.zoomIn' | 'diagram.zoomOut' | 'diagram.reset' | 'diagram.close' | 'diagram.viewer' | 'consent.title' | 'consent.message' | 'consent.privacyPolicy' | 'consent.customize' | 'consent.rejectAll' | 'consent.acceptAll' | 'consent.acceptAllCookies' | 'consent.savePreferences' | 'consent.dialog.title' | 'consent.dialog.description' | 'consent.dialog.close' | 'consent.alwaysOn' | 'consent.alwaysOff' | 'consent.gpcNotice' | 'consent.category.necessary.title' | 'consent.category.necessary.description' | 'consent.category.analytics.title' | 'consent.category.analytics.description' | 'consent.category.marketing.title' | 'consent.category.marketing.description' | 'consent.settingsLink';
2
+ export type TranslationKey = 'dev.newApp' | 'dev.newApp.text' | 'dev.sidebar.header' | 'dev.sidebar.footer.text' | 'dev.create.app.dialog.appName.placeholder' | 'dev.create.app.dialog.appName.error' | 'dev.create.app.dialog.selectAPIs' | 'dev.create.app.dialog.description' | 'dev.create.app.dialog.description.placeholder' | 'dev.create.app.dialog.create' | 'dev.create.app.dialog.cancel' | 'dev.main.tab.appKeys' | 'dev.main.tab.logs' | 'dev.app.description.title' | 'dev.edit.description.dialog.title' | 'dev.edit.description.dialog.save' | 'dev.edit.description.dialog.cancel' | 'dev.create.app.dialog.callbackUrls' | 'dev.create.app.dialog.callbackUrls.placeholder' | 'dev.create.app.dialog.callbackUrls.hint' | 'dev.app.callbackUrls.title' | 'dev.edit.callbackUrls.dialog.title' | 'dev.edit.callbackUrls.dialog.placeholder' | 'dev.edit.callbackUrls.dialog.hint' | 'dev.edit.callbackUrls.dialog.save' | 'dev.edit.callbackUrls.dialog.cancel' | 'dev.edit.apis.dialog.selectedAPIs' | 'dev.app.key.create' | 'dev.create.key.dialog.title' | 'dev.create.key.dialog.create' | 'dev.create.key.dialog.cancel' | 'dev.app.edit' | 'dev.app.delete' | 'dev.edit.app.dialog.title' | 'dev.edit.app.dialog.save' | 'dev.edit.app.dialog.cancel' | 'dev.delete.app.dialog.title' | 'dev.delete.app.dialog.confirmation' | 'dev.delete.app.dialog.delete' | 'dev.delete.app.dialog.cancel' | 'dev.app.key.roll' | 'dev.roll.key.dialog.title' | 'dev.roll.key.dialog.apiKey' | 'dev.roll.key.dialog.expires' | 'dev.roll.key.dialog.confirmation' | 'dev.roll.key.dialog.cancel' | 'dev.roll.key.dialog.roll' | 'dev.update.key.dialog.title' | 'dev.update.key.dialog.update' | 'dev.update.key.dialog.cancel' | 'dev.app.key.api.name' | 'dev.app.key.api.status' | 'dev.app.key.api.edit' | 'dev.edit.apis.dialog.title' | 'dev.edit.apis.dialog.apiKey' | 'dev.edit.apis.dialog.save' | 'dev.edit.apis.dialog.cancel' | 'dev.select.placeholder' | 'dev.app.overview.status.pending' | 'dev.app.overview.status.approved' | 'dev.app.overview.status.revoked' | 'dev.app.overview.status' | 'dev.app.overview.non-production' | 'dev.app.overview.production' | 'dev.app.overview.clientId' | 'dev.app.overview.apiKey' | 'dev.app.key.revoke' | 'dev.revoke.key.dialog.title' | 'dev.revoke.key.dialog.apiKey' | 'dev.revoke.key.dialog.expires' | 'dev.revoke.key.dialog.confirmation' | 'dev.revoke.key.dialog.revoke' | 'dev.revoke.key.dialog.cancel' | 'dev.app.overview.expires' | 'dev.app.overview.created' | 'dev.app.overview.visibilityToggle.hide' | 'dev.app.overview.visibilityToggle.show' | 'search.loading' | 'search.noResults.title' | 'search.keys.navigate' | 'search.keys.select' | 'search.keys.exit' | 'search.label' | 'search.cancel' | 'search.recent' | 'search.navbar.label' | 'search.suggested' | 'search.showMore' | 'search.error.title' | 'search.error.button' | 'search.filter.title' | 'search.filter.reset' | 'search.filter.field.reset' | 'search.ai.welcomeText' | 'search.ai.newConversation' | 'search.ai.backToSearch' | 'search.ai.back' | 'search.ai.assistant' | 'search.ai.placeholder' | 'search.ai.generatingResponse' | 'search.ai.followUpQuestion' | 'search.ai.send' | 'search.ai.suggestionsTitle' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.resourcesFound.basedOn' | 'search.ai.resourcesFound.resources' | 'search.ai.feedback.title' | 'search.ai.feedback.detailsPlaceholder' | 'search.ai.feedback.thanks' | 'search.ai.toolCall.executed' | 'search.ai.toolCall.executing' | 'search.ai.toolCall.usedTools' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'search.ai.error.description' | 'search.ai.error.description.forbidden' | 'search.ai.error.description.unauthorized' | 'search.ai.error.header' | 'search.ai.error.header.forbidden' | 'search.ai.error.header.unauthorized' | 'search.ai.feedback.more' | 'search.ai.actions.copy' | 'search.ai.actions.like' | 'search.ai.actions.dislike' | 'search.searchItem.deprecated' | 'search.searchItem.enum' | 'search.searchItem.example' | 'search.groups.all' | 'search.filter.field.footer' | 'aiAssistant.trigger' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.forbidden.description' | 'page.notFound.title' | 'page.notFound.description' | 'page.apiNotFound.title' | 'page.apiNotFound.description' | 'page.apiNotFound.hint' | 'page.apiNotFound.backButton' | 'page.mcpNotAllowed.title' | 'page.mcpNotAllowed.description' | 'page.mcpNotAllowed.publicNote' | 'page.mcpNotAllowed.publicEndpointHint' | 'page.mcpNotAllowed.mainEndpointHint' | 'page.mcpNotAllowed.snippets.terminal.hint' | 'page.mcpNotAllowed.snippets.claudeCode.headline' | 'page.mcpNotAllowed.snippets.claudeDesktop.headline' | 'page.mcpNotAllowed.snippets.claudeDesktop.accountHint' | 'page.mcpNotAllowed.snippets.claudeDesktop.instruction' | 'page.mcpNotAllowed.snippets.chatgptApp.headline' | 'page.mcpNotAllowed.snippets.chatgptApp.instruction' | 'page.mcpNotAllowed.snippets.codexCli.headline' | 'page.mcpNotAllowed.snippets.prompt.headline' | 'page.mcpNotAllowed.snippets.prompt.hint' | 'page.mcpNotAllowed.snippets.json.hint' | 'page.mcpNotAllowed.tabs.claude' | 'page.mcpNotAllowed.tabs.chatgpt' | 'page.mcpNotAllowed.tabs.json' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.add' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'catalog.catalogs.all.title' | 'catalog.catalogs.all.description' | 'catalog.catalogs.all.switcherLabel' | 'catalog.catalogs.service.title' | 'catalog.catalogs.service.description' | 'catalog.catalogs.service.switcherLabel' | 'catalog.catalogs.user.title' | 'catalog.catalogs.user.description' | 'catalog.catalogs.user.switcherLabel' | 'catalog.catalogs.team.title' | 'catalog.catalogs.team.description' | 'catalog.catalogs.team.switcherLabel' | 'catalog.catalogs.domain.title' | 'catalog.catalogs.domain.description' | 'catalog.catalogs.domain.switcherLabel' | 'catalog.catalogs.apiDescription.title' | 'catalog.catalogs.apiDescription.description' | 'catalog.catalogs.apiDescription.switcherLabel' | 'catalog.catalogs.dataSchema.title' | 'catalog.catalogs.dataSchema.description' | 'catalog.catalogs.dataSchema.switcherLabel' | 'catalog.catalogs.apiOperation.title' | 'catalog.catalogs.apiOperation.description' | 'catalog.catalogs.apiOperation.switcherLabel' | 'catalog.entity.metadata.title' | 'catalog.entity.schema.title' | 'catalog.entity.properties.apiDescription.title' | 'catalog.backToAllLabel' | 'catalog.notConnected' | 'catalog.tags.label' | 'catalog.sort' | 'catalog.catalogs.label' | 'catalog.owners.label' | 'catalog.repositories.label' | 'catalog.email.label' | 'catalog.format.label' | 'catalog.entityType.label' | 'catalog.domains.label' | 'catalog.contact.label' | 'catalog.methodAndPath.label' | 'catalog.links.label' | 'catalog.metadata.domains' | 'catalog.metadata.owners' | 'catalog.history.button.label' | 'catalog.history.sidebar.title' | 'catalog.history.sidebar.close' | 'catalog.history.version.label' | 'catalog.filters.close' | 'catalog.history.version.notSpecified' | 'catalog.history.version.default' | 'catalog.history.revisions.limitMessage' | 'catalog.history.revision.current' | 'catalog.history.revisions.showLess' | 'catalog.history.revisions.showMore' | 'navigationTabs.label' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeToSingleColumn' | 'sidebar.actions.changeToTwoColumns' | 'sidebar.actions.singleColumn' | 'sidebar.actions.twoColumns' | 'versionPicker.label' | 'versionPicker.unversioned' | 'codeSnippet.copy.buttonText' | 'codeSnippet.copy.tooltipText' | 'codeSnippet.copy.toasterText' | 'markdown.editPage.text' | 'feedback.settings.comment.submitText' | 'feedback.settings.comment.label' | 'feedback.settings.comment.send' | 'feedback.settings.comment.cancel' | 'feedback.settings.comment.maxLength' | 'feedback.settings.comment.satisfiedLabel' | 'feedback.settings.comment.neutralLabel' | 'feedback.settings.comment.dissatisfiedLabel' | 'feedback.settings.submitText' | 'feedback.settings.label' | 'feedback.settings.reasons.label' | 'feedback.submit' | 'feedback.cancel' | 'feedback.settings.comment.likeLabel' | 'feedback.settings.comment.dislikeLabel' | 'feedback.sentiment.thumbUp' | 'feedback.sentiment.thumbDown' | 'feedback.settings.leftScaleLabel' | 'feedback.settings.rightScaleLabel' | 'feedback.settings.optionalEmail.placeholder' | 'feedback.settings.optionalEmail.label' | 'codeSnippet.report.buttonText' | 'codeSnippet.report.tooltipText' | 'codeSnippet.report.label' | 'codeSnippet.expand.tooltipText' | 'codeSnippet.collapse.tooltipText' | 'jsonViewer.showMore' | 'jsonViewer.showLess' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'mobileMenu.version' | 'navbar.label' | 'navbar.products' | 'page.nextButton' | 'page.previousButton' | 'page.actions.copyButtonText' | 'page.actions.copyTitle' | 'page.actions.copyDescription' | 'page.actions.viewAsMdTitle' | 'page.actions.viewAsMdButtonText' | 'page.actions.viewAsMdDescription' | 'page.actions.chatGptTitle' | 'page.actions.chatGptButtonText' | 'page.actions.chatGptDescription' | 'page.actions.claudeTitle' | 'page.actions.claudeButtonText' | 'page.actions.claudeDescription' | 'page.actions.grokTitle' | 'page.actions.grokButtonText' | 'page.actions.grokDescription' | 'page.actions.cursorMcpButtonText' | 'page.actions.cursorMcpTitle' | 'page.actions.cursorMcpDescription' | 'page.actions.connectMcp' | 'page.actions.connectMcp.cursor' | 'page.actions.connectMcp.cursorDescription' | 'page.actions.connectMcp.vscode' | 'page.actions.connectMcp.vscodeDescription' | 'page.actions.connectMcp.claude' | 'page.actions.connectMcp.claudeDescription' | 'page.actions.connectMcp.claudeCode' | 'page.actions.connectMcp.claudeCodeDescription' | 'page.actions.connectMcp.codex' | 'page.actions.connectMcp.codexDescription' | 'page.actions.connectMcp.copyConfig' | 'page.actions.connectMcp.copyConfigDescription' | 'page.actions.connectMcp.moreOptions' | 'page.actions.moreActions' | 'promptTag.copy.buttonText' | 'promptTag.showPrompt' | 'promptTag.hidePrompt' | 'promptTag.showFullPrompt' | 'promptTag.showLess' | 'promptTag.openIn.cursor' | 'promptTag.openIn.cursorDescription' | 'promptTag.openIn.claude' | 'promptTag.openIn.claudeDescription' | 'promptTag.openIn.chatgpt' | 'promptTag.openIn.chatgptDescription' | 'openapi.download.description.title' | 'openapi.info.title' | 'openapi.info.contact.url' | 'openapi.info.contact.name' | 'openapi.info.license' | 'openapi.info.termsOfService' | 'openapi.info.metadata.title' | 'graphql.info.metadata.title' | 'openapi.key' | 'openapi.value' | 'openapi.enum' | 'openapi.items' | 'openapi.default' | 'openapi.variable' | 'openapi.variables' | 'openapi.actions.show' | 'openapi.actions.hide' | 'openapi.actions.more' | 'openapi.languages.title' | 'openapi.languages.moreButton.tooltipText' | 'openapi.servers.title' | 'openapi.operations' | 'openapi.schemas' | 'openapi.webhooks' | 'openapi.description' | 'openapi.badges.deprecated' | 'openapi.badges.required' | 'openapi.badges.webhook' | 'openapi.request' | 'openapi.path' | 'openapi.query' | 'openapi.querystring' | 'openapi.cookie' | 'openapi.header' | 'openapi.body' | 'openapi.responses' | 'openapi.response' | 'openapi.callbacks' | 'openapi.callbackRequest' | 'openapi.callbackResponse' | 'openapi.payload' | 'openapi.discriminator' | 'openapi.contentType' | 'openapi.tryIt' | 'openapi.loading' | 'openapi.example' | 'openapi.examples' | 'openapi.additionalProperties' | 'openapi.patternProperties' | 'openapi.required' | 'openapi.recursive' | 'openapi.complex' | 'openapi.property' | 'openapi.properties' | 'openapi.details' | 'openapi.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.deepLink.tooltip' | 'openapi.collapseAll' | 'openapi.viewSecurityDetails' | 'openapi.noResponseExample' | 'openapi.discriminator.searchPlaceholder' | 'openapi.discriminator.searchNoResults' | 'openapi.discriminator.defaultMapping' | 'openapi.discriminator.defaultMappingTooltip' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.noRequestData' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.showOptionalScopes' | 'openapi.hideOptionalScopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'openapi.schemaCatalogLink.title' | 'openapi.schemaCatalogLink.copyButtonTooltip' | 'openapi.schemaCatalogLink.copiedTooltip' | 'openapi.mcp.title' | 'openapi.mcp.endpoint' | 'openapi.mcp.tools' | 'openapi.mcp.protocolVersion' | 'openapi.mcp.capabilities' | 'openapi.mcp.experimentalCapabilities' | 'openapi.mcp.inputSchema' | 'openapi.mcp.inputExample' | 'openapi.mcp.outputSchema' | 'openapi.mcp.outputExample' | 'openapi.mcp.exampleTitle' | 'openapi.mcp.argumentsExample' | 'asyncapi.download.description.title' | 'asyncapi.info.title' | 'asyncapi.info.contact.url' | 'asyncapi.info.contact.name' | 'asyncapi.info.license' | 'asyncapi.info.termsOfService' | 'asyncapi.key' | 'asyncapi.value' | 'asyncapi.variables' | 'asyncapi.required' | 'asyncapi.requiredScopes' | 'asyncapi.viewSecurityDetails' | 'graphql.download.description.title' | 'graphql.info.title' | 'graphql.info.contact.url' | 'graphql.info.contact.name' | 'graphql.info.license' | 'graphql.info.termsOfService' | 'graphql.overview' | 'graphql.metadata' | 'graphql.key' | 'graphql.value' | 'graphql.queries' | 'graphql.mutations' | 'graphql.subscriptions' | 'graphql.directives' | 'graphql.objects' | 'graphql.interfaces' | 'graphql.unions' | 'graphql.enums' | 'graphql.inputs' | 'graphql.scalars' | 'graphql.actions.show' | 'graphql.actions.hide' | 'graphql.actions.more' | 'graphql.fields' | 'graphql.inputFields' | 'graphql.values' | 'graphql.arguments.label' | 'graphql.arguments.show' | 'graphql.arguments.hide' | 'graphql.arguments.here' | 'graphql.returnTypes.label' | 'graphql.returnTypes.show' | 'graphql.returnTypes.hide' | 'graphql.possibleTypes' | 'graphql.defaultValue' | 'graphql.deprecationReason' | 'graphql.requiredScopes' | 'graphql.viewSecurityDetails' | 'graphql.objectScopes' | 'graphql.fieldScopes' | 'graphql.implementedInterfaces' | 'graphql.implementedBy' | 'graphql.nonNull' | 'graphql.required' | 'asyncapi.badges.deprecated' | 'graphql.badges.deprecated' | 'asyncapi.actions.show' | 'asyncapi.actions.hide' | 'asyncapi.actions.more' | 'asyncapi.payload' | 'asyncapi.parameters' | 'asyncapi.messages' | 'asyncapi.contentType' | 'asyncapi.moreDetails' | 'asyncapi.property' | 'asyncapi.properties' | 'asyncapi.details' | 'asyncapi.reply' | 'asyncapi.servers.title' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'graphql.content.fragment' | 'graphql.tryIt' | 'graphql.loading' | 'button.download.tooltipText' | 'button.externalLink.tooltipText' | 'button.email.tooltipText' | 'codeWalkthrough.download' | 'codeWalkthrough.preview' | 'time.justNow' | 'time.past.second' | 'time.past.seconds' | 'time.past.minute' | 'time.past.minutes' | 'time.past.hour' | 'time.past.hours' | 'time.past.day' | 'time.past.days' | 'time.past.week' | 'time.past.weeks' | 'time.past.month' | 'time.past.months' | 'time.past.year' | 'time.past.years' | 'page.internalServerError.title' | 'page.internalServerError.description' | 'page.skipToContent.label' | 'select.noResults' | 'loaders.loading' | 'filter.dateRange.from' | 'filter.dateRange.to' | 'diagram.openFullscreen' | 'diagram.zoomIn' | 'diagram.zoomOut' | 'diagram.reset' | 'diagram.close' | 'diagram.viewer' | 'consent.title' | 'consent.message' | 'consent.privacyPolicy' | 'consent.customize' | 'consent.rejectAll' | 'consent.acceptAll' | 'consent.acceptAllCookies' | 'consent.savePreferences' | 'consent.dialog.title' | 'consent.dialog.description' | 'consent.dialog.close' | 'consent.alwaysOn' | 'consent.alwaysOff' | 'consent.gpcNotice' | 'consent.category.necessary.title' | 'consent.category.necessary.description' | 'consent.category.analytics.title' | 'consent.category.analytics.description' | 'consent.category.marketing.title' | 'consent.category.marketing.description' | 'consent.settingsLink';
3
3
  export type Locale = {
4
4
  code: string;
5
5
  name: string;
@@ -1,4 +1,5 @@
1
1
  import type { AiPromptTarget } from '../types/prompt';
2
+ type AiPromptLinkTarget = AiPromptTarget | 'grok';
2
3
  /**
3
4
  * Builds a link that opens an AI tool with the prompt prefilled.
4
5
  *
@@ -12,4 +13,5 @@ import type { AiPromptTarget } from '../types/prompt';
12
13
  * // 'https://claude.ai/new?q=Summarize+this+page'
13
14
  * ```
14
15
  */
15
- export declare function getAiPromptLink(target: AiPromptTarget, prompt: string): string;
16
+ export declare function getAiPromptLink(target: AiPromptLinkTarget, prompt: string): string;
17
+ export {};
@@ -4,6 +4,7 @@ exports.getAiPromptLink = getAiPromptLink;
4
4
  const AI_PROMPT_ENDPOINTS = {
5
5
  chatgpt: { baseUrl: 'https://chat.openai.com', queryParam: 'q' },
6
6
  claude: { baseUrl: 'https://claude.ai/new', queryParam: 'q' },
7
+ grok: { baseUrl: 'https://grok.com/', queryParam: 'q' },
7
8
  // Encoding: https://cursor.com/docs/integrations/deeplinks
8
9
  cursor: { baseUrl: 'https://cursor.com/link/prompt', queryParam: 'text' },
9
10
  };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import type { IconProps } from '../../icons/types';
3
+ declare const Icon: (props: IconProps) => React.JSX.Element;
4
+ export declare const GrokIcon: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<{
5
+ color?: string;
6
+ size?: string;
7
+ className?: string;
8
+ 'data-component-name'?: string;
9
+ } & React.SVGProps<SVGSVGElement>, "data-component-name" | "size" | keyof React.SVGProps<SVGSVGElement>> & {
10
+ color?: string;
11
+ size?: string;
12
+ className?: string;
13
+ 'data-component-name'?: string;
14
+ } & React.SVGProps<SVGSVGElement>, "data-component-name"> & Partial<Pick<import("styled-components").FastOmit<{
15
+ color?: string;
16
+ size?: string;
17
+ className?: string;
18
+ 'data-component-name'?: string;
19
+ } & React.SVGProps<SVGSVGElement>, "data-component-name" | "size" | keyof React.SVGProps<SVGSVGElement>> & {
20
+ color?: string;
21
+ size?: string;
22
+ className?: string;
23
+ 'data-component-name'?: string;
24
+ } & React.SVGProps<SVGSVGElement>, "data-component-name">>> & string & Omit<typeof Icon, keyof React.Component<any, {}, any>>;
25
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GrokIcon = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const utils_1 = require("../../core/utils");
10
+ const Icon = (props) => (react_1.default.createElement("svg", Object.assign({ viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, props),
11
+ react_1.default.createElement("path", { d: "M6.6345 9.6347L10.6239 6.6862C10.8195 6.5417 11.0991 6.598 11.1923 6.8226C11.6827 8.0067 11.4636 9.4297 10.4877 10.4067C9.5119 11.3837 8.1541 11.5979 6.9131 11.1099L5.5573 11.7384C7.5019 13.0691 9.8631 12.74 11.3387 11.2617C12.5091 10.0899 12.8716 8.4926 12.5327 7.0522L12.5357 7.0553C12.0442 4.9393 12.6566 4.0935 13.9109 2.364C13.9406 2.3229 13.9703 2.2819 14 2.2399L12.3493 3.8925V3.8874L6.6335 9.6357" }),
12
+ react_1.default.createElement("path", { d: "M5.8113 10.3513C4.4156 9.0165 4.6562 6.9507 5.8471 5.7594C6.7277 4.8777 8.1705 4.5179 9.43 5.0469L10.7827 4.4215C10.539 4.2452 10.2266 4.0555 9.8682 3.9223C8.2483 3.2549 6.3089 3.587 4.9921 4.9044C3.7254 6.1726 3.3271 8.1225 4.0111 9.7864C4.5221 11.03 3.6844 11.9096 2.8407 12.7974C2.5417 13.1122 2.2417 13.4269 2 13.7601L5.8102 10.3523" })));
13
+ exports.GrokIcon = (0, styled_components_1.default)(Icon).attrs({
14
+ 'data-component-name': 'icons/GrokIcon/GrokIcon',
15
+ }) `
16
+ path {
17
+ fill: ${({ color }) => (0, utils_1.getCssColorVariable)(color)};
18
+ }
19
+
20
+ height: ${({ size }) => size || '16px'};
21
+ width: ${({ size }) => size || '16px'};
22
+ `;
23
+ //# sourceMappingURL=GrokIcon.js.map
package/lib/index.d.ts CHANGED
@@ -165,6 +165,7 @@ export * from './icons/SpinnerIcon/SpinnerIcon';
165
165
  export * from './icons/ChatGptIcon/ChatGptIcon';
166
166
  export * from './icons/CopyIcon/CopyIcon';
167
167
  export * from './icons/ClaudeIcon/ClaudeIcon';
168
+ export * from './icons/GrokIcon/GrokIcon';
168
169
  export * from './icons/CleanIcon/CleanIcon';
169
170
  export * from './icons/GraphqlIcon/GraphqlIcon';
170
171
  export * from './icons/DeselectIcon/DeselectIcon';
package/lib/index.js CHANGED
@@ -229,6 +229,7 @@ __exportStar(require("./icons/SpinnerIcon/SpinnerIcon"), exports);
229
229
  __exportStar(require("./icons/ChatGptIcon/ChatGptIcon"), exports);
230
230
  __exportStar(require("./icons/CopyIcon/CopyIcon"), exports);
231
231
  __exportStar(require("./icons/ClaudeIcon/ClaudeIcon"), exports);
232
+ __exportStar(require("./icons/GrokIcon/GrokIcon"), exports);
232
233
  __exportStar(require("./icons/CleanIcon/CleanIcon"), exports);
233
234
  __exportStar(require("./icons/GraphqlIcon/GraphqlIcon"), exports);
234
235
  __exportStar(require("./icons/DeselectIcon/DeselectIcon"), exports);
@@ -30,6 +30,7 @@ import { icon } from '../markdoc/tags/icon';
30
30
  import { connectMcp } from '../markdoc/tags/connect-mcp';
31
31
  import { loginButton } from '../markdoc/tags/login-button';
32
32
  import { diagram } from '../markdoc/tags/diagram';
33
+ import { visibility } from '../markdoc/tags/visibility';
33
34
  import { prompt } from '../markdoc/tags/prompt';
34
35
  export declare const tags: {
35
36
  [accordion.tagName]: import("./tags/types").MarkdocTagSchema;
@@ -57,5 +58,6 @@ export declare const tags: {
57
58
  [connectMcp.tagName]: import("./tags/types").MarkdocTagSchema;
58
59
  [loginButton.tagName]: import("./tags/types").MarkdocTagSchema;
59
60
  [diagram.tagName]: import("./tags/types").MarkdocTagSchema;
61
+ [visibility.tagName]: import("./tags/types").MarkdocTagSchema;
60
62
  [prompt.tagName]: import("./tags/types").MarkdocTagSchema;
61
63
  };
@@ -72,6 +72,7 @@ const icon_1 = require("../markdoc/tags/icon");
72
72
  const connect_mcp_1 = require("../markdoc/tags/connect-mcp");
73
73
  const login_button_1 = require("../markdoc/tags/login-button");
74
74
  const diagram_1 = require("../markdoc/tags/diagram");
75
+ const visibility_1 = require("../markdoc/tags/visibility");
75
76
  const prompt_1 = require("../markdoc/tags/prompt");
76
77
  exports.tags = {
77
78
  [accordion_1.accordion.tagName]: accordion_1.accordion.schema,
@@ -99,6 +100,7 @@ exports.tags = {
99
100
  [connect_mcp_1.connectMcp.tagName]: connect_mcp_1.connectMcp.schema,
100
101
  [login_button_1.loginButton.tagName]: login_button_1.loginButton.schema,
101
102
  [diagram_1.diagram.tagName]: diagram_1.diagram.schema,
103
+ [visibility_1.visibility.tagName]: visibility_1.visibility.schema,
102
104
  [prompt_1.prompt.tagName]: prompt_1.prompt.schema,
103
105
  };
104
106
  //# sourceMappingURL=default.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.71.0-next.4",
3
+ "version": "0.71.0-next.5",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@babel/core": "7.29.7",
45
45
  "@markdoc/markdoc": "0.5.2",
46
- "@redocly/realm-asyncapi-sdk": "0.16.0",
46
+ "@redocly/realm-asyncapi-sdk": "0.17.0-next.0",
47
47
  "@redocly/tooling-test-utils": "0.0.0",
48
48
  "@shikijs/types": "3.21.0",
49
49
  "@testing-library/react": "16.3.0",
@@ -58,6 +58,7 @@
58
58
  "@vitest/coverage-v8": "4.1.11",
59
59
  "@vitest/ui": "4.1.11",
60
60
  "concurrently": "7.6.0",
61
+ "jest-styled-components": "7.4.0",
61
62
  "react-router": "^8.3.0",
62
63
  "resize-observer-polyfill": "1.5.1",
63
64
  "rimraf": "5.0.7",
@@ -0,0 +1,45 @@
1
+ import React, { useTransition } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+
6
+ import { breakpoints } from '@redocly/theme/core/utils';
7
+ import { Button } from '@redocly/theme/components/Button/Button';
8
+ import { MenuIcon } from '@redocly/theme/icons/MenuIcon/MenuIcon';
9
+ import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
10
+
11
+ export type MobileMenuButtonProps = {
12
+ isOpen: boolean;
13
+ /** Called inside a transition; the button shows the requested state until it commits. */
14
+ onOpenChange: (isOpen: boolean) => void;
15
+ className?: string;
16
+ };
17
+
18
+ export function MobileMenuButton({
19
+ isOpen,
20
+ onOpenChange,
21
+ className,
22
+ }: MobileMenuButtonProps): JSX.Element {
23
+ const [isToggling, startToggle] = useTransition();
24
+ const showClose = isToggling ? !isOpen : isOpen;
25
+
26
+ return (
27
+ <MobileMenuButtonWrapper
28
+ data-component-name="Buttons/MobileMenuButton"
29
+ className={className}
30
+ variant="text"
31
+ data-testid="mobile-menu-button"
32
+ onClick={() => startToggle(() => onOpenChange(!showClose))}
33
+ icon={showClose ? <CloseIcon /> : <MenuIcon />}
34
+ aria-label={showClose ? 'Close menu button' : 'Open menu button'}
35
+ />
36
+ );
37
+ }
38
+
39
+ const MobileMenuButtonWrapper = styled(Button)`
40
+ margin-left: 0px !important;
41
+
42
+ @media screen and (min-width: ${breakpoints.medium}) {
43
+ display: none;
44
+ }
45
+ `;
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
- import type { FilterProps, FilterTypes } from '@redocly/theme/core/types';
5
+ import type { FilterProps } from '@redocly/theme/core/types';
6
6
 
7
7
  import { CatalogFilterSelect } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterSelect';
8
8
  import { CatalogFilterCheckboxes } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterCheckboxes';
@@ -31,7 +31,7 @@ export function CatalogFilter({
31
31
  return null;
32
32
  }
33
33
 
34
- const FilterComponent = filterComponents[(filter.type || 'checkboxes') as FilterTypes];
34
+ const FilterComponent = filterComponents[filter.type || 'checkboxes'];
35
35
 
36
36
  return (
37
37
  <CatalogFilterGroup
@@ -43,9 +43,7 @@ export function Feedback(props: FeedbackProps & { path?: string }) {
43
43
  const renderFeedbackComponent = () => {
44
44
  const { type, settings, path } = feedbackConf;
45
45
 
46
- const FeedbackComponent = feedbackComponents[
47
- type as FeedbackComponentsKeys
48
- ] as FeedbackComponents[FeedbackComponentsKeys];
46
+ const FeedbackComponent = feedbackComponents[type as FeedbackComponentsKeys];
49
47
 
50
48
  if (!FeedbackComponent) {
51
49
  return null;
@@ -19,9 +19,7 @@ import { Search } from '@redocly/theme/components/Search/Search';
19
19
  import { UserMenu } from '@redocly/theme/components/UserMenu/UserMenu';
20
20
  import { LanguagePicker } from '@redocly/theme/components/LanguagePicker/LanguagePicker';
21
21
  import { ProductPicker } from '@redocly/theme/components/Product/ProductPicker';
22
- import { Button } from '@redocly/theme/components/Button/Button';
23
- import { MenuIcon } from '@redocly/theme/icons/MenuIcon/MenuIcon';
24
- import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
22
+ import { MobileMenuButton } from '@redocly/theme/components/Buttons/MobileMenuButton';
25
23
  import { Banner } from '@redocly/theme/components/Banner/Banner';
26
24
 
27
25
  export type NavbarProps = {
@@ -69,25 +67,20 @@ export function Navbar({ className }: NavbarProps): JSX.Element | null {
69
67
  <LanguagePicker onChangeLanguage={changeLanguage} onlyIcon alignment="end" />
70
68
  <ColorModeSwitcher />
71
69
  <MobileMenuButton
72
- variant="text"
73
- data-testid="mobile-menu-button"
74
- onClick={
75
- isOpen
76
- ? () => {
77
- closeMobileMenu();
78
- telemetry.sendMobileMenuClosedMessage([
79
- getBaseDataAttributes('mobileMenuClose', 'button'),
80
- ]);
81
- }
82
- : () => {
83
- openMobileMenu();
84
- telemetry.sendMobileMenuOpenedMessage([
85
- getBaseDataAttributes('mobileMenuOpen', 'button'),
86
- ]);
87
- }
88
- }
89
- icon={isOpen ? <CloseIcon /> : <MenuIcon />}
90
- aria-label={isOpen ? 'Close menu button' : 'Open menu button'}
70
+ isOpen={isOpen}
71
+ onOpenChange={(open) => {
72
+ if (open) {
73
+ openMobileMenu();
74
+ telemetry.sendMobileMenuOpenedMessage([
75
+ getBaseDataAttributes('mobileMenuOpen', 'button'),
76
+ ]);
77
+ } else {
78
+ closeMobileMenu();
79
+ telemetry.sendMobileMenuClosedMessage([
80
+ getBaseDataAttributes('mobileMenuClose', 'button'),
81
+ ]);
82
+ }
83
+ }}
91
84
  />
92
85
  {hideUserMenu ? null : <UserMenu />}
93
86
  </NavbarRow>
@@ -147,11 +140,3 @@ const NavbarRow = styled.div`
147
140
  margin-right: auto;
148
141
  }
149
142
  `;
150
-
151
- const MobileMenuButton = styled(Button)`
152
- margin-left: 0px !important;
153
-
154
- @media screen and (min-width: ${breakpoints.medium}) {
155
- display: none;
156
- }
157
- `;
@@ -2,19 +2,22 @@ import { useEffect } from 'react';
2
2
 
3
3
  import type { RefObject } from 'react';
4
4
 
5
+ const FOCUSABLE_SELECTOR =
6
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
7
+
5
8
  export function useFocusTrap(containerRef: RefObject<HTMLElement | null>): void {
6
9
  useEffect(() => {
7
10
  const trapElement = containerRef.current;
8
11
 
9
12
  if (!trapElement) return;
10
13
 
11
- const focusableElements = trapElement.querySelectorAll<HTMLElement>(
12
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
13
- );
14
- const firstElement = focusableElements[0];
14
+ const getFocusableElements = () =>
15
+ trapElement.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
15
16
 
16
17
  const handleKeyDown = (event: KeyboardEvent) => {
17
18
  if (event.key === 'Tab') {
19
+ const focusableElements = getFocusableElements();
20
+ const firstElement = focusableElements[0];
18
21
  const lastElement = focusableElements[focusableElements.length - 1];
19
22
  if (event.shiftKey && document.activeElement === firstElement) {
20
23
  lastElement?.focus();
@@ -28,9 +31,7 @@ export function useFocusTrap(containerRef: RefObject<HTMLElement | null>): void
28
31
 
29
32
  trapElement.addEventListener('keydown', handleKeyDown);
30
33
 
31
- if (firstElement) {
32
- firstElement.focus();
33
- }
34
+ getFocusableElements()[0]?.focus();
34
35
 
35
36
  return () => {
36
37
  trapElement.removeEventListener('keydown', handleKeyDown);
@@ -1,4 +1,5 @@
1
1
  import { useMemo, useCallback } from 'react';
2
+ import type { ComponentType } from 'react';
2
3
 
3
4
  import type { PageProps, UiAccessibleConfig } from '@redocly/config';
4
5
  import type { PageAction, MCPClientType, McpConnectionParams } from '../types';
@@ -6,6 +7,7 @@ import type { PageAction, MCPClientType, McpConnectionParams } from '../types';
6
7
  import { CopyIcon } from '@redocly/theme/icons/CopyIcon/CopyIcon';
7
8
  import { ChatGptIcon } from '@redocly/theme/icons/ChatGptIcon/ChatGptIcon';
8
9
  import { ClaudeIcon } from '@redocly/theme/icons/ClaudeIcon/ClaudeIcon';
10
+ import { GrokIcon } from '@redocly/theme/icons/GrokIcon/GrokIcon';
9
11
  import { MarkdownFullIcon } from '@redocly/theme/icons/MarkdownFullIcon/MarkdownFullIcon';
10
12
  import { VSCodeIcon } from '@redocly/theme/icons/VSCodeIcon/VSCodeIcon';
11
13
  import { CursorIcon } from '@redocly/theme/icons/CursorIcon/CursorIcon';
@@ -30,6 +32,7 @@ const DEFAULT_ENABLED_ACTIONS = [
30
32
  'view',
31
33
  'chatgpt',
32
34
  'claude',
35
+ 'grok',
33
36
  'docs-mcp-cursor',
34
37
  'docs-mcp-vscode',
35
38
  'docs-mcp-claude',
@@ -40,6 +43,7 @@ export type PageActionType =
40
43
  | 'view'
41
44
  | 'chatgpt'
42
45
  | 'claude'
46
+ | 'grok'
43
47
  | 'docs-mcp-cursor'
44
48
  | 'docs-mcp-vscode'
45
49
  | 'docs-mcp-claude'
@@ -115,6 +119,30 @@ export function usePageActions(
115
119
  : combineUrls(origin, removeTrailingSlash(pathname) + '.md');
116
120
  const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
117
121
 
122
+ const createAiPromptAction =
123
+ (
124
+ target: 'chatgpt' | 'claude' | 'grok',
125
+ iconComponent: ComponentType,
126
+ texts: Pick<PageAction, 'buttonText' | 'title' | 'description'>,
127
+ ) =>
128
+ (): PageAction => {
129
+ const link = getAiPromptLink(target, externalAiPrompt);
130
+ return {
131
+ ...texts,
132
+ iconComponent,
133
+ link,
134
+ onClick: () => {
135
+ telemetry.sendPageActionsClickedMessage([
136
+ {
137
+ ...getBaseDataAttributes('pageActions', 'button'),
138
+ actionType: target,
139
+ },
140
+ ]);
141
+ window.location.href = link;
142
+ },
143
+ };
144
+ };
145
+
118
146
  const actionHandlers: Record<PageActionType, () => PageAction> = {
119
147
  'docs-mcp-cursor': createMCPHandler('cursor', false),
120
148
  'docs-mcp-vscode': createMCPHandler('vscode', false),
@@ -163,45 +191,23 @@ export function usePageActions(
163
191
  },
164
192
  }),
165
193
 
166
- chatgpt: () => {
167
- const link = getAiPromptLink('chatgpt', externalAiPrompt);
168
- return {
169
- buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
170
- title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
171
- description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
172
- iconComponent: ChatGptIcon,
173
- link,
174
- onClick: () => {
175
- telemetry.sendPageActionsClickedMessage([
176
- {
177
- ...getBaseDataAttributes('pageActions', 'button'),
178
- actionType: 'chatgpt',
179
- },
180
- ]);
181
- window.location.href = link;
182
- },
183
- };
184
- },
194
+ chatgpt: createAiPromptAction('chatgpt', ChatGptIcon, {
195
+ buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
196
+ title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
197
+ description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
198
+ }),
185
199
 
186
- claude: () => {
187
- const link = getAiPromptLink('claude', externalAiPrompt);
188
- return {
189
- buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
190
- title: translate('page.actions.claudeTitle', 'Open in Claude'),
191
- description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
192
- iconComponent: ClaudeIcon,
193
- link,
194
- onClick: () => {
195
- telemetry.sendPageActionsClickedMessage([
196
- {
197
- ...getBaseDataAttributes('pageActions', 'button'),
198
- actionType: 'claude',
199
- },
200
- ]);
201
- window.location.href = link;
202
- },
203
- };
204
- },
200
+ claude: createAiPromptAction('claude', ClaudeIcon, {
201
+ buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
202
+ title: translate('page.actions.claudeTitle', 'Open in Claude'),
203
+ description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
204
+ }),
205
+
206
+ grok: createAiPromptAction('grok', GrokIcon, {
207
+ buttonText: translate('page.actions.grokButtonText', 'Open in Grok'),
208
+ title: translate('page.actions.grokTitle', 'Open in Grok'),
209
+ description: translate('page.actions.grokDescription', 'Get insights from Grok'),
210
+ }),
205
211
  };
206
212
 
207
213
  return (themeConfig.navigation?.actions?.items || actions || DEFAULT_ENABLED_ACTIONS)
@@ -311,7 +317,7 @@ function shouldHideAction(
311
317
  return true;
312
318
  }
313
319
 
314
- const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude'].includes(action);
320
+ const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude', 'grok'].includes(action);
315
321
 
316
322
  if (actionDependsOnLlmsTxt) {
317
323
  // Can't use any actions if search is globally disabled (markdown files are not generated)
@@ -345,6 +351,7 @@ function shouldHideActionByType(
345
351
  switch (action) {
346
352
  case 'chatgpt':
347
353
  case 'claude':
354
+ case 'grok':
348
355
  return !isPublic;
349
356
  case 'docs-mcp-cursor':
350
357
  case 'docs-mcp-vscode':
@@ -292,6 +292,9 @@ export type TranslationKey =
292
292
  | 'page.actions.claudeTitle'
293
293
  | 'page.actions.claudeButtonText'
294
294
  | 'page.actions.claudeDescription'
295
+ | 'page.actions.grokTitle'
296
+ | 'page.actions.grokButtonText'
297
+ | 'page.actions.grokDescription'
295
298
  | 'page.actions.cursorMcpButtonText'
296
299
  | 'page.actions.cursorMcpTitle'
297
300
  | 'page.actions.cursorMcpDescription'
@@ -2,9 +2,12 @@ import type { AiPromptTarget } from '../types/prompt';
2
2
 
3
3
  type AiPromptEndpoint = { baseUrl: string; queryParam: string };
4
4
 
5
- const AI_PROMPT_ENDPOINTS: Record<AiPromptTarget, AiPromptEndpoint> = {
5
+ type AiPromptLinkTarget = AiPromptTarget | 'grok';
6
+
7
+ const AI_PROMPT_ENDPOINTS: Record<AiPromptLinkTarget, AiPromptEndpoint> = {
6
8
  chatgpt: { baseUrl: 'https://chat.openai.com', queryParam: 'q' },
7
9
  claude: { baseUrl: 'https://claude.ai/new', queryParam: 'q' },
10
+ grok: { baseUrl: 'https://grok.com/', queryParam: 'q' },
8
11
  // Encoding: https://cursor.com/docs/integrations/deeplinks
9
12
  cursor: { baseUrl: 'https://cursor.com/link/prompt', queryParam: 'text' },
10
13
  };
@@ -22,7 +25,7 @@ const AI_PROMPT_ENDPOINTS: Record<AiPromptTarget, AiPromptEndpoint> = {
22
25
  * // 'https://claude.ai/new?q=Summarize+this+page'
23
26
  * ```
24
27
  */
25
- export function getAiPromptLink(target: AiPromptTarget, prompt: string): string {
28
+ export function getAiPromptLink(target: AiPromptLinkTarget, prompt: string): string {
26
29
  const { baseUrl, queryParam } = AI_PROMPT_ENDPOINTS[target];
27
30
  const url = new URL(baseUrl);
28
31
  url.searchParams.set(queryParam, prompt);
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { IconProps } from '@redocly/theme/icons/types';
5
+
6
+ import { getCssColorVariable } from '@redocly/theme/core/utils';
7
+
8
+ const Icon = (props: IconProps) => (
9
+ <svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
10
+ <path d="M6.6345 9.6347L10.6239 6.6862C10.8195 6.5417 11.0991 6.598 11.1923 6.8226C11.6827 8.0067 11.4636 9.4297 10.4877 10.4067C9.5119 11.3837 8.1541 11.5979 6.9131 11.1099L5.5573 11.7384C7.5019 13.0691 9.8631 12.74 11.3387 11.2617C12.5091 10.0899 12.8716 8.4926 12.5327 7.0522L12.5357 7.0553C12.0442 4.9393 12.6566 4.0935 13.9109 2.364C13.9406 2.3229 13.9703 2.2819 14 2.2399L12.3493 3.8925V3.8874L6.6335 9.6357" />
11
+ <path d="M5.8113 10.3513C4.4156 9.0165 4.6562 6.9507 5.8471 5.7594C6.7277 4.8777 8.1705 4.5179 9.43 5.0469L10.7827 4.4215C10.539 4.2452 10.2266 4.0555 9.8682 3.9223C8.2483 3.2549 6.3089 3.587 4.9921 4.9044C3.7254 6.1726 3.3271 8.1225 4.0111 9.7864C4.5221 11.03 3.6844 11.9096 2.8407 12.7974C2.5417 13.1122 2.2417 13.4269 2 13.7601L5.8102 10.3523" />
12
+ </svg>
13
+ );
14
+
15
+ export const GrokIcon = styled(Icon).attrs({
16
+ 'data-component-name': 'icons/GrokIcon/GrokIcon',
17
+ })`
18
+ path {
19
+ fill: ${({ color }) => getCssColorVariable(color)};
20
+ }
21
+
22
+ height: ${({ size }) => size || '16px'};
23
+ width: ${({ size }) => size || '16px'};
24
+ `;
package/src/index.ts CHANGED
@@ -193,6 +193,7 @@ export * from '@redocly/theme/icons/SpinnerIcon/SpinnerIcon';
193
193
  export * from '@redocly/theme/icons/ChatGptIcon/ChatGptIcon';
194
194
  export * from '@redocly/theme/icons/CopyIcon/CopyIcon';
195
195
  export * from '@redocly/theme/icons/ClaudeIcon/ClaudeIcon';
196
+ export * from '@redocly/theme/icons/GrokIcon/GrokIcon';
196
197
  export * from '@redocly/theme/icons/CleanIcon/CleanIcon';
197
198
  export * from '@redocly/theme/icons/GraphqlIcon/GraphqlIcon';
198
199
  export * from '@redocly/theme/icons/DeselectIcon/DeselectIcon';
@@ -33,6 +33,7 @@ import { icon } from '@redocly/theme/markdoc/tags/icon';
33
33
  import { connectMcp } from '@redocly/theme/markdoc/tags/connect-mcp';
34
34
  import { loginButton } from '@redocly/theme/markdoc/tags/login-button';
35
35
  import { diagram } from '@redocly/theme/markdoc/tags/diagram';
36
+ import { visibility } from '@redocly/theme/markdoc/tags/visibility';
36
37
  import { prompt } from '@redocly/theme/markdoc/tags/prompt';
37
38
 
38
39
  export const tags = {
@@ -61,5 +62,6 @@ export const tags = {
61
62
  [connectMcp.tagName]: connectMcp.schema,
62
63
  [loginButton.tagName]: loginButton.schema,
63
64
  [diagram.tagName]: diagram.schema,
65
+ [visibility.tagName]: visibility.schema,
64
66
  [prompt.tagName]: prompt.schema,
65
67
  };