@redocly/theme 0.70.0-next.4 → 0.70.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/ConnectMCPButton.js +8 -13
- package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.d.ts +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +1 -1
- package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +30 -11
- package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +2 -1
- package/lib/components/Dropdown/Dropdown.d.ts +4 -2
- package/lib/components/Dropdown/Dropdown.js +28 -8
- package/lib/components/Dropdown/DropdownMenu.d.ts +1 -0
- package/lib/components/Dropdown/DropdownMenu.js +1 -1
- package/lib/components/Feedback/Reasons.js +3 -2
- package/lib/components/Filter/FilterCheckboxes.js +30 -11
- package/lib/components/Filter/FilterDateRange.js +36 -2
- package/lib/components/Image/Image.js +24 -4
- package/lib/components/Search/SearchAiActivity.d.ts +10 -0
- package/lib/components/Search/SearchAiActivity.js +278 -0
- package/lib/components/Search/SearchAiDialog.js +5 -1
- package/lib/components/Search/SearchAiMessage.d.ts +2 -1
- package/lib/components/Search/SearchAiMessage.js +20 -108
- package/lib/components/Search/SearchItem.js +1 -8
- package/lib/components/Search/variables.js +15 -2
- package/lib/components/Segmented/Segmented.js +23 -3
- package/lib/components/Select/Select.js +10 -3
- package/lib/components/Select/SelectInput.d.ts +1 -0
- package/lib/components/Select/SelectInput.js +5 -11
- package/lib/components/SplitButton/SplitButton.js +37 -3
- package/lib/core/constants/mcp.d.ts +2 -2
- package/lib/core/constants/mcp.js +3 -8
- package/lib/core/constants/search.js +5 -1
- package/lib/core/hooks/index.d.ts +2 -0
- package/lib/core/hooks/index.js +2 -0
- package/lib/core/hooks/menu/use-collapse.js +1 -1
- package/lib/core/hooks/search/use-search-ai-activity.d.ts +11 -0
- package/lib/core/hooks/search/use-search-ai-activity.js +15 -0
- package/lib/core/hooks/use-connect-mcp-button.js +2 -1
- package/lib/core/hooks/use-full-height.d.ts +1 -1
- package/lib/core/hooks/use-full-height.js +8 -8
- package/lib/core/hooks/use-tab-list.d.ts +31 -0
- package/lib/core/hooks/use-tab-list.js +49 -0
- package/lib/core/styles/global.d.ts +2 -0
- package/lib/core/styles/global.js +80 -76
- package/lib/core/styles/index.d.ts +1 -0
- package/lib/core/styles/index.js +3 -1
- package/lib/core/styles/search-ai-appear.d.ts +1 -0
- package/lib/core/styles/search-ai-appear.js +18 -0
- package/lib/core/types/dropdown.d.ts +1 -0
- package/lib/core/types/dropdown.js +3 -0
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/mcp.d.ts +2 -1
- package/lib/core/types/search.d.ts +23 -0
- package/lib/core/utils/Dynamic.d.ts +8 -1
- package/lib/core/utils/Dynamic.js +34 -3
- package/lib/core/utils/assign-ref.d.ts +2 -0
- package/lib/core/utils/assign-ref.js +12 -0
- package/lib/core/utils/content-segments.d.ts +12 -1
- package/lib/core/utils/content-segments.js +90 -5
- package/lib/core/utils/dropdown.d.ts +2 -0
- package/lib/core/utils/dropdown.js +12 -0
- package/lib/core/utils/index.d.ts +2 -0
- package/lib/core/utils/index.js +2 -0
- package/lib/core/utils/mcp.d.ts +2 -1
- package/lib/core/utils/mcp.js +5 -0
- package/lib/core/utils/search-ai-activity.d.ts +4 -0
- package/lib/core/utils/search-ai-activity.js +83 -0
- package/lib/markdoc/tags/connect-mcp.js +15 -0
- package/package.json +2 -2
- package/src/components/Buttons/ConnectMCPButton.tsx +8 -14
- package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.tsx +3 -1
- package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +37 -22
- package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +3 -2
- package/src/components/Dropdown/Dropdown.tsx +28 -6
- package/src/components/Dropdown/DropdownMenu.tsx +7 -1
- package/src/components/Feedback/Reasons.tsx +3 -2
- package/src/components/Filter/FilterCheckboxes.tsx +33 -18
- package/src/components/Filter/FilterDateRange.tsx +3 -2
- package/src/components/Image/Image.tsx +26 -5
- package/src/components/Search/SearchAiActivity.tsx +318 -0
- package/src/components/Search/SearchAiDialog.tsx +4 -3
- package/src/components/Search/SearchAiMessage.tsx +27 -135
- package/src/components/Search/SearchItem.tsx +2 -9
- package/src/components/Search/variables.ts +15 -2
- package/src/components/Segmented/Segmented.tsx +25 -16
- package/src/components/Select/Select.tsx +12 -6
- package/src/components/Select/SelectInput.tsx +10 -18
- package/src/components/SplitButton/SplitButton.tsx +8 -2
- package/src/core/constants/mcp.ts +3 -9
- package/src/core/constants/search.ts +5 -1
- package/src/core/hooks/__mocks__/index.ts +1 -0
- package/src/core/hooks/__mocks__/use-theme-hooks.ts +1 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/menu/use-collapse.ts +1 -1
- package/src/core/hooks/search/use-search-ai-activity.ts +31 -0
- package/src/core/hooks/use-connect-mcp-button.ts +7 -2
- package/src/core/hooks/use-full-height.ts +8 -8
- package/src/core/hooks/use-tab-list.ts +63 -0
- package/src/core/styles/global.ts +80 -75
- package/src/core/styles/index.ts +2 -1
- package/src/core/styles/search-ai-appear.ts +16 -0
- package/src/core/types/dropdown.ts +1 -0
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +1 -1
- package/src/core/types/mcp.ts +3 -1
- package/src/core/types/search.ts +21 -0
- package/src/core/utils/Dynamic.tsx +67 -35
- package/src/core/utils/assign-ref.ts +9 -0
- package/src/core/utils/content-segments.ts +98 -6
- package/src/core/utils/dropdown.ts +14 -0
- package/src/core/utils/index.ts +2 -0
- package/src/core/utils/mcp.ts +7 -1
- package/src/core/utils/search-ai-activity.ts +110 -0
- package/src/icons/CheckboxIcon/CheckboxIcon.tsx +1 -0
- package/src/icons/ChevronSortIcon/ChevronSortIcon.tsx +1 -0
- package/src/markdoc/tags/connect-mcp.ts +16 -1
|
@@ -5,34 +5,43 @@ import type { ForwardedRef, ReactElement } from 'react';
|
|
|
5
5
|
import type { SegmentedProps } from '@redocly/theme/core/types';
|
|
6
6
|
|
|
7
7
|
import { typedMemo } from '@redocly/theme/core/hoc/typed-memo';
|
|
8
|
+
import { useTabList } from '@redocly/theme/core/hooks';
|
|
8
9
|
|
|
9
10
|
function SegmentedComponent<T>(
|
|
10
11
|
{ options, onChange, value, className = '', size = 'regular' }: SegmentedProps<T>,
|
|
11
12
|
ref?: ForwardedRef<HTMLDivElement>,
|
|
12
13
|
): ReactElement {
|
|
14
|
+
const { tabListProps, getTabProps } = useTabList({
|
|
15
|
+
count: options.length,
|
|
16
|
+
activeIndex: options.findIndex((opt) => value == opt.value),
|
|
17
|
+
onChange: (index) => onChange(options[index]),
|
|
18
|
+
});
|
|
19
|
+
|
|
13
20
|
return (
|
|
14
21
|
<SegmentedGroup
|
|
15
22
|
ref={ref}
|
|
16
23
|
data-component-name="Segmented/Segmented"
|
|
17
24
|
className={`tag-grey ${size} ${className}`}
|
|
18
|
-
|
|
25
|
+
{...tabListProps}
|
|
19
26
|
$size={size}
|
|
20
27
|
>
|
|
21
|
-
{options.map((opt, index) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
{options.map((opt, index) => {
|
|
29
|
+
const { 'aria-controls': _noPanel, ...tabProps } = getTabProps(index);
|
|
30
|
+
return (
|
|
31
|
+
<React.Fragment key={index}>
|
|
32
|
+
{opt.divider}
|
|
33
|
+
<SegmentedItem
|
|
34
|
+
key={opt.label}
|
|
35
|
+
{...tabProps}
|
|
36
|
+
title={opt.label}
|
|
37
|
+
$isActive={value == opt.value}
|
|
38
|
+
$size={size}
|
|
39
|
+
>
|
|
40
|
+
{opt.element || opt.label}
|
|
41
|
+
</SegmentedItem>
|
|
42
|
+
</React.Fragment>
|
|
43
|
+
);
|
|
44
|
+
})}
|
|
36
45
|
</SegmentedGroup>
|
|
37
46
|
);
|
|
38
47
|
}
|
|
@@ -63,6 +63,7 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
63
63
|
const [filteredOptions, setFilteredOptions] = useState<SelectOption<T>[]>(options);
|
|
64
64
|
const [stickyInputValue, setStickyInputValue] = useState<string>(placeholder || '');
|
|
65
65
|
const inputId = useId();
|
|
66
|
+
const listboxId = `${inputId}-listbox`;
|
|
66
67
|
|
|
67
68
|
useOutsideClick(selectRef, () => {
|
|
68
69
|
setDropdownActive(false);
|
|
@@ -189,7 +190,8 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
189
190
|
return (
|
|
190
191
|
<SelectInput
|
|
191
192
|
id={inputId}
|
|
192
|
-
listboxId={
|
|
193
|
+
listboxId={listboxId}
|
|
194
|
+
expanded={dropdownActive}
|
|
193
195
|
selectedOptions={selectedOptions}
|
|
194
196
|
searchValue={searchValue}
|
|
195
197
|
placeholder={placeholder}
|
|
@@ -223,10 +225,14 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
223
225
|
setDropdownActive(!dropdownActive);
|
|
224
226
|
}
|
|
225
227
|
}}
|
|
226
|
-
tabIndex={0}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
tabIndex={searchable ? undefined : 0}
|
|
229
|
+
{...(!searchable && {
|
|
230
|
+
role: 'combobox',
|
|
231
|
+
'aria-haspopup': 'listbox' as const,
|
|
232
|
+
'aria-expanded': dropdownActive,
|
|
233
|
+
'aria-controls': listboxId,
|
|
234
|
+
'aria-label': 'Select option',
|
|
235
|
+
})}
|
|
230
236
|
onFocus={(e) => {
|
|
231
237
|
if (e.target === selectRef.current) {
|
|
232
238
|
selectInputRef.current?.focus();
|
|
@@ -245,7 +251,7 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
245
251
|
alignment={alignment}
|
|
246
252
|
active={!renderInput ? dropdownActive : undefined}
|
|
247
253
|
>
|
|
248
|
-
<SelectDropdownMenu role="listbox" footer={footer}>
|
|
254
|
+
<SelectDropdownMenu id={listboxId} role="listbox" footer={footer}>
|
|
249
255
|
{filteredOptions.length === 0 ? (
|
|
250
256
|
<DropdownMenuItem disabled>
|
|
251
257
|
{translate('select.noResults', 'No results')}
|
|
@@ -6,6 +6,7 @@ import type { SelectOption } from '@redocly/theme/core/types';
|
|
|
6
6
|
import { Tag } from '@redocly/theme/components/Tag/Tag';
|
|
7
7
|
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
|
|
8
8
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
9
|
+
import { assignRef } from '@redocly/theme/core/utils';
|
|
9
10
|
|
|
10
11
|
type SelectInputProps<T> = {
|
|
11
12
|
id?: string;
|
|
@@ -22,6 +23,7 @@ type SelectInputProps<T> = {
|
|
|
22
23
|
placeholder?: string;
|
|
23
24
|
multiple?: boolean;
|
|
24
25
|
searchable?: boolean;
|
|
26
|
+
expanded?: boolean;
|
|
25
27
|
clearable?: boolean;
|
|
26
28
|
inputRef?: React.ForwardedRef<HTMLInputElement>;
|
|
27
29
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
@@ -48,6 +50,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
48
50
|
stickyValue,
|
|
49
51
|
multiple,
|
|
50
52
|
searchable,
|
|
53
|
+
expanded,
|
|
51
54
|
clearable,
|
|
52
55
|
clearHandler,
|
|
53
56
|
searchHandler,
|
|
@@ -123,17 +126,15 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
123
126
|
if (!input) return;
|
|
124
127
|
|
|
125
128
|
inputRef.current = input;
|
|
126
|
-
|
|
127
|
-
if (!forwardedInputRef) return;
|
|
128
|
-
|
|
129
|
-
if (typeof forwardedInputRef === 'function') {
|
|
130
|
-
forwardedInputRef(input);
|
|
131
|
-
} else {
|
|
132
|
-
forwardedInputRef.current = input;
|
|
133
|
-
}
|
|
129
|
+
assignRef(forwardedInputRef, input);
|
|
134
130
|
}}
|
|
135
131
|
$width={multiple ? (!searchValue && selectedOptions.length ? '10px' : 'auto') : '100%'}
|
|
136
132
|
tabIndex={0}
|
|
133
|
+
role="combobox"
|
|
134
|
+
aria-expanded={!!expanded}
|
|
135
|
+
aria-controls={listboxId}
|
|
136
|
+
aria-autocomplete="list"
|
|
137
|
+
aria-label="Select option"
|
|
137
138
|
onFocus={(e) => {
|
|
138
139
|
e.stopPropagation();
|
|
139
140
|
}}
|
|
@@ -156,16 +157,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
156
157
|
);
|
|
157
158
|
|
|
158
159
|
return (
|
|
159
|
-
<SelectInputWrapper
|
|
160
|
-
id={id}
|
|
161
|
-
onFocus={onFocusHandler}
|
|
162
|
-
onClick={onClickHandler}
|
|
163
|
-
role="button"
|
|
164
|
-
aria-haspopup="listbox"
|
|
165
|
-
aria-controls={listboxId}
|
|
166
|
-
aria-owns={listboxId}
|
|
167
|
-
aria-label="Select option"
|
|
168
|
-
>
|
|
160
|
+
<SelectInputWrapper id={id} onFocus={onFocusHandler} onClick={onClickHandler}>
|
|
169
161
|
{!onlyIcon && (
|
|
170
162
|
<>
|
|
171
163
|
<SelectInputValue>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import type { ComponentProps, JSX } from 'react';
|
|
@@ -45,9 +45,14 @@ export function SplitButton(props: SplitButtonProps): JSX.Element {
|
|
|
45
45
|
} = props;
|
|
46
46
|
|
|
47
47
|
const hasMenu = typeof children === 'function' || React.Children.toArray(children).length > 0;
|
|
48
|
+
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
48
49
|
|
|
49
50
|
return (
|
|
50
|
-
<SplitButtonWrapper
|
|
51
|
+
<SplitButtonWrapper
|
|
52
|
+
data-component-name="SplitButton/SplitButton"
|
|
53
|
+
className={className}
|
|
54
|
+
ref={wrapperRef}
|
|
55
|
+
>
|
|
51
56
|
<ButtonGroup variant={variant} size={size}>
|
|
52
57
|
{button}
|
|
53
58
|
{hasMenu ? (
|
|
@@ -56,6 +61,7 @@ export function SplitButton(props: SplitButtonProps): JSX.Element {
|
|
|
56
61
|
placement={placement}
|
|
57
62
|
alignment={alignment}
|
|
58
63
|
portalled={portalled}
|
|
64
|
+
alignmentAnchorRef={wrapperRef}
|
|
59
65
|
withArrow
|
|
60
66
|
onOpen={onOpen}
|
|
61
67
|
onClose={onClose}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import type { MCPOption } from '../types/mcp';
|
|
2
|
-
|
|
3
1
|
export const DEFAULT_MCP_SERVER_NAME = 'mcp-server';
|
|
4
2
|
|
|
5
3
|
export const MCP_ROUTE = '/mcp';
|
|
6
4
|
|
|
7
5
|
export const PUBLIC_MCP_ROUTE = '/mcp-public';
|
|
8
6
|
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'claude-code',
|
|
13
|
-
'codex',
|
|
14
|
-
'copy',
|
|
15
|
-
];
|
|
7
|
+
export const MCP_OPTIONS = ['cursor', 'vscode', 'claude-code', 'codex', 'copy'] as const;
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_MCP_OPTIONS = [...MCP_OPTIONS];
|
|
@@ -53,7 +53,7 @@ export const TOOL_CALL_DISPLAY_TEXT: Partial<
|
|
|
53
53
|
> = {
|
|
54
54
|
[ToolCallName.SearchDocumentation]: {
|
|
55
55
|
inProgressText: 'Searching documentation...',
|
|
56
|
-
completedText: '
|
|
56
|
+
completedText: 'Searched the documentation',
|
|
57
57
|
},
|
|
58
58
|
[ToolCallName.ListApis]: {
|
|
59
59
|
inProgressText: 'Browsing APIs...',
|
|
@@ -75,6 +75,10 @@ export const TOOL_CALL_DISPLAY_TEXT: Partial<
|
|
|
75
75
|
inProgressText: 'Reading API description...',
|
|
76
76
|
completedText: 'Read API description',
|
|
77
77
|
},
|
|
78
|
+
[ToolCallName.Execute]: {
|
|
79
|
+
inProgressText: 'Running code...',
|
|
80
|
+
completedText: 'Ran code',
|
|
81
|
+
},
|
|
78
82
|
[ToolCallName.DescribeTools]: {
|
|
79
83
|
inProgressText: 'Discovering available tools...',
|
|
80
84
|
completedText: 'Discovered available tools',
|
|
@@ -23,6 +23,7 @@ export * from '../menu/use-mobile-menu-levels';
|
|
|
23
23
|
export * from '../feedback/use-report-dialog';
|
|
24
24
|
export * from '../use-product-picker';
|
|
25
25
|
export * from '../search/use-search-dialog';
|
|
26
|
+
export * from '../search/use-search-ai-activity';
|
|
26
27
|
export * from '../use-language-picker';
|
|
27
28
|
export * from './use-element-size';
|
|
28
29
|
export * from './use-time-ago';
|
|
@@ -20,6 +20,7 @@ export const useThemeHooks = vi.fn(() => ({
|
|
|
20
20
|
sendPageActionsButtonClickedMessage: vi.fn(),
|
|
21
21
|
sendColorModeSwitchedMessage: vi.fn(),
|
|
22
22
|
sendConsentChangedMessage: vi.fn(),
|
|
23
|
+
sendFilterCheckboxToggledMessage: vi.fn(),
|
|
23
24
|
})),
|
|
24
25
|
useBreadcrumbs: vi.fn().mockReturnValue({ breadcrumbs: [], siblings: undefined }),
|
|
25
26
|
useBanner: vi.fn(() => ({
|
package/src/core/hooks/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from './menu/use-mobile-menu-levels';
|
|
|
21
21
|
export * from './menu/use-collapse';
|
|
22
22
|
export * from './feedback/use-report-dialog';
|
|
23
23
|
export * from './use-tabs';
|
|
24
|
+
export * from './use-tab-list';
|
|
24
25
|
export * from './use-focus-trap';
|
|
25
26
|
export * from './use-consent-dialog';
|
|
26
27
|
export * from './use-consent-telemetry';
|
|
@@ -29,6 +30,7 @@ export * from './use-product-picker';
|
|
|
29
30
|
export * from './search/use-search-dialog';
|
|
30
31
|
export * from './search/use-search-filter';
|
|
31
32
|
export * from './search/use-feedback-tooltip';
|
|
33
|
+
export * from './search/use-search-ai-activity';
|
|
32
34
|
export * from './use-controlled-state';
|
|
33
35
|
export * from './use-codeblock-tabs-controls';
|
|
34
36
|
export * from './code-walkthrough/use-code-walkthrough';
|
|
@@ -207,5 +207,5 @@ export function useEvent<T extends (...args: any[]) => any>(callback?: T): T {
|
|
|
207
207
|
ref.current = callback;
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
-
return useCallback((
|
|
210
|
+
return useCallback((...args: Parameters<T>) => ref.current?.(...args), []) as T;
|
|
211
211
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SearchAiActivityStatus, SearchAiActivityStep, ToolCall } from '../../types/search';
|
|
2
|
+
|
|
3
|
+
import { useThemeHooks } from '../use-theme-hooks';
|
|
4
|
+
import { getSearchAiActivityStatus, getSearchAiActivityStep } from '../../utils/search-ai-activity';
|
|
5
|
+
|
|
6
|
+
type UseSearchAiActivityParams = {
|
|
7
|
+
toolCalls: ToolCall[];
|
|
8
|
+
isActive: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type UseSearchAiActivityReturn = {
|
|
12
|
+
status: SearchAiActivityStatus;
|
|
13
|
+
steps: SearchAiActivityStep[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function useSearchAiActivity({
|
|
17
|
+
toolCalls,
|
|
18
|
+
isActive,
|
|
19
|
+
}: UseSearchAiActivityParams): UseSearchAiActivityReturn {
|
|
20
|
+
const { useTranslate } = useThemeHooks();
|
|
21
|
+
const { translate } = useTranslate();
|
|
22
|
+
|
|
23
|
+
const runningToolCall = isActive ? toolCalls.find((toolCall) => !toolCall.result) : undefined;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
status: getSearchAiActivityStatus(toolCalls, isActive, runningToolCall, translate),
|
|
27
|
+
steps: toolCalls.map((toolCall) =>
|
|
28
|
+
getSearchAiActivityStep(toolCall, isActive && !toolCall.result, translate),
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -7,7 +7,7 @@ import { useThemeHooks } from './use-theme-hooks';
|
|
|
7
7
|
import { useMCPConfig } from './use-mcp-config';
|
|
8
8
|
import { ClipboardService } from '../utils/clipboard-service';
|
|
9
9
|
import { DEFAULT_MCP_OPTIONS } from '../constants/mcp';
|
|
10
|
-
import { generateClaudeCodeCommand, generateCodexCommand } from '../utils/mcp';
|
|
10
|
+
import { generateClaudeCodeCommand, generateCodexCommand, isMCPOption } from '../utils/mcp';
|
|
11
11
|
import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
|
|
12
12
|
|
|
13
13
|
export type McpButtonOptions = {
|
|
@@ -80,7 +80,12 @@ export function useConnectMCPButton({
|
|
|
80
80
|
[translate],
|
|
81
81
|
);
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
// Markdoc reports attribute errors but still renders the tag, so `options` can be any value.
|
|
84
|
+
const visibleOptions = useMemo(
|
|
85
|
+
() =>
|
|
86
|
+
Array.isArray(options) ? [...new Set(options.filter(isMCPOption))] : [...DEFAULT_MCP_OPTIONS],
|
|
87
|
+
[options],
|
|
88
|
+
);
|
|
84
89
|
|
|
85
90
|
return {
|
|
86
91
|
copiedOption,
|
|
@@ -3,9 +3,9 @@ import throttle from 'lodash.throttle';
|
|
|
3
3
|
|
|
4
4
|
import type { RefObject } from 'react';
|
|
5
5
|
|
|
6
|
-
export function useFullHeight(
|
|
6
|
+
export function useFullHeight(elementRef: RefObject<HTMLElement | null>): void {
|
|
7
7
|
const handler = useCallback(() => {
|
|
8
|
-
if (!
|
|
8
|
+
if (!elementRef.current) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -14,19 +14,19 @@ export function useFullHeight(element: RefObject<HTMLElement | null>): void {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const elementTop =
|
|
17
|
+
const elementTop = elementRef.current.getBoundingClientRect().top;
|
|
18
18
|
const footerOffset = footer.offsetTop - window.scrollY - elementTop;
|
|
19
19
|
if (footerOffset > window.innerHeight) {
|
|
20
|
-
|
|
20
|
+
elementRef.current.style.maxHeight = `${window.innerHeight - elementTop}px`;
|
|
21
21
|
} else {
|
|
22
|
-
|
|
22
|
+
elementRef.current.style.maxHeight = `${footerOffset}px`;
|
|
23
23
|
}
|
|
24
|
-
}, [
|
|
24
|
+
}, [elementRef]);
|
|
25
25
|
|
|
26
26
|
const throttledHandler = useMemo(() => throttle(handler, 150), [handler]);
|
|
27
27
|
|
|
28
28
|
useEffect(() => {
|
|
29
|
-
if (typeof window === 'undefined' || !
|
|
29
|
+
if (typeof window === 'undefined' || !elementRef.current) {
|
|
30
30
|
return () => {
|
|
31
31
|
// nope
|
|
32
32
|
};
|
|
@@ -45,5 +45,5 @@ export function useFullHeight(element: RefObject<HTMLElement | null>): void {
|
|
|
45
45
|
window.removeEventListener('scroll', throttledHandler);
|
|
46
46
|
window.removeEventListener('resize', throttledHandler);
|
|
47
47
|
};
|
|
48
|
-
}, [throttledHandler,
|
|
48
|
+
}, [throttledHandler, elementRef, handler]);
|
|
49
49
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useCallback, useId, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { KeyboardEvent } from 'react';
|
|
4
|
+
|
|
5
|
+
type UseTabListProps = {
|
|
6
|
+
count: number;
|
|
7
|
+
activeIndex: number;
|
|
8
|
+
onChange: (index: number) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for a WAI-ARIA tab list with automatic activation: only the active tab is a tab stop,
|
|
13
|
+
* and Left/Right/Home/End select and focus another tab.
|
|
14
|
+
*/
|
|
15
|
+
export function useTabList({ count, activeIndex, onChange }: UseTabListProps) {
|
|
16
|
+
const idPrefix = useId();
|
|
17
|
+
const focusableIndex = activeIndex >= 0 ? activeIndex : 0;
|
|
18
|
+
const tabRefs = useRef<(HTMLElement | null)[]>([]);
|
|
19
|
+
|
|
20
|
+
const select = useCallback(
|
|
21
|
+
(index: number) => {
|
|
22
|
+
onChange(index);
|
|
23
|
+
tabRefs.current[index]?.focus();
|
|
24
|
+
},
|
|
25
|
+
[onChange],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const onKeyDown = useCallback(
|
|
29
|
+
(event: KeyboardEvent, index: number) => {
|
|
30
|
+
const target = (<Record<string, number>>{
|
|
31
|
+
ArrowRight: (index + 1) % count,
|
|
32
|
+
ArrowLeft: (index - 1 + count) % count,
|
|
33
|
+
Home: 0,
|
|
34
|
+
End: count - 1,
|
|
35
|
+
})[event.key];
|
|
36
|
+
if (target === undefined) return;
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
select(target);
|
|
39
|
+
},
|
|
40
|
+
[count, select],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const getTabProps = (index: number) => ({
|
|
44
|
+
id: `${idPrefix}-tab-${index}`,
|
|
45
|
+
role: 'tab',
|
|
46
|
+
'aria-selected': index === activeIndex,
|
|
47
|
+
'aria-controls': `${idPrefix}-panel-${index}`,
|
|
48
|
+
tabIndex: index === focusableIndex ? 0 : -1,
|
|
49
|
+
ref: (element: HTMLElement | null) => {
|
|
50
|
+
tabRefs.current[index] = element;
|
|
51
|
+
},
|
|
52
|
+
onClick: () => select(index),
|
|
53
|
+
onKeyDown: (event: KeyboardEvent) => onKeyDown(event, index),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const getTabPanelProps = (index: number) => ({
|
|
57
|
+
id: `${idPrefix}-panel-${index}`,
|
|
58
|
+
role: 'tabpanel',
|
|
59
|
+
'aria-labelledby': `${idPrefix}-tab-${index}`,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { tabListProps: { role: 'tablist' }, getTabProps, getTabPanelProps };
|
|
63
|
+
}
|
|
@@ -1330,83 +1330,88 @@ const replay = css`
|
|
|
1330
1330
|
`;
|
|
1331
1331
|
|
|
1332
1332
|
|
|
1333
|
+
/** The light theme tokens without a selector, for a host that sets them on one element. */
|
|
1334
|
+
export const tokens = css`
|
|
1335
|
+
${accordion}
|
|
1336
|
+
${admonition}
|
|
1337
|
+
${apiReferenceDocs}
|
|
1338
|
+
${apiReferencePanels}
|
|
1339
|
+
${badges}
|
|
1340
|
+
${banner}
|
|
1341
|
+
${borders}
|
|
1342
|
+
${consent}
|
|
1343
|
+
${breadcrumbs}
|
|
1344
|
+
${button}
|
|
1345
|
+
${aiAssistantButton}
|
|
1346
|
+
${cards}
|
|
1347
|
+
${catalog}
|
|
1348
|
+
${catalogClassic}
|
|
1349
|
+
${code}
|
|
1350
|
+
${codeWalkthrough}
|
|
1351
|
+
${docsDropdown}
|
|
1352
|
+
${dropdown}
|
|
1353
|
+
${error}
|
|
1354
|
+
${filter}
|
|
1355
|
+
${footer}
|
|
1356
|
+
${headingsTypography}
|
|
1357
|
+
${httpTag}
|
|
1358
|
+
${image}
|
|
1359
|
+
${inputs}
|
|
1360
|
+
${languagePicker}
|
|
1361
|
+
${lastUpdated}
|
|
1362
|
+
${links}
|
|
1363
|
+
${loadProgressBar}
|
|
1364
|
+
${logo}
|
|
1365
|
+
${markdown}
|
|
1366
|
+
${markdownAccordion}
|
|
1367
|
+
${markdownTabs}
|
|
1368
|
+
${markdownNumberedList}
|
|
1369
|
+
${diagram}
|
|
1370
|
+
${menu}
|
|
1371
|
+
${mobileMenu}
|
|
1372
|
+
${modal}
|
|
1373
|
+
${navbar}
|
|
1374
|
+
${pages}
|
|
1375
|
+
${productPicker}
|
|
1376
|
+
${responsePanelColors}
|
|
1377
|
+
${search}
|
|
1378
|
+
${navigationTabs}
|
|
1379
|
+
${select}
|
|
1380
|
+
${sidebar}
|
|
1381
|
+
${sizeAndSpace}
|
|
1382
|
+
${tag}
|
|
1383
|
+
${themeColors}
|
|
1384
|
+
${activeBrandPaletteLight}
|
|
1385
|
+
${tile}
|
|
1386
|
+
${toc}
|
|
1387
|
+
${tooltip}
|
|
1388
|
+
${typography}
|
|
1389
|
+
${userMenu}
|
|
1390
|
+
${versionPicker}
|
|
1391
|
+
${zIndexDepth}
|
|
1392
|
+
${scorecardColors}
|
|
1393
|
+
${statusCode}
|
|
1394
|
+
${tab}
|
|
1395
|
+
${icon}
|
|
1396
|
+
${tree}
|
|
1397
|
+
${segmented}
|
|
1398
|
+
${switcher}
|
|
1399
|
+
${checkbox}
|
|
1400
|
+
${feedback}
|
|
1401
|
+
${scorecard}
|
|
1402
|
+
${datePicker}
|
|
1403
|
+
${replay}
|
|
1404
|
+
${skipContent}
|
|
1405
|
+
${pageActions}
|
|
1406
|
+
${svgViewer}
|
|
1407
|
+
${toast}
|
|
1408
|
+
${pageNavigation}
|
|
1409
|
+
|
|
1410
|
+
`;
|
|
1411
|
+
|
|
1333
1412
|
export const styles = css<{ palette?: string }>`
|
|
1334
1413
|
:root {
|
|
1335
|
-
${
|
|
1336
|
-
${admonition}
|
|
1337
|
-
${apiReferenceDocs}
|
|
1338
|
-
${apiReferencePanels}
|
|
1339
|
-
${badges}
|
|
1340
|
-
${banner}
|
|
1341
|
-
${borders}
|
|
1342
|
-
${consent}
|
|
1343
|
-
${breadcrumbs}
|
|
1344
|
-
${button}
|
|
1345
|
-
${aiAssistantButton}
|
|
1346
|
-
${cards}
|
|
1347
|
-
${catalog}
|
|
1348
|
-
${catalogClassic}
|
|
1349
|
-
${code}
|
|
1350
|
-
${codeWalkthrough}
|
|
1351
|
-
${docsDropdown}
|
|
1352
|
-
${dropdown}
|
|
1353
|
-
${error}
|
|
1354
|
-
${filter}
|
|
1355
|
-
${footer}
|
|
1356
|
-
${headingsTypography}
|
|
1357
|
-
${httpTag}
|
|
1358
|
-
${image}
|
|
1359
|
-
${inputs}
|
|
1360
|
-
${languagePicker}
|
|
1361
|
-
${lastUpdated}
|
|
1362
|
-
${links}
|
|
1363
|
-
${loadProgressBar}
|
|
1364
|
-
${logo}
|
|
1365
|
-
${markdown}
|
|
1366
|
-
${markdownAccordion}
|
|
1367
|
-
${markdownTabs}
|
|
1368
|
-
${markdownNumberedList}
|
|
1369
|
-
${diagram}
|
|
1370
|
-
${menu}
|
|
1371
|
-
${mobileMenu}
|
|
1372
|
-
${modal}
|
|
1373
|
-
${navbar}
|
|
1374
|
-
${pages}
|
|
1375
|
-
${productPicker}
|
|
1376
|
-
${responsePanelColors}
|
|
1377
|
-
${search}
|
|
1378
|
-
${navigationTabs}
|
|
1379
|
-
${select}
|
|
1380
|
-
${sidebar}
|
|
1381
|
-
${sizeAndSpace}
|
|
1382
|
-
${tag}
|
|
1383
|
-
${themeColors}
|
|
1384
|
-
${activeBrandPaletteLight}
|
|
1385
|
-
${tile}
|
|
1386
|
-
${toc}
|
|
1387
|
-
${tooltip}
|
|
1388
|
-
${typography}
|
|
1389
|
-
${userMenu}
|
|
1390
|
-
${versionPicker}
|
|
1391
|
-
${zIndexDepth}
|
|
1392
|
-
${scorecardColors}
|
|
1393
|
-
${statusCode}
|
|
1394
|
-
${tab}
|
|
1395
|
-
${icon}
|
|
1396
|
-
${tree}
|
|
1397
|
-
${segmented}
|
|
1398
|
-
${switcher}
|
|
1399
|
-
${checkbox}
|
|
1400
|
-
${feedback}
|
|
1401
|
-
${scorecard}
|
|
1402
|
-
${datePicker}
|
|
1403
|
-
${replay}
|
|
1404
|
-
${skipContent}
|
|
1405
|
-
${pageActions}
|
|
1406
|
-
${svgViewer}
|
|
1407
|
-
${toast}
|
|
1408
|
-
${pageNavigation}
|
|
1409
|
-
|
|
1414
|
+
${tokens}
|
|
1410
1415
|
background-color: var(--bg-color);
|
|
1411
1416
|
color: var(--text-color-primary);
|
|
1412
1417
|
font-family: var(--font-family-base);
|
package/src/core/styles/index.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { css, keyframes } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
const appear = keyframes`
|
|
4
|
+
from {
|
|
5
|
+
opacity: 0;
|
|
6
|
+
transform: translateY(4px);
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
export const searchAiAppearAnimation = css`
|
|
11
|
+
animation: ${appear} var(--search-ai-activity-fade-duration) ease-out;
|
|
12
|
+
|
|
13
|
+
@media (prefers-reduced-motion: reduce) {
|
|
14
|
+
animation: none;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type DropdownAlignment = 'start' | 'end';
|
package/src/core/types/index.ts
CHANGED
package/src/core/types/l10n.ts
CHANGED
|
@@ -113,7 +113,7 @@ export type TranslationKey =
|
|
|
113
113
|
| 'search.ai.feedback.thanks'
|
|
114
114
|
| 'search.ai.toolCall.executed'
|
|
115
115
|
| 'search.ai.toolCall.executing'
|
|
116
|
-
| 'search.ai.toolCall.
|
|
116
|
+
| 'search.ai.toolCall.usedTools'
|
|
117
117
|
| 'search.ai.button'
|
|
118
118
|
| 'search.ai.label'
|
|
119
119
|
| 'search.ai.disclaimer'
|
package/src/core/types/mcp.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { MCP_OPTIONS } from '../constants/mcp';
|
|
2
|
+
|
|
1
3
|
export type MCPClientType = 'cursor' | 'vscode';
|
|
2
4
|
|
|
3
|
-
export type MCPOption =
|
|
5
|
+
export type MCPOption = (typeof MCP_OPTIONS)[number];
|
|
4
6
|
|
|
5
7
|
export type McpConnectionParams = {
|
|
6
8
|
serverName: string;
|