@sybilion/uilib 1.2.19 → 1.2.21
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 +1 -1
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +4 -3
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.js +11 -0
- package/dist/esm/components/ui/Chat/ChatMessage/icons/CsvIcon.js +8 -0
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +12 -18
- package/dist/esm/components/ui/Chat/chat-preset-utils.js +12 -3
- package/dist/esm/contexts/chat-context.js +69 -10
- package/dist/esm/index.js +1 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +14 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/ChatMessage.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.d.ts +4 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/icons/CsvIcon.d.ts +3 -0
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -1
- package/dist/esm/types/src/contexts/chat-context.d.ts +21 -7
- package/dist/esm/types/src/docs/pages/ChatUserCsvAttachmentPage.d.ts +1 -0
- package/dist/esm/types/src/utils/downloadTextFile.d.ts +2 -0
- package/dist/esm/utils/downloadTextFile.js +14 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.ts +16 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +1 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl +67 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl.d.ts +6 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +8 -1
- package/src/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.tsx +36 -0
- package/src/components/ui/Chat/ChatMessage/icons/CsvIcon.tsx +7 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +15 -15
- package/src/components/ui/Chat/chat-preset-utils.ts +12 -6
- package/src/components/ui/Chat/index.ts +3 -1
- package/src/contexts/chat-context.tsx +124 -13
- package/src/docs/pages/ChatUserCsvAttachmentPage.tsx +171 -0
- package/src/docs/registry.ts +6 -0
- package/src/utils/downloadTextFile.ts +16 -0
|
@@ -30,7 +30,7 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
30
30
|
}, [isEmpty, messages.length]);
|
|
31
31
|
return (jsxs("div", { className: S.root, children: [showResizeHandle && resizeHandle ? (jsx(PanelResizeHandle, { edge: "leading", isActive: resizeHandle.isActive, startWidthPx: resizeHandle.startWidthPx, getShellWidth: resizeHandle.getShellWidth, onDragWidth: resizeHandle.onDragWidth, onDragComplete: resizeHandle.onDragComplete, className: cn(SidebarStem.sidebarResizeHandle, S.chatResizeHandle) })) : null, jsx("div", { className: S.panelHeader, children: onClose ? (jsx(Button, { type: "button", variant: "ghost", icon: true, className: S.panelClose, "aria-label": "Close chat", onClick: onClose, children: jsx(X, { className: "size-4" }) })) : null }), jsx("div", { className: S.content, children: jsxs(Chat, { isEmpty: isEmpty, scopeId: effectiveScopeId, onChatDeleted: onChatDeleted, children: [isEmpty ? (jsxs(Fragment, { children: [jsx(Chat.EmptyState, { ...emptyState }), renderPresets('fixed')] })) : (jsx("div", { className: S.scrollWrapper, children: jsxs(Scroll, { y: true, yScrollbarClassName: S.scrollbar, className: S.scroll, innerClassName: S.scrollInner, offset: { y: { before: 56, after: 180 } }, fadeSize: "m", autoHide: true, ref: scrollRef, children: [messages.map((msg, index, arr) => {
|
|
32
32
|
const isLast = index === arr.length - 1;
|
|
33
|
-
return (jsx(Chat.Message, { role: msg.role, text: msg.text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: isLoading, isLastMessage: isLast, scriptContinue: isLast && scriptContinueLabel
|
|
33
|
+
return (jsx(Chat.Message, { role: msg.role, text: msg.text, userCsvAttachment: msg.userCsvAttachment, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: isLoading, isLastMessage: isLast, scriptContinue: isLast && scriptContinueLabel
|
|
34
34
|
? { label: scriptContinueLabel }
|
|
35
35
|
: undefined, onScriptContinue: isLast && scriptContinueLabel
|
|
36
36
|
? onScriptContinue
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import { TextShimmer } from '../../TextShimmer/TextShimmer.js';
|
|
4
4
|
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
|
|
5
5
|
import { AgentMessageContent } from './AgentMessageContent.js';
|
|
6
6
|
import S from './ChatMessage.styl.js';
|
|
7
|
+
import { UserCsvAttachmentBubble } from './UserCsvAttachmentBubble.js';
|
|
7
8
|
|
|
8
|
-
function ChatMessage({ role, text, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, isLastMessage = true, scriptContinue, onScriptContinue, renderMessageChart, }) {
|
|
9
|
+
function ChatMessage({ role, text, userCsvAttachment, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, isLastMessage = true, scriptContinue, onScriptContinue, renderMessageChart, }) {
|
|
9
10
|
const isAssistant = role === MessageRole.ASSISTANT;
|
|
10
11
|
const isSystem = role === MessageRole.SYSTEM;
|
|
11
|
-
return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: text === GENERATING_DASHBOARD_SYSTEM_TEXT ? (jsx(TextShimmer, { as: "span", children: text })) : (text) })) : isAssistant ? (jsx(AgentMessageContent, { text: text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: quickReplyDisabled, isLastMessage: isLastMessage, scriptContinue: scriptContinue, onScriptContinue: onScriptContinue, renderMessageChart: renderMessageChart })) : (jsx("div", { className: S.text, children: text })) }));
|
|
12
|
+
return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: text === GENERATING_DASHBOARD_SYSTEM_TEXT ? (jsx(TextShimmer, { as: "span", children: text })) : (text) })) : isAssistant ? (jsx(AgentMessageContent, { text: text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: quickReplyDisabled, isLastMessage: isLastMessage, scriptContinue: scriptContinue, onScriptContinue: onScriptContinue, renderMessageChart: renderMessageChart })) : (jsxs("div", { className: S.userColumn, children: [jsx("div", { className: S.text, children: text }), userCsvAttachment ? (jsx(UserCsvAttachmentBubble, { attachment: userCsvAttachment })) : null] })) }));
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export { ChatMessage };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = ".ChatMessage_root__6rnsF{background:var(--bg-secondary);display:flex;flex-direction:column;gap:var(--p-1);padding:var(--p-6)}.ChatMessage_text__Y1XNR{color:var(--text-secondary);font-size:var(--text-sm);max-width:100%;-webkit-user-select:text;-moz-user-select:text;user-select:text;width:-moz-fit-content;width:fit-content}.ChatMessage_role-user__u4JPV{align-items:flex-end}.ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-slate-100);border-radius:var(--p-4);border-bottom-right-radius:0;padding:var(--p-3) var(--p-4);white-space:pre-wrap}.dark .ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-gray-800)}.ChatMessage_role-system__g13OP{align-items:center}.ChatMessage_role-system__g13OP .ChatMessage_text__Y1XNR{color:var(--muted-foreground);font-size:var(--text-xs);width:100%}.ChatMessage_role-assistant__wketE .ChatMessage_text__Y1XNR{width:100%}.ChatMessage_role-assistant__wketE h3{line-height:2.4}.ChatMessage_role-assistant__wketE h4{font-size:1.1em;font-weight:600;line-height:2.2}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq{display:inline-block;margin-left:4px;margin-right:6px}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq:before{color:var(--text-secondary);content:\"•\";display:inline-block}.ChatMessage_role-assistant__wketE .ChatMessage_scrollHorizontal__Rms9n{max-width:100%}.ChatMessage_role-assistant__wketE table{border:1px solid var(--border);border-collapse:collapse;border-radius:var(--p-2);border-spacing:0;margin:var(--p-4) 0;overflow:hidden}.ChatMessage_role-assistant__wketE table td,.ChatMessage_role-assistant__wketE table th{border:1px solid var(--border);min-width:100px;padding:var(--p-1)}.ChatMessage_role-assistant__wketE table th{text-align:left}.ChatMessage_role-assistant__wketE ol,.ChatMessage_role-assistant__wketE ul{padding-left:var(--p-4)}.ChatMessage_role-assistant__wketE ul{list-style-type:disc}.ChatMessage_role-assistant__wketE ol{list-style-type:decimal}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{align-items:center;border:1px dashed var(--sb-slate-300);border-radius:8px;color:var(--foreground);display:inline-flex;font-size:var(--text-xs);gap:6px;margin:1px;max-width:100%;padding:2px 6px 2px 4px;text-decoration:none;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-slate-50);border-color:var(--sb-slate-400);border-style:solid}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{border-color:var(--sb-gray-600)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-gray-900)}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLinkLabel__PMU7e{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ChatMessage_role-assistant__wketE .ChatMessage_quickReplyWrap__1UFyD{display:inline-block;margin:var(--p-1) var(--p-1) var(--p-1) 0;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_downloadButtons__RygM-{display:flex;gap:var(--p-2);margin-top:var(--p-4)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{align-items:center;background-color:var(--background);border-radius:var(--p-3);box-shadow:0 0 0 1px var(--border);cursor:pointer;display:flex;gap:var(--p-4);margin-top:var(--p-3);padding:var(--p-3);padding-right:var(--p-4);transition:all .15s;width:-moz-fit-content;width:fit-content}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-50);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{background-color:var(--sb-gray-900);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-800)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardIcon__jkxDJ{align-items:center;border-radius:var(--p-2);display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardContent__PTPwz{display:flex;flex:1;flex-direction:column;min-width:0}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardTitle__K1wqr{font-size:var(--text-base);font-weight:600;line-height:1.4}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardSubtitle__fVeF2{color:var(--muted-foreground);font-size:var(--text-sm);line-height:1.4}";
|
|
4
|
-
var S = {"root":"ChatMessage_root__6rnsF","text":"ChatMessage_text__Y1XNR","role-user":"ChatMessage_role-user__u4JPV","role-system":"ChatMessage_role-system__g13OP","role-assistant":"ChatMessage_role-assistant__wketE","bullet":"ChatMessage_bullet__6vAhq","scrollHorizontal":"ChatMessage_scrollHorizontal__Rms9n","datasetAppLink":"ChatMessage_datasetAppLink__Pxy-T","datasetAppLinkLabel":"ChatMessage_datasetAppLinkLabel__PMU7e","quickReplyWrap":"ChatMessage_quickReplyWrap__1UFyD","downloadButtons":"ChatMessage_downloadButtons__RygM-","downloadCard":"ChatMessage_downloadCard__NsNRa","downloadCardIcon":"ChatMessage_downloadCardIcon__jkxDJ","downloadCardContent":"ChatMessage_downloadCardContent__PTPwz","downloadCardTitle":"ChatMessage_downloadCardTitle__K1wqr","downloadCardSubtitle":"ChatMessage_downloadCardSubtitle__fVeF2"};
|
|
3
|
+
var css_248z = ".ChatMessage_root__6rnsF{background:var(--bg-secondary);display:flex;flex-direction:column;gap:var(--p-1);padding:var(--p-6)}.ChatMessage_text__Y1XNR{color:var(--text-secondary);font-size:var(--text-sm);max-width:100%;-webkit-user-select:text;-moz-user-select:text;user-select:text;width:-moz-fit-content;width:fit-content}.ChatMessage_role-user__u4JPV{align-items:flex-end}.ChatMessage_role-user__u4JPV .ChatMessage_userColumn__cQM6-{align-items:flex-end;display:flex;flex-direction:column;gap:var(--p-2);max-width:100%}.ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-slate-100);border-radius:var(--p-4);border-bottom-right-radius:0;padding:var(--p-3) var(--p-4);white-space:pre-wrap}.dark .ChatMessage_role-user__u4JPV .ChatMessage_text__Y1XNR{background-color:var(--sb-gray-800)}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCard__D1M7y{align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--sb-slate-100);border:0;border-radius:var(--p-4);border-bottom-right-radius:0;box-shadow:0 0 0 1px var(--border);color:var(--sb-green-600);cursor:pointer;display:flex;font:inherit;gap:var(--p-4);margin:0;max-width:100%;padding:var(--p-3);padding-right:var(--p-4);text-align:left;transition:background-color .15s;width:-moz-fit-content;width:fit-content}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCard__D1M7y:hover{background-color:var(--sb-gray-50)}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCard__D1M7y:focus-visible{outline:2px solid var(--ring);outline-offset:2px}.dark .ChatMessage_role-user__u4JPV .ChatMessage_userCsvCard__D1M7y{background-color:var(--sb-gray-800);color:var(--sb-green-400)}.dark .ChatMessage_role-user__u4JPV .ChatMessage_userCsvCard__D1M7y:hover{background-color:var(--sb-gray-900)}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCardIcon__0-KS6{align-items:center;display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCardContent__LoMGE{display:flex;flex:1;flex-direction:column;min-width:0}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCardTitle__9W76E{color:var(--text-secondary);font-size:var(--text-base);font-weight:600;line-height:1.4}.ChatMessage_role-user__u4JPV .ChatMessage_userCsvCardSubtitle__YZeHv{color:var(--muted-foreground);font-size:var(--text-sm);line-height:1.4}.ChatMessage_role-system__g13OP{align-items:center}.ChatMessage_role-system__g13OP .ChatMessage_text__Y1XNR{color:var(--muted-foreground);font-size:var(--text-xs);width:100%}.ChatMessage_role-assistant__wketE .ChatMessage_text__Y1XNR{width:100%}.ChatMessage_role-assistant__wketE h3{line-height:2.4}.ChatMessage_role-assistant__wketE h4{font-size:1.1em;font-weight:600;line-height:2.2}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq{display:inline-block;margin-left:4px;margin-right:6px}.ChatMessage_role-assistant__wketE .ChatMessage_bullet__6vAhq:before{color:var(--text-secondary);content:\"•\";display:inline-block}.ChatMessage_role-assistant__wketE .ChatMessage_scrollHorizontal__Rms9n{max-width:100%}.ChatMessage_role-assistant__wketE table{border:1px solid var(--border);border-collapse:collapse;border-radius:var(--p-2);border-spacing:0;margin:var(--p-4) 0;overflow:hidden}.ChatMessage_role-assistant__wketE table td,.ChatMessage_role-assistant__wketE table th{border:1px solid var(--border);min-width:100px;padding:var(--p-1)}.ChatMessage_role-assistant__wketE table th{text-align:left}.ChatMessage_role-assistant__wketE ol,.ChatMessage_role-assistant__wketE ul{padding-left:var(--p-4)}.ChatMessage_role-assistant__wketE ul{list-style-type:disc}.ChatMessage_role-assistant__wketE ol{list-style-type:decimal}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{align-items:center;border:1px dashed var(--sb-slate-300);border-radius:8px;color:var(--foreground);display:inline-flex;font-size:var(--text-xs);gap:6px;margin:1px;max-width:100%;padding:2px 6px 2px 4px;text-decoration:none;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-slate-50);border-color:var(--sb-slate-400);border-style:solid}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T{border-color:var(--sb-gray-600)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLink__Pxy-T:hover{background-color:var(--sb-gray-900)}.ChatMessage_role-assistant__wketE .ChatMessage_datasetAppLinkLabel__PMU7e{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ChatMessage_role-assistant__wketE .ChatMessage_quickReplyWrap__1UFyD{display:inline-block;margin:var(--p-1) var(--p-1) var(--p-1) 0;vertical-align:middle}.ChatMessage_role-assistant__wketE .ChatMessage_downloadButtons__RygM-{display:flex;gap:var(--p-2);margin-top:var(--p-4)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{align-items:center;background-color:var(--background);border-radius:var(--p-3);box-shadow:0 0 0 1px var(--border);cursor:pointer;display:flex;gap:var(--p-4);margin-top:var(--p-3);padding:var(--p-3);padding-right:var(--p-4);transition:all .15s;width:-moz-fit-content;width:fit-content}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-50);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa{background-color:var(--sb-gray-900);border-color:var(--border)}.dark .ChatMessage_role-assistant__wketE .ChatMessage_downloadCard__NsNRa:hover{background-color:var(--sb-gray-800)}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardIcon__jkxDJ{align-items:center;border-radius:var(--p-2);display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardContent__PTPwz{display:flex;flex:1;flex-direction:column;min-width:0}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardTitle__K1wqr{font-size:var(--text-base);font-weight:600;line-height:1.4}.ChatMessage_role-assistant__wketE .ChatMessage_downloadCardSubtitle__fVeF2{color:var(--muted-foreground);font-size:var(--text-sm);line-height:1.4}";
|
|
4
|
+
var S = {"root":"ChatMessage_root__6rnsF","text":"ChatMessage_text__Y1XNR","role-user":"ChatMessage_role-user__u4JPV","userColumn":"ChatMessage_userColumn__cQM6-","userCsvCard":"ChatMessage_userCsvCard__D1M7y","userCsvCardIcon":"ChatMessage_userCsvCardIcon__0-KS6","userCsvCardContent":"ChatMessage_userCsvCardContent__LoMGE","userCsvCardTitle":"ChatMessage_userCsvCardTitle__9W76E","userCsvCardSubtitle":"ChatMessage_userCsvCardSubtitle__YZeHv","role-system":"ChatMessage_role-system__g13OP","role-assistant":"ChatMessage_role-assistant__wketE","bullet":"ChatMessage_bullet__6vAhq","scrollHorizontal":"ChatMessage_scrollHorizontal__Rms9n","datasetAppLink":"ChatMessage_datasetAppLink__Pxy-T","datasetAppLinkLabel":"ChatMessage_datasetAppLinkLabel__PMU7e","quickReplyWrap":"ChatMessage_quickReplyWrap__1UFyD","downloadButtons":"ChatMessage_downloadButtons__RygM-","downloadCard":"ChatMessage_downloadCard__NsNRa","downloadCardIcon":"ChatMessage_downloadCardIcon__jkxDJ","downloadCardContent":"ChatMessage_downloadCardContent__PTPwz","downloadCardTitle":"ChatMessage_downloadCardTitle__K1wqr","downloadCardSubtitle":"ChatMessage_downloadCardSubtitle__fVeF2"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { downloadTextFile } from '../../../../utils/downloadTextFile.js';
|
|
3
|
+
import { CsvIcon } from './icons/CsvIcon.js';
|
|
4
|
+
import S from './ChatMessage.styl.js';
|
|
5
|
+
|
|
6
|
+
const CSV_DOWNLOAD_HINT = 'Download .CSV file';
|
|
7
|
+
function UserCsvAttachmentBubble({ attachment, }) {
|
|
8
|
+
return (jsxs("button", { type: "button", className: S.userCsvCard, "aria-label": `${CSV_DOWNLOAD_HINT}: ${attachment.displayName}`, onClick: () => downloadTextFile(attachment.content, attachment.filename, 'text/csv;charset=utf-8'), children: [jsx("div", { className: S.userCsvCardIcon, children: jsx(CsvIcon, { size: 32 }) }), jsxs("div", { className: S.userCsvCardContent, children: [jsx("div", { className: S.userCsvCardTitle, children: attachment.displayName }), jsx("div", { className: S.userCsvCardSubtitle, children: CSV_DOWNLOAD_HINT })] })] }));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { UserCsvAttachmentBubble };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FileSpreadsheet } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
function CsvIcon({ size = 32 }) {
|
|
5
|
+
return (jsx(FileSpreadsheet, { size: size, "aria-hidden": true, strokeWidth: 1.75, color: "currentColor" }));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export { CsvIcon };
|
|
@@ -3,7 +3,7 @@ import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
|
3
3
|
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
|
|
4
4
|
import { isGraphIntakeAssistantStepComplete, matchUserTextToQuickReply, parseScriptLine, textHasQuickReplyMarkers, branchKeysUsedFromChatHistory, branchKeysUsedByUserMessages, extractQuickReplyLabelKeyPairsFromText, entryBranchKeyBeforeLastAssistant, isPresetScriptGraph, branchesFromPresetScriptGraph } from '../ChatMessage/presetScript.js';
|
|
5
5
|
import { usedPresetIdsFromMessages, formatChatTranscript } from '../chat-preset-utils.js';
|
|
6
|
-
import { useChatsForScopeId, useChat, isChatEmpty } from '../../../../contexts/chat-context.js';
|
|
6
|
+
import { useChatsForScopeId, useChat, useChatOutboundPending, isChatEmpty } from '../../../../contexts/chat-context.js';
|
|
7
7
|
import useEvent from '../../../../hooks/useEvent.js';
|
|
8
8
|
import { useIsMobile } from '../../../../hooks/useIsMobile.js';
|
|
9
9
|
import { useQueryParams } from '../../../../hooks/useQueryParams.js';
|
|
@@ -25,11 +25,13 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
25
25
|
const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
|
|
26
26
|
const isMobile = useIsMobile();
|
|
27
27
|
const { chatPanelContainer, isOpen: sidebarNavOpen, setOpen: setSidebarNavOpen, chatWidthPx, setChatWidthPx, getShellWidth, setChatPanelOpen, } = useSidebar();
|
|
28
|
+
const [localUiBusy, setLocalUiBusy] = useState(false);
|
|
28
29
|
const { chats, currentChatId, setCurrentChatId, newChat, sendMessage, addMessage, removeMessageById, } = useChatsForScopeId(effectiveScopeId);
|
|
29
30
|
const chat = useChat(effectiveScopeId, currentChatId);
|
|
31
|
+
const isOutboundPending = useChatOutboundPending(effectiveScopeId, currentChatId);
|
|
32
|
+
const isLoading = isOutboundPending || localUiBusy;
|
|
30
33
|
const { searchParams, addSearchParams, removeSearchParams, mutateSearchParams, } = useQueryParams();
|
|
31
34
|
const chatOpen = searchParams.has(CHAT_QUERY_PARAM);
|
|
32
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
33
35
|
const [isOpen, setIsOpen] = useState(false);
|
|
34
36
|
/** `?prompt=` deep link text for one-shot composer pre-fill (see ChatPrompt). */
|
|
35
37
|
const [promptLinkPrefill, setPromptLinkPrefill] = useState(null);
|
|
@@ -215,7 +217,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
215
217
|
if (quickReplyLockRef.current)
|
|
216
218
|
return;
|
|
217
219
|
quickReplyLockRef.current = true;
|
|
218
|
-
|
|
220
|
+
setLocalUiBusy(true);
|
|
219
221
|
setUsedScriptBranchKeysByChat(prev => ({
|
|
220
222
|
...prev,
|
|
221
223
|
[chatId]: [...new Set([...(prev[chatId] || []), branchKey])],
|
|
@@ -266,7 +268,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
266
268
|
logger.error('Error resolving preset quick reply:', error);
|
|
267
269
|
}
|
|
268
270
|
finally {
|
|
269
|
-
|
|
271
|
+
setLocalUiBusy(false);
|
|
270
272
|
quickReplyLockRef.current = false;
|
|
271
273
|
}
|
|
272
274
|
})();
|
|
@@ -274,7 +276,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
274
276
|
}
|
|
275
277
|
endLocalDemoFlow(chatId);
|
|
276
278
|
void (async () => {
|
|
277
|
-
setIsLoading(true);
|
|
278
279
|
try {
|
|
279
280
|
await sendMessage(displayLabel);
|
|
280
281
|
onMessage?.(displayLabel);
|
|
@@ -282,9 +283,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
282
283
|
catch (error) {
|
|
283
284
|
logger.error('Error sending chat message:', error);
|
|
284
285
|
}
|
|
285
|
-
finally {
|
|
286
|
-
setIsLoading(false);
|
|
287
|
-
}
|
|
288
286
|
})();
|
|
289
287
|
}, [
|
|
290
288
|
currentChatId,
|
|
@@ -337,7 +335,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
337
335
|
if (quickReplyLockRef.current)
|
|
338
336
|
return;
|
|
339
337
|
quickReplyLockRef.current = true;
|
|
340
|
-
|
|
338
|
+
setLocalUiBusy(true);
|
|
341
339
|
const newAnswers = {
|
|
342
340
|
...intake.answers,
|
|
343
341
|
[intake.scriptStepId]: message,
|
|
@@ -381,7 +379,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
381
379
|
logger.error('Error advancing freeform preset script:', e);
|
|
382
380
|
}
|
|
383
381
|
finally {
|
|
384
|
-
|
|
382
|
+
setLocalUiBusy(false);
|
|
385
383
|
quickReplyLockRef.current = false;
|
|
386
384
|
}
|
|
387
385
|
})();
|
|
@@ -401,7 +399,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
401
399
|
}
|
|
402
400
|
}
|
|
403
401
|
}
|
|
404
|
-
setIsLoading(true);
|
|
405
402
|
try {
|
|
406
403
|
if (chatId)
|
|
407
404
|
endLocalDemoFlow(chatId);
|
|
@@ -411,9 +408,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
411
408
|
catch (error) {
|
|
412
409
|
logger.error('Error sending chat message:', error);
|
|
413
410
|
}
|
|
414
|
-
finally {
|
|
415
|
-
setIsLoading(false);
|
|
416
|
-
}
|
|
417
411
|
}, [
|
|
418
412
|
currentChatId,
|
|
419
413
|
chat?.messages,
|
|
@@ -443,7 +437,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
443
437
|
await handlePromptSubmit(preset.text);
|
|
444
438
|
return;
|
|
445
439
|
}
|
|
446
|
-
|
|
440
|
+
setLocalUiBusy(true);
|
|
447
441
|
try {
|
|
448
442
|
if (!currentChatId)
|
|
449
443
|
return;
|
|
@@ -518,7 +512,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
518
512
|
logger.error('Error sending chat message:', error);
|
|
519
513
|
}
|
|
520
514
|
finally {
|
|
521
|
-
|
|
515
|
+
setLocalUiBusy(false);
|
|
522
516
|
}
|
|
523
517
|
};
|
|
524
518
|
const activeScript = currentChatId
|
|
@@ -550,7 +544,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
550
544
|
return;
|
|
551
545
|
const chatId = currentChatId;
|
|
552
546
|
scriptAdvanceLockRef.current = true;
|
|
553
|
-
|
|
547
|
+
setLocalUiBusy(true);
|
|
554
548
|
addMessage(chatId, MessageRole.USER, step.buttonLabel);
|
|
555
549
|
void (async () => {
|
|
556
550
|
try {
|
|
@@ -593,7 +587,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
593
587
|
logger.error('Error advancing preset script:', error);
|
|
594
588
|
}
|
|
595
589
|
finally {
|
|
596
|
-
|
|
590
|
+
setLocalUiBusy(false);
|
|
597
591
|
scriptAdvanceLockRef.current = false;
|
|
598
592
|
}
|
|
599
593
|
})();
|
|
@@ -8,14 +8,23 @@ function formatChatTranscript(messages) {
|
|
|
8
8
|
function normalizePresetMatchText(s) {
|
|
9
9
|
return s.trim().normalize('NFC');
|
|
10
10
|
}
|
|
11
|
+
function presetMatchesUserText(presetText, userTextNorm) {
|
|
12
|
+
const presetNorm = normalizePresetMatchText(presetText);
|
|
13
|
+
if (!presetNorm)
|
|
14
|
+
return false;
|
|
15
|
+
if (userTextNorm === presetNorm)
|
|
16
|
+
return true;
|
|
17
|
+
const prefix = `${presetNorm} `;
|
|
18
|
+
return userTextNorm.startsWith(prefix);
|
|
19
|
+
}
|
|
11
20
|
function usedPresetIdsFromMessages(messages, presets) {
|
|
12
21
|
if (!messages?.length || !presets?.length)
|
|
13
22
|
return [];
|
|
14
|
-
const userTexts =
|
|
23
|
+
const userTexts = messages
|
|
15
24
|
.filter(m => m.role === MessageRole.USER)
|
|
16
|
-
.map(m => normalizePresetMatchText(m.text))
|
|
25
|
+
.map(m => normalizePresetMatchText(m.text));
|
|
17
26
|
return presets
|
|
18
|
-
.filter(p => userTexts.
|
|
27
|
+
.filter(p => userTexts.some(ut => presetMatchesUserText(p.text, ut)))
|
|
19
28
|
.map(p => p.id);
|
|
20
29
|
}
|
|
21
30
|
|
|
@@ -7,6 +7,10 @@ import { LS } from '@homecode/ui';
|
|
|
7
7
|
const CHATS_PREFIX = 'chats-';
|
|
8
8
|
const CHAT_SCOPE_IDS_REGISTRY_KEY = 'chat-scope-ids';
|
|
9
9
|
const ChatContext = createContext(undefined);
|
|
10
|
+
/** Stable composite key; avoids collisions if `scopeId` contains `:`. */
|
|
11
|
+
function outboundPendingKey(scopeId, chatSessionId) {
|
|
12
|
+
return `${scopeId}\0${chatSessionId}`;
|
|
13
|
+
}
|
|
10
14
|
function getCurrentChatIdKey(scopeId) {
|
|
11
15
|
return `chat-current-id-${scopeId}`;
|
|
12
16
|
}
|
|
@@ -86,6 +90,26 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
86
90
|
return {};
|
|
87
91
|
return loadChatsFromLS(userSwitchKey).currentChatId;
|
|
88
92
|
});
|
|
93
|
+
const [outboundPendingByKey, setOutboundPendingByKey] = useState({});
|
|
94
|
+
const beginOutboundPending = useCallback((scopeId, chatSessionId) => {
|
|
95
|
+
const key = outboundPendingKey(scopeId, chatSessionId);
|
|
96
|
+
setOutboundPendingByKey(prev => ({
|
|
97
|
+
...prev,
|
|
98
|
+
[key]: (prev[key] ?? 0) + 1,
|
|
99
|
+
}));
|
|
100
|
+
}, []);
|
|
101
|
+
const endOutboundPending = useCallback((scopeId, chatSessionId) => {
|
|
102
|
+
const key = outboundPendingKey(scopeId, chatSessionId);
|
|
103
|
+
setOutboundPendingByKey(prev => {
|
|
104
|
+
const next = { ...prev };
|
|
105
|
+
const n = (next[key] ?? 0) - 1;
|
|
106
|
+
if (n <= 0)
|
|
107
|
+
delete next[key];
|
|
108
|
+
else
|
|
109
|
+
next[key] = n;
|
|
110
|
+
return next;
|
|
111
|
+
});
|
|
112
|
+
}, []);
|
|
89
113
|
const getChatsForScopeId = useCallback((scopeId) => chats[scopeId] ?? [], [chats]);
|
|
90
114
|
const getCurrentChatId = useCallback((scopeId) => {
|
|
91
115
|
const v = currentChatId[scopeId];
|
|
@@ -152,16 +176,18 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
152
176
|
return { ...prev, [scopeId]: updatedChats };
|
|
153
177
|
});
|
|
154
178
|
}, []);
|
|
155
|
-
const addMessage = useCallback((scopeId, chatId, role, text) => {
|
|
179
|
+
const addMessage = useCallback((scopeId, chatId, role, text, options) => {
|
|
156
180
|
if (userSwitchKey === null)
|
|
157
181
|
return undefined;
|
|
158
182
|
addScopeIdToRegistry(scopeId);
|
|
159
183
|
const storedText = stripJsonDashboardFences(text);
|
|
184
|
+
const attachment = role === MessageRole.USER ? options?.userCsvAttachment : undefined;
|
|
160
185
|
const newMessage = {
|
|
161
186
|
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
162
187
|
role,
|
|
163
188
|
text: storedText,
|
|
164
189
|
timestamp: Date.now(),
|
|
190
|
+
...(attachment ? { userCsvAttachment: attachment } : {}),
|
|
165
191
|
};
|
|
166
192
|
setChats(prev => {
|
|
167
193
|
const scopeChats = prev[scopeId] ?? [];
|
|
@@ -200,13 +226,23 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
200
226
|
if (targetChatId === null || targetChatId === '') {
|
|
201
227
|
throw new Error('No chat selected');
|
|
202
228
|
}
|
|
203
|
-
|
|
229
|
+
const apiPayload = typeof message === 'string' ? message : message.apiMessage;
|
|
230
|
+
if (typeof message === 'string') {
|
|
231
|
+
addMessage(scopeId, targetChatId, MessageRole.USER, message);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
addMessage(scopeId, targetChatId, MessageRole.USER, message.displayText, {
|
|
235
|
+
userCsvAttachment: message.userCsvAttachment,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const pendingChatSessionId = targetChatId;
|
|
239
|
+
beginOutboundPending(scopeId, pendingChatSessionId);
|
|
204
240
|
try {
|
|
205
|
-
const data = await sendChatMessageFn(
|
|
206
|
-
if (data.session_id && data.session_id !==
|
|
241
|
+
const data = await sendChatMessageFn(apiPayload, pendingChatSessionId);
|
|
242
|
+
if (data.session_id && data.session_id !== pendingChatSessionId) {
|
|
207
243
|
setChats(prev => {
|
|
208
244
|
const scopeChats = prev[scopeId] ?? [];
|
|
209
|
-
const updatedChats = scopeChats.map(chat => chat.session_id ===
|
|
245
|
+
const updatedChats = scopeChats.map(chat => chat.session_id === pendingChatSessionId
|
|
210
246
|
? { ...chat, session_id: data.session_id }
|
|
211
247
|
: chat);
|
|
212
248
|
const chatsKey = getChatsKey(scopeId);
|
|
@@ -215,21 +251,32 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
215
251
|
});
|
|
216
252
|
setCurrentChatId(scopeId, data.session_id);
|
|
217
253
|
}
|
|
218
|
-
addMessage(scopeId, data.session_id ? data.session_id :
|
|
254
|
+
addMessage(scopeId, data.session_id ? data.session_id : pendingChatSessionId, MessageRole.ASSISTANT, data.response);
|
|
219
255
|
return data.response;
|
|
220
256
|
}
|
|
221
257
|
catch (error) {
|
|
222
258
|
const errorMessage = error instanceof Error
|
|
223
259
|
? error.message
|
|
224
260
|
: 'Sorry, I encountered an error processing your message. Please try again.';
|
|
225
|
-
addMessage(scopeId,
|
|
261
|
+
addMessage(scopeId, pendingChatSessionId, MessageRole.ASSISTANT, errorMessage);
|
|
226
262
|
throw error;
|
|
227
263
|
}
|
|
228
|
-
|
|
264
|
+
finally {
|
|
265
|
+
endOutboundPending(scopeId, pendingChatSessionId);
|
|
266
|
+
}
|
|
267
|
+
}, [
|
|
268
|
+
addMessage,
|
|
269
|
+
beginOutboundPending,
|
|
270
|
+
endOutboundPending,
|
|
271
|
+
getCurrentChatId,
|
|
272
|
+
sendChatMessageFn,
|
|
273
|
+
setCurrentChatId,
|
|
274
|
+
]);
|
|
229
275
|
useEffect(() => {
|
|
230
276
|
if (userSwitchKey === null) {
|
|
231
277
|
setChats({});
|
|
232
278
|
setCurrentChatIdState({});
|
|
279
|
+
setOutboundPendingByKey({});
|
|
233
280
|
return;
|
|
234
281
|
}
|
|
235
282
|
const loaded = loadChatsFromLS(userSwitchKey);
|
|
@@ -253,6 +300,7 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
|
|
|
253
300
|
getChatsForScopeId,
|
|
254
301
|
getCurrentChatId,
|
|
255
302
|
deleteChat,
|
|
303
|
+
outboundPendingByKey,
|
|
256
304
|
}, children: children }));
|
|
257
305
|
}
|
|
258
306
|
const isChatEmpty = (chat) => chat?.messages.length === 0;
|
|
@@ -272,18 +320,29 @@ function useChat(scopeId, chatId) {
|
|
|
272
320
|
null);
|
|
273
321
|
}, [scopeId, chatId, getChatsForScopeId]);
|
|
274
322
|
}
|
|
323
|
+
function useChatOutboundPending(scopeId, chatSessionId) {
|
|
324
|
+
const { outboundPendingByKey } = useChats();
|
|
325
|
+
return useMemo(() => {
|
|
326
|
+
if (!scopeId || !chatSessionId)
|
|
327
|
+
return false;
|
|
328
|
+
const key = outboundPendingKey(scopeId, chatSessionId);
|
|
329
|
+
return (outboundPendingByKey[key] ?? 0) > 0;
|
|
330
|
+
}, [scopeId, chatSessionId, outboundPendingByKey]);
|
|
331
|
+
}
|
|
275
332
|
function useChatsForScopeId(scopeId) {
|
|
276
333
|
const { getChatsForScopeId, getCurrentChatId, setCurrentChatId, newChat, addMessage, removeMessageById, sendMessage, deleteChat, } = useChats();
|
|
277
334
|
const chats = getChatsForScopeId(scopeId);
|
|
278
335
|
const currentChatId = getCurrentChatId(scopeId);
|
|
279
336
|
const currentChat = useChat(scopeId, currentChatId ?? undefined);
|
|
337
|
+
const isOutboundPending = useChatOutboundPending(scopeId, currentChatId);
|
|
280
338
|
return {
|
|
281
339
|
chats,
|
|
282
340
|
currentChat,
|
|
283
341
|
currentChatId,
|
|
342
|
+
isOutboundPending,
|
|
284
343
|
setCurrentChatId: (targetId) => setCurrentChatId(scopeId, targetId),
|
|
285
344
|
newChat: () => newChat(scopeId),
|
|
286
|
-
addMessage: (chatId, role, text) => addMessage(scopeId, chatId, role, text),
|
|
345
|
+
addMessage: (chatId, role, text, options) => addMessage(scopeId, chatId, role, text, options),
|
|
287
346
|
removeMessageById: (chatId, messageId) => removeMessageById(scopeId, chatId, messageId),
|
|
288
347
|
sendMessage: (message, chatId) => sendMessage(scopeId, message, chatId),
|
|
289
348
|
deleteChat: (sessionId) => deleteChat(scopeId, sessionId),
|
|
@@ -299,4 +358,4 @@ function useCurrentChat(scopeId) {
|
|
|
299
358
|
return useChat(scopeId, chatId ?? undefined);
|
|
300
359
|
}
|
|
301
360
|
|
|
302
|
-
export { ChatContext, ChatProvider, isChatEmpty, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat };
|
|
361
|
+
export { ChatContext, ChatProvider, isChatEmpty, outboundPendingKey, useChat, useChatOutboundPending, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat };
|
package/dist/esm/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { DEFAULT_THEME_ACTIVE_COLOR } from './docs/lib/theme.js';
|
|
|
4
4
|
export { SybilionAuthProvider, createSybilionApiFetch, getSybilionApiOriginFromSdk, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth } from './sybilion-auth/SybilionAuthProvider.js';
|
|
5
5
|
export { SYBILION_AUTH_LOGIN_PATH, normalizeApiBaseUrl } from './sybilion-auth/authPaths.js';
|
|
6
6
|
export { exchangeAuth0AccessTokenForSybilionJwt } from './sybilion-auth/exchangeSybilionToken.js';
|
|
7
|
-
export { ChatContext, ChatProvider, isChatEmpty, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
|
|
7
|
+
export { ChatContext, ChatProvider, isChatEmpty, outboundPendingKey, useChat, useChatOutboundPending, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
|
|
8
8
|
export { AnalysesSelector } from './components/ui/AnalysesSelector/AnalysesSelector.js';
|
|
9
9
|
export { AnalysisLineIcon } from './components/ui/AnalysisLineIcon/AnalysisLineIcon.js';
|
|
10
10
|
export { AppHeaderHost, AppHeaderPortal } from './components/ui/AppHeader/AppHeader.js';
|
|
@@ -7,11 +7,24 @@ export declare enum MessageRole {
|
|
|
7
7
|
}
|
|
8
8
|
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
9
9
|
export declare const GENERATING_DASHBOARD_SYSTEM_TEXT = "Generating dashboard\u2026";
|
|
10
|
+
/** USER-only: sample CSV attached to a preset message; shown as a file row + client-side download. */
|
|
11
|
+
export type UserCsvAttachment = {
|
|
12
|
+
displayName: string;
|
|
13
|
+
filename: string;
|
|
14
|
+
content: string;
|
|
15
|
+
};
|
|
16
|
+
/** Send full text to the chat API while showing `displayText` + optional CSV attachment in the UI. */
|
|
17
|
+
export type ChatSendMessagePayload = {
|
|
18
|
+
apiMessage: string;
|
|
19
|
+
displayText: string;
|
|
20
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
21
|
+
};
|
|
10
22
|
export interface Message {
|
|
11
23
|
id: string;
|
|
12
24
|
role: MessageRole;
|
|
13
25
|
text: string;
|
|
14
26
|
timestamp: number;
|
|
27
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
15
28
|
}
|
|
16
29
|
export interface Chat {
|
|
17
30
|
session_id: string;
|
|
@@ -57,6 +70,7 @@ export interface ChatPromptProps {
|
|
|
57
70
|
export interface ChatMessageProps {
|
|
58
71
|
role: MessageRole;
|
|
59
72
|
text: string;
|
|
73
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
60
74
|
onQuickReply?: (branchKey: string, displayLabel: string) => void;
|
|
61
75
|
/** Branch keys already taken (e.g. from chat history); hide quick-reply buttons for these. */
|
|
62
76
|
suppressedQuickReplyKeys?: ReadonlySet<string>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type ChatMessageProps } from '../Chat.types';
|
|
2
|
-
export declare function ChatMessage({ role, text, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, isLastMessage, scriptContinue, onScriptContinue, renderMessageChart, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ChatMessage({ role, text, userCsvAttachment, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, isLastMessage, scriptContinue, onScriptContinue, renderMessageChart, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,5 +9,5 @@ export type { UseChatPanelChromeModelInput, UseChatPanelChromeModelResult, } fro
|
|
|
9
9
|
export { ChatMessage } from './ChatMessage';
|
|
10
10
|
export { ChatPrompt } from './ChatPrompt';
|
|
11
11
|
export { ChatPresets } from './ChatPresets';
|
|
12
|
-
export type { Chat as ChatType,
|
|
12
|
+
export type { Chat as ChatType, ChatSendMessagePayload, ChatProps, ChatPreset as ChatPresetType, Message, UserCsvAttachment, } from './Chat.types';
|
|
13
13
|
export { MessageRole } from './Chat.types';
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { type Chat, MessageRole } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
|
+
import { type Chat, type ChatSendMessagePayload, MessageRole, type UserCsvAttachment } 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
|
+
export type { ChatSendMessagePayload, UserCsvAttachment, } from '#uilib/components/ui/Chat/Chat.types';
|
|
6
|
+
export type AddChatMessageOptions = {
|
|
7
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
8
|
+
};
|
|
5
9
|
export interface ChatContextType {
|
|
6
10
|
/** Returns the new session id, or undefined if no user / not created. */
|
|
7
11
|
newChat: (scopeId: string) => string | undefined;
|
|
8
12
|
setCurrentChatId: (currScopeId: string, sessionId: string) => void;
|
|
9
|
-
addMessage: (scopeId: string, chatId: string, role: MessageRole, text: string) => string | undefined;
|
|
13
|
+
addMessage: (scopeId: string, chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string | undefined;
|
|
10
14
|
removeMessageById: (scopeId: string, chatId: string, messageId: string) => void;
|
|
11
|
-
sendMessage: (scopeId: string, message: string, chatId?: string) => Promise<string>;
|
|
15
|
+
sendMessage: (scopeId: string, message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
|
|
12
16
|
getChatsForScopeId: (scopeId: string) => Chat[];
|
|
13
17
|
getCurrentChatId: (scopeId: string) => string | null;
|
|
14
18
|
deleteChat: (scopeId: string, sessionId: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Ref-count of in-flight `sendChatMessage` requests keyed by
|
|
21
|
+
* `outboundPendingKey(scopeId, chatSessionId)` (session id at send start).
|
|
22
|
+
*/
|
|
23
|
+
outboundPendingByKey: Readonly<Record<string, number>>;
|
|
15
24
|
}
|
|
16
25
|
declare const ChatContext: import("react").Context<ChatContextType>;
|
|
26
|
+
/** Stable composite key; avoids collisions if `scopeId` contains `:`. */
|
|
27
|
+
export declare function outboundPendingKey(scopeId: string, chatSessionId: string): string;
|
|
17
28
|
export interface ChatProviderProps {
|
|
18
29
|
children: ReactNode;
|
|
19
30
|
/** When null, chat state is cleared (logged out). When set, only LS rows for scopes starting with `${userId}-` are loaded. */
|
|
@@ -24,15 +35,17 @@ export declare function ChatProvider({ children, userSwitchKey, sendChatMessage:
|
|
|
24
35
|
export declare const isChatEmpty: (chat: Chat | null) => boolean;
|
|
25
36
|
export declare function useChats(): ChatContextType;
|
|
26
37
|
export declare function useChat(scopeId: string | undefined, chatId: string | undefined): Chat | null;
|
|
38
|
+
export declare function useChatOutboundPending(scopeId: string | undefined | null, chatSessionId: string | null | undefined): boolean;
|
|
27
39
|
export declare function useChatsForScopeId(scopeId: string): {
|
|
28
40
|
chats: Chat[];
|
|
29
41
|
currentChat: Chat;
|
|
30
42
|
currentChatId: string;
|
|
43
|
+
isOutboundPending: boolean;
|
|
31
44
|
setCurrentChatId: (targetId: string) => void;
|
|
32
45
|
newChat: () => string;
|
|
33
|
-
addMessage: (chatId: string, role: MessageRole, text: string) => string;
|
|
46
|
+
addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
|
|
34
47
|
removeMessageById: (chatId: string, messageId: string) => void;
|
|
35
|
-
sendMessage: (message: string, chatId?: string) => Promise<string>;
|
|
48
|
+
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
|
|
36
49
|
deleteChat: (sessionId: string) => void;
|
|
37
50
|
};
|
|
38
51
|
/** @deprecated Use useChatsForScopeId */
|
|
@@ -40,11 +53,12 @@ export declare function useChatsForDataset(scopeId: string): {
|
|
|
40
53
|
chats: Chat[];
|
|
41
54
|
currentChat: Chat;
|
|
42
55
|
currentChatId: string;
|
|
56
|
+
isOutboundPending: boolean;
|
|
43
57
|
setCurrentChatId: (targetId: string) => void;
|
|
44
58
|
newChat: () => string;
|
|
45
|
-
addMessage: (chatId: string, role: MessageRole, text: string) => string;
|
|
59
|
+
addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
|
|
46
60
|
removeMessageById: (chatId: string, messageId: string) => void;
|
|
47
|
-
sendMessage: (message: string, chatId?: string) => Promise<string>;
|
|
61
|
+
sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
|
|
48
62
|
deleteChat: (sessionId: string) => void;
|
|
49
63
|
};
|
|
50
64
|
export declare function useCurrentChat(scopeId: string): Chat;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function ChatUserCsvAttachmentPage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Trigger a browser download for text/binary content (no backend). */
|
|
2
|
+
function downloadTextFile(content, filename, mimeType) {
|
|
3
|
+
const blob = new Blob([content], { type: mimeType });
|
|
4
|
+
const url = URL.createObjectURL(blob);
|
|
5
|
+
const link = document.createElement('a');
|
|
6
|
+
link.href = url;
|
|
7
|
+
link.download = filename;
|
|
8
|
+
document.body.appendChild(link);
|
|
9
|
+
link.click();
|
|
10
|
+
document.body.removeChild(link);
|
|
11
|
+
URL.revokeObjectURL(url);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { downloadTextFile };
|
package/package.json
CHANGED
|
@@ -11,11 +11,26 @@ export enum MessageRole {
|
|
|
11
11
|
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
12
12
|
export const GENERATING_DASHBOARD_SYSTEM_TEXT = 'Generating dashboard…';
|
|
13
13
|
|
|
14
|
+
/** USER-only: sample CSV attached to a preset message; shown as a file row + client-side download. */
|
|
15
|
+
export type UserCsvAttachment = {
|
|
16
|
+
displayName: string;
|
|
17
|
+
filename: string;
|
|
18
|
+
content: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Send full text to the chat API while showing `displayText` + optional CSV attachment in the UI. */
|
|
22
|
+
export type ChatSendMessagePayload = {
|
|
23
|
+
apiMessage: string;
|
|
24
|
+
displayText: string;
|
|
25
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
26
|
+
};
|
|
27
|
+
|
|
14
28
|
export interface Message {
|
|
15
29
|
id: string;
|
|
16
30
|
role: MessageRole;
|
|
17
31
|
text: string;
|
|
18
32
|
timestamp: number;
|
|
33
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
export interface Chat {
|
|
@@ -66,6 +81,7 @@ export interface ChatPromptProps {
|
|
|
66
81
|
export interface ChatMessageProps {
|
|
67
82
|
role: MessageRole;
|
|
68
83
|
text: string;
|
|
84
|
+
userCsvAttachment?: UserCsvAttachment;
|
|
69
85
|
onQuickReply?: (branchKey: string, displayLabel: string) => void;
|
|
70
86
|
/** Branch keys already taken (e.g. from chat history); hide quick-reply buttons for these. */
|
|
71
87
|
suppressedQuickReplyKeys?: ReadonlySet<string>;
|
|
@@ -120,6 +120,7 @@ export function ChatChrome({
|
|
|
120
120
|
key={msg.id}
|
|
121
121
|
role={msg.role}
|
|
122
122
|
text={msg.text}
|
|
123
|
+
userCsvAttachment={msg.userCsvAttachment}
|
|
123
124
|
onQuickReply={onQuickReply}
|
|
124
125
|
suppressedQuickReplyKeys={suppressedQuickReplyKeys}
|
|
125
126
|
quickReplyDisabled={isLoading}
|