@stack-spot/ai-chat-widget 1.24.4 → 1.25.1-beta.0
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/CHANGELOG.md +7 -0
- package/dist/app-metadata.json +3 -3
- package/dist/chat-interceptors/send-message.js +3 -3
- package/dist/chat-interceptors/send-message.js.map +1 -1
- package/dist/components/AgentCard/dictionary.d.ts +4 -2
- package/dist/components/AgentCard/dictionary.d.ts.map +1 -1
- package/dist/components/AgentCard/dictionary.js +4 -2
- package/dist/components/AgentCard/dictionary.js.map +1 -1
- package/dist/components/FileDescription.d.ts +10 -0
- package/dist/components/FileDescription.d.ts.map +1 -0
- package/dist/components/FileDescription.js +85 -0
- package/dist/components/FileDescription.js.map +1 -0
- package/dist/components/Selector/index.d.ts +2 -2
- package/dist/components/Selector/index.d.ts.map +1 -1
- package/dist/components/Selector/index.js +2 -2
- package/dist/components/Selector/index.js.map +1 -1
- package/dist/state/ChatEntry.d.ts +9 -0
- package/dist/state/ChatEntry.d.ts.map +1 -1
- package/dist/state/ChatEntry.js.map +1 -1
- package/dist/state/ChatState.d.ts +5 -0
- package/dist/state/ChatState.d.ts.map +1 -1
- package/dist/state/ChatState.js +6 -0
- package/dist/state/ChatState.js.map +1 -1
- package/dist/state/constants.d.ts +5 -0
- package/dist/state/constants.d.ts.map +1 -0
- package/dist/state/constants.js +9 -0
- package/dist/state/constants.js.map +1 -0
- package/dist/state/types.d.ts +5 -1
- package/dist/state/types.d.ts.map +1 -1
- package/dist/utils/chat.d.ts +2 -1
- package/dist/utils/chat.d.ts.map +1 -1
- package/dist/utils/chat.js +2 -1
- package/dist/utils/chat.js.map +1 -1
- package/dist/utils/tools.d.ts +2 -2
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +3 -6
- package/dist/utils/tools.js.map +1 -1
- package/dist/utils/upload/FileUpload.d.ts +21 -0
- package/dist/utils/upload/FileUpload.d.ts.map +1 -0
- package/dist/utils/upload/FileUpload.js +55 -0
- package/dist/utils/upload/FileUpload.js.map +1 -0
- package/dist/utils/upload/UploadManager.d.ts +40 -0
- package/dist/utils/upload/UploadManager.d.ts.map +1 -0
- package/dist/utils/upload/UploadManager.js +131 -0
- package/dist/utils/upload/UploadManager.js.map +1 -0
- package/dist/utils/upload/context.d.ts +15 -0
- package/dist/utils/upload/context.d.ts.map +1 -0
- package/dist/utils/upload/context.js +37 -0
- package/dist/utils/upload/context.js.map +1 -0
- package/dist/utils/upload/errors.d.ts +17 -0
- package/dist/utils/upload/errors.d.ts.map +1 -0
- package/dist/utils/upload/errors.js +27 -0
- package/dist/utils/upload/errors.js.map +1 -0
- package/dist/utils/upload/types.d.ts +7 -0
- package/dist/utils/upload/types.d.ts.map +1 -0
- package/dist/utils/upload/types.js +2 -0
- package/dist/utils/upload/types.js.map +1 -0
- package/dist/utils/upload/utils.d.ts +4 -0
- package/dist/utils/upload/utils.d.ts.map +1 -0
- package/dist/utils/upload/utils.js +10 -0
- package/dist/utils/upload/utils.js.map +1 -0
- package/dist/views/Agents/AgentDescription.d.ts +2 -9
- package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
- package/dist/views/Agents/AgentDescription.js +11 -9
- package/dist/views/Agents/AgentDescription.js.map +1 -1
- package/dist/views/Agents/AgentsPanel.d.ts.map +1 -1
- package/dist/views/Agents/AgentsPanel.js +11 -11
- package/dist/views/Agents/AgentsPanel.js.map +1 -1
- package/dist/views/Agents/AgentsTab.d.ts +2 -2
- package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
- package/dist/views/Agents/AgentsTab.js +4 -4
- package/dist/views/Agents/AgentsTab.js.map +1 -1
- package/dist/views/Agents/useAgentFavorites.d.ts +1 -1
- package/dist/views/Agents/useAgentFavorites.js +4 -4
- package/dist/views/Agents/useAgentFavorites.js.map +1 -1
- package/dist/views/Chat/AgentInfo.d.ts +2 -1
- package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
- package/dist/views/Chat/AgentInfo.js +2 -2
- package/dist/views/Chat/AgentInfo.js.map +1 -1
- package/dist/views/Chat/ChatMessage.d.ts +1 -1
- package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/views/Chat/ChatMessage.js +27 -8
- package/dist/views/Chat/ChatMessage.js.map +1 -1
- package/dist/views/Chat/styled.d.ts.map +1 -1
- package/dist/views/Chat/styled.js +15 -1
- package/dist/views/Chat/styled.js.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.d.ts.map +1 -1
- package/dist/views/ChatHistory/HistoryItem.js +8 -5
- package/dist/views/ChatHistory/HistoryItem.js.map +1 -1
- package/dist/views/ChatHistory/utils.d.ts +0 -6
- package/dist/views/ChatHistory/utils.d.ts.map +1 -1
- package/dist/views/ChatHistory/utils.js +1 -16
- package/dist/views/ChatHistory/utils.js.map +1 -1
- package/dist/views/Home/CustomAgent.js +3 -3
- package/dist/views/Home/CustomAgent.js.map +1 -1
- package/dist/views/MessageInput/AgentSelector.js +4 -4
- package/dist/views/MessageInput/AgentSelector.js.map +1 -1
- package/dist/views/MessageInput/ButtonAgent.js +2 -2
- package/dist/views/MessageInput/ButtonAgent.js.map +1 -1
- package/dist/views/MessageInput/{InfoBar.d.ts → ContextBar.d.ts} +2 -2
- package/dist/views/MessageInput/ContextBar.d.ts.map +1 -0
- package/dist/views/MessageInput/{InfoBar.js → ContextBar.js} +5 -5
- package/dist/views/MessageInput/ContextBar.js.map +1 -0
- package/dist/views/MessageInput/SelectContent.d.ts.map +1 -1
- package/dist/views/MessageInput/SelectContent.js +14 -17
- package/dist/views/MessageInput/SelectContent.js.map +1 -1
- package/dist/views/MessageInput/UploadBar.d.ts +2 -0
- package/dist/views/MessageInput/UploadBar.d.ts.map +1 -0
- package/dist/views/MessageInput/UploadBar.js +47 -0
- package/dist/views/MessageInput/UploadBar.js.map +1 -0
- package/dist/views/MessageInput/dictionary.d.ts +1 -1
- package/dist/views/MessageInput/dictionary.d.ts.map +1 -1
- package/dist/views/MessageInput/dictionary.js +18 -4
- package/dist/views/MessageInput/dictionary.js.map +1 -1
- package/dist/views/MessageInput/index.d.ts.map +1 -1
- package/dist/views/MessageInput/index.js +46 -5
- package/dist/views/MessageInput/index.js.map +1 -1
- package/dist/views/MessageInput/styled.d.ts.map +1 -1
- package/dist/views/MessageInput/styled.js +56 -27
- package/dist/views/MessageInput/styled.js.map +1 -1
- package/dist/views/Steps/dictionary.d.ts +1 -1
- package/dist/views/Tools.js +3 -3
- package/dist/views/Tools.js.map +1 -1
- package/dist/views/Workspaces/WorkspacesTab.js +1 -1
- package/package.json +2 -2
- package/src/app-metadata.json +3 -3
- package/src/chat-interceptors/send-message.ts +3 -3
- package/src/components/AgentCard/dictionary.ts +4 -2
- package/src/components/FileDescription.tsx +114 -0
- package/src/components/Selector/index.tsx +4 -5
- package/src/state/ChatEntry.ts +10 -0
- package/src/state/ChatState.ts +6 -0
- package/src/state/constants.ts +12 -0
- package/src/state/types.ts +6 -1
- package/src/utils/chat.ts +3 -1
- package/src/utils/tools.ts +5 -7
- package/src/utils/upload/FileUpload.ts +64 -0
- package/src/utils/upload/UploadManager.ts +147 -0
- package/src/utils/upload/context.tsx +44 -0
- package/src/utils/upload/errors.ts +34 -0
- package/src/utils/upload/types.ts +7 -0
- package/src/utils/upload/utils.ts +12 -0
- package/src/views/Agents/AgentDescription.tsx +18 -25
- package/src/views/Agents/AgentsPanel.tsx +11 -12
- package/src/views/Agents/AgentsTab.tsx +8 -16
- package/src/views/Agents/useAgentFavorites.ts +4 -4
- package/src/views/Chat/AgentInfo.tsx +3 -2
- package/src/views/Chat/ChatMessage.tsx +51 -16
- package/src/views/Chat/styled.ts +15 -1
- package/src/views/ChatHistory/HistoryItem.tsx +10 -5
- package/src/views/ChatHistory/utils.ts +1 -18
- package/src/views/Home/CustomAgent.tsx +4 -4
- package/src/views/MessageInput/AgentSelector.tsx +4 -4
- package/src/views/MessageInput/ButtonAgent.tsx +2 -2
- package/src/views/MessageInput/{InfoBar.tsx → ContextBar.tsx} +9 -9
- package/src/views/MessageInput/SelectContent.tsx +17 -21
- package/src/views/MessageInput/UploadBar.tsx +69 -0
- package/src/views/MessageInput/dictionary.ts +18 -4
- package/src/views/MessageInput/index.tsx +77 -32
- package/src/views/MessageInput/styled.ts +56 -27
- package/src/views/Tools.tsx +4 -3
- package/src/views/Workspaces/WorkspacesTab.tsx +1 -1
- package/dist/views/MessageInput/InfoBar.d.ts.map +0 -1
- package/dist/views/MessageInput/InfoBar.js.map +0 -1
|
@@ -8,15 +8,15 @@ import { FadingOverflow } from '../../components/FadingOverflow'
|
|
|
8
8
|
import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
|
|
9
9
|
import { useMessageInputDictionary } from './dictionary'
|
|
10
10
|
|
|
11
|
-
interface
|
|
11
|
+
interface ContextBadgeProps {
|
|
12
12
|
label: string,
|
|
13
13
|
color: ColorPaletteName,
|
|
14
14
|
dismiss: string,
|
|
15
15
|
onDismiss?: () => void,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
<Badge appearance="square" palette={color} className="
|
|
18
|
+
const ContextBadge = ({ label, color, dismiss, onDismiss }: ContextBadgeProps) => (
|
|
19
|
+
<Badge appearance="square" palette={color} className="context-badge"
|
|
20
20
|
afterElement={
|
|
21
21
|
onDismiss &&
|
|
22
22
|
<IconButton appearance="square" colorIcon={`${color}.800`} onClick={onDismiss} title={dismiss} arial-label={dismiss}>
|
|
@@ -34,7 +34,7 @@ const InfoBadge = ({ label, color, dismiss, onDismiss }: InfoBadgeProps) => (
|
|
|
34
34
|
* - which stack is being used;
|
|
35
35
|
* - which knowledge sources are being used.
|
|
36
36
|
*/
|
|
37
|
-
export const
|
|
37
|
+
export const ContextBar = () => {
|
|
38
38
|
const t = useMessageInputDictionary()
|
|
39
39
|
const chat = useCurrentChat()
|
|
40
40
|
const currentStack = useCurrentChatState('stack')
|
|
@@ -47,7 +47,7 @@ export const InfoBar = () => {
|
|
|
47
47
|
const onDismiss = features.knowledgeSource
|
|
48
48
|
? (() => chat.set('knowledgeSources', currentKnowledgeSources.filter(({ id }) => id !== ks.id)))
|
|
49
49
|
: undefined
|
|
50
|
-
return <li key={ks.id}><
|
|
50
|
+
return <li key={ks.id}><ContextBadge label={ks.label} dismiss={t.removeKS} color="teal" onDismiss={onDismiss} /></li>
|
|
51
51
|
}), [currentKnowledgeSources])
|
|
52
52
|
const shouldRenderRemoveAllButton = (
|
|
53
53
|
currentSelection
|
|
@@ -69,7 +69,7 @@ export const InfoBar = () => {
|
|
|
69
69
|
}, [])
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
|
-
<div className={listToClass(['info-bar', visible && 'visible'])}>
|
|
72
|
+
<div className={listToClass(['info-bar', 'context-bar', visible && 'visible'])}>
|
|
73
73
|
<div className="space"></div>
|
|
74
74
|
<div className="content">
|
|
75
75
|
{shouldRenderRemoveAllButton && (
|
|
@@ -83,12 +83,12 @@ export const InfoBar = () => {
|
|
|
83
83
|
<ul>
|
|
84
84
|
{currentSelection && (
|
|
85
85
|
<li>
|
|
86
|
-
<
|
|
86
|
+
<ContextBadge label={t.selected} dismiss={t.removeSelection} color="blue" onDismiss={removeCodeSelection} />
|
|
87
87
|
</li>
|
|
88
88
|
)}
|
|
89
89
|
{currentStack && (
|
|
90
90
|
<li>
|
|
91
|
-
<
|
|
91
|
+
<ContextBadge
|
|
92
92
|
label={currentStack.label}
|
|
93
93
|
dismiss={t.removeStack}
|
|
94
94
|
color="cyan"
|
|
@@ -98,7 +98,7 @@ export const InfoBar = () => {
|
|
|
98
98
|
)}
|
|
99
99
|
{currentWorkspace && (
|
|
100
100
|
<li>
|
|
101
|
-
<
|
|
101
|
+
<ContextBadge
|
|
102
102
|
label={currentWorkspace.label}
|
|
103
103
|
dismiss={t.removeWorkspace}
|
|
104
104
|
color="pink"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { KnowledgeSource, Plus, Spaces, Stack } from '@citric/icons'
|
|
1
|
+
import { DocumentUpload, KnowledgeSource, Plus, Spaces, Stack } from '@citric/icons'
|
|
2
2
|
import { IconButton } from '@citric/ui'
|
|
3
3
|
import { SelectionList } from '@stack-spot/portal-components/SelectionList'
|
|
4
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
5
4
|
import { useMemo, useState } from 'react'
|
|
6
5
|
import { useCurrentChatState, useWidget } from '../../context/hooks'
|
|
6
|
+
import { useUploadManager } from '../../utils/upload/context'
|
|
7
|
+
import { useMessageInputDictionary } from './dictionary'
|
|
7
8
|
|
|
8
9
|
type chatFeatures = 'workspace' | 'knowledgeSource' | 'stack'
|
|
9
10
|
type chatPanel = 'ks' | 'workspace' | 'stack'
|
|
@@ -12,8 +13,9 @@ export const SelectContent = () => {
|
|
|
12
13
|
const widget = useWidget()
|
|
13
14
|
const [visibleMenu, setVisibleMenu] = useState(false)
|
|
14
15
|
const features = useCurrentChatState('features')
|
|
16
|
+
const t = useMessageInputDictionary()
|
|
17
|
+
const uploadManager = useUploadManager()
|
|
15
18
|
const hasFeatureButtons = features.workspace || features.knowledgeSource || features.stack
|
|
16
|
-
const t = useTranslate(dictionary)
|
|
17
19
|
|
|
18
20
|
const itemConfigs = [
|
|
19
21
|
{
|
|
@@ -59,31 +61,25 @@ export const SelectContent = () => {
|
|
|
59
61
|
title={visibleMenu ? t.collapse : t.expand}
|
|
60
62
|
data-test-hint="button-options"
|
|
61
63
|
aria-label={visibleMenu ? t.collapse : t.expand}
|
|
64
|
+
aria-controls="chatMessageMenu"
|
|
62
65
|
onClick={() => setVisibleMenu(state => !state)}>
|
|
63
66
|
<Plus />
|
|
64
67
|
</IconButton>
|
|
65
68
|
<SelectionList
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
top: '-140px',
|
|
69
|
-
}}
|
|
70
|
-
id="menuConfig"
|
|
69
|
+
className="message-menu"
|
|
70
|
+
id="chatMessageMenu"
|
|
71
71
|
visible={visibleMenu}
|
|
72
72
|
onHide={() => setVisibleMenu(false)}
|
|
73
|
-
items={
|
|
73
|
+
items={[
|
|
74
|
+
...listItems,
|
|
75
|
+
{
|
|
76
|
+
label: t.upload,
|
|
77
|
+
onClick: () => uploadManager.open(),
|
|
78
|
+
className: 'upload-item',
|
|
79
|
+
icon: <DocumentUpload />,
|
|
80
|
+
},
|
|
81
|
+
]}
|
|
74
82
|
/>
|
|
75
83
|
</>
|
|
76
84
|
)
|
|
77
85
|
}
|
|
78
|
-
|
|
79
|
-
const dictionary = {
|
|
80
|
-
en: {
|
|
81
|
-
expand: 'Expand options',
|
|
82
|
-
collapse: 'Collapse options',
|
|
83
|
-
},
|
|
84
|
-
pt: {
|
|
85
|
-
expand: 'Mostrar opções',
|
|
86
|
-
collapse: 'Esconder opções',
|
|
87
|
-
},
|
|
88
|
-
} satisfies Dictionary
|
|
89
|
-
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { interpolate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import { FadingOverflow } from '../../components/FadingOverflow'
|
|
5
|
+
import { FileDescription } from '../../components/FileDescription'
|
|
6
|
+
import { useCurrentChat } from '../../context/hooks'
|
|
7
|
+
import { ChatEntry } from '../../state/ChatEntry'
|
|
8
|
+
import { useUploadErrorEffect, useUploadManager, useUploads, useUploadStatus } from '../../utils/upload/context'
|
|
9
|
+
import { FileIsTooLarge, MaxFilesReached } from '../../utils/upload/errors'
|
|
10
|
+
import { FileUpload } from '../../utils/upload/FileUpload'
|
|
11
|
+
import { useMessageInputDictionary } from './dictionary'
|
|
12
|
+
|
|
13
|
+
interface UploadedFileProps {
|
|
14
|
+
upload: FileUpload,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createImageFromFile(file: File) {
|
|
18
|
+
return <img src={URL.createObjectURL(file)} alt={file.name} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const UploadItem = ({ upload }: UploadedFileProps) => {
|
|
22
|
+
const uploadManager = useUploadManager()
|
|
23
|
+
const icon = upload.file.type.toLowerCase().startsWith('image/') ? createImageFromFile(upload.file) : undefined
|
|
24
|
+
const status = useUploadStatus(upload)
|
|
25
|
+
return <FileDescription
|
|
26
|
+
fileName={upload.file.name}
|
|
27
|
+
icon={icon}
|
|
28
|
+
onRemove={() => uploadManager.remove(upload)}
|
|
29
|
+
onRetry={() => upload.retry()}
|
|
30
|
+
status={status}
|
|
31
|
+
/>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const UploadBar = () => {
|
|
35
|
+
const uploads = useUploads()
|
|
36
|
+
const listItems = useMemo(() => uploads.map((up) => <li key={up.id}><UploadItem upload={up} /></li>), [uploads])
|
|
37
|
+
const t = useMessageInputDictionary()
|
|
38
|
+
const chat = useCurrentChat()
|
|
39
|
+
const visible = !!uploads.length
|
|
40
|
+
|
|
41
|
+
useUploadErrorEffect((errors) => {
|
|
42
|
+
const sizeErrors = errors.filter(e => e instanceof FileIsTooLarge)
|
|
43
|
+
const maxItemsErrors = errors.filter(e => e instanceof MaxFilesReached)
|
|
44
|
+
const maxSize = sizeErrors[0]?.maxSize
|
|
45
|
+
const maxItems = maxItemsErrors[0]?.maxFiles
|
|
46
|
+
const sizeErrorsNames = sizeErrors.map(e => e.fileName)
|
|
47
|
+
const maxItemsErrorsNames = maxItemsErrors.map(e => e.fileName)
|
|
48
|
+
const lines: string[] = []
|
|
49
|
+
if (sizeErrors.length) {
|
|
50
|
+
lines.push(`${interpolate(t.uploadSizeError, `${maxSize.value} ${maxSize.unit}`)}\n- ${sizeErrorsNames.join('\n- ')}`)
|
|
51
|
+
}
|
|
52
|
+
if (maxItemsErrors.length) {
|
|
53
|
+
lines.push(`${interpolate(t.uploadItemLimitError, maxItems)}\n- ${maxItemsErrorsNames.join('\n- ')}`)
|
|
54
|
+
}
|
|
55
|
+
if (!lines.length) return
|
|
56
|
+
chat.pushMessage(new ChatEntry({ agentType: 'system', type: 'md', content: lines.join('\n\n') }))
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className={listToClass(['info-bar', 'upload-bar', visible && 'visible'])}>
|
|
61
|
+
<div className="space"></div>
|
|
62
|
+
<div className="content">
|
|
63
|
+
<FadingOverflow className="list-overflow" scroll="arrows" enableHorizontalScrollWithVerticalWheel>
|
|
64
|
+
<ul>{listItems}</ul>
|
|
65
|
+
</FadingOverflow>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -7,8 +7,6 @@ const dictionary = {
|
|
|
7
7
|
spot: 'Select spot',
|
|
8
8
|
knowledgeSource: 'Select knowledge sources',
|
|
9
9
|
agent: 'Select agent',
|
|
10
|
-
collapse: 'Hide buttons',
|
|
11
|
-
expand: 'Show buttons',
|
|
12
10
|
send: 'Send message',
|
|
13
11
|
placeholder: 'Message to $0 or use / or @.',
|
|
14
12
|
cancel: 'Cancel',
|
|
@@ -19,6 +17,14 @@ const dictionary = {
|
|
|
19
17
|
selected: 'Selected',
|
|
20
18
|
removeSelection: 'Remove current code selection',
|
|
21
19
|
remove: 'Remove',
|
|
20
|
+
upload: 'Upload file',
|
|
21
|
+
expand: 'Expand options',
|
|
22
|
+
collapse: 'Collapse options',
|
|
23
|
+
uploadSizeError: 'The following files were not added to the upload list because they\'re larger than $1:',
|
|
24
|
+
uploadItemLimitError: 'The following files were not added because no more than $1 items may be uploaded at a time:',
|
|
25
|
+
cantSendBecauseOfUploadError: 'Can\'t send the message because one of the files in the upload list could not be uploaded. Please, retry it or remove it from the list.',
|
|
26
|
+
cantSendBecauseOfUploadProgress: 'Please wait until all files are uploaded before sending the message. You can also cancel the upload by removing it from the list of uploads.',
|
|
27
|
+
cantSendBecauseOfEmptyContent: 'You can\'t send empty messages. Please write some text or upload a file.',
|
|
22
28
|
},
|
|
23
29
|
pt: {
|
|
24
30
|
stack: 'Selecionar stack',
|
|
@@ -26,8 +32,6 @@ const dictionary = {
|
|
|
26
32
|
spot: 'Selecionar spot',
|
|
27
33
|
knowledgeSource: 'Selecionar knowledge sources',
|
|
28
34
|
agent: 'Selecionar agente',
|
|
29
|
-
collapse: 'Esconder botões',
|
|
30
|
-
expand: 'Mostrar botões',
|
|
31
35
|
send: 'Enviar mensagem',
|
|
32
36
|
placeholder: 'Mensagem para $0 ou use / ou @.',
|
|
33
37
|
cancel: 'Cancelar',
|
|
@@ -38,6 +42,16 @@ const dictionary = {
|
|
|
38
42
|
selected: 'Selecionado',
|
|
39
43
|
removeSelection: 'Desfazer seleção de código',
|
|
40
44
|
remove: 'Remover',
|
|
45
|
+
upload: 'Enviar arquivo',
|
|
46
|
+
expand: 'Mostrar opções',
|
|
47
|
+
collapse: 'Esconder opções',
|
|
48
|
+
uploadSizeError: 'Os seguintes arquivos não foram adicionados à lista de upload porque eles são maiores que $0:',
|
|
49
|
+
uploadItemLimitError: 'Os seguintes arquivos não foram adicionados à lista de upload porque é permitido enviar no máximo $0 arquivos por vez:',
|
|
50
|
+
uploadError: 'Ocorreu um erro ao enviar o arquivo "$0".',
|
|
51
|
+
unknownUploadError: 'Ocorreu um erro ao enviar os arquivos.',
|
|
52
|
+
cantSendBecauseOfUploadError: 'Não é possível enviar a mensagem, pois um dos arquivos na lista de uploads não pôde ser enviado. Por favor, tente enviá-lo novamente ou remova-o da lista.',
|
|
53
|
+
cantSendBecauseOfUploadProgress: 'Por favor aguarde todos os uploads de arquivos antes de enviar a mensagem. Você pode cancelar o upload removendo o arquivo da lista de uploads.',
|
|
54
|
+
cantSendBecauseOfEmptyContent: 'Não é possível enviar mensagens vazia. Por favor, escreva algum texto ou envie um arquivo.',
|
|
41
55
|
},
|
|
42
56
|
} satisfies Dictionary
|
|
43
57
|
|
|
@@ -7,14 +7,16 @@ import { useCurrentChat, useCurrentChatState, useWidgetState } from '../../conte
|
|
|
7
7
|
import { quickCommandRegex } from '../../regex'
|
|
8
8
|
import { ChatEntry } from '../../state/ChatEntry'
|
|
9
9
|
import { checkIsTrial } from '../../utils/check-is-trial'
|
|
10
|
+
import { UploadProvider } from '../../utils/upload/context'
|
|
10
11
|
import { AgentSelector } from './AgentSelector'
|
|
11
12
|
import { ButtonAgent } from './ButtonAgent'
|
|
12
13
|
import { ButtonBar } from './ButtonBar'
|
|
13
14
|
import { useUserEntryHistoryShortcut } from './chat-entry-history'
|
|
15
|
+
import { ContextBar } from './ContextBar'
|
|
14
16
|
import { useMessageInputDictionary } from './dictionary'
|
|
15
|
-
import { InfoBar } from './InfoBar'
|
|
16
17
|
import { QuickCommandSelector } from './QuickCommandSelector'
|
|
17
18
|
import { MAX_INPUT_HEIGHT, MessageInputBox, MIN_INPUT_HEIGHT } from './styled'
|
|
19
|
+
import { UploadBar } from './UploadBar'
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* This renders the MessageInput part of the layout which includes the progress bar, the actual textarea, the badges telling what is
|
|
@@ -35,14 +37,56 @@ export const MessageInput = () => {
|
|
|
35
37
|
const { handleKeyDown, handleKeyUp } = useUserEntryHistoryShortcut()
|
|
36
38
|
const isTrial = checkIsTrial()
|
|
37
39
|
|
|
40
|
+
const checkSendRequirements = useCallback(() => {
|
|
41
|
+
if (chat.uploadManager.status === 'error') {
|
|
42
|
+
chat.pushMessage(new ChatEntry({
|
|
43
|
+
agentType: 'system',
|
|
44
|
+
type: 'text',
|
|
45
|
+
content: t.cantSendBecauseOfUploadError,
|
|
46
|
+
}))
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
if (chat.uploadManager.status === 'uploading') {
|
|
50
|
+
chat.pushMessage(new ChatEntry({
|
|
51
|
+
agentType: 'system',
|
|
52
|
+
type: 'text',
|
|
53
|
+
content: t.cantSendBecauseOfUploadProgress,
|
|
54
|
+
}))
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
if (!chat.get('nextMessage') && !chat.uploadManager.get().length) {
|
|
58
|
+
chat.pushMessage(new ChatEntry({
|
|
59
|
+
agentType: 'system',
|
|
60
|
+
type: 'text',
|
|
61
|
+
content: t.cantSendBecauseOfEmptyContent,
|
|
62
|
+
}))
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
return true
|
|
66
|
+
}, [chat])
|
|
67
|
+
|
|
38
68
|
const onSend = useCallback(async () => {
|
|
39
|
-
const message = chat.get('nextMessage')
|
|
40
|
-
|
|
69
|
+
const message = chat.get('nextMessage')
|
|
70
|
+
const canSend = checkSendRequirements()
|
|
71
|
+
if (!canSend) return
|
|
41
72
|
const code = chat.get('codeSelection')
|
|
42
73
|
const language = chat.get('codeLanguage')
|
|
43
|
-
const prompt = code && !quickCommandRegex.test(message) ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message
|
|
44
|
-
chat.pushMessage(ChatEntry
|
|
74
|
+
const prompt = code && !quickCommandRegex.test(message ?? '') ? `${message}\n\`\`\`${language}\n${code}\n\`\`\`` : message
|
|
75
|
+
chat.pushMessage(new ChatEntry({
|
|
76
|
+
type: 'md',
|
|
77
|
+
agentType: 'user',
|
|
78
|
+
content: prompt || '',
|
|
79
|
+
upload: chat.uploadManager.get().map(
|
|
80
|
+
up => ({
|
|
81
|
+
id: up.uploadId!, // we know that all files have been uploaded, so they have an id
|
|
82
|
+
name: up.file.name,
|
|
83
|
+
image: up.file.type.toLowerCase().startsWith('image/') ? URL.createObjectURL(up.file) : undefined,
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
86
|
+
updated: new Date().toISOString(),
|
|
87
|
+
}))
|
|
45
88
|
chat.set('nextMessage', '')
|
|
89
|
+
chat.uploadManager.reset()
|
|
46
90
|
}, [chat])
|
|
47
91
|
|
|
48
92
|
const onKeyDown = useCallback((event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
@@ -59,33 +103,34 @@ export const MessageInput = () => {
|
|
|
59
103
|
}, [isLoading])
|
|
60
104
|
|
|
61
105
|
return (
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
<UploadProvider value={chat.uploadManager}>
|
|
107
|
+
<MessageInputBox aria-busy={isLoading} className="message-input" $inputFocused={focused}>
|
|
108
|
+
<div className="wrapper-action">
|
|
109
|
+
<QuickCommandSelector inputRef={textAreaRef} isTrial={isTrial} />
|
|
110
|
+
<AgentSelector inputRef={textAreaRef} isTrial={isTrial} />
|
|
111
|
+
<div className={listToClass(['action-box', focused && 'focused', isLoading && 'disabled'])}>
|
|
112
|
+
<ButtonAgent />
|
|
113
|
+
<AdaptiveTextArea
|
|
114
|
+
ref={textAreaRef}
|
|
115
|
+
placeholder={agentLabel && interpolate(t.placeholder, agentLabel)}
|
|
116
|
+
onChange={e => chat.set('nextMessage', e.target.value)}
|
|
117
|
+
value={value}
|
|
118
|
+
onFocus={() => setFocused(true)}
|
|
119
|
+
onBlur={() => setFocused(false)}
|
|
120
|
+
onKeyDown={onKeyDown}
|
|
121
|
+
onKeyUp={handleKeyUp}
|
|
122
|
+
onIncreaseSize={() => setExpanded(false)}
|
|
123
|
+
onResetSize={() => !expansionLocked.current && setExpanded(true)}
|
|
124
|
+
maxHeight={isMinimized ? MIN_INPUT_HEIGHT : MAX_INPUT_HEIGHT}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
81
127
|
</div>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
<ProgressBar visible={true} animate={isLoading}
|
|
129
|
+
backgroundColor={isLoading || !focused ? theme.color.light[500] : theme.color.primary[500]} />
|
|
130
|
+
<ContextBar />
|
|
131
|
+
<UploadBar />
|
|
132
|
+
<ButtonBar onSend={onSend} isLoading={isLoading} />
|
|
133
|
+
</MessageInputBox>
|
|
134
|
+
</UploadProvider>
|
|
88
135
|
)
|
|
89
136
|
}
|
|
90
|
-
|
|
91
|
-
|
|
@@ -2,8 +2,10 @@ import { IconButton } from '@citric/ui'
|
|
|
2
2
|
import { theme } from '@stack-spot/portal-theme'
|
|
3
3
|
import { styled } from 'styled-components'
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const CONTEXT_BAR_HEIGHT = 38
|
|
6
|
+
const CONTEXT_BAR_DISPLACEMENT = 4
|
|
7
|
+
const UPLOAD_BAR_HEIGHT = 60
|
|
8
|
+
const UPLOAD_BAR_DISPLACEMENT = 4
|
|
7
9
|
export const MAX_INPUT_HEIGHT = 300
|
|
8
10
|
export const MIN_INPUT_HEIGHT = 24
|
|
9
11
|
|
|
@@ -60,7 +62,7 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
60
62
|
|
|
61
63
|
&.visible {
|
|
62
64
|
> .space {
|
|
63
|
-
height:
|
|
65
|
+
height: var(--space-height, 'auto');
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -74,17 +76,15 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
74
76
|
top: 0;
|
|
75
77
|
left: 0;
|
|
76
78
|
right: 0;
|
|
77
|
-
height:
|
|
79
|
+
height: var(--content-height, 'auto');
|
|
78
80
|
padding-top: 8px;
|
|
79
81
|
background-color: ${theme.color.light[500]};
|
|
80
82
|
display: flex;
|
|
81
83
|
flex-direction: row;
|
|
82
84
|
gap: 4px;
|
|
83
|
-
border-right: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
|
|
84
|
-
border-left: 2px solid ${({ $inputFocused }) => theme.color.light[$inputFocused ? 600 : 500]};
|
|
85
85
|
|
|
86
86
|
.list-overflow {
|
|
87
|
-
max-width: calc(100% -
|
|
87
|
+
max-width: calc(100% - var(--list-margins, 0px));
|
|
88
88
|
height: 24px;
|
|
89
89
|
&:first-child {
|
|
90
90
|
margin-left: 0.25rem; // space added to the left when the close all button isn't rendered
|
|
@@ -100,31 +100,54 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
100
100
|
align-items: center;
|
|
101
101
|
gap: 6px;
|
|
102
102
|
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
gap: 4px;
|
|
109
|
-
line-height: 0.75rem;
|
|
110
|
-
white-space: nowrap;
|
|
111
|
-
|
|
112
|
-
${IconButton} {
|
|
113
|
-
padding: 4px 0;
|
|
114
|
-
background: none;
|
|
115
|
-
border: none;
|
|
116
|
-
width: auto;
|
|
117
|
-
height: auto;
|
|
106
|
+
> .context-bar {
|
|
107
|
+
--space-height: ${CONTEXT_BAR_HEIGHT - CONTEXT_BAR_DISPLACEMENT}px;
|
|
108
|
+
--content-height: ${CONTEXT_BAR_HEIGHT}px;
|
|
109
|
+
--list-margins: 30px; // close button width + gap
|
|
118
110
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
111
|
+
.context-badge > span {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: row;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: 4px;
|
|
116
|
+
line-height: 0.75rem;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
|
|
119
|
+
${IconButton} {
|
|
120
|
+
padding: 4px 0;
|
|
121
|
+
background: none;
|
|
122
|
+
border: none;
|
|
123
|
+
width: auto;
|
|
124
|
+
height: auto;
|
|
125
|
+
|
|
126
|
+
svg {
|
|
127
|
+
width: auto;
|
|
128
|
+
height: 12px;
|
|
123
129
|
}
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
> .upload-bar {
|
|
135
|
+
--space-height: ${UPLOAD_BAR_HEIGHT - UPLOAD_BAR_DISPLACEMENT}px;
|
|
136
|
+
--content-height: ${UPLOAD_BAR_HEIGHT}px;
|
|
137
|
+
--list-margins: 12px; // margins from .list-overflow
|
|
138
|
+
|
|
139
|
+
.list-overflow {
|
|
140
|
+
margin: 0 6px !important;
|
|
141
|
+
.scroll-to-left, .scroll-to-right {
|
|
142
|
+
top: 11px;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
input[type="file"] {
|
|
147
|
+
display: none;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
128
151
|
.wrapper-action {
|
|
129
152
|
position: relative;
|
|
130
153
|
|
|
@@ -153,8 +176,6 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
153
176
|
}
|
|
154
177
|
}
|
|
155
178
|
|
|
156
|
-
|
|
157
|
-
|
|
158
179
|
.button-group {
|
|
159
180
|
display: flex;
|
|
160
181
|
flex-direction: row;
|
|
@@ -252,6 +273,14 @@ export const MessageInputBox = styled.div<{$inputFocused?: boolean}>`
|
|
|
252
273
|
}
|
|
253
274
|
}
|
|
254
275
|
|
|
276
|
+
.message-menu {
|
|
277
|
+
position: absolute;
|
|
278
|
+
bottom: 34px;
|
|
279
|
+
.upload-item {
|
|
280
|
+
border-top: 1px solid ${theme.color.light[600]};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
255
284
|
textarea {
|
|
256
285
|
resize: none;
|
|
257
286
|
border: none;
|
package/src/views/Tools.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { agentToolsClient } from '@stack-spot/portal-network'
|
|
2
2
|
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
3
|
import { useEffect, useMemo } from 'react'
|
|
4
4
|
import styled from 'styled-components'
|
|
@@ -40,8 +40,9 @@ const ToolsPanel = () => {
|
|
|
40
40
|
const chat = widget.chatTabs.getAll().find(c => c.id === chatId)
|
|
41
41
|
return chat?.getMessages().find(m => m.id === messageId)?.getValue()
|
|
42
42
|
}, [messageId])
|
|
43
|
-
|
|
44
|
-
const
|
|
43
|
+
|
|
44
|
+
const [toolKits] = agentToolsClient.tools.useStatefulQuery({ enabled: !!message?.agent?.id })
|
|
45
|
+
const tools = useMemo(() => message?.tools?.map(id => toolById(id, toolKits) ?? { id }), [messageId, toolKits])
|
|
45
46
|
|
|
46
47
|
return !!tools?.length && (
|
|
47
48
|
<ToolList>
|
|
@@ -60,7 +60,7 @@ export const WorkspaceResources = ({ workspaceId, allKS, agent, stack }: Omit<Ta
|
|
|
60
60
|
const handleNavigate = (resource: WorkspaceResource) => {
|
|
61
61
|
startTransition(() => {
|
|
62
62
|
if (resource.resourceType === 'agent')
|
|
63
|
-
navigate({ component: 'agent', props: { visibility: '
|
|
63
|
+
navigate({ component: 'agent', props: { visibility: 'workspace', agent, workspaceId, showSubmitButton }, fullScreen: true })
|
|
64
64
|
|
|
65
65
|
if (resource.resourceType === 'ks')
|
|
66
66
|
navigate({ component: 'ks', props: { visibility: 'workspace', allKS, workspaceId, showSubmitButton }, fullScreen: true })
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InfoBar.d.ts","sourceRoot":"","sources":["../../../src/views/MessageInput/InfoBar.tsx"],"names":[],"mappings":"AA6BA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,+CA8EnB,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InfoBar.js","sourceRoot":"","sources":["../../../src/views/MessageInput/InfoBar.tsx"],"names":[],"mappings":"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"}
|