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

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 (80) hide show
  1. package/lib/components/Buttons/MobileMenuButton.d.ts +8 -0
  2. package/lib/components/Buttons/MobileMenuButton.js +58 -0
  3. package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +1 -1
  4. package/lib/components/Feedback/Mood.js +1 -1
  5. package/lib/components/Feedback/Rating.js +1 -1
  6. package/lib/components/Feedback/Scale.js +1 -1
  7. package/lib/components/Feedback/Sentiment.js +1 -1
  8. package/lib/components/Filter/Filter.js +1 -1
  9. package/lib/components/MCP/McpConnectionTabs.d.ts +1 -1
  10. package/lib/components/MCP/McpConnectionTabs.js +24 -1
  11. package/lib/components/MissingTranslationNotice/MissingTranslationNotice.d.ts +11 -0
  12. package/lib/components/MissingTranslationNotice/MissingTranslationNotice.js +83 -0
  13. package/lib/components/MissingTranslationNotice/variables.d.ts +1 -0
  14. package/lib/components/MissingTranslationNotice/variables.dark.d.ts +1 -0
  15. package/lib/components/MissingTranslationNotice/variables.dark.js +10 -0
  16. package/lib/components/MissingTranslationNotice/variables.js +29 -0
  17. package/lib/components/Navbar/Navbar.js +10 -18
  18. package/lib/components/PageActions/PageActions.js +1 -0
  19. package/lib/components/Select/Select.js +1 -2
  20. package/lib/core/hooks/use-active-section-id.js +8 -0
  21. package/lib/core/hooks/use-focus-trap.js +6 -5
  22. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  23. package/lib/core/hooks/use-page-actions.js +47 -35
  24. package/lib/core/styles/dark.js +4 -2
  25. package/lib/core/styles/global.js +70 -68
  26. package/lib/core/types/hooks.d.ts +2 -5
  27. package/lib/core/types/l10n.d.ts +2 -1
  28. package/lib/core/utils/get-ai-prompt-link.d.ts +3 -1
  29. package/lib/core/utils/get-ai-prompt-link.js +1 -0
  30. package/lib/core/utils/mcp.d.ts +1 -0
  31. package/lib/core/utils/mcp.js +4 -0
  32. package/lib/icons/GrokIcon/GrokIcon.d.ts +25 -0
  33. package/lib/icons/GrokIcon/GrokIcon.js +23 -0
  34. package/lib/icons/McpIcon/McpIcon.d.ts +25 -0
  35. package/lib/icons/McpIcon/McpIcon.js +24 -0
  36. package/lib/icons/TranslateIcon/TranslateIcon.d.ts +25 -0
  37. package/lib/icons/TranslateIcon/TranslateIcon.js +21 -0
  38. package/lib/index.d.ts +3 -0
  39. package/lib/index.js +4 -0
  40. package/lib/layouts/CodeWalkthroughLayout.js +9 -1
  41. package/lib/layouts/DocumentationLayout.js +2 -0
  42. package/lib/markdoc/default.d.ts +2 -0
  43. package/lib/markdoc/default.js +2 -0
  44. package/package.json +4 -3
  45. package/src/components/Buttons/MobileMenuButton.tsx +45 -0
  46. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +2 -2
  47. package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +1 -1
  48. package/src/components/Feedback/Feedback.tsx +1 -3
  49. package/src/components/Feedback/Mood.tsx +1 -1
  50. package/src/components/Feedback/Rating.tsx +1 -1
  51. package/src/components/Feedback/Scale.tsx +1 -1
  52. package/src/components/Feedback/Sentiment.tsx +1 -1
  53. package/src/components/Filter/Filter.tsx +2 -2
  54. package/src/components/MCP/McpConnectionTabs.tsx +56 -2
  55. package/src/components/MissingTranslationNotice/MissingTranslationNotice.tsx +113 -0
  56. package/src/components/MissingTranslationNotice/variables.dark.ts +7 -0
  57. package/src/components/MissingTranslationNotice/variables.ts +26 -0
  58. package/src/components/Navbar/Navbar.tsx +15 -30
  59. package/src/components/PageActions/PageActions.tsx +1 -0
  60. package/src/components/Search/FilterFields/SearchFilterFieldSelect.tsx +1 -1
  61. package/src/components/Select/Select.tsx +1 -2
  62. package/src/core/hooks/__mocks__/use-theme-hooks.ts +1 -1
  63. package/src/core/hooks/use-active-section-id.ts +13 -0
  64. package/src/core/hooks/use-codeblock-tabs-controls.ts +1 -1
  65. package/src/core/hooks/use-focus-trap.ts +8 -7
  66. package/src/core/hooks/use-page-actions.ts +73 -43
  67. package/src/core/styles/dark.ts +2 -0
  68. package/src/core/styles/global.ts +2 -0
  69. package/src/core/types/hooks.ts +2 -2
  70. package/src/core/types/l10n.ts +15 -2
  71. package/src/core/utils/get-ai-prompt-link.ts +5 -2
  72. package/src/core/utils/mcp.ts +4 -0
  73. package/src/icons/GrokIcon/GrokIcon.tsx +24 -0
  74. package/src/icons/McpIcon/McpIcon.tsx +37 -0
  75. package/src/icons/TranslateIcon/TranslateIcon.tsx +22 -0
  76. package/src/index.ts +4 -0
  77. package/src/layouts/CodeWalkthroughLayout.tsx +11 -1
  78. package/src/layouts/DocumentationLayout.tsx +2 -0
  79. package/src/markdoc/default.ts +2 -0
  80. package/src/markdoc/tags/code-walkthrough.ts +1 -1
@@ -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
  }
@@ -118,8 +118,8 @@ function Mood({ settings, onSubmit, className }) {
118
118
  const hasOptionalEmail = !(optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.hide) && !(userData === null || userData === void 0 ? void 0 : userData.isAuthenticated);
119
119
  const displayReasons = checkIfShouldDisplayReasons(score);
120
120
  const displayComment = !!score && hasComment;
121
- const displaySubmitBnt = !!(score && (displayReasons || displayComment));
122
121
  const displayFeedbackEmail = !!score && hasOptionalEmail;
122
+ const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
123
123
  const submitMood = (moodScore) => {
124
124
  const trimmedComment = (comment === null || comment === void 0 ? void 0 : comment.trim()) || undefined;
125
125
  const trimmedEmail = (email === null || email === void 0 ? void 0 : email.trim()) || undefined;
@@ -94,8 +94,8 @@ function Rating({ settings, onSubmit, className }) {
94
94
  };
95
95
  const displayReasons = !!score && hasReasons;
96
96
  const displayComment = !!score && hasComment;
97
- const displaySubmitBnt = !!(score && (displayReasons || displayComment));
98
97
  const displayFeedbackEmail = !!score && hasOptionalEmail;
98
+ const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
99
99
  if (isSubmitted) {
100
100
  return (React.createElement(RatingWrapper, { "data-component-name": "Feedback/Rating" },
101
101
  React.createElement(StyledFormMandatoryFields, null,
@@ -98,8 +98,8 @@ function Scale({ settings, onSubmit, className }) {
98
98
  }
99
99
  const displayReasons = !!score && hasReasons;
100
100
  const displayComment = !!score && hasComment;
101
- const displaySubmitBnt = !!score && (displayReasons || displayComment);
102
101
  const displayFeedbackEmail = !!score && hasOptionalEmail;
102
+ const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
103
103
  if (isSubmitted) {
104
104
  return (React.createElement(ScaleWrapper, { "data-component-name": "Feedback/Scale" },
105
105
  React.createElement(Label, { "data-translation-key": "feedback.settings.submitText" }, submitText ||
@@ -85,8 +85,8 @@ function Sentiment({ settings, onSubmit, className }) {
85
85
  const hasOptionalEmail = !(optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.hide) && !(userData === null || userData === void 0 ? void 0 : userData.isAuthenticated);
86
86
  const displayReasons = checkIfShouldDisplayReasons(score);
87
87
  const displayComment = !!score && hasComment;
88
- const displaySubmitBnt = !!(score && (displayReasons || displayComment));
89
88
  const displayFeedbackEmail = !!score && hasOptionalEmail;
89
+ const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
90
90
  const commentLabel = score === 1
91
91
  ? (commentSettings && commentSettings.likeLabel) ||
92
92
  translate('feedback.settings.comment.likeLabel', 'What was most helpful?')
@@ -17,7 +17,7 @@ const filterComponents = {
17
17
  function Filter({ filter, filterValuesCasing, showCounter = true, }) {
18
18
  if (!filter.parentUsed)
19
19
  return null;
20
- const FilterComponent = filterComponents[(filter.type || 'checkboxes')];
20
+ const FilterComponent = filterComponents[filter.type || 'checkboxes'];
21
21
  return (react_1.default.createElement(FilterGroup, { "data-component-name": "Filter/Filter", key: filter.property + filter.title },
22
22
  react_1.default.createElement(FilterComponent, { filter: filter, filterValuesCasing: filterValuesCasing, showCounter: showCounter })));
23
23
  }
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from 'react';
2
- export type McpConnectionTab = 'claude' | 'chatgpt' | 'json';
2
+ export type McpConnectionTab = 'claude' | 'chatgpt' | 'grok' | 'json';
3
3
  export type McpConnectionTabsProps = {
4
4
  options?: McpConnectionTab[];
5
5
  className?: string;
@@ -45,11 +45,12 @@ const Button_1 = require("../../components/Button/Button");
45
45
  const CodeBlock_1 = require("../../components/CodeBlock/CodeBlock");
46
46
  const ClaudeIcon_1 = require("../../icons/ClaudeIcon/ClaudeIcon");
47
47
  const ChatGptIcon_1 = require("../../icons/ChatGptIcon/ChatGptIcon");
48
+ const GrokIcon_1 = require("../../icons/GrokIcon/GrokIcon");
48
49
  const Tabs_1 = require("../../markdoc/components/Tabs/Tabs");
49
50
  const TabItemFragment_1 = require("../../markdoc/components/Tabs/TabItemFragment");
50
51
  const NumberedList_1 = require("../../markdoc/components/NumberedList/NumberedList");
51
52
  const NumberedItem_1 = require("../../markdoc/components/NumberedList/NumberedItem");
52
- const DEFAULT_TAB_OPTIONS = ['claude', 'chatgpt', 'json'];
53
+ const DEFAULT_TAB_OPTIONS = ['claude', 'chatgpt', 'grok', 'json'];
53
54
  const TAB_LABELS = {
54
55
  claude: {
55
56
  translationKey: 'page.mcpNotAllowed.tabs.claude',
@@ -61,8 +62,14 @@ const TAB_LABELS = {
61
62
  defaultLabel: 'ChatGPT',
62
63
  icon: ChatGptIcon_1.ChatGptIcon,
63
64
  },
65
+ grok: {
66
+ translationKey: 'page.mcpNotAllowed.tabs.grok',
67
+ defaultLabel: 'Grok',
68
+ icon: GrokIcon_1.GrokIcon,
69
+ },
64
70
  json: { translationKey: 'page.mcpNotAllowed.tabs.json', defaultLabel: 'JSON' },
65
71
  };
72
+ const GROK_CONNECTORS_URL = 'https://grok.com/connectors';
66
73
  function McpConnectionTabs({ options = DEFAULT_TAB_OPTIONS, className, }) {
67
74
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
68
75
  const { translate } = useTranslate();
@@ -136,6 +143,22 @@ function renderTabContent(option, connection, translate, snippetHeader) {
136
143
  },
137
144
  assistantPromptClient,
138
145
  ]);
146
+ case 'grok':
147
+ return renderClientList([
148
+ {
149
+ headline: translate('page.mcpNotAllowed.snippets.grokBuild.headline', 'Grok Build'),
150
+ content: (react_1.default.createElement(react_1.default.Fragment, null,
151
+ terminalHint,
152
+ react_1.default.createElement(CodeBlock_1.CodeBlock, { lang: "shell", source: (0, utils_1.generateGrokBuildCommand)(connection), header: snippetHeader(option, 'grok-build'), wrapContents: true }))),
153
+ },
154
+ {
155
+ headline: translate('page.mcpNotAllowed.snippets.grokApp.headline', 'Grok app'),
156
+ content: (react_1.default.createElement(react_1.default.Fragment, null,
157
+ react_1.default.createElement(ConnectButton, { variant: "secondary", icon: react_1.default.createElement(GrokIcon_1.GrokIcon, null), onClick: () => window.open(GROK_CONNECTORS_URL, '_blank') }, translate('page.mcpNotAllowed.snippets.grokApp.openConnectors', 'Open Grok connectors')),
158
+ react_1.default.createElement(TabHint, null, translate('page.mcpNotAllowed.snippets.grokApp.instruction', 'On grok.com, open Connectors → New Connector → Custom and paste this server URL:')),
159
+ react_1.default.createElement(CodeBlock_1.CodeBlock, { lang: "text", source: connection.url, header: snippetHeader(option, 'grok-app') }))),
160
+ },
161
+ ]);
139
162
  case 'json':
140
163
  return (react_1.default.createElement(react_1.default.Fragment, null,
141
164
  react_1.default.createElement(TabHint, null, translate('page.mcpNotAllowed.snippets.json.hint', 'Add this server entry to your MCP client configuration file:')),
@@ -0,0 +1,11 @@
1
+ import type { JSX } from 'react';
2
+ export type MissingTranslationNoticeProps = {
3
+ className?: string;
4
+ };
5
+ /**
6
+ * Tells the reader that the page shows the original language,
7
+ * because the translation for the selected locale is missing.
8
+ * Renders only on fallback pages of a locale that sets `showMissingTranslationNotice`.
9
+ * The reader can close the notice for the current page.
10
+ */
11
+ export declare function MissingTranslationNotice({ className, }: MissingTranslationNoticeProps): JSX.Element | null;
@@ -0,0 +1,83 @@
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.MissingTranslationNotice = MissingTranslationNotice;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const CloseIcon_1 = require("../../icons/CloseIcon/CloseIcon");
10
+ const TranslateIcon_1 = require("../../icons/TranslateIcon/TranslateIcon");
11
+ const Markdown_1 = require("../../components/Markdown/Markdown");
12
+ const hooks_1 = require("../../core/hooks");
13
+ /**
14
+ * Tells the reader that the page shows the original language,
15
+ * because the translation for the selected locale is missing.
16
+ * Renders only on fallback pages of a locale that sets `showMissingTranslationNotice`.
17
+ * The reader can close the notice for the current page.
18
+ */
19
+ function MissingTranslationNotice({ className, }) {
20
+ var _a;
21
+ const { useTranslate, useMarkdownText, usePageProps, useL10nConfig } = (0, hooks_1.useThemeHooks)();
22
+ const { translate } = useTranslate();
23
+ const { isTranslationFallback } = usePageProps();
24
+ const { currentLocale, defaultLocale, locales } = useL10nConfig();
25
+ const [isDismissed, setIsDismissed] = react_1.default.useState(false);
26
+ const isShown = isTranslationFallback === true &&
27
+ currentLocale !== defaultLocale &&
28
+ locales.some(({ code, showMissingTranslationNotice }) => code === currentLocale && showMissingTranslationNotice === true);
29
+ const language = ((_a = locales.find(({ code }) => code === defaultLocale)) === null || _a === void 0 ? void 0 : _a.name) || defaultLocale;
30
+ const content = useMarkdownText(isShown ? translate('missingTranslationNotice.content', { language }) : '');
31
+ if (!isShown || isDismissed) {
32
+ return null;
33
+ }
34
+ return (react_1.default.createElement(MissingTranslationNoticeWrapper, { className: className, "data-component-name": "MissingTranslationNotice/MissingTranslationNotice" },
35
+ react_1.default.createElement(Icon, { color: "--missing-translation-notice-icon-color" }),
36
+ react_1.default.createElement(Content, null,
37
+ react_1.default.createElement(Title, null, translate('missingTranslationNotice.title', {
38
+ defaultValue: 'This page has not yet been translated',
39
+ })),
40
+ react_1.default.createElement(Markdown_1.Markdown, { compact: true }, content)),
41
+ react_1.default.createElement(CloseButton, { type: "button", "aria-label": translate('missingTranslationNotice.close', { defaultValue: 'Close' }), onClick: () => setIsDismissed(true) },
42
+ react_1.default.createElement(CloseIcon_1.CloseIcon, { color: "--missing-translation-notice-text-color" }))));
43
+ }
44
+ const MissingTranslationNoticeWrapper = styled_components_1.default.div `
45
+ display: flex;
46
+ flex-direction: row;
47
+ align-items: flex-start;
48
+ gap: var(--missing-translation-notice-gap);
49
+
50
+ padding: var(--missing-translation-notice-padding);
51
+ margin: var(--missing-translation-notice-margin-vertical) 0;
52
+ color: var(--missing-translation-notice-text-color);
53
+ background-color: var(--missing-translation-notice-bg-color);
54
+ border: 1px solid var(--missing-translation-notice-border-color);
55
+ border-left: var(--missing-translation-notice-accent-width) solid
56
+ var(--missing-translation-notice-accent-color);
57
+ border-radius: var(--missing-translation-notice-border-radius);
58
+ `;
59
+ const Icon = (0, styled_components_1.default)(TranslateIcon_1.TranslateIcon) `
60
+ flex-shrink: 0;
61
+ `;
62
+ const Content = styled_components_1.default.div `
63
+ display: flex;
64
+ flex-direction: column;
65
+ flex: 1;
66
+ gap: var(--spacing-xxs);
67
+
68
+ font-size: var(--missing-translation-notice-font-size);
69
+ line-height: var(--missing-translation-notice-line-height);
70
+ `;
71
+ const Title = styled_components_1.default.span `
72
+ font-size: var(--missing-translation-notice-title-font-size);
73
+ font-weight: var(--missing-translation-notice-title-font-weight);
74
+ `;
75
+ const CloseButton = styled_components_1.default.button `
76
+ display: flex;
77
+ flex-shrink: 0;
78
+ padding: 0;
79
+ border: none;
80
+ background: none;
81
+ cursor: pointer;
82
+ `;
83
+ //# sourceMappingURL=MissingTranslationNotice.js.map
@@ -0,0 +1 @@
1
+ export declare const missingTranslationNotice: import("styled-components").RuleSet<object>;
@@ -0,0 +1 @@
1
+ export declare const missingTranslationNoticeDarkMode: import("styled-components").RuleSet<object>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.missingTranslationNoticeDarkMode = void 0;
4
+ const styled_components_1 = require("styled-components");
5
+ exports.missingTranslationNoticeDarkMode = (0, styled_components_1.css) `
6
+ --missing-translation-notice-bg-color: var(--color-warm-grey-3); // @presenter Color
7
+ --missing-translation-notice-icon-color: var(--color-blue-4); // @presenter Color
8
+ --missing-translation-notice-accent-color: var(--color-blue-4); // @presenter Color
9
+ `;
10
+ //# sourceMappingURL=variables.dark.js.map
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.missingTranslationNotice = void 0;
4
+ const styled_components_1 = require("styled-components");
5
+ exports.missingTranslationNotice = (0, styled_components_1.css) `
6
+ /**
7
+ * @tokens Missing translation notice
8
+ */
9
+
10
+ --missing-translation-notice-bg-color: var(--color-warm-grey-1); // @presenter Color
11
+ --missing-translation-notice-text-color: var(--text-color-primary); // @presenter Color
12
+ --missing-translation-notice-icon-color: var(--color-blue-6); // @presenter Color
13
+ --missing-translation-notice-border-color: var(--border-color-secondary); // @presenter Color
14
+ --missing-translation-notice-accent-color: var(--color-blue-6); // @presenter Color
15
+
16
+ --missing-translation-notice-accent-width: 3px; // @presenter Spacing
17
+ --missing-translation-notice-border-radius: var(--border-radius-lg); // @presenter BorderRadius
18
+ --missing-translation-notice-padding: var(--spacing-sm) var(--spacing-base); // @presenter Spacing
19
+ --missing-translation-notice-margin-vertical: var(--spacing-base); // @presenter Spacing
20
+ --missing-translation-notice-gap: var(--spacing-sm); // @presenter Spacing
21
+
22
+ --missing-translation-notice-title-font-size: var(--font-size-base); // @presenter FontSize
23
+ --missing-translation-notice-title-font-weight: var(--font-weight-semibold); // @presenter FontWeight
24
+ --missing-translation-notice-font-size: var(--font-size-base); // @presenter FontSize
25
+ --missing-translation-notice-line-height: var(--line-height-base); // @presenter LineHeight
26
+
27
+ // @tokens End
28
+ `;
29
+ //# sourceMappingURL=variables.js.map
@@ -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
@@ -102,6 +102,7 @@ function renderIcon(buttonAction, actionState) {
102
102
  const PageActionsWrapper = styled_components_1.default.div `
103
103
  margin-left: auto;
104
104
  padding-left: var(--page-actions-padding-left);
105
+ user-select: none;
105
106
  --button-color: var(--page-actions-button-text-color);
106
107
 
107
108
  .button-group-size-medium .button.button-size-medium {
@@ -55,8 +55,7 @@ function Select(props) {
55
55
  return values
56
56
  .map((value) => {
57
57
  const selectedOption = options.find((option) => option.value === value);
58
- return (selectedOption ||
59
- (typeof value === 'string' ? { value } : value));
58
+ return (selectedOption || (typeof value === 'string' ? { value } : value));
60
59
  })
61
60
  .filter((option) => !!option);
62
61
  };
@@ -46,7 +46,14 @@ function useActiveSectionId(location, hasOverviewPage = false, withNavbar = true
46
46
  if (!dom_1.IS_BROWSER) {
47
47
  return;
48
48
  }
49
+ // A one-event scroll jump is read before the section it lands on stops being skipped.
50
+ const onContentVisibilityChange = (event) => {
51
+ if ('skipped' in event && event.skipped === false) {
52
+ scrollListener();
53
+ }
54
+ };
49
55
  window.addEventListener('scroll', scrollListener, { capture: false });
56
+ window.addEventListener('contentvisibilityautostatechange', onContentVisibilityChange, true);
50
57
  const initialRead = setTimeout(() => {
51
58
  scrollListener();
52
59
  }, 10);
@@ -54,6 +61,7 @@ function useActiveSectionId(location, hasOverviewPage = false, withNavbar = true
54
61
  clearTimeout(initialRead);
55
62
  scrollListener.cancel();
56
63
  window.removeEventListener('scroll', scrollListener);
64
+ window.removeEventListener('contentvisibilityautostatechange', onContentVisibilityChange, true);
57
65
  };
58
66
  }, [location, heightOffset, scrollListener]);
59
67
  return itemId;
@@ -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-claude' | 'docs-mcp-vscode' | 'docs-mcp-cursor' | 'copy-mcp-url' | 'mcp-cursor' | 'mcp-vscode';
3
3
  export declare function usePageActions(pageSlug: string, mcpUrl?: string, actions?: PageActionType[]): PageAction[];
@@ -14,6 +14,8 @@ 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");
18
+ const McpIcon_1 = require("../../icons/McpIcon/McpIcon");
17
19
  const MarkdownFullIcon_1 = require("../../icons/MarkdownFullIcon/MarkdownFullIcon");
18
20
  const VSCodeIcon_1 = require("../../icons/VSCodeIcon/VSCodeIcon");
19
21
  const CursorIcon_1 = require("../../icons/CursorIcon/CursorIcon");
@@ -31,9 +33,11 @@ const DEFAULT_ENABLED_ACTIONS = [
31
33
  'view',
32
34
  'chatgpt',
33
35
  'claude',
34
- 'docs-mcp-cursor',
35
- 'docs-mcp-vscode',
36
+ 'grok',
36
37
  'docs-mcp-claude',
38
+ 'docs-mcp-vscode',
39
+ 'docs-mcp-cursor',
40
+ 'copy-mcp-url',
37
41
  ];
38
42
  function usePageActions(pageSlug, mcpUrl, actions) {
39
43
  var _a;
@@ -84,10 +88,32 @@ function usePageActions(pageSlug, mcpUrl, actions) {
84
88
  ? (0, urls_1.combineUrls)(origin, pathname, 'index.html.md')
85
89
  : (0, urls_1.combineUrls)(origin, (0, urls_1.removeTrailingSlash)(pathname) + '.md');
86
90
  const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
91
+ const createAiPromptAction = (target, iconComponent, texts) => () => {
92
+ const link = (0, get_ai_prompt_link_1.getAiPromptLink)(target, externalAiPrompt);
93
+ return Object.assign(Object.assign({}, texts), { iconComponent,
94
+ link, onClick: () => {
95
+ telemetry.sendPageActionsClickedMessage([
96
+ Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('pageActions', 'button')), { actionType: target }),
97
+ ]);
98
+ window.location.href = link;
99
+ } });
100
+ };
87
101
  const actionHandlers = {
88
102
  'docs-mcp-cursor': createMCPHandler('cursor', false),
89
103
  'docs-mcp-vscode': createMCPHandler('vscode', false),
90
104
  'docs-mcp-claude': createMCPHandler('claude', false),
105
+ 'copy-mcp-url': () => ({
106
+ buttonText: translate('page.actions.connectMcp.copyUrl', 'Copy MCP server URL'),
107
+ title: translate('page.actions.connectMcp.copyUrl', 'Copy MCP server URL'),
108
+ description: translate('page.actions.connectMcp.copyUrlDescription', 'Copy the MCP server URL to the clipboard'),
109
+ iconComponent: McpIcon_1.McpIcon,
110
+ onClick: () => {
111
+ clipboard_service_1.ClipboardService.copyCustom(mcpConfig.serverUrl);
112
+ telemetry.sendPageActionsClickedMessage([
113
+ Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('pageActions', 'button')), { actionType: 'copy-mcp-url' }),
114
+ ]);
115
+ },
116
+ }),
91
117
  'mcp-cursor': createMCPHandler('cursor', true),
92
118
  'mcp-vscode': createMCPHandler('vscode', true),
93
119
  copy: () => ({
@@ -124,38 +150,21 @@ function usePageActions(pageSlug, mcpUrl, actions) {
124
150
  ]);
125
151
  },
126
152
  }),
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
- },
153
+ chatgpt: createAiPromptAction('chatgpt', ChatGptIcon_1.ChatGptIcon, {
154
+ buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
155
+ title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
156
+ description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
157
+ }),
158
+ claude: createAiPromptAction('claude', ClaudeIcon_1.ClaudeIcon, {
159
+ buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
160
+ title: translate('page.actions.claudeTitle', 'Open in Claude'),
161
+ description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
162
+ }),
163
+ grok: createAiPromptAction('grok', GrokIcon_1.GrokIcon, {
164
+ buttonText: translate('page.actions.grokButtonText', 'Open in Grok'),
165
+ title: translate('page.actions.grokTitle', 'Open in Grok'),
166
+ description: translate('page.actions.grokDescription', 'Get insights from Grok'),
167
+ }),
159
168
  };
160
169
  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
170
  .filter((action) => !shouldHideAction(action, hideActionContext))
@@ -168,6 +177,7 @@ function usePageActions(pageSlug, mcpUrl, actions) {
168
177
  isPublic,
169
178
  mcpUrl,
170
179
  mcpConfig.isMcpDisabled,
180
+ mcpConfig.serverUrl,
171
181
  pageSlug,
172
182
  actions,
173
183
  translate,
@@ -198,7 +208,7 @@ function shouldHideAction(action, { pageProps, themeConfig, apiDocsExcludeFromSe
198
208
  if ((_b = (_a = themeConfig.navigation) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.hide) {
199
209
  return true;
200
210
  }
201
- const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude'].includes(action);
211
+ const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude', 'grok'].includes(action);
202
212
  if (actionDependsOnLlmsTxt) {
203
213
  // Can't use any actions if search is globally disabled (markdown files are not generated)
204
214
  if ((_c = themeConfig.search) === null || _c === void 0 ? void 0 : _c.hide) {
@@ -219,10 +229,12 @@ function shouldHideActionByType(action, { isPublic, mcpUrl, isMcpDisabled, }) {
219
229
  switch (action) {
220
230
  case 'chatgpt':
221
231
  case 'claude':
232
+ case 'grok':
222
233
  return !isPublic;
223
234
  case 'docs-mcp-cursor':
224
235
  case 'docs-mcp-vscode':
225
236
  case 'docs-mcp-claude':
237
+ case 'copy-mcp-url':
226
238
  return Boolean(mcpUrl) || isMcpDisabled;
227
239
  case 'mcp-cursor':
228
240
  case 'mcp-vscode':