@redocly/theme 0.70.0-next.5 → 0.70.0-next.7
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.js +1 -1
- 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/Filter/FilterDateRange.js +36 -2
- package/lib/components/Portal/Portal.js +37 -5
- package/lib/components/Search/SearchAiActionButtons.js +8 -3
- 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/SearchDialog.js +10 -0
- package/lib/components/Search/SearchItem.js +1 -8
- package/lib/components/Search/variables.js +15 -2
- package/lib/components/Select/SelectInput.js +2 -8
- 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/contexts/PortalContainerContext.d.ts +2 -0
- package/lib/core/contexts/PortalContainerContext.js +7 -0
- 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-persisted-quick-filter.d.ts +21 -0
- package/lib/core/hooks/search/use-persisted-quick-filter.js +92 -0
- 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/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/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 +3 -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/CatalogFilterDateRange.tsx +3 -2
- package/src/components/Dropdown/Dropdown.tsx +28 -6
- package/src/components/Filter/FilterDateRange.tsx +3 -2
- package/src/components/Portal/Portal.tsx +4 -2
- package/src/components/Search/SearchAiActionButtons.tsx +31 -19
- 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/SearchDialog.tsx +11 -0
- package/src/components/Search/SearchItem.tsx +2 -9
- package/src/components/Search/variables.ts +15 -2
- package/src/components/Select/SelectInput.tsx +2 -8
- 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/contexts/PortalContainerContext.tsx +4 -0
- package/src/core/hooks/__mocks__/index.ts +2 -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-persisted-quick-filter.ts +125 -0
- 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/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 +4 -1
- package/src/core/types/mcp.ts +3 -1
- package/src/core/types/search.ts +21 -0
- 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/markdoc/tags/connect-mcp.ts +16 -1
|
@@ -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;
|
|
@@ -125,14 +126,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
125
126
|
if (!input) return;
|
|
126
127
|
|
|
127
128
|
inputRef.current = input;
|
|
128
|
-
|
|
129
|
-
if (!forwardedInputRef) return;
|
|
130
|
-
|
|
131
|
-
if (typeof forwardedInputRef === 'function') {
|
|
132
|
-
forwardedInputRef(input);
|
|
133
|
-
} else {
|
|
134
|
-
forwardedInputRef.current = input;
|
|
135
|
-
}
|
|
129
|
+
assignRef(forwardedInputRef, input);
|
|
136
130
|
}}
|
|
137
131
|
$width={multiple ? (!searchValue && selectedOptions.length ? '10px' : 'auto') : '100%'}
|
|
138
132
|
tabIndex={0}
|
|
@@ -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,8 @@ 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';
|
|
27
|
+
export * from '../search/use-persisted-quick-filter';
|
|
26
28
|
export * from '../use-language-picker';
|
|
27
29
|
export * from './use-element-size';
|
|
28
30
|
export * from './use-time-ago';
|
package/src/core/hooks/index.ts
CHANGED
|
@@ -30,6 +30,8 @@ export * from './use-product-picker';
|
|
|
30
30
|
export * from './search/use-search-dialog';
|
|
31
31
|
export * from './search/use-search-filter';
|
|
32
32
|
export * from './search/use-feedback-tooltip';
|
|
33
|
+
export * from './search/use-search-ai-activity';
|
|
34
|
+
export * from './search/use-persisted-quick-filter';
|
|
33
35
|
export * from './use-controlled-state';
|
|
34
36
|
export * from './use-codeblock-tabs-controls';
|
|
35
37
|
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,125 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Dispatch, SetStateAction } from 'react';
|
|
4
|
+
import type { SearchFacet, SearchFilterItem } from '../../types/search';
|
|
5
|
+
|
|
6
|
+
import { createStore, useStore } from '../use-store';
|
|
7
|
+
|
|
8
|
+
type StoredQuickFilter = Pick<SearchFilterItem, 'field' | 'values'>;
|
|
9
|
+
|
|
10
|
+
const quickFilterStore = createStore<StoredQuickFilter | null>({
|
|
11
|
+
storageKey: 'searchQuickFilter',
|
|
12
|
+
storageType: 'sessionStorage',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const findQuickFilter = (filter: SearchFilterItem[]) => filter.find((item) => item.isQuickFilter);
|
|
16
|
+
|
|
17
|
+
const withQuickFilter = (filter: SearchFilterItem[], quickFilter: StoredQuickFilter) => [
|
|
18
|
+
...filter.filter((item) => !item.isQuickFilter),
|
|
19
|
+
...(quickFilter.values.length ? [{ ...quickFilter, isQuickFilter: true }] : []),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const groupFacetValues = (facets: SearchFacet[], field: string) => {
|
|
23
|
+
const facet = facets.find((item) => item.field === field);
|
|
24
|
+
const values = facet?.values ?? [];
|
|
25
|
+
return {
|
|
26
|
+
present: facet !== undefined,
|
|
27
|
+
values: values.map((item) => (typeof item === 'string' ? item : item.value)),
|
|
28
|
+
counted: values.some((item) => typeof item !== 'string' && item.isCounterVisible),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const isRestorable = (
|
|
33
|
+
stored: StoredQuickFilter | null,
|
|
34
|
+
groupField: string,
|
|
35
|
+
): stored is StoredQuickFilter =>
|
|
36
|
+
!!stored &&
|
|
37
|
+
stored.field === groupField &&
|
|
38
|
+
Array.isArray(stored.values) &&
|
|
39
|
+
stored.values.length > 0 &&
|
|
40
|
+
stored.values.every((value) => typeof value === 'string');
|
|
41
|
+
|
|
42
|
+
type UsePersistedQuickFilterOptions = {
|
|
43
|
+
filter: SearchFilterItem[];
|
|
44
|
+
setFilter: Dispatch<SetStateAction<SearchFilterItem[]>>;
|
|
45
|
+
facets: SearchFacet[];
|
|
46
|
+
groupField: string;
|
|
47
|
+
query: string;
|
|
48
|
+
isSearchLoading: boolean;
|
|
49
|
+
searchError: string | null;
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Remembers the quick filter on `groupField` for the browser tab and restores it on mount unless
|
|
55
|
+
* one is already selected. Restored values are dropped once known not to exist: absent from the
|
|
56
|
+
* full value list, or from the `groupField` facet left by the first search that finishes without
|
|
57
|
+
* error on an empty query (the restored value is that search's filter, so a value without
|
|
58
|
+
* documents is not counted). Does nothing while `enabled` is false.
|
|
59
|
+
*/
|
|
60
|
+
export function usePersistedQuickFilter({
|
|
61
|
+
filter,
|
|
62
|
+
setFilter,
|
|
63
|
+
facets,
|
|
64
|
+
groupField,
|
|
65
|
+
query,
|
|
66
|
+
isSearchLoading,
|
|
67
|
+
searchError,
|
|
68
|
+
enabled = true,
|
|
69
|
+
}: UsePersistedQuickFilterOptions): void {
|
|
70
|
+
const [stored, setStored] = useStore(quickFilterStore, null);
|
|
71
|
+
const [initial] = useState(() =>
|
|
72
|
+
enabled && isRestorable(stored, groupField) && !findQuickFilter(filter) ? stored.values : null,
|
|
73
|
+
);
|
|
74
|
+
const phaseRef = useRef<'restoring' | 'validating' | 'done'>(initial ? 'restoring' : 'done');
|
|
75
|
+
const searchedRef = useRef(false);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!initial) return;
|
|
79
|
+
// oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- the filter state belongs to the product's `useSearch` and starts empty on every mount
|
|
80
|
+
setFilter((current) => withQuickFilter(current, { field: groupField, values: initial }));
|
|
81
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- applies only to the initial state
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!enabled) return;
|
|
86
|
+
const quickFilter = findQuickFilter(filter);
|
|
87
|
+
// The commit that starts the restore still sees the empty initial filter.
|
|
88
|
+
if (phaseRef.current === 'restoring') {
|
|
89
|
+
if (!quickFilter) return;
|
|
90
|
+
phaseRef.current = 'validating';
|
|
91
|
+
}
|
|
92
|
+
if (phaseRef.current === 'validating' && initial) {
|
|
93
|
+
if (isSearchLoading) searchedRef.current = true;
|
|
94
|
+
const settled = searchedRef.current && !isSearchLoading && !searchError && !query.trim();
|
|
95
|
+
const { present, values: known, counted } = groupFacetValues(facets, groupField);
|
|
96
|
+
if ((known.length && !counted) || (settled && present)) {
|
|
97
|
+
phaseRef.current = 'done';
|
|
98
|
+
const values = quickFilter?.values.filter(
|
|
99
|
+
(value) => known.includes(value) || !initial.includes(value),
|
|
100
|
+
);
|
|
101
|
+
if (quickFilter && values && values.length !== quickFilter.values.length) {
|
|
102
|
+
// oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- the facets arrive after the restore
|
|
103
|
+
setFilter((current) => withQuickFilter(current, { field: groupField, values }));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const next = quickFilter ? { field: quickFilter.field, values: quickFilter.values } : null;
|
|
109
|
+
if (JSON.stringify(next) !== JSON.stringify(stored)) {
|
|
110
|
+
setStored(next);
|
|
111
|
+
}
|
|
112
|
+
}, [
|
|
113
|
+
enabled,
|
|
114
|
+
filter,
|
|
115
|
+
facets,
|
|
116
|
+
groupField,
|
|
117
|
+
query,
|
|
118
|
+
isSearchLoading,
|
|
119
|
+
searchError,
|
|
120
|
+
initial,
|
|
121
|
+
stored,
|
|
122
|
+
setFilter,
|
|
123
|
+
setStored,
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
@@ -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
|
}
|
|
@@ -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'
|
|
@@ -124,6 +124,9 @@ export type TranslationKey =
|
|
|
124
124
|
| 'search.ai.error.header.forbidden'
|
|
125
125
|
| 'search.ai.error.header.unauthorized'
|
|
126
126
|
| 'search.ai.feedback.more'
|
|
127
|
+
| 'search.ai.actions.copy'
|
|
128
|
+
| 'search.ai.actions.like'
|
|
129
|
+
| 'search.ai.actions.dislike'
|
|
127
130
|
| 'search.searchItem.deprecated'
|
|
128
131
|
| 'search.searchItem.enum'
|
|
129
132
|
| 'search.searchItem.example'
|
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;
|
package/src/core/types/search.ts
CHANGED
|
@@ -117,6 +117,27 @@ export type ToolCall = {
|
|
|
117
117
|
|
|
118
118
|
export type ContentSegment = { type: 'text'; text: string } | { type: 'tool'; toolCall: ToolCall };
|
|
119
119
|
|
|
120
|
+
export type GroupedContentSegment =
|
|
121
|
+
| { type: 'text'; text: string }
|
|
122
|
+
/** `isActive`: the assistant still works on this group, with nothing written after it yet. */
|
|
123
|
+
| { type: 'tools'; toolCalls: ToolCall[]; isActive: boolean };
|
|
124
|
+
|
|
125
|
+
export type SearchAiActivityKind = 'thinking' | 'search' | 'code' | 'support';
|
|
126
|
+
|
|
127
|
+
export type SearchAiActivityStatus = {
|
|
128
|
+
label: string;
|
|
129
|
+
detail?: string;
|
|
130
|
+
kind: SearchAiActivityKind;
|
|
131
|
+
isRunning: boolean;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export type SearchAiActivityStep = {
|
|
135
|
+
id: string;
|
|
136
|
+
label: string;
|
|
137
|
+
kind: SearchAiActivityKind;
|
|
138
|
+
isRunning: boolean;
|
|
139
|
+
};
|
|
140
|
+
|
|
120
141
|
export type AiSearchConversationItem = {
|
|
121
142
|
role: AiSearchConversationRole;
|
|
122
143
|
content: string;
|