@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.
- package/lib/components/Buttons/MobileMenuButton.d.ts +8 -0
- package/lib/components/Buttons/MobileMenuButton.js +58 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +1 -1
- package/lib/components/Feedback/Mood.js +1 -1
- package/lib/components/Feedback/Rating.js +1 -1
- package/lib/components/Feedback/Scale.js +1 -1
- package/lib/components/Feedback/Sentiment.js +1 -1
- package/lib/components/Filter/Filter.js +1 -1
- package/lib/components/MCP/McpConnectionTabs.d.ts +1 -1
- package/lib/components/MCP/McpConnectionTabs.js +24 -1
- package/lib/components/MissingTranslationNotice/MissingTranslationNotice.d.ts +11 -0
- package/lib/components/MissingTranslationNotice/MissingTranslationNotice.js +83 -0
- package/lib/components/MissingTranslationNotice/variables.d.ts +1 -0
- package/lib/components/MissingTranslationNotice/variables.dark.d.ts +1 -0
- package/lib/components/MissingTranslationNotice/variables.dark.js +10 -0
- package/lib/components/MissingTranslationNotice/variables.js +29 -0
- package/lib/components/Navbar/Navbar.js +10 -18
- package/lib/components/PageActions/PageActions.js +1 -0
- package/lib/components/Select/Select.js +1 -2
- package/lib/core/hooks/use-active-section-id.js +8 -0
- package/lib/core/hooks/use-focus-trap.js +6 -5
- package/lib/core/hooks/use-page-actions.d.ts +1 -1
- package/lib/core/hooks/use-page-actions.js +47 -35
- package/lib/core/styles/dark.js +4 -2
- package/lib/core/styles/global.js +70 -68
- package/lib/core/types/hooks.d.ts +2 -5
- package/lib/core/types/l10n.d.ts +2 -1
- package/lib/core/utils/get-ai-prompt-link.d.ts +3 -1
- package/lib/core/utils/get-ai-prompt-link.js +1 -0
- package/lib/core/utils/mcp.d.ts +1 -0
- package/lib/core/utils/mcp.js +4 -0
- package/lib/icons/GrokIcon/GrokIcon.d.ts +25 -0
- package/lib/icons/GrokIcon/GrokIcon.js +23 -0
- package/lib/icons/McpIcon/McpIcon.d.ts +25 -0
- package/lib/icons/McpIcon/McpIcon.js +24 -0
- package/lib/icons/TranslateIcon/TranslateIcon.d.ts +25 -0
- package/lib/icons/TranslateIcon/TranslateIcon.js +21 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -0
- package/lib/layouts/CodeWalkthroughLayout.js +9 -1
- package/lib/layouts/DocumentationLayout.js +2 -0
- package/lib/markdoc/default.d.ts +2 -0
- package/lib/markdoc/default.js +2 -0
- package/package.json +4 -3
- package/src/components/Buttons/MobileMenuButton.tsx +45 -0
- package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +2 -2
- package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +1 -1
- package/src/components/Feedback/Feedback.tsx +1 -3
- package/src/components/Feedback/Mood.tsx +1 -1
- package/src/components/Feedback/Rating.tsx +1 -1
- package/src/components/Feedback/Scale.tsx +1 -1
- package/src/components/Feedback/Sentiment.tsx +1 -1
- package/src/components/Filter/Filter.tsx +2 -2
- package/src/components/MCP/McpConnectionTabs.tsx +56 -2
- package/src/components/MissingTranslationNotice/MissingTranslationNotice.tsx +113 -0
- package/src/components/MissingTranslationNotice/variables.dark.ts +7 -0
- package/src/components/MissingTranslationNotice/variables.ts +26 -0
- package/src/components/Navbar/Navbar.tsx +15 -30
- package/src/components/PageActions/PageActions.tsx +1 -0
- package/src/components/Search/FilterFields/SearchFilterFieldSelect.tsx +1 -1
- package/src/components/Select/Select.tsx +1 -2
- package/src/core/hooks/__mocks__/use-theme-hooks.ts +1 -1
- package/src/core/hooks/use-active-section-id.ts +13 -0
- package/src/core/hooks/use-codeblock-tabs-controls.ts +1 -1
- package/src/core/hooks/use-focus-trap.ts +8 -7
- package/src/core/hooks/use-page-actions.ts +73 -43
- package/src/core/styles/dark.ts +2 -0
- package/src/core/styles/global.ts +2 -0
- package/src/core/types/hooks.ts +2 -2
- package/src/core/types/l10n.ts +15 -2
- package/src/core/utils/get-ai-prompt-link.ts +5 -2
- package/src/core/utils/mcp.ts +4 -0
- package/src/icons/GrokIcon/GrokIcon.tsx +24 -0
- package/src/icons/McpIcon/McpIcon.tsx +37 -0
- package/src/icons/TranslateIcon/TranslateIcon.tsx +22 -0
- package/src/index.ts +4 -0
- package/src/layouts/CodeWalkthroughLayout.tsx +11 -1
- package/src/layouts/DocumentationLayout.tsx +2 -0
- package/src/markdoc/default.ts +2 -0
- 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[
|
|
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[
|
|
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
|
}
|
|
@@ -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
|
|
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, {
|
|
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
|
|
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
|
-
|
|
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-
|
|
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
|
-
'
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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':
|