@sybilion/uilib 1.3.10 → 1.3.11
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 +52 -17
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.js +3 -4
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +6 -4
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPromptAttachments.js +11 -0
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.js +1 -1
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +4 -1
- package/dist/esm/components/ui/Chat/chatAttachmentAccept.js +54 -0
- package/dist/esm/components/ui/Chat/chatAttachmentExtract.js +26 -0
- package/dist/esm/components/ui/Chat/chatPdfExtract.js +31 -0
- package/dist/esm/components/ui/DropZone/DropZone.js +50 -21
- package/dist/esm/components/ui/DropZone/DropZone.styl.js +2 -2
- package/dist/esm/components/ui/FileChip/FileChip.js +26 -0
- package/dist/esm/components/ui/FileChip/FileChip.styl.js +7 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +10 -1
- 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 +9 -2
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/index.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptAttachments.d.ts +8 -0
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +7 -1
- package/dist/esm/types/src/components/ui/Chat/chatAttachmentAccept.d.ts +8 -0
- package/dist/esm/types/src/components/ui/Chat/chatAttachmentExtract.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Chat/chatPdfExtract.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +2 -1
- package/dist/esm/types/src/components/ui/DropZone/DropZone.d.ts +2 -0
- package/dist/esm/types/src/components/ui/FileChip/FileChip.d.ts +2 -0
- package/dist/esm/types/src/components/ui/FileChip/FileChip.types.d.ts +10 -0
- package/dist/esm/types/src/components/ui/FileChip/index.d.ts +2 -0
- package/dist/esm/types/src/docs/pages/ChatAttachmentsDropzonePage.d.ts +1 -0
- package/dist/esm/types/src/docs/pages/FileChipPage.d.ts +1 -0
- package/dist/esm/types/src/index.d.ts +1 -0
- package/package.json +2 -1
- package/src/components/ui/Chat/Chat.types.ts +11 -1
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl +20 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.styl.d.ts +2 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +88 -4
- package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +18 -2
- package/src/components/ui/Chat/ChatChrome/index.ts +1 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl +0 -56
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl.d.ts +0 -5
- package/src/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.tsx +6 -15
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +11 -15
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl.d.ts +2 -1
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +17 -8
- package/src/components/ui/Chat/ChatPrompt/ChatPromptAttachments.tsx +34 -0
- package/src/components/ui/Chat/ChatSheet/ChatSelector.tsx +12 -11
- package/src/components/ui/Chat/ChatSheet/ChatSheet.styl.d.ts +13 -13
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +14 -0
- package/src/components/ui/Chat/chat-preset-utils.ts +4 -1
- package/src/components/ui/Chat/chatAttachmentAccept.ts +70 -0
- package/src/components/ui/Chat/chatAttachmentExtract.ts +33 -0
- package/src/components/ui/Chat/chatPdfExtract.ts +37 -0
- package/src/components/ui/Chat/index.ts +5 -0
- package/src/components/ui/DropZone/DropZone.styl +24 -0
- package/src/components/ui/DropZone/DropZone.styl.d.ts +3 -0
- package/src/components/ui/DropZone/DropZone.tsx +77 -24
- package/src/components/ui/FileChip/FileChip.styl +108 -0
- package/src/components/ui/FileChip/FileChip.styl.d.ts +12 -0
- package/src/components/ui/FileChip/FileChip.tsx +93 -0
- package/src/components/ui/FileChip/FileChip.types.ts +11 -0
- package/src/components/ui/FileChip/index.ts +2 -0
- package/src/docs/pages/ChatAttachmentsDropzonePage.tsx +162 -0
- package/src/docs/pages/FileChipPage.tsx +50 -0
- package/src/docs/registry.ts +12 -0
- package/src/index.ts +1 -0
|
@@ -1,17 +1,52 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
-
import { useEffect } from 'react';
|
|
3
|
+
import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
4
4
|
import { displayLabelForBranchKeyFromMessages, humanizeBranchKey } from '../ChatMessage/presetScript.js';
|
|
5
5
|
import { TextShimmer } from '../../TextShimmer/TextShimmer.js';
|
|
6
6
|
import { Scroll } from '@homecode/ui';
|
|
7
7
|
import { X, PaperPlaneRightIcon, ChartLineIcon } from '@phosphor-icons/react';
|
|
8
8
|
import { Button } from '../../Button/Button.js';
|
|
9
|
+
import { DropZone } from '../../DropZone/DropZone.js';
|
|
9
10
|
import { PanelResizeHandle } from '../../Sidebar/Sidebar.js';
|
|
10
11
|
import SidebarStem from '../../Sidebar/Sidebar.styl.js';
|
|
11
12
|
import { Chat } from '../Chat.js';
|
|
13
|
+
import { filterToTextAttachments, isAttachmentsDropzoneEnabled, buildAcceptAttr } from '../chatAttachmentAccept.js';
|
|
14
|
+
import { extractChatAttachmentItems } from '../chatAttachmentExtract.js';
|
|
12
15
|
import S from './ChatChrome.styl.js';
|
|
13
16
|
|
|
14
|
-
function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showBranchActionsRow, showSyntheticBranchButtons, unusedBranchKeys, isScriptComplete, onGenerateDashboard, generatingDashboard, onGenerateDashboardClick, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, }) {
|
|
17
|
+
function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showBranchActionsRow, showSyntheticBranchButtons, unusedBranchKeys, isScriptComplete, onGenerateDashboard, generatingDashboard, onGenerateDashboardClick, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, }) {
|
|
18
|
+
const filteredAllowedAttachments = useMemo(() => filterToTextAttachments(allowedAttachments), [allowedAttachments]);
|
|
19
|
+
const attachmentsDropzoneEnabled = isAttachmentsDropzoneEnabled(allowedAttachments, allowPdfAttachments);
|
|
20
|
+
const attachmentAccept = useMemo(() => buildAcceptAttr(filteredAllowedAttachments, allowPdfAttachments), [filteredAllowedAttachments, allowPdfAttachments]);
|
|
21
|
+
const [pendingAttachments, setPendingAttachments] = useState([]);
|
|
22
|
+
const [isExtractingAttachments, setIsExtractingAttachments] = useState(false);
|
|
23
|
+
const promptBusy = isLoading || isExtractingAttachments;
|
|
24
|
+
const handleAttachmentFiles = useCallback((files) => {
|
|
25
|
+
if (promptBusy || files.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
setIsExtractingAttachments(true);
|
|
28
|
+
void extractChatAttachmentItems(files, allowPdfAttachments)
|
|
29
|
+
.then(items => {
|
|
30
|
+
if (items.length > 0) {
|
|
31
|
+
setPendingAttachments(prev => [...prev, ...items]);
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.finally(() => setIsExtractingAttachments(false));
|
|
35
|
+
}, [allowPdfAttachments, promptBusy]);
|
|
36
|
+
const handleRemoveAttachment = useCallback((index) => {
|
|
37
|
+
setPendingAttachments(prev => prev.filter((_, i) => i !== index));
|
|
38
|
+
}, []);
|
|
39
|
+
const handlePromptSubmitWithAttachments = useCallback((message, submittedAttachments) => {
|
|
40
|
+
const trimmed = message.trim();
|
|
41
|
+
const attachments = submittedAttachments ?? [];
|
|
42
|
+
if (!trimmed && attachments.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
void onPromptSubmit(trimmed, attachments);
|
|
45
|
+
if (attachments.length > 0 && onAttachmentsDropped) {
|
|
46
|
+
void onAttachmentsDropped(attachments);
|
|
47
|
+
}
|
|
48
|
+
setPendingAttachments([]);
|
|
49
|
+
}, [onAttachmentsDropped, onPromptSubmit]);
|
|
15
50
|
useEffect(() => {
|
|
16
51
|
if (isEmpty)
|
|
17
52
|
return;
|
|
@@ -28,21 +63,21 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
28
63
|
if (inner)
|
|
29
64
|
setTimeout(scrollToBottom, 100);
|
|
30
65
|
}, [isEmpty, messages.length]);
|
|
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 }),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
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 }), jsxs("div", { className: S.content, children: [attachmentsDropzoneEnabled ? (jsx(DropZone, { accept: attachmentAccept, label: "Drop text files to attach", multiple: true, ghost: true, overlayScope: "container", disabled: promptBusy, className: S.attachmentDropzone, onFiles: handleAttachmentFiles })) : null, 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) => {
|
|
67
|
+
const isLast = index === arr.length - 1;
|
|
68
|
+
return (jsx(Chat.Message, { role: msg.role, text: msg.text, userCsvAttachment: msg.userCsvAttachment, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: isLoading, isLastMessage: isLast, scriptContinue: isLast && scriptContinueLabel
|
|
69
|
+
? { label: scriptContinueLabel }
|
|
70
|
+
: undefined, onScriptContinue: isLast && scriptContinueLabel
|
|
71
|
+
? onScriptContinue
|
|
72
|
+
: undefined, renderMessageChart: renderMessageChart }, msg.id));
|
|
73
|
+
}), showBranchActionsRow && (jsxs("div", { className: S.branchRow, children: [showSyntheticBranchButtons
|
|
74
|
+
? unusedBranchKeys.map(key => {
|
|
75
|
+
const label = displayLabelForBranchKeyFromMessages(key, messages) ?? humanizeBranchKey(key);
|
|
76
|
+
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));
|
|
77
|
+
})
|
|
78
|
+
: null, isScriptComplete &&
|
|
79
|
+
onGenerateDashboard &&
|
|
80
|
+
!generatingDashboard ? (jsxs(Button, { type: "button", variant: "default", size: "lg", disabled: isLoading, onClick: onGenerateDashboardClick, children: [jsx(ChartLineIcon, {}), "Generate Dashboard"] })) : null] })), showInlinePresets && renderPresets('inline'), isLoading && isLastMessageFromUser && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: "Thinking..." }))] }) })), 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: promptBusy, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined })] })] })] })] }));
|
|
46
81
|
}
|
|
47
82
|
|
|
48
83
|
export { ChatChrome };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatChrome_root__oh4Ay{border-radius:var(--p-4);display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;position:relative}.ChatChrome_chatResizeHandle__epfiT{background-color:var(--page-color);border-radius:2.5px;height:calc(100vh + 200px);left:0;opacity:0;position:absolute;right:auto;top:-200px;touch-action:none;transition:opacity .15s ease-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:5px;z-index:30}.ChatChrome_chatResizeHandle__epfiT:before{left:0;right:auto}.ChatChrome_panelHeader__Hkfit{align-items:center;display:flex;flex-shrink:0;justify-content:flex-end;min-height:70px;padding:var(--p-2) var(--p-3);position:absolute;width:100%;z-index:100}.ChatChrome_panelClose__DbKxz{flex-shrink:0}.ChatChrome_content__5qFEi{display:flex;flex:1;flex-direction:column;min-height:0;position:relative}.ChatChrome_scrollWrapper__m4HMu{flex:1;min-height:0;position:relative}.ChatChrome_scroll__oCxoJ{align-items:flex-end;height:100%;max-height:100%;max-width:100%;padding-bottom:var(--p-2);position:absolute;width:100%;z-index:3}.ChatChrome_scrollbar__Hu0aG{right:0!important}.ChatChrome_scrollInner__K9hIy{min-height:100%;padding-top:var(--p-10)}.ChatChrome_scrollInner__K9hIy:after{content:\"\";display:block;height:170px}.ChatChrome_footer__a5Bpp{backdrop-filter:blur(30px);background-color:var(--background-alpha-800);border-top:1px solid var(--border);bottom:0;box-shadow:0 0 20px 16px var(--background);display:flex;flex-direction:column;position:absolute;width:100%;z-index:50}.ChatChrome_loader__9-lnf{color:var(--muted-foreground);margin:var(--p-2) var(--p-6) var(--p-10)}.ChatChrome_branchRow__NMDNv{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--p-6);padding:0 var(--p-6);width:100%}.ChatChrome_branchBtnWrap__aOSVP{display:inline-flex;vertical-align:middle}";
|
|
4
|
-
var S = {"root":"ChatChrome_root__oh4Ay","chatResizeHandle":"ChatChrome_chatResizeHandle__epfiT","panelHeader":"ChatChrome_panelHeader__Hkfit","panelClose":"ChatChrome_panelClose__DbKxz","content":"ChatChrome_content__5qFEi","scrollWrapper":"ChatChrome_scrollWrapper__m4HMu","scroll":"ChatChrome_scroll__oCxoJ","scrollbar":"ChatChrome_scrollbar__Hu0aG","scrollInner":"ChatChrome_scrollInner__K9hIy","footer":"ChatChrome_footer__a5Bpp","loader":"ChatChrome_loader__9-lnf","branchRow":"ChatChrome_branchRow__NMDNv","branchBtnWrap":"ChatChrome_branchBtnWrap__aOSVP"};
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatChrome_root__oh4Ay{border-radius:var(--p-4);display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;position:relative}.ChatChrome_chatResizeHandle__epfiT{background-color:var(--page-color);border-radius:2.5px;height:calc(100vh + 200px);left:0;opacity:0;position:absolute;right:auto;top:-200px;touch-action:none;transition:opacity .15s ease-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:5px;z-index:30}.ChatChrome_chatResizeHandle__epfiT:before{left:0;right:auto}.ChatChrome_panelHeader__Hkfit{align-items:center;display:flex;flex-shrink:0;justify-content:flex-end;min-height:70px;padding:var(--p-2) var(--p-3);position:absolute;width:100%;z-index:100}.ChatChrome_panelClose__DbKxz{flex-shrink:0}.ChatChrome_content__5qFEi{display:flex;flex:1;flex-direction:column;min-height:0;position:relative}.ChatChrome_attachmentDropzone__OC8UI{inset:0;position:absolute;z-index:200}.ChatChrome_scrollWrapper__m4HMu{flex:1;min-height:0;position:relative}.ChatChrome_scroll__oCxoJ{align-items:flex-end;height:100%;max-height:100%;max-width:100%;padding-bottom:var(--p-2);position:absolute;width:100%;z-index:3}.ChatChrome_scrollbar__Hu0aG{right:0!important}.ChatChrome_scrollInner__K9hIy{min-height:100%;padding-top:var(--p-10)}.ChatChrome_scrollInner__K9hIy:after{content:\"\";display:block;height:170px}.ChatChrome_footer__a5Bpp{backdrop-filter:blur(30px);background-color:var(--background-alpha-800);border-top:1px solid var(--border);bottom:0;box-shadow:0 0 20px 16px var(--background);display:flex;flex-direction:column;position:absolute;width:100%;z-index:50}.ChatChrome_notice__JACIw{color:var(--muted-foreground);font-size:var(--text-xs);left:0;margin-bottom:var(--p-1);pointer-events:none;position:absolute;right:0;text-align:center;top:calc(var(--p-7)*-1)}@media (max-width:768px){.ChatChrome_notice__JACIw{font-size:10px}}.ChatChrome_loader__9-lnf{color:var(--muted-foreground);margin:var(--p-2) var(--p-6) var(--p-10)}.ChatChrome_branchRow__NMDNv{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--p-6);padding:0 var(--p-6);width:100%}.ChatChrome_branchBtnWrap__aOSVP{display:inline-flex;vertical-align:middle}";
|
|
4
|
+
var S = {"root":"ChatChrome_root__oh4Ay","chatResizeHandle":"ChatChrome_chatResizeHandle__epfiT","panelHeader":"ChatChrome_panelHeader__Hkfit","panelClose":"ChatChrome_panelClose__DbKxz","content":"ChatChrome_content__5qFEi","attachmentDropzone":"ChatChrome_attachmentDropzone__OC8UI","scrollWrapper":"ChatChrome_scrollWrapper__m4HMu","scroll":"ChatChrome_scroll__oCxoJ","scrollbar":"ChatChrome_scrollbar__Hu0aG","scrollInner":"ChatChrome_scrollInner__K9hIy","footer":"ChatChrome_footer__a5Bpp","notice":"ChatChrome_notice__JACIw","loader":"ChatChrome_loader__9-lnf","branchRow":"ChatChrome_branchRow__NMDNv","branchBtnWrap":"ChatChrome_branchBtnWrap__aOSVP"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -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_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-
|
|
4
|
-
var S = {"root":"ChatMessage_root__6rnsF","text":"ChatMessage_text__Y1XNR","role-user":"ChatMessage_role-user__u4JPV","userColumn":"ChatMessage_userColumn__cQM6-","
|
|
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-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-","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 };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FileChip } from '../../FileChip/FileChip.js';
|
|
2
3
|
import { downloadTextFile } from '../../../../utils/downloadTextFile.js';
|
|
3
|
-
import { CsvIcon } from '../../../icons/CsvIcon/CsvIcon.js';
|
|
4
|
-
import S from './ChatMessage.styl.js';
|
|
5
4
|
|
|
6
5
|
const CSV_DOWNLOAD_HINT = 'Download .CSV file';
|
|
7
6
|
function UserCsvAttachmentBubble({ attachment, }) {
|
|
8
|
-
return (
|
|
7
|
+
return (jsx(FileChip, { name: attachment.displayName, format: "csv", hint: CSV_DOWNLOAD_HINT, onClick: () => downloadTextFile(attachment.content, attachment.filename, 'text/csv;charset=utf-8') }));
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
export { UserCsvAttachmentBubble };
|
|
@@ -7,8 +7,9 @@ import { Button } from '../../Button/Button.js';
|
|
|
7
7
|
import { Input } from '../../Input/Input.js';
|
|
8
8
|
import { syncChatPromptTextareaHeight } from './ChatPrompt.helpers.js';
|
|
9
9
|
import S from './ChatPrompt.styl.js';
|
|
10
|
+
import { ChatPromptAttachments } from './ChatPromptAttachments.js';
|
|
10
11
|
|
|
11
|
-
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
|
|
12
|
+
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments = [], onRemoveAttachment, disabled = false, }) {
|
|
12
13
|
const [message, setMessage] = useState('');
|
|
13
14
|
const inputRef = useRef(null);
|
|
14
15
|
useLayoutEffect(() => {
|
|
@@ -24,9 +25,10 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
|
|
|
24
25
|
}, [prefillMessage]);
|
|
25
26
|
const handleSubmit = (e) => {
|
|
26
27
|
const trimmedMessage = message.trim();
|
|
27
|
-
|
|
28
|
+
const hasAttachments = attachments.length > 0;
|
|
29
|
+
if (trimmedMessage || hasAttachments) {
|
|
28
30
|
e.preventDefault();
|
|
29
|
-
onSubmit(trimmedMessage);
|
|
31
|
+
onSubmit(trimmedMessage, hasAttachments ? attachments : undefined);
|
|
30
32
|
setMessage('');
|
|
31
33
|
}
|
|
32
34
|
};
|
|
@@ -40,7 +42,7 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
|
|
|
40
42
|
}
|
|
41
43
|
},
|
|
42
44
|
});
|
|
43
|
-
return (jsxs("form", { onSubmit: handleSubmit, className: cn(S.root, className), children: [
|
|
45
|
+
return (jsxs("form", { onSubmit: handleSubmit, className: cn(S.root, className), children: [jsx(ChatPromptAttachments, { attachments: attachments, onRemove: index => onRemoveAttachment?.(index), disabled: disabled }), jsxs("div", { className: S.composer, children: [jsx(Input, { ref: inputRef, type: "textarea", rows: 1, value: message, onChange: e => setMessage(e.target.value), placeholder: placeholder || 'Type a message...', className: cn(S.input) }), jsx("div", { className: S.submitColumn, children: jsx(Button, { type: "submit", size: "sm", disabled: disabled || (!message.trim() && attachments.length === 0), children: jsx(SendHorizontalIcon, { size: 16 }) }) })] }), footer] }));
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export { ChatPrompt };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.
|
|
4
|
-
var S = {"root":"ChatPrompt_root__5G5bq","composer":"ChatPrompt_composer__H3c3N","
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.ChatPrompt_input__QPKBV{border:none;border-radius:0!important;flex:1;max-height:200px;min-height:40px;min-width:0;overflow-y:auto;padding:var(--p-2) 0 0!important;resize:none}.ChatPrompt_input__QPKBV,.ChatPrompt_input__QPKBV:focus{box-shadow:none!important}.ChatPrompt_submitColumn__0rY1R{align-items:center;display:flex;flex-direction:column;flex-shrink:0;justify-content:flex-end}.ChatPrompt_submitColumn__0rY1R>button:focus{box-shadow:0 0 0 2px var(--brand-color-500)!important}.ChatPrompt_submitColumn__0rY1R>button:first-child{border:none;position:relative;transition:all .2s}.ChatPrompt_submitColumn__0rY1R>button:first-child:focus{transform:scale(1.2)}.ChatPrompt_submitColumn__0rY1R>button:first-child:before{bottom:-100%;content:\"\";left:-100%;position:absolute;right:-100%;top:-100%}.ChatPrompt_submitColumn__0rY1R .ChatPrompt_attachButton__gi-qF{background-color:var(--page-color);box-shadow:0 0 20px var(--background)}.ChatPrompt_attachments__KG-fG{display:flex;flex-wrap:wrap;gap:var(--p-2);margin-bottom:var(--p-2)}.ChatPrompt_attachmentItem__QJk7J{flex:1 1 300px;max-width:300px;min-width:0}";
|
|
4
|
+
var S = {"root":"ChatPrompt_root__5G5bq","composer":"ChatPrompt_composer__H3c3N","input":"ChatPrompt_input__QPKBV","submitColumn":"ChatPrompt_submitColumn__0rY1R","attachButton":"ChatPrompt_attachButton__gi-qF","attachments":"ChatPrompt_attachments__KG-fG","attachmentItem":"ChatPrompt_attachmentItem__QJk7J"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FileChip } from '../../FileChip/FileChip.js';
|
|
3
|
+
import S from './ChatPrompt.styl.js';
|
|
4
|
+
|
|
5
|
+
function ChatPromptAttachments({ attachments, onRemove, disabled = false, }) {
|
|
6
|
+
if (attachments.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
return (jsx("div", { className: S.attachments, children: attachments.map((item, index) => (jsx(FileChip, { className: S.attachmentItem, name: item.file.name, format: item.kind === 'pdf' ? 'pdf' : 'text', hint: item.kind === 'pdf' ? 'PDF' : 'Text file', onRemove: () => onRemove(index), disabled: disabled }, `${item.file.name}-${index}`))) }));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { ChatPromptAttachments };
|
|
@@ -34,7 +34,7 @@ function ChatSelector({ id, className, onChatDeleted, }) {
|
|
|
34
34
|
}
|
|
35
35
|
return 'New chat';
|
|
36
36
|
};
|
|
37
|
-
return (jsxs("div", { className: S.wrapper, children: [jsx("div", { className: S.selectGrow, children: jsxs(Select, { variant: "clear", value: currentChatId?.toString() ?? '', onValueChange: handleValueChange, children: [jsx(SelectTrigger, { size: "sm", className: className ? cn(S.selectTrigger, className) : S.selectTrigger, children: jsx(SelectValue, { placeholder: "Select chat" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "new", children: "+ New Chat" }), chats.map(chat => (jsx(SelectItem, { value: chat.session_id.toString(), selected: chat.session_id === currentChatId, children: getChatDisplayName(chat) }, chat.session_id)))] })] }) }), jsx(Button, { type: "button", variant: "ghost", size: "sm", className: S.deleteBtn, "aria-label": "Delete chat",
|
|
37
|
+
return (jsxs("div", { className: S.wrapper, children: [jsx("div", { className: S.selectGrow, children: jsxs(Select, { variant: "clear", value: currentChatId?.toString() ?? '', onValueChange: handleValueChange, children: [jsx(SelectTrigger, { size: "sm", className: className ? cn(S.selectTrigger, className) : S.selectTrigger, children: jsx(SelectValue, { placeholder: "Select chat" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "new", children: "+ New Chat" }), chats.map(chat => (jsx(SelectItem, { value: chat.session_id.toString(), selected: chat.session_id === currentChatId, children: getChatDisplayName(chat) }, chat.session_id)))] })] }) }), currentChatId && chats.length > 0 && (jsx(Button, { type: "button", variant: "ghost", size: "sm", className: S.deleteBtn, "aria-label": "Delete chat", onClick: handleDeleteChat, children: jsx(Trash2Icon, { size: 16 }) }))] }));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export { ChatSelector };
|
|
@@ -21,7 +21,7 @@ const CHAT_NAV_COLLAPSE_BREAKPOINT_PX = 1400;
|
|
|
21
21
|
const CHAT_QUERY_PARAM = 'chat';
|
|
22
22
|
const CHAT_OPEN_VALUE = 'open';
|
|
23
23
|
const PROMPT_QUERY_PARAM = 'prompt';
|
|
24
|
-
function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, }) {
|
|
24
|
+
function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, }) {
|
|
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();
|
|
@@ -762,6 +762,9 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
762
762
|
onChatDeleted: endLocalDemoFlow,
|
|
763
763
|
promptPrefill: promptLinkPrefill,
|
|
764
764
|
emptyState,
|
|
765
|
+
allowedAttachments,
|
|
766
|
+
allowPdfAttachments,
|
|
767
|
+
onAttachmentsDropped,
|
|
765
768
|
};
|
|
766
769
|
const toggleOpen = () => onOpenChange(!isOpen);
|
|
767
770
|
return {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** MIME types and extensions accepted for chat text attachments. */
|
|
2
|
+
const TEXT_ATTACHMENT_ACCEPT_PARTS = [
|
|
3
|
+
'text/plain',
|
|
4
|
+
'.txt',
|
|
5
|
+
'text/csv',
|
|
6
|
+
'.csv',
|
|
7
|
+
'text/markdown',
|
|
8
|
+
'.md',
|
|
9
|
+
'.markdown',
|
|
10
|
+
'application/json',
|
|
11
|
+
'.json',
|
|
12
|
+
'text/html',
|
|
13
|
+
'.html',
|
|
14
|
+
'.htm',
|
|
15
|
+
'text/xml',
|
|
16
|
+
'application/xml',
|
|
17
|
+
'.xml',
|
|
18
|
+
'text/yaml',
|
|
19
|
+
'application/yaml',
|
|
20
|
+
'application/x-yaml',
|
|
21
|
+
'.yaml',
|
|
22
|
+
'.yml',
|
|
23
|
+
'text/tab-separated-values',
|
|
24
|
+
'.tsv',
|
|
25
|
+
'text/calendar',
|
|
26
|
+
'.ics',
|
|
27
|
+
];
|
|
28
|
+
const PDF_ATTACHMENT_ACCEPT_PARTS = ['application/pdf', '.pdf'];
|
|
29
|
+
const TEXT_ATTACHMENT_ACCEPT_SET = new Set(TEXT_ATTACHMENT_ACCEPT_PARTS.map(part => part.toLowerCase()));
|
|
30
|
+
/** Keep only tokens from `parts` that appear in the text attachment allowlist. */
|
|
31
|
+
function filterToTextAttachments(parts) {
|
|
32
|
+
if (!parts?.length)
|
|
33
|
+
return [];
|
|
34
|
+
return parts.filter(part => TEXT_ATTACHMENT_ACCEPT_SET.has(part.trim().toLowerCase()));
|
|
35
|
+
}
|
|
36
|
+
function buildAcceptAttr(filteredTextParts, allowPdf) {
|
|
37
|
+
const parts = [...filteredTextParts];
|
|
38
|
+
if (allowPdf) {
|
|
39
|
+
parts.push(...PDF_ATTACHMENT_ACCEPT_PARTS);
|
|
40
|
+
}
|
|
41
|
+
return parts.join(',');
|
|
42
|
+
}
|
|
43
|
+
function isPdfFile(file) {
|
|
44
|
+
const type = file.type.toLowerCase();
|
|
45
|
+
if (type === 'application/pdf')
|
|
46
|
+
return true;
|
|
47
|
+
return file.name.toLowerCase().endsWith('.pdf');
|
|
48
|
+
}
|
|
49
|
+
function isAttachmentsDropzoneEnabled(allowedAttachments, allowPdfAttachments) {
|
|
50
|
+
return (filterToTextAttachments(allowedAttachments).length > 0 ||
|
|
51
|
+
Boolean(allowPdfAttachments));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { PDF_ATTACHMENT_ACCEPT_PARTS, TEXT_ATTACHMENT_ACCEPT_PARTS, buildAcceptAttr, filterToTextAttachments, isAttachmentsDropzoneEnabled, isPdfFile };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isPdfFile } from './chatAttachmentAccept.js';
|
|
2
|
+
import { extractPdfFileToText } from './chatPdfExtract.js';
|
|
3
|
+
|
|
4
|
+
function readTextFile(file) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const reader = new FileReader();
|
|
7
|
+
reader.onload = () => resolve(String(reader.result ?? ''));
|
|
8
|
+
reader.onerror = () => reject(reader.error ?? new Error(`Failed to read ${file.name}`));
|
|
9
|
+
reader.readAsText(file);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async function extractChatAttachmentItems(files, allowPdfAttachments) {
|
|
13
|
+
const items = await Promise.all(files.map(async (file) => {
|
|
14
|
+
if (isPdfFile(file)) {
|
|
15
|
+
if (!allowPdfAttachments)
|
|
16
|
+
return null;
|
|
17
|
+
const text = await extractPdfFileToText(file);
|
|
18
|
+
return { file, text, kind: 'pdf' };
|
|
19
|
+
}
|
|
20
|
+
const text = await readTextFile(file);
|
|
21
|
+
return { file, text, kind: 'text' };
|
|
22
|
+
}));
|
|
23
|
+
return items.filter((item) => item != null);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { extractChatAttachmentItems };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
let workerConfigured = false;
|
|
2
|
+
async function configurePdfWorker(pdfjs) {
|
|
3
|
+
if (workerConfigured)
|
|
4
|
+
return;
|
|
5
|
+
const version = pdfjs.version ?? '4.10.38';
|
|
6
|
+
pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${version}/legacy/build/pdf.worker.min.mjs`;
|
|
7
|
+
workerConfigured = true;
|
|
8
|
+
}
|
|
9
|
+
/** Best-effort plain text from PDF; pages separated with lightweight markdown headings. */
|
|
10
|
+
async function extractPdfFileToText(file) {
|
|
11
|
+
const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
12
|
+
await configurePdfWorker(pdfjs);
|
|
13
|
+
const data = new Uint8Array(await file.arrayBuffer());
|
|
14
|
+
const doc = await pdfjs.getDocument({ data }).promise;
|
|
15
|
+
const pageTexts = [];
|
|
16
|
+
for (let pageNumber = 1; pageNumber <= doc.numPages; pageNumber += 1) {
|
|
17
|
+
const page = await doc.getPage(pageNumber);
|
|
18
|
+
const content = await page.getTextContent();
|
|
19
|
+
const pageText = content.items
|
|
20
|
+
.map(item => ('str' in item ? item.str : ''))
|
|
21
|
+
.join(' ')
|
|
22
|
+
.replace(/\s+/g, ' ')
|
|
23
|
+
.trim();
|
|
24
|
+
if (pageText) {
|
|
25
|
+
pageTexts.push(`## Page ${pageNumber}\n\n${pageText}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return pageTexts.join('\n\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { extractPdfFileToText };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import cn from 'classnames';
|
|
2
3
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
3
4
|
import S from './DropZone.styl.js';
|
|
4
5
|
|
|
@@ -32,28 +33,34 @@ function matchesAccept(file, accept) {
|
|
|
32
33
|
return parts.some(part => matchesAcceptPart(file, part));
|
|
33
34
|
}
|
|
34
35
|
function DropZone(props) {
|
|
35
|
-
const { accept, label, error, disabled = false, ghost = false, id, className, } = props;
|
|
36
|
+
const { accept, label, error, disabled = false, ghost = false, overlayScope = 'viewport', id, className, } = props;
|
|
36
37
|
const [isDragging, setIsDragging] = useState(false);
|
|
38
|
+
const rootRef = useRef(null);
|
|
37
39
|
const dropAreaRef = useRef(null);
|
|
38
40
|
const inputId = id || `dropzone-file-input-${Math.random().toString(36).substr(2, 9)}`;
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const dropProcessedRef = useRef(false);
|
|
42
|
+
const emitDroppedFiles = useCallback((files) => {
|
|
43
|
+
if (disabled || dropProcessedRef.current || files.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
const list = Array.from(files).filter(f => matchesAccept(f, accept));
|
|
46
|
+
if (list.length === 0)
|
|
43
47
|
return;
|
|
44
|
-
|
|
48
|
+
dropProcessedRef.current = true;
|
|
45
49
|
if (props.multiple === true) {
|
|
46
|
-
|
|
47
|
-
props.onFiles(list);
|
|
48
|
-
}
|
|
50
|
+
props.onFiles(list);
|
|
49
51
|
}
|
|
50
52
|
else {
|
|
51
|
-
|
|
52
|
-
if (file) {
|
|
53
|
-
props.onFile(file);
|
|
54
|
-
}
|
|
53
|
+
props.onFile(list[0]);
|
|
55
54
|
}
|
|
56
55
|
}, [accept, disabled, props]);
|
|
56
|
+
const handleDrop = useCallback((e) => {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
e.stopPropagation();
|
|
59
|
+
setIsDragging(false);
|
|
60
|
+
if (disabled)
|
|
61
|
+
return;
|
|
62
|
+
emitDroppedFiles(e.dataTransfer.files);
|
|
63
|
+
}, [disabled, emitDroppedFiles]);
|
|
57
64
|
const handleFileInput = useCallback((e) => {
|
|
58
65
|
if (disabled)
|
|
59
66
|
return;
|
|
@@ -75,11 +82,18 @@ function DropZone(props) {
|
|
|
75
82
|
e.target.value = '';
|
|
76
83
|
}, [accept, disabled, props]);
|
|
77
84
|
useEffect(() => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
const root = rootRef.current;
|
|
86
|
+
if (!root)
|
|
87
|
+
return;
|
|
88
|
+
const targetElement = overlayScope === 'container'
|
|
89
|
+
? root.parentElement
|
|
90
|
+
: (root.closest('[role="dialog"]') ??
|
|
91
|
+
document.body);
|
|
92
|
+
if (!targetElement)
|
|
93
|
+
return;
|
|
81
94
|
const handleGlobalDragOver = (e) => {
|
|
82
95
|
e.preventDefault();
|
|
96
|
+
dropProcessedRef.current = false;
|
|
83
97
|
setIsDragging(true);
|
|
84
98
|
};
|
|
85
99
|
const handleGlobalDragLeave = (e) => {
|
|
@@ -90,21 +104,36 @@ function DropZone(props) {
|
|
|
90
104
|
setIsDragging(false);
|
|
91
105
|
}
|
|
92
106
|
};
|
|
93
|
-
const
|
|
107
|
+
const handleGlobalDropPrevent = (e) => {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
};
|
|
110
|
+
const handleGlobalDropFinalize = (e) => {
|
|
111
|
+
if (overlayScope === 'container' && e.dataTransfer?.files?.length) {
|
|
112
|
+
emitDroppedFiles(e.dataTransfer.files);
|
|
113
|
+
}
|
|
94
114
|
setIsDragging(false);
|
|
95
115
|
};
|
|
96
116
|
targetElement.addEventListener('dragover', handleGlobalDragOver);
|
|
97
117
|
targetElement.addEventListener('dragleave', handleGlobalDragLeave);
|
|
98
|
-
targetElement.addEventListener('drop',
|
|
118
|
+
targetElement.addEventListener('drop', handleGlobalDropPrevent, true);
|
|
119
|
+
targetElement.addEventListener('drop', handleGlobalDropFinalize);
|
|
99
120
|
return () => {
|
|
100
121
|
targetElement.removeEventListener('dragover', handleGlobalDragOver);
|
|
101
122
|
targetElement.removeEventListener('dragleave', handleGlobalDragLeave);
|
|
102
|
-
targetElement.removeEventListener('drop',
|
|
123
|
+
targetElement.removeEventListener('drop', handleGlobalDropPrevent, true);
|
|
124
|
+
targetElement.removeEventListener('drop', handleGlobalDropFinalize);
|
|
103
125
|
};
|
|
104
|
-
}, []);
|
|
126
|
+
}, [emitDroppedFiles, overlayScope]);
|
|
105
127
|
const shouldShowDropArea = !ghost || isDragging;
|
|
106
128
|
const multiple = props.multiple === true;
|
|
107
|
-
|
|
129
|
+
const isContainerOverlay = overlayScope === 'container';
|
|
130
|
+
return (jsxs("div", { ref: rootRef, className: cn(S.root, isContainerOverlay && S.rootContained, isDragging && isContainerOverlay && S.rootDragging, className), style: isContainerOverlay
|
|
131
|
+
? { pointerEvents: isDragging ? 'auto' : 'none' }
|
|
132
|
+
: undefined, children: [shouldShowDropArea && (jsxs("div", { ref: dropAreaRef, className: cn(S.dropArea, isDragging
|
|
133
|
+
? isContainerOverlay
|
|
134
|
+
? S.isDraggingContained
|
|
135
|
+
: S.isDragging
|
|
136
|
+
: '', disabled ? S.disabled : ''), onDrop: handleDrop, children: [jsx("input", { type: "file", accept: accept, multiple: multiple, onChange: handleFileInput, className: S.fileInput, id: inputId, disabled: disabled }), jsx("label", { htmlFor: inputId, className: S.label, style: {
|
|
108
137
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
109
138
|
}, children: label })] })), error && jsx("div", { className: S.error, children: error })] }));
|
|
110
139
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.DropZone_root__ct0cl{display:flex;flex-direction:column;gap:var(--p-2)}.DropZone_dropArea__GIrZ-{background-color:var(--page-color);border:2px dashed var(--border);border-radius:var(--p-4);cursor:pointer;padding:var(--p-4);position:relative;text-align:center;transition:all .2s ease}.DropZone_dropArea__GIrZ- label:before{border-radius:var(--p-4);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.DropZone_dropArea__GIrZ
|
|
4
|
-
var S = {"root":"DropZone_root__ct0cl","dropArea":"DropZone_dropArea__GIrZ-","disabled":"DropZone_disabled__nJAXj","isDragging":"DropZone_isDragging__g7h0n","fileInput":"DropZone_fileInput__K-7dx","label":"DropZone_label__FaOwC","error":"DropZone_error__hwV4z"};
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.DropZone_root__ct0cl{display:flex;flex-direction:column;gap:var(--p-2)}.DropZone_rootContained__UhRMG{height:100%;position:relative;width:100%}.DropZone_rootDragging__37R3u{pointer-events:auto}.DropZone_dropArea__GIrZ-{background-color:var(--page-color);border:2px dashed var(--border);border-radius:var(--p-4);cursor:pointer;padding:var(--p-4);position:relative;text-align:center;transition:all .2s ease}.DropZone_dropArea__GIrZ- label:before{border-radius:var(--p-4);content:\"\";height:100%;left:0;position:absolute;top:0;width:100%}.DropZone_dropArea__GIrZ-:hover:not(.DropZone_disabled__nJAXj){background-color:var(--page-color-alpha-800);border-color:var(--primary-color)}.DropZone_dropArea__GIrZ-.DropZone_isDragging__g7h0n{border-color:var(--primary-color);position:fixed;z-index:9999}.DropZone_dropArea__GIrZ-.DropZone_isDraggingContained__M6ay8,.DropZone_dropArea__GIrZ-.DropZone_isDragging__g7h0n{align-items:center;background-color:var(--page-color-alpha-800);display:flex;height:100%;justify-content:center;left:0;top:0;width:100%}.DropZone_dropArea__GIrZ-.DropZone_isDraggingContained__M6ay8{border-color:var(--primary-color);border-radius:var(--p-4);position:absolute;z-index:1}.DropZone_dropArea__GIrZ-.DropZone_isDraggingContained__M6ay8,.DropZone_dropArea__GIrZ-.DropZone_isDragging__g7h0n,.DropZone_dropArea__GIrZ-:hover:not(.DropZone_disabled__nJAXj){border:2px dashed var(--muted-border)}.dark .DropZone_dropArea__GIrZ-.DropZone_isDraggingContained__M6ay8,.dark .DropZone_dropArea__GIrZ-.DropZone_isDragging__g7h0n,.dark .DropZone_dropArea__GIrZ-:hover:not(.DropZone_disabled__nJAXj){background-color:var(--background)}.DropZone_dropArea__GIrZ-.DropZone_disabled__nJAXj{cursor:not-allowed;opacity:.6}.DropZone_fileInput__K-7dx{display:none}.DropZone_label__FaOwC{color:var(--text-color-secondary);cursor:pointer}.DropZone_error__hwV4z{color:var(--error-color);font-size:.875rem;padding:var(--p-1) var(--p-2)}";
|
|
4
|
+
var S = {"root":"DropZone_root__ct0cl","rootContained":"DropZone_rootContained__UhRMG","rootDragging":"DropZone_rootDragging__37R3u","dropArea":"DropZone_dropArea__GIrZ-","disabled":"DropZone_disabled__nJAXj","isDragging":"DropZone_isDragging__g7h0n","isDraggingContained":"DropZone_isDraggingContained__M6ay8","fileInput":"DropZone_fileInput__K-7dx","label":"DropZone_label__FaOwC","error":"DropZone_error__hwV4z"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { CsvIcon } from '../../icons/CsvIcon/CsvIcon.js';
|
|
4
|
+
import { CardHeader, CardAction } from '../Card/Card.js';
|
|
5
|
+
import { XIcon, FileTextIcon } from '@phosphor-icons/react';
|
|
6
|
+
import S from './FileChip.styl.js';
|
|
7
|
+
|
|
8
|
+
const FORMAT_ICON_SIZE = 32;
|
|
9
|
+
function FormatIcon({ format }) {
|
|
10
|
+
if (format === 'csv') {
|
|
11
|
+
return jsx(CsvIcon, { size: FORMAT_ICON_SIZE });
|
|
12
|
+
}
|
|
13
|
+
return (jsx(FileTextIcon, { size: FORMAT_ICON_SIZE, "aria-hidden": true, style: { color: 'var(--muted-foreground)' } }));
|
|
14
|
+
}
|
|
15
|
+
function FileChipHeader({ name, format, hint, onRemove, }) {
|
|
16
|
+
return (jsx(CardHeader, { className: S.header, icon: jsx(FormatIcon, { format: format }), title: name, description: hint, titleClassName: S.title, descriptionClassName: S.hint, tooltipProps: { maxWidth: 400 }, children: onRemove ? (jsx(CardAction, { children: jsx("button", { type: "button", className: S.remove, "aria-label": `Remove ${name}`, onClick: onRemove, children: jsx(XIcon, { size: 14, "aria-hidden": true }) }) })) : null }));
|
|
17
|
+
}
|
|
18
|
+
function FileChip({ name, format, hint, onClick, onRemove, className, }) {
|
|
19
|
+
const header = (jsx(FileChipHeader, { name: name, format: format, hint: hint, onRemove: onRemove }));
|
|
20
|
+
if (onClick) {
|
|
21
|
+
return (jsx("button", { type: "button", className: cn(S.root, S.interactive, className), "aria-label": `${hint}: ${name}`, onClick: onClick, children: header }));
|
|
22
|
+
}
|
|
23
|
+
return (jsx("div", { className: cn(S.root, className), role: "group", children: header }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { FileChip };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.FileChip_root__LkR-k{align-items:stretch;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--background);border:0;border-radius:var(--p-3);box-shadow:0 0 0 1px var(--border);color:inherit;display:flex;font:inherit;margin:0;max-width:100%;padding:var(--p-3);padding-right:var(--p-4);text-align:left;width:-moz-fit-content;width:fit-content}.dark .FileChip_root__LkR-k{background-color:var(--sb-gray-800)}.FileChip_header__pjZxs{align-items:center;display:flex;flex-direction:row;font-size:inherit;gap:var(--p-2);min-width:0;width:100%}.FileChip_header__pjZxs [data-slot=card-action]{align-self:center;flex-shrink:0;grid-column:auto;grid-row:auto;margin-left:auto;order:3}.FileChip_header__pjZxs>div:first-child{align-items:center;background:transparent;border-radius:0;box-shadow:none;display:flex;flex-shrink:0;height:32px;justify-content:center;min-width:32px;order:1;width:32px}.FileChip_header__pjZxs>div:first-child>svg{height:32px;width:32px}.FileChip_header__pjZxs>div:last-child{flex:1;min-width:0;order:2}.FileChip_interactive__g0-Hk{cursor:pointer;transition:background-color .15s}.FileChip_interactive__g0-Hk:hover{background-color:var(--sb-gray-50)}.FileChip_interactive__g0-Hk:focus-visible{outline:2px solid var(--ring);outline-offset:2px}.dark .FileChip_interactive__g0-Hk:hover{background-color:var(--sb-gray-900)}.FileChip_title__2sUIf{font-size:var(--text-sm);font-weight:500;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.FileChip_hint__ftFdf{font-size:var(--text-xs);line-height:1.4}.FileChip_remove__IkREj{align-items:center;background:transparent;border:none;border-radius:var(--p-2);color:var(--muted-foreground);cursor:pointer;display:inline-flex;height:28px;justify-content:center;width:28px}.FileChip_remove__IkREj:hover:not(:disabled){background-color:var(--muted);color:var(--foreground)}.FileChip_remove__IkREj:disabled{cursor:not-allowed;opacity:.5}";
|
|
4
|
+
var S = {"root":"FileChip_root__LkR-k","header":"FileChip_header__pjZxs","interactive":"FileChip_interactive__g0-Hk","title":"FileChip_title__2sUIf","hint":"FileChip_hint__ftFdf","remove":"FileChip_remove__IkREj"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { S as default };
|
package/dist/esm/index.js
CHANGED
|
@@ -22,6 +22,7 @@ export { ChartAreaInteractive, chartConfig } from './components/ui/ChartAreaInte
|
|
|
22
22
|
export { Chat } from './components/ui/Chat/Chat.js';
|
|
23
23
|
export { usedPresetIdsFromMessages } from './components/ui/Chat/chat-preset-utils.js';
|
|
24
24
|
export { ChatChrome } from './components/ui/Chat/ChatChrome/ChatChrome.js';
|
|
25
|
+
export { TEXT_ATTACHMENT_ACCEPT_PARTS, filterToTextAttachments } from './components/ui/Chat/chatAttachmentAccept.js';
|
|
25
26
|
export { ChatSheet } from './components/ui/Chat/ChatSheet/ChatSheet.js';
|
|
26
27
|
export { useChatPanelChromeModel } from './components/ui/Chat/ChatSheet/useChatPanelChromeModel.js';
|
|
27
28
|
export { ChatMessage } from './components/ui/Chat/ChatMessage/ChatMessage.js';
|
|
@@ -35,6 +36,7 @@ export { Dialog } from './components/ui/Dialog/Dialog.js';
|
|
|
35
36
|
export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from './components/ui/Drawer/Drawer.js';
|
|
36
37
|
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from './components/ui/DropdownMenu/DropdownMenu.js';
|
|
37
38
|
export { DropZone } from './components/ui/DropZone/DropZone.js';
|
|
39
|
+
export { FileChip } from './components/ui/FileChip/FileChip.js';
|
|
38
40
|
export { FlickeringGrid } from './components/ui/FlickeringGrid/FlickeringGrid.js';
|
|
39
41
|
export { Foldable } from './components/ui/Foldable/Foldable.js';
|
|
40
42
|
export { Gap } from './components/ui/Gap/Gap.js';
|
|
@@ -57,9 +57,15 @@ export type ScriptCompletePayload = {
|
|
|
57
57
|
presetId: string;
|
|
58
58
|
answers: Record<string, string>;
|
|
59
59
|
};
|
|
60
|
+
export type ChatAttachmentDropItem = {
|
|
61
|
+
file: File;
|
|
62
|
+
/** UTF-8 text for native text files; PDF yields extracted text. */
|
|
63
|
+
text: string;
|
|
64
|
+
kind: 'text' | 'pdf';
|
|
65
|
+
};
|
|
60
66
|
export interface ChatPromptProps {
|
|
61
67
|
className?: string;
|
|
62
|
-
onSubmit: (message: string) => void;
|
|
68
|
+
onSubmit: (message: string, attachments?: ChatAttachmentDropItem[]) => void;
|
|
63
69
|
placeholder?: string;
|
|
64
70
|
presets?: ChatPreset[];
|
|
65
71
|
disabled?: boolean;
|
|
@@ -68,6 +74,9 @@ export interface ChatPromptProps {
|
|
|
68
74
|
prefillMessage?: string | null;
|
|
69
75
|
/** Disclaimer above composer; default true. ChatChrome sets false when thread has messages. */
|
|
70
76
|
showNotice?: boolean;
|
|
77
|
+
/** Staged files shown above the composer until send. */
|
|
78
|
+
attachments?: ChatAttachmentDropItem[];
|
|
79
|
+
onRemoveAttachment?: (index: number) => void;
|
|
71
80
|
}
|
|
72
81
|
export interface ChatMessageProps {
|
|
73
82
|
role: MessageRole;
|