@sybilion/uilib 1.3.11 → 1.3.12
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/ChatPrompt/ChatPrompt.js +16 -4
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +2 -2
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +4 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.ts +4 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +6 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +7 -4
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl.d.ts +1 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +50 -11
|
@@ -77,7 +77,7 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
77
77
|
})
|
|
78
78
|
: null, isScriptComplete &&
|
|
79
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 })] })] })] })] }));
|
|
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, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export { ChatChrome };
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import { useState, useRef, useLayoutEffect, useEffect } from 'react';
|
|
4
4
|
import useEvent from '../../../../hooks/useEvent.js';
|
|
5
|
-
import { SendHorizontalIcon } from 'lucide-react';
|
|
5
|
+
import { PaperclipIcon, SendHorizontalIcon } from 'lucide-react';
|
|
6
6
|
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
10
|
import { ChatPromptAttachments } from './ChatPromptAttachments.js';
|
|
11
11
|
|
|
12
|
-
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments = [], onRemoveAttachment, disabled = false, }) {
|
|
12
|
+
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
|
|
13
13
|
const [message, setMessage] = useState('');
|
|
14
14
|
const inputRef = useRef(null);
|
|
15
|
+
const fileInputRef = useRef(null);
|
|
16
|
+
const showAttachButton = Boolean(attachmentAccept && onAttachmentFiles);
|
|
15
17
|
useLayoutEffect(() => {
|
|
16
18
|
const el = inputRef.current;
|
|
17
19
|
if (!el)
|
|
@@ -32,6 +34,13 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
|
|
|
32
34
|
setMessage('');
|
|
33
35
|
}
|
|
34
36
|
};
|
|
37
|
+
const handleFileInputChange = (e) => {
|
|
38
|
+
const files = Array.from(e.target.files ?? []);
|
|
39
|
+
e.target.value = '';
|
|
40
|
+
if (files.length > 0) {
|
|
41
|
+
onAttachmentFiles?.(files);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
35
44
|
useEvent({
|
|
36
45
|
event: 'keydown',
|
|
37
46
|
callback: (e) => {
|
|
@@ -42,7 +51,10 @@ function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage,
|
|
|
42
51
|
}
|
|
43
52
|
},
|
|
44
53
|
});
|
|
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(
|
|
54
|
+
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: [showAttachButton ? (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", accept: attachmentAccept, multiple: true, className: S.fileInput, disabled: disabled, onChange: handleFileInputChange }), jsx(Button, { type: "button", variant: "ghost", icon: true, size: "sm", className: S.attachButton, "aria-label": "Attach file", disabled: disabled, onClick: e => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
fileInputRef.current?.click();
|
|
57
|
+
}, children: jsx(PaperclipIcon, { size: 16 }) })] })) : null, 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] }));
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
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%}.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%}.
|
|
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_fileInput__xdgPn{display:none}.ChatPrompt_attachButton__gi-qF{align-self:flex-end;flex-shrink:0}.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_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","fileInput":"ChatPrompt_fileInput__xdgPn","attachButton":"ChatPrompt_attachButton__gi-qF","input":"ChatPrompt_input__QPKBV","submitColumn":"ChatPrompt_submitColumn__0rY1R","attachments":"ChatPrompt_attachments__KG-fG","attachmentItem":"ChatPrompt_attachmentItem__QJk7J"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -77,6 +77,10 @@ export interface ChatPromptProps {
|
|
|
77
77
|
/** Staged files shown above the composer until send. */
|
|
78
78
|
attachments?: ChatAttachmentDropItem[];
|
|
79
79
|
onRemoveAttachment?: (index: number) => void;
|
|
80
|
+
/** HTML `accept` for the attach file picker; set with `onAttachmentFiles`. */
|
|
81
|
+
attachmentAccept?: string;
|
|
82
|
+
/** Called when the user picks files via the attach button. */
|
|
83
|
+
onAttachmentFiles?: (files: File[]) => void;
|
|
80
84
|
}
|
|
81
85
|
export interface ChatMessageProps {
|
|
82
86
|
role: MessageRole;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatPromptProps } from '../Chat.types';
|
|
2
|
-
export declare function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments, onRemoveAttachment, disabled, }: ChatPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments, onRemoveAttachment, disabled, attachmentAccept, onAttachmentFiles, }: ChatPromptProps): import("react/jsx-runtime").JSX.Element;
|
package/package.json
CHANGED
|
@@ -88,6 +88,10 @@ export interface ChatPromptProps {
|
|
|
88
88
|
/** Staged files shown above the composer until send. */
|
|
89
89
|
attachments?: ChatAttachmentDropItem[];
|
|
90
90
|
onRemoveAttachment?: (index: number) => void;
|
|
91
|
+
/** HTML `accept` for the attach file picker; set with `onAttachmentFiles`. */
|
|
92
|
+
attachmentAccept?: string;
|
|
93
|
+
/** Called when the user picks files via the attach button. */
|
|
94
|
+
onAttachmentFiles?: (files: File[]) => void;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
export interface ChatMessageProps {
|
|
@@ -284,6 +284,12 @@ export function ChatChrome({
|
|
|
284
284
|
attachments={pendingAttachments}
|
|
285
285
|
onRemoveAttachment={handleRemoveAttachment}
|
|
286
286
|
prefillMessage={promptPrefill ?? undefined}
|
|
287
|
+
attachmentAccept={
|
|
288
|
+
attachmentsDropzoneEnabled ? attachmentAccept : undefined
|
|
289
|
+
}
|
|
290
|
+
onAttachmentFiles={
|
|
291
|
+
attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined
|
|
292
|
+
}
|
|
287
293
|
/>
|
|
288
294
|
</div>
|
|
289
295
|
</Chat>
|
|
@@ -19,6 +19,13 @@ INPUT_MAX_HEIGHT = 200px
|
|
|
19
19
|
gap var(--p-3)
|
|
20
20
|
width 100%
|
|
21
21
|
|
|
22
|
+
.fileInput
|
|
23
|
+
display none
|
|
24
|
+
|
|
25
|
+
.attachButton
|
|
26
|
+
flex-shrink 0
|
|
27
|
+
align-self flex-end
|
|
28
|
+
|
|
22
29
|
.input
|
|
23
30
|
flex 1
|
|
24
31
|
min-width 0
|
|
@@ -61,10 +68,6 @@ INPUT_MAX_HEIGHT = 200px
|
|
|
61
68
|
right -100%
|
|
62
69
|
bottom -100%
|
|
63
70
|
|
|
64
|
-
.attachButton
|
|
65
|
-
background-color var(--page-color)
|
|
66
|
-
box-shadow 0 0 20px var(--background)
|
|
67
|
-
|
|
68
71
|
.attachments
|
|
69
72
|
display flex
|
|
70
73
|
flex-wrap wrap
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ChangeEvent,
|
|
4
|
+
FormEvent,
|
|
5
|
+
useEffect,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react';
|
|
3
10
|
|
|
4
11
|
import useEvent from '#uilib/hooks/useEvent';
|
|
5
|
-
import { SendHorizontalIcon } from 'lucide-react';
|
|
12
|
+
import { PaperclipIcon, SendHorizontalIcon } from 'lucide-react';
|
|
6
13
|
|
|
7
14
|
import { Button } from '../../Button';
|
|
8
15
|
import { Input } from '../../Input';
|
|
@@ -20,9 +27,13 @@ export function ChatPrompt({
|
|
|
20
27
|
attachments = [],
|
|
21
28
|
onRemoveAttachment,
|
|
22
29
|
disabled = false,
|
|
30
|
+
attachmentAccept,
|
|
31
|
+
onAttachmentFiles,
|
|
23
32
|
}: ChatPromptProps) {
|
|
24
33
|
const [message, setMessage] = useState('');
|
|
25
34
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
35
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
36
|
+
const showAttachButton = Boolean(attachmentAccept && onAttachmentFiles);
|
|
26
37
|
|
|
27
38
|
useLayoutEffect(() => {
|
|
28
39
|
const el = inputRef.current;
|
|
@@ -47,6 +58,14 @@ export function ChatPrompt({
|
|
|
47
58
|
}
|
|
48
59
|
};
|
|
49
60
|
|
|
61
|
+
const handleFileInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
62
|
+
const files = Array.from(e.target.files ?? []);
|
|
63
|
+
e.target.value = '';
|
|
64
|
+
if (files.length > 0) {
|
|
65
|
+
onAttachmentFiles?.(files);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
50
69
|
useEvent({
|
|
51
70
|
event: 'keydown',
|
|
52
71
|
callback: (e: KeyboardEvent) => {
|
|
@@ -66,6 +85,35 @@ export function ChatPrompt({
|
|
|
66
85
|
disabled={disabled}
|
|
67
86
|
/>
|
|
68
87
|
<div className={S.composer}>
|
|
88
|
+
{showAttachButton ? (
|
|
89
|
+
<>
|
|
90
|
+
<input
|
|
91
|
+
ref={fileInputRef}
|
|
92
|
+
type="file"
|
|
93
|
+
accept={attachmentAccept}
|
|
94
|
+
multiple
|
|
95
|
+
className={S.fileInput}
|
|
96
|
+
disabled={disabled}
|
|
97
|
+
onChange={handleFileInputChange}
|
|
98
|
+
/>
|
|
99
|
+
<Button
|
|
100
|
+
type="button"
|
|
101
|
+
variant="ghost"
|
|
102
|
+
icon
|
|
103
|
+
size="sm"
|
|
104
|
+
className={S.attachButton}
|
|
105
|
+
aria-label="Attach file"
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
onClick={e => {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
fileInputRef.current?.click();
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
<PaperclipIcon size={16} />
|
|
113
|
+
</Button>
|
|
114
|
+
</>
|
|
115
|
+
) : null}
|
|
116
|
+
|
|
69
117
|
<Input
|
|
70
118
|
ref={inputRef}
|
|
71
119
|
type="textarea"
|
|
@@ -85,15 +133,6 @@ export function ChatPrompt({
|
|
|
85
133
|
<SendHorizontalIcon size={16} />
|
|
86
134
|
</Button>
|
|
87
135
|
</div>
|
|
88
|
-
|
|
89
|
-
{/* <Button
|
|
90
|
-
variant="outline"
|
|
91
|
-
size="sm"
|
|
92
|
-
onClick={e => e.preventDefault()}
|
|
93
|
-
className={S.attachButton}
|
|
94
|
-
>
|
|
95
|
-
<PaperclipIcon size={16} />
|
|
96
|
-
</Button> */}
|
|
97
136
|
</div>
|
|
98
137
|
|
|
99
138
|
{footer}
|