@sybilion/uilib 1.3.78 → 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/Chat.js +2 -2
- package/dist/esm/components/ui/Chat/Chat.styl.js +1 -1
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +10 -6
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl.js +1 -1
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +2 -2
- package/dist/esm/components/ui/Chat/ChatPresets/ChatPresets.styl.js +1 -1
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.js +2 -2
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +2 -1
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +6 -5
- package/dist/esm/components/ui/Chat/buildChatSendMessagePayload.js +3 -1
- package/dist/esm/components/ui/FileChip/FileChip.js +4 -0
- package/dist/esm/components/widgets/DriversComparisonChart/driversComparisonChart.helpers.js +3 -1
- package/dist/esm/contexts/chat-context.js +51 -5
- package/dist/esm/index.js +1 -0
- package/dist/esm/types/src/components/ui/Chat/Chat.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +12 -0
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.types.d.ts +4 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/ChatMessage.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSelector.d.ts +2 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +5 -3
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +3 -1
- package/dist/esm/types/src/components/ui/FileChip/FileChip.types.d.ts +1 -1
- package/dist/esm/types/src/contexts/chat-context.d.ts +16 -4
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.styl +4 -1
- package/src/components/ui/Chat/Chat.tsx +2 -1
- package/src/components/ui/Chat/Chat.types.ts +14 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl +20 -10
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl.d.ts +2 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +27 -10
- package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +4 -0
- package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl +1 -2
- package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +9 -1
- package/src/components/ui/Chat/ChatPresets/ChatPresets.styl +5 -4
- package/src/components/ui/Chat/ChatSheet/ChatSelector.tsx +3 -1
- package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +2 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +24 -5
- package/src/components/ui/Chat/buildChatSendMessagePayload.ts +2 -1
- package/src/components/ui/Chat/index.ts +4 -0
- package/src/components/ui/FileChip/FileChip.tsx +11 -0
- package/src/components/ui/FileChip/FileChip.types.ts +1 -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 +80 -6
- package/src/docs/pages/ChatPage.styl +6 -0
- package/src/docs/pages/ChatPage.styl.d.ts +7 -0
- package/src/docs/pages/ChatPage.tsx +30 -87
- package/src/docs/pages/ChatSlashCommandsPage.tsx +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { type Chat, type ChatSendMessagePayload, type Message, MessageRole, type UserTextFileAttachment } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
|
+
import { type Chat, type ChatMeta, type ChatSendMessagePayload, type Message, MessageRole, type UserTextFileAttachment } from '#uilib/components/ui/Chat/Chat.types';
|
|
3
3
|
import type { ChatResponse } from '#uilib/types/chat-api.types';
|
|
4
4
|
export type SendChatMessageFn = (message: string, targetChatId: string) => Promise<ChatResponse>;
|
|
5
5
|
export type { ChatSendMessagePayload, UserTextFileAttachment, } from '#uilib/components/ui/Chat/Chat.types';
|
|
@@ -7,16 +7,22 @@ export type AddChatMessageOptions = {
|
|
|
7
7
|
userTextFileAttachments?: UserTextFileAttachment[];
|
|
8
8
|
/** SYSTEM-only: mark as transient progress placeholder (shimmer until removed). */
|
|
9
9
|
inProgress?: boolean;
|
|
10
|
+
meta?: ChatMeta;
|
|
10
11
|
};
|
|
11
12
|
export type UpdateChatMessagePatch = {
|
|
12
13
|
role?: MessageRole;
|
|
13
14
|
text?: string;
|
|
14
15
|
inProgress?: boolean;
|
|
16
|
+
meta?: ChatMeta;
|
|
15
17
|
};
|
|
16
18
|
export type NewChatOptions = {
|
|
17
19
|
/** When set, seeds the new session's messages (e.g. continue dialog in reports). */
|
|
18
20
|
seedMessages?: readonly Message[];
|
|
19
21
|
};
|
|
22
|
+
export type SendMessageResult = {
|
|
23
|
+
response: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
};
|
|
20
26
|
export interface ChatContextType {
|
|
21
27
|
/** Returns the new session id, or undefined if no user / not created. */
|
|
22
28
|
newChat: (scopeId: string, options?: NewChatOptions) => string | undefined;
|
|
@@ -26,7 +32,9 @@ export interface ChatContextType {
|
|
|
26
32
|
updateMessageById: (scopeId: string, chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
|
|
27
33
|
/** Replaces all messages on a session (e.g. seeding from another chat). */
|
|
28
34
|
setChatMessages: (scopeId: string, chatId: string, messages: Message[]) => void;
|
|
29
|
-
|
|
35
|
+
/** Shallow-merge keys into `chat.meta` and persist. */
|
|
36
|
+
updateChatMeta: (scopeId: string, chatId: string, patch: ChatMeta) => void;
|
|
37
|
+
sendMessage: (scopeId: string, message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
|
|
30
38
|
getChatsForScopeId: (scopeId: string) => Chat[];
|
|
31
39
|
getCurrentChatId: (scopeId: string) => string | null;
|
|
32
40
|
deleteChat: (scopeId: string, sessionId: string) => void;
|
|
@@ -68,7 +76,9 @@ export declare function useChatsForScopeId(scopeId: string): {
|
|
|
68
76
|
addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
|
|
69
77
|
removeMessageById: (chatId: string, messageId: string) => void;
|
|
70
78
|
updateMessageById: (chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
|
|
71
|
-
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<
|
|
79
|
+
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
|
|
80
|
+
updateChatMeta: (chatId: string, patch: ChatMeta) => void;
|
|
81
|
+
setChatMessages: (chatId: string, messages: Message[]) => void;
|
|
72
82
|
deleteChat: (sessionId: string) => void;
|
|
73
83
|
};
|
|
74
84
|
/** @deprecated Use useChatsForScopeId */
|
|
@@ -82,7 +92,9 @@ export declare function useChatsForDataset(scopeId: string): {
|
|
|
82
92
|
addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
|
|
83
93
|
removeMessageById: (chatId: string, messageId: string) => void;
|
|
84
94
|
updateMessageById: (chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
|
|
85
|
-
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<
|
|
95
|
+
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
|
|
96
|
+
updateChatMeta: (chatId: string, patch: ChatMeta) => void;
|
|
97
|
+
setChatMessages: (chatId: string, messages: Message[]) => void;
|
|
86
98
|
deleteChat: (sessionId: string) => void;
|
|
87
99
|
};
|
|
88
100
|
export declare function useCurrentChat(scopeId: string): Chat;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
.root
|
|
2
2
|
display flex
|
|
3
3
|
flex-direction column
|
|
4
|
+
flex 1
|
|
4
5
|
height 100%
|
|
5
6
|
min-height 0
|
|
6
7
|
// max-height 100vh
|
|
@@ -15,4 +16,6 @@
|
|
|
15
16
|
padding-right var(--p-12) // to not overlap with ChatSheet close button
|
|
16
17
|
|
|
17
18
|
.isEmpty
|
|
18
|
-
|
|
19
|
+
flex 1
|
|
20
|
+
min-height 0
|
|
21
|
+
overflow hidden
|
|
@@ -16,11 +16,12 @@ export function Chat({
|
|
|
16
16
|
scopeId,
|
|
17
17
|
onChatDeleted,
|
|
18
18
|
onNewChat,
|
|
19
|
+
hideChatSelector = false,
|
|
19
20
|
...props
|
|
20
21
|
}: ChatProps) {
|
|
21
22
|
return (
|
|
22
23
|
<div className={cn(S.root, className, isEmpty && S.isEmpty)} {...props}>
|
|
23
|
-
{scopeId ? (
|
|
24
|
+
{scopeId && !hideChatSelector ? (
|
|
24
25
|
<div className={S.header}>
|
|
25
26
|
<ChatSelector
|
|
26
27
|
id={scopeId}
|
|
@@ -29,8 +29,14 @@ 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
|
|
|
36
|
+
export type ChatMetaValue = string | number | boolean | null;
|
|
37
|
+
|
|
38
|
+
export type ChatMeta = Record<string, ChatMetaValue>;
|
|
39
|
+
|
|
34
40
|
export interface Message {
|
|
35
41
|
id: string;
|
|
36
42
|
role: MessageRole;
|
|
@@ -39,12 +45,14 @@ export interface Message {
|
|
|
39
45
|
userTextFileAttachments?: UserTextFileAttachment[];
|
|
40
46
|
/** SYSTEM-only: transient progress placeholder while work is in flight. */
|
|
41
47
|
inProgress?: boolean;
|
|
48
|
+
meta?: ChatMeta;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
export interface Chat {
|
|
45
52
|
session_id: string;
|
|
46
53
|
name: string;
|
|
47
54
|
messages: Message[];
|
|
55
|
+
meta?: ChatMeta;
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
export interface ChatPreset {
|
|
@@ -124,6 +132,10 @@ export interface ChatMessageProps {
|
|
|
124
132
|
onScriptContinue?: () => void;
|
|
125
133
|
/** Renders `[CHART]` placeholder in assistant messages (app supplies dataset chart). */
|
|
126
134
|
renderMessageChart?: () => ReactNode;
|
|
135
|
+
/** Full message for system-role render delegation. */
|
|
136
|
+
message?: Message;
|
|
137
|
+
/** When set, SYSTEM messages render via this callback instead of plain text. */
|
|
138
|
+
renderSystemMessage?: (message: Message) => ReactNode;
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
|
|
@@ -135,4 +147,6 @@ export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
135
147
|
onChatDeleted?: (sessionId: string) => void;
|
|
136
148
|
/** "+ New Chat" in the selector; when omitted, starts an empty session. */
|
|
137
149
|
onNewChat?: () => void;
|
|
150
|
+
/** When true, skip built-in header ChatSelector (e.g. external page-header slot). */
|
|
151
|
+
hideChatSelector?: boolean;
|
|
138
152
|
}
|
|
@@ -83,29 +83,39 @@
|
|
|
83
83
|
.scrollInner
|
|
84
84
|
padding-top var(--p-10)
|
|
85
85
|
min-height 100%
|
|
86
|
-
// padding-bottom 320px // goes under prompt
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
.emptyBody
|
|
88
|
+
flex 1
|
|
89
|
+
min-height 0
|
|
90
|
+
display flex
|
|
91
|
+
flex-direction column
|
|
92
|
+
overflow hidden
|
|
92
93
|
|
|
94
|
+
& > *
|
|
95
|
+
flex 1
|
|
96
|
+
min-height 0
|
|
97
|
+
overflow auto
|
|
93
98
|
|
|
94
99
|
// ---------------
|
|
95
100
|
|
|
96
101
|
.footer
|
|
102
|
+
position relative
|
|
97
103
|
z-index 50
|
|
98
104
|
display flex
|
|
99
105
|
flex-direction column
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
bottom 0
|
|
103
|
-
width 100%;
|
|
106
|
+
flex-shrink 0
|
|
107
|
+
width 100%
|
|
104
108
|
|
|
105
109
|
backdrop-filter blur(30px)
|
|
106
110
|
background-color var(--background-alpha-800)
|
|
107
111
|
border-top 1px solid var(--border)
|
|
108
|
-
box-shadow 0
|
|
112
|
+
box-shadow 0 8px 24px 0 var(--background)
|
|
113
|
+
|
|
114
|
+
.fixedPresets
|
|
115
|
+
position relative
|
|
116
|
+
z-index 10
|
|
117
|
+
flex-shrink 0
|
|
118
|
+
width 100%
|
|
109
119
|
|
|
110
120
|
.notice
|
|
111
121
|
position absolute
|
|
@@ -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,
|
|
@@ -38,6 +38,7 @@ export function ChatChrome({
|
|
|
38
38
|
scriptContinueLabel,
|
|
39
39
|
onScriptContinue,
|
|
40
40
|
renderMessageChart,
|
|
41
|
+
renderSystemMessage,
|
|
41
42
|
showSyntheticBranchButtons,
|
|
42
43
|
unusedBranchKeys,
|
|
43
44
|
showInlinePresets,
|
|
@@ -56,6 +57,7 @@ export function ChatChrome({
|
|
|
56
57
|
slashCommandItems,
|
|
57
58
|
onSlashItemCommand,
|
|
58
59
|
promptPlaceholder,
|
|
60
|
+
hideChatSelector = false,
|
|
59
61
|
}: ChatChromeProps) {
|
|
60
62
|
const filteredAllowedAttachments = useMemo(
|
|
61
63
|
() => filterToTextAttachments(allowedAttachments),
|
|
@@ -74,6 +76,13 @@ export function ChatChrome({
|
|
|
74
76
|
>([]);
|
|
75
77
|
const [isExtractingAttachments, setIsExtractingAttachments] = useState(false);
|
|
76
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
|
+
);
|
|
77
86
|
|
|
78
87
|
const handleAttachmentFiles = useCallback(
|
|
79
88
|
(files: File[]) => {
|
|
@@ -186,12 +195,12 @@ export function ChatChrome({
|
|
|
186
195
|
scopeId={effectiveScopeId}
|
|
187
196
|
onChatDeleted={onChatDeleted}
|
|
188
197
|
onNewChat={onNewChat}
|
|
198
|
+
hideChatSelector={hideChatSelector}
|
|
189
199
|
>
|
|
190
200
|
{isEmpty ? (
|
|
191
|
-
|
|
201
|
+
<div className={S.emptyBody}>
|
|
192
202
|
<Chat.EmptyState {...emptyState} />
|
|
193
|
-
|
|
194
|
-
</>
|
|
203
|
+
</div>
|
|
195
204
|
) : (
|
|
196
205
|
<div className={S.scrollWrapper}>
|
|
197
206
|
<Scroll
|
|
@@ -199,7 +208,7 @@ export function ChatChrome({
|
|
|
199
208
|
yScrollbarClassName={S.scrollbar}
|
|
200
209
|
className={S.scroll}
|
|
201
210
|
innerClassName={S.scrollInner}
|
|
202
|
-
offset={{ y: { before: 56, after:
|
|
211
|
+
offset={{ y: { before: 56, after: 24 } }}
|
|
203
212
|
fadeSize="m"
|
|
204
213
|
autoHide
|
|
205
214
|
ref={scrollRef}
|
|
@@ -209,6 +218,7 @@ export function ChatChrome({
|
|
|
209
218
|
return (
|
|
210
219
|
<Chat.Message
|
|
211
220
|
key={msg.id}
|
|
221
|
+
message={msg}
|
|
212
222
|
role={msg.role}
|
|
213
223
|
text={msg.text}
|
|
214
224
|
inProgress={msg.inProgress}
|
|
@@ -229,6 +239,7 @@ export function ChatChrome({
|
|
|
229
239
|
: undefined
|
|
230
240
|
}
|
|
231
241
|
renderMessageChart={renderMessageChart}
|
|
242
|
+
renderSystemMessage={renderSystemMessage}
|
|
232
243
|
/>
|
|
233
244
|
);
|
|
234
245
|
})}
|
|
@@ -259,15 +270,21 @@ export function ChatChrome({
|
|
|
259
270
|
|
|
260
271
|
{showInlinePresets && renderPresets('inline')}
|
|
261
272
|
|
|
262
|
-
{isLoading &&
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
273
|
+
{isLoading &&
|
|
274
|
+
!hasInProgressSystemMessage &&
|
|
275
|
+
(isLastMessageFromUser || loadingLabel) && (
|
|
276
|
+
<TextShimmer duration={1} spread={5} className={S.loader}>
|
|
277
|
+
{loadingLabel ?? 'Thinking...'}
|
|
278
|
+
</TextShimmer>
|
|
279
|
+
)}
|
|
267
280
|
</Scroll>
|
|
268
281
|
</div>
|
|
269
282
|
)}
|
|
270
283
|
|
|
284
|
+
{isEmpty ? (
|
|
285
|
+
<div className={S.fixedPresets}>{renderPresets('fixed')}</div>
|
|
286
|
+
) : null}
|
|
287
|
+
|
|
271
288
|
<div className={cn(S.footer, footerClassName)}>
|
|
272
289
|
{isEmpty ? (
|
|
273
290
|
<div className={S.notice}>
|
|
@@ -38,6 +38,8 @@ export interface ChatChromeProps {
|
|
|
38
38
|
scriptContinueLabel: string | undefined;
|
|
39
39
|
onScriptContinue: (() => void) | undefined;
|
|
40
40
|
renderMessageChart?: () => React.ReactNode;
|
|
41
|
+
/** When set, SYSTEM messages render via this callback instead of plain text. */
|
|
42
|
+
renderSystemMessage?: (message: Message) => React.ReactNode;
|
|
41
43
|
showSyntheticBranchButtons: boolean;
|
|
42
44
|
unusedBranchKeys: string[];
|
|
43
45
|
showInlinePresets: boolean;
|
|
@@ -69,4 +71,6 @@ export interface ChatChromeProps {
|
|
|
69
71
|
onSlashItemCommand?: SlashOnItemCommand;
|
|
70
72
|
/** Composer placeholder forwarded to `Chat.Prompt`. */
|
|
71
73
|
promptPlaceholder?: string;
|
|
74
|
+
/** When true, skip built-in header ChatSelector (e.g. external page-header slot). */
|
|
75
|
+
hideChatSelector?: boolean;
|
|
72
76
|
}
|
|
@@ -22,6 +22,8 @@ export function ChatMessage({
|
|
|
22
22
|
scriptContinue,
|
|
23
23
|
onScriptContinue,
|
|
24
24
|
renderMessageChart,
|
|
25
|
+
message,
|
|
26
|
+
renderSystemMessage,
|
|
25
27
|
}: ChatMessageProps) {
|
|
26
28
|
const fileAttachments = userTextFileAttachmentsFromMessage({
|
|
27
29
|
userTextFileAttachments,
|
|
@@ -33,7 +35,13 @@ export function ChatMessage({
|
|
|
33
35
|
<div className={cn(S.root, S[`role-${role}`])}>
|
|
34
36
|
{isSystem ? (
|
|
35
37
|
<div className={S.text}>
|
|
36
|
-
{inProgress ?
|
|
38
|
+
{inProgress ? (
|
|
39
|
+
<TextShimmer as="span">{text}</TextShimmer>
|
|
40
|
+
) : renderSystemMessage && message ? (
|
|
41
|
+
renderSystemMessage(message)
|
|
42
|
+
) : (
|
|
43
|
+
text
|
|
44
|
+
)}
|
|
37
45
|
</div>
|
|
38
46
|
) : isAssistant ? (
|
|
39
47
|
<AgentMessageContent
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.root
|
|
2
|
-
|
|
2
|
+
position relative
|
|
3
|
+
flex-shrink 0
|
|
3
4
|
width 100%
|
|
4
|
-
bottom 160px
|
|
5
5
|
|
|
6
6
|
.inlineRoot
|
|
7
7
|
position relative
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
display flex
|
|
13
13
|
flex-wrap wrap
|
|
14
14
|
gap 8px
|
|
15
|
+
min-width 0
|
|
15
16
|
padding var(--p-2) var(--p-6) var(--p-3)
|
|
16
17
|
background-color var(--background)
|
|
17
18
|
|
|
@@ -19,9 +20,9 @@
|
|
|
19
20
|
background-color transparent
|
|
20
21
|
|
|
21
22
|
.item
|
|
22
|
-
flex
|
|
23
|
+
flex 0 1 auto
|
|
23
24
|
min-width 0
|
|
24
|
-
max-width 300px
|
|
25
|
+
max-width unquote('min(300px, 100%)')
|
|
25
26
|
height auto
|
|
26
27
|
min-height auto
|
|
27
28
|
padding var(--p-3)
|
|
@@ -16,6 +16,7 @@ import S from './ChatSelector.styl';
|
|
|
16
16
|
|
|
17
17
|
export interface ChatSelectorProps {
|
|
18
18
|
id: string;
|
|
19
|
+
wrapperClassName?: string;
|
|
19
20
|
className?: string;
|
|
20
21
|
onChatDeleted?: (sessionId: string) => void;
|
|
21
22
|
/** When set, used for "+ New Chat" instead of the default empty `newChat()`. */
|
|
@@ -24,6 +25,7 @@ export interface ChatSelectorProps {
|
|
|
24
25
|
|
|
25
26
|
export function ChatSelector({
|
|
26
27
|
id,
|
|
28
|
+
wrapperClassName,
|
|
27
29
|
className,
|
|
28
30
|
onChatDeleted,
|
|
29
31
|
onNewChat,
|
|
@@ -65,7 +67,7 @@ export function ChatSelector({
|
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
return (
|
|
68
|
-
<div className={S.wrapper}>
|
|
70
|
+
<div className={cn(S.wrapper, wrapperClassName)}>
|
|
69
71
|
<div className={S.selectGrow}>
|
|
70
72
|
<Select
|
|
71
73
|
variant="clear"
|
|
@@ -44,6 +44,7 @@ export function ChatSheet({
|
|
|
44
44
|
onMessage,
|
|
45
45
|
onScriptComplete,
|
|
46
46
|
renderMessageChart,
|
|
47
|
+
renderSystemMessage,
|
|
47
48
|
emptyState,
|
|
48
49
|
allowedAttachments,
|
|
49
50
|
allowPdfAttachments,
|
|
@@ -60,6 +61,7 @@ export function ChatSheet({
|
|
|
60
61
|
onMessage,
|
|
61
62
|
onScriptComplete,
|
|
62
63
|
renderMessageChart,
|
|
64
|
+
renderSystemMessage,
|
|
63
65
|
emptyState,
|
|
64
66
|
allowedAttachments,
|
|
65
67
|
allowPdfAttachments,
|
|
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import {
|
|
4
4
|
ChatPreset,
|
|
5
5
|
type ChatSendMessagePayload,
|
|
6
|
+
type Message,
|
|
6
7
|
MessageRole,
|
|
7
8
|
type ScriptCompletePayload,
|
|
8
9
|
} from '#uilib/components/ui/Chat/Chat.types';
|
|
@@ -69,11 +70,17 @@ export type UseChatPanelChromeModelInput = {
|
|
|
69
70
|
/** Composite chat scope (e.g. `${userId}-${datasetId}`, `${userId}-dashboard`, `${userId}-report-${reportId}`). */
|
|
70
71
|
scopeId?: string | null;
|
|
71
72
|
/** Fires after send; second arg is the assistant reply when the API call succeeded. */
|
|
72
|
-
onMessage?: (
|
|
73
|
+
onMessage?: (
|
|
74
|
+
displayText: string,
|
|
75
|
+
assistantResponse?: string,
|
|
76
|
+
chatSessionId?: string,
|
|
77
|
+
) => void;
|
|
73
78
|
/** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
|
|
74
79
|
onScriptComplete?: (payload: ScriptCompletePayload) => void;
|
|
75
80
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
76
81
|
renderMessageChart?: () => React.ReactNode;
|
|
82
|
+
/** When set, SYSTEM messages render via this callback instead of plain text. */
|
|
83
|
+
renderSystemMessage?: (message: Message) => React.ReactNode;
|
|
77
84
|
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
78
85
|
emptyState?: ChatEmptyStateConfig;
|
|
79
86
|
/** MIME types / extensions for text-only chat attachments (filtered by uilib allowlist). */
|
|
@@ -144,6 +151,7 @@ export function useChatPanelChromeModel({
|
|
|
144
151
|
onMessage,
|
|
145
152
|
onScriptComplete,
|
|
146
153
|
renderMessageChart,
|
|
154
|
+
renderSystemMessage,
|
|
147
155
|
emptyState,
|
|
148
156
|
allowedAttachments,
|
|
149
157
|
allowPdfAttachments,
|
|
@@ -557,8 +565,13 @@ export function useChatPanelChromeModel({
|
|
|
557
565
|
}
|
|
558
566
|
setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
|
|
559
567
|
try {
|
|
560
|
-
const assistantResponse
|
|
561
|
-
|
|
568
|
+
const { response: assistantResponse, sessionId } =
|
|
569
|
+
await sendMessage(payload);
|
|
570
|
+
onMessage?.(
|
|
571
|
+
displayTextFromSendPayload(payload),
|
|
572
|
+
assistantResponse,
|
|
573
|
+
sessionId,
|
|
574
|
+
);
|
|
562
575
|
} finally {
|
|
563
576
|
setOutboundLoadingLabel(undefined);
|
|
564
577
|
}
|
|
@@ -720,8 +733,13 @@ export function useChatPanelChromeModel({
|
|
|
720
733
|
}
|
|
721
734
|
setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
|
|
722
735
|
try {
|
|
723
|
-
const assistantResponse
|
|
724
|
-
|
|
736
|
+
const { response: assistantResponse, sessionId } =
|
|
737
|
+
await sendMessage(payload);
|
|
738
|
+
onMessage?.(
|
|
739
|
+
displayTextFromSendPayload(payload),
|
|
740
|
+
assistantResponse,
|
|
741
|
+
sessionId,
|
|
742
|
+
);
|
|
725
743
|
} finally {
|
|
726
744
|
setOutboundLoadingLabel(undefined);
|
|
727
745
|
}
|
|
@@ -1245,6 +1263,7 @@ export function useChatPanelChromeModel({
|
|
|
1245
1263
|
scriptContinueLabel,
|
|
1246
1264
|
onScriptContinue,
|
|
1247
1265
|
renderMessageChart,
|
|
1266
|
+
renderSystemMessage,
|
|
1248
1267
|
showSyntheticBranchButtons,
|
|
1249
1268
|
unusedBranchKeys,
|
|
1250
1269
|
showInlinePresets,
|
|
@@ -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
|
}
|
|
@@ -18,6 +18,8 @@ export {
|
|
|
18
18
|
normalizeUserTextFileAttachments,
|
|
19
19
|
} from './buildChatSendMessagePayload';
|
|
20
20
|
export { sanitizeAttachmentFilename } from './sanitizeAttachmentFilename';
|
|
21
|
+
export { ChatSelector } from './ChatSheet/ChatSelector';
|
|
22
|
+
export type { ChatSelectorProps } from './ChatSheet/ChatSelector';
|
|
21
23
|
export { ChatSheet } from './ChatSheet/ChatSheet';
|
|
22
24
|
export { useChatPanelChromeModel } from './ChatSheet/useChatPanelChromeModel';
|
|
23
25
|
export type { ChatSheetActions, ChatSheetProps } from './ChatSheet/ChatSheet';
|
|
@@ -45,6 +47,8 @@ export type {
|
|
|
45
47
|
export type {
|
|
46
48
|
Chat as ChatType,
|
|
47
49
|
ChatAttachmentDropItem,
|
|
50
|
+
ChatMeta,
|
|
51
|
+
ChatMetaValue,
|
|
48
52
|
ChatSendMessagePayload,
|
|
49
53
|
ChatProps,
|
|
50
54
|
ChatPreset as ChatPresetType,
|
|
@@ -3,6 +3,7 @@ import cn from 'classnames';
|
|
|
3
3
|
import { CsvIcon } from '#uilib/components/icons/CsvIcon/CsvIcon';
|
|
4
4
|
import { CardAction, CardHeader } from '#uilib/components/ui/Card';
|
|
5
5
|
import { FileTextIcon, XIcon } from '@phosphor-icons/react';
|
|
6
|
+
import { LayoutDashboard } from 'lucide-react';
|
|
6
7
|
|
|
7
8
|
import S from './FileChip.styl';
|
|
8
9
|
import type { FileChipFormat, FileChipProps } from './FileChip.types';
|
|
@@ -14,6 +15,16 @@ function FormatIcon({ format }: { format: FileChipFormat }) {
|
|
|
14
15
|
return <CsvIcon size={FORMAT_ICON_SIZE} />;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
if (format === 'dashboard') {
|
|
19
|
+
return (
|
|
20
|
+
<LayoutDashboard
|
|
21
|
+
size={FORMAT_ICON_SIZE}
|
|
22
|
+
aria-hidden
|
|
23
|
+
style={{ color: 'var(--muted-foreground)' }}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
return (
|
|
18
29
|
<FileTextIcon
|
|
19
30
|
size={FORMAT_ICON_SIZE}
|
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 =
|