@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.
Files changed (113) hide show
  1. package/lib/components/Buttons/ConnectMCPButton.js +8 -13
  2. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.d.ts +1 -1
  3. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +1 -1
  4. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +30 -11
  5. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +2 -1
  6. package/lib/components/Dropdown/Dropdown.d.ts +4 -2
  7. package/lib/components/Dropdown/Dropdown.js +28 -8
  8. package/lib/components/Dropdown/DropdownMenu.d.ts +1 -0
  9. package/lib/components/Dropdown/DropdownMenu.js +1 -1
  10. package/lib/components/Feedback/Reasons.js +3 -2
  11. package/lib/components/Filter/FilterCheckboxes.js +30 -11
  12. package/lib/components/Filter/FilterDateRange.js +36 -2
  13. package/lib/components/Image/Image.js +24 -4
  14. package/lib/components/Search/SearchAiActivity.d.ts +10 -0
  15. package/lib/components/Search/SearchAiActivity.js +278 -0
  16. package/lib/components/Search/SearchAiDialog.js +5 -1
  17. package/lib/components/Search/SearchAiMessage.d.ts +2 -1
  18. package/lib/components/Search/SearchAiMessage.js +20 -108
  19. package/lib/components/Search/SearchItem.js +1 -8
  20. package/lib/components/Search/variables.js +15 -2
  21. package/lib/components/Segmented/Segmented.js +23 -3
  22. package/lib/components/Select/Select.js +10 -3
  23. package/lib/components/Select/SelectInput.d.ts +1 -0
  24. package/lib/components/Select/SelectInput.js +5 -11
  25. package/lib/components/SplitButton/SplitButton.js +37 -3
  26. package/lib/core/constants/mcp.d.ts +2 -2
  27. package/lib/core/constants/mcp.js +3 -8
  28. package/lib/core/constants/search.js +5 -1
  29. package/lib/core/hooks/index.d.ts +2 -0
  30. package/lib/core/hooks/index.js +2 -0
  31. package/lib/core/hooks/menu/use-collapse.js +1 -1
  32. package/lib/core/hooks/search/use-search-ai-activity.d.ts +11 -0
  33. package/lib/core/hooks/search/use-search-ai-activity.js +15 -0
  34. package/lib/core/hooks/use-connect-mcp-button.js +2 -1
  35. package/lib/core/hooks/use-full-height.d.ts +1 -1
  36. package/lib/core/hooks/use-full-height.js +8 -8
  37. package/lib/core/hooks/use-tab-list.d.ts +31 -0
  38. package/lib/core/hooks/use-tab-list.js +49 -0
  39. package/lib/core/styles/global.d.ts +2 -0
  40. package/lib/core/styles/global.js +80 -76
  41. package/lib/core/styles/index.d.ts +1 -0
  42. package/lib/core/styles/index.js +3 -1
  43. package/lib/core/styles/search-ai-appear.d.ts +1 -0
  44. package/lib/core/styles/search-ai-appear.js +18 -0
  45. package/lib/core/types/dropdown.d.ts +1 -0
  46. package/lib/core/types/dropdown.js +3 -0
  47. package/lib/core/types/index.d.ts +1 -0
  48. package/lib/core/types/l10n.d.ts +1 -1
  49. package/lib/core/types/mcp.d.ts +2 -1
  50. package/lib/core/types/search.d.ts +23 -0
  51. package/lib/core/utils/Dynamic.d.ts +8 -1
  52. package/lib/core/utils/Dynamic.js +34 -3
  53. package/lib/core/utils/assign-ref.d.ts +2 -0
  54. package/lib/core/utils/assign-ref.js +12 -0
  55. package/lib/core/utils/content-segments.d.ts +12 -1
  56. package/lib/core/utils/content-segments.js +90 -5
  57. package/lib/core/utils/dropdown.d.ts +2 -0
  58. package/lib/core/utils/dropdown.js +12 -0
  59. package/lib/core/utils/index.d.ts +2 -0
  60. package/lib/core/utils/index.js +2 -0
  61. package/lib/core/utils/mcp.d.ts +2 -1
  62. package/lib/core/utils/mcp.js +5 -0
  63. package/lib/core/utils/search-ai-activity.d.ts +4 -0
  64. package/lib/core/utils/search-ai-activity.js +83 -0
  65. package/lib/markdoc/tags/connect-mcp.js +15 -0
  66. package/package.json +2 -2
  67. package/src/components/Buttons/ConnectMCPButton.tsx +8 -14
  68. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.tsx +3 -1
  69. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +37 -22
  70. package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +3 -2
  71. package/src/components/Dropdown/Dropdown.tsx +28 -6
  72. package/src/components/Dropdown/DropdownMenu.tsx +7 -1
  73. package/src/components/Feedback/Reasons.tsx +3 -2
  74. package/src/components/Filter/FilterCheckboxes.tsx +33 -18
  75. package/src/components/Filter/FilterDateRange.tsx +3 -2
  76. package/src/components/Image/Image.tsx +26 -5
  77. package/src/components/Search/SearchAiActivity.tsx +318 -0
  78. package/src/components/Search/SearchAiDialog.tsx +4 -3
  79. package/src/components/Search/SearchAiMessage.tsx +27 -135
  80. package/src/components/Search/SearchItem.tsx +2 -9
  81. package/src/components/Search/variables.ts +15 -2
  82. package/src/components/Segmented/Segmented.tsx +25 -16
  83. package/src/components/Select/Select.tsx +12 -6
  84. package/src/components/Select/SelectInput.tsx +10 -18
  85. package/src/components/SplitButton/SplitButton.tsx +8 -2
  86. package/src/core/constants/mcp.ts +3 -9
  87. package/src/core/constants/search.ts +5 -1
  88. package/src/core/hooks/__mocks__/index.ts +1 -0
  89. package/src/core/hooks/__mocks__/use-theme-hooks.ts +1 -0
  90. package/src/core/hooks/index.ts +2 -0
  91. package/src/core/hooks/menu/use-collapse.ts +1 -1
  92. package/src/core/hooks/search/use-search-ai-activity.ts +31 -0
  93. package/src/core/hooks/use-connect-mcp-button.ts +7 -2
  94. package/src/core/hooks/use-full-height.ts +8 -8
  95. package/src/core/hooks/use-tab-list.ts +63 -0
  96. package/src/core/styles/global.ts +80 -75
  97. package/src/core/styles/index.ts +2 -1
  98. package/src/core/styles/search-ai-appear.ts +16 -0
  99. package/src/core/types/dropdown.ts +1 -0
  100. package/src/core/types/index.ts +1 -0
  101. package/src/core/types/l10n.ts +1 -1
  102. package/src/core/types/mcp.ts +3 -1
  103. package/src/core/types/search.ts +21 -0
  104. package/src/core/utils/Dynamic.tsx +67 -35
  105. package/src/core/utils/assign-ref.ts +9 -0
  106. package/src/core/utils/content-segments.ts +98 -6
  107. package/src/core/utils/dropdown.ts +14 -0
  108. package/src/core/utils/index.ts +2 -0
  109. package/src/core/utils/mcp.ts +7 -1
  110. package/src/core/utils/search-ai-activity.ts +110 -0
  111. package/src/icons/CheckboxIcon/CheckboxIcon.tsx +1 -0
  112. package/src/icons/ChevronSortIcon/ChevronSortIcon.tsx +1 -0
  113. package/src/markdoc/tags/connect-mcp.ts +16 -1
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SearchAiActivity = SearchAiActivity;
37
+ const react_1 = __importStar(require("react"));
38
+ const styled_components_1 = __importStar(require("styled-components"));
39
+ const hooks_1 = require("../../core/hooks");
40
+ const styles_1 = require("../../core/styles");
41
+ const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
42
+ const ChatIcon_1 = require("../../icons/ChatIcon/ChatIcon");
43
+ const ChevronDownIcon_1 = require("../../icons/ChevronDownIcon/ChevronDownIcon");
44
+ const CodeIcon_1 = require("../../icons/CodeIcon/CodeIcon");
45
+ const SearchIcon_1 = require("../../icons/SearchIcon/SearchIcon");
46
+ const ACTIVITY_KIND_ICONS = {
47
+ thinking: AiStarsIcon_1.AiStarsIcon,
48
+ search: SearchIcon_1.SearchIcon,
49
+ support: ChatIcon_1.ChatIcon,
50
+ code: CodeIcon_1.CodeIcon,
51
+ };
52
+ function SearchAiActivity({ toolCalls, isActive, className, }) {
53
+ var _a;
54
+ const [isExpanded, setIsExpanded] = (0, react_1.useState)(false);
55
+ const listId = (0, react_1.useId)();
56
+ const { status, steps } = (0, hooks_1.useSearchAiActivity)({ toolCalls, isActive });
57
+ const StatusIcon = ACTIVITY_KIND_ICONS[status.kind];
58
+ const header = (react_1.default.createElement(react_1.default.Fragment, null,
59
+ react_1.default.createElement(IconWrapper, { "aria-hidden": true },
60
+ react_1.default.createElement(StatusIcon, { size: "var(--search-ai-activity-icon-size)" })),
61
+ react_1.default.createElement(StatusLabel, { key: `${status.label}${(_a = status.detail) !== null && _a !== void 0 ? _a : ''}`, title: status.detail ? `${status.label} “${status.detail}”` : status.label, $isRunning: status.isRunning },
62
+ status.label,
63
+ status.detail && react_1.default.createElement(StatusDetail, null,
64
+ " \u201C",
65
+ status.detail,
66
+ "\u201D")),
67
+ steps.length > 1 && (react_1.default.createElement(react_1.default.Fragment, null,
68
+ ' ',
69
+ react_1.default.createElement(ToolCallCount, null, steps.length))),
70
+ steps.length > 0 && (react_1.default.createElement(ExpandIcon, { size: "var(--search-ai-activity-icon-size)", $isExpanded: isExpanded }))));
71
+ return (react_1.default.createElement(SearchAiActivityWrapper, { "data-component-name": "Search/SearchAiActivity", "data-testid": "tool-calls-info", className: className }, steps.length > 0 ? (react_1.default.createElement(react_1.default.Fragment, null,
72
+ react_1.default.createElement(HeaderButton, { type: "button", "aria-expanded": isExpanded, "aria-controls": listId, onClick: () => setIsExpanded((expanded) => !expanded) }, header),
73
+ react_1.default.createElement(StepsWrapper, { id: listId, $isExpanded: isExpanded },
74
+ react_1.default.createElement(StepList, null, steps.map((step) => {
75
+ const StepIcon = ACTIVITY_KIND_ICONS[step.kind];
76
+ return (react_1.default.createElement(StepItem, { key: step.id },
77
+ react_1.default.createElement(StepIconWrapper, { "aria-hidden": true },
78
+ react_1.default.createElement(StepIcon, { size: "var(--search-ai-activity-icon-size)" })),
79
+ react_1.default.createElement(StepText, { $isRunning: step.isRunning && isExpanded }, step.label)));
80
+ }))))) : (react_1.default.createElement(HeaderWrapper, null, header))));
81
+ }
82
+ const labelIn = (0, styled_components_1.keyframes) `
83
+ from {
84
+ opacity: 0.35;
85
+ }
86
+ `;
87
+ const shimmerSweep = (0, styled_components_1.keyframes) `
88
+ from {
89
+ background-position: 100% 0;
90
+ }
91
+ to {
92
+ background-position: 0 0;
93
+ }
94
+ `;
95
+ const shimmer = (0, styled_components_1.css) `
96
+ @supports (background-clip: text) or (-webkit-background-clip: text) {
97
+ @media (prefers-reduced-motion: no-preference) {
98
+ background:
99
+ linear-gradient(
100
+ 90deg,
101
+ transparent 0%,
102
+ transparent 40%,
103
+ var(--search-ai-activity-shimmer-color) 50%,
104
+ transparent 60%,
105
+ transparent 100%
106
+ )
107
+ 100% 0 / 250% 100% no-repeat,
108
+ linear-gradient(currentColor, currentColor);
109
+ -webkit-background-clip: text;
110
+ background-clip: text;
111
+ -webkit-text-fill-color: transparent;
112
+ animation:
113
+ ${labelIn} var(--search-ai-activity-fade-duration) ease-out,
114
+ ${shimmerSweep} var(--search-ai-activity-shimmer-duration) linear
115
+ infinite;
116
+ }
117
+ }
118
+ `;
119
+ const SearchAiActivityWrapper = styled_components_1.default.div `
120
+ font-size: var(--search-ai-activity-font-size);
121
+ line-height: var(--search-ai-activity-line-height);
122
+ color: var(--search-ai-activity-text-color);
123
+ `;
124
+ const headerStyles = (0, styled_components_1.css) `
125
+ display: flex;
126
+ align-items: center;
127
+ gap: var(--spacing-xs);
128
+ max-width: 100%;
129
+ min-height: var(--search-ai-activity-line-height);
130
+ white-space: nowrap;
131
+ `;
132
+ const HeaderWrapper = styled_components_1.default.div `
133
+ ${headerStyles}
134
+ `;
135
+ const HeaderButton = styled_components_1.default.button `
136
+ ${headerStyles}
137
+ margin: 0;
138
+ padding: 0;
139
+ border: 0;
140
+ border-radius: var(--border-radius);
141
+ background: none;
142
+ font: inherit;
143
+ color: inherit;
144
+ text-align: start;
145
+ cursor: pointer;
146
+ transition: color var(--search-ai-activity-fade-duration) ease-out;
147
+
148
+ &:hover {
149
+ color: var(--search-ai-activity-text-color-hover);
150
+ }
151
+
152
+ &:focus-visible {
153
+ outline: 2px solid var(--button-border-color-focused);
154
+ outline-offset: 2px;
155
+ }
156
+ `;
157
+ const IconWrapper = styled_components_1.default.span `
158
+ display: inline-flex;
159
+ flex-shrink: 0;
160
+ width: var(--search-ai-activity-icon-size);
161
+ height: var(--search-ai-activity-icon-size);
162
+ `;
163
+ const StatusLabel = styled_components_1.default.span `
164
+ min-width: 0;
165
+ overflow: hidden;
166
+ text-overflow: ellipsis;
167
+ font-weight: var(--font-weight-regular);
168
+ animation: ${labelIn} var(--search-ai-activity-fade-duration) ease-out;
169
+
170
+ @media (prefers-reduced-motion: reduce) {
171
+ animation: none;
172
+ }
173
+
174
+ ${({ $isRunning }) => $isRunning && shimmer}
175
+ `;
176
+ const StatusDetail = styled_components_1.default.span `
177
+ color: var(--search-ai-activity-detail-color);
178
+ `;
179
+ const ToolCallCount = styled_components_1.default.span `
180
+ flex-shrink: 0;
181
+ min-width: calc(var(--search-ai-activity-line-height) - 4px);
182
+ padding: 0 var(--spacing-xxs);
183
+ border-radius: calc(var(--search-ai-activity-line-height) / 2);
184
+ background: var(--search-ai-activity-count-bg-color);
185
+ font-size: var(--search-ai-activity-count-font-size);
186
+ line-height: calc(var(--search-ai-activity-line-height) - 4px);
187
+ font-variant-numeric: tabular-nums;
188
+ text-align: center;
189
+ `;
190
+ const ExpandIcon = (0, styled_components_1.default)(ChevronDownIcon_1.ChevronDownIcon) `
191
+ flex-shrink: 0;
192
+ margin-inline-start: calc(var(--spacing-xxs) * -1);
193
+ transform: rotate(${({ $isExpanded }) => ($isExpanded ? '180deg' : '0deg')});
194
+ transition: transform var(--search-ai-activity-expand-duration)
195
+ var(--search-ai-activity-easing);
196
+
197
+ @media (prefers-reduced-motion: reduce) {
198
+ transition: none;
199
+ }
200
+ `;
201
+ const StepsWrapper = styled_components_1.default.div `
202
+ display: grid;
203
+ grid-template-rows: ${({ $isExpanded }) => ($isExpanded ? '1fr' : '0fr')};
204
+ opacity: ${({ $isExpanded }) => ($isExpanded ? 1 : 0)};
205
+ visibility: ${({ $isExpanded }) => ($isExpanded ? 'visible' : 'hidden')};
206
+ ${({ $isExpanded }) => !$isExpanded &&
207
+ (0, styled_components_1.css) `
208
+ contain: inline-size;
209
+ `}
210
+ transition:
211
+ grid-template-rows var(--search-ai-activity-expand-duration) var(--search-ai-activity-easing),
212
+ opacity var(--search-ai-activity-expand-duration) ease-out,
213
+ visibility var(--search-ai-activity-expand-duration);
214
+
215
+ @media (prefers-reduced-motion: reduce) {
216
+ transition: none;
217
+ }
218
+ `;
219
+ const STEP_GAP = 'var(--spacing-xs)';
220
+ const ICON_OFFSET = 'calc((var(--search-ai-activity-line-height) - var(--search-ai-activity-icon-size)) / 2)';
221
+ const RAIL_GAP = '3px';
222
+ const RAIL_INSET = 'calc(var(--search-ai-activity-icon-size) / 2 - 0.5px)';
223
+ const StepList = styled_components_1.default.ol `
224
+ min-height: 0;
225
+ overflow: hidden;
226
+ margin: 0;
227
+ padding: 0;
228
+ list-style: none;
229
+
230
+ /* Padding would stay visible while the list is collapsed; the spacer collapses with it. */
231
+ &::before {
232
+ content: "";
233
+ display: block;
234
+ height: ${STEP_GAP};
235
+ }
236
+ `;
237
+ const StepItem = styled_components_1.default.li `
238
+ position: relative;
239
+ display: flex;
240
+ gap: var(--spacing-xs);
241
+ padding-bottom: ${STEP_GAP};
242
+ ${styles_1.searchAiAppearAnimation}
243
+
244
+ &:last-child {
245
+ padding-bottom: 0;
246
+ }
247
+
248
+ &:first-child::before,
249
+ &:not(:last-child)::after {
250
+ content: "";
251
+ position: absolute;
252
+ inset-inline-start: ${RAIL_INSET};
253
+ width: 1px;
254
+ background: var(--search-ai-activity-rail-color);
255
+ }
256
+
257
+ &:first-child::before {
258
+ top: calc(${STEP_GAP} * -1 - ${ICON_OFFSET} + ${RAIL_GAP});
259
+ height: calc(${STEP_GAP} + ${ICON_OFFSET} * 2 - ${RAIL_GAP} * 2);
260
+ }
261
+
262
+ &:not(:last-child)::after {
263
+ top: calc(
264
+ ${ICON_OFFSET} + var(--search-ai-activity-icon-size) + ${RAIL_GAP}
265
+ );
266
+ bottom: calc(${RAIL_GAP} - ${ICON_OFFSET});
267
+ }
268
+ `;
269
+ const StepIconWrapper = (0, styled_components_1.default)(IconWrapper) `
270
+ margin-top: ${ICON_OFFSET};
271
+ `;
272
+ const StepText = styled_components_1.default.span `
273
+ min-width: 0;
274
+ overflow-wrap: anywhere;
275
+
276
+ ${({ $isRunning }) => $isRunning && shimmer}
277
+ `;
278
+ //# sourceMappingURL=SearchAiActivity.js.map
@@ -99,6 +99,10 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
99
99
  }
100
100
  // oxlint-disable-next-line react-you-might-not-need-an-effect/no-pass-data-to-parent -- merges the streamed assistant response into the lifted conversation state as chunks arrive
101
101
  setConversation((prev) => {
102
+ // A reset can land between this effect's render and its update.
103
+ if (prev.length === 0) {
104
+ return prev;
105
+ }
102
106
  const lastMessage = prev[prev.length - 1];
103
107
  const content = response || '';
104
108
  if (lastMessage && lastMessage.role === constants_1.AiSearchConversationRole.ASSISTANT) {
@@ -153,7 +157,7 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
153
157
  react_1.default.createElement(ConversationWrapper, null,
154
158
  conversation.map((item, index) => (react_1.default.createElement(SearchAiMessage_1.SearchAiMessage, { key: `search-ai-message-${index}`, role: item.role, content: item.content, isThinking: item.role === constants_1.AiSearchConversationRole.ASSISTANT &&
155
159
  isGeneratingResponse &&
156
- index === conversation.length - 1, resources: item.resources, messageId: item.messageId, feedback: item.feedback, onFeedbackChange: handleFeedbackChange, toolCalls: item.toolCalls || (index === conversation.length - 1 ? toolCalls : undefined), contentSegments: item.contentSegments }))),
160
+ index === conversation.length - 1, resources: item.resources, messageId: item.messageId, feedback: item.feedback, onFeedbackChange: handleFeedbackChange, contentSegments: item.contentSegments }))),
157
161
  error && (react_1.default.createElement(Admonition_1.Admonition, { type: "danger", name: translate(constants_1.AI_SEARCH_ERROR_CONFIG[error].headerKey, constants_1.AI_SEARCH_ERROR_CONFIG[error].headerDefault) }, translate(constants_1.AI_SEARCH_ERROR_CONFIG[error].messageKey, constants_1.AI_SEARCH_ERROR_CONFIG[error].messageDefault))),
158
162
  !conversation.length && !error && (react_1.default.createElement(SuggestionsWrapper, null, suggestions === null || suggestions === void 0 ? void 0 : suggestions.map((suggestion) => (react_1.default.createElement(Button_1.Button, { key: suggestion, variant: "outlined", onClick: () => handleOnMessageSent(suggestion) }, suggestion))))),
159
163
  react_1.default.createElement("div", { ref: conversationEndRef })),
@@ -10,9 +10,10 @@ export type SearchAiMessageProps = {
10
10
  messageId?: string;
11
11
  feedback?: FeedbackType;
12
12
  onFeedbackChange: (messageId: string, feedback: FeedbackType | undefined) => void;
13
+ /** @deprecated Not used; the message reads its tool calls from `contentSegments`. */
13
14
  toolCalls?: ToolCall[];
14
15
  contentSegments?: ContentSegment[];
15
16
  };
16
- declare function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, toolCalls, contentSegments, }: SearchAiMessageProps): JSX.Element;
17
+ declare function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, contentSegments, }: SearchAiMessageProps): JSX.Element;
17
18
  export declare const SearchAiMessage: React.MemoExoticComponent<typeof SearchAiMessageComponent>;
18
19
  export {};
@@ -41,6 +41,7 @@ const react_1 = __importStar(require("react"));
41
41
  const styled_components_1 = __importDefault(require("styled-components"));
42
42
  const types_1 = require("../../core/types");
43
43
  const constants_1 = require("../../core/constants");
44
+ const utils_1 = require("../../core/utils");
44
45
  const Link_1 = require("../../components/Link/Link");
45
46
  const Tag_1 = require("../../components/Tag/Tag");
46
47
  const hooks_1 = require("../../core/hooks");
@@ -50,36 +51,14 @@ const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
50
51
  const CheckmarkOutlineIcon_1 = require("../../icons/CheckmarkOutlineIcon/CheckmarkOutlineIcon");
51
52
  const SearchAiActionButtons_1 = require("../../components/Search/SearchAiActionButtons");
52
53
  const SearchAiNegativeFeedbackForm_1 = require("../../components/Search/SearchAiNegativeFeedbackForm");
54
+ const SearchAiActivity_1 = require("../../components/Search/SearchAiActivity");
55
+ const styles_1 = require("../../core/styles");
53
56
  function MarkdownSegment({ text }) {
54
57
  const { useMarkdownText } = (0, hooks_1.useThemeHooks)();
55
58
  const markdown = useMarkdownText(text);
56
59
  return react_1.default.createElement(ResponseText, { as: "div", children: markdown, "data-testid": "response-text" });
57
60
  }
58
- // The codemode `execute` tool passes a human-readable `description` of what its code
59
- // does; show that instead of a generic "Executing execute..." label.
60
- function getExecuteDescription(args) {
61
- if (args && typeof args === 'object' && 'description' in args) {
62
- const { description } = args;
63
- if (typeof description === 'string' && description.trim().length > 0) {
64
- return description.trim();
65
- }
66
- }
67
- return undefined;
68
- }
69
- function getToolCallDisplayText(toolCall) {
70
- var _a;
71
- if (toolCall.name === types_1.ToolCallName.Execute) {
72
- const description = getExecuteDescription(toolCall.args);
73
- if (description) {
74
- return { inProgressText: description, completedText: description };
75
- }
76
- }
77
- return ((_a = constants_1.TOOL_CALL_DISPLAY_TEXT[toolCall.name]) !== null && _a !== void 0 ? _a : {
78
- inProgressText: `Executing ${toolCall.name}...`,
79
- completedText: `${toolCall.name} executed`,
80
- });
81
- }
82
- function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, toolCalls = [], contentSegments = [{ type: 'text', text: content }], }) {
61
+ function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, contentSegments = [{ type: 'text', text: content }], }) {
83
62
  var _a;
84
63
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
85
64
  const { translate } = useTranslate();
@@ -88,7 +67,8 @@ function SearchAiMessageComponent({ role, content, isThinking, resources, classN
88
67
  const hasResources = !isThinking && resources && resources.length > 0;
89
68
  const resourcesCount = (_a = resources === null || resources === void 0 ? void 0 : resources.length) !== null && _a !== void 0 ? _a : 0;
90
69
  const showSuccessMessage = feedbackSent && feedback;
91
- const isLoading = isThinking && content.length === 0 && toolCalls.length === 0;
70
+ const groupedSegments = (0, utils_1.groupToolCallSegments)(contentSegments, isThinking);
71
+ const isFullWidth = (0, utils_1.hasToolCallsAndText)(contentSegments);
92
72
  const sendFeedbackTelemetry = (feedbackValue, dislikeReason) => {
93
73
  if (!messageId)
94
74
  return;
@@ -122,17 +102,11 @@ function SearchAiMessageComponent({ role, content, isThinking, resources, classN
122
102
  };
123
103
  return (react_1.default.createElement(SearchAiMessageWrapper, { "data-component-name": "Search/SearchAiMessage", $role: role, className: className, "data-testid": "search-ai-message" },
124
104
  role === constants_1.AiSearchConversationRole.ASSISTANT && (react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { size: "32px", background: "var(--search-ai-icon-bg-color)", borderRadius: "var(--border-radius-lg)", color: "var(--search-ai-icon-color)", margin: "0 var(--spacing-xs) 0 0" })),
125
- react_1.default.createElement(MessageContentWrapper, null, role === constants_1.AiSearchConversationRole.ASSISTANT ? (react_1.default.createElement(react_1.default.Fragment, null,
126
- react_1.default.createElement(MessageWrapper, { $role: role },
127
- contentSegments.map((segment, index) => {
128
- if (segment.type === 'tool') {
129
- const toolCallCompleted = Boolean(segment.toolCall.result);
130
- const { inProgressText, completedText } = getToolCallDisplayText(segment.toolCall);
131
- const toolCallDisplayText = toolCallCompleted ? completedText : inProgressText;
132
- return (react_1.default.createElement(ToolCallsInfoWrapper, { key: `tool-${index}`, "data-testid": "tool-calls-info" },
133
- react_1.default.createElement(ToolCallInfoItem, null,
134
- react_1.default.createElement(DocumentIcon_1.DocumentIcon, { size: "14px", color: "--search-ai-text-color" }),
135
- react_1.default.createElement(ToolCallText, { $isSearching: !toolCallCompleted }, toolCallDisplayText))));
105
+ react_1.default.createElement(MessageContentWrapper, { $isFullWidth: isFullWidth }, role === constants_1.AiSearchConversationRole.ASSISTANT ? (react_1.default.createElement(react_1.default.Fragment, null,
106
+ react_1.default.createElement(MessageWrapper, { $role: role, $isFullWidth: isFullWidth },
107
+ groupedSegments.map((segment, index) => {
108
+ if (segment.type === 'tools') {
109
+ return (react_1.default.createElement(ToolCallsActivity, { key: `tools-${index}`, toolCalls: segment.toolCalls, isActive: segment.isActive }));
136
110
  }
137
111
  return react_1.default.createElement(MarkdownSegment, { key: `text-${index}`, text: segment.text });
138
112
  }),
@@ -146,11 +120,7 @@ function SearchAiMessageComponent({ role, content, isThinking, resources, classN
146
120
  translate('search.ai.resourcesFound.resources', 'resources')),
147
121
  react_1.default.createElement(ResourceTagsWrapper, null, resources === null || resources === void 0 ? void 0 : resources.map((resource, index) => (react_1.default.createElement(Link_1.Link, { key: `${resource.url}-${index}`, to: resource.url, target: "_blank" },
148
122
  react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title)))))))),
149
- isLoading && (react_1.default.createElement(ThinkingDotsWrapper, { "data-testid": "thinking-dots-wrapper" },
150
- react_1.default.createElement(ThinkingDot, null),
151
- react_1.default.createElement(ThinkingDot, null),
152
- react_1.default.createElement(ThinkingDot, null))),
153
- content.length > 0 && (react_1.default.createElement(FeedbackWrapper, null,
123
+ !isThinking && content.length > 0 && (react_1.default.createElement(FeedbackWrapper, null,
154
124
  react_1.default.createElement(SearchAiActionButtons_1.SearchAiActionButtons, { content: content, feedback: feedback, onFeedback: handleFeedbackClick, disabled: !!feedback })))),
155
125
  messageId && feedback === types_1.FeedbackType.Dislike && !showSuccessMessage && (react_1.default.createElement(SearchAiNegativeFeedbackForm_1.SearchAiNegativeFeedbackForm, { messageId: messageId, onClose: (messageId, feedback) => {
156
126
  onFeedbackChange(messageId, feedback);
@@ -171,7 +141,6 @@ function areResourcesEqual(prev, next) {
171
141
  });
172
142
  }
173
143
  exports.SearchAiMessage = (0, react_1.memo)(SearchAiMessageComponent, (prevProps, nextProps) => {
174
- var _a, _b;
175
144
  return (prevProps.role === nextProps.role &&
176
145
  prevProps.content === nextProps.content &&
177
146
  prevProps.isThinking === nextProps.isThinking &&
@@ -179,7 +148,6 @@ exports.SearchAiMessage = (0, react_1.memo)(SearchAiMessageComponent, (prevProps
179
148
  prevProps.feedback === nextProps.feedback &&
180
149
  prevProps.onFeedbackChange === nextProps.onFeedbackChange &&
181
150
  areResourcesEqual(prevProps.resources, nextProps.resources) &&
182
- ((_a = prevProps.toolCalls) === null || _a === void 0 ? void 0 : _a.length) === ((_b = nextProps.toolCalls) === null || _b === void 0 ? void 0 : _b.length) &&
183
151
  prevProps.contentSegments === nextProps.contentSegments);
184
152
  });
185
153
  const SearchAiMessageWrapper = styled_components_1.default.div `
@@ -192,6 +160,7 @@ const SearchAiMessageWrapper = styled_components_1.default.div `
192
160
  const MessageContentWrapper = styled_components_1.default.div `
193
161
  display: flex;
194
162
  flex-direction: column;
163
+ flex-grow: ${({ $isFullWidth }) => ($isFullWidth ? 1 : 0)};
195
164
  gap: var(--spacing-sm);
196
165
  max-width: 80%;
197
166
  min-width: 0;
@@ -217,7 +186,7 @@ const MessageWrapper = styled_components_1.default.div `
217
186
  ? 'var(--spacing-sm)'
218
187
  : 'var(--spacing-sm) var(--spacing-sm) var(--spacing-xs) var(--spacing-sm)'};
219
188
  border-radius: var(--border-radius-xl);
220
- width: fit-content;
189
+ width: ${({ $isFullWidth }) => ($isFullWidth ? 'auto' : 'fit-content')};
221
190
  max-width: 100%;
222
191
  word-wrap: break-word;
223
192
  white-space: pre-wrap;
@@ -234,12 +203,14 @@ const ResourcesWrapper = styled_components_1.default.div `
234
203
  display: flex;
235
204
  flex-direction: column;
236
205
  margin: 0;
206
+ ${styles_1.searchAiAppearAnimation}
237
207
  `;
238
208
  const FeedbackWrapper = styled_components_1.default.div `
239
209
  display: flex;
240
210
  flex-direction: row;
241
211
  gap: var(--search-ai-feedback-gap);
242
212
  margin-top: var(--spacing-sm);
213
+ ${styles_1.searchAiAppearAnimation}
243
214
  `;
244
215
  const ResourcesTitle = styled_components_1.default.div `
245
216
  font-weight: var(--search-ai-resources-title-font-weight);
@@ -270,41 +241,6 @@ const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag).attrs({ textTran
270
241
  max-width: 100%;
271
242
  }
272
243
  `;
273
- const ThinkingDotsWrapper = styled_components_1.default.div `
274
- display: flex;
275
- gap: var(--search-ai-thinking-dots-gap);
276
- padding: var(--search-ai-thinking-dots-padding);
277
- `;
278
- const ThinkingDot = styled_components_1.default.div `
279
- width: var(--search-ai-thinking-dot-size);
280
- height: var(--search-ai-thinking-dot-size);
281
- border-radius: 50%;
282
- background: var(--search-ai-thinking-dot-color);
283
- animation: bounce 1.4s infinite ease-in-out;
284
-
285
- &:nth-child(1) {
286
- animation-delay: -0.32s;
287
- }
288
- &:nth-child(2) {
289
- animation-delay: -0.16s;
290
- }
291
- &:nth-child(3) {
292
- animation-delay: 0s;
293
- }
294
-
295
- @keyframes bounce {
296
- 0%,
297
- 80%,
298
- 100% {
299
- opacity: 0.2;
300
- transform: scale(0.8);
301
- }
302
- 40% {
303
- opacity: 1;
304
- transform: scale(1);
305
- }
306
- }
307
- `;
308
244
  const SuccessMessageWrapper = styled_components_1.default.div `
309
245
  max-width: fit-content;
310
246
  display: flex;
@@ -319,35 +255,11 @@ const SuccessMessageText = styled_components_1.default.div `
319
255
  font-size: var(--font-size-base);
320
256
  color: var(--color-success-darker);
321
257
  `;
322
- const ToolCallsInfoWrapper = styled_components_1.default.div `
323
- display: flex;
324
- flex-direction: column;
325
- gap: var(--spacing-xxs);
258
+ const ToolCallsActivity = (0, styled_components_1.default)(SearchAiActivity_1.SearchAiActivity) `
326
259
  margin: 0 0 var(--spacing-sm) 0;
327
- font-size: var(--font-size-xs);
328
- color: var(--search-ai-text-color);
329
- opacity: 0.6;
330
- `;
331
- const ToolCallInfoItem = styled_components_1.default.div `
332
- display: flex;
333
- align-items: center;
334
- gap: var(--spacing-xxs);
335
- `;
336
- const ToolCallText = styled_components_1.default.span `
337
- font-weight: var(--font-weight-regular);
338
260
 
339
- ${({ $isSearching }) => $isSearching &&
340
- `
341
- animation: pulse 1.5s ease-in-out infinite;
342
-
343
- @keyframes pulse {
344
- 0%, 100% {
345
- opacity: 1;
346
- }
347
- 50% {
348
- opacity: 0.4;
349
- }
350
- }
351
- `}
261
+ &:last-child {
262
+ margin-bottom: var(--spacing-xxs);
263
+ }
352
264
  `;
353
265
  //# sourceMappingURL=SearchAiMessage.js.map
@@ -67,14 +67,7 @@ function SearchItem({ onClick, item, className, product, innerRef, }) {
67
67
  });
68
68
  return (react_1.default.createElement(SearchItemWrapper, { className: className, to: document.url, onClick: onClick, tabIndex: 0, innerRef: (el) => {
69
69
  ref.current = el;
70
- if (!innerRef)
71
- return;
72
- if (typeof innerRef === 'function') {
73
- innerRef(el);
74
- }
75
- else {
76
- innerRef.current = el;
77
- }
70
+ (0, utils_1.assignRef)(innerRef, el);
78
71
  }, "data-component-name": "Search/SearchItem" },
79
72
  react_1.default.createElement(SearchItemContent, null,
80
73
  react_1.default.createElement(SearchItemHeader, null,
@@ -200,8 +200,21 @@ exports.search = (0, styled_components_1.css) `
200
200
  --search-ai-suggestions-title-font-weight: var(--font-weight-light);
201
201
  --search-ai-suggestions-text-color: var(--text-color-description);
202
202
 
203
- --search-ai-thinking-dots-gap: 4px;
204
- --search-ai-thinking-dots-padding: 4px 0;
203
+ --search-ai-activity-font-size: var(--font-size-base);
204
+ --search-ai-activity-line-height: var(--line-height-sm);
205
+ --search-ai-activity-count-font-size: var(--font-size-sm);
206
+ --search-ai-activity-count-bg-color: var(--border-color-secondary);
207
+ --search-ai-activity-text-color: var(--text-color-description);
208
+ --search-ai-activity-text-color-hover: var(--text-color-primary);
209
+ --search-ai-activity-detail-color: var(--text-color-secondary);
210
+ --search-ai-activity-shimmer-color: var(--text-color-primary);
211
+ --search-ai-activity-rail-color: var(--border-color-primary);
212
+ --search-ai-activity-icon-size: 14px;
213
+ --search-ai-activity-fade-duration: 200ms;
214
+ --search-ai-activity-expand-duration: 250ms;
215
+ --search-ai-activity-shimmer-duration: 2s;
216
+ --search-ai-activity-easing: cubic-bezier(0.32, 0.72, 0, 1);
217
+
205
218
  --search-ai-thinking-dot-size: 6px;
206
219
  --search-ai-thinking-dot-color: var(--search-ai-gradient);
207
220
 
@@ -32,15 +32,35 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __rest = (this && this.__rest) || function (s, e) {
36
+ var t = {};
37
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
38
+ t[p] = s[p];
39
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
40
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
41
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
42
+ t[p[i]] = s[p[i]];
43
+ }
44
+ return t;
45
+ };
35
46
  Object.defineProperty(exports, "__esModule", { value: true });
36
47
  exports.Segmented = void 0;
37
48
  const react_1 = __importStar(require("react"));
38
49
  const styled_components_1 = __importStar(require("styled-components"));
39
50
  const typed_memo_1 = require("../../core/hoc/typed-memo");
51
+ const hooks_1 = require("../../core/hooks");
40
52
  function SegmentedComponent({ options, onChange, value, className = '', size = 'regular' }, ref) {
41
- return (react_1.default.createElement(SegmentedGroup, { ref: ref, "data-component-name": "Segmented/Segmented", className: `tag-grey ${size} ${className}`, role: "tablist", $size: size }, options.map((opt, index) => (react_1.default.createElement(react_1.default.Fragment, { key: index },
42
- opt.divider,
43
- react_1.default.createElement(SegmentedItem, { key: opt.label, role: "tab", title: opt.label, onClick: () => onChange(opt), $isActive: value == opt.value, $size: size }, opt.element || opt.label))))));
53
+ const { tabListProps, getTabProps } = (0, hooks_1.useTabList)({
54
+ count: options.length,
55
+ activeIndex: options.findIndex((opt) => value == opt.value),
56
+ onChange: (index) => onChange(options[index]),
57
+ });
58
+ return (react_1.default.createElement(SegmentedGroup, Object.assign({ ref: ref, "data-component-name": "Segmented/Segmented", className: `tag-grey ${size} ${className}` }, tabListProps, { $size: size }), options.map((opt, index) => {
59
+ const _a = getTabProps(index), { 'aria-controls': _noPanel } = _a, tabProps = __rest(_a, ['aria-controls']);
60
+ return (react_1.default.createElement(react_1.default.Fragment, { key: index },
61
+ opt.divider,
62
+ react_1.default.createElement(SegmentedItem, Object.assign({ key: opt.label }, tabProps, { title: opt.label, $isActive: value == opt.value, $size: size }), opt.element || opt.label)));
63
+ })));
44
64
  }
45
65
  exports.Segmented = (0, typed_memo_1.typedMemo)((0, react_1.forwardRef)(SegmentedComponent));
46
66
  const SegmentedGroup = styled_components_1.default.div `
@@ -68,6 +68,7 @@ function Select(props) {
68
68
  const [filteredOptions, setFilteredOptions] = (0, react_1.useState)(options);
69
69
  const [stickyInputValue, setStickyInputValue] = (0, react_1.useState)(placeholder || '');
70
70
  const inputId = (0, react_1.useId)();
71
+ const listboxId = `${inputId}-listbox`;
71
72
  (0, hooks_1.useOutsideClick)(selectRef, () => {
72
73
  setDropdownActive(false);
73
74
  });
@@ -169,14 +170,20 @@ function Select(props) {
169
170
  return !!selectedOptions.find((selectOption) => selectOption.value === option.value || selectOption.value === option.label);
170
171
  };
171
172
  const renderDefaultInput = (inputRef) => {
172
- return (react_1.default.createElement(SelectInput_1.SelectInput, { id: inputId, listboxId: inputId, selectedOptions: selectedOptions, searchValue: searchValue, placeholder: placeholder, stickyValue: stickyInputValue, multiple: multiple, searchable: searchable, clearable: clearable, customIcon: icon, inputRef: inputRef, onlyIcon: onlyIcon, clearHandler: clearHandler, searchHandler: searchHandler, inputBlurHandler: inputBlurHandler, inputFocusHandler: inputFocusHandler, clickHandler: inputClickHandler }));
173
+ return (react_1.default.createElement(SelectInput_1.SelectInput, { id: inputId, listboxId: listboxId, expanded: dropdownActive, selectedOptions: selectedOptions, searchValue: searchValue, placeholder: placeholder, stickyValue: stickyInputValue, multiple: multiple, searchable: searchable, clearable: clearable, customIcon: icon, inputRef: inputRef, onlyIcon: onlyIcon, clearHandler: clearHandler, searchHandler: searchHandler, inputBlurHandler: inputBlurHandler, inputFocusHandler: inputFocusHandler, clickHandler: inputClickHandler }));
173
174
  };
174
175
  return (react_1.default.createElement(exports.SelectWrapper, Object.assign({ "data-component-name": "Select/Select", ref: selectRef }, dataAttributes, { disabled: disabled, "data-testid": dataTestId, className: className, onKeyDown: (e) => {
175
176
  if (e.key === 'Enter' && document.activeElement === selectRef.current) {
176
177
  e.preventDefault();
177
178
  setDropdownActive(!dropdownActive);
178
179
  }
179
- }, tabIndex: 0, "aria-haspopup": "listbox", "aria-expanded": dropdownActive, "aria-label": "Select option", onFocus: (e) => {
180
+ }, tabIndex: searchable ? undefined : 0 }, (!searchable && {
181
+ role: 'combobox',
182
+ 'aria-haspopup': 'listbox',
183
+ 'aria-expanded': dropdownActive,
184
+ 'aria-controls': listboxId,
185
+ 'aria-label': 'Select option',
186
+ }), { onFocus: (e) => {
180
187
  var _a;
181
188
  if (e.target === selectRef.current) {
182
189
  (_a = selectInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
@@ -185,7 +192,7 @@ function Select(props) {
185
192
  e.preventDefault();
186
193
  } }),
187
194
  react_1.default.createElement(SelectDropdown, { closeOnClick: !multiple, withArrow: withArrow, trigger: renderInput ? renderInput() : renderDefaultInput(selectInputRef), triggerEvent: triggerEvent, placement: placement, alignment: alignment, active: !renderInput ? dropdownActive : undefined },
188
- react_1.default.createElement(SelectDropdownMenu, { role: "listbox", footer: footer }, filteredOptions.length === 0 ? (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { disabled: true }, translate('select.noResults', 'No results'))) : (filteredOptions.map((option, index) => {
195
+ react_1.default.createElement(SelectDropdownMenu, { id: listboxId, role: "listbox", footer: footer }, filteredOptions.length === 0 ? (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { disabled: true }, translate('select.noResults', 'No results'))) : (filteredOptions.map((option, index) => {
189
196
  return (react_1.default.createElement(react_1.Fragment, { key: index },
190
197
  react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { onAction: () => selectHandler(option), prefix: !hideCheckmarkIcon &&
191
198
  (checkmarkIconPosition ? checkmarkIconPosition === 'start' : false) &&