@vertesia/ui 1.4.0-dev.20260629.130134Z → 1.4.1
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/core/components/SidePanel.js +1 -1
- package/lib/core/components/SidePanel.js.map +1 -1
- package/lib/core/components/shadcn/button.d.ts +10 -3
- package/lib/core/components/shadcn/button.d.ts.map +1 -1
- package/lib/core/components/shadcn/button.js +41 -9
- package/lib/core/components/shadcn/button.js.map +1 -1
- package/lib/core/components/shadcn/modal/dialog.js +1 -1
- package/lib/core/components/shadcn/modal/dialog.js.map +1 -1
- package/lib/core/components/shadcn/resizeable.d.ts +1 -1
- package/lib/core/components/shadcn/resizeable.d.ts.map +1 -1
- package/lib/core/components/shadcn/resizeable.js +12 -2
- package/lib/core/components/shadcn/resizeable.js.map +1 -1
- package/lib/core/components/shadcn/tabs.d.ts +12 -1
- package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
- package/lib/core/components/shadcn/tabs.js +12 -4
- package/lib/core/components/shadcn/tabs.js.map +1 -1
- package/lib/core/components/shadcn/theme/ThemeSwitcher.js +1 -1
- package/lib/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
- package/lib/core/hooks/useIntersectionObserver.d.ts.map +1 -1
- package/lib/core/hooks/useIntersectionObserver.js +3 -1
- package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
- package/lib/features/agent/chat/AgentChatPlaybackControls.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentChatPlaybackControls.js +2 -2
- package/lib/features/agent/chat/AgentChatPlaybackControls.js.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.js +2 -2
- package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
- package/lib/features/agent/chat/AgentRightPanel.js +1 -1
- package/lib/features/agent/chat/AgentRightPanel.js.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.js +4 -3
- package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
- package/lib/features/agent/chat/ImageLightbox.d.ts.map +1 -1
- package/lib/features/agent/chat/ImageLightbox.js +1 -1
- package/lib/features/agent/chat/ImageLightbox.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +57 -12
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +8 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +45 -36
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/Header.js +4 -2
- package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +7 -3
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +158 -8
- package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +6 -0
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +15 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +1 -0
- package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useFileProcessing.js +37 -3
- package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js +13 -13
- package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
- package/lib/features/magic-pdf/DownloadPopover.js +2 -2
- package/lib/features/magic-pdf/DownloadPopover.js.map +1 -1
- package/lib/features/magic-pdf/MagicPdfView.js +3 -3
- package/lib/features/magic-pdf/MagicPdfView.js.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsButton.js +2 -2
- package/lib/features/oauth/McpConnectionsButton.js.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.d.ts.map +1 -1
- package/lib/features/oauth/McpConnectionsDialog.js +1 -1
- package/lib/features/oauth/McpConnectionsDialog.js.map +1 -1
- package/lib/features/oauth/RemoteMcpConnectionButton.d.ts.map +1 -1
- package/lib/features/oauth/RemoteMcpConnectionButton.js +1 -1
- package/lib/features/oauth/RemoteMcpConnectionButton.js.map +1 -1
- package/lib/features/pdf-viewer/PdfPageSlider.js +2 -2
- package/lib/features/pdf-viewer/PdfPageSlider.js.map +1 -1
- package/lib/features/pdf-viewer/SimplePdfViewer.d.ts.map +1 -1
- package/lib/features/pdf-viewer/SimplePdfViewer.js +3 -3
- package/lib/features/pdf-viewer/SimplePdfViewer.js.map +1 -1
- package/lib/features/store/objects/DocumentQuickFilter.d.ts.map +1 -1
- package/lib/features/store/objects/DocumentQuickFilter.js +5 -5
- package/lib/features/store/objects/DocumentQuickFilter.js.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/features/store/objects/DocumentSearchResults.js +2 -2
- package/lib/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/features/store/objects/components/ContentDispositionButton.d.ts.map +1 -1
- package/lib/features/store/objects/components/ContentDispositionButton.js +1 -1
- package/lib/features/store/objects/components/ContentDispositionButton.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +3 -3
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/DocumentIcon.js +1 -1
- package/lib/features/store/objects/components/VectorSearchWidget.js +1 -1
- package/lib/features/store/objects/components/VectorSearchWidget.js.map +1 -1
- package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
- package/lib/features/store/objects/layout/renderers.js +2 -2
- package/lib/features/store/objects/layout/renderers.js.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js +1 -1
- package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js +4 -4
- package/lib/features/store/objects/selection/actions/StartWorkflowComponent.js.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.d.ts.map +1 -1
- package/lib/features/store/types/ContentObjectTypesTable.js +4 -4
- package/lib/features/store/types/ContentObjectTypesTable.js.map +1 -1
- package/lib/features/store/types/SelectContentTypeModal.js +1 -1
- package/lib/features/store/types/SelectContentTypeModal.js.map +1 -1
- package/lib/features/user/UserInfo.js +7 -3
- package/lib/features/user/UserInfo.js.map +1 -1
- package/lib/router/HistoryNavigator.d.ts.map +1 -1
- package/lib/router/HistoryNavigator.js +5 -0
- package/lib/router/HistoryNavigator.js.map +1 -1
- package/lib/router/Nav.d.ts.map +1 -1
- package/lib/router/Nav.js +9 -1
- package/lib/router/Nav.js.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +1 -1
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-router.js +1 -1
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +1 -1
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/form/Form.js +1 -1
- package/lib/widgets/form/Form.js.map +1 -1
- package/lib/widgets/json-view/JSONSwitcher.js +1 -1
- package/lib/widgets/json-view/JSONSwitcher.js.map +1 -1
- package/lib/widgets/json-view/JSONView.js +6 -6
- package/lib/widgets/json-view/JSONView.js.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.js +29 -0
- package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts +9 -0
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.d.ts.map +1 -1
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js +20 -17
- package/lib/widgets/xml-viewer/hooks/useXMLViewer.js.map +1 -1
- package/package.json +29 -30
- package/src/__tests__/a11y.test.tsx +73 -0
- package/src/core/components/SidePanel.tsx +1 -1
- package/src/core/components/shadcn/button.tsx +67 -12
- package/src/core/components/shadcn/modal/dialog.tsx +1 -1
- package/src/core/components/shadcn/resizeable.test.tsx +77 -0
- package/src/core/components/shadcn/resizeable.tsx +28 -2
- package/src/core/components/shadcn/tabs.test.tsx +65 -0
- package/src/core/components/shadcn/tabs.tsx +25 -3
- package/src/core/components/shadcn/theme/ThemeSwitcher.tsx +1 -1
- package/src/core/hooks/useIntersectionObserver.tsx +3 -1
- package/src/features/agent/chat/AgentChatPlaybackControls.tsx +0 -5
- package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +31 -0
- package/src/features/agent/chat/AgentRequestInputOverlay.tsx +6 -2
- package/src/features/agent/chat/AgentRightPanel.tsx +1 -1
- package/src/features/agent/chat/AskUserWidget.test.tsx +49 -0
- package/src/features/agent/chat/AskUserWidget.tsx +32 -22
- package/src/features/agent/chat/ImageLightbox.tsx +0 -1
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +246 -1
- package/src/features/agent/chat/ModernAgentConversation.tsx +62 -17
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +6 -1
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +81 -49
- package/src/features/agent/chat/ModernAgentOutput/Header.tsx +3 -3
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +25 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +5 -3
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +4 -1
- package/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +328 -42
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.test.ts +80 -0
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +21 -1
- package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +70 -0
- package/src/features/agent/chat/hooks/useFileProcessing.ts +37 -6
- package/src/features/magic-pdf/AnnotatedImageSlider.tsx +62 -66
- package/src/features/magic-pdf/DownloadPopover.tsx +2 -2
- package/src/features/magic-pdf/MagicPdfView.tsx +3 -3
- package/src/features/oauth/McpConnectionsButton.tsx +0 -2
- package/src/features/oauth/McpConnectionsDialog.tsx +0 -1
- package/src/features/oauth/RemoteMcpConnectionButton.tsx +0 -1
- package/src/features/pdf-viewer/PdfPageSlider.tsx +5 -5
- package/src/features/pdf-viewer/SimplePdfViewer.tsx +11 -12
- package/src/features/store/objects/DocumentQuickFilter.tsx +13 -15
- package/src/features/store/objects/DocumentSearchResults.tsx +3 -6
- package/src/features/store/objects/components/ContentDispositionButton.tsx +5 -1
- package/src/features/store/objects/components/ContentOverview.tsx +11 -11
- package/src/features/store/objects/components/DocumentIcon.tsx +1 -1
- package/src/features/store/objects/components/VectorSearchWidget.tsx +2 -2
- package/src/features/store/objects/layout/renderers.tsx +1 -2
- package/src/features/store/objects/selection/SelectionActions.tsx +1 -1
- package/src/features/store/objects/selection/actions/StartWorkflowComponent.tsx +6 -5
- package/src/features/store/types/ContentObjectTypesTable.tsx +12 -14
- package/src/features/store/types/SelectContentTypeModal.tsx +2 -2
- package/src/features/user/UserInfo.tsx +7 -7
- package/src/router/HistoryNavigator.ts +5 -0
- package/src/router/Nav.tsx +12 -3
- package/src/widgets/form/Form.tsx +1 -1
- package/src/widgets/json-view/JSONSwitcher.tsx +2 -2
- package/src/widgets/json-view/JSONView.tsx +7 -7
- package/src/widgets/monacoEditor/MonacoEditor.tsx +29 -0
- package/src/widgets/xml-viewer/hooks/useXMLViewer.test.ts +26 -0
- package/src/widgets/xml-viewer/hooks/useXMLViewer.ts +23 -18
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type JSONSchema,
|
|
7
7
|
type Plan,
|
|
8
8
|
} from '@vertesia/common';
|
|
9
|
-
import { Badge, Button, cn
|
|
9
|
+
import { Badge, Button, cn } from '@vertesia/ui/core';
|
|
10
10
|
import { i18nInstance, NAMESPACE, useUITranslation } from '@vertesia/ui/i18n';
|
|
11
11
|
import { NavLink } from '@vertesia/ui/router';
|
|
12
12
|
import { MarkdownRenderer, type MarkdownRendererProps } from '@vertesia/ui/widgets';
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
getRequestInputDisplayText,
|
|
50
50
|
getRequestInputMessageKey,
|
|
51
51
|
getResolvedToolApprovalKeys,
|
|
52
|
+
getToolApprovalResponseMetadata,
|
|
52
53
|
hasRequestInputUx,
|
|
53
54
|
isRequestInputAnswered,
|
|
54
55
|
isToolApprovalAnswerHidden,
|
|
@@ -763,7 +764,9 @@ function SummaryMessage({
|
|
|
763
764
|
options={uxConfig.options}
|
|
764
765
|
variant={uxConfig.variant}
|
|
765
766
|
multiSelect={uxConfig.multiSelect}
|
|
766
|
-
onSelect={(optionId) =>
|
|
767
|
+
onSelect={(optionId) =>
|
|
768
|
+
onSendMessage?.(optionId, getToolApprovalResponseMetadata(message, optionId))
|
|
769
|
+
}
|
|
767
770
|
onMultiSelect={(optionIds) => onSendMessage?.(optionIds.join(', '))}
|
|
768
771
|
allowFreeResponse={!uxConfig.options?.length || !!uxConfig.free_response}
|
|
769
772
|
placeholder={uxConfig.free_response?.placeholder}
|
|
@@ -2240,7 +2243,12 @@ function SummaryActivityRow({
|
|
|
2240
2243
|
options={uxConfig.options}
|
|
2241
2244
|
variant={uxConfig.variant}
|
|
2242
2245
|
multiSelect={uxConfig.multiSelect}
|
|
2243
|
-
onSelect={(optionId) =>
|
|
2246
|
+
onSelect={(optionId) =>
|
|
2247
|
+
onSendMessage?.(
|
|
2248
|
+
optionId,
|
|
2249
|
+
getToolApprovalResponseMetadata(message, optionId),
|
|
2250
|
+
)
|
|
2251
|
+
}
|
|
2244
2252
|
onMultiSelect={(optionIds) => onSendMessage?.(optionIds.join(', '))}
|
|
2245
2253
|
allowFreeResponse={
|
|
2246
2254
|
!uxConfig.options?.length || !!uxConfig.free_response
|
|
@@ -2328,8 +2336,43 @@ class MessageErrorBoundary extends Component<{ children: ReactNode }, { hasError
|
|
|
2328
2336
|
}
|
|
2329
2337
|
}
|
|
2330
2338
|
|
|
2339
|
+
// Sort chronologically, drop document-panel events + hidden types, and dedupe adjacent
|
|
2340
|
+
// identical messages. Shared by the rendered transcript and the workstream tab derivation.
|
|
2341
|
+
function sortAndDedupeMessages(messages: AgentMessage[], hiddenMessageTypes?: AgentMessageType[]): AgentMessage[] {
|
|
2342
|
+
const filtered = messages.filter(
|
|
2343
|
+
(message) => !isDocumentPanelEventMessage(message) && !hiddenMessageTypes?.includes(message.type),
|
|
2344
|
+
);
|
|
2345
|
+
|
|
2346
|
+
const sorted = [...filtered].sort((a, b) => {
|
|
2347
|
+
const timeA = typeof a.timestamp === 'number' ? a.timestamp : new Date(a.timestamp).getTime();
|
|
2348
|
+
const timeB = typeof b.timestamp === 'number' ? b.timestamp : new Date(b.timestamp).getTime();
|
|
2349
|
+
return timeA - timeB;
|
|
2350
|
+
});
|
|
2351
|
+
|
|
2352
|
+
const deduped: AgentMessage[] = [];
|
|
2353
|
+
for (const msg of sorted) {
|
|
2354
|
+
const previous = deduped[deduped.length - 1];
|
|
2355
|
+
if (previous && shouldCollapseAdjacentRenderedMessage(previous, msg)) {
|
|
2356
|
+
continue;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
if (previous && shouldDedupeAdjacentCompletedToolMessage(previous, msg)) {
|
|
2360
|
+
continue;
|
|
2361
|
+
}
|
|
2362
|
+
deduped.push(msg);
|
|
2363
|
+
}
|
|
2364
|
+
return deduped;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2331
2367
|
interface AllMessagesMixedProps {
|
|
2332
2368
|
messages: AgentMessage[];
|
|
2369
|
+
/**
|
|
2370
|
+
* Full, workstream-unfiltered message set used to derive the workstream tab list, per-tab
|
|
2371
|
+
* counts, and completion status. Falls back to `messages` when omitted. The parent filters
|
|
2372
|
+
* `messages` down to the active workstream, so deriving the tab list from `messages` alone
|
|
2373
|
+
* makes the tabs collapse (and vanish once `main` is selected) as you navigate between them.
|
|
2374
|
+
*/
|
|
2375
|
+
workstreamSourceMessages?: AgentMessage[];
|
|
2333
2376
|
bottomRef: React.RefObject<HTMLDivElement>;
|
|
2334
2377
|
viewMode?: 'stacked' | 'sliding';
|
|
2335
2378
|
isCompleted?: boolean;
|
|
@@ -2397,6 +2440,7 @@ const SCROLL_THROTTLE_MS = 100; // Max 10 scrolls per second
|
|
|
2397
2440
|
|
|
2398
2441
|
function AllMessagesMixedComponent({
|
|
2399
2442
|
messages,
|
|
2443
|
+
workstreamSourceMessages,
|
|
2400
2444
|
bottomRef,
|
|
2401
2445
|
viewMode = 'stacked',
|
|
2402
2446
|
isCompleted = false,
|
|
@@ -2536,36 +2580,26 @@ function AllMessagesMixedComponent({
|
|
|
2536
2580
|
|
|
2537
2581
|
// Sort all messages chronologically and dedupe adjacent identical messages
|
|
2538
2582
|
// Low-signal messages are suppressed at the source (server-side) via shouldSuppressLowSignalMessage
|
|
2539
|
-
const sortedMessages = React.useMemo(
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
const sorted = [...filtered].sort((a, b) => {
|
|
2545
|
-
const timeA = typeof a.timestamp === 'number' ? a.timestamp : new Date(a.timestamp).getTime();
|
|
2546
|
-
const timeB = typeof b.timestamp === 'number' ? b.timestamp : new Date(b.timestamp).getTime();
|
|
2547
|
-
return timeA - timeB;
|
|
2548
|
-
});
|
|
2549
|
-
|
|
2550
|
-
const deduped: AgentMessage[] = [];
|
|
2551
|
-
for (const msg of sorted) {
|
|
2552
|
-
const previous = deduped[deduped.length - 1];
|
|
2553
|
-
if (previous && shouldCollapseAdjacentRenderedMessage(previous, msg)) {
|
|
2554
|
-
continue;
|
|
2555
|
-
}
|
|
2583
|
+
const sortedMessages = React.useMemo(
|
|
2584
|
+
() => sortAndDedupeMessages(messages, hiddenMessageTypes),
|
|
2585
|
+
[messages, hiddenMessageTypes],
|
|
2586
|
+
);
|
|
2556
2587
|
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2588
|
+
// The workstream tabs are navigation over the WHOLE conversation, so derive them from the
|
|
2589
|
+
// unfiltered source rather than `messages` (which the parent filters to the active workstream).
|
|
2590
|
+
// Falling back to `sortedMessages` keeps callers that don't pass the source working unchanged.
|
|
2591
|
+
const workstreamSourceSorted = React.useMemo(
|
|
2592
|
+
() =>
|
|
2593
|
+
workstreamSourceMessages
|
|
2594
|
+
? sortAndDedupeMessages(workstreamSourceMessages, hiddenMessageTypes)
|
|
2595
|
+
: sortedMessages,
|
|
2596
|
+
[workstreamSourceMessages, hiddenMessageTypes, sortedMessages],
|
|
2597
|
+
);
|
|
2564
2598
|
|
|
2565
2599
|
const workstreams = React.useMemo(() => {
|
|
2566
|
-
const extractedWorkstreams = extractWorkstreams(
|
|
2600
|
+
const extractedWorkstreams = extractWorkstreams(workstreamSourceSorted);
|
|
2567
2601
|
|
|
2568
|
-
|
|
2602
|
+
workstreamSourceSorted.forEach((message) => {
|
|
2569
2603
|
const details = getWorkstreamLaunchDetails(message) ?? getWorkstreamActivityDetails(message);
|
|
2570
2604
|
if (!details) return;
|
|
2571
2605
|
extractedWorkstreams.set(
|
|
@@ -2575,7 +2609,7 @@ function AllMessagesMixedComponent({
|
|
|
2575
2609
|
});
|
|
2576
2610
|
|
|
2577
2611
|
return extractedWorkstreams;
|
|
2578
|
-
}, [
|
|
2612
|
+
}, [workstreamSourceSorted]);
|
|
2579
2613
|
|
|
2580
2614
|
const activeWorkstreamName = React.useMemo(() => {
|
|
2581
2615
|
if (activeWorkstream === 'all') return undefined;
|
|
@@ -2616,17 +2650,17 @@ function AllMessagesMixedComponent({
|
|
|
2616
2650
|
}
|
|
2617
2651
|
}, [activeWorkstream, workstreams, setActiveWorkstream]);
|
|
2618
2652
|
|
|
2619
|
-
// Count messages per workstream
|
|
2653
|
+
// Count messages per workstream (from the full source so counts don't shrink to the active tab)
|
|
2620
2654
|
const workstreamCounts = React.useMemo(() => {
|
|
2621
2655
|
const counts = new Map<string, number>();
|
|
2622
|
-
counts.set('all',
|
|
2656
|
+
counts.set('all', workstreamSourceSorted.length);
|
|
2623
2657
|
|
|
2624
2658
|
// Count main messages
|
|
2625
|
-
const mainMessages = filterMessagesByWorkstream(
|
|
2659
|
+
const mainMessages = filterMessagesByWorkstream(workstreamSourceSorted, 'main');
|
|
2626
2660
|
counts.set('main', mainMessages.length);
|
|
2627
2661
|
|
|
2628
2662
|
// Count other workstreams
|
|
2629
|
-
|
|
2663
|
+
workstreamSourceSorted.forEach((msg) => {
|
|
2630
2664
|
const workstreamId = getWorkstreamId(msg);
|
|
2631
2665
|
if (workstreamId !== 'main') {
|
|
2632
2666
|
counts.set(workstreamId, (counts.get(workstreamId) || 0) + 1);
|
|
@@ -2634,7 +2668,7 @@ function AllMessagesMixedComponent({
|
|
|
2634
2668
|
});
|
|
2635
2669
|
|
|
2636
2670
|
return counts;
|
|
2637
|
-
}, [
|
|
2671
|
+
}, [workstreamSourceSorted]);
|
|
2638
2672
|
|
|
2639
2673
|
// Filter messages based on active workstream
|
|
2640
2674
|
const displayMessages = React.useMemo(() => {
|
|
@@ -2869,7 +2903,7 @@ function AllMessagesMixedComponent({
|
|
|
2869
2903
|
// Group messages by workstream
|
|
2870
2904
|
const workstreamMessages = new Map<string, AgentMessage[]>();
|
|
2871
2905
|
|
|
2872
|
-
|
|
2906
|
+
workstreamSourceSorted.forEach((message) => {
|
|
2873
2907
|
const workstreamId = getWorkstreamId(message);
|
|
2874
2908
|
if (!workstreamMessages.has(workstreamId)) {
|
|
2875
2909
|
workstreamMessages.set(workstreamId, []);
|
|
@@ -2905,7 +2939,7 @@ function AllMessagesMixedComponent({
|
|
|
2905
2939
|
}
|
|
2906
2940
|
|
|
2907
2941
|
return statusMap;
|
|
2908
|
-
}, [
|
|
2942
|
+
}, [workstreamSourceSorted]);
|
|
2909
2943
|
|
|
2910
2944
|
return (
|
|
2911
2945
|
<div
|
|
@@ -3220,18 +3254,16 @@ function AllMessagesMixedComponent({
|
|
|
3220
3254
|
{isSummaryView && activeWorkstream !== 'all' && activeWorkstreamName && (
|
|
3221
3255
|
<div className="sticky top-0 z-20 -mx-2 bg-background/95 px-2 pb-2 pt-1 backdrop-blur sm:-mx-4 sm:px-4">
|
|
3222
3256
|
<div className="mx-auto flex w-full max-w-3xl items-center gap-2 border-b border-border/70 pb-3">
|
|
3223
|
-
<
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
</Button>
|
|
3234
|
-
</VTooltip>
|
|
3257
|
+
<Button
|
|
3258
|
+
type="button"
|
|
3259
|
+
variant="ghost"
|
|
3260
|
+
size="icon"
|
|
3261
|
+
className="size-8 shrink-0 text-muted hover:text-foreground"
|
|
3262
|
+
title={t('agent.backToMainAgent')}
|
|
3263
|
+
onClick={handleShowMainAgentChat}
|
|
3264
|
+
>
|
|
3265
|
+
<ArrowLeft className="size-4" aria-hidden="true" />
|
|
3266
|
+
</Button>
|
|
3235
3267
|
<div className="min-w-0 flex-1 truncate text-sm font-medium text-foreground">
|
|
3236
3268
|
{activeWorkstreamName}
|
|
3237
3269
|
</div>
|
|
@@ -100,7 +100,9 @@ export default function Header({
|
|
|
100
100
|
const controls = (
|
|
101
101
|
<div
|
|
102
102
|
className={cn(
|
|
103
|
-
isCompact
|
|
103
|
+
isCompact
|
|
104
|
+
? 'flex flex-row items-center gap-1 lg:flex-col'
|
|
105
|
+
: 'flex justify-end items-center gap-2 ms-auto',
|
|
104
106
|
)}
|
|
105
107
|
>
|
|
106
108
|
{showPlanButton && (
|
|
@@ -177,7 +179,6 @@ export default function Header({
|
|
|
177
179
|
variant={isPlaybackEnabled ? 'primary' : 'ghost'}
|
|
178
180
|
onClick={onTogglePlayback}
|
|
179
181
|
aria-pressed={isPlaybackEnabled}
|
|
180
|
-
aria-label={t('agent.rewind.label')}
|
|
181
182
|
title={t('agent.rewind.label')}
|
|
182
183
|
className={cn(
|
|
183
184
|
'transition-all duration-200 rounded-md',
|
|
@@ -376,7 +377,6 @@ function MoreDropdown({
|
|
|
376
377
|
size={compact ? 'icon' : 'xs'}
|
|
377
378
|
variant="ghost"
|
|
378
379
|
title={t('agent.moreActions')}
|
|
379
|
-
aria-label={t('agent.moreActions')}
|
|
380
380
|
className={compact ? 'size-8 rounded-lg' : undefined}
|
|
381
381
|
>
|
|
382
382
|
<MoreVertical className="size-4" />
|
|
@@ -54,6 +54,31 @@ describe('MessageInput', () => {
|
|
|
54
54
|
expect(onFilesSelected).toHaveBeenCalledWith([file]);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
it('does not send or clear the text when Enter is pressed while files are still processing', () => {
|
|
58
|
+
const onSend = vi.fn();
|
|
59
|
+
renderWithProviders(<MessageInput onSend={onSend} onFilesSelected={vi.fn()} hasProcessingFiles />);
|
|
60
|
+
|
|
61
|
+
const textbox = screen.getByRole('textbox') as HTMLTextAreaElement;
|
|
62
|
+
fireEvent.change(textbox, { target: { value: 'please analyze this' } });
|
|
63
|
+
fireEvent.keyDown(textbox, { key: 'Enter' });
|
|
64
|
+
|
|
65
|
+
expect(onSend).not.toHaveBeenCalled();
|
|
66
|
+
// The typed text must be retained so the user doesn't have to re-type it.
|
|
67
|
+
expect(textbox.value).toBe('please analyze this');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('sends and clears the text on Enter once files have finished processing', () => {
|
|
71
|
+
const onSend = vi.fn();
|
|
72
|
+
renderWithProviders(<MessageInput onSend={onSend} onFilesSelected={vi.fn()} />);
|
|
73
|
+
|
|
74
|
+
const textbox = screen.getByRole('textbox') as HTMLTextAreaElement;
|
|
75
|
+
fireEvent.change(textbox, { target: { value: 'please analyze this' } });
|
|
76
|
+
fireEvent.keyDown(textbox, { key: 'Enter' });
|
|
77
|
+
|
|
78
|
+
expect(onSend).toHaveBeenCalledWith('please analyze this');
|
|
79
|
+
expect(textbox.value).toBe('');
|
|
80
|
+
});
|
|
81
|
+
|
|
57
82
|
it('renders attachment actions before the MCP slot', () => {
|
|
58
83
|
renderWithProviders(<MessageInput onSend={vi.fn()} mcpSlot={<button type="button">MCP</button>} isCompleted />);
|
|
59
84
|
|
|
@@ -346,7 +346,11 @@ export default function MessageInput({
|
|
|
346
346
|
|
|
347
347
|
const handleSend = () => {
|
|
348
348
|
const message = value.trim();
|
|
349
|
-
|
|
349
|
+
// Mirror the send button's disabled condition so the keyboard (Enter) path can't
|
|
350
|
+
// dispatch a send that the parent will reject while files are still uploading —
|
|
351
|
+
// otherwise the text would be cleared below and lost. The disabled button + its
|
|
352
|
+
// "wait for files" tooltip already explain why Enter does nothing here.
|
|
353
|
+
if (!message || disabled || isSending || hasProcessingFiles) return;
|
|
350
354
|
|
|
351
355
|
onSend(message);
|
|
352
356
|
setValue('');
|
|
@@ -519,7 +523,6 @@ export default function MessageInput({
|
|
|
519
523
|
variant="ghost"
|
|
520
524
|
size="icon"
|
|
521
525
|
className="size-8 rounded-full text-muted hover:bg-muted"
|
|
522
|
-
aria-label={t('agent.addAttachment')}
|
|
523
526
|
title={t('agent.addAttachment')}
|
|
524
527
|
>
|
|
525
528
|
<PlusIcon className="size-4" />
|
|
@@ -650,7 +653,6 @@ export default function MessageInput({
|
|
|
650
653
|
'[&_svg]:text-destructive disabled:[&_svg]:text-muted',
|
|
651
654
|
)}
|
|
652
655
|
title={t('agent.stopTooltip')}
|
|
653
|
-
aria-label={t('agent.stopTooltip')}
|
|
654
656
|
>
|
|
655
657
|
{isStopping ? (
|
|
656
658
|
<Spinner size="sm" />
|
|
@@ -35,6 +35,7 @@ import { ThinkingMessages } from '../WaitingMessages';
|
|
|
35
35
|
import { AttachmentPreviewList, parseUserMessageAttachments } from './AttachmentPreview';
|
|
36
36
|
import { MessageDeliveryStatus } from './MessageDeliveryStatus';
|
|
37
37
|
import { processContentForMarkdown } from './processContentForMarkdown';
|
|
38
|
+
import { getToolApprovalResponseMetadata } from './requestInputMessages';
|
|
38
39
|
import { getWorkstreamId } from './utils';
|
|
39
40
|
|
|
40
41
|
/** className overrides for MessageItem — single source of truth for all className overrides. */
|
|
@@ -649,7 +650,9 @@ function MessageItemComponent({
|
|
|
649
650
|
options={uxConfig.options}
|
|
650
651
|
variant={uxConfig.variant}
|
|
651
652
|
multiSelect={uxConfig.multiSelect}
|
|
652
|
-
onSelect={(optionId) =>
|
|
653
|
+
onSelect={(optionId) =>
|
|
654
|
+
onSendMessage?.(optionId, getToolApprovalResponseMetadata(message, optionId))
|
|
655
|
+
}
|
|
653
656
|
onMultiSelect={(optionIds) => onSendMessage?.(optionIds.join(', '))}
|
|
654
657
|
allowFreeResponse={!uxConfig.options?.length || !!uxConfig.free_response}
|
|
655
658
|
placeholder={uxConfig.free_response?.placeholder}
|