@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
|
@@ -45,6 +45,7 @@ const constants_1 = require("../core/constants");
|
|
|
45
45
|
const CodeSnippetContext_1 = require("../core/contexts/CodeSnippetContext");
|
|
46
46
|
const DocumentationLayoutTop_1 = require("../layouts/DocumentationLayoutTop");
|
|
47
47
|
const DocumentationLayoutBottom_1 = require("../layouts/DocumentationLayoutBottom");
|
|
48
|
+
const MissingTranslationNotice_1 = require("../components/MissingTranslationNotice/MissingTranslationNotice");
|
|
48
49
|
function DocumentationLayout({ tableOfContent, feedback, config, editPage, lastModified, nextPage, prevPage, className, children, }) {
|
|
49
50
|
var _a, _b;
|
|
50
51
|
const { markdown } = (0, hooks_1.useThemeConfig)();
|
|
@@ -53,6 +54,7 @@ function DocumentationLayout({ tableOfContent, feedback, config, editPage, lastM
|
|
|
53
54
|
react_1.default.createElement(LayoutWrapper, { "data-component-name": "Layout/DocumentationLayout", className: className, $withToc: withToc },
|
|
54
55
|
react_1.default.createElement(ContentWrapper, null,
|
|
55
56
|
react_1.default.createElement(DocumentationLayoutTop_1.DocumentationLayoutTop, { config: config, editPage: editPage, lastModified: lastModified }),
|
|
57
|
+
react_1.default.createElement(MissingTranslationNotice_1.MissingTranslationNotice, null),
|
|
56
58
|
children,
|
|
57
59
|
react_1.default.createElement(DocumentationLayoutBottom_1.DocumentationLayoutBottom, { feedback: feedback, nextPage: nextPage, prevPage: prevPage })),
|
|
58
60
|
tableOfContent)));
|
package/lib/markdoc/default.d.ts
CHANGED
|
@@ -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
|
};
|
package/lib/markdoc/default.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.71.0-next.6",
|
|
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.
|
|
46
|
+
"@redocly/realm-asyncapi-sdk": "0.17.0-next.1",
|
|
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",
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
"webpack": "5.105.2"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
|
-
"@redocly/config": "0.
|
|
73
|
+
"@redocly/config": "0.67.0",
|
|
73
74
|
"@tanstack/react-query": "5.62.3",
|
|
74
75
|
"@tanstack/react-virtual": "3.13.0",
|
|
75
76
|
"@xyflow/react": "12.8.3",
|
|
@@ -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
|
|
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[
|
|
34
|
+
const FilterComponent = filterComponents[filter.type || 'checkboxes'];
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<CatalogFilterGroup
|
|
@@ -21,7 +21,7 @@ export type CatalogClassicInfoBlockProps = {
|
|
|
21
21
|
export function CatalogClassicInfoBlock(props: CatalogClassicInfoBlockProps): JSX.Element {
|
|
22
22
|
const scorecardBadge = props.metadata?.scorecardLevel ? (
|
|
23
23
|
<ScorecardBadge
|
|
24
|
-
level={props.metadata.scorecardLevel
|
|
24
|
+
level={props.metadata.scorecardLevel}
|
|
25
25
|
slug={props.metadata?.scoreCardSlug as string}
|
|
26
26
|
colorVariable={getScorecardColorVariable(
|
|
27
27
|
props.metadata?.scorecardLevelIdx || 0,
|
|
@@ -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;
|
|
@@ -134,8 +134,8 @@ export function Mood({ settings, onSubmit, className }: MoodProps): JSX.Element
|
|
|
134
134
|
|
|
135
135
|
const displayReasons = checkIfShouldDisplayReasons(score);
|
|
136
136
|
const displayComment = !!score && hasComment;
|
|
137
|
-
const displaySubmitBnt = !!(score && (displayReasons || displayComment));
|
|
138
137
|
const displayFeedbackEmail = !!score && hasOptionalEmail;
|
|
138
|
+
const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
|
|
139
139
|
|
|
140
140
|
const submitMood = (moodScore: string) => {
|
|
141
141
|
const trimmedComment = comment?.trim() || undefined;
|
|
@@ -101,8 +101,8 @@ export function Rating({ settings, onSubmit, className }: RatingProps): JSX.Elem
|
|
|
101
101
|
|
|
102
102
|
const displayReasons = !!score && hasReasons;
|
|
103
103
|
const displayComment = !!score && hasComment;
|
|
104
|
-
const displaySubmitBnt = !!(score && (displayReasons || displayComment));
|
|
105
104
|
const displayFeedbackEmail = !!score && hasOptionalEmail;
|
|
105
|
+
const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
|
|
106
106
|
|
|
107
107
|
if (isSubmitted) {
|
|
108
108
|
return (
|
|
@@ -121,8 +121,8 @@ export function Scale({ settings, onSubmit, className }: ScaleProps): JSX.Elemen
|
|
|
121
121
|
|
|
122
122
|
const displayReasons = !!score && hasReasons;
|
|
123
123
|
const displayComment = !!score && hasComment;
|
|
124
|
-
const displaySubmitBnt = !!score && (displayReasons || displayComment);
|
|
125
124
|
const displayFeedbackEmail = !!score && hasOptionalEmail;
|
|
125
|
+
const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
|
|
126
126
|
|
|
127
127
|
if (isSubmitted) {
|
|
128
128
|
return (
|
|
@@ -90,8 +90,8 @@ export function Sentiment({ settings, onSubmit, className }: SentimentProps): JS
|
|
|
90
90
|
|
|
91
91
|
const displayReasons = checkIfShouldDisplayReasons(score);
|
|
92
92
|
const displayComment = !!score && hasComment;
|
|
93
|
-
const displaySubmitBnt = !!(score && (displayReasons || displayComment));
|
|
94
93
|
const displayFeedbackEmail = !!score && hasOptionalEmail;
|
|
94
|
+
const displaySubmitBnt = !!score && (displayReasons || displayComment || displayFeedbackEmail);
|
|
95
95
|
|
|
96
96
|
const commentLabel =
|
|
97
97
|
score === 1
|
|
@@ -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
|
|
5
|
+
import type { FilterProps } from '@redocly/theme/core/types';
|
|
6
6
|
|
|
7
7
|
import { FilterSelect } from '@redocly/theme/components/Filter/FilterSelect';
|
|
8
8
|
import { FilterCheckboxes } from '@redocly/theme/components/Filter/FilterCheckboxes';
|
|
@@ -21,7 +21,7 @@ export function Filter({
|
|
|
21
21
|
}: FilterProps): JSX.Element | null {
|
|
22
22
|
if (!filter.parentUsed) return null;
|
|
23
23
|
|
|
24
|
-
const FilterComponent = filterComponents[
|
|
24
|
+
const FilterComponent = filterComponents[filter.type || 'checkboxes'];
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<FilterGroup data-component-name="Filter/Filter" key={filter.property + filter.title}>
|
|
@@ -10,6 +10,7 @@ import { useMCPConfig, useThemeHooks } from '@redocly/theme/core/hooks';
|
|
|
10
10
|
import {
|
|
11
11
|
generateClaudeCodeCommand,
|
|
12
12
|
generateCodexCommand,
|
|
13
|
+
generateGrokBuildCommand,
|
|
13
14
|
generateMCPDeepLink,
|
|
14
15
|
generateMcpInstallPrompt,
|
|
15
16
|
getBaseDataAttributes,
|
|
@@ -18,19 +19,20 @@ import { Button } from '@redocly/theme/components/Button/Button';
|
|
|
18
19
|
import { CodeBlock } from '@redocly/theme/components/CodeBlock/CodeBlock';
|
|
19
20
|
import { ClaudeIcon } from '@redocly/theme/icons/ClaudeIcon/ClaudeIcon';
|
|
20
21
|
import { ChatGptIcon } from '@redocly/theme/icons/ChatGptIcon/ChatGptIcon';
|
|
22
|
+
import { GrokIcon } from '@redocly/theme/icons/GrokIcon/GrokIcon';
|
|
21
23
|
import { Tabs, TabsSize } from '@redocly/theme/markdoc/components/Tabs/Tabs';
|
|
22
24
|
import { TabItemFragment } from '@redocly/theme/markdoc/components/Tabs/TabItemFragment';
|
|
23
25
|
import { NumberedList } from '@redocly/theme/markdoc/components/NumberedList/NumberedList';
|
|
24
26
|
import { NumberedItem } from '@redocly/theme/markdoc/components/NumberedList/NumberedItem';
|
|
25
27
|
|
|
26
|
-
export type McpConnectionTab = 'claude' | 'chatgpt' | 'json';
|
|
28
|
+
export type McpConnectionTab = 'claude' | 'chatgpt' | 'grok' | 'json';
|
|
27
29
|
|
|
28
30
|
export type McpConnectionTabsProps = {
|
|
29
31
|
options?: McpConnectionTab[];
|
|
30
32
|
className?: string;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
const DEFAULT_TAB_OPTIONS: McpConnectionTab[] = ['claude', 'chatgpt', 'json'];
|
|
35
|
+
const DEFAULT_TAB_OPTIONS: McpConnectionTab[] = ['claude', 'chatgpt', 'grok', 'json'];
|
|
34
36
|
|
|
35
37
|
const TAB_LABELS: Record<
|
|
36
38
|
McpConnectionTab,
|
|
@@ -46,9 +48,16 @@ const TAB_LABELS: Record<
|
|
|
46
48
|
defaultLabel: 'ChatGPT',
|
|
47
49
|
icon: ChatGptIcon,
|
|
48
50
|
},
|
|
51
|
+
grok: {
|
|
52
|
+
translationKey: 'page.mcpNotAllowed.tabs.grok',
|
|
53
|
+
defaultLabel: 'Grok',
|
|
54
|
+
icon: GrokIcon,
|
|
55
|
+
},
|
|
49
56
|
json: { translationKey: 'page.mcpNotAllowed.tabs.json', defaultLabel: 'JSON' },
|
|
50
57
|
};
|
|
51
58
|
|
|
59
|
+
const GROK_CONNECTORS_URL = 'https://grok.com/connectors';
|
|
60
|
+
|
|
52
61
|
type TabClient = { headline: string; content: ReactNode };
|
|
53
62
|
|
|
54
63
|
type SnippetId =
|
|
@@ -235,6 +244,51 @@ function renderTabContent(
|
|
|
235
244
|
},
|
|
236
245
|
assistantPromptClient,
|
|
237
246
|
]);
|
|
247
|
+
case 'grok':
|
|
248
|
+
return renderClientList([
|
|
249
|
+
{
|
|
250
|
+
headline: translate('page.mcpNotAllowed.snippets.grokBuild.headline', 'Grok Build'),
|
|
251
|
+
content: (
|
|
252
|
+
<>
|
|
253
|
+
{terminalHint}
|
|
254
|
+
<CodeBlock
|
|
255
|
+
lang="shell"
|
|
256
|
+
source={generateGrokBuildCommand(connection)}
|
|
257
|
+
header={snippetHeader(option, 'grok-build')}
|
|
258
|
+
wrapContents
|
|
259
|
+
/>
|
|
260
|
+
</>
|
|
261
|
+
),
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
headline: translate('page.mcpNotAllowed.snippets.grokApp.headline', 'Grok app'),
|
|
265
|
+
content: (
|
|
266
|
+
<>
|
|
267
|
+
<ConnectButton
|
|
268
|
+
variant="secondary"
|
|
269
|
+
icon={<GrokIcon />}
|
|
270
|
+
onClick={() => window.open(GROK_CONNECTORS_URL, '_blank')}
|
|
271
|
+
>
|
|
272
|
+
{translate(
|
|
273
|
+
'page.mcpNotAllowed.snippets.grokApp.openConnectors',
|
|
274
|
+
'Open Grok connectors',
|
|
275
|
+
)}
|
|
276
|
+
</ConnectButton>
|
|
277
|
+
<TabHint>
|
|
278
|
+
{translate(
|
|
279
|
+
'page.mcpNotAllowed.snippets.grokApp.instruction',
|
|
280
|
+
'On grok.com, open Connectors → New Connector → Custom and paste this server URL:',
|
|
281
|
+
)}
|
|
282
|
+
</TabHint>
|
|
283
|
+
<CodeBlock
|
|
284
|
+
lang="text"
|
|
285
|
+
source={connection.url}
|
|
286
|
+
header={snippetHeader(option, 'grok-app')}
|
|
287
|
+
/>
|
|
288
|
+
</>
|
|
289
|
+
),
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
238
292
|
case 'json':
|
|
239
293
|
return (
|
|
240
294
|
<>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
|
|
6
|
+
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
|
|
7
|
+
import { TranslateIcon } from '@redocly/theme/icons/TranslateIcon/TranslateIcon';
|
|
8
|
+
import { Markdown } from '@redocly/theme/components/Markdown/Markdown';
|
|
9
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
10
|
+
|
|
11
|
+
export type MissingTranslationNoticeProps = {
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tells the reader that the page shows the original language,
|
|
17
|
+
* because the translation for the selected locale is missing.
|
|
18
|
+
* Renders only on fallback pages of a locale that sets `showMissingTranslationNotice`.
|
|
19
|
+
* The reader can close the notice for the current page.
|
|
20
|
+
*/
|
|
21
|
+
export function MissingTranslationNotice({
|
|
22
|
+
className,
|
|
23
|
+
}: MissingTranslationNoticeProps): JSX.Element | null {
|
|
24
|
+
const { useTranslate, useMarkdownText, usePageProps, useL10nConfig } = useThemeHooks();
|
|
25
|
+
const { translate } = useTranslate();
|
|
26
|
+
const { isTranslationFallback } = usePageProps<{ isTranslationFallback?: boolean }>();
|
|
27
|
+
const { currentLocale, defaultLocale, locales } = useL10nConfig();
|
|
28
|
+
const [isDismissed, setIsDismissed] = React.useState(false);
|
|
29
|
+
|
|
30
|
+
const isShown =
|
|
31
|
+
isTranslationFallback === true &&
|
|
32
|
+
currentLocale !== defaultLocale &&
|
|
33
|
+
locales.some(
|
|
34
|
+
({ code, showMissingTranslationNotice }) =>
|
|
35
|
+
code === currentLocale && showMissingTranslationNotice === true,
|
|
36
|
+
);
|
|
37
|
+
const language = locales.find(({ code }) => code === defaultLocale)?.name || defaultLocale;
|
|
38
|
+
const content = useMarkdownText(
|
|
39
|
+
isShown ? translate('missingTranslationNotice.content', { language }) : '',
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!isShown || isDismissed) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<MissingTranslationNoticeWrapper
|
|
48
|
+
className={className}
|
|
49
|
+
data-component-name="MissingTranslationNotice/MissingTranslationNotice"
|
|
50
|
+
>
|
|
51
|
+
<Icon color="--missing-translation-notice-icon-color" />
|
|
52
|
+
<Content>
|
|
53
|
+
<Title>
|
|
54
|
+
{translate('missingTranslationNotice.title', {
|
|
55
|
+
defaultValue: 'This page has not yet been translated',
|
|
56
|
+
})}
|
|
57
|
+
</Title>
|
|
58
|
+
<Markdown compact>{content}</Markdown>
|
|
59
|
+
</Content>
|
|
60
|
+
<CloseButton
|
|
61
|
+
type="button"
|
|
62
|
+
aria-label={translate('missingTranslationNotice.close', { defaultValue: 'Close' })}
|
|
63
|
+
onClick={() => setIsDismissed(true)}
|
|
64
|
+
>
|
|
65
|
+
<CloseIcon color="--missing-translation-notice-text-color" />
|
|
66
|
+
</CloseButton>
|
|
67
|
+
</MissingTranslationNoticeWrapper>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const MissingTranslationNoticeWrapper = styled.div`
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: row;
|
|
74
|
+
align-items: flex-start;
|
|
75
|
+
gap: var(--missing-translation-notice-gap);
|
|
76
|
+
|
|
77
|
+
padding: var(--missing-translation-notice-padding);
|
|
78
|
+
margin: var(--missing-translation-notice-margin-vertical) 0;
|
|
79
|
+
color: var(--missing-translation-notice-text-color);
|
|
80
|
+
background-color: var(--missing-translation-notice-bg-color);
|
|
81
|
+
border: 1px solid var(--missing-translation-notice-border-color);
|
|
82
|
+
border-left: var(--missing-translation-notice-accent-width) solid
|
|
83
|
+
var(--missing-translation-notice-accent-color);
|
|
84
|
+
border-radius: var(--missing-translation-notice-border-radius);
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const Icon = styled(TranslateIcon)`
|
|
88
|
+
flex-shrink: 0;
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
const Content = styled.div`
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
flex: 1;
|
|
95
|
+
gap: var(--spacing-xxs);
|
|
96
|
+
|
|
97
|
+
font-size: var(--missing-translation-notice-font-size);
|
|
98
|
+
line-height: var(--missing-translation-notice-line-height);
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
const Title = styled.span`
|
|
102
|
+
font-size: var(--missing-translation-notice-title-font-size);
|
|
103
|
+
font-weight: var(--missing-translation-notice-title-font-weight);
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const CloseButton = styled.button`
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-shrink: 0;
|
|
109
|
+
padding: 0;
|
|
110
|
+
border: none;
|
|
111
|
+
background: none;
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const missingTranslationNoticeDarkMode = css`
|
|
4
|
+
--missing-translation-notice-bg-color: var(--color-warm-grey-3); // @presenter Color
|
|
5
|
+
--missing-translation-notice-icon-color: var(--color-blue-4); // @presenter Color
|
|
6
|
+
--missing-translation-notice-accent-color: var(--color-blue-4); // @presenter Color
|
|
7
|
+
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const missingTranslationNotice = css`
|
|
4
|
+
/**
|
|
5
|
+
* @tokens Missing translation notice
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
--missing-translation-notice-bg-color: var(--color-warm-grey-1); // @presenter Color
|
|
9
|
+
--missing-translation-notice-text-color: var(--text-color-primary); // @presenter Color
|
|
10
|
+
--missing-translation-notice-icon-color: var(--color-blue-6); // @presenter Color
|
|
11
|
+
--missing-translation-notice-border-color: var(--border-color-secondary); // @presenter Color
|
|
12
|
+
--missing-translation-notice-accent-color: var(--color-blue-6); // @presenter Color
|
|
13
|
+
|
|
14
|
+
--missing-translation-notice-accent-width: 3px; // @presenter Spacing
|
|
15
|
+
--missing-translation-notice-border-radius: var(--border-radius-lg); // @presenter BorderRadius
|
|
16
|
+
--missing-translation-notice-padding: var(--spacing-sm) var(--spacing-base); // @presenter Spacing
|
|
17
|
+
--missing-translation-notice-margin-vertical: var(--spacing-base); // @presenter Spacing
|
|
18
|
+
--missing-translation-notice-gap: var(--spacing-sm); // @presenter Spacing
|
|
19
|
+
|
|
20
|
+
--missing-translation-notice-title-font-size: var(--font-size-base); // @presenter FontSize
|
|
21
|
+
--missing-translation-notice-title-font-weight: var(--font-weight-semibold); // @presenter FontWeight
|
|
22
|
+
--missing-translation-notice-font-size: var(--font-size-base); // @presenter FontSize
|
|
23
|
+
--missing-translation-notice-line-height: var(--line-height-base); // @presenter LineHeight
|
|
24
|
+
|
|
25
|
+
// @tokens End
|
|
26
|
+
`;
|
|
@@ -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 {
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
`;
|
|
@@ -112,6 +112,7 @@ function renderIcon(buttonAction: PageAction, actionState: ActionState) {
|
|
|
112
112
|
const PageActionsWrapper = styled.div`
|
|
113
113
|
margin-left: auto;
|
|
114
114
|
padding-left: var(--page-actions-padding-left);
|
|
115
|
+
user-select: none;
|
|
115
116
|
--button-color: var(--page-actions-button-text-color);
|
|
116
117
|
|
|
117
118
|
.button-group-size-medium .button.button-size-medium {
|
|
@@ -92,7 +92,7 @@ function facetValueToSelectOption(facetValue?: SearchFacetCount | string) {
|
|
|
92
92
|
value: facetValue,
|
|
93
93
|
};
|
|
94
94
|
} else {
|
|
95
|
-
const { value, count, isCounterVisible } = facetValue
|
|
95
|
+
const { value, count, isCounterVisible } = facetValue;
|
|
96
96
|
return {
|
|
97
97
|
element: (
|
|
98
98
|
<FilterSelectOptionWrapper>
|
|
@@ -46,8 +46,7 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
46
46
|
.map((value) => {
|
|
47
47
|
const selectedOption = options.find((option) => option.value === value);
|
|
48
48
|
return (
|
|
49
|
-
selectedOption ||
|
|
50
|
-
(typeof value === 'string' ? ({ value } as SelectOption<T>) : (value as SelectOption<T>))
|
|
49
|
+
selectedOption || (typeof value === 'string' ? { value } : (value as SelectOption<T>))
|
|
51
50
|
);
|
|
52
51
|
})
|
|
53
52
|
.filter((option) => !!option);
|
|
@@ -7,7 +7,7 @@ export const useThemeHooks = vi.fn(() => ({
|
|
|
7
7
|
translate: vi.fn((key: string, defaultValue: string | { defaultValue: string }) =>
|
|
8
8
|
defaultValue
|
|
9
9
|
? typeof defaultValue !== 'string'
|
|
10
|
-
?
|
|
10
|
+
? defaultValue.defaultValue
|
|
11
11
|
: defaultValue
|
|
12
12
|
: key,
|
|
13
13
|
),
|
|
@@ -60,7 +60,15 @@ export function useActiveSectionId(
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// A one-event scroll jump is read before the section it lands on stops being skipped.
|
|
64
|
+
const onContentVisibilityChange = (event: Event) => {
|
|
65
|
+
if ('skipped' in event && event.skipped === false) {
|
|
66
|
+
scrollListener();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
63
70
|
window.addEventListener('scroll', scrollListener, { capture: false });
|
|
71
|
+
window.addEventListener('contentvisibilityautostatechange', onContentVisibilityChange, true);
|
|
64
72
|
const initialRead = setTimeout(() => {
|
|
65
73
|
scrollListener();
|
|
66
74
|
}, 10);
|
|
@@ -69,6 +77,11 @@ export function useActiveSectionId(
|
|
|
69
77
|
clearTimeout(initialRead);
|
|
70
78
|
scrollListener.cancel();
|
|
71
79
|
window.removeEventListener('scroll', scrollListener);
|
|
80
|
+
window.removeEventListener(
|
|
81
|
+
'contentvisibilityautostatechange',
|
|
82
|
+
onContentVisibilityChange,
|
|
83
|
+
true,
|
|
84
|
+
);
|
|
72
85
|
};
|
|
73
86
|
}, [location, heightOffset, scrollListener]);
|
|
74
87
|
|
|
@@ -41,7 +41,7 @@ export function useCodeBlockTabsControls({ tabs, containerRef, tabRefs }: CodeBl
|
|
|
41
41
|
resizeObserver.observe(container);
|
|
42
42
|
|
|
43
43
|
return () => {
|
|
44
|
-
resizeObserver.unobserve(container
|
|
44
|
+
resizeObserver.unobserve(container);
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|