@redocly/theme 0.67.0-next.4 → 0.67.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/Breadcrumbs/BreadcrumbDropdown.js +2 -6
- package/lib/components/Breadcrumbs/Breadcrumbs.js +3 -18
- package/lib/components/Buttons/AIAssistantButton.js +2 -6
- package/lib/components/Buttons/EditPageButton.js +2 -1
- package/lib/components/Catalog/CatalogCardView/CatalogCard.js +1 -1
- package/lib/components/CatalogClassic/CatalogClassicActions.js +3 -1
- package/lib/components/CatalogClassic/CatalogClassicCard.js +1 -1
- package/lib/components/CatalogClassic/CatalogClassicInfoBlock.js +2 -1
- package/lib/components/Feedback/ReportDialog.js +4 -1
- package/lib/components/Filter/FilterCheckboxes.js +3 -1
- package/lib/components/Footer/FooterItem.js +1 -1
- package/lib/components/LanguagePicker/LanguagePicker.js +3 -1
- package/lib/components/Logo/Logo.js +2 -1
- package/lib/components/Menu/MenuContainer.js +1 -0
- package/lib/components/Menu/MenuItem.js +21 -7
- package/lib/components/Navbar/Navbar.js +6 -2
- package/lib/components/Navbar/NavbarItem.js +3 -1
- package/lib/components/Search/SearchDialog.js +5 -33
- package/lib/components/Search/SearchInput.js +4 -1
- package/lib/components/Search/SearchRecent.js +3 -1
- package/lib/components/SidebarActions/SidebarActions.js +10 -3
- package/lib/components/TableOfContent/TableOfContent.js +1 -1
- package/lib/components/UserMenu/LoginButton.js +2 -1
- package/lib/components/UserMenu/LogoutMenuItem.js +2 -1
- package/lib/core/constants/search.js +4 -0
- package/lib/core/hooks/search/use-search-dialog.js +7 -2
- package/lib/core/hooks/use-banner-telemetry.js +2 -11
- package/lib/core/hooks/use-color-switcher.js +2 -1
- package/lib/core/hooks/use-page-actions.js +29 -36
- package/lib/core/hooks/use-product-picker.js +14 -4
- package/lib/core/hooks/use-telemetry-fallback.d.ts +45 -37
- package/lib/core/hooks/use-telemetry-fallback.js +47 -39
- package/lib/core/styles/global.js +33 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/search.d.ts +2 -1
- package/lib/core/types/search.js +1 -0
- package/lib/core/utils/index.d.ts +2 -0
- package/lib/core/utils/index.js +2 -0
- package/lib/core/utils/telemetry/generate-before-after-context.d.ts +37 -0
- package/lib/core/utils/telemetry/generate-before-after-context.js +32 -0
- package/lib/core/utils/telemetry/generate-resource-urn.d.ts +7 -0
- package/lib/core/utils/telemetry/generate-resource-urn.js +13 -0
- package/lib/core/utils/telemetry/get-base-data-attributes.d.ts +14 -0
- package/lib/core/utils/telemetry/get-base-data-attributes.js +17 -0
- package/lib/layouts/McpNotAllowed.d.ts +2 -0
- package/lib/layouts/McpNotAllowed.js +67 -0
- package/package.json +3 -3
- package/src/components/Breadcrumbs/BreadcrumbDropdown.tsx +2 -1
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +4 -4
- package/src/components/Buttons/AIAssistantButton.tsx +2 -3
- package/src/components/Buttons/EditPageButton.tsx +4 -1
- package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +2 -2
- package/src/components/CatalogClassic/CatalogClassicActions.tsx +4 -2
- package/src/components/CatalogClassic/CatalogClassicCard.tsx +9 -2
- package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +2 -1
- package/src/components/Feedback/ReportDialog.tsx +4 -1
- package/src/components/Filter/FilterCheckboxes.tsx +4 -2
- package/src/components/Footer/FooterItem.tsx +4 -2
- package/src/components/LanguagePicker/LanguagePicker.tsx +9 -2
- package/src/components/Logo/Logo.tsx +7 -1
- package/src/components/Menu/MenuContainer.tsx +1 -0
- package/src/components/Menu/MenuItem.tsx +35 -4
- package/src/components/Navbar/Navbar.tsx +7 -3
- package/src/components/Navbar/NavbarItem.tsx +7 -1
- package/src/components/Search/SearchDialog.tsx +6 -13
- package/src/components/Search/SearchInput.tsx +4 -1
- package/src/components/Search/SearchRecent.tsx +4 -2
- package/src/components/SidebarActions/SidebarActions.tsx +10 -3
- package/src/components/TableOfContent/TableOfContent.tsx +7 -2
- package/src/components/UserMenu/LoginButton.tsx +4 -1
- package/src/components/UserMenu/LogoutMenuItem.tsx +2 -1
- package/src/core/constants/search.ts +4 -0
- package/src/core/hooks/search/use-search-dialog.ts +13 -2
- package/src/core/hooks/use-banner-telemetry.ts +5 -4
- package/src/core/hooks/use-color-switcher.ts +7 -1
- package/src/core/hooks/use-page-actions.ts +38 -44
- package/src/core/hooks/use-product-picker.ts +19 -5
- package/src/core/hooks/use-telemetry-fallback.ts +47 -39
- package/src/core/styles/global.ts +33 -0
- package/src/core/types/l10n.ts +2 -0
- package/src/core/types/search.ts +1 -0
- package/src/core/utils/index.ts +2 -0
- package/src/core/utils/telemetry/generate-before-after-context.ts +59 -0
- package/src/core/utils/telemetry/generate-resource-urn.ts +9 -0
- package/src/core/utils/telemetry/get-base-data-attributes.ts +27 -0
- package/src/layouts/McpNotAllowed.tsx +81 -0
|
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
|
|
3
3
|
|
|
4
4
|
import type { JSX } from 'react';
|
|
5
5
|
|
|
6
|
-
import { breakpoints } from '@redocly/theme/core/utils';
|
|
6
|
+
import { breakpoints, getBaseDataAttributes } from '@redocly/theme/core/utils';
|
|
7
7
|
import { useThemeHooks, useRecentSearches } from '@redocly/theme/core/hooks';
|
|
8
8
|
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
|
|
9
9
|
import { RecentlyViewedIcon } from '@redocly/theme/icons/RecentlyViewedIcon/RecentlyViewedIcon';
|
|
@@ -25,7 +25,9 @@ export function SearchRecent({ onSelect, className }: SearchRecentProps): JSX.El
|
|
|
25
25
|
const handleOnRemove = (e: React.MouseEvent<SVGSVGElement, MouseEvent>, item: string) => {
|
|
26
26
|
e.stopPropagation();
|
|
27
27
|
removeSearchHistoryItem(item);
|
|
28
|
-
telemetry.
|
|
28
|
+
telemetry.sendSearchRecentRemoveClickedMessage([
|
|
29
|
+
getBaseDataAttributes('searchRecentRemove', 'search'),
|
|
30
|
+
]);
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, item: string, index: number) => {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ControlsWrapChangeLayoutButtons,
|
|
12
12
|
} from '@redocly/theme/components/SidebarActions/styled';
|
|
13
13
|
import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
|
|
14
|
+
import { getBaseDataAttributes } from '@redocly/theme/core/utils';
|
|
14
15
|
|
|
15
16
|
export { LayoutVariant };
|
|
16
17
|
|
|
@@ -57,9 +58,13 @@ export const SidebarActions = ({
|
|
|
57
58
|
onClick={() => {
|
|
58
59
|
onChangeCollapseSidebarClick();
|
|
59
60
|
if (collapsedSidebar) {
|
|
60
|
-
telemetry.
|
|
61
|
+
telemetry.sendSidebarExpandedMessage([
|
|
62
|
+
getBaseDataAttributes('sidebarExpand', 'button'),
|
|
63
|
+
]);
|
|
61
64
|
} else {
|
|
62
|
-
telemetry.
|
|
65
|
+
telemetry.sendSidebarCollapsedMessage([
|
|
66
|
+
getBaseDataAttributes('sidebarCollapse', 'button'),
|
|
67
|
+
]);
|
|
63
68
|
}
|
|
64
69
|
}}
|
|
65
70
|
size="small"
|
|
@@ -76,7 +81,9 @@ export const SidebarActions = ({
|
|
|
76
81
|
layout={layout}
|
|
77
82
|
onClick={() => {
|
|
78
83
|
onChangeViewClick();
|
|
79
|
-
telemetry.
|
|
84
|
+
telemetry.sendChangeLayoutClickedMessage([
|
|
85
|
+
getBaseDataAttributes('changeLayout', 'button'),
|
|
86
|
+
]);
|
|
80
87
|
}}
|
|
81
88
|
/>
|
|
82
89
|
</ControlsWrapChangeLayoutButtons>
|
|
@@ -10,7 +10,12 @@ import {
|
|
|
10
10
|
useThemeConfig,
|
|
11
11
|
useFullHeight,
|
|
12
12
|
} from '@redocly/theme/core/hooks';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
breakpoints,
|
|
15
|
+
getDisplayedHeadings,
|
|
16
|
+
getLeastDepth,
|
|
17
|
+
getBaseDataAttributes,
|
|
18
|
+
} from '@redocly/theme/core/utils';
|
|
14
19
|
|
|
15
20
|
export type TableOfContentProps = {
|
|
16
21
|
headings?: Array<MdHeading | null> | null | undefined;
|
|
@@ -62,7 +67,7 @@ export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
|
|
|
62
67
|
data-testid={`toc-${heading.value}`}
|
|
63
68
|
onClick={(e) => {
|
|
64
69
|
e.preventDefault();
|
|
65
|
-
telemetry.sendTocItemClickedMessage();
|
|
70
|
+
telemetry.sendTocItemClickedMessage([getBaseDataAttributes('tocItem', 'toc')]);
|
|
66
71
|
handleHeadingClick(heading.id);
|
|
67
72
|
}}
|
|
68
73
|
/>
|
|
@@ -4,6 +4,7 @@ import type { ButtonVariant, ButtonSize } from '@redocly/theme/components/Button
|
|
|
4
4
|
|
|
5
5
|
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
6
6
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
7
|
+
import { getBaseDataAttributes } from '@redocly/theme/core/utils';
|
|
7
8
|
|
|
8
9
|
export type LoginButtonProps = {
|
|
9
10
|
href: string;
|
|
@@ -36,7 +37,9 @@ export function LoginButton({
|
|
|
36
37
|
data-translation-key={label ? undefined : labelTranslationKey}
|
|
37
38
|
to={href}
|
|
38
39
|
languageInsensitive
|
|
39
|
-
onClick={() =>
|
|
40
|
+
onClick={() =>
|
|
41
|
+
telemetry.sendLoginClickedMessage([getBaseDataAttributes('login', 'button')])
|
|
42
|
+
}
|
|
40
43
|
data-testid="login-btn"
|
|
41
44
|
extraClass={className}
|
|
42
45
|
variant={variant}
|
|
@@ -5,6 +5,7 @@ import type { JSX } from 'react';
|
|
|
5
5
|
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
6
6
|
import { LogoutIcon } from '@redocly/theme/icons/LogoutIcon/LogoutIcon';
|
|
7
7
|
import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
|
|
8
|
+
import { getBaseDataAttributes } from '@redocly/theme/core/utils';
|
|
8
9
|
|
|
9
10
|
export type LogoutMenuItemProps = {
|
|
10
11
|
iconOnly?: boolean;
|
|
@@ -18,7 +19,7 @@ export function LogoutMenuItem({ iconOnly, className }: LogoutMenuItemProps): JS
|
|
|
18
19
|
const { translate } = useTranslate();
|
|
19
20
|
|
|
20
21
|
const handleClick = () => {
|
|
21
|
-
telemetry.
|
|
22
|
+
telemetry.sendLogoutClickedMessage([getBaseDataAttributes('logoutItem', 'userMenu')]);
|
|
22
23
|
handleLogout();
|
|
23
24
|
};
|
|
24
25
|
|
|
@@ -73,4 +73,8 @@ export const TOOL_CALL_DISPLAY_TEXT: Partial<
|
|
|
73
73
|
inProgressText: 'Reading API description...',
|
|
74
74
|
completedText: 'Read API description',
|
|
75
75
|
},
|
|
76
|
+
[ToolCallName.DescribeTools]: {
|
|
77
|
+
inProgressText: 'Discovering available tools...',
|
|
78
|
+
completedText: 'Discovered available tools',
|
|
79
|
+
},
|
|
76
80
|
};
|
|
@@ -5,6 +5,7 @@ import hotkeys from 'hotkeys-js';
|
|
|
5
5
|
import { useThemeHooks } from '../use-theme-hooks';
|
|
6
6
|
import { useThemeConfig } from '../use-theme-config';
|
|
7
7
|
import { useSearchSession } from '../../contexts';
|
|
8
|
+
import { getBaseDataAttributes } from '../../utils';
|
|
8
9
|
|
|
9
10
|
export function useSearchDialog() {
|
|
10
11
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -20,7 +21,12 @@ export function useSearchDialog() {
|
|
|
20
21
|
if (hotKeys) {
|
|
21
22
|
hotkeys(hotKeys, (ev) => {
|
|
22
23
|
setIsOpen(true);
|
|
23
|
-
telemetry.sendSearchOpenedMessage([
|
|
24
|
+
telemetry.sendSearchOpenedMessage([
|
|
25
|
+
{
|
|
26
|
+
...getBaseDataAttributes('searchDialogId', 'search'),
|
|
27
|
+
method: 'shortcut',
|
|
28
|
+
},
|
|
29
|
+
]);
|
|
24
30
|
ev.preventDefault();
|
|
25
31
|
});
|
|
26
32
|
|
|
@@ -30,7 +36,12 @@ export function useSearchDialog() {
|
|
|
30
36
|
}, [hotKeys]);
|
|
31
37
|
|
|
32
38
|
const onOpen = useCallback(function () {
|
|
33
|
-
telemetry.sendSearchOpenedMessage([
|
|
39
|
+
telemetry.sendSearchOpenedMessage([
|
|
40
|
+
{
|
|
41
|
+
...getBaseDataAttributes('searchDialogId', 'search'),
|
|
42
|
+
method: 'click',
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
34
45
|
setIsOpen(true);
|
|
35
46
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
47
|
}, []);
|
|
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
|
|
|
3
3
|
import type { DisplayBanner } from '@redocly/theme/components/Banner/Banner';
|
|
4
4
|
|
|
5
5
|
import { useThemeHooks } from './use-theme-hooks';
|
|
6
|
+
import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
|
|
6
7
|
|
|
7
8
|
const noop = (): void => {};
|
|
8
9
|
const noopLink = (_href: string): void => {};
|
|
@@ -28,11 +29,11 @@ export function useBannerTelemetry(
|
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const bannerUri = 'urn:redocly:realm:ui:banner:banner-id';
|
|
32
32
|
const payload = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
...getBaseDataAttributes<'bannerId', 'banner', 'urn:redocly:realm:ui:banner:bannerId'>(
|
|
34
|
+
'bannerId',
|
|
35
|
+
'banner',
|
|
36
|
+
),
|
|
36
37
|
trackingId: displayBanner.trackingId,
|
|
37
38
|
hash: displayBanner.hash,
|
|
38
39
|
color: displayBanner.color,
|
|
@@ -6,6 +6,7 @@ import { useThemeConfig } from './use-theme-config';
|
|
|
6
6
|
import { useThemeHooks } from './use-theme-hooks';
|
|
7
7
|
import { createStore, useStore } from './use-store';
|
|
8
8
|
import { DEFAULT_COLOR_MODES } from '../constants';
|
|
9
|
+
import { generateBeforeAfterContext } from '../utils';
|
|
9
10
|
|
|
10
11
|
const COLOR_MODE_KEY = 'colorSchema';
|
|
11
12
|
const colorModeStore = createStore<string>({
|
|
@@ -51,7 +52,12 @@ export const useColorSwitcher = () => {
|
|
|
51
52
|
root.classList.remove('notransition');
|
|
52
53
|
});
|
|
53
54
|
telemetry.sendColorModeSwitchedMessage([
|
|
54
|
-
|
|
55
|
+
...generateBeforeAfterContext<'sendColorModeSwitchedMessage'>(
|
|
56
|
+
'colorMode',
|
|
57
|
+
'button',
|
|
58
|
+
{ mode: activeColorMode },
|
|
59
|
+
{ mode: newMode },
|
|
60
|
+
),
|
|
55
61
|
]);
|
|
56
62
|
setActiveColorMode(newMode);
|
|
57
63
|
};
|
|
@@ -22,14 +22,7 @@ import {
|
|
|
22
22
|
removeTrailingSlash,
|
|
23
23
|
withoutPathPrefix,
|
|
24
24
|
} from '../utils/urls';
|
|
25
|
-
|
|
26
|
-
function createPageActionResource(pageSlug: string, pageUrl: string) {
|
|
27
|
-
return {
|
|
28
|
-
id: pageSlug,
|
|
29
|
-
object: 'page' as const,
|
|
30
|
-
uri: pageUrl,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
25
|
+
import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
|
|
33
26
|
|
|
34
27
|
const DEFAULT_ENABLED_ACTIONS = [
|
|
35
28
|
'copy',
|
|
@@ -47,6 +40,7 @@ export type PageActionType =
|
|
|
47
40
|
| 'claude'
|
|
48
41
|
| 'docs-mcp-cursor'
|
|
49
42
|
| 'docs-mcp-vscode'
|
|
43
|
+
// Used in OpenAPI docs x-mcp
|
|
50
44
|
| 'mcp-cursor'
|
|
51
45
|
| 'mcp-vscode';
|
|
52
46
|
|
|
@@ -78,25 +72,22 @@ export function usePageActions(
|
|
|
78
72
|
|
|
79
73
|
const isDocsMcp = !requiresMcpUrl;
|
|
80
74
|
|
|
81
|
-
const origin = IS_BROWSER ? window.location.origin : '';
|
|
82
|
-
const pageUrl = `${origin}${pageSlug}`;
|
|
83
|
-
|
|
84
75
|
return createMCPAction({
|
|
85
76
|
clientType,
|
|
86
77
|
mcpConfig: config,
|
|
87
78
|
translate,
|
|
88
79
|
onClickCallback: isDocsMcp
|
|
89
80
|
? () =>
|
|
90
|
-
telemetry.
|
|
81
|
+
telemetry.sendPageActionsClickedMessage([
|
|
91
82
|
{
|
|
92
|
-
...
|
|
93
|
-
|
|
83
|
+
...getBaseDataAttributes('pageActions', 'button'),
|
|
84
|
+
actionType: `docs-mcp-${clientType}` as const,
|
|
94
85
|
},
|
|
95
86
|
])
|
|
96
87
|
: undefined,
|
|
97
88
|
});
|
|
98
89
|
},
|
|
99
|
-
[mcpUrl, mcpConfig, translate, telemetry
|
|
90
|
+
[mcpUrl, mcpConfig, translate, telemetry],
|
|
100
91
|
);
|
|
101
92
|
|
|
102
93
|
const result: PageAction[] = useMemo(() => {
|
|
@@ -113,7 +104,6 @@ export function usePageActions(
|
|
|
113
104
|
? window.location.origin
|
|
114
105
|
: ((globalThis as { SSR_HOSTNAME?: string })['SSR_HOSTNAME'] ?? '');
|
|
115
106
|
const pathname = addTrailingSlash(pageSlug);
|
|
116
|
-
const pageUrl = combineUrls(origin, pathname);
|
|
117
107
|
const isRoot = withoutPathPrefix(pathname) === '/';
|
|
118
108
|
const mdPageUrl = isRoot
|
|
119
109
|
? combineUrls(origin, pathname, 'index.html.md')
|
|
@@ -138,10 +128,10 @@ export function usePageActions(
|
|
|
138
128
|
}
|
|
139
129
|
const text = await result.text();
|
|
140
130
|
ClipboardService.copyCustom(text);
|
|
141
|
-
telemetry.
|
|
131
|
+
telemetry.sendPageActionsClickedMessage([
|
|
142
132
|
{
|
|
143
|
-
...
|
|
144
|
-
|
|
133
|
+
...getBaseDataAttributes('pageActions', 'button'),
|
|
134
|
+
actionType: 'copy',
|
|
145
135
|
},
|
|
146
136
|
]);
|
|
147
137
|
} catch (error) {
|
|
@@ -157,10 +147,10 @@ export function usePageActions(
|
|
|
157
147
|
iconComponent: MarkdownFullIcon,
|
|
158
148
|
link: mdPageUrl,
|
|
159
149
|
onClick: () => {
|
|
160
|
-
telemetry.
|
|
150
|
+
telemetry.sendPageActionsClickedMessage([
|
|
161
151
|
{
|
|
162
|
-
...
|
|
163
|
-
|
|
152
|
+
...getBaseDataAttributes('pageActions', 'button'),
|
|
153
|
+
actionType: 'view',
|
|
164
154
|
},
|
|
165
155
|
]);
|
|
166
156
|
},
|
|
@@ -175,10 +165,10 @@ export function usePageActions(
|
|
|
175
165
|
iconComponent: ChatGptIcon,
|
|
176
166
|
link,
|
|
177
167
|
onClick: () => {
|
|
178
|
-
telemetry.
|
|
168
|
+
telemetry.sendPageActionsClickedMessage([
|
|
179
169
|
{
|
|
180
|
-
...
|
|
181
|
-
|
|
170
|
+
...getBaseDataAttributes('pageActions', 'button'),
|
|
171
|
+
actionType: 'chatgpt',
|
|
182
172
|
},
|
|
183
173
|
]);
|
|
184
174
|
window.location.href = link;
|
|
@@ -195,10 +185,10 @@ export function usePageActions(
|
|
|
195
185
|
iconComponent: ClaudeIcon,
|
|
196
186
|
link,
|
|
197
187
|
onClick: () => {
|
|
198
|
-
telemetry.
|
|
188
|
+
telemetry.sendPageActionsClickedMessage([
|
|
199
189
|
{
|
|
200
|
-
...
|
|
201
|
-
|
|
190
|
+
...getBaseDataAttributes('pageActions', 'button'),
|
|
191
|
+
actionType: 'claude',
|
|
202
192
|
},
|
|
203
193
|
]);
|
|
204
194
|
window.location.href = link;
|
|
@@ -300,28 +290,32 @@ function shouldHideAction(
|
|
|
300
290
|
isMcpDisabled,
|
|
301
291
|
}: ShouldHideActionContext,
|
|
302
292
|
): boolean {
|
|
303
|
-
// Can't use any actions if search is globally disabled (markdown files are not generated)
|
|
304
|
-
if (themeConfig.search?.hide) {
|
|
305
|
-
return true;
|
|
306
|
-
}
|
|
307
|
-
// Can't use any actions if no markdown files are generated for LLMs
|
|
308
|
-
if (pageProps?.seo?.llmstxt?.hide) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
293
|
// Page actions are explicitly disabled in config
|
|
312
294
|
if (themeConfig.navigation?.actions?.hide) {
|
|
313
295
|
return true;
|
|
314
296
|
}
|
|
315
297
|
|
|
316
|
-
const
|
|
317
|
-
pageProps?.metadata?.type === 'openapi' || pageProps?.metadata?.subType === 'openapi-operation';
|
|
318
|
-
const isPageExcludedFromSearch =
|
|
319
|
-
pageProps?.frontmatter?.excludeFromSearch || (isOpenApiPage && openapiExcludeFromSearch);
|
|
298
|
+
const actionDependsOnLlmsTxt = ['view', 'copy', 'chatgpt', 'claude'].includes(action);
|
|
320
299
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
300
|
+
if (actionDependsOnLlmsTxt) {
|
|
301
|
+
// Can't use any actions if search is globally disabled (markdown files are not generated)
|
|
302
|
+
if (themeConfig.search?.hide) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
// Can't use any actions if no markdown files are generated for LLMs
|
|
306
|
+
if (pageProps?.seo?.llmstxt?.hide) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const isOpenApiPage =
|
|
311
|
+
pageProps?.metadata?.type === 'openapi' ||
|
|
312
|
+
pageProps?.metadata?.subType === 'openapi-operation';
|
|
313
|
+
const isPageExcludedFromSearch =
|
|
314
|
+
pageProps?.frontmatter?.excludeFromSearch || (isOpenApiPage && openapiExcludeFromSearch);
|
|
315
|
+
|
|
316
|
+
if (isPageExcludedFromSearch) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
325
319
|
}
|
|
326
320
|
|
|
327
321
|
return shouldHideActionByType(action, { isPublic, mcpUrl, isMcpDisabled });
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
1
2
|
import { useNavigate } from 'react-router-dom';
|
|
2
3
|
|
|
3
4
|
import { getProductClassName } from '@redocly/theme/components/Product/utils';
|
|
4
5
|
|
|
5
6
|
import { useThemeHooks } from './use-theme-hooks';
|
|
6
|
-
import { withPathPrefix } from '../utils';
|
|
7
|
+
import { withPathPrefix, generateBeforeAfterContext } from '../utils';
|
|
7
8
|
|
|
8
9
|
export function useProductPicker() {
|
|
9
10
|
const { useCurrentProduct, useProducts, useTelemetry, useLoadAndNavigate } = useThemeHooks();
|
|
@@ -12,20 +13,33 @@ export function useProductPicker() {
|
|
|
12
13
|
const telemetry = useTelemetry();
|
|
13
14
|
const navigate = useNavigate();
|
|
14
15
|
const loadAndNavigate = useLoadAndNavigate();
|
|
16
|
+
// An open dropdown can fire a menu item's onAction from an earlier render, so `product.link`
|
|
17
|
+
// may be stale (e.g. after a locale switch); resolve the live product via a ref.
|
|
18
|
+
const productsRef = useRef(products);
|
|
19
|
+
productsRef.current = products;
|
|
15
20
|
function setProduct(product: typeof currentProduct) {
|
|
16
21
|
if (!product) return;
|
|
17
|
-
|
|
22
|
+
// match on `name` (locale-stable; link/slug are re-localized)
|
|
23
|
+
const freshProduct = productsRef.current.find((p) => p.name === product.name) ?? product;
|
|
24
|
+
telemetry.sendProductPickerChangedMessage([
|
|
25
|
+
...generateBeforeAfterContext<'sendProductPickerChangedMessage'>(
|
|
26
|
+
'productPicker',
|
|
27
|
+
'dropdown',
|
|
28
|
+
{ product: currentProduct?.slug || '' },
|
|
29
|
+
{ product: freshProduct.slug },
|
|
30
|
+
),
|
|
31
|
+
]);
|
|
18
32
|
if (typeof document === 'undefined') return;
|
|
19
33
|
|
|
20
|
-
if (
|
|
34
|
+
if (freshProduct.name) {
|
|
21
35
|
const root = document.documentElement;
|
|
22
36
|
Array.from(root.classList)
|
|
23
37
|
.filter((c) => c.startsWith('product-'))
|
|
24
38
|
.forEach((c) => root.classList.remove(c));
|
|
25
|
-
root.classList.add(getProductClassName(
|
|
39
|
+
root.classList.add(getProductClassName(freshProduct.name));
|
|
26
40
|
}
|
|
27
41
|
|
|
28
|
-
loadAndNavigate({ navigate, to: withPathPrefix(
|
|
42
|
+
loadAndNavigate({ navigate, to: withPathPrefix(freshProduct.link) });
|
|
29
43
|
}
|
|
30
44
|
return {
|
|
31
45
|
currentProduct,
|
|
@@ -1,50 +1,61 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Fallback no-op telemetry used by `useThemeHooks` when no ThemeDataContext
|
|
2
|
+
// provider supplies real hooks.
|
|
3
3
|
export const useTelemetryFallback = () => ({
|
|
4
4
|
send: () => {},
|
|
5
|
-
sendPageViewedMessage: () => {},
|
|
6
|
-
sendPageActionsButtonClickedMessage: () => {},
|
|
7
5
|
sendErrorMessage: () => {},
|
|
8
6
|
sendClientErrorMessage: () => {},
|
|
7
|
+
sendCatalogEntitiesFilterCheckboxToggledMessage: () => {},
|
|
8
|
+
sendCatalogEntitiesCopyDataSchemaClickedMessage: () => {},
|
|
9
|
+
sendCatalogEntitiesViewModeChangedMessage: () => {},
|
|
10
|
+
sendCatalogEntitiesRelatedEntitiesListSearchQueryMessage: () => {},
|
|
11
|
+
sendCatalogEntitiesRelatedEntitiesListSearchResultClickMessage: () => {},
|
|
12
|
+
sendCatalogEntitiesListSearchQueryMessage: () => {},
|
|
13
|
+
sendCatalogEntitiesListSearchResultClickedMessage: () => {},
|
|
14
|
+
sendRedirectMessage: () => {},
|
|
15
|
+
sendCustomMessage: () => {},
|
|
16
|
+
sendSearchAIQueryMessage: () => {},
|
|
17
|
+
sendSearchAiOpenedMessage: () => {},
|
|
18
|
+
sendSearchAIFeedbackMessage: () => {},
|
|
19
|
+
sendLoginClickedMessage: () => {},
|
|
20
|
+
sendLoginProviderClickedMessage: () => {},
|
|
21
|
+
sendLogoutClickedMessage: () => {},
|
|
22
|
+
sendBannerViewedMessage: () => {},
|
|
23
|
+
sendBannerLinkClickedMessage: () => {},
|
|
24
|
+
sendBannerDismissedMessage: () => {},
|
|
9
25
|
sendBreadcrumbClickedMessage: () => {},
|
|
10
|
-
|
|
11
|
-
sendSidebarItemClickedMessage: () => {},
|
|
12
|
-
sendSidebarItemExpandedMessage: () => {},
|
|
13
|
-
sendSidebarItemCollapsedMessage: () => {},
|
|
14
|
-
sendChangeLayoutButtonClickedMessage: () => {},
|
|
15
|
-
sendEditPageLinkClickedMessage: () => {},
|
|
16
|
-
sendCodeSnippetReportedMessage: () => {},
|
|
17
|
-
sendNavbarMenuItemClickedMessage: () => {},
|
|
18
|
-
sendLoginButtonClickedMessage: () => {},
|
|
19
|
-
sendLoginProviderButtonClickedMessage: () => {},
|
|
20
|
-
sendLogoutMenuItemClickedMessage: () => {},
|
|
21
|
-
sendLogoClickedMessage: () => {},
|
|
22
|
-
sendTocItemClickedMessage: () => {},
|
|
26
|
+
sendCatalogActionsClickedMessage: () => {},
|
|
23
27
|
sendCatalogFilterChangedMessage: () => {},
|
|
24
28
|
sendCatalogItemClickedMessage: () => {},
|
|
25
29
|
sendScorecardLinkClickedMessage: () => {},
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
sendFilterCheckboxToggledMessage: () => {},
|
|
31
|
+
sendFeedbackMessage: () => {},
|
|
28
32
|
sendFooterItemClickedMessage: () => {},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
sendNavbarMenuItemClickedMessage: () => {},
|
|
34
|
+
sendColorModeSwitchedMessage: () => {},
|
|
35
|
+
sendLanguagePickerChangedMessage: () => {},
|
|
36
|
+
sendProductPickerChangedMessage: () => {},
|
|
37
|
+
sendLogoClickedMessage: () => {},
|
|
38
|
+
sendCodeSnippetReportedMessage: () => {},
|
|
39
|
+
sendMobileMenuClosedMessage: () => {},
|
|
40
|
+
sendMobileMenuOpenedMessage: () => {},
|
|
41
|
+
sendPageViewedMessage: () => {},
|
|
42
|
+
sendPageTimeMessage: () => {},
|
|
43
|
+
sendPageActionsClickedMessage: () => {},
|
|
44
|
+
sendPageEditClickedMessage: () => {},
|
|
45
|
+
sendSidebarDrilldownBackClickedMessage: () => {},
|
|
46
|
+
sendSidebarItemClickedMessage: () => {},
|
|
47
|
+
sendSidebarExpandedMessage: () => {},
|
|
48
|
+
sendSidebarCollapsedMessage: () => {},
|
|
49
|
+
sendChangeLayoutClickedMessage: () => {},
|
|
50
|
+
sendRequestApiAccessClickedMessage: () => {},
|
|
51
|
+
sendVersionPickerChangedMessage: () => {},
|
|
52
|
+
sendSearchInputResetClickedMessage: () => {},
|
|
53
|
+
sendSearchResultClickedMessage: () => {},
|
|
54
|
+
sendSearchRecentRemoveClickedMessage: () => {},
|
|
34
55
|
sendSearchRecentClickedMessage: () => {},
|
|
35
|
-
sendRequestApiAccessButtonClickedMessage: () => {},
|
|
36
|
-
sendVersionPickerSelectionChangeMessage: () => {},
|
|
37
|
-
sendProductPickedMessage: () => {},
|
|
38
|
-
sendFilterCheckboxToggledMessage: () => {},
|
|
39
|
-
sendLanguagePickerLocaleChangedMessage: () => {},
|
|
40
56
|
sendSearchOpenedMessage: () => {},
|
|
41
57
|
sendSearchQueryMessage: () => {},
|
|
42
|
-
|
|
43
|
-
sendSearchAIQueryMessage: () => {},
|
|
44
|
-
sendSearchAIFeedbackMessage: () => {},
|
|
45
|
-
sendFeedbackMessage: () => {},
|
|
46
|
-
sendSearchResultClickedMessage: () => {},
|
|
47
|
-
sendRedirectMessage: () => {},
|
|
58
|
+
sendTocItemClickedMessage: () => {},
|
|
48
59
|
sendOpenapiDocsMessage: () => {},
|
|
49
60
|
sendCopyCodeSnippetClickedMessage: () => {},
|
|
50
61
|
sendViewedMessage: () => {},
|
|
@@ -67,10 +78,7 @@ export const useTelemetryFallback = () => ({
|
|
|
67
78
|
sendAsyncapiDocsReferencedInClickedMessage: () => {},
|
|
68
79
|
sendGraphqlDocsViewedMessage: () => {},
|
|
69
80
|
sendGraphqlDocsPerformanceMetricsMessage: () => {},
|
|
81
|
+
sendGraphqlDocsDownloadDefinitionClickedMessage: () => {},
|
|
70
82
|
sendGraphqlDocsReferencedInLinkClickedMessage: () => {},
|
|
71
83
|
sendGraphqlDocsRequiredScopesModalOpenedMessage: () => {},
|
|
72
|
-
sendGraphqlDocsDownloadDefinitionClickedMessage: () => {},
|
|
73
|
-
sendBannerViewedMessage: () => {},
|
|
74
|
-
sendBannerLinkClickedMessage: () => {},
|
|
75
|
-
sendBannerDismissedMessage: () => {},
|
|
76
84
|
});
|
|
@@ -1026,6 +1026,39 @@ const pages = css`
|
|
|
1026
1026
|
|
|
1027
1027
|
// @tokens End
|
|
1028
1028
|
|
|
1029
|
+
/**
|
|
1030
|
+
* @tokens 405 MCP Page
|
|
1031
|
+
* @presenter Color
|
|
1032
|
+
*/
|
|
1033
|
+
--page-mcp-405-font-family: var(--font-family-base); // @presenter FontFamily
|
|
1034
|
+
--page-mcp-405-margin-vertical: var(--spacing-xl);
|
|
1035
|
+
--page-mcp-405-margin-horizontal: calc(var(--spacing-xxl) * 2);
|
|
1036
|
+
--page-mcp-405-gap: var(--spacing-lg);
|
|
1037
|
+
--page-mcp-405-max-width: 680px;
|
|
1038
|
+
--page-mcp-405-margin-vertical-mobile: var(--spacing-xxl);
|
|
1039
|
+
--page-mcp-405-margin-horizontal-mobile: 1rem;
|
|
1040
|
+
|
|
1041
|
+
--page-mcp-405-status-text-color: var(--text-color-helper);
|
|
1042
|
+
--page-mcp-405-status-font-size: var(--font-size-lg); // @presenter FontSize
|
|
1043
|
+
--page-mcp-405-status-line-height: var(--line-height-lg); // @presenter LineHeight
|
|
1044
|
+
--page-mcp-405-status-font-weight: var(--font-weight-semibold); // @presenter FontWeight
|
|
1045
|
+
|
|
1046
|
+
--page-mcp-405-title-text-color: var(--text-color-primary);
|
|
1047
|
+
--page-mcp-405-title-font-size: 42px; // @presenter FontSize
|
|
1048
|
+
--page-mcp-405-title-font-weight: var(--font-weight-bold); // @presenter FontWeight
|
|
1049
|
+
--page-mcp-405-title-line-height: 50px; // @presenter LineHeight
|
|
1050
|
+
--page-mcp-405-title-font-size-mobile: var(--h3-font-size);
|
|
1051
|
+
--page-mcp-405-title-line-height-mobile: var(--h3-line-height);
|
|
1052
|
+
|
|
1053
|
+
--page-mcp-405-description-text-color: var(--text-color-secondary);
|
|
1054
|
+
--page-mcp-405-description-font-size: var(--font-size-xl); // @presenter FontSize
|
|
1055
|
+
--page-mcp-405-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
|
|
1056
|
+
--page-mcp-405-description-line-height: var(--line-height-xl);
|
|
1057
|
+
--page-mcp-405-description-font-size-mobile: var(--font-size-lg);
|
|
1058
|
+
--page-mcp-405-description-line-height-mobile: var(--line-height-lg);
|
|
1059
|
+
|
|
1060
|
+
// @tokens End
|
|
1061
|
+
|
|
1029
1062
|
/**
|
|
1030
1063
|
* @tokens 500 Page
|
|
1031
1064
|
* @presenter Color
|
package/src/core/types/l10n.ts
CHANGED
|
@@ -134,6 +134,8 @@ export type TranslationKey =
|
|
|
134
134
|
| 'page.forbidden.description'
|
|
135
135
|
| 'page.notFound.title'
|
|
136
136
|
| 'page.notFound.description'
|
|
137
|
+
| 'page.mcpNotAllowed.title'
|
|
138
|
+
| 'page.mcpNotAllowed.description'
|
|
137
139
|
| 'page.lastUpdated.timeago'
|
|
138
140
|
| 'page.lastUpdated.on'
|
|
139
141
|
| 'catalog.filters.placeholder'
|
package/src/core/types/search.ts
CHANGED
package/src/core/utils/index.ts
CHANGED
|
@@ -46,3 +46,5 @@ export * from './content-segments';
|
|
|
46
46
|
export * from './custom-catalog-options-casing';
|
|
47
47
|
export * from './get-auto-dismiss-duration';
|
|
48
48
|
export * from './tooltip-placement';
|
|
49
|
+
export * from './telemetry/get-base-data-attributes';
|
|
50
|
+
export * from './telemetry/generate-before-after-context';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AsyncApiRealmUI } from '@redocly/realm-asyncapi-sdk';
|
|
2
|
+
|
|
3
|
+
import { getBaseDataAttributes } from './get-base-data-attributes';
|
|
4
|
+
|
|
5
|
+
type UITelemetryClient = AsyncApiRealmUI.Telemetry;
|
|
6
|
+
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
8
|
+
type UITelemetryClientMethods = {
|
|
9
|
+
[K in keyof UITelemetryClient]: UITelemetryClient[K] extends (...args: any) => any
|
|
10
|
+
? UITelemetryClient[K]
|
|
11
|
+
: never;
|
|
12
|
+
};
|
|
13
|
+
type UITelemetryClientMethodKeys = {
|
|
14
|
+
[K in keyof UITelemetryClientMethods]: Parameters<
|
|
15
|
+
UITelemetryClientMethods[K]
|
|
16
|
+
>[0] extends readonly [any, any, ...any[]]
|
|
17
|
+
? K
|
|
18
|
+
: never;
|
|
19
|
+
}[keyof UITelemetryClientMethods];
|
|
20
|
+
|
|
21
|
+
type EventSchemaProps<T extends UITelemetryClientMethodKeys> = Parameters<
|
|
22
|
+
UITelemetryClientMethods[T]
|
|
23
|
+
>[0];
|
|
24
|
+
|
|
25
|
+
type EventAfterContext<T extends UITelemetryClientMethodKeys> = EventSchemaProps<T>[0];
|
|
26
|
+
type EventBeforeContext<T extends UITelemetryClientMethodKeys> = EventSchemaProps<T>[1];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Builds a typed `[after, before]` tuple for Realm UI telemetry events whose AsyncAPI
|
|
30
|
+
* contract lists the updated state first, then the prior state (per CloudEvents styleguide).
|
|
31
|
+
*
|
|
32
|
+
* Each slice merges {@link getBaseDataAttributes} (`id`, `object`, `uri`) with the
|
|
33
|
+
* corresponding partial payload and sets `context` to `'after'` or `'before'`.
|
|
34
|
+
*
|
|
35
|
+
* @template T - Telemetry client method key whose `data` tuple is
|
|
36
|
+
* `[after, before, ...]` (for example `'sendLanguagePickerChangedMessage'`).
|
|
37
|
+
* @param id - Stable resource identifier passed to {@link getBaseDataAttributes}
|
|
38
|
+
* and included on both payloads.
|
|
39
|
+
* @param object - Resource kind passed to {@link getBaseDataAttributes}
|
|
40
|
+
* and included on both payloads (for example `'dropdown'` or `'button'`).
|
|
41
|
+
* @param before - Event-specific fields for the prior state, excluding `id`, `object`,
|
|
42
|
+
* `uri`, and `context` (filled by this helper).
|
|
43
|
+
* @param after - Event-specific fields for the updated state, excluding `id`, `object`,
|
|
44
|
+
* `uri`, and `context` (filled by this helper).
|
|
45
|
+
* @returns A two-tuple in send order: the fully merged after payload, then the fully
|
|
46
|
+
* merged before payload. Spread into the matching `send*Message` call.
|
|
47
|
+
*/
|
|
48
|
+
export function generateBeforeAfterContext<T extends UITelemetryClientMethodKeys>(
|
|
49
|
+
id: EventAfterContext<T>['id'],
|
|
50
|
+
object: EventAfterContext<T>['object'],
|
|
51
|
+
before: Omit<EventBeforeContext<T>, 'id' | 'object' | 'uri' | 'context'>,
|
|
52
|
+
after: Omit<EventAfterContext<T>, 'id' | 'object' | 'uri' | 'context'>,
|
|
53
|
+
): [EventAfterContext<T> & { context: 'after' }, EventBeforeContext<T> & { context: 'before' }] {
|
|
54
|
+
const baseAttributes = getBaseDataAttributes(id, object);
|
|
55
|
+
return [
|
|
56
|
+
{ ...baseAttributes, context: 'after' as const, ...after },
|
|
57
|
+
{ ...baseAttributes, context: 'before' as const, ...before },
|
|
58
|
+
];
|
|
59
|
+
}
|