@sybilion/uilib 1.3.79 → 1.3.80
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/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +5 -1
- package/dist/esm/components/ui/Chat/buildChatSendMessagePayload.js +3 -1
- package/dist/esm/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js +3 -1
- package/dist/esm/contexts/chat-context.js +18 -4
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.ts +2 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +15 -6
- package/src/components/ui/Chat/buildChatSendMessagePayload.ts +2 -1
- package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.test.ts +13 -4
- package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.ts +2 -1
- package/src/contexts/chat-context.tsx +24 -2
|
@@ -10,6 +10,7 @@ import { DropZone } from '../../DropZone/DropZone.js';
|
|
|
10
10
|
import { PanelResizeHandle } from '../../Sidebar/Sidebar.js';
|
|
11
11
|
import SidebarStem from '../../Sidebar/Sidebar.styl.js';
|
|
12
12
|
import { Chat } from '../Chat.js';
|
|
13
|
+
import { MessageRole } from '../Chat.types.js';
|
|
13
14
|
import { filterToTextAttachments, isAttachmentsDropzoneEnabled, buildAcceptAttr } from '../chatAttachmentAccept.js';
|
|
14
15
|
import { extractChatAttachmentItems } from '../chatAttachmentExtract.js';
|
|
15
16
|
import S from './ChatChrome.styl.js';
|
|
@@ -21,6 +22,7 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
21
22
|
const [pendingAttachments, setPendingAttachments] = useState([]);
|
|
22
23
|
const [isExtractingAttachments, setIsExtractingAttachments] = useState(false);
|
|
23
24
|
const promptDisabled = isExtractingAttachments;
|
|
25
|
+
const hasInProgressSystemMessage = useMemo(() => messages.some(msg => msg.role === MessageRole.SYSTEM && msg.inProgress === true), [messages]);
|
|
24
26
|
const handleAttachmentFiles = useCallback((files) => {
|
|
25
27
|
if (promptDisabled || files.length === 0)
|
|
26
28
|
return;
|
|
@@ -85,7 +87,9 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
85
87
|
const label = displayLabelForBranchKeyFromMessages(key, messages) ??
|
|
86
88
|
humanizeBranchKey(key);
|
|
87
89
|
return (jsx("span", { className: S.branchBtnWrap, children: jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: isLoading, onClick: () => onQuickReply(key, label), children: [jsx(PaperPlaneRightIcon, {}), label] }) }, key));
|
|
88
|
-
}) })) : null, showInlinePresets && renderPresets('inline'), isLoading &&
|
|
90
|
+
}) })) : null, showInlinePresets && renderPresets('inline'), isLoading &&
|
|
91
|
+
!hasInProgressSystemMessage &&
|
|
92
|
+
(isLastMessageFromUser || loadingLabel) && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: loadingLabel ?? 'Thinking...' }))] }) })), isEmpty ? (jsx("div", { className: S.fixedPresets, children: renderPresets('fixed') })) : null, jsxs("div", { className: cn(S.footer, footerClassName), children: [isEmpty ? (jsx("div", { className: S.notice, children: "Forecast Assistant can make mistakes." })) : null, jsx(Chat.Prompt, { onSubmit: handlePromptSubmitWithAttachments, disabled: promptDisabled, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined, placeholder: promptPlaceholder, slashCommandItems: slashCommandItems, onSlashItemCommand: onSlashItemCommand, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
export { ChatChrome };
|
|
@@ -68,7 +68,9 @@ function displayTextFromSendPayload(message) {
|
|
|
68
68
|
}
|
|
69
69
|
/** Optional loading shimmer label from a structured send payload. */
|
|
70
70
|
function loadingLabelFromSendPayload(message) {
|
|
71
|
-
|
|
71
|
+
if (typeof message === 'string')
|
|
72
|
+
return undefined;
|
|
73
|
+
return message.loadingLabel ?? message.systemProgressLabel;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export { buildChatSendMessagePayload, displayTextFromSendPayload, loadingLabelFromSendPayload, normalizeUserTextFileAttachments };
|
package/dist/esm/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js
CHANGED
|
@@ -97,12 +97,14 @@ function shiftNormalizedSeriesBackward(series, lagMonths) {
|
|
|
97
97
|
function applyDriversComparisonViewToPayload(payload, tab) {
|
|
98
98
|
if (!payload || tab === 'lagged')
|
|
99
99
|
return payload;
|
|
100
|
+
if (!payload.target?.normalized_series)
|
|
101
|
+
return payload;
|
|
100
102
|
return {
|
|
101
103
|
target: {
|
|
102
104
|
...payload.target,
|
|
103
105
|
normalized_series: { ...payload.target.normalized_series },
|
|
104
106
|
},
|
|
105
|
-
drivers: payload.drivers.map(driver => {
|
|
107
|
+
drivers: (payload.drivers ?? []).map(driver => {
|
|
106
108
|
const lagMonths = parseLagMonthsFromLabel(resolveDriverLagLabel(driver));
|
|
107
109
|
const series = driver.normalized_series ?? {};
|
|
108
110
|
const normalized_series = lagMonths != null && lagMonths > 0
|
|
@@ -343,13 +343,19 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
343
343
|
userTextFileAttachments: normalizeUserTextFileAttachments(message),
|
|
344
344
|
});
|
|
345
345
|
}
|
|
346
|
+
let progressMessageId;
|
|
347
|
+
if (typeof message !== 'string' && message.systemProgressLabel) {
|
|
348
|
+
progressMessageId = addMessage(scopeId, targetChatId, MessageRole.SYSTEM, message.systemProgressLabel, { inProgress: true });
|
|
349
|
+
}
|
|
346
350
|
const pendingChatSessionId = targetChatId;
|
|
347
351
|
beginOutboundPending(scopeId, pendingChatSessionId);
|
|
352
|
+
let effectiveSessionId = pendingChatSessionId;
|
|
348
353
|
try {
|
|
349
354
|
const data = await sendChatMessageFn(apiPayload, pendingChatSessionId);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
355
|
+
effectiveSessionId =
|
|
356
|
+
data.session_id && data.session_id !== pendingChatSessionId
|
|
357
|
+
? data.session_id
|
|
358
|
+
: pendingChatSessionId;
|
|
353
359
|
if (data.session_id && data.session_id !== pendingChatSessionId) {
|
|
354
360
|
setChats(prev => {
|
|
355
361
|
const scopeChats = prev[scopeId] ?? [];
|
|
@@ -362,14 +368,21 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
362
368
|
});
|
|
363
369
|
setCurrentChatId(scopeId, data.session_id);
|
|
364
370
|
}
|
|
371
|
+
if (progressMessageId) {
|
|
372
|
+
removeMessageById(scopeId, effectiveSessionId, progressMessageId);
|
|
373
|
+
progressMessageId = undefined;
|
|
374
|
+
}
|
|
365
375
|
addMessage(scopeId, effectiveSessionId, MessageRole.ASSISTANT, data.response);
|
|
366
376
|
return { response: data.response, sessionId: effectiveSessionId };
|
|
367
377
|
}
|
|
368
378
|
catch (error) {
|
|
379
|
+
if (progressMessageId) {
|
|
380
|
+
removeMessageById(scopeId, effectiveSessionId, progressMessageId);
|
|
381
|
+
}
|
|
369
382
|
const errorMessage = error instanceof Error
|
|
370
383
|
? error.message
|
|
371
384
|
: 'Sorry, I encountered an error processing your message. Please try again.';
|
|
372
|
-
addMessage(scopeId,
|
|
385
|
+
addMessage(scopeId, effectiveSessionId, MessageRole.ASSISTANT, errorMessage);
|
|
373
386
|
throw error;
|
|
374
387
|
}
|
|
375
388
|
finally {
|
|
@@ -377,6 +390,7 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
377
390
|
}
|
|
378
391
|
}, [
|
|
379
392
|
addMessage,
|
|
393
|
+
removeMessageById,
|
|
380
394
|
beginOutboundPending,
|
|
381
395
|
endOutboundPending,
|
|
382
396
|
getCurrentChatId,
|
|
@@ -21,6 +21,8 @@ export type ChatSendMessagePayload = {
|
|
|
21
21
|
omitUserMessage?: boolean;
|
|
22
22
|
/** Shimmer label while waiting on the API; defaults to "Thinking...". */
|
|
23
23
|
loadingLabel?: string;
|
|
24
|
+
/** When set, show as a SYSTEM inProgress bubble instead of the footer shimmer. */
|
|
25
|
+
systemProgressLabel?: string;
|
|
24
26
|
};
|
|
25
27
|
export type ChatMetaValue = string | number | boolean | null;
|
|
26
28
|
export type ChatMeta = Record<string, ChatMetaValue>;
|
package/package.json
CHANGED
|
@@ -29,6 +29,8 @@ export type ChatSendMessagePayload = {
|
|
|
29
29
|
omitUserMessage?: boolean;
|
|
30
30
|
/** Shimmer label while waiting on the API; defaults to "Thinking...". */
|
|
31
31
|
loadingLabel?: string;
|
|
32
|
+
/** When set, show as a SYSTEM inProgress bubble instead of the footer shimmer. */
|
|
33
|
+
systemProgressLabel?: string;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export type ChatMetaValue = string | number | boolean | null;
|
|
@@ -14,7 +14,7 @@ import { DropZone } from '../../DropZone/DropZone';
|
|
|
14
14
|
import { PanelResizeHandle } from '../../Sidebar/Sidebar';
|
|
15
15
|
import SidebarStem from '../../Sidebar/Sidebar.styl';
|
|
16
16
|
import { Chat } from '../Chat';
|
|
17
|
-
import type
|
|
17
|
+
import { type ChatAttachmentDropItem, MessageRole } from '../Chat.types';
|
|
18
18
|
import {
|
|
19
19
|
buildAcceptAttr,
|
|
20
20
|
filterToTextAttachments,
|
|
@@ -76,6 +76,13 @@ export function ChatChrome({
|
|
|
76
76
|
>([]);
|
|
77
77
|
const [isExtractingAttachments, setIsExtractingAttachments] = useState(false);
|
|
78
78
|
const promptDisabled = isExtractingAttachments;
|
|
79
|
+
const hasInProgressSystemMessage = useMemo(
|
|
80
|
+
() =>
|
|
81
|
+
messages.some(
|
|
82
|
+
msg => msg.role === MessageRole.SYSTEM && msg.inProgress === true,
|
|
83
|
+
),
|
|
84
|
+
[messages],
|
|
85
|
+
);
|
|
79
86
|
|
|
80
87
|
const handleAttachmentFiles = useCallback(
|
|
81
88
|
(files: File[]) => {
|
|
@@ -263,11 +270,13 @@ export function ChatChrome({
|
|
|
263
270
|
|
|
264
271
|
{showInlinePresets && renderPresets('inline')}
|
|
265
272
|
|
|
266
|
-
{isLoading &&
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
273
|
+
{isLoading &&
|
|
274
|
+
!hasInProgressSystemMessage &&
|
|
275
|
+
(isLastMessageFromUser || loadingLabel) && (
|
|
276
|
+
<TextShimmer duration={1} spread={5} className={S.loader}>
|
|
277
|
+
{loadingLabel ?? 'Thinking...'}
|
|
278
|
+
</TextShimmer>
|
|
279
|
+
)}
|
|
271
280
|
</Scroll>
|
|
272
281
|
</div>
|
|
273
282
|
)}
|
|
@@ -85,5 +85,6 @@ export function displayTextFromSendPayload(
|
|
|
85
85
|
export function loadingLabelFromSendPayload(
|
|
86
86
|
message: string | ChatSendMessagePayload,
|
|
87
87
|
): string | undefined {
|
|
88
|
-
|
|
88
|
+
if (typeof message === 'string') return undefined;
|
|
89
|
+
return message.loadingLabel ?? message.systemProgressLabel;
|
|
89
90
|
}
|
package/src/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.test.ts
CHANGED
|
@@ -127,7 +127,16 @@ describe('applyDriversComparisonViewToPayload', () => {
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
it('returns original payload for lagged tab', () => {
|
|
130
|
-
expect(applyDriversComparisonViewToPayload(payload, 'lagged')).toBe(
|
|
130
|
+
expect(applyDriversComparisonViewToPayload(payload, 'lagged')).toBe(
|
|
131
|
+
payload,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('returns original payload for overlapped tab when target series missing', () => {
|
|
136
|
+
const incomplete = { drivers: payload.drivers } as typeof payload;
|
|
137
|
+
expect(applyDriversComparisonViewToPayload(incomplete, 'overlapped')).toBe(
|
|
138
|
+
incomplete,
|
|
139
|
+
);
|
|
131
140
|
});
|
|
132
141
|
|
|
133
142
|
it('shifts driver series backward for overlapped tab without mutating source', () => {
|
|
@@ -327,8 +336,8 @@ describe('buildDriversComparisonChartData historical window floor', () => {
|
|
|
327
336
|
|
|
328
337
|
expect(laggedChart[0]?.date).toBe('2014-10-01');
|
|
329
338
|
expect(overlappedChart[0]?.date).toBe('2014-07-01');
|
|
330
|
-
expect(
|
|
331
|
-
|
|
332
|
-
)
|
|
339
|
+
expect(overlappedChart[0]?.date!.localeCompare(laggedChart[0]?.date!)).toBe(
|
|
340
|
+
-1,
|
|
341
|
+
);
|
|
333
342
|
});
|
|
334
343
|
});
|
|
@@ -125,13 +125,14 @@ export function applyDriversComparisonViewToPayload(
|
|
|
125
125
|
tab: DriversComparisonViewTab,
|
|
126
126
|
): BacktestsComponentPayload | null {
|
|
127
127
|
if (!payload || tab === 'lagged') return payload;
|
|
128
|
+
if (!payload.target?.normalized_series) return payload;
|
|
128
129
|
|
|
129
130
|
return {
|
|
130
131
|
target: {
|
|
131
132
|
...payload.target,
|
|
132
133
|
normalized_series: { ...payload.target.normalized_series },
|
|
133
134
|
},
|
|
134
|
-
drivers: payload.drivers.map(driver => {
|
|
135
|
+
drivers: (payload.drivers ?? []).map(driver => {
|
|
135
136
|
const lagMonths = parseLagMonthsFromLabel(resolveDriverLagLabel(driver));
|
|
136
137
|
const series = driver.normalized_series ?? {};
|
|
137
138
|
const normalized_series =
|
|
@@ -546,12 +546,24 @@ export function ChatProvider({
|
|
|
546
546
|
);
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
+
let progressMessageId: string | undefined;
|
|
550
|
+
if (typeof message !== 'string' && message.systemProgressLabel) {
|
|
551
|
+
progressMessageId = addMessage(
|
|
552
|
+
scopeId,
|
|
553
|
+
targetChatId,
|
|
554
|
+
MessageRole.SYSTEM,
|
|
555
|
+
message.systemProgressLabel,
|
|
556
|
+
{ inProgress: true },
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
549
560
|
const pendingChatSessionId = targetChatId;
|
|
550
561
|
beginOutboundPending(scopeId, pendingChatSessionId);
|
|
562
|
+
let effectiveSessionId = pendingChatSessionId;
|
|
551
563
|
try {
|
|
552
564
|
const data = await sendChatMessageFn(apiPayload, pendingChatSessionId);
|
|
553
565
|
|
|
554
|
-
|
|
566
|
+
effectiveSessionId =
|
|
555
567
|
data.session_id && data.session_id !== pendingChatSessionId
|
|
556
568
|
? data.session_id
|
|
557
569
|
: pendingChatSessionId;
|
|
@@ -573,6 +585,11 @@ export function ChatProvider({
|
|
|
573
585
|
setCurrentChatId(scopeId, data.session_id);
|
|
574
586
|
}
|
|
575
587
|
|
|
588
|
+
if (progressMessageId) {
|
|
589
|
+
removeMessageById(scopeId, effectiveSessionId, progressMessageId);
|
|
590
|
+
progressMessageId = undefined;
|
|
591
|
+
}
|
|
592
|
+
|
|
576
593
|
addMessage(
|
|
577
594
|
scopeId,
|
|
578
595
|
effectiveSessionId,
|
|
@@ -582,6 +599,10 @@ export function ChatProvider({
|
|
|
582
599
|
|
|
583
600
|
return { response: data.response, sessionId: effectiveSessionId };
|
|
584
601
|
} catch (error) {
|
|
602
|
+
if (progressMessageId) {
|
|
603
|
+
removeMessageById(scopeId, effectiveSessionId, progressMessageId);
|
|
604
|
+
}
|
|
605
|
+
|
|
585
606
|
const errorMessage =
|
|
586
607
|
error instanceof Error
|
|
587
608
|
? error.message
|
|
@@ -589,7 +610,7 @@ export function ChatProvider({
|
|
|
589
610
|
|
|
590
611
|
addMessage(
|
|
591
612
|
scopeId,
|
|
592
|
-
|
|
613
|
+
effectiveSessionId,
|
|
593
614
|
MessageRole.ASSISTANT,
|
|
594
615
|
errorMessage,
|
|
595
616
|
);
|
|
@@ -600,6 +621,7 @@ export function ChatProvider({
|
|
|
600
621
|
},
|
|
601
622
|
[
|
|
602
623
|
addMessage,
|
|
624
|
+
removeMessageById,
|
|
603
625
|
beginOutboundPending,
|
|
604
626
|
endOutboundPending,
|
|
605
627
|
getCurrentChatId,
|